Todos os posts e artigos sobre Direct3D

D3DMobileDoctor: identificação de drivers gráfico para dispositivos móveis

Publicado por Marcos Dell Antonio em 03/07/2007 | .NET, Direct3D, Mobilidade

O D3DMobileDoctor, disponível no Channel9, é um aplicativo que identifica certas configurações gráficas de um device, tais como: DriverName, DriverVersion, etc.

Através destas informações você pode configurar uma aplicação 3D de acordo com as características do dispositivo. Por exemplo, se você identificar um driver Intel 2007G, uma boa opção é desabilitar as luzes, pois há uma queda de performance quando elas estão habilitadas com este processador.

Veja uma screenshot do programa rodando no emulador para Pocket PC + Windows Mobile 5.0 do Visual Studio 2005:

D3DMobileDoctor

O download do código fonte pode ser feito aqui: D3DMobileDoctor.

Saiba mais sobre o Managed Direct3D Mobile (MD3DM) lendo os posts sobre o assunto aqui no blog ou assistindo o vídeo Writing Managed 3D App for Mobile Devices, do Chris Muench.

O Mark Prentice também escreveu um ótimo post sobre o desenvolvimento de soluções para dispositivos móveis utilizando o Direct3D Mobile e o Windows Mobile 5.0.

É isso ae. Até +.

Seja o primeiro a comentar este post

MD3DM: Renderizando objetos texturizados

Publicado por Marcos Dell Antonio em 01/05/2007 | .NET, Direct3D, Mobilidade

A renderização de objetos texturizados, penúltima etapa do meu TCC, é muito simples de ser executada no Managed Direct3D Mobile.

Basicamente o desenvolvedor deve se preocupar com o seguinte:

  • Carregar todas as texturas necessárias dentro de uma lista ou array de objetos Texture;
  • Definir a luz ambiente do Device;
  • Definir a luz ambiente do objeto utilizando um material;
  • Para cada objeto, definir a textura que o Device utilizará para desenhá-lo.

