Balder 3D

Essa semana lá no C.E.S.A.R, nós precisamos iniciar a migração de uma aplicação em WPF para Silverlight. Essa aplicação utiliza a API 3D do WPF, por isso tivemos que buscar uma alternativa para termos os mesmos recursos em Silverlight. Foi então que encontramos o Balder.

O Balder é um projeto opensource hospedado no Codeplex e que possui licença Ms-PL, salvo o caso de se querer comercializar uma API 3D. Ele foi desenvolvido para rodar no Silverlight, WPF e Windows Phone 7. Isso está nos dando uma grande flexibilidade.

Os principais elementos para criar uma cena com Balder são:

  • Game: clr-namespace:Balder.Execution;assembly=Balder
  • Camera: clr-namespace:Balder.View;assembly=Balder
  • Light: clr-namespace:Balder.Lighting;assembly=Balder
  • Geometria: clr-namespace:Balder.Objects.Geometries;assembly=Balder
  • Material: clr-namespace:Balder.Materials;assembly=Balder

Abaixo segue um exemplo com adicionar os namespaces e utilizar esses elementos básicos:

<UserControl x:Class="BalderLab.SimpleExample"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:execution="clr-namespace:Balder.Execution;assembly=Balder"
             xmlns:geometries="clr-namespace:Balder.Objects.Geometries;assembly=Balder"
             xmlns:view="clr-namespace:Balder.View;assembly=Balder"
             xmlns:lighting="clr-namespace:Balder.Lighting;assembly=Balder"
             xmlns:material="clr-namespace:Balder.Materials;assembly=Balder">
    <Grid x:Name="LayoutRoot" Background="White">
        <execution:Game Width="640" Height="480">
            <execution:Game.Camera>
                <view:Camera Position="0,5,-10" />
            </execution:Game.Camera>
            <lighting:OmniLight Position="0,10,-10" Diffuse="White" />
            <geometries:Box Dimension="4,4,4" Position="0,0,0">
                <geometries:Box.Material>
                    <material:Material Ambient="White" Diffuse="Red" Specular="LightGreen" Shade="Gouraud" />
                </geometries:Box.Material>
            </geometries:Box>
        </execution:Game>
    </Grid>
</UserControl>

Sempre que adicionamos algum elemento 3D na tela, nossa maior vontade é de poder interagir com ele. Para rotacionar um elemento na tela, o Balder oferece um propriedade chamada InteractionEnabled. Caso você queira compor objetos separados para que eles se comportem como um, pode usar um Container conforme ilustrado abaixo.

<execution:Game Width="640" Height="480">
    <execution:Game.Camera>
        <view:Camera Position="0,5,-10" />
    </execution:Game.Camera>
    <lighting:OmniLight Position="0,10,-10" Diffuse="White" />
    <balder:Container InteractionEnabled="True">
        <geometries:ChamferBox Dimension="2,2,2">
            <geometries:Box.Material>
                <material:Material Ambient="White" Diffuse="Green" Specular="LightGreen" Shade="Gouraud" />
            </geometries:Box.Material>
        </geometries:ChamferBox>
        <geometries:Box Dimension="2,2,2" Position="4,0,0">
            <geometries:Box.Material>
                <material:Material Ambient="White" Diffuse="Red" Specular="LightGreen" Shade="Gouraud" />
            </geometries:Box.Material>
        </geometries:Box>
    </balder:Container>
</execution:Game>

O Balder não oferece nativamente um recurso de zoom, no entando ele pode ser facilmente implementado programaticamente alterando as propriedades da câmera. Esse exemplo pode ser encontrado no próprio site de samples do projeto.

No caso de querer trabalhar com modelos 3D mais complexos, a forma mais fácil é carregar os arquivos no formato .ASE (ASCII Scene Exporter). Até onde sei, esse formato pode ser exportado a partir do 3ds Max e existe um plugin para o Blender 3D.

<Geometries:Mesh AssetName="/MyGameApp;component/Assets/teapot.ase"/>

Para ter uma introdução rápida sobre o Balder em vídeo, veja esse link. Minha advertência sobre esse vídeo é que os valores default da versão atual são diferentes da versão utilizada pelo vídeo.

Anúncios

Sincronizar scroll do ListView no WPF

Vou mostrar a forma que encontrei para solucionar o problema de como sincronizar as barras de rolagem de dois LisViews, embora esse mesmo código possa ser adaptado para qualquer componente que use scrolls. No C.E.S.A.R, usamos essa técnica para simular o congelamento de colunas, recurso ainda não suportado nativamente pelo WPF. Esse não é um caso real, mantive o mais simples possível para facilitar o entendimento.

