Skip to content

Rendering 3D Models

Cole Campbell edited this page Oct 10, 2020 · 1 revision

Ultraviolet 2020.10 adds built-in support for loading, rendering, and animating 3D models. Currently, Ultraviolet supports .stl, .gltf, and .glb files, though support for additional files can be provided by extending the content pipeline with custom code.

3D Model Basics

A static (i.e., non-animated) model is represented by the Model class and can be loaded through ContentManager as with any other kind of asset.

var myModel = content.Load<Model>("path/to/modelfile");

A model is a hierarchical type consisting of one more more ModelScene objects, each of which represents a tree of ModelNode objects. A node can optionally contain renderable geometry, though some nodes only contain transform data, and others are merely organizational. Nodes which contain geometry will have an associated ModelMesh instance, which is accessible through its Mesh property.

Scenes are drawn using the ModelSceneRenderer class or another class which derives from ModelSceneRendererBase<T1, T2>. The Draw() method will render the scene's geometry using the specified camera parameters, as in the example below.

var camera = PerspectiveCamera.Create();
camera.Position = new Vector3(0, 1, 500);
camera.Target = new Vector3(0, 100, 0);
camera.Update();

var renderer = new ModelSceneRenderer();
var worldMatrix = Matrix.Identity;
renderer.Draw(myModel.Scenes.DefaultScene, camera, ref worldMatrix);

Note that Model and ModelSceneRenderer do not support animations.

Skinned Models and Animation

To animate 3D models, you will need to use the SkinnedModel class. As with Model, you can load instances of this class using the content system.

var myModel = content.Load<SkinnedModel>("path/to/modelfile");

A SkinnedModel is a kind of template; it contains the model's geometry and the definitions of all of its animations, but it does not track animation state and therefore cannot be animated itself. To play animations, you need to create instances of the template using the SkinnedModelInstance class.

var instance = new SkinnedModelInstance(myModel);

Each SkinnedModelInstance represents a distinct animation state for the model. If your game has 100 characters in it, and each of those characters is separately animated from all of the others, then you will need 100 SkinnedModelInstance objects, one for each character. Each instance will have its own copy of the data used to animate the model's bones, but they'll all refer back to the original SkinnedModel instance for the basic geometry data.

You can play an animation by calling the PlayAnimation() method on SkinnedAnimationInstance. You can specify an animation either by its name (if it has one) or its logical index. Each SkinnedModelInstance contains 1 or more animation tracks, each of which can play a single animation; tracks are allocated to animations automatically by the PlayAnimation() method. If you attempt to play more animations on one instance than there are tracks, the least-recently-played animation will be stopped and its track will be reused for the new animation.

To render a skinned model instance, use the SkinnedModelSceneRenderer class. This class works analogously to the ModelSceneRenderer class used above, but supports skinned animation using SkinnedModelInstance and SkinnedEffect.

Effects and Materials

As it always has, Ultraviolet represents a shader program with the Effect class. The built-in BasicEffect can be used to render static 3D models using a standard lighting setup of three directional lights, and the built-in SkinnedEffect can be used to support basic skinned animation. You can also load custom shaders into your own Effect instances; see Creating Effects for more details.

The geometry of a ModelNode is associated with a Material object. A material specifies the set of parameters which are passed to the Effect used to render that geometry. For example, a material might specify the diffuse color and texture of the mesh.

The Material class is an abstract base class. Derived classes are associated with a specific kind of Effect and expose properties corresponding to that effect's expected parameters. For example, the BasicMaterial class is designed to be used with BasicEffect, and SkinnedMaterial is designed to be used with SkinnedEffect. Changing a mesh's Material will therefore change the Effect which is used to render that mesh.

Clone this wiki locally