Com essa receita de bolo as coisas ficam mais fáceis. Quem dera eu tê-la a 8 horas atrás (é, feriado só existe pros amigos :().

Na prática, isso fica da seguinte maneira:

- Carregar as texturas

A partir de um array de nomes de arquivos (normalmente) basta utilizar o método FromFile da classe TextureLoader para carregar todas as texturas em um array ou lista de objetos Texture. Exemplo:

string file = @"Program Files...cube_text.jpg";
texture = TextureLoader.FromFile(device, file);

No exemplo acima só tenho um objeto e uma textura, por isso utilizei uma única variável.

- Definir a luz do ambiente e do objeto

Eu simplesmente esqueci disso por 8 horas. Passei todo este tempo removendo e adicionando código tentando fazer com que as texturas aparecessem. Nada!

Porém, vale a pena salientar um detalhe: as texturas eram renderizadas, só não estavam iluminadas. Logo, o que acontece se você entra com um papel preto numa sala escura? Não vê ele. O mesmo acontecia com as minhas texturas.

Para definir a luz ambiente do Device, faça o seguinte:

device.RenderState.Ambient = Color.White;

E a luz ambiente do objeto:

material = new Material();
material.Ambient = Color.White;

device.Material = material;

- Definir a textura do objeto a ser desenhado

Esta é a parte mais fácil e óbvia. Sempre que o Device for desenhar os objetos, utilize o seguinte comando:

device.SetTexture(0, texture);

- Exemplo

Um trecho de código demonstrando todos os itens acima é apresentado abaixo:

private void Render()
{
    device.Clear(ClearFlags.Target,
                 Color.White,
                 1.0f,
                 0);
    device.BeginScene();

    if (texture != null)
        texture.Dispose();

    string file = @"...cube_text.jpg";
    texture = TextureLoader.FromFile(device, file);

    material = new Material();
    material.Ambient = Color.White;

    device.Material = material;
    device.SetTexture(0, texture);

    device.RenderState.Ambient = Color.White;

    device.SetStreamSource(0, vertexBuffer, 0);
    device.DrawPrimitives(PrimitiveType.TriangleList,
                          0,
                          2);

    device.EndScene();
    device.Present();
}

É claro que o seu VertexBuffer deve possuir, além dos vértices e/ou normais, as coordenadas de textura (U e V).

É isso ae. Até +.

Seja o primeiro a comentar este post

.NET: alternativa para o OpenFileDialog nos Smartphones

Publicado por Marcos Dell Antonio em 27/03/2007 | .NET, Direct3D, Mobilidade

Os Smartphones não suportam a janela de pesquisa OpenFileDialog (os Pocket PC sim). Logo, nada mais natural do que arrumar uma alternativa.

O OpenNETCF é um framework open source que estende o .NET Compact Framework em diversas direções. Uma destas extensões é justamente a OpenFileDialog.

Estou utilizando ela no meu TCC e até o momento não tenho o que reclamar. A “instalação” foi muito simples e o uso mais ainda, pois segue a mesma idéia da implementação oficial.

Para ter acesso a esta funcionalidade, basta fazer o download da OpenFileDialog implementada pela OpenNETCF, executar a instalação, navegar até a pasta bin do projeto SmartphoneDialogs e usar a dll que está disponível.

Eu estou usando o Windows Mobile 6 e, consequentemente, o .NET CF 2.0. Funcionou perfeitamente. Bastou fazer a referência e usar o seguinte trecho de código:

OpenNETCF.Windows.Forms.OpenFileDialog dlg =
    new OpenNETCF.Windows.Forms.OpenFileDialog();

dlg.Title = "...";
dlg.Filter = "TXT Files|*.txt";
dlg.FilterIndex = 1;                

dlg.InitialDirectory = @"your path";

if (dlg.ShowDialog() == DialogResult.OK)
{
    // do some stuff :) 
}

A tela de pesquisa que aprece é a seguinte:

É isso ae! Até +.

Fonte: MSDN

Seja o primeiro a comentar este post

MD3DM e suas limitações

Publicado por Marcos Dell Antonio em 27/03/2007 | .NET, Direct3D, Mobilidade

O Managed Direct3D Mobile, API que estou usando para desenvolver o meu Trabalho de Conclusão de Curso, possui infinitas limitações.

Somando-se estas com as dos dispositivos móveis, temos um número ainda maior que o infinito (?). Às vezes chega a torrar o saco do cara.

As últimas duas que encontrei foram estas:

- Não é possível criar um device e usar um panel no parâmetro renderWindow;

- O parâmetro renderWindow, além de não poder ser um panel, deve ser o form principal, aquele do Application.Run().

Considerando o nível de sono que estou no momento, não tive saco para pesquisar mais detalhes sobre isto. O que achei foi só um comentário no site brains-N-brawn relatando exatamente este problema.

O erro grotesco que aparece quando tento criar o device usando um panel é o 80004005.

No que isso influencia? Bom, você simplesmente não poderá colocar um menu, criar uma tela de apresentação da sua aplicação, etc. Eu, por exemplo, inicio a aplicação e para carregar algum arquivo o usuário deve utilizar a tecla número 1 para abrir uma tela de pesquisa. Ficou meio torto, mas como não é focado pros ditos “usuários finais” (leigos), quebrou o galho.

Falando em abrir uma tela para selecionar arquivos (o tal do OpenFileDialog), isto não está disponível para os Smartphones (para Pocket PC está). Amanhã escrevo um post sobre como contornar este outro problema.

É isso ae. Até +.

Seja o primeiro a comentar este post

.NET: como acessar os arquivos em um smart device?

Publicado por Marcos Dell Antonio em 26/03/2007 | .NET, Direct3D, Mobilidade

Complementando o post anterior, neste vou escrever sobre como acessar um arquivo (seja ele uma imagem, um txt, ou qualquer outra coisa) que está no emulador ou smart device.

Como já foi descrito, existem duas maneiras de enviar um arquivo para o dispositivo: embutido no executável (Build Action = Embedded Resource) ou literalmente copiado da máquina origem para a destino (Build Action = Content).

Para acessar arquivos enviados como Embedded Resource, o código é este:

string f = "NomeDoProjeto.NomeDoArquivo.xxx";
Stream s = Assembly.GetExecutingAssembly().
               GetManifestResourceStream(f);

Veja que o nome do arquivo deve conter o nome do projeto também. Caso o arquivo esteja dentro de alguma pasta, deverá ser aparecer também. Ex: NomeDoProjeto.Imagens.Figura1.bmp.

Para acessar os arquivos enviados como Content, ou seja, copiados da origem pro destino da forma mais normal que existe, é da seguinte maneira:

string path = Path.GetDirectoryName
    (Assembly.GetExecutingAssembly().
     GetName().CodeBase);

string file = path + @"NomeDoArquivo.xxx";
StreamReader stream = new StreamReader(file); 

Repare na manobra usada para pegar o diretório atual. Todo aquele estupro de legibilidade existe porque o método GetCurrentDirectory da classe Directory não funciona no .NET Compact Framework.

Também vale a pena lembrar que por default o executável e os arquivos são copiados para a pasta \Program Files\NomeDoProjeto.

É isso ae. Até +.

Seja o primeiro a comentar este post

.NET: Propriedades dos arquivos

Publicado por Marcos Dell Antonio em 26/03/2007 | .NET, Direct3D, Mobilidade

Provavelmente muitos desenvolvedores já clicaram com o botão direito sobre um arquivo (seja ele um .cs, .aspx, .bmp, etc) e checaram as propriedades apresentadas na janela que abriu.

Muitas delas são intuitivas, ou seja, de fácil entendimento. Porém, neste tarde tive que passar algumas horas até descobrir exatamente o funcionamento de todas elas.

Eu precisava copiar um arquivo .bmp para o emulador assim que fosse feito o deploy da aplicação. Uma maneira muito simples para ter acesso ao arquivo é usando o Build Action como Embedded Resource. Mas desta forma o executável cresce exponencialmente (aqui passou de 10k para 70k em um teste simples).

A solução mais correta, neste caso, é copiar o arquivo .bmp somente uma vez para o emulador ou dispositivo, pois ele permanecerá intacto durante toda a execução. Como fazer isso?

Antes de apresentar a minha solução, vou descrever o que cada opção significa.

- Build Action: indica o que será feito com o arquivo assim que o build do projeto for executado. Possui as seguintes opções:

  1. None: o arquivo não é copiado para o diretório destino do executável e também não é compilado. É usado normalmente para arquivos de documentação.
  2. Compile: o arquivo é compilado dentro do executável. Usado para arquivos fonte (.cs, .vb, etc).
  3. Content: o arquivo não é compilado mas é copiado para a mesma pasta do executável. Normalmente é usado para arquivos de imagem, .htm, etc.
  4. Embedded Resource: o arquivo fará parte do executável gerado. Ou seja, não será copiado para o destino, mas sim estará “embutido” no executábel (embedded).

Também é possível criar novas opções personalizadas. O blog do time de desenvolvimento do MSBuild publicou um post falando mais sobre o assunto.

- Copy to output directory: específica as condições de cópia do arquivo para o diretório destino do executável. Possui as seguintes opções:

  1. Do not copy: nunca copia o arquivo.
  2. Copy always: sempre copia.
  3. Copy if newer: copia o arquivo somente se for novo. Para os smart devices, as técnicas de comparação para saber se o arquivo é novo estão descritas na MSDN.

- Custom Tool e Custom Tool Namespace: define os componentes e o namespace usado para converter arquivos de um tipo para outro em tempo de design. Veja mais sobre o assunto na MSDN.

Sobre o meu problema, bastou adicionar o arquivo bmp ao projeto e definir a opção Build Action como Content e a Copy to output folder como Copy if newer. Desta forma, sempre que o arquivo bmp mudar no meu computador, será recopiado para o emulador ou dispositivo.

Claro que esta dica também é válida para projetos Windows Forms e WEB.

Fonte: MSDN - File Properties

É isso ae. Até +.

Seja o primeiro a comentar este post

MD3DM: Onde foram parar meus Meshes?

Publicado por Marcos Dell Antonio em 17/03/2007 | .NET, Direct3D, Mobilidade

Dando continuidade à série de posts sobre o meu Trabalho de Conclusão de Curso, vou escrever hoje sobre um erro muito comum que qualquer iniciante pode cometer quando for desenvolver utilizando o .NET CF 2.0 e o Managed Direct3D Mobile.

Para maiores informações sobre o assunto, veja meus outros posts aqui.

- O problema

Em todos os exemplos que desenvolvi até agora, sempre carreguei os objetos a serem desenhados dentro do método Render(), que é chamado no OnPaint() (saiba mais sobre esse conjunto de métodos aqui). Em outras palavras, quando era preciso desenhar, eu criava os objetos e desenhava. Ok, sem problemas à primeira vista, mas e a performance? E se os objetos forem gigantes, vindos de arquivos fonte, que é ainda mais demorado para ler e carregar todas as informações na memória do dispositivo?

Solução: carregar os objetos no início da aplicação e mantê-los em memória para que na hora da renderização não seja necessário carregar coisa alguma. Certo? Errado!

A implementação sugerida acima fica desta maneira:

- Inicialização do Device e do objeto a ser desenhado (Mesh)

private void InitializeGraphics()
{
    try
    {
        PresentParameters presentParams =
            new PresentParameters();
        presentParams.Windowed = true;
        presentParams.SwapEffect = SwapEffect.Discard;

        // Device
        device = new Device(
            0,
            DeviceType.Default,
            this,
            CreateFlags.None,
            presentParams);
        device.RenderState.CullMode = Cull.None;
        device.RenderState.Lighting = false;

        mesh = Mesh.Box(device, 1, 1, 0);
    }
    catch (Exception ex)
    {
        MessageBox.Show("Erro na inicialização: " +
            ex.Message);
    }
}

- Renderização do Mesh

/// <summary>
/// Render the data
/// </summary>
private void Render()
{
    // Clear the device
    device.Clear(ClearFlags.Target, Color.White, 1.0f, 0);

    // Begin the scene
    device.BeginScene();

    mesh.DrawSubset(0);

    // End the scene and present
    device.EndScene();
    device.Present();
}

A princípio não há problema algum nessa implementação. Porém, ao executá-la a seguinte exception (System.ObjectDisposedException) é lançada na chamada do método mesh.DrawSubset(0):


Ora pois, se criei o Mesh e mandei o Device desenhá-lo não deveria acontecer este problema… mas isso aqui é computação, esqueceu? Faz parte do show.

- A Solução

Ao invés de criar o objeto Mesh logo após a criação do Device, devemos criá-lo quando o Device é reiniciado.

Por que hããm? Pois antes de renderizar as informações, o Device é reiniciado e perde todas as definições que o Mesh tinha quando foi criado.

Como resolver este problema? Simples:

- Alterar a criação do device (repare no evento OnDeviceReset)

private void InitializeGraphics()
{
    try
    {
        PresentParameters presentParams =
            new PresentParameters();
        presentParams.Windowed = true;
        presentParams.SwapEffect = SwapEffect.Discard;

        // Device
        device = new Device(
            0,
            DeviceType.Default,
            this,
            CreateFlags.None,
            presentParams);
        device.RenderState.CullMode = Cull.None;
        device.RenderState.Lighting = false;

        device.DeviceReset += new EventHandler(OnDeviceReset);
    }
    catch (Exception ex)
    {
        MessageBox.Show("Erro na inicialização: " +
            ex.Message);
    }
}

- Criar o Mesh quando o Device for reiniciado

protected void OnDeviceReset(object sender, EventArgs e)
{
    mesh = Mesh.Box(device, 1, 1, 0);
}

Discretamente vou dar um palpite da origem do erro: todas as informações do Mesh (VertexBuffer, IndexBuffer, etc) ficam na memória do Device. Assim que é reiniciado essas informações são apagadas, conforme descrito em um post anterior.

É isso ae! Até +. ;)

Seja o primeiro a comentar este post

Gita: visualizador de modelos 3D para dispositivos móveis

Publicado por Marcos Dell Antonio em 16/03/2007 | .NET, Direct3D, Mobilidade

Calma! Não é trabalhando 12h por dia que conseguiria publicar o projeto completo tão cedo.

Gita é o nome escolhido para o meu projeto de conclusão de curso. Faz alusão à música Gita do cantor e compositor Raul Seixas, no qual sou um fã descarado.

Hospedei os fontes no Codeplex, que é um host de projetos open source (estilo o java.net). Para acessá-lo precisei instalar um plugin pro Visual Studio que dá acesso ao Team Foundation Server que roda no servidor. Este plugin é o tal do Team Explorer.

Por enquanto só o básico está lá: uma solução (Class Library) chamada Gita e um projeto exemplo (que ainda não está 100%).

Tudo isso ainda é insuficiente para gerar um resultado final decente. No entanto resolvi publicar e anunciar aqui, pois pode ser que mais algum maluco esteja precisando de alguns exemplos. Como o material sobre este assunto em brasileiro é praticamente null, quem sabe pode tornar-se uma boa fonte de referência.

O projeto pode ser acessado aqui. Para ir diretamente aos fontes, pode ser por aqui.

Abaixo uma breve TO-DO list:

  • Corrigir o bug ao renderizar a lista de Meshes da classe World;
  • Ler os vértices e faces do arquivo OBJ, carregar em memória e gerar um Mesh a partir deles;
  • Codificar rotinas básicas de rotação, translação e escala acessíveis através das teclas do dispositivo.

É isso ae. Até +!

Seja o primeiro a comentar este post

MD3DM: Como ler um arquivo no formato OBJ (Wavefront)

Publicado por Marcos Dell Antonio em 06/03/2007 | .NET, Direct3D, Mobilidade

Finalmente cheguei na parte interessante do Trabalho de Conclusão de Curso: ler um arquivo no formato OBJ e renderizá-lo na tela do celular.

Antes de chegar até este ponto, estudei desde o início o funcionamento da API Managed Direct3D Mobile. Até agora ela está se saindo muito bem, apesar da falta de documentação (compensada pelos documentos sobre o irmão mais velho, Managed DirectX).

Em vários sites encontrei a definição do formato OBJ, portanto me parece que é bem aceitado entre a comunidade de desenvolvedores. No entanto, até agora não achei uma implementação em C# para trabalhar com ele. Logo, parti do princípio: criar uma classe, ler o arquivo, carregar algumas informações na memória e renderizá-las.

Com a ajuda do usuário Punkoff (é só o que sei sobre ele) do fórum MSDN, consegui tomar um excelente rumo no desenvolvimento do trabalho. Ele me ajudou em vários momentos onde estava pensando em chutar o balde. Acredito que ele não lerá isto, mas de qualquer forma fica aqui a minha gratidão ao tempo dedicado a um desconhecido.

