忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

Silverlight5からXnaを使う その5 2つのDrawingSurface

DrawingSurfaceはここまで、1つだけでした。
DrawingSurfaceはXNAで3DCGを表示するコントロールです。
一つの画面に一つの3DCGが表示されるのです。

ここではそうではなく、2つの画面に3DCGを表示する準備をしてみましょう。


ここには2つのDrawingSurfaceがあります。
それぞれ背景を赤と青に塗っています。
そしてちょっとした情報を表示しています。

どうやら2つのDrawingSurfaceが使うGraphicsDeviceは共通で、スレッドも同じようです。

なお、ここでやることはViewportでも同じようなことができますが、どうやら実装レベルでは異なっているようです。


プログラム

MainPage.xaml
<UserControl x:Class="SilverlightXna06.MainPage"
    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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="2*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBlock x:Name="textBlock" Grid.RowSpan="2"></TextBlock>
        <DrawingSurface Draw="DrawingSurface1_Draw"/>
        <DrawingSurface Draw="DrawingSurface2_Draw" Grid.Row="1" />
        <sdk:Label Foreground="White" Name="label1"/>
        <sdk:Label Foreground="White" Name="label2" Grid.Row="1"/>
    </Grid>
</UserControl>

MainPage.xaml.cs

using System.Windows.Controls;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Windows.Graphics;

namespace SilverlightXna06
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            textBlock.Text = GpuError.Message;
        }

        GraphicsDevice GraphicsDevice
        {
            get { return GraphicsDeviceManager.Current.GraphicsDevice; }
        }

        private void DrawingSurface1_Draw(object sender, DrawEventArgs e)
        {
            GraphicsDevice.Clear(new Color(1f, 0, 0));
            outputLog(label1);
            e.InvalidateSurface();
        }

        private void DrawingSurface2_Draw(object sender, DrawEventArgs e)
        {
            GraphicsDevice.Clear(new Color(0, 0, 1f));
            outputLog(label2);
            e.InvalidateSurface();
        }

        private void outputLog(Label label)
        {
            var gpu = GraphicsDevice;
            var threadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
            var viewport = gpu.Viewport;
            Dispatcher.BeginInvoke(delegate
            {
                label.Content = string.Format(
                    "gpu:{0}, thread:{1}, viewport.Bounds:{2}",
                    gpu.GetHashCode(),
                    threadID,
                    viewport.Bounds
                    );
            });
        }
    }
}


GpuError.cs
using System.Windows.Graphics;

namespace SilverlightXna06
{
    public class GpuError
    {
        public static bool ErrorExists
        {
            get
            {
                return GraphicsDeviceManager.Current.RenderMode != RenderMode.Hardware;
            }
        }

        public static string Message
        {
            get
            {
                if (!ErrorExists)
                { return ""; }

                string message;

                switch (GraphicsDeviceManager.Current.RenderModeReason)
                {
                    case RenderModeReason.Not3DCapable:
                        message = "あなたのグラフィックスハードウェアはこのページを表示出来ません";
                        break;
                    case RenderModeReason.GPUAccelerationDisabled:
                        message = "ハードウェアグラフィックスアクセラレーションがこのwebページでは有効にされていません。\n\n" +
                            "webサイトのオーナーに通知してください";
                        break;
                    case RenderModeReason.TemporarilyUnavailable:
                        message = "あなたのグラフィックスハードウェアは一時的に使用不可能になっています。\n\n" +
                            "webページをリロードするかブラウザを再起動してください。";
                        break;
                    case RenderModeReason.SecurityBlocked:
                        message =
                          "webサイトが3Dグラフィックスを表示できるようにするには、システム構成を変える必要があります。\n\n" +
                          "  1. ここを右クリックします。\n" +
                          "  2. [Silverlight]を選択します\n" +
                          "     ([Microsoft Silverlight の構成ダイヤログ]が表示されます)\n" +
                          "  3. [アクセス許可]タブを選択します\n" +
                          "  4. このサイトをリストの中から見つけ、その3Dグラフィックスアクセス許可を[拒否]から[許可]に変えます\n" +
                          "  5. [OK]をクリックします\n" +
                          "  6. ページをリロードします";
                        break;
                    default:
                        message = "不明なエラー";
                        break;
                }

                return "3D表示がブロックされました!\n\n\n" + message;
            }
        }
    }
}










拍手[0回]

PR

Silverlight5からXnaを使う その4 物理シミュレーション

前回の記事では普通のXnaで物理シミュレーションを行いました。
JigLibXというライブラリを使ったのです。

今回はSilverlightでやってみます。
やってみました。




「new box」というボタンを押してください。
空中に箱が突如として現れ落下していきます。
消す方法はありません(!!)。
あんまり出し過ぎたらパソコンが重くなる前にページをリロードしてください。


