You can also make your own procedural model by inheriting from PrimitiveProceduralModelBase
. This has several advantages:
- Write less code
- Available in Game Studio with preview and gizmo in scene editor
- Properties (including material) are editable in Game Studio
- Auto-generate tangents and bi-tangents required for advanced lighting
- Auto-generate bounding box and bounding sphere for correct shadow map bias
- Auto-generate additional texture coordinate channels
- Model space position offset
- Better upload/render performance due to correct data types

The workflow to add the model in Game Studio is then:
- Add any procedural model (Cube, Plane, etc.)
- Change the dropdown to your custom procedural model implementation
The workflow in code or script is:
- Add an instance of the script below to any entity
EDIT: Full sources - including model update per frame - are now available here: github.com/tebjan/StrideProceduralModel
Code of the custom model:
[DataContract("MyProceduralModel")]
[Display("MyModel")] // This name shows up in the procedural model dropdown list
public class MyProceduralModel : PrimitiveProceduralModelBase
{
// A custom property that shows up in Game Studio
/// <summary>
/// Gets or sets the size of the model.
/// </summary>
public Vector3 Size { get; set; } = Vector3.One;
protected override GeometricMeshData<VertexPositionNormalTexture> CreatePrimitiveMeshData()
{
// First generate the arrays for vertices and indices with the correct size
var vertexCount = 4;
var indexCount = 6;
var vertices = new VertexPositionNormalTexture[vertexCount];
var indices = new int[indexCount];
// Create custom vertices, in this case just a quad facing in Y direction
var normal = Vector3.UnitZ;
vertices[0] = new VertexPositionNormalTexture(new Vector3(-0.5f, 0.5f, 0) * Size, normal, new Vector2(0, 0));
vertices[1] = new VertexPositionNormalTexture(new Vector3(0.5f, 0.5f, 0) * Size, normal, new Vector2(1, 0));
vertices[2] = new VertexPositionNormalTexture(new Vector3(-0.5f, -0.5f, 0) * Size, normal, new Vector2(0, 1));
vertices[3] = new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, 0) * Size, normal, new Vector2(1, 1));
// Create custom indices
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
indices[3] = 1;
indices[4] = 3;
indices[5] = 2;
// Create the primitive object for further processing by the base class
return new GeometricMeshData<VertexPositionNormalTexture>(vertices, indices, isLeftHanded: false) { Name = "MyModel" };
}
}
Code of the script that adds the model by code:
public class AddMyModelScript : AsyncScript
{
// Declared public member fields and properties will show in the game studio
public float RotationSpeed { get; set; } = 1;
public override async Task Execute()
{
// Setup the custom model
await CreateMyModel();
while (Game.IsRunning)
{
// Do stuff every new frame
Entity.Transform.Rotation *= Quaternion.RotationY(MathUtil.DegreesToRadians(RotationSpeed));
await Script.NextFrame();
}
}
async Task CreateMyModel()
{
// The model classes
var myModel = new MyProceduralModel();
var model = new Model();
var modelComponent = new ModelComponent(model);
// Generate the procedual model
myModel.Generate(Services, model);
// Add a meterial
var material = Content.Load<Material>("MyModel Material");
model.Add(material);
// Add everything to the entity
Entity.Add(modelComponent);
}
}