A implementação que fiz é algo bem simples. Antes de descrevê-la, vou explicar rapidamente como funciona um arquivo no formato OBJ.

Este formato de arquivo é o tão conhecido “texto puro”, ou seja, dá pra abrir com o bloco de notas ou qualquer outro editor simples. No meu ponto de vista, é muito mais fácil trabalhar com arquivos deste tipo do que com os binários, pois a legibilidade do conteúdo é essencial para os iniciantes entenderem exatamente o que estão fazendo.

Dentro de um arquivo OBJ pode-se encontrar várias “coisas”, dentre elas:

- comentários: são linhas que começam com o caracter #. Exemplo:

# Isso é um teste

- definições de vértices: são linhas que começam com a letra v seguida de três coordenadas: x, y e z. Exemplo:

v -0.5 -0.5 0.0

Por enquanto só vou tratar destes dois elementos, que é o suficiente para desenhar um objeto na tela.

O arquivo OBJ que usei durante os testes contem o seguinte:

# Simple Wavefront file
v -0.5 -0.5 0.0
v 0.0 0.5 0.0
v 0.5 -0.5 0.0

Através destas três coordenadas é possível desenhar um triângulo para ilustrar a rotina que foi desenvolvida. Falando nela:

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Drawing;
using System.Reflection;
using Microsoft.WindowsMobile.DirectX;
using Microsoft.WindowsMobile.DirectX.Direct3D;
using System.Windows.Forms;

namespace LoadOBJ
{
    public class ObjLoader
    {
        private Device _device = null;
        private StreamReader _streamReader = null;
        private List<string> _vertices = null;
        private List<string> _faces = null;

        /// <summary>
        /// Constructor
        /// </summary>        
        public ObjLoader(Device device, string file)
        {
            // Set the device
            _device = device;

            // Create the stream reader
            Stream stream =
                Assembly.GetExecutingAssembly().
                    GetManifestResourceStream(file);
            _streamReader = new StreamReader(stream);

            // Load the data to fill the lists and
            // another informations about the model
            LoadData();
        }

