quinta-feira, 26 de março de 2015

Consumir um serviço web com C#

Neste artigo vamos aprender como consumir um serviço web com uma aplicação desktop criada em C#.

Um serviço web (web service) é uma API disponível através da Web que pode ser utilizada desde que se respeite as condições de uso. Em alguns casos os web services são gratuítos e não requerem nenhum registo ou credênciais, no entanto, existem outros serviços que depende de um registo e até de um pagamento.

Para demonstrar o conceito vamos utilizar um serviço oferecido pelo Banco Central Europeu disponível no seguinte link http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml.
Este serviço devolve as cotações diárias de moedas em função do valor do euro. Os dados são codificados em formato XML.

O pedido dos dados não pode acontecer na thread principal do programa uma vez que isso provocaria a paragem da interface. Assim é necessário recorrer a um pedido assincrono numa thread separada da thread responsável pela atualização a interface gráfica (GUI).

O código que vai permitir consumir os dados é o seguinte:

async void downloadXML()
        {
            string url = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml";
            WebRequest pedido = WebRequest.Create(url);
            WebResponse resposta = await pedido.GetResponseAsync();
            XDocument documento = XDocument.Load(resposta.GetResponseStream());
            textBox1.Text = documento.ToString();

            //preencher a listview
            XmlDocument xml = new XmlDocument();
            xml.LoadXml(documento.ToString());
            XmlNodeList lista = xml.GetElementsByTagName("Cube");
            foreach (XmlNode no in lista)
            {
                if (no.Attributes.Count > 1)
                {
                    ListViewItem novo = new ListViewItem(no.Attributes[0].InnerXml);
                    novo.SubItems.Add(no.Attributes[1].InnerXml);
                    listView1.Items.Add(novo);
                }
            }
        }


Trata-se de uma função assincrona que é chamada a partir de um clique num botão.
Como se pode verificar na linha de definição da função é utilizada a palavra reservada async.
Esta foi introduzida no Visual Studio 2012 e permite simplificar o trabalho de criar uma função assincrona deixando a parte mais dificil para o compilador (Ler mais sobre o assunto).
A execução corre normalmente até encontrar a linha com o termo await onde o controlo da execução é devolvido para a linha que chamou a função assincrona, não bloqueando a interface, enquanto espera que o processamento dessa linha termine para continuar até ao final da função.

O resto da função permite manipular os dados recebidos em formato XML.

Projeto

sexta-feira, 9 de janeiro de 2015

Arduino através de PHP

Neste post vamos criar um projeto que vai implicar utilizar três linguagens de programação diferentes e tudo para permitir comunicar com um Arduino através do browser.

O objetivo é criar uma página Web que permitirá enviar e receber dados de um Arduino ligado a um servidor Web.

No Arduino implementamos um protocolo muito simples de comunicação que faz a interação com os sensores e comunica com o servidor Web.

O servidor Web é um computador com Windows a correr o Apache e a linguagem de programação utilizada para a criação das páginas será o PHP.

Para permitir a comunicação do PHP com a porta USB do Arduino será desenvolvimento um pequeno programa em C#, modo Consola, que será chamado a partir do PHP com alguns parâmetros e que posteriormente fará a ligação ao Arduino.

Apesar de existirem livrarias de funções em PHP para utilizar a porta série não consegui fazer com que funcionassem em Windows.



No Arduino vamos ter um servo ligado ao pino 9, um LED ligado ao pino 12 e um LDR no pino A0. O pino 9 e o pino 12 são digitais, o pino A0 é um pino analógico. Os pinos analógicos estão ligados a um ADC (Analog Digital Convert) que faz a conversão entre a tensão elétrica recebida e um valor entre 0 e 1024. O valor máximo, por defeito, é de 5 volts que corresponde a 1024.
O pino 9 é um pino com função PWM (Pulse Width Modulation) que permite simular o envio de valores entre 0 (LOW) e 1 (HIGH), é esta função que permite fazer efeito de fade com LEDs.

Neste caso vamos ter um protocolo muito simples que respeita as seguintes regras:
- Se o valor recebido pelo Arduino estiver entre 0 e 180, inclusive, este corresponderá ao ângulo de posicionamento do servo.
- Se o valor recebido é 181 então o LED liga ou desliga, de acordo com o estado anterior.
- Se o valor recebido é 182 então o Arduino envia o valor do LDR.

Assim o código do Arduino fica:

 #include <Servo.h>

Servo myservo; 
int pos = 0;   
int led=0;
int ledPort=12;
int ldrPin=A0;
byte nivelLuz=0;

void setup()
{
  myservo.attach(9); 
  Serial.begin(9600);
  pinMode(ledPort,OUTPUT);
}

