Vamos aprender XNA? – Parte 5 – Rotating Cube 3D

Publicado em 10/04/2011

3


Olá pessoal, tudo certo?

No post de hoje mostro um exemplo clássico de computação gráfica: um cubo 3D que pode ser rotacionado, transladado e escalado.

Para entender o post de hoje, é fundamental que você tenha algum entendimento sobre utilização com 3D com XNA. Por isso, recomendo fortemente que, antes de ler esse post, dê uma olhada no post anterior dessa série. Se você nunca escreveu nada com XNA, talvez deseje dar uma olhada nos posts dessa série desde seu início.

Como de prática, todo o código-fonte demonstrado hoje está disponível no GitHub da série em http://github.com/ElemarJR/VamosAprenderXNA

Informação para desenhar um cubo

Lembra como desenhamos um “retângulo 3D”?! Pela combinação de dois triângulos. Logo, como será que desenhamos um Cubo? Combinando diversos retângulos. Repare o código que segue:

VertexPositionColor[] verts;
VertexBuffer vertexBuffer;
BasicEffect effect;
Camera camera;
protected override void LoadContent()
{
    camera = new Camera(this, new Vector3(0, 0, 5));
    Components.Add(camera);

    // Create a new SpriteBatch, which can be used to draw textures.
    spriteBatch = new SpriteBatch(GraphicsDevice);

    var tlf = new Vector3(-1.0f,  1.0f, -1.0f);
    var blf = new Vector3(-1.0f, -1.0f, -1.0f);
    var trf = new Vector3( 1.0f,  1.0f, -1.0f);
    var brf = new Vector3( 1.0f, -1.0f, -1.0f);
    var tlb = new Vector3(-1.0f,  1.0f,  1.0f);
    var trb = new Vector3( 1.0f,  1.0f,  1.0f);
    var blb = new Vector3(-1.0f, -1.0f,  1.0f);
    var brb = new Vector3( 1.0f, -1.0f,  1.0f);

    verts = new [] {
        // front
        new VertexPositionColor(tlf, Color.Red),
        new VertexPositionColor(blf, Color.Red),
        new VertexPositionColor(trf, Color.Red),
        new VertexPositionColor(blf, Color.Red),
        new VertexPositionColor(brf, Color.Red),
        new VertexPositionColor(trf, Color.Red),

        // back
        new VertexPositionColor(tlb, Color.Blue),
        new VertexPositionColor(trb, Color.Blue),
        new VertexPositionColor(blb, Color.Blue),
        new VertexPositionColor(blb, Color.Blue),
        new VertexPositionColor(trb, Color.Blue),
        new VertexPositionColor(brb, Color.Blue),

        // top
        new VertexPositionColor(tlf, Color.Green),
        new VertexPositionColor(trb, Color.Green),
        new VertexPositionColor(tlb, Color.Green),
        new VertexPositionColor(tlf, Color.Green),
        new VertexPositionColor(trf, Color.Green),
        new VertexPositionColor(trb, Color.Green),

        // bottom
        new VertexPositionColor(blf, Color.Yellow),
        new VertexPositionColor(blb, Color.Yellow),
        new VertexPositionColor(brb, Color.Yellow),
        new VertexPositionColor(blf, Color.Yellow),
        new VertexPositionColor(brb, Color.Yellow),
        new VertexPositionColor(brf, Color.Yellow),

        // left
        new VertexPositionColor(tlf, Color.Purple),
        new VertexPositionColor(blb, Color.Purple),
        new VertexPositionColor(blf, Color.Purple),
        new VertexPositionColor(tlb, Color.Purple),
        new VertexPositionColor(blb, Color.Purple),
        new VertexPositionColor(tlf, Color.Purple),

        // right
        new VertexPositionColor(trf, Color.Magenta),
        new VertexPositionColor(brf, Color.Magenta),
        new VertexPositionColor(brb, Color.Magenta),
        new VertexPositionColor(trb, Color.Magenta),
        new VertexPositionColor(trf, Color.Magenta),
        new VertexPositionColor(brb, Color.Magenta),
    };

    vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor),
        verts.Length, BufferUsage.None);

    vertexBuffer.SetData(verts);

    effect = new BasicEffect(GraphicsDevice);
}

