Comunidad orientada al desarrollo de videojuegos

Creando Geometría en Wave Engine

OBJETIVOS

Ser capaces de crear un objeto geométrico.

Por ejemplo un cubo.

RESUMEN

En WaveServices.GraphicsDevice encontraremos una API a bajo nivel para dibujar.

Solo tenemos que generar un array de vértices y otro de índices y enviarlos al la GPU.

Hay dos formas de hacer esto.

USANDO UNA PRIMITIVA BÁSICA Y ESTÁTICA

La primera, si queremos crear una primitiva básica y estática, podemos hacerlo como se hace internamente en Wave con (WaveEngine.Components.Graphics3D) Model.CreateCube.

Model.CreateCube

En este ejemplo vamos a crear una clase cubo e inicializarla.

/// 
   /// 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]);
           }
       }
Obteniendo el cubo

Y después en la clase Model vamos a poder crear un método estático y obtener todos los cubos que queramos, solo usando nuestra nueva primitiva con el componente ModelRenderer.

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;
       }
Creando un componente personalizado

Podemos mantener los datos del modelo (vértices e índices) en una nueva clase componente (MyPrimitive : Component) y crear otro Drawable3D nuevo.

Para crear los vértices y los índices:

Vértices e Índices

Creamos e inicializamos las variables necesarias, por ejemplo:

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

//ToDo: Fill these arrays
Búfer

Ahora necesitamos subir todos los datos a la 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);
Dibujar

Después de configurar el búfer, necesitamos un método Draw para dibujar, donde añadimos este drawable capaz de renderizar cualquier capa, y establecemos los valores para el material que queremos utilizar. (Por ejemplo 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.

En DrawBasicUnit, se dibuja cada parte, si sólo tiene una parte es 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);

O si los vértices de nuestra primitiva cambian dinámicamente como un sistema de partículas podemos mantener esto en el Drawable3D también, y usar DynamicVertexBuffer para un mejor rendimiento.

Subiendo

También necesitamos subir los índices, vértices o ambos si cambian en cada frame (dentro del método DrawBasicUnit)

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);

Ejemplo

Aquí tenemos un simple ejemplo con una geometría personalizada.

clip_image002

FUENTES

Tutorial original en inglés: http://blog.waveengine.net

Para descargar Wave Engine: http://www.waveengine.net

Traducción por Carlos Sánchez López

, , , , , , , ,

Leave a Reply