忍者ブログ

Memeplexes

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

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回]

PR