void loop()
{
    byte valor;
    if(Serial.available()){
    valor=Serial.read();
    if(valor>=0 && valor<=180) myservo.write(valor);
    if(valor==181){
      if(led==0){
        digitalWrite(ledPort,HIGH);
        led=1; 
      }else{
        digitalWrite(ledPort,LOW);
        led=0;
      }
    }
    if(valor==182){
      delay(150);
      nivelLuz=analogRead(ldrPin);
      Serial.println(nivelLuz);
    }
  }
}

A página Web vai conter um formulário simples com três botões para controlar cada um dos dispositivos presentes no Arduino (LED, LDR e o servo).
Cada botão invoca o programa desenvolvido em C# com o comando e a porta séria a utilizar.

O código PHP fica:
<html>
<body>
    <form method="POST">
        <input type="text" name="angulo">
        <input type="submit" name="bt" value="Servo">
        <input type="submit" name="bt_luz" value="Led">
        <input type="submit" name="bt_ldr" value="Luz">
    </form>
</body>
</html>
<?php
    ini_set("display_errors",1);
    if(!empty($_POST)){
        if(!empty($_POST["bt"])){
            echo "POST servo<br>";
            if(empty($_POST["angulo"])){
                echo "Preencha o angulo<br>";
                return;
            }
            $angulo=$_POST["angulo"];
            $comando="console_servo.exe COM18 ".$angulo;
            $resultado=exec($comando);
        }
        if(!empty($_POST["bt_luz"])){
            echo "POST led<br>";
            $resultado=exec("console_servo.exe COM18 181");
        }
        if(!empty($_POST["bt_ldr"])){
            echo "POST ldr<br>";
            $resultado=exec("console_servo.exe COM18 182 er");
            $resultado=(int)$resultado;
        }
        if($resultado<0) $resultado="";
        echo $resultado."<br>";
    }
?>

O programa em C# inclui uma classe que implementa a comunicação com a porta série.
O código do programa começa por verificar os parâmetros que são passados e em função deles utilizar um objeto criado com base na classe de comunicação para enviar e receber os dados do Arduino.

namespace console_servo
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 1)
            {
                string strler = args[0];
                Comunica portaler = new Comunica(strler);
                int valorlido=portaler.lerLinha();
                Console.Write(valorlido.ToString());
                return;
            }
            if (args.Length == 3)
            {
                string strenvia = args[0];
                byte valorenviar;
                if (byte.TryParse(args[1], out valorenviar) == false)
                {
                    Console.Write("Usage: servo porta valor er");
                    return;
                }
                Comunica portalerenviar = new Comunica(strenvia);
                portalerenviar.envia(valorenviar);
                Thread.Sleep(150);
                int valordevolver = portalerenviar.lerLinha();
                Console.Write(valordevolver);
                return;
            }
            if (args.Length != 2)
            {
                Console.Write("Usage: servo porta valor");
                return;
            }
            byte valor = 0;
            if (byte.TryParse(args[1], out valor)==false)
            {
                Console.Write("Usage: servo porta valor");
                return;
            }
            string str=args[0];
            Comunica porta = new Comunica(str);
            porta.envia(valor);
            Console.Write("ok");
        }

    }
    class Comunica
    {
        string porta;
        SerialPort sp;
        string buffer;

        public Comunica(string porta)
        {
            this.porta = porta;
            sp = new SerialPort(porta);
            sp.Open();
        }
        ~Comunica()
        {
            if (sp.IsOpen) sp.Close();
            sp.Dispose();
        }
        void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            buffer = sp.ReadExisting();
        }
        public void envia(byte x)
        {
            byte[] valores = new byte[2];
            valores[0] = x;
            sp.Write(valores, 0, 1);
        }
        public byte ler()
        {
            byte[] valores=new byte[2];
           
            sp.Read(valores, 0, 1);
            return valores[0];
        }
        public int lerLinha()
        {
            string linha =sp.ReadLine();
            int valor = 0;
            if(int.TryParse(linha, out valor)==false)
                return -1;
            else
                return valor;
        }
    }
}

O projeto completo pode ser descarregado aqui.

segunda-feira, 31 de março de 2014

Numeração Romana

Vamos hoje fazer um pequeno programa que converte os números da numeração árabe para a numeração romana.

Segundo a Wikipédia "O sistema de numeração romana (ou números romanos) desenvolveu-se na Roma Antiga e utilizou-se em todo o seu Império."

Uma vez que os romanos não conheciam o 0 (zero) vamos limitar o nosso programa a valores entre 1 e 3999.



Para começar definimos dois vetores:

Dim valores As Integer() = New Integer() {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
Dim romanos As String() = New String() {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}

O primeiro tem os valores inteiros que correspondem a determinadas letras que se encontram no segundo.
O segundo vetor contém as letras que são combinadas, ou não, e que permitem representar os valores na numeração romana.

Para fazermos a conversão vamos criar uma função que recebe o valor inteiro árabe e devolve a string com o valor em numeração romana.

Public Function paraRomanos(numero As Integer) As String

Nesta função utilizamos um ciclo que vai percorrer os valores do primeiro vetor e enquanto o valor a converter for maior que o valor existente no vetor subtrai esse valor do valor original, ou seja, começa em 1000 e se o número é superior a 1000 subtrai este valor do original até restar um valor menor que 1000 para passar à segunda posição do vetor.
De cada vez que um valor é subtraído é adicionada a letra ou letras correspondentes a esse valor a uma string onde a numeração romana está a ser construída.

For i = 0 To 12
            Do While numero >= valores(i)
                numero -= valores(i)
                resultado.Append(romanos(i))
            Loop
Next

No final a função devolve a string com a numeração romana.

Return resultado.ToString()

A função completa fica assim:
    Public Function paraRomanos(numero As Integer) As String
        Dim valores As Integer() = New Integer() {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
        Dim romanos As String() = New String() {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}
        Dim i As Integer
        Dim resultado As New StringBuilder()

        If numero > 3999 Or numero < 1 Then
            MessageBox.Show("Erro! Deve introduzir valores entre 1 e 3999")
            Return ""
        End If
        For i = 0 To 12
            Do While numero >= valores(i)
                numero -= valores(i)
                resultado.Append(romanos(i))
            Loop
        Next
        Return resultado.ToString()
    End Function

O projeto em Visual Basic pode ser retirado aqui.

segunda-feira, 10 de fevereiro de 2014

OpenGL Parte II

No artigo anterior criámos a base do nosso pequeno jogo que agora vamos expandir. O objetivo de hoje é ter uma função que lê os modelos exportados do Blender, ou de outro programa de modelação 3D, em formato Wavefront (extensão OBJ) e apresentar esse modelo no controlo SharpGL.

Neste nosso pequeno projeto os objetos têm de ser sempre formados por triângulos por isso quando exportamos no Blender temos de garantir que todas as faces são "trianguladas".

Fica aqui uma imagem com as opções de exportação a utilizar:


O ficheiro com o modelo tem linhas de vários tipos:
Vértices:
P.Ex: v 0.940139 -0.495046 0.940139
Coordenadas das texturas:
P.Ex: vt 0.000000 0.424059
Normais:
P.Ex: vn -0.000000 -1.000000 -0.000001
Faces ou triângulos:
P.Ex: f 1/1/1 2/2/1 3/3/1

Basicamente com estes três tipos de linhas e com uma imagem podemos moldar qualquer objeto.

Os vértices, como é fácil de perceber representam os pontos de cada triângulo (x,y,z), as coordenadas das texturas permitem aplicar a textura ao modelo corretamente (U,V), as normais vão permitir ao OpenGL calcular com melhor qualidade a iluminação do nosso jogo e por fim as faces que indicam quais os vértices que são unidos para formar os triângulos.

Agora criamos uma classe que vai conter as seguintes listas:

  • Lista de pontos - são os vértices do nosso modelo
  • Lista de triângulos - baseada na classe triangulo criada no artigo anterior
  • Lista de pontos 2d - classe igual à dos pontos mas só com 2 coordenadas uma vez que as coordenadas das texturas são 2D
  • Lista de normais - mais uma lista de pontos 3D


Para além destas listas precisamos de uma textura, que não é mais do que uma variável do tipo Texture, para a qual necessitamos de incluir o namespace SharpGL.SceneGraph.Assets.

Comecemos por criar a classe Ponto2D com o seguinte código:
class Ponto2D
    {
        float x, y;

        public Ponto2D()
        {
            x = 0;
            y = 0;
        }
        public Ponto2D(float px, float py)
        {
            x = px;
            y = py;
        }
        public void setX(float x)
        {
            this.x = x;
        }
        public void setY(float y)
        {
            this.y = y;
        }
        public float getX()
        {
            return x;
        }
        public float getY()
        {
            return y;
        }
        public void moveXY(float _x, float _y)
        {
            x += _x;
            y += _y;
        }
        public void moveXY(Ponto a)
        {
            x += a.getX();
            y += a.getY();
        }
    }

Agora a classe Modelo já descrita:
    class Modelo
    {
        List<Ponto> lPontos = new List<Ponto>();
        List<Triangulo> lTriangulos = new List<Triangulo>();
        List<Ponto2D> lCoordText = new List<Ponto2D>();
        List<Ponto> lNormais = new List<Ponto>();
        Texture textura;
        
    }

De seguida vamos acrescentar alguns métodos, começando pela função que vai permitir carregar a textura:
        public void CarregaTextura(OpenGL gl, string nome)
        {
            if (File.Exists(nome) == false) return;
            textura = new Texture();
            textura.Create(gl, nome);
        }

A próxima função vai carregar o modelo do ficheiro e preencher as listas, mas primeiro algumas alterações à classe triângulo, criada no artigo anterior. Temos de adicionar as coordenadas das texturas, uma coordenada para cada vértice do triângulo, adicionamos as normais, mais uma vez uma para cada vértice, e por fim uma variável que nos permite saber se temos normais definidas ou não.
Assim a classe triângulo fica com o seguinte aspeto:
    class Triangulo : Objeto
    {
        Ponto p1, p2, p3;
        Ponto2D tx1, tx2, tx3;
        Ponto n1, n2, n3;
        bool hasNormals = false;

Como nem sempre os modelos são exportados com normais definimos um construtor da classe que recebe as coordenadas das texturas e adicionamos uma função para definir as normais independente do construtor.
        public Triangulo(Ponto p1, Ponto p2, Ponto p3, Ponto2D tx1, Ponto2D tx2, Ponto2D tx3)
        {
            this.p1 = p1;
            this.p2 = p2;
            this.p3 = p3;
            this.tx1 = tx1;
            this.tx2 = tx2;
            this.tx3 = tx3;
            n1 = new Ponto(0, 0, 0);
            n2 = new Ponto(0, 0, 0);
            n3 = new Ponto(0, 0, 0);
            origem = new Ponto(0, 0, 0);
            rotacao = new Ponto(0, 0, 0);
            n1 = new Ponto(0, 0, 1);
            n2 = new Ponto(0, 0, 1);
            n3 = new Ponto(0, 0, 1);
            largura = 1;
            hasNormals = false;
        }
        public void defineNormais(Ponto n1, Ponto n2, Ponto n3)
        {
            this.n1 = n1;
            this.n2 = n2;
            this.n3 = n3;
            hasNormals = true;
        }

Agora já podemos tratar da classe Modelo e da função que carrega o modelo:
        public Modelo(string nome)
        {
            //ler o ficheiro
            StreamReader ficheiro = new StreamReader(nome);

            string linha;
            string[] campos;
            float x, y, z;
            while (ficheiro.Peek() != -1)
            {
                linha = "";
                linha = ficheiro.ReadLine();
                linha = linha.Trim();
                if (linha != null && linha[0] != '#')
                {
                    linha = linha.Replace(".", ",");   //troca . por ,
                    campos = linha.Split(' ');  //separa nos espaços
                    //vertices
                    if (linha[0] == 'v' && linha[1] != 't' && linha[1] != 'n')
                    {
                        x = float.Parse(campos[1]);
                        y = float.Parse(campos[2]);
                        z = float.Parse(campos[3]);
                        lPontos.Add(new Ponto(x, y, z));
                    }

                    //coord texturas
                    if (linha[0] == 'v' && linha[1] == 't')
                    {
                        float t1 = float.Parse(campos[1]);
                        float t2 = float.Parse(campos[2]);
                        lCoordText.Add(new Ponto2D(t1, t2));
                    }
                    //normais
                    if (linha[0] == 'v' && linha[1] == 'n')
                    {
                        float t1 = float.Parse(campos[1]);
                        float t2 = float.Parse(campos[2]);
                        float t3 = float.Parse(campos[3]);
                        lNormais.Add(new Ponto(t1, t2, t3));
                    }
                    //faces
                    if (linha[0] == 'f')
                    {
                        if (lCoordText.Count > 0)
                        {
                            string[] temp = campos[1].Split('/');
                            int t1 = int.Parse(temp[0]) - 1;
                            int t4 = int.Parse(temp[1]) - 1;
                            int n1 = 0;
                            if (lNormais.Count > 0)
                                n1 = int.Parse(temp[2]) - 1;

                            temp = campos[2].Split('/');
                            int t2 = int.Parse(temp[0]) - 1;
                            int t5 = int.Parse(temp[1]) - 1;
                            int n2 = 0;
                            if (lNormais.Count > 0)
                                n2 = int.Parse(temp[2]) - 1;

                            temp = campos[3].Split('/');
                            int t3 = int.Parse(temp[0]) - 1;
                            int t6 = int.Parse(temp[1]) - 1;
                            int n3 = 0;
                            if (lNormais.Count > 0)
                                n3 = int.Parse(temp[2]) - 1;
                            lTriangulos.Add(new Triangulo(lPontos[t1], lPontos[t2], lPontos[t3], lCoordText[t4], lCoordText[t5], lCoordText[t6]));
                            if (lNormais.Count > 0)
                                lTriangulos[lTriangulos.Count - 1].defineNormais(lNormais[n1], lNormais[n2], lNormais[n3]);
                        }
                        else if (lNormais.Count > 0)
                        {
                            string[] temp = campos[1].Split('/');
                            int t1 = int.Parse(temp[0]) - 1;
                            int t4 = int.Parse(temp[2]) - 1;

                            temp = campos[2].Split('/');
                            int t2 = int.Parse(temp[0]) - 1;
                            int t5 = int.Parse(temp[2]) - 1;

                            temp = campos[3].Split('/');
                            int t3 = int.Parse(temp[0]) - 1;
                            int t6 = int.Parse(temp[2]) - 1;
                            lTriangulos.Add(new Triangulo(lPontos[t1], lPontos[t2], lPontos[t3]));
                            if (lNormais.Count > 0)
                                lTriangulos[lTriangulos.Count - 1].defineNormais(lNormais[t1], lNormais[t2], lNormais[t3]);
                        }
                        else
                        {
                            int t1 = int.Parse(campos[1]) - 1;
                            int t2 = int.Parse(campos[2]) - 1;
                            int t3 = int.Parse(campos[3]) - 1;
                            lTriangulos.Add(new Triangulo(lPontos[t1], lPontos[t2], lPontos[t3]));
                        }
                    }
                }
            }

            ficheiro.Close();
            //textura
            textura = null;
        }

Para testar estas funções vamos adicionar um modelo e uma textura ao nosso projeto, de preferência dentro de pastas criadas especificamente para esse efeito.
Com esta textura

Vamos ter isto


Para isso precisamos de uma função de desenhar nova na classe triângulo, pois a versão anterior só desenhava as arestas dos triângulos, o código passa a ser:
        public void desenhar(OpenGL gl, Texture textura)
        {
            gl.PushMatrix();
            gl.Translate(origem.getX(), origem.getY(), origem.getZ());
            gl.Rotate(rotacao.getX(), rotacao.getY(), rotacao.getZ());
            gl.Enable(OpenGL.GL_TEXTURE_2D);
            textura.Bind(gl);
            gl.Enable(OpenGL.GL_REPEAT);
            gl.Enable(OpenGL.GL_LIGHTING);
            gl.Begin(OpenGL.GL_POLYGON);
            gl.Enable(OpenGL.GL_COLOR_MATERIAL);

            gl.TexCoord(tx1.getX(), tx1.getY());
            if (hasNormals) gl.Normal(n1.getX(), n1.getY(), n1.getZ());
            gl.Vertex(p1.getX(), p1.getY(), p1.getZ());
            gl.TexCoord(tx2.getX(), tx2.getY());
            if (hasNormals) gl.Normal(n2.getX(), n2.getY(), n2.getZ());
            gl.Vertex(p2.getX(), p2.getY(), p2.getZ());
            gl.TexCoord(tx3.getX(), tx3.getY());
            if (hasNormals) gl.Normal(n3.getX(), n3.getY(), n3.getZ());
            gl.Vertex(p3.getX(), p3.getY(), p3.getZ());
            gl.End();
            gl.Disable(OpenGL.GL_TEXTURE_2D);
            gl.Disable(OpenGL.GL_LIGHTING);
            gl.Disable(OpenGL.GL_REPEAT);
            gl.Disable(OpenGL.GL_AUTO_NORMAL);
            gl.Disable(OpenGL.GL_COLOR_MATERIAL);
            gl.PopMatrix();
        }

Com este código cada triângulo vai ter normais, textura e tudo que é necessário para apresentar os modelos criados no Blender.

Por fim só falta a função desenhar da classe modelo, que vai chamar a função desenhar de cada triângulo que compõe o modelo 3D.
        public  void desenhar(SharpGL.OpenGL gl)
        {

            gl.PushMatrix();
            gl.MatrixMode(OpenGL.GL_MODELVIEW);
            gl.LoadIdentity();
            gl.Enable(OpenGL.GL_LIGHTING);
            gl.Translate(origem.getX(), origem.getY(), origem.getZ());
            gl.Rotate(rotacao.getX(), rotacao.getY(), rotacao.getZ());

            foreach (Triangulo triangulo in lTriangulos)
                triangulo.desenhar(gl, textura);

            gl.Disable(OpenGL.GL_LIGHTING);
            gl.PopMatrix();

        }

Como é possível verificar pelo código apresentado ativámos a iluminação no OpenGL por isso temos de adicionar pelo menos uma luz, o seguinte código deve ser adicionado ao formulário principal na função openGLControl_Resized:

            float[] cor = {1,1,1 };
            gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, cor);
            float[] posicao = { 0, 5, 0 };
            gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, posicao);
            gl.Enable(OpenGL.GL_LIGHT0);

As luzes no OpenGL podem ter muitas mais características, aqui só definimos o mínimo necessário, ou seja, a posição e a cor.

Agora que já adicionámos um modelo e uma textura aos recursos do nosso projeto, não esquecer de definir que devem ser copiados para a pasta do projeto, já podemos criar um objeto da classe modelo:

Modelo modelo = new Modelo(@"Modelos\nave.obj");

De seguida carregamos a textura:

modelo.CarregaTextura(gl, @"Texturas\nave.png");

E por fim desenhamos:

modelo.desenhar(gl);

No próximo capítulo desta saga vamos inserir código para manipular o nosso objeto fazendo-o rodar, mover e tudo o mais.

Faça o download do projeto aqui.

sexta-feira, 31 de janeiro de 2014

OpenGL

Neste artigo vou apresentar um método simples de como criar aplicações 3D utilizando a linguagem de programação C# através de um controlo que encapsula as funções OpenGL para a plataforma .NET.
O controlo a utilizar responde pelo nome de SharpGL.

A versão atual do controlo inclui modelos de aplicações que podem ser instalados diretamente na versão 2010 do Visual C#, incluindo a versão Express.
Depois de instalado o SharpGL basta escolher o tipo de aplicação pretendida: Windows Forms ou WPF.


A partir daqui podemos gravar o projeto criado e abri-lo na versão 2012 do Visual Studio.

De seguida é possível exportar o projeto a partir do Visual Studio 2012 como um Template para que se possam criar mais projetos sem ser necessário voltar ao Visual C# 2010.


Esta opção abre um Wizard que passo a passo permite gerar o Template na Versão 2012 do Visual Studio.

O modelo agora criado apresenta código que permite gerar uma pirâmide 3D a rodar com cores diferentes entre os vertices.


O controlo gera automaticamente três métodos/funções:

  • openGLControl_OpenGLDraw
  • openGLControl_OpenGLInitialized
  • openGLControl_Resized


A função openGLControl_OpenGLInitialized é chamada uma vez quando o controlo é inicializado em conjunto com a janela da aplicação. Aqui devemos colocar o código que necessita de ser executado uma só vez como por exemplo a definição de valores iniciais de objetos a utilizar no programa.

A função openGLControl_Resized é chamada sempre que o controlo é redimensionado, por força da alteração do tamanho da janela ou da alteração da resolução do ecrã.

Por fim a função openGLControl_OpenGLDraw controla o evento de redesenhar o controlo, portanto a cena tridimensional. Será nesta função que vamos colocar o código que faz aparecer os objetos do nosso programa.

OpenGL

Hoje em dia os programas 3D utilizam um método de desenho baseado em Shaders. Neste artigo vamos abordar o chamado método imediato de desenho 3D, sem shaders, por ser mais simples de implementar.

Para começar vamos definir algumas classes que mais tarde vão facilitar o nosso trabalho, especialmente quando estivermos a conceber uma aplicação já com alguma dimensão.

Classe Ponto - esta classe vai permitir definir um ponto no espaço 3D. Define três floats que representam as coordenadas dos três eixos (X,Y,Z).
Classe Objeto - esta classe representa um objeto 3D, nomeadamente as caraterísticas base do objeto, a origem, a rotação, a cor, a largura (para desenhar em wireframe) e a transparência (alpha).
Classe Triangulo - esta classe, tal como o nome indica, representa um triângulo, derivando a partir da classe objeto só temos de definir os vertices do triangulo (três pontos).

Com estas três classes já podemos criar algumas imagens interessantes.

Desenhar

O processo de desenhar com o OpenGL implica diversas operações matemáticas, as principais implicam passar as coordenadas que definem o objeto para as coordenadas que o colocam numa determinada posição no espaço do mundo 3D. Estas operações são realizadas através de cálculos matriciais. Para que os cálculos realizados para colocar um modelo no espaço não influenciem os restantes o estado deve ser guardado antes de iniciar o processo de desenho do objeto. Para isso deve-se guardar a matriz do espaço 3D antes de desenhar e no final do processo tornar a repor a matriz original. As funçãos PushMatrix e PopMatrix são utilizadas para essas operações, devendo ocorrer entre elas as operações de desenho.

gl.PushMatrix();
//desenhar
gl.PopMatrix();

O objeto gl é uma referência para o controlo colocado no formulário, esta referência deve ser passada para todas as funções de desenho das classes referidas.
Assim a função de desenho em wireframe (esqueleto) do triângulo fica assim:

            //guardar o estado da matriz
            gl.PushMatrix();
            //desenhar
            gl.Translate(origem.getX(), origem.getY(), origem.getZ());
            gl.Rotate(rotacao.getX(), rotacao.getY(), rotacao.getZ());
            gl.Color(cor.getX(), cor.getY(), cor.getZ());
            gl.LineWidth(largura);
            //vertice 1 ao 2
            gl.Begin(OpenGL.GL_LINES);
                gl.Vertex(p1.getX(), p1.getY(), p1.getZ());
                gl.Vertex(p2.getX(), p2.getY(), p2.getZ());
            gl.End();
            //vertice 2 ao 3
            gl.Begin(OpenGL.GL_LINES);
                gl.Vertex(p2.getX(), p2.getY(), p2.getZ());
                gl.Vertex(p3.getX(), p3.getY(), p3.getZ());
            gl.End();
           //vertice 3 ao 1
            gl.Begin(OpenGL.GL_LINES);
                gl.Vertex(p3.getX(), p3.getY(), p3.getZ());
                gl.Vertex(p1.getX(), p1.getY(), p1.getZ());
            gl.End();
            //repor a matriz
            gl.PopMatrix();

Primeiro fazemos uma translação, que na prática significa mover o objeto para a sua origem, depois uma rotação, de seguida definimos a cor de desenho e a largura da linha a desenhar.
A função de desenho utilizada no OpenGL necessita de dois pontos, o inicial e final que no caso do nosso triângulo são os vértices.

Para que o código funcione a classe Ponto deve implementar três funções públicas que devolvem as coordenadas de cada eixo (getX, getY, getZ).

O resultado ainda não é espetacular mas é um bom começo:

No próximo artigo vamos passar para a fase em que o triângulo será desenhado com uma textura, para além de uma função para importar objetos 3D desenhados a partir do Blender.

O projeto pode ser picado aqui.

sábado, 14 de setembro de 2013

Let there be light - vamos fazer uma lanterna

Neste post vamos fazer um pequeno programa que permite utilizar o telefone Android como se de uma lanterna se tratasse.

O projeto é muito simples, vamos inserir um botão na interface para ligar e desligar a luz do flash que vamos utilizar para a iluminação.


Primeiro a interface, o código:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/relativeLayout1"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <Button
        android:id="@+id/buttonFlashlight"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:text="Luz" />

</RelativeLayout>

Muito simples, só um botão, optei por inserir o texto diretamente na propriedade do botão uma vez que não vou reutilizar o texto nem pretendo traduzir a interface para outra língua.

Agora as permissões, para podermos ligar e desligar a luz do flash do telefone precisamos de autorização. Para isso adicionamos a seguinte linha ao manifesto da aplicação:

    <uses-permission android:name="android.permission.CAMERA" />

Para além disso devemos indicar que a aplicação requer a camera com a seguinte linha:

 <uses-feature android:name="android.hardware.camera" />

O manifesto completo fica assim:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="edu.pjcferreira.flashlight"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />

    <application
        android:debuggable="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name="edu.pjcferreira.flashlight.FlashLightActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Por fim o código, igualmente simples. Na função onCreate vamos associar um evento click ao botão para ligar e desligar a luz, mas a parte mais difícil não é essa, primeiro temos de verificar se o programa já está em execução e não está a ser retomado, para isso temos uma variável boolean que passa de false para true quando o programa já está em execução.

Caso seja a primeira vez verificamos se temos camera e em caso afirmativo capturamos o dispositivo para o nosso programa.

Dentro da função onClick ligamos e desligamos o flash através da função setFlashMode.

Aqui fica o código completo:

public class FlashLightActivity extends Activity {

//luz ligada
protected boolean isLighOn = false;
//camera
private Camera camera;
//botao da atividade
private Button button;
private boolean repete=false;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if(repete==false){
repete=true;
button = (Button) findViewById(R.id.buttonFlashlight);
 
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
 
Context context = this;
PackageManager pm = context.getPackageManager();
// if device support camera?
if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
Log.e("err", "Dispositivo não tem camera!");
return;
}
camera = Camera.open();
final Parameters p = camera.getParameters();
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if (isLighOn) {
Log.i("info", "luz está desligada!");
p.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(p);
camera.stopPreview();
isLighOn = false;
} else {
Log.i("info", "luz está ligada!");
p.setFlashMode(Parameters.FLASH_MODE_TORCH);
camera.setParameters(p);
camera.startPreview();
isLighOn = true;
}
}
});
}
}
@Override
protected void onStop() {
super.onStop();

if (camera != null) {
camera.release();
}
}
}

