• Register

Wave Engine was born on February 21, 2013 with the exciting mission of developing an engine to help the mobile game developers’ community.

Post tutorial Report RSS Creating Geometry

Be able to create a Geometry Object. For example a Cube.

Posted by on - Basic Client Side Coding

Objectives

Be able to create a Geometry Object.
For example a Cube.

Resume

In WaveServices.GraphicsDevice you will find a low level API to draw.
You only need to generate a vertices and indices array and send it to the GPU.
There are two ways to do this.

Using Static and Base Primitive

The first one, if you want to create a static and basic primitive, you can do that like our
(WaveEngine.Components.Graphics3D) Model.CreateCube

In this example we are going to create a cube Class and initialices it.

/// 
/// A 3D cube.
/// 
internal sealed class Cube : Geometric
{
    #region Initialize
    /// 
    /// Initializes a new instance of the  class.
    /// 
    /// The size of cube.
    public Cube(float size)
    {
        // A cube has six faces, each one pointing in a different direction.
        Vector3[] normals =
        {
            new Vector3(0, 0, 1),
            new Vector3(0, 0, -1),
            new Vector3(1, 0, 0),
            new Vector3(-1, 0, 0),
            new Vector3(0, 1, 0),
            new Vector3(0, -1, 0)
        };

        Vector2[] texCoord =
        {
            new Vector2(1, 1), new Vector2(0, 1), new Vector2(0, 0), new Vector2(1, 0),
            new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1),
            new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1), new Vector2(0, 0),
            new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1), new Vector2(0, 0),
            new Vector2(0, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1),
            new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1), new Vector2(0, 0),
        };

        float sizeOverTwo = size / 2;

        // Create each face in turn.
        for (int i = 0, j = 0; i < normals.Length; i++, j += 4)
        {
            Vector3 normal = normals[i];

            // Get two vectors perpendicular to the face normal and to each other.
            Vector3 side1 = new Vector3(normal.Y, normal.Z, normal.X);
            Vector3 side2 = Vector3.Cross(normal, side1);

            // Six indices (two triangles) per face.
            this.AddIndex(this.VerticesCount + 0);
            this.AddIndex(this.VerticesCount + 1);
            this.AddIndex(this.VerticesCount + 3);

            this.AddIndex(this.VerticesCount + 1);
            this.AddIndex(this.VerticesCount + 2);
            this.AddIndex(this.VerticesCount + 3);

            // 0   3
            // 1   2

            // Four vertices per face.
            this.AddVertex((normal - side1 - side2) * sizeOverTwo, normal, texCoord[j]);
            this.AddVertex((normal - side1 + side2) * sizeOverTwo, normal, texCoord[j + 1]);
            this.AddVertex((normal + side1 + side2) * sizeOverTwo, normal, texCoord[j + 2]);
            this.AddVertex((normal + side1 - side2) * sizeOverTwo, normal, texCoord[j + 3]);
        }
    }
}

Getting the Cube
And later in Model class we can just create a static method and get as many cubes we want, only using your new primitive with ModelRenderer component.

public static Model CreateCube(float size)
{
   Model cube = new Model(string.Empty) { InternalModel = new InternalStaticModel() };
   cube.InternalModel.FromPrimitive(WaveServices.GraphicsDevice, new Cube(size));

   return cube;
}

Creating Custom Component

You can hold the model data (vertices and indices) in a new component class (MyPrimitive : Component) and create another new Drawable3D.

To create a vertices and indices:
Vertex and Index
Create and initialise variables needed, for example:

 ushort[] indices = new ushort[this.numIndices];
this.vertices = new VertexPositionColorTexture[this.numVertices];  //You can use different vertex formats

//ToDo: Fill these arrays

Buffer
Now we can upload all data to GPU

this.indexBuffer = new IndexBuffer(indices);
this.GraphicsDevice.BindIndexBuffer(this.indexBuffer);

this.vertexBuffer = new VertexBuffer(VertexPositionColorTexture.VertexFormat);
this.vertexBuffer.SetData(this.numVertices, this.vertices);
this.GraphicsDevice.BindVertexBuffer(this.vertexBuffer);

Draw
After setting buffer, you need to override the Draw method, where you are able to draw any render layer, and set values for the material that you want to use. (for example BasicMaterial)

public override void Draw(TimeSpan gameTime)
{
   base.Draw(gameTime); // Add to render layer (default behavior, but you can manage this to draw each part of you model in a different render layer)
   this.material.Matrices.World = this.localWorld;  //You can write this or in each DrawBasicUnit pass if this changes for each part.

In DrawBasicUnit, you draw each part, if you only have one part it is simple:

currentMaterial.Apply(this.RenderManager);  //You always need a shader to draw anything, but you can use a default material, for example BasicMaterial.

this.GraphicsDevice.DrawVertexBuffer(
                   NumVertices,
                   PrimitiveCount,
                   PrimitiveType.TriangleList,
                   VertexBuffer,
                   IndexBuffer);

Or if the vertices of your primitive change dynamically like a particle system you can hold this in the Drawable3D too, and use DynamicVertexBuffer for better performance.

Updating
You also need to upload indices, vertices or both if this changes in each frame (inside the DrawBasicUnit method)

this.vertexBuffer.SetData(this.numVertices, this.vertices);
this.GraphicsDevice.BindVertexBuffer(this.vertexBuffer);
this.GraphicsDevice.DrawVertexBuffer(this.numVertices,
                                     this.numPrimitives,
                                     PrimitiveType.TriangleList,
                                     this.vertexBuffer,
                                     this.indexBuffer);

Example


Here you have another simple demo about custom geometry (Custom Geometry Sample).

RYSkD6v

Post comment Comments
Guest
Guest - - 688,981 comments

This comment is currently awaiting admin approval, join now to view.

mtylerjr
mtylerjr - - 1 comments

I have to agree.. I am devastated at how bad this documentation is.

All I want is to see how to manually create an object programatically, with vertices, faces, normals, etc. This example starts to describe creating a cube manually, then gets 100% crytpic and unhelpful with things like "Getting the Cube
And later in Model class we can just create a static method and get as many cubes we want, only using your new primitive with ModelRenderer component."

There is no context, no information about what that means. What model class? What modelrenderer component?

It just leaves the reader to flounder around, trying to add various classes, trying to put the code into a class somewhere where it even compiles...

The closes I could come was to create my own class derived from Model, and stick the code in, but even then, it will not compile ("InternalModel is read only")

Whats worse is that there is no link to a complete compilable project with this code in it, to have a prayer of getting something to compile.

And to add insult to injury, there is a different example linked on the bottom that doesnt even attempt to create a 3d object, but instead focuses on custom coloring?

And so, I look at the actual waveengin documentation on models and meshes, and I get unhelpful descriptions like Model (name, modelpath)... name = "the name" and modelpath = "the modelpath"??

That's worse than useless!

There is absolutely no way to get any of the REQUIRED and NECESSARY information. NONE of the samples construct even a single, basic 3D custom (non primitive) object.

Ugh. So frustating. At LEAST post a link to the project you are showing excerpts from... a project that compiles... not some other mostly unrelated project.

The engine could be so awesome... but the documentation is utterly worse than useless.. it is almost as if it is designed to NOT be helpful.

Reply Good karma Bad karma+1 vote
Guest
Guest - - 688,981 comments

Even if this example worked, it isn't as helpful as it should be because it's describing a cube. I see a bunch of 1's and can't tell which bit corresponds to which corner or face.

It's pretty easy to go from a rectangular prism to a cube, but not the other way around.

Reply Good karma Bad karma+1 vote
Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: