using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Color = Microsoft.Xna.Framework.Color;
using Matrix = Microsoft.Xna.Framework.Matrix;
namespace WpfInteropSample
{
///
/// Host a Direct3D 11 scene.
///
public class D3D11Host : Image
{
// The Direct3D 11 device (shared by all D3D11Host elements):
private static GraphicsDevice _graphicsDevice;
private static int _referenceCount;
private static readonly object _graphicsDeviceLock = new object();
// Image source:
private RenderTarget2D _renderTarget;
private D3D11Image _d3D11Image;
private bool _resetBackBuffer;
// Render timing:
private readonly Stopwatch _timer;
private TimeSpan _lastRenderingTime;
///
/// Gets a value indicating whether the controls runs in the context of a designer (e.g.
/// Visual Studio Designer or Expression Blend).
///
///
/// if controls run in design mode; otherwise,
/// .
///
public static bool IsInDesignMode
{
get
{
if (!_isInDesignMode.HasValue)
_isInDesignMode = (bool)DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(FrameworkElement)).Metadata.DefaultValue;
return _isInDesignMode.Value;
}
}
private static bool? _isInDesignMode;
///
/// Gets the graphics device.
///
/// The graphics device.
public GraphicsDevice GraphicsDevice
{
get { return _graphicsDevice; }
}
///
/// Initializes a new instance of the class.
///
public D3D11Host()
{
_timer = new Stopwatch();
Loaded += OnLoaded;
Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs eventArgs)
{
if (IsInDesignMode)
return;
InitializeGraphicsDevice();
InitializeImageSource();
Initialize();
StartRendering();
}
private void OnUnloaded(object sender, RoutedEventArgs eventArgs)
{
if (IsInDesignMode)
return;
StopRendering();
Unitialize();
UnitializeImageSource();
UninitializeGraphicsDevice();
}
private static void InitializeGraphicsDevice()
{
lock (_graphicsDeviceLock)
{
_referenceCount++;
if (_referenceCount == 1)
{
// Create Direct3D 11 device.
var graphicsAdapter = GraphicsAdapter.DefaultAdapter;
var presentationParameters = new PresentationParameters
{
DeviceWindowHandle = IntPtr.Zero,
};
_graphicsDevice = new GraphicsDevice(graphicsAdapter, GraphicsProfile.HiDef, presentationParameters);
}
}
}
private static void UninitializeGraphicsDevice()
{
lock (_graphicsDeviceLock)
{
_referenceCount--;
if (_referenceCount == 0)
{
_graphicsDevice.Dispose();
_graphicsDevice = null;
}
}
}
private void InitializeImageSource()
{
_d3D11Image = new D3D11Image();
_d3D11Image.IsFrontBufferAvailableChanged += OnIsFrontBufferAvailableChanged;
CreateBackBuffer();
Source = _d3D11Image;
}
private void UnitializeImageSource()
{
_d3D11Image.IsFrontBufferAvailableChanged -= OnIsFrontBufferAvailableChanged;
Source = null;
if (_d3D11Image != null)
{
_d3D11Image.Dispose();
_d3D11Image = null;
}
if (_renderTarget != null)
{
_renderTarget.Dispose();
_renderTarget = null;
}
}
private void CreateBackBuffer()
{
_d3D11Image.SetBackBuffer(null);
if (_renderTarget != null)
{
_renderTarget.Dispose();
_renderTarget = null;
}
int width = Math.Max((int)ActualWidth, 1);
int height = Math.Max((int)ActualHeight, 1);
_renderTarget = new RenderTarget2D(_graphicsDevice, width, height, false, SurfaceFormat.Bgr32, DepthFormat.Depth24Stencil8, 0, RenderTargetUsage.DiscardContents, true);
_d3D11Image.SetBackBuffer(_renderTarget);
}
private void StartRendering()
{
if (_timer.IsRunning)
return;
CompositionTarget.Rendering += OnRendering;
_timer.Start();
}
private void StopRendering()
{
if (!_timer.IsRunning)
return;
CompositionTarget.Rendering -= OnRendering;
_timer.Stop();
}
private void OnRendering(object sender, EventArgs eventArgs)
{
if (!_timer.IsRunning)
return;
// Recreate back buffer if necessary.
if (_resetBackBuffer)
CreateBackBuffer();
// CompositionTarget.Rendering event may be raised multiple times per frame
// (e.g. during window resizing).
var renderingEventArgs = (RenderingEventArgs)eventArgs;
if (_lastRenderingTime != renderingEventArgs.RenderingTime || _resetBackBuffer)
{
_lastRenderingTime = renderingEventArgs.RenderingTime;
GraphicsDevice.SetRenderTarget(_renderTarget);
Render(_timer.Elapsed);
GraphicsDevice.Flush();
}
_d3D11Image.Invalidate(); // Always invalidate D3DImage to reduce flickering
// during window resizing.
_resetBackBuffer = false;
}
///
/// Raises the event, using the specified
/// information as part of the eventual event data.
///
/// Details of the old and new size involved in the change.
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
_resetBackBuffer = true;
base.OnRenderSizeChanged(sizeInfo);
}
private void OnIsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs eventArgs)
{
if (_d3D11Image.IsFrontBufferAvailable)
{
StartRendering();
_resetBackBuffer = true;
}
else
{
StopRendering();
}
}
// Source: http://msdn.microsoft.com/en-us/library/bb203926(v=xnagamestudio.40).aspx
// Note: This is just an example. To improve the D3D11Host make the methods
// Initialize(), Unitialize(), and Render() protected virtual or call an
// external "renderer".
Matrix _worldMatrix;
Matrix _viewMatrix;
Matrix _projectionMatrix;
VertexDeclaration vertexDeclaration;
VertexBuffer _vertexBuffer;
BasicEffect _basicEffect;
private void Initialize()
{
float tilt = MathHelper.ToRadians(0); // 0 degree angle
// Use the world matrix to tilt the cube along x and y axes.
_worldMatrix = Matrix.CreateRotationX(tilt) * Matrix.CreateRotationY(tilt);
_viewMatrix = Matrix.CreateLookAt(new Vector3(5, 5, 5), Vector3.Zero, Vector3.Up);
_projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45), // 45 degree angle
(float)GraphicsDevice.Viewport.Width /
(float)GraphicsDevice.Viewport.Height,
1.0f, 100.0f);
_basicEffect = new BasicEffect(GraphicsDevice);
_basicEffect.World = _worldMatrix;
_basicEffect.View = _viewMatrix;
_basicEffect.Projection = _projectionMatrix;
// primitive color
_basicEffect.AmbientLightColor = new Vector3(0.1f, 0.1f, 0.1f);
_basicEffect.DiffuseColor = new Vector3(1.0f, 1.0f, 1.0f);
_basicEffect.SpecularColor = new Vector3(0.25f, 0.25f, 0.25f);
_basicEffect.SpecularPower = 5.0f;
_basicEffect.Alpha = 1.0f;
_basicEffect.LightingEnabled = true;
if (_basicEffect.LightingEnabled)
{
_basicEffect.DirectionalLight0.Enabled = true; // enable each light individually
if (_basicEffect.DirectionalLight0.Enabled)
{
// x direction
_basicEffect.DirectionalLight0.DiffuseColor = new Vector3(1, 0, 0); // range is 0 to 1
_basicEffect.DirectionalLight0.Direction = Vector3.Normalize(new Vector3(-1, 0, 0));
// points from the light to the origin of the scene
_basicEffect.DirectionalLight0.SpecularColor = Vector3.One;
}
_basicEffect.DirectionalLight1.Enabled = true;
if (_basicEffect.DirectionalLight1.Enabled)
{
// y direction
_basicEffect.DirectionalLight1.DiffuseColor = new Vector3(0, 0.75f, 0);
_basicEffect.DirectionalLight1.Direction = Vector3.Normalize(new Vector3(0, -1, 0));
_basicEffect.DirectionalLight1.SpecularColor = Vector3.One;
}
_basicEffect.DirectionalLight2.Enabled = true;
if (_basicEffect.DirectionalLight2.Enabled)
{
// z direction
_basicEffect.DirectionalLight2.DiffuseColor = new Vector3(0, 0, 0.5f);
_basicEffect.DirectionalLight2.Direction = Vector3.Normalize(new Vector3(0, 0, -1));
_basicEffect.DirectionalLight2.SpecularColor = Vector3.One;
}
}
vertexDeclaration = new VertexDeclaration(new[]
{
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
new VertexElement(24, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
});
Vector3 topLeftFront = new Vector3(-1.0f, 1.0f, 1.0f);
Vector3 bottomLeftFront = new Vector3(-1.0f, -1.0f, 1.0f);
Vector3 topRightFront = new Vector3(1.0f, 1.0f, 1.0f);
Vector3 bottomRightFront = new Vector3(1.0f, -1.0f, 1.0f);
Vector3 topLeftBack = new Vector3(-1.0f, 1.0f, -1.0f);
Vector3 topRightBack = new Vector3(1.0f, 1.0f, -1.0f);
Vector3 bottomLeftBack = new Vector3(-1.0f, -1.0f, -1.0f);
Vector3 bottomRightBack = new Vector3(1.0f, -1.0f, -1.0f);
Vector2 textureTopLeft = new Vector2(0.0f, 0.0f);
Vector2 textureTopRight = new Vector2(1.0f, 0.0f);
Vector2 textureBottomLeft = new Vector2(0.0f, 1.0f);
Vector2 textureBottomRight = new Vector2(1.0f, 1.0f);
Vector3 frontNormal = new Vector3(0.0f, 0.0f, 1.0f);
Vector3 backNormal = new Vector3(0.0f, 0.0f, -1.0f);
Vector3 topNormal = new Vector3(0.0f, 1.0f, 0.0f);
Vector3 bottomNormal = new Vector3(0.0f, -1.0f, 0.0f);
Vector3 leftNormal = new Vector3(-1.0f, 0.0f, 0.0f);
Vector3 rightNormal = new Vector3(1.0f, 0.0f, 0.0f);
var cubeVertices = new VertexPositionNormalTexture[36];
// Front face.
cubeVertices[0] = new VertexPositionNormalTexture(topLeftFront, frontNormal, textureTopLeft);
cubeVertices[1] = new VertexPositionNormalTexture(bottomLeftFront, frontNormal, textureBottomLeft);
cubeVertices[2] = new VertexPositionNormalTexture(topRightFront, frontNormal, textureTopRight);
cubeVertices[3] = new VertexPositionNormalTexture(bottomLeftFront, frontNormal, textureBottomLeft);
cubeVertices[4] = new VertexPositionNormalTexture(bottomRightFront, frontNormal, textureBottomRight);
cubeVertices[5] = new VertexPositionNormalTexture(topRightFront, frontNormal, textureTopRight);
// Back face.
cubeVertices[6] = new VertexPositionNormalTexture(topLeftBack, backNormal, textureTopRight);
cubeVertices[7] = new VertexPositionNormalTexture(topRightBack, backNormal, textureTopLeft);
cubeVertices[8] = new VertexPositionNormalTexture(bottomLeftBack, backNormal, textureBottomRight);
cubeVertices[9] = new VertexPositionNormalTexture(bottomLeftBack, backNormal, textureBottomRight);
cubeVertices[10] = new VertexPositionNormalTexture(topRightBack, backNormal, textureTopLeft);
cubeVertices[11] = new VertexPositionNormalTexture(bottomRightBack, backNormal, textureBottomLeft);
// Top face.
cubeVertices[12] = new VertexPositionNormalTexture(topLeftFront, topNormal, textureBottomLeft);
cubeVertices[13] = new VertexPositionNormalTexture(topRightBack, topNormal, textureTopRight);
cubeVertices[14] = new VertexPositionNormalTexture(topLeftBack, topNormal, textureTopLeft);
cubeVertices[15] = new VertexPositionNormalTexture(topLeftFront, topNormal, textureBottomLeft);
cubeVertices[16] = new VertexPositionNormalTexture(topRightFront, topNormal, textureBottomRight);
cubeVertices[17] = new VertexPositionNormalTexture(topRightBack, topNormal, textureTopRight);
// Bottom face.
cubeVertices[18] = new VertexPositionNormalTexture(bottomLeftFront, bottomNormal, textureTopLeft);
cubeVertices[19] = new VertexPositionNormalTexture(bottomLeftBack, bottomNormal, textureBottomLeft);
cubeVertices[20] = new VertexPositionNormalTexture(bottomRightBack, bottomNormal, textureBottomRight);
cubeVertices[21] = new VertexPositionNormalTexture(bottomLeftFront, bottomNormal, textureTopLeft);
cubeVertices[22] = new VertexPositionNormalTexture(bottomRightBack, bottomNormal, textureBottomRight);
cubeVertices[23] = new VertexPositionNormalTexture(bottomRightFront, bottomNormal, textureTopRight);
// Left face.
cubeVertices[24] = new VertexPositionNormalTexture(topLeftFront, leftNormal, textureTopRight);
cubeVertices[25] = new VertexPositionNormalTexture(bottomLeftBack, leftNormal, textureBottomLeft);
cubeVertices[26] = new VertexPositionNormalTexture(bottomLeftFront, leftNormal, textureBottomRight);
cubeVertices[27] = new VertexPositionNormalTexture(topLeftBack, leftNormal, textureTopLeft);
cubeVertices[28] = new VertexPositionNormalTexture(bottomLeftBack, leftNormal, textureBottomLeft);
cubeVertices[29] = new VertexPositionNormalTexture(topLeftFront, leftNormal, textureTopRight);
// Right face.
cubeVertices[30] = new VertexPositionNormalTexture(topRightFront, rightNormal, textureTopLeft);
cubeVertices[31] = new VertexPositionNormalTexture(bottomRightFront, rightNormal, textureBottomLeft);
cubeVertices[32] = new VertexPositionNormalTexture(bottomRightBack, rightNormal, textureBottomRight);
cubeVertices[33] = new VertexPositionNormalTexture(topRightBack, rightNormal, textureTopRight);
cubeVertices[34] = new VertexPositionNormalTexture(topRightFront, rightNormal, textureTopLeft);
cubeVertices[35] = new VertexPositionNormalTexture(bottomRightBack, rightNormal, textureBottomRight);
_vertexBuffer = new VertexBuffer(GraphicsDevice, vertexDeclaration, cubeVertices.Length, BufferUsage.None);
_vertexBuffer.SetData(cubeVertices);
}
private void Unitialize()
{
_vertexBuffer.Dispose();
_vertexBuffer = null;
vertexDeclaration.Dispose();
vertexDeclaration = null;
_basicEffect.Dispose();
_basicEffect = null;
}
private void Render(TimeSpan time)
{
GraphicsDevice.Clear(Color.SteelBlue);
GraphicsDevice.RasterizerState = RasterizerState.CullNone;
GraphicsDevice.SetVertexBuffer(_vertexBuffer);
// Rotate cube around up-axis.
_basicEffect.World = Matrix.CreateRotationY((float)time.Milliseconds / 1000 * MathHelper.TwoPi) * _worldMatrix;
foreach (var pass in _basicEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 12);
}
}
}
}