Não esquecer a função onStop que liberta a camera quando o programa é fechado.

Para evitar problemas quando o telefone muda de posição a interface fica bloqueada em posição vertical.

O projeto pode ser retirado aqui.
A aplicação pronta a usar está aqui.

Espero que tenha gostado se sim passe pela nossa página web e do facebook.

sexta-feira, 10 de maio de 2013

Unity 3D - Jogo em Split Screen

Neste artigo vou criar um pequeno jogo para dois jogadores humanos em ecrã dividido utilizando o Unity 3D.

Para começar vou mostrar o resultado final.


Na parte de cima da imagem anterior temos a composição da cena e na parte de baixo a imagem que os jogadores vão ver quando jogarem.

Primeiro vamos preparar a cena e depois vamos ao código, assim adicione um plano para servir de chão e depois defina uma textura a aplicar.

Altere o tamanho do plano, nomeadamente a largura nos eixos x e z para acomodar os dois jogadores.

Agora que temos o campo de jogo vamos tratar dos jogadores, para isso adicione uma capsule.

Agora vamos posicionar esta capsula no lado esquerdo do ecrã.

De seguida vamos posicionar a camera que temos na cena atrás da capsula, assim:


Continuamos a preparar o jogador do lado esquerdo alterando a sua cor e definindo um ponto de disparo à sua frente, para isso vamos utilizar um ponto vazio.


