Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Udpate 8 - Moving MeshBuilder to manual section #52

Merged
merged 6 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Access the source code or contribute 🤝 to the Stride Game Engine on its [GitH

This section highlights the amazing contributors who have helped enhance the Stride Community Toolkit with their efforts.

- [DotLogix](https://github.com/dotlogix): MeshBuilder
- [DotLogix](https://github.com/dotlogix): Utility @Stride.CommunityToolkit.Rendering.Utilities.MeshBuilder, docs
- [Doprez](https://github.com/Doprez): Extensions
- [IXLLEGACYIXL](https://github.com/IXLLEGACYIXL): Extensions
- [Vaclav Elias](https://github.com/VaclavElias): Code only approach implementation, toolkit docs
Expand Down
139 changes: 4 additions & 135 deletions docs/manual/code-only/examples/procedural-geometry.md
Original file line number Diff line number Diff line change
@@ -1,145 +1,14 @@
# Procedural geometry

This example demonstrates how to create a procedural geometry mesh triangle, plane and circle and add them to a scene.
In this example, we delve into creating procedural geometry meshes including a triangle, a plane, and a circle, and integrate them into a Stride scene. Utilizing the @Stride.CommunityToolkit.Rendering.Utilities.MeshBuilder class from the toolkit, the process of crafting and rendering these geometries is streamlined.

![Stride UI Example](media/stride-game-engine-procedural-geometry.webp)

For this example we use a new community class called MeshBuilder.
it streamlines the creation of meshes using a simple builder syntax.
The `MeshBuilder` class is employed to define the layout and data for each mesh. For instance, the `GiveMeATriangle`, `GiveMeAPlane`, and `GiveMeACircle` methods demonstrate how to configure vertices, indices, and other properties to form a triangle, a plane, and a circle, respectively.

## Once there was a triangle:
Like in all rendering examples let's start with a simple triangle.
The `Update` method demonstrates a dynamic adjustment to the circle's segments count over time, showcasing a simple animation effect.


### VertexElement
A vertex element is a value assigned to a specific vertex.
In Stride we can use a lot of different types to define our data some of them are `Vector3`, `Vector4`, `Color`, `Int`, `Float`, `Half` and more.

We need to tell stride how we want layout this data so our shader can read our various values.
This is where Vertex elements come in. They define a SemanticName, SemanticIndex, Offset and a Size.

By default stride allows you to define structs for your data or to use custom buffers for your data.
For most use-cases this is probably fine and the easier approach compared to a mesh builder.

However if you need to define your data dynamically or want a generalized method here we go.

### MeshBuilder
The meshbuilder abstracts away a lot of complexity behind custom buffers, type erasure, memory allocation and instead
provides you with a simpler builder API to define your mesh dynamically.

It uses the same concepts as Stride so you still have to define your layout manually but the process is easier and you don't need to worry about the memory layout yourself.
To use a MeshBuilder just create a new instance.

```csharp
using var meshBuilder = new MeshBuilder()
```

__Note__:
Notice the using at the front. This part is important always dispose your mesh builder if you don't need it anymore.
The builder uses pooling behind the scenes and if you don't dispose it it can never return the internal buffers which can lead to significant performance degregation.

### Layout:
As we discussed earlier we need to tell the `MeshBuilder` which data types and fields we want to use.
For this we use the various `With...` methods.

##### Primitive Type:
First we need to select a primitive type in our example we create a bunch of triangles so we use this code:

```csharp
meshBuilder.WithPrimitiveType(PrimitiveType.TriangleList);
```

##### Indexing:
The mesh builder supports three types of indexing, `None`, `Int16` or `Int32`.
In our case we definitely don't need more than 32k indices so we can safely use the Int16 version.

```csharp
meshBuilder.WithIndexType(IndexingType.Int16);
```
### Elements:
For our example we will use a Vertex with a Position and a Color element.

```csharp
var position = meshBuilder.WithPosition<Vector3>();
var color = meshBuilder.WithColor<Color>();
```

These methods return the element index we need when we set our actual values.
We use a `Vector3` as our vertex position and a `Color` for our vertex color.
Other types would work as well but these are very common so we will use them as well.

### Vertices:
Next we define our vertices. For that we use a new method `AddVertex`.
This will add a new vertex to our builder and allows us to use the `Get/SetElement` methods.

You can also declare multiple Vertices before setting the actual values but this is the simplest way for now.

```csharp
meshBuilder.AddVertex();
```

After this we can set our element data. For this we use the `SetElement` method.
It expects an element index (which we received from `WithPosition` and `WithColor` earlier) as well as your desired value.
```csharp
meshBuilder.SetElement(position, new Vector3(0, 0, 0));
meshBuilder.SetElement(color, Color.Red);
```

We repeat this for the other two triangle points as well.
```csharp
meshBuilder.AddVertex();
meshBuilder.SetElement(position, new Vector3(1, 0, 0));
meshBuilder.SetElement(color, Color.Green);

meshBuilder.AddVertex();
meshBuilder.SetElement(position, new Vector3(.5f, 1, 0));
meshBuilder.SetElement(color, Color.Blue);
```

### Indices:
Next we need to tell the `MeshBuilder` how we want to connect the vertices.
We configured indexing for our builder so we need to do this explicitly.
For a simple example like this you could also completely skip the indexing part and use `IndexingType.None` instead.

the winding order in stride is counter-clockwise so we use these indices.
```csharp
meshBuilder.AddIndex(0);
meshBuilder.AddIndex(2);
meshBuilder.AddIndex(1);
```

### Mesh:
The only thing left is building the actual mesh. For this we use this method.
It expects a graphics device as an argument. If you call this from a script this is usually avalable for you using the `GraphicsDevice` property of your script.
```csharp
meshBuilder.ToMeshDraw(GraphicsDevice)
```

##### Display
To show this `MeshDraw` on screen we create a `ModelComponent` and add our `MeshDraw` as new model.
We also need to define a material to use our vertex colors on screen. Else the Triangle would just be white.
```
var model = new Model
{
new MaterialInstance {
Material = Material.New(graphicsDevice, new MaterialDescriptor {
Attributes = new MaterialAttributes {
DiffuseModel = new MaterialDiffuseLambertModelFeature(),
Diffuse = new MaterialDiffuseMapFeature {
DiffuseMap = new ComputeVertexStreamColor()
},
}
})
},
new Mesh {
Draw = meshBuilder.ToMeshDraw(graphicsDevice),
MaterialIndex = 0
}
}
```

Congrats you got a triangle.
For more examples take a look at the full example:
For more details of `MeshBuilder`, refer to our [MeshBuilder manual](../../rendering/mesh-builder.md).

View on [GitHub](https://github.com/stride3d/stride-community-toolkit/tree/main/examples/code-only/Example05_ProceduralGeometry).

Expand Down
143 changes: 143 additions & 0 deletions docs/manual/rendering/mesh-builder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# MeshBuilder

## Introduction

The `MeshBuilder` is a utility class allowing dynamic creation of meshes at runtime. Acting as a wrapper around the `Mesh` class, it provides a simpler API for defining the mesh layout and data.

## Once there was a triangle
Like in all rendering examples let's start with a simple triangle.

## `VertexElement`
A vertex element is a value assigned to a specific vertex.
In Stride we can use a lot of different types to define our data some of them are `Vector3`, `Vector4`, `Color`, `Int`, `Float`, `Half` and more.

We need to tell Stride how we want layout this data so our shader can read our various values.
This is where vertex elements come in. They define a `semanticName`, `semanticIndex`, `offset` and a `size`.

By default Stride allows you to define structs for your data or to use custom buffers for your data.
For most use-cases this is probably fine and the easier approach compared to a mesh builder.

However if you need to define your data dynamically or want a generalized method here we go.

## `MeshBuilder`
The @Stride.CommunityToolkit.Rendering.Utilities.MeshBuilder abstracts away a lot of complexity behind custom buffers, type erasure, memory allocation and instead
provides you with a simpler builder API to define your mesh dynamically.

It uses the same concepts as Stride so you still have to define your layout manually but the process is easier and you don't need to worry about the memory layout yourself.
To use a `MeshBuilder` just create a new instance.

```csharp
using var meshBuilder = new MeshBuilder()
```

> [!NOTE]
> Notice the `using` at the front. This part is crucial as you should always dispose of your mesh builder when it's no longer needed. The builder utilizes pooling behind the scenes, and failing to dispose of it prevents the return of internal buffers, which can lead to significant performance degradation.

## Layout
As we discussed earlier we need to tell the `MeshBuilder` which data types and fields we want to use.
For this we use the various `With...` methods.

### Primitive Type
First we need to select a primitive type in our example we create a bunch of triangles so we use this code:

```csharp
meshBuilder.WithPrimitiveType(PrimitiveType.TriangleList);
```

### Indexing
The mesh builder supports three types of indexing, `None`, `Int16` or `Int32`.
In our case we definitely don't need more than 32k indices so we can safely use the `Int16` version.

```csharp
meshBuilder.WithIndexType(IndexingType.Int16);
```
## Elements
For our example we will use a vertex with a `position` and a `color` element.

```csharp
var position = meshBuilder.WithPosition<Vector3>();
var color = meshBuilder.WithColor<Color>();
```

These methods return the element index we need when we set our actual values.
We use a `Vector3` as our vertex position and a `Color` for our vertex color.
Other types would work as well but these are very common so we will use them as well.

## Vertices
Next we define our vertices. For that we use a new method @Stride.CommunityToolkit.Rendering.Utilities.MeshBuilder.AddVertex.
This will add a new vertex to our builder and allows us to use the `Get/SetElement` methods.

You can also declare multiple vertices before setting the actual values but this is the simplest way for now.

```csharp
meshBuilder.AddVertex();
```

After this we can set our element data. For this we use the [SetElement()](xref:Stride.CommunityToolkit.Rendering.Utilities.MeshBuilder.SetElement``1(System.Int32,System.Int32,``0)) method.
It expects an element index (which we received from `WithPosition` and `WithColor` earlier) as well as your desired value.

```csharp
meshBuilder.SetElement(position, new Vector3(0, 0, 0));
meshBuilder.SetElement(color, Color.Red);
```

We repeat this for the other two triangle points as well.

```csharp
meshBuilder.AddVertex();
meshBuilder.SetElement(position, new Vector3(1, 0, 0));
meshBuilder.SetElement(color, Color.Green);

meshBuilder.AddVertex();
meshBuilder.SetElement(position, new Vector3(.5f, 1, 0));
meshBuilder.SetElement(color, Color.Blue);
```

## Indices
Next we need to tell the `MeshBuilder` how we want to connect the vertices.
We configured indexing for our builder so we need to do this explicitly.
For a simple example like this you could also completely skip the indexing part and use `IndexingType.None` instead.

The winding order in Stride is counter-clockwise so we use these indices.

```csharp
meshBuilder.AddIndex(0);
meshBuilder.AddIndex(2);
meshBuilder.AddIndex(1);
```

## Mesh
The only thing left is building the actual mesh. For this we use this method.
It expects a graphics device as an argument. If you call this from a script this is usually available for you using the `GraphicsDevice` property of your script.
```csharp
meshBuilder.ToMeshDraw(GraphicsDevice);
```

### Display
To show this `MeshDraw` on screen we create a `ModelComponent` and add our `MeshDraw` as new model.
We also need to define a material to use our vertex colors on screen. Else the Triangle would just be white.
```
var model = new Model
{
new MaterialInstance {
Material = Material.New(graphicsDevice, new MaterialDescriptor {
Attributes = new MaterialAttributes {
DiffuseModel = new MaterialDiffuseLambertModelFeature(),
Diffuse = new MaterialDiffuseMapFeature {
DiffuseMap = new ComputeVertexStreamColor()
},
}
})
},
new Mesh {
Draw = meshBuilder.ToMeshDraw(graphicsDevice),
MaterialIndex = 0
}
}
```

Congrats 🥳 you got a triangle.

## Example

For a more comprehensive usage of `MeshBuilder`, explore our [Procedural Geometry](../code-only/examples/procedural-geometry.md) example where you'll find detailed code on creating complex geometries dynamically.
4 changes: 4 additions & 0 deletions docs/manual/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,9 @@
href: camera-extensions/index.md
- name: Game Extensions
href: game-extensions/index.md
- name: Rendering
items:
- name: MeshBuilder
href: rendering/mesh-builder.md
- name: Troubleshooting
href: troubleshooting.md
2 changes: 1 addition & 1 deletion examples/code-only/Example05_ProceduralGeometry/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
Entity? circleEntity = null;
game.Run(start: Start, update: Update);


void Start(Scene rootScene)
{
game.SetupBase3DScene();

AddMesh(game.GraphicsDevice, rootScene, Vector3.Zero, GiveMeATriangle);
AddMesh(game.GraphicsDevice, rootScene, Vector3.UnitX * 2, GiveMeAPlane);
}

void Update(Scene rootScene, GameTime gameTime)
{
var segments = (int)((Math.Cos(gameTime.Total.TotalMilliseconds / 500) + 1) / 2 * 47) + 3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Pastel" Version="4.1.0" />
<PackageReference Include="Pastel" Version="4.2.0" />
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using Stride.Graphics;
using Stride.Rendering;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Stride.Graphics;
using Stride.Rendering;
using Buffer = System.Buffer;

namespace Stride.CommunityToolkit.Rendering.Utilities;

/// <summary>
/// A utility class for building meshes by defining vertex elements with different data types and primitives types.
/// </summary>
public class MeshBuilder : IDisposable
{
/// <summary>
Expand Down