        /// <summary>
        /// Load the data to fill the lists and another
        /// informations about the model
        /// </summary>
        /// <remarks>
        /// Just will load those lists that are null
        /// to avoid overhead
        /// </remarks>
        private void LoadData()
        {
            bool loadVertices = (_vertices == null);
            if (loadVertices)
                _vertices = new List<string>();

            bool loadFaces = (_faces == null);
            if (loadFaces)
                _faces = new List<string>();

            if (loadVertices || loadFaces)
            {
                string line;

                while
                ((line = _streamReader.ReadLine()) != null)
                {
                    // Ignore comments
                    if (line.StartsWith("#"))
                        continue;

                    // Vertex
                    if (loadVertices && line.StartsWith("v"))
                        _vertices.Add(line);

                    // Face
                    else if (loadFaces && line.StartsWith("f"))
                        _faces.Add(line);
                }
            }
        }

        /// <summary>
        /// Load the model
        /// </summary>
        public void Load(out VertexBuffer vertexBuffer)
        {
            vertexBuffer = GetVertexBuffer();
        }        

        /// <summary>
        /// Return a Vertex Buffer
        /// </summary>
        private VertexBuffer GetVertexBuffer()
        {
            VertexBuffer result =
                new VertexBuffer(
                    typeof(CustomVertex.PositionColored),
                    _vertices.Count,
                    _device,
                    Usage.None,
                    CustomVertex.PositionColored.Format,
                    Pool.SystemMemory);

            result.Created +=
                new EventHandler(OnVertexBufferCreate);
            OnVertexBufferCreate(result, null);

            return result;
        }

        /// <summary>
        /// Handle that is executed when the
        /// vertex buffer is created
        /// </summary>
        private void OnVertexBufferCreate(object sender, EventArgs e)
        {
            VertexBuffer buffer = (VertexBuffer)sender;
            buffer.SetData(GetVerticesArray(), 0, LockFlags.None);
        }

        /// <summary>
        /// Return an array of vertices
        /// </summary>
        private CustomVertex.PositionColored[] GetVerticesArray()
        {
            CustomVertex.PositionColored[] result =
                new CustomVertex.PositionColored[_vertices.Count];
            for (int i = 0; i < _vertices.Count; i++)
            {
                float x, y, z;
                LoadXYZ(_vertices[i], out x, out y, out z);

                result[i] =
                    new CustomVertex.PositionColored(
                        x,
                        y,
                        z,
                        Color.Black.ToArgb());
            }

            return result;
        }

        /// <summary>
        /// Load x, y and z from line into the params
        /// </summary>        
        private void LoadXYZ(string line,
            out float x, out float y, out float z)
        {
            string[] text = Regex.Split(line, " ");

            // text[0] = v; text[1] = x; text[2] = y; text[3] = z
            x = float.Parse(text[1]);
            y = float.Parse(text[2]);
            z = float.Parse(text[3]);
        }
    }
}

Para usá-la:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.WindowsMobile.DirectX;
using Microsoft.WindowsMobile.DirectX.Direct3D;

namespace LoadOBJ
{
    public partial class Form1 : Form
    {
        private Device _device;
        private VertexBuffer _vertexBuffer;

        static void Main()
        {
            Form1 form = new Form1();
            form.InitializeGraphics();
            try
            {
                Application.Run(form);
            }
            catch (Exception ex)
            {
                MessageBox.Show("
                    Erro ao iniciar a app: " + ex.Message);
            }
        }

        private void InitializeGraphics()
        {
            try
            {
                PresentParameters presentParams =
                    new PresentParameters();
                presentParams.Windowed = true;
                presentParams.SwapEffect = SwapEffect.Discard;

                // Device
                _device = new Device(
                    0,
                    DeviceType.Default,
                    this,
                    CreateFlags.None,
                    presentParams);
                _device.RenderState.CullMode = Cull.None;
                _device.RenderState.Lighting = false;

                // Load the model
                ObjLoader obj = new ObjLoader(
                    _device,
                    "LoadOBJ.Ex1.obj");
                obj.Load(out _vertexBuffer);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Erro na inicialização: " +
                    ex.Message);
            }
        }

        public Form1()
        {

        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            //
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            // Render
            Render();

            // Invalidating the window will cause it to
            // be redrawn in the future
            Invalidate();
        }

        /// <summary>
        /// Render the data
        /// </summary>
        private void Render()
        {
            // Clear the device
            _device.Clear(ClearFlags.Target, Color.White, 1.0f, 0);

            // Begin the scene
            _device.BeginScene();

            // Set stream buffer
            _device.SetStreamSource(0, _vertexBuffer, 0);
            _device.DrawPrimitives(
                PrimitiveType.TriangleList,
                0, 1);

            // End the scene and present
            _device.EndScene();
            _device.Present();
        }
    }
}