O próximo passo é muito importante porque vai permitir organizar o nosso trabalho por isso muita atenção.

Primeiro alteramos o nome dos objetos para os podermos identificar mais facilmente, assim, Capsule passa a graficos, GameObject passa ponto_disparo e Main Camera a Camera_esquerda.

De seguida vamos criar um novo ponto vazio e arrastar para dentro desse ponto a capsula e o ponto de disparo. Agora mudamos o nome do novo ponto vazio para jogador. No final temos algo assim:

Agora a camera, vamos às propriedades e alteramos a largura para 0.5, o campo a alterar é o w (width). Assim ficamos com metade do ecrã preenchido com a imagem desta camera a outra metade vai ser preenchida com a outra.

Então para preencher a outra metade duplicamos a camera e alteramos o nome para camera_direita, de seguida arrastamos para a direita e rodamos a camera para mostrar o jogador a partir do outro lado do plano.
Agora basta alterar a posição da imagem para 0.5, a propriedade é X.


Agora já temos duas cameras vamos criar o código para os jogadores.

Crie um javascript com o nome jogador.js

Primeiro as variáveis, como vamos utilizar o mesmo código para os dois jogadores vamos criar variáveis para configurar as teclas que controlam os jogadores a partir do inspector.

public var tecla_esquerda : String = "z";
public var tecla_direita : String = "x";
public var tecla_dispara : String = "c";