O que fizemos?

  • definimos um conjunto básico de vértices representando os “quatro” extremos de nosso retângulo;
  • utilizando esses “quatro vértices fundamentais”, criamos os “dois triângulos” de cada lado do cubo;
  • utilizamos uma cor para cada lado;
  • inicializamos um objeto câmera (demonstrado no post anterior)
  • inicializamos um “VertexBuffer” para armazenar todo o conjunto.

Desenhando o Cubo 3D

Uma das belezas do XNA é que ele facilita o desenvolvimento de atividades complexas. Aliás, grandes parcelas de código permanecem “imutáveis” porque seu funcionamento é influenciado pelo conjunto de dados de entrada. Um ótimo exemplo disso são as rotinas de desenho. Observe o código que segue:

Matrix worldRotation = Matrix.Identity;
Matrix worldTranslation = Matrix.Identity;
Matrix worldScale = Matrix.Identity;
protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    GraphicsDevice.SetVertexBuffer(vertexBuffer);

    effect.World = worldScale * worldRotation * worldTranslation;
    effect.View = camera.View;
    effect.Projection = camera.Projection;
    effect.VertexColorEnabled = true;

    foreach (var pass in effect.CurrentTechnique.Passes)
    {
        pass.Apply();
        GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList,
            verts, 0, 12);
    }

    base.Draw(gameTime);
}

Essa rotina é basicamente idêntica a que utilizamos para desenhar um triângulo ou um retângulo. Como estamos utilizando um conjunto maior de dados temos um desenho diferente.

Já temos condições de executar o programa. O resultado obtido é ilustrado na figura que segue:

image 

Basicamente, estamos vendo a face frontal do cubo. Legal né?! Vamos adicionar alguma rotação?

Permitindo que o usuário rotacione o Cubo 3D

Se você observou nossa rotina de desenho, perceberá que utilizamos três matrizes para definir o mundo. Mostrei o conceito no post anterior. Vamos alterar nosso “Update” para “animar as coisas”.

float rotationStep = MathHelper.PiOver4 / 30;
protected override void Update(GameTime gameTime)
{
    // Allows the game to exit
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        this.Exit();

    if (Keyboard.GetState().IsKeyDown(Keys.Left))
        worldRotation *= Matrix.CreateRotationY(-rotationStep);
    else if (Keyboard.GetState().IsKeyDown(Keys.Right))
        worldRotation *= Matrix.CreateRotationY(rotationStep);

    if (Keyboard.GetState().IsKeyDown(Keys.Up))
        worldRotation *= Matrix.CreateRotationX(-rotationStep);
    else if (Keyboard.GetState().IsKeyDown(Keys.Down))
        worldRotation *= Matrix.CreateRotationX(rotationStep);


    if (Keyboard.GetState().IsKeyDown(Keys.L))
        worldTranslation *= Matrix.CreateTranslation(new Vector3(-0.01f, 0, 0));
    else if (Keyboard.GetState().IsKeyDown(Keys.R))
        worldTranslation *= Matrix.CreateTranslation(new Vector3(0.01f, 0, 0));

    if (Keyboard.GetState().IsKeyDown(Keys.T))
        worldTranslation *= Matrix.CreateTranslation(new Vector3(0, 0.01f, 0));
    else if (Keyboard.GetState().IsKeyDown(Keys.B))
        worldTranslation *= Matrix.CreateTranslation(new Vector3(0, -0.01f, 0));

    if (Keyboard.GetState().IsKeyDown(Keys.I))
        worldScale *= Matrix.CreateScale(1.01f);
    else if (Keyboard.GetState().IsKeyDown(Keys.O))
        worldScale *= Matrix.CreateScale(0.99f);
            
    base.Update(gameTime);
}

Pronto! Estou monitorando algumas teclas e alterando as matrizes de translação, rotação e escala conforme essas são pressionadas.

Brincando um pouco com o programa, temos:

image

Lindo!

Por hoje, era isso!

Smiley piscando

Etiquetado:, ,
Publicado em: Post