忍者ブログ

Memeplexes

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

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

PR