As variáveis para associar ao jogador e a velocidade de movimento:
public var jogador : CharacterController;
public var velocidade : float = 0.5;

E algumas variáveis para o modelo da bola que vamos atirar:

public var modelo_bala : GameObject;

O ponto de disparo:
public var ponto_disparo : GameObject;

A força:
public var forca : float=200.0;
public var forcaTotal : float =0;
public var forcaMax : float= 1000;
public var barraForca : Texture;
public var posBarra : int=0;
private var temp : float;
public var boladas : float = 0;
public var posTexto : int=0;

A cor do nosso jogador, que também vamos atribuir à bola:
public var cor : Color = Color.red;

De seguida apresento a função que atualiza a interface, nós vamos mostrar duas informações ao utilizador: a força com que ele vai atirar a bola (esta força é controlada pelo tempo que fica a carregar na tecla de disparo) e quantas vezes já levou com a bola.

function OnGUI(){
    if(forcaTotal<0) temp = forcaTotal * (-1);
    else temp=forcaTotal;
   
    if(temp>0){
        GUI.DrawTexture(Rect(posBarra,10,temp/forcaMax*100,10),barraForca);
    }

    GUI.TextArea(Rect(posTexto,20,30,20),boladas.ToString());
}

 A função mais importante:
function Update () {

    if (Input.GetKey(tecla_esquerda)) jogador.transform.position.z +=(velocidade*Time.deltaTime);
    if (Input.GetKey(tecla_direita)) jogador.transform.position.z -=(velocidade*Time.deltaTime);
    if (Input.GetKey(tecla_dispara)){
        forcaTotal += (forca*Time.deltaTime);
    }
    if(Input.GetKeyUp(tecla_dispara)){
        var bala : GameObject;
        bala=Instantiate(modelo_bala,ponto_disparo.transform.position,this.transform.rotation);
        bala.gameObject.renderer.material.color = cor;
       
        bala.rigidbody.AddForce(Vector3(forcaTotal,0,0));
        forcaTotal=0;
    }
   
}

Nesta função controlamos a posição do jogador bem como a força a atribuir à bola quando a lançamos.

Agora vamos criar uma função que vai ser chamada sempre que a bola acertar no jogador:

function bolada(x : float){

    boladas += x;
   
}

Passamos à bola, para isso adicionamos uma esfera e associamos-lhe um rigidbody.

Agora criamos outro ficheiro javascript com o nome bola.js, neste colocamos o seguinte código:

var tempo : float =4.0;

function Start () {

    Destroy(this.gameObject,tempo);
}

function OnCollisionEnter(col:Collision){
    if(col.gameObject.tag=="Player"){
        col.gameObject.SendMessage("bolada",1.0,SendMessageOptions.DontRequireReceiver);
        Destroy(this.gameObject);
    }
}

A função start é utilizada para controlar o tempo que a bola vai viver, depois temos a função que deteta as colisões e chama a função para contar mais um tiro certeiro.

Agora associamos este código à esfera e criamos um prefab com a esfera, para isso basta arrastar da hierarchy para a pasta project. Depois apagamos da hierarchy a cópia uma vez que vamos criar cópias por código.

Também pode criar um material para definir as propriedades da bola.

Agora duplicamos o jogador e arrastamos para o outro lado do ecrã, isto já com o script jogador.js associado. De seguida configuramos o script através do inspector para podermos definir teclas diferentes para os dois jogadores.

O jogador da direita tem de ser rodado para ficar de frente para o outro jogador, para isso devemos rodar o ponto vazio criado.

Outro ponto importante é o código que deve ser associado ao elemento gráfico do jogador e não ao ponto vazio.

Para terminar de preparar os jogadores devemos definir a tag Player nos dois jogadores.

A velocidade e a força fica ao critério de cada um basta testar e afinar até atingir os valores desejados.

Os meus valores ficaram assim:



Antes de terminar podemos adicionar algumas luzes e definir alguns pormenores visuais como um rasto para as bolas ou sons.

O projeto pode ser retirado aqui.