プログラム

今回のプログラムは主に5つのファイルからなります。

ファイル名 解説
BoxActor.cs 物理的に動く箱を表すクラス。
BoxRenderer.cs 箱を描画するクラス。
GpuError.cs Gpuに関連したエラー解説文の生成クラス。
MainPage.xaml メインページのXamlです。
MainPage.xaml.cs メインページのクラスです。ほとんどの操作はここで行います。

一部は前回とかぶりますが、念のため全部書いておきます。

BoxActor.cs
using Microsoft.Xna.Framework;
using JigLibX.Physics;
using JigLibX.Collision;
using JigLibX.Geometry;
using JigLibX.Math;


namespace SilverlightXna04
{
    public class BoxActor
    {
        private Vector3 scale;

        public Body Body { get; private set; }
        private CollisionSkin skin;

        public BoxActor(Vector3 position, Vector3 scale)
        {
            this.scale = scale;

            this.Body = new Body();
            this.skin = new CollisionSkin(this.Body);

            this.Body.CollisionSkin = this.skin;

            this.skin.AddPrimitive(
                new Box(Vector3.Zero, Matrix.Identity, scale),
                new MaterialProperties(
                    0.8f, // elasticity
                    0.8f, // static roughness
                    0.7f  // dynamic roughness
                ));

            SetMass(1.0f);
            this.Body.MoveTo(position, Matrix.Identity);
        }

        private void SetMass(float mass)
        {
            PrimitiveProperties primitiveProperties = new PrimitiveProperties(
                PrimitiveProperties.MassDistributionEnum.Solid,
                PrimitiveProperties.MassTypeEnum.Mass,
                mass
                );

            float junk;
            Vector3 centerOfMass;
            Matrix inertiaTensor, inertiaTensorCenterOfMass;

            this.skin.GetMassProperties(
                primitiveProperties,
                out junk,
                out centerOfMass,
                out inertiaTensor,
                out inertiaTensorCenterOfMass
                );

            this.Body.BodyInertia = inertiaTensorCenterOfMass;
            this.Body.Mass = mass;
            this.skin.ApplyLocalTransform(new Transform(-centerOfMass, Matrix.Identity));
        }

        public Matrix GetWorldTransform()
        {
            return Matrix.CreateScale(scale)
                * skin.GetPrimitiveLocal(0).Transform.Orientation
                * Body.Orientation
                * Matrix.CreateTranslation(Body.Position);
        }
    }
}

BoxRenderer.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace SilverlightXna04
{
    class BoxRenderer : System.IDisposable
    {
        public GraphicsDevice GraphicsDevice { get; private set; }
        VertexBuffer vertexBuffer;
        BasicEffect basicEffect;

        public BoxRenderer(GraphicsDevice graphicsDevice)
        {
            GraphicsDevice = graphicsDevice;
            basicEffect = new BasicEffect(GraphicsDevice) { VertexColorEnabled = true };

            Vector3[] positions = new[]
            {
                new Vector3(1, 1, 1),
                new Vector3(1, -1, 1),
                new Vector3(-1, -1,1),
                new Vector3(-1, 1, 1),

                new Vector3(1, 1, -1),
                new Vector3(1, -1, -1),
                new Vector3(-1, -1,-1),
                new Vector3(-1, 1, -1),
            };
            Color[] colors = new[]
            {
                new Color(0, 0, 1f),
                new Color(1f, 0, 0),
                new Color(0, 1f, 0),
                new Color(0, 1f, 1f),
                new Color(1f, 0, 1f),
                new Color(1f, 1f, 0),
            };

            VertexPositionColor[] vertices = new[]
            {
            new VertexPositionColor(positions[0], colors[0]),
            new VertexPositionColor(positions[1], colors[0]),
            new VertexPositionColor(positions[2], colors[0]),

            new VertexPositionColor(positions[3], colors[0]),
            new VertexPositionColor(positions[0], colors[0]),
            new VertexPositionColor(positions[2], colors[0]),

            
            new VertexPositionColor(positions[3], colors[1]),
            new VertexPositionColor(positions[2], colors[1]),
            new VertexPositionColor(positions[7], colors[1]),

            new VertexPositionColor(positions[7], colors[1]),
            new VertexPositionColor(positions[2], colors[1]),
            new VertexPositionColor(positions[6], colors[1]),
            

            new VertexPositionColor(positions[0], colors[2]),
            new VertexPositionColor(positions[3], colors[2]),
            new VertexPositionColor(positions[7], colors[2]),

            new VertexPositionColor(positions[0], colors[2]),
            new VertexPositionColor(positions[7], colors[2]),
            new VertexPositionColor(positions[4], colors[2]),


            new VertexPositionColor(positions[4], colors[3]),
            new VertexPositionColor(positions[6], colors[3]),
            new VertexPositionColor(positions[5], colors[3]),

            new VertexPositionColor(positions[4], colors[3]),
            new VertexPositionColor(positions[7], colors[3]),
            new VertexPositionColor(positions[6], colors[3]),


            new VertexPositionColor(positions[5], colors[4]),
            new VertexPositionColor(positions[2], colors[4]),
            new VertexPositionColor(positions[1], colors[4]),

            new VertexPositionColor(positions[5], colors[4]),
            new VertexPositionColor(positions[6], colors[4]),
            new VertexPositionColor(positions[2], colors[4]),


            new VertexPositionColor(positions[0], colors[5]),
            new VertexPositionColor(positions[5], colors[5]),
            new VertexPositionColor(positions[1], colors[5]),

            new VertexPositionColor(positions[0], colors[5]),
            new VertexPositionColor(positions[4], colors[5]),
            new VertexPositionColor(positions[5], colors[5]),
        };

            vertexBuffer = new VertexBuffer(
                graphicsDevice,
                typeof(VertexPositionColor),
                vertices.Length,
                BufferUsage.WriteOnly
                );
            vertexBuffer.SetData<VertexPositionColor>(vertices);
        }

        public void SetCamera(Matrix view, Matrix projection)
        {
            basicEffect.View = view;
            basicEffect.Projection = projection;
        }

        public void Draw(Matrix world)
        {
            basicEffect.World = Matrix.CreateScale(1 / 2f) * world;
            basicEffect.CurrentTechnique.Passes[0].Apply();


            GraphicsDevice.SetVertexBuffer(vertexBuffer);
            GraphicsDevice.DrawPrimitives(
                PrimitiveType.TriangleList,
                0,
                vertexBuffer.VertexCount / 3
                );
        }

        public void Dispose()
        {
            basicEffect.Dispose();
            vertexBuffer.Dispose();
        }
    }

}

GpuError.cs
using System.Windows.Graphics;

namespace SilverlightXna04
{
    public class GpuError
    {
        public static string Message
        {
            get
            {
                if (GraphicsDeviceManager.Current.RenderMode == RenderMode.Hardware)
                { return ""; }

                string message;

                switch (GraphicsDeviceManager.Current.RenderModeReason)
                {
                    case RenderModeReason.Not3DCapable:
                        message = "あなたのグラフィックスハードウェアはこのページを表示出来ません";
                        break;
                    case RenderModeReason.GPUAccelerationDisabled:
                        message = "ハードウェアグラフィックスアクセラレーションがこのwebページでは有効にされていません。\n\n" +
                            "webサイトのオーナーに通知してください";
                        break;
                    case RenderModeReason.TemporarilyUnavailable:
                        message = "あなたのグラフィックスハードウェアは一時的に使用不可能になっています。\n\n" +
                            "webページをリロードするかブラウザを再起動してください。";
                        break;
                    case RenderModeReason.SecurityBlocked:
                        message =
                          "webサイトが3Dグラフィックスを表示できるようにするには、システム構成を変える必要があります。\n\n" +
                          "  1. ここを右クリックします\n" +
                          "  2. 'Silverlight'を選択します\n" +
                          "     ('Microsoft Silverlight Configuration'ダイヤログが表示されます)\n" +
                          "  3. 'Permissions'タブを選択します\n" +
                          "  4. このサイトをリストの中から見つけ、その3Dグラフィックスパーミッションを'Deny'から'Allow'に変えます\n" +
                          "  5. 'OK'をクリックします\n" +
                          "  6. ページをリロードします";
                        break;
                    default:
                        message = "不明なエラー";
                        break;
                }

                return "3D表示がブロックされました!\n\n\n" + message;
            }
        }

    }
}

MainPage.xaml
<UserControl x:Class="SilverlightXna04.MainPage"
    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"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock x:Name="textBlock"></TextBlock>
        <DrawingSurface Draw="DrawingSurface_Draw"/>
        <Button Content="new Box" Width="80" Click="Button_Click" Margin="0,0,12,12" HorizontalAlignment="Right" Height="40" VerticalAlignment="Bottom" />
    </Grid>
</UserControl>

MainPage.xaml.cs
using System.Windows.Controls;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Xna = Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Windows.Graphics;
using JigLibX.Physics;
using JigLibX.Collision;

namespace SilverlightXna04
{
    public partial class MainPage : UserControl
    {
        private bool loaded = false;

        private BoxRenderer boxRenderer;

        private PhysicsSystem world;
        private List<BoxActor> mobileBoxes = new List<BoxActor>();
        private BoxActor immobileBox;

        public MainPage()
        {
            InitializeComponent();
            textBlock.Text = GpuError.Message;
            InitializePhysics();
        }

        private void InitializePhysics()
        {
            world = new PhysicsSystem { };
            world.CollisionSystem = new CollisionSystemSAP();

            immobileBox = new BoxActor(new Vector3(0, -5, 0), new Vector3(25, 5, 25));
            immobileBox.Body.Immovable = true;

            world.AddBody(immobileBox.Body);
        }

        private void DrawingSurface_Draw(object sender, DrawEventArgs e)
        {
            if (!loaded)
            {
                LoadContent();
                loaded = true;
            }

            Update();
            Draw();
            e.InvalidateSurface();
        }

        private GraphicsDevice GraphicsDevice
        {
            get
            {
                return GraphicsDeviceManager.Current.GraphicsDevice;
            }
        }

        private void LoadContent()
        {
            boxRenderer = new BoxRenderer(GraphicsDevice);
            boxRenderer.SetCamera(
                Matrix.CreateLookAt(
                    new Vector3(15, 15, 30),
                    new Vector3(),
                    Vector3.Up
                    ),
                Matrix.CreatePerspectiveFieldOfView(
                    MathHelper.ToRadians(45),
                    GraphicsDevice.Viewport.AspectRatio,
                    0.1f,
                    1000
                    )
                 );
        }

        private void Draw()
        {
            GraphicsDevice.Clear(new Xna.Color(0.39f, 0.58f, 0.93f));
            GraphicsDevice.RasterizerState = RasterizerState.CullNone;

            foreach (var box in mobileBoxes)
            {
                boxRenderer.Draw(box.GetWorldTransform());
            }
            boxRenderer.Draw(immobileBox.GetWorldTransform());

        }

        private void Update()
        {
            if (addingBox)
            {
                addBox();
                addingBox = false;
            }
            world.Integrate(1 / 60f);
        }

        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            //こんな仕掛けが必要なのは
            //Update, Drawがスレッドで動いているため
            addingBox = true;
        }

        private bool addingBox;

        private void addBox()
        {
            var actor = new BoxActor(new Vector3(0, 10, 0), new Vector3(1));
            world.AddBody(actor.Body);
            mobileBoxes.Add(actor);
        }
    }
}




おまけ

プロジェクトファイルです。


拍手[0回]


Silverlight5からXnaを使う その3 マウスとキーボード

前回までは三角形を回転させました。
しかしその三角形は自分で勝手に回るだけです。
ゲームを作りたいならユーザーがマウスを動かしたりキーボードをカチャカチャしたりするのに反応すべきです。


(キーボードの矢印キー「←か→」を押してください)

非SilverlightなXnaにはMouseクラスやKeyboardクラスがあります。
それを使ってマウスやキーボードの状態を取得できたのです。
が、Silverlight版Xnaには該当するクラスが公式にはありません。

ありません、では困るのでちゃんと用意されています。

Silverlight 5 Toolkit December 11

このサイトにあるmsiファイルを実行すると、Microsoft.Xna.Framework.Toolkitがインストールされます。
そのdllから、MouseクラスやKeyboardクラスが使えるようになるのです。
ただし使い方はオリジナルのものとちょっと違います。
はじめにRootControlにコントロールのインスタンスをセットする必要があります。

詳しく見て行きましょう。


プログラム

using System;
using System.Windows.Controls;

using Microsoft.Xna.Framework;
using Xna = Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Windows.Graphics;
using Microsoft.Xna.Framework.Input;


namespace SilverlightXna03
{
    public partial class MainPage : UserControl
    {
        private VertexBuffer vertexBuffer;
        private BasicEffect basicEffect;
        private bool loaded = false;
        private float angle;

        public MainPage()
        {
            InitializeComponent();
            showGpuError();
            Keyboard.RootControl = this;
        }

        private void showGpuError()
        {
            if (GraphicsDeviceManager.Current.RenderMode == RenderMode.Hardware)
            { return; }

            string message;
            switch (GraphicsDeviceManager.Current.RenderModeReason)
            {
                case RenderModeReason.Not3DCapable:
                    message = "あなたのグラフィックスハードウェアはこのページを表示出来ません";
                    break;
                case RenderModeReason.GPUAccelerationDisabled:
                    message = "ハードウェアグラフィックスアクセラレーションがこのwebページでは有効にされていません。\n\n" +
                        "webサイトのオーナーに通知してください";
                    break;
                case RenderModeReason.TemporarilyUnavailable:
                    message = "あなたのグラフィックスハードウェアは一時的に使用不可能になっています。\n\n" +
                        "webページをリロードするかブラウザを再起動してください。";
                    break;
                case RenderModeReason.SecurityBlocked:
                    message =
                      "webサイトが3Dグラフィックスを表示できるようにするには、システム構成を変える必要があります。\n\n" +
                      "  1. ここを右クリックします\n" +
                      "  2. 'Silverlight'を選択します\n" +
                      "     ('Microsoft Silverlight Configuration'ダイヤログが表示されます)\n" +
                      "  3. 'Permissions'タブを選択します\n" +
                      "  4. このサイトをリストの中から見つけ、その3Dグラフィックスパーミッションを'Deny'から'Allow'に変えます\n" +
                      "  5. 'OK'をクリックします\n" +
                      "  6. ページをリロードします";
                    break;
                default:
                    message = "不明なエラー";
                    break;
            }

            textBlock.Text = "3D表示がブロックされました!\n\n\n" + message;
        }

        private void DrawingSurface_Draw(object sender, DrawEventArgs e)
        {
            if (!loaded)
            {
                LoadContent();
                loaded = true;
            }

            Update();
            Draw();
            e.InvalidateSurface();
        }


        private GraphicsDevice GraphicsDevice
        {
            get
            {
                return GraphicsDeviceManager.Current.GraphicsDevice;
            }
        }

        private void LoadContent()
        {
            VertexPositionColor[] vertices = new[]
                {
                    new VertexPositionColor(
                        new Vector3(-1, -1, 0), 
                        new Color(1f, 0, 0)
                    ),
                    new VertexPositionColor(
                        new Vector3(0, 1, 0),
                        new Color(1f, 1f, 1f)
                    ),
                    new VertexPositionColor(
                        new Vector3(1, -1, 0),
                        new Color(0, 0, 1f)
                    )
                };

            vertexBuffer = new VertexBuffer(
                GraphicsDevice,
                VertexPositionColor.VertexDeclaration,
                vertices.Length,
                BufferUsage.WriteOnly
                );
            vertexBuffer.SetData(0, vertices, 0, vertices.Length, 0);

            basicEffect = new BasicEffect(GraphicsDevice)
            {
                VertexColorEnabled = true,
                Projection = Matrix.CreatePerspectiveFieldOfView(
                    MathHelper.ToRadians(45),
                    GraphicsDevice.Viewport.AspectRatio,
                    1, 100
                    )
            };
        }

        private void Update()
        {
            KeyboardState keyboardState = Keyboard.GetState();

            if (keyboardState.IsKeyDown(System.Windows.Input.Key.Left))
            {
                angle += 0.01f;
            }
            if (keyboardState.IsKeyDown(System.Windows.Input.Key.Right))
            {
                angle += -0.01f;
            }
        }

        private void Draw()
        {
            GraphicsDevice.Clear(new Xna.Color(0.39f, 0.58f, 0.93f));
            GraphicsDevice.RasterizerState = RasterizerState.CullNone;

            basicEffect.View = Matrix.CreateLookAt(
                new Vector3
                    (
                    3 * (float)Math.Sin(angle),
                    0,
                    3 * (float)Math.Cos(angle)
                    ),
                new Vector3(),
                Vector3.Up
                );

            basicEffect.CurrentTechnique.Passes[0].Apply();
            GraphicsDevice.SetVertexBuffer(vertexBuffer);
            GraphicsDevice.DrawPrimitives(
                PrimitiveType.TriangleList,
                0,
                vertexBuffer.VertexCount / 3
                );
        }
    }
}

Silverlight版のKeyboardStateクラス、Keyboardクラス、MouseStateクラス、Mouseクラスは
オリジナルのXnaバージョンのクラスとほとんどかわりません。
ただし気を付けなければいけないことが1つだけ。
GetStateメソッドで状態を得る前に、RootControlプロパティでコントロールをセットしなければいけません。
このプログラムではコントロールのコンストラクタでそれを行なっています。
あとは普通のXnaと全く同じです。


追記 2012/03/10

どうやらMouse.RootControlにセットすると、右クリックした時にSilverlightメニューが現れなくなるようです。
ですからshowGpuErrorのメッセージは次のように書き換えるべきかもしれません:

1.[スタート]-[すべてのプログラム]をクリックします。
2.[Microsoft Silverlight]-[Silverlight]を選択します
    ([Microsoft Silverlight の構成
]ダイヤログが表示されます)
3. [アクセス許可]タブを選択します
4. このサイトをリストの中から見つけ、その3Dグラフィックスアクセス許可を[拒否]から[許可]に変えます
5. [OK]をクリックします
6. ページをリロードします

拍手[1回]


Silverlight5からXnaを使う その2 テクスチャ三角形

前回はくるくる回る色つき三角形を表示しました。
今回はテクスチャ付き三角形を表示してみます。


Microsoft Visual Web Developer 2010 Expressでこれをやるには一苦労です。
(といっても最初からやり方がわかっていればそう難しくないのですが)
プログラムは次のようになります。


プログラム

MainPage.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using Microsoft.Xna.Framework;
using Xna = Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Windows.Graphics;

namespace SilverlightXna02
{
    public partial class MainPage : UserControl
    {
        private VertexBuffer vertexBuffer;
        private BasicEffect basicEffect;
        private bool loaded = false;

        public MainPage()
        {
            InitializeComponent();
            showGpuError();
        }

        private void showGpuError()
        {
            if (GraphicsDeviceManager.Current.RenderMode == RenderMode.Hardware)
            { return; }

            string message;
            switch (GraphicsDeviceManager.Current.RenderModeReason)
            {
                case RenderModeReason.Not3DCapable:
                    message = "あなたのグラフィックスハードウェアはこのページを表示することが出来ません";
                    break;
                case RenderModeReason.GPUAccelerationDisabled:
                    message = "ハードウェアグラフィックスアクセラレーションがこのwebページでは有効にされていません。\n\n" +
                        "webサイトのオーナーに教えてあげてください";
                    break;
                case RenderModeReason.TemporarilyUnavailable:
                    message = "あなたのグラフィックスハードウェアは一時的に使用不可能になっています。\n\n" +
                        "webページをリロードするかブラウザを再起動してください。";
                    break;
                case RenderModeReason.SecurityBlocked:
                    message =
                      "webサイトが3Dグラフィックスを表示できるようにするには、システム構成を変える必要があります。\n\n" +
                      "  1. ページを右クリックします\n" +
                      "  2. 'Silverlight'を選択します\n" +
                      "     ('Microsoft Silverlight Configuration'ダイヤログが表示されます)\n" +
                      "  3. 'Permissions'タブを選択します\n" +
                      "  4. このサイトをリストの中から見つけ、その3Dグラフィックスパーミッションを'Deny'から'Allow'に変えます\n" +
                      "  5. 'OK'をクリックします\n" +
                      "  6. ページをリロードします";
                    break;
                default:
                    message = "不明なエラー";
                    break;
            }

            textBlock.Text = "3D表示がブロックされました!\n\n\n" + message;
        }

        private void DrawingSurface_Draw(object sender, DrawEventArgs e)
        {
            if (!loaded)
            {
                LoadContent();
                loaded = true;
            }

            Draw();
            e.InvalidateSurface();
        }

        private GraphicsDevice GraphicsDevice
        {
            get
            {
                return GraphicsDeviceManager.Current.GraphicsDevice;
            }
        }

        private void LoadContent()
        {
            VertexPositionTexture[] vertices = new[]
                {
                    new VertexPositionTexture(
                        new Vector3(-1, -1, 0),
                        new Vector2(0, 1)
                    ),
                    new VertexPositionTexture(
                        new Vector3(0, 1, 0),
                        new Vector2(0.5f, 0)
                    ),
                    new VertexPositionTexture(
                        new Vector3(1, -1, 0),
                        new Vector2(1, 1)
                    )
                };

            vertexBuffer = new VertexBuffer(
                GraphicsDevice,
                typeof(VertexPositionTexture), 
                vertices.Length,
                BufferUsage.WriteOnly
                );
            vertexBuffer.SetData(0, vertices, 0, vertices.Length, 0);

            basicEffect = new BasicEffect(GraphicsDevice)
            {
                TextureEnabled = true,
                Texture = LoadTexture("PenguinsSmall.jpg"),
                Projection = Matrix.CreatePerspectiveFieldOfView(
                    MathHelper.ToRadians(45),
                    GraphicsDevice.Viewport.AspectRatio,
                    1, 100
                    )
            };

            //大きさが2^nでないテクスチャはClampにする必要がある?
            GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;
        }

        private Texture2D LoadTexture(string name)
        {
            System.IO.Stream imageStream = Application.GetResourceStream(
                new Uri(name, UriKind.Relative)
                ).Stream;

            Texture2D texture = null;
            System.Threading.ManualResetEvent eventWaiter 
                = new System.Threading.ManualResetEvent(false);

            Dispatcher.BeginInvoke(delegate
            {
                var image = new BitmapImage();
                image.SetSource(imageStream);

                texture = new Texture2D(
                    GraphicsDevice,
                    image.PixelWidth,
                    image.PixelHeight,
                    false,
                    SurfaceFormat.Color);
                image.CopyTo(texture);
                eventWaiter.Set();
            });

            eventWaiter.WaitOne();
            return texture;
        }

        private void Draw()
        {
            GraphicsDevice.Clear(new Xna.Color(0.39f, 0.58f, 0.93f));
            GraphicsDevice.RasterizerState = RasterizerState.CullNone;

            basicEffect.View = Matrix.CreateLookAt(
                new Vector3
                    (
                    3 * (float)Math.Sin(Environment.TickCount / 400d),
                    0,
                    3 * (float)Math.Cos(Environment.TickCount / 400d)
                    ),
                new Vector3(),
                Vector3.Up
                );

            basicEffect.CurrentTechnique.Passes[0].Apply();
            GraphicsDevice.SetVertexBuffer(vertexBuffer);
            GraphicsDevice.DrawPrimitives(
                PrimitiveType.TriangleList,
                0,
                vertexBuffer.VertexCount / 3
                );
        }
    }
}


いくつか注意する点があります。
まずBitmapImageですが、これはポリゴンに貼り付けるテクスチャをロードするのに使っています。
これはWPFに根ざしたクラスなので非UIスレッドからは呼び出せません。
そしてどうやら、Drawは非UIスレッドらしいのです。
もうお分かりでしょう。
Dispatcher.BeginInvokeの中でBitmapImageの操作を行う必要があるのです。

また、サンプラーステートも設定をきちんとしなくてはいけません。
大きさが2のn乗でないテクスチャを使う場合には、SamplerStates[0]をClampに明示的にしなくてはいけないようです。
普通のXnaの場合にはそうでなかったのに不思議な話ですね。

テクスチャはBuild ActionをContentにしてください。


おまけ

プロジェクトファイルです。



拍手[0回]


Silverlight5からXnaを使う

Silverlight5からXnaを使えるようになりました。
つまり自分のホームページに3DCGをリアルタイムで表示することが出来るようになったのです。


以下のページを参考にしてプログラムを書いてみました。
[Silverlight]Silverlight5ベータで3D表示する方法(「関西ゲームプログラミング勉強会」LT内容)
3D Basics using Silverlight-5 and XNA 


プログラム

MainPage.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;

using Microsoft.Xna.Framework;
using Xna = Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Windows.Graphics;

namespace SilverlightXna
{
    public partial class MainPage : UserControl
    {
        private VertexBuffer vertexBuffer;
        private BasicEffect basicEffect;
        private bool loaded = false;

        public MainPage()
        {
            InitializeComponent();
            showGpuError();
        }

        private void showGpuError()
        {
            if (GraphicsDeviceManager.Current.RenderMode == RenderMode.Hardware)
            { return; }

            string message;
            switch (GraphicsDeviceManager.Current.RenderModeReason)
            {
                case RenderModeReason.Not3DCapable:
                    message = "あなたのグラフィックスハードウェアはこのページを表示することが出来ません";
                    break;
                case RenderModeReason.GPUAccelerationDisabled:
                    message = "ハードウェアグラフィックスアクセラレーションがこのwebページでは有効にされていません。\n\n" +
                        "webサイトのオーナーに教えてあげてください";
                    break;
                case RenderModeReason.TemporarilyUnavailable:
                    message = "あなたのグラフィックスハードウェアは一時的に使用不可能になっています。\n\n" +
                        "webページをリロードするかブラウザを再起動してください。";
                    break;
                case RenderModeReason.SecurityBlocked:
                    message =
                      "webサイトが3Dグラフィックスを表示できるようにするには、システム構成を変える必要があります。\n\n" +
                      "  1. ページを右クリックします\n" +
                      "  2. 'Silverlight'を選択します\n" +
                      "     ('Microsoft Silverlight Configuration'ダイヤログが表示されます)\n" +
                      "  3. 'Permissions'タブを選択します\n" +
                      "  4. このサイトをリストの中から見つけ、その3Dグラフィックスパーミッションを'Deny'から'Allow'に変えます\n" +
                      "  5. 'OK'をクリックします\n" +
                      "  6. ページをリロードします";
                    break;
                default:
                    message = "不明なエラー";
                    break;
            }

            textBlock.Text = "3D表示がブロックされました!\n\n\n" + message;
        }
        private void DrawingSurface_Draw(object sender, DrawEventArgs e)
        {
            if (!loaded)
            {
                LoadContent();
                loaded = true;
            }

            Draw();
            e.InvalidateSurface();
        }

        private GraphicsDevice GraphicsDevice
        {
            get
            {
                return GraphicsDeviceManager.Current.GraphicsDevice;
            }
        }

        private void LoadContent()
        {
            VertexPositionColor[] vertices = new[]
                {
                    new VertexPositionColor(
                        new Vector3(-1, -1, 0), 
                        new Xna.Color(1f, 0, 0)
                    ),
                    new VertexPositionColor(
                        new Vector3(0, 1, 0),
                        new Xna.Color(1f, 1f, 1f)
                    ),
                    new VertexPositionColor(
                        new Vector3(1, -1, 0),
                        new Xna.Color(0, 0, 1f)
                    )
                };

            vertexBuffer = new VertexBuffer(
                GraphicsDevice,
                VertexPositionColor.VertexDeclaration,
                vertices.Length,
                BufferUsage.WriteOnly
                );
            vertexBuffer.SetData(0, vertices, 0, vertices.Length, 0);

            basicEffect = new BasicEffect(GraphicsDevice)
            {
                VertexColorEnabled = true,
                Projection = Matrix.CreatePerspectiveFieldOfView(
                    MathHelper.ToRadians(45),
                    GraphicsDevice.Viewport.AspectRatio,
                    1, 100
                    )
            };
        }

        private void Draw()
        {
            GraphicsDevice.Clear(new Xna.Color(0.39f, 0.58f, 0.93f));
            GraphicsDevice.RasterizerState = RasterizerState.CullNone;

            basicEffect.View = Matrix.CreateLookAt(
                new Vector3
                    (
                    3 * (float)Math.Sin(Environment.TickCount / 400d),
                    0,
                    3 * (float)Math.Cos(Environment.TickCount / 400d)
                    ), 
                new Vector3(),
                Vector3.Up
                );

            basicEffect.CurrentTechnique.Passes[0].Apply();
            GraphicsDevice.SetVertexBuffer(vertexBuffer);
            GraphicsDevice.DrawPrimitives(
                PrimitiveType.TriangleList,
                0, 
                vertexBuffer.VertexCount / 3
                );
        }
    }
}
MainPage.xaml

<UserControl x:Class="SilverlightXna.MainPage"
    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"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="LightGray">
        <TextBlock x:Name="textBlock"></TextBlock>
        <DrawingSurface Draw="DrawingSurface_Draw"/>
    </Grid>
</UserControl>
SilverlightXnaTestPage.aspx
<%@ Page Language="C#" AutoEventWireup="true" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>SilverlightXna</title>
    <style type="text/css">
    html, body {
	    height: 100%;
	    overflow: auto;
    }
    body {
	    padding: 0;
	    margin: 0;
    }
    #silverlightControlHost {
	    height: 100%;
	    text-align:center;
    }
    </style>
    <script type="text/javascript" src="Silverlight.js"></script>
    <script type="text/javascript">
        function onSilverlightError(sender, args) {
            var appSource = "";
            if (sender != null && sender != 0) {
              appSource = sender.getHost().Source;
            }
            
            var errorType = args.ErrorType;
            var iErrorCode = args.ErrorCode;

            if (errorType == "ImageError" || errorType == "MediaError") {
              return;
            }

            var errMsg = "Unhandled Error in Silverlight Application " +  appSource + "\n" ;

            errMsg += "Code: "+ iErrorCode + "    \n";
            errMsg += "Category: " + errorType + "       \n";
            errMsg += "Message: " + args.ErrorMessage + "     \n";

            if (errorType == "ParserError") {
                errMsg += "File: " + args.xamlFile + "     \n";
                errMsg += "Line: " + args.lineNumber + "     \n";
                errMsg += "Position: " + args.charPosition + "     \n";
            }
            else if (errorType == "RuntimeError") {           
                if (args.lineNumber != 0) {
                    errMsg += "Line: " + args.lineNumber + "     \n";
                    errMsg += "Position: " +  args.charPosition + "     \n";
                }
                errMsg += "MethodName: " + args.methodName + "     \n";
            }

            throw new Error(errMsg);
        }
    </script>
</head>
<body>
    <form id="form1" runat="server" style="height:100%">
    <div id="silverlightControlHost">
        <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
		  <param name="source" value="ClientBin/SilverlightXna.xap"/>
		  <param name="onError" value="onSilverlightError" />
		  <param name="background" value="white" />
		  <param name="minRuntimeVersion" value="5.0.61118.0" />
		  <param name="autoUpgrade" value="true" />
          <param name="EnableGPUAcceleration" value="true" />
		  <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=5.0.61118.0" style="text-decoration:none">
 			  <img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Get Microsoft Silverlight" style="border-style:none"/>
		  </a>
	    </object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div>
    </form>
</body>
</html>



MainPage.xaml.csは赤白青の三角形を表示し、それを回転させます。
何の変哲もないxnaプログラムと言いたいところですが、LoadContentやDrawは自分で書かなければいけないようです。
SilverlightXnaTestPage.aspxではEnableGPUAccelerationをtrueにするのがキモです。

そうそう、このプログラムをMicrosoft Visual Web Developer 2010 Expressで動かすには参照の追加が必要です。

Microsoft.Xna.Framework
Microsoft.Xna.Framework.Graphics
Microsoft.Xna.Framework.Graphics.Extensions
Microsoft.Xna.Framework.Math
System.Windows.Xna


beta版の時には結構不自由だったようですが、リリース番ではそこそこ使えるようになっているようですね。


おまけ

プロジェクトファイルです。

拍手[1回]


        
  • 1
  • 2