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); } } } }