No código abaixo podemos perceber dois ListViews posicionados usando Grid: um a esquerda e outro a direita. O que quero mostrar é o efeito de quando o usuário usar a barra de rolagem no lado direito, a barra do lado esquerdo também irá se movimentar na mesma proporção.

<Window x:Class="ScrollDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:models="clr-namespace:ScrollDemo.Models"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <models:People x:Key="people" />
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ListView x:Name="listView" 
                  ItemsSource="{StaticResource ResourceKey=people}">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="Id" 
                                        DisplayMemberBinding="{Binding Id}" />
                        <GridViewColumn Header="Name"  
                                        DisplayMemberBinding="{Binding Name}" />
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
        <ListView ItemsSource="{StaticResource ResourceKey=people}" Grid.Column="1">
            <ListView.Resources>
                <Style TargetType="ScrollBar">
                    <EventSetter Event="ValueChanged" 
                                 Handler="ScrollValueChanged" />
                </Style>
            </ListView.Resources>
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="Phone" 
                                        DisplayMemberBinding="{Binding Phone}" />
                        <GridViewColumn Header="Address" 
                                        DisplayMemberBinding="{Binding Address}" />
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

Para conseguir capturar o evento, usamos o recurso de estilo com um target para ScrollBar dentro do ListView. O que as linhas 28 a 33 estão dizendo é: para qualquer ScrollBar dentro desse ListView, dispare o evento ValueChanged chamando o método ScrollValueChanged. O método ScrollValueChanged está ilustrado abaixo.

protected void ScrollValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    ScrollBar source = sender as ScrollBar;
    if (source == null || source.Orientation == Orientation.Horizontal)
        return;

    ScrollViewer target = GetScrollViewer(this.listView) as ScrollViewer;
    if (target == null)
        return;

    target.ScrollToVerticalOffset(source.Value);
}

O grande segredo desse evento está no método GetScrollViewer.

public static DependencyObject GetScrollViewer(DependencyObject container)
{
    if (container is ScrollViewer)
        return container;

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(container); i++)
    {
        var child = VisualTreeHelper.GetChild(container, i);
        var result = GetScrollViewer(child);
        if (result != null)
            return result;
    }

    return null;
}

Projeto Rosetta: aprendendo Silverlight

O projeto Rosetta (ou Rosetta Project) é um site dedicado a transmitir conhecimento sobre a tecnologia Silverlight para pessoas que já possuem algum grau de conhecimentos em outras tecnologias (principalmente o Flash). Acredito que o principal artigo foi escrito por Rick Barraza e possui o título de From Flash to Silverlight. Existe também uma entrevista no Channel 9 com o autor desse artigo onde é discutido um pouco mais sobre esse projeto. Segundo o site, a melhor forma de ficar atualizado sobre novos artigos é acompanhar utilizando o Twitter.

Curiosidade: "The Rosetta Stone is an Ancient Egyptian artifact (حجر رشيد in Arabic) which was instrumental in advancing modern understanding of hieroglyphic writing. The stone is a Ptolemaic era stele with carved text made up of three translations of a single passage: two in Egyptian language scripts (hieroglyphic and Demotic) and one in classical Greek. It was created in 196 BC, discovered by the French in 1799 at Rashid (a harbour on the Mediterranean coast in Egypt which the French referred to as Rosetta during Napoleon Bonaparte’s campaign in Egypt) and contributed greatly to the decipherment of the principles of hieroglyphic writing in 1822 by the British scientist Thomas Young and the French scholar Jean-François Champollion. Comparative translation of the stone assisted in understanding many previously undecipherable examples of hieroglyphic writing. The text of the Rosetta Stone is a decree from Ptolemy V, describing the repealing of various taxes and instructions to erect statues in temples." (fonte: wikipedia)

Links

Exemplos de código WPF

SamplesWPF Na biblioteca do MSDN sobre WPF, existe um tópico com exemplos de código para o WPF. Ele divide os exemplos em duas categorias: os com casos de uso mais práticos e os relacionados a tecnologia em si, como por exemplo globalização e dados.

Lá tem o exemplo que eu usei na apresentação do HCL para demonstrar o uso de “temas” no WPF que foi o “Logon Screen Demo”.

Links:

%d blogueiros gostam disto: