• Series
  • Blazor
  • BabylonJS
  • C#
  • .NET

BabylonJS and Blazor - Importing Meshes

For this article we will go over Importing Meshes implemented in C#, this article will go over the process of importing glTF models. Checkout the What Changed section, as one of the interesting areas was what changed to accommodate better performance of ground detection.

Checkout BabylonJS and Blazor - Lanterns, the next step in the series!

Demo and Source Code

Below is a demo of the Blazor application after Step 7 is implemented, you can open the demo in a new tab by going to BabylonJS Blazor Step 07.

Use the Arrow keys to move the around the map. Space key to Jump and the Shift key to Dash while in the Air.

You can see the full source code in GitHub: canhorn/BabylonJS.Blazor.Game.Tutorial at step/07_Importing-Meshes

If you want to see the original step in the Series: Importing Meshes | Babylon.js Documentation (babylonjs.com)

Implementation Overview

With this step we remove the old MeshBuilder created Mesh objects and replace them with SceneLoader loaded Mesh objects. By using the SceneLoader we are able to take a fully built out Mesh Environment and load it into our Game. By using glTF, Graphics Language Transmission Format, asset files for our models we get a few advantages by using it with BabylonJS. The glTF Model format is a supported format for BabylonJS and if using the non-binary version you can see the structure of the model in plain text.

Not much else was done in this Step so we only have a few places updated to use the SceneLoader. I also uploaded all the asset files to the wwwroot folder, these will be used in the future articles. This gives the game a very nice polished look, it still has a few features left to implement but this is a large step in the direction of a finished Game.

What Changed

One area that needed changing, compared to how it was done with TypeScript, was in how the Player calculated if the mesh was on the ground or not. The calculation for gravity to the Player and if they are able to Dash needs to know if the state of the Player. The change was to the Ray check and the predicate, the predicate is used to check if a model is visible or not.

The Ray cast in in the down direction to the Player model, this uses a callback/predicate to check each mesh it was hit. The hit check problem causes a performance hit passing through the Blazor interop layer. This layer is performant enough for most scenarios but not performant enough when it has to happen 50 plus times a frame, at 60 frames per second.

The fix was to eliminated the per mesh callback used by the intersection check. The Ray checks IsVisible by default but the original Tutorial logic uses the IsVisible to hide meshes that are used for the ground. To get the performance we want, hide specific ground meshes, we set the mesh IsVisible = true and make the Visibility = 0 giving us the same results.

The reason to do this is so our Meshes have a consistent and predictable hit box. Some meshes have gaps for ascetics and the Ray has a chance to cast between these gaps. This can cause the Player to get stuck in some in the air, but collision stopping them from moving state. The Ray check against the hidden Ground meshes give the Player a consistently flat area to stand on.

Problem Area

We had one problem area with the Ray checks causing frame dips, making the game hard to play with the way the movement is locked to frames. We were able to work around this issue by removing the interop section of code and implementing the changes stated in the prior What Changed section. This issue was the only one ran into and fixed in this article, and future articles will build on what was done here for more advanced features.

Source Code Example(s)

Below is an example of loading the envSettings.glb file into BabylonJS, and working against the meshes after the glTF is loaded.

private async Task<EnvSettingAsset> LoadAsset()
{
    // Using SceneLoader of BabylonJS we are able
    // to load in a fully built out Game environment!
    var result = await SceneLoader.ImportMeshAsync(
        null,
        "./models/",
        "envSetting.glb",
        _scene
    );

    return new(
        result.meshes[0],
        result.meshes[0].getChildMeshes()
    );
}

After the player is loaded we are need to move them to the starting location in Environment, below we are looking for a node by id and using the Absolute Position to position the player model.

scene.getMeshByName(
    // The name on our player mesh 
    "outer"
).position = scene.getTransformNodeByID(
    // The starting position loaded as part of the environment glTF loading.
    "startPosition"
).getAbsolutePosition();

Cody's logo image, it is an abstract of a black hole with a white Event Horizon.

Cody Merritt Anhorn

A Engineer with a passion for Platform Architecture and Tool Development.