Não gosto de colar todo o código fonte num post, pois acaba deixando ele muito extenso e chato para ler. No entanto, é com base neste novo protótipo que vou escrever os próximos posts.

Bem rapidamente, o que isso tudo faz?

Muita coisa é exatamente igual ao que eu já postei há alguns dias. A única alteração é de onde virá a informação a ser desenhada.

A classe ObjLoader possui um constructor e um método chamado Load(). A partir deles é possível carregar um modelo especificado através de vértices. No código acima, comecei alguma implementação para faces, porém está incompleta, pois para utilizá-la vou precisar de um IndexBuffer também.

O resultado final é o mesmo apresentado há dias, só que desta vez usando o Windows Mobile 6.0 SDK (com o 5.0 dá pra fazer extamente a mesma coisa sem alterar o código acima) e um arquivo OBJ. Veja:

A definição de um arquivo OBJ pode ser encontrada aqui, aqui ou aqui.

É isso ae! Até +.

3 comentários - clique aqui para fazer o seu

MD3DM: Como desenhar um quadrado usando a classe Mesh

Publicado por Marcos Dell Antonio em 27/02/2007 | .NET, Direct3D, Mobilidade

Em todos os posts apresentados até agora (1, 2, 3) utilizei um array de vértices para desenhar algo na tela.

Neste post vou mostrar como desenhar um objeto pré-definido pelo Managed Direct3D Mobile: a caixa (ou quadrado, como diz o título do tópico. Em inglês é Box, mas interprete da forma que desejar).

Para este exemplo não será necessário nenhum tipo de buffer de vértices, somente um objeto Mesh.

A única alteração que deve ser feita tomando como base os exemplos anteriores, é no método Render():

 1 private void Render()
 2 {
 3     _device.Clear(ClearFlags.Target,
 4                   Color.Bisque,
 5                   1.0f,
 6                   0);
 7 
 8     _device.BeginScene();
 9 
10     _mesh = Mesh.Box(_device, 0.5f, 0.5f, 0);
11 
12     _device.Transform.World =
13         Matrix.Translation(0, 0, 0);
14     _mesh.DrawSubset(0);
15 
16     _device.Transform.World =
17         Matrix.Translation(-0.75f, 0, 0);
18     _mesh.DrawSubset(0);
19 
20     _device.Transform.World =
21         Matrix.Translation(0.75f, 0, 0);
22     _mesh.DrawSubset(0);
23 
24     _device.Transform.World =
25         Matrix.Translation(0, 0.75f, 0);
26     _mesh.DrawSubset(0);
27 
28     _device.Transform.World =
29         Matrix.Translation(0, -0.75f, 0);
30     _mesh.DrawSubset(0);
31 
32     _device.Transform.World =
33         Matrix.Translation(-0.75f, 0.75f, 0);
34     _mesh.DrawSubset(0);
35 
36     _device.Transform.World =
37         Matrix.Translation(-0.75f, -0.75f, 0);
38     _mesh.DrawSubset(0);
39 
40     _device.Transform.World =
41         Matrix.Translation(0.75f, 0.75f, 0);
42     _mesh.DrawSubset(0);
43 
44     _device.Transform.World =
45         Matrix.Translation(0.75f, -0.75f, 0);
46 
47     _mesh.DrawSubset(0);
48 
49     _device.EndScene();
50     _device.Present();
51 }

Mas o que isso tudo faz? Pois bem, vamos por partes. Além de limpar o device e iniciar o desenho da cena, é criado um Mesh (modelo). Este mesh representa o quadrado que será desenhado na tela.

Já que eu quero desenhar nove quadrados, foi preciso fazer uma translação sobre o eixo X, desenhar o primeiro quadrado, transladar novamente, desenhar o próximo quadrado e assim sucessivamente. Repare que em determinadas situações foi preciso transladar o eixo Y também, pois são três quadrados por linha.

O resultado final é este:

É isso ae. Nada muito difícil ou demorado. Daqui pra frente o bicho pega, pois vou começar a estudar Index Buffer e a especificação dos arquivos OBJ.

Até +.

Seja o primeiro a comentar este post

Anúncios

Anúncio provido pelo BuscaPé