www.pudn.com > IntroductionTo3DGameEngineDesign.rar > D3DApp.cs
//----------------------------------------------------------------------------- // File: D3DApp.cs // // Desc: Application class for the Direct3D samples framework library. // // Copyright (c) 2001-2002 Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- using System; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using Direct3D=Microsoft.DirectX.Direct3D; #region Enums for D3D Applications ////// Messages that can be used when displaying an error /// public enum ApplicationMessage { None, ApplicationMustExit, WarnSwitchToRef }; #endregion #region Various SampleExceptions ////// The default sample exception type /// public class SampleException : System.ApplicationException { ////// Return information about the exception /// public override string Message { get { string strMsg = string.Empty; strMsg = "Generic application error. Enable\n"; strMsg += "debug output for detailed information."; return strMsg; } } } ////// Exception informing user no compatible devices were found /// public class NoCompatibleDevicesException : SampleException { ////// Return information about the exception /// public override string Message { get { string strMsg = string.Empty; strMsg = "This sample cannot run in a desktop\n"; strMsg += "window with the current display settings.\n"; strMsg += "Please change your desktop settings to a\n"; strMsg += "16- or 32-bit display mode and re-run this\n"; strMsg += "sample."; return strMsg; } } } ////// An exception for when the ReferenceDevice is null /// public class NullReferenceDeviceException : SampleException { ////// Return information about the exception /// public override string Message { get { string strMsg = string.Empty; strMsg = "Warning: Nothing will be rendered.\n"; strMsg += "The reference rendering device was selected, but your\n"; strMsg += "computer only has a reduced-functionality reference device\n"; strMsg += "installed. Install the DirectX SDK to get the full\n"; strMsg += "reference device.\n"; return strMsg; } } } ////// An exception for when reset fails /// public class ResetFailedException : SampleException { ////// Return information about the exception /// public override string Message { get { string strMsg = string.Empty; strMsg = "Could not reset the Direct3D device."; return strMsg; } } } ////// The exception thrown when media couldn't be found /// public class MediaNotFoundException : SampleException { private string mediaFile; public MediaNotFoundException(string filename) : base() { mediaFile = filename; } public MediaNotFoundException() : base() { mediaFile = string.Empty; } ////// Return information about the exception /// public override string Message { get { string strMsg = string.Empty; strMsg = "Could not load required media."; if (mediaFile.Length > 0) strMsg += string.Format("\r\nFile: {0}", mediaFile); return strMsg; } } } #endregion ////// The base class for all the graphics (D3D) samples, it derives from windows forms /// public class GraphicsSample : System.Windows.Forms.Form { #region Menu Information // The menu items that *all* samples will need protected System.Windows.Forms.MainMenu mnuMain; protected System.Windows.Forms.MenuItem mnuFile; private System.Windows.Forms.MenuItem mnuGo; private System.Windows.Forms.MenuItem mnuSingle; private System.Windows.Forms.MenuItem mnuBreak1; private System.Windows.Forms.MenuItem mnuChange; private System.Windows.Forms.MenuItem mnuBreak2; protected System.Windows.Forms.MenuItem mnuExit; #endregion // The window we will render too private System.Windows.Forms.Control ourRenderTarget; // Should we use the default windows protected bool isUsingMenus = true; public bool m_bTerminate = false; // We need to keep track of our enumeration settings protected D3DEnumeration enumerationSettings = new D3DEnumeration(); protected D3DSettings graphicsSettings = new D3DSettings(); private bool isMaximized = false; // Are we maximized? private bool isHandlingSizeChanges = true; // Are we handling size changes? private float lastTime = 0.0f; // The last time private int frames = 0; // Number of rames since our last update private int appPausedCount = 0; // How many times has the app been paused (and when can it resume)? // Internal variables for the state of the app protected bool windowed; protected bool active; protected bool ready; protected bool hasFocus; // Internal variables used for timing protected bool frameMoving; protected bool singleStep; // Main objects used for creating and rendering the 3D scene protected PresentParameters presentParams = new PresentParameters(); // Parameters for CreateDevice/Reset protected Device device; // The rendering device protected RenderStates renderState; protected SamplerStates sampleState; protected TextureStates textureStates; private Caps graphicsCaps; // Caps for the device protected Caps Caps { get { return graphicsCaps; } } private CreateFlags behavior; // Indicate sw or hw vertex processing protected BehaviorFlags BehaviorFlags { get { return new BehaviorFlags(behavior); } } protected System.Windows.Forms.Control RenderTarget { get { return ourRenderTarget; } set { ourRenderTarget = value; } } // Variables for timing protected float appTime; // Current time in seconds protected float elapsedTime; // Time elapsed since last frame protected float framePerSecond; // Instanteous frame rate protected string deviceStats;// String to hold D3D device stats protected string frameStats; // String to hold frame stats private bool deviceLost = false; // Overridable variables for the app private int minDepthBits; // Minimum number of bits needed in depth buffer protected int MinDepthBits { get { return minDepthBits; } set { minDepthBits = value; enumerationSettings.AppMinDepthBits = value;} } private int minStencilBits; // Minimum number of bits needed in stencil buffer protected int MinStencilBits { get { return minStencilBits; } set { minStencilBits = value; enumerationSettings.AppMinStencilBits = value;} } protected bool showCursorWhenFullscreen; // Whether to show cursor when fullscreen protected bool clipCursorWhenFullscreen; // Whether to limit cursor pos when fullscreen protected bool startFullscreen; // Whether to start up the app in fullscreen mode private System.Drawing.Size storedSize; // Overridable functions for the 3D scene created by the app protected virtual bool ConfirmDevice(Caps caps, VertexProcessingType vertexProcessingType, Format format) { return true; } protected virtual void OneTimeSceneInitialization() { /* Do Nothing */ } protected virtual void InitializeDeviceObjects() { /* Do Nothing */ } protected virtual void RestoreDeviceObjects(System.Object sender, System.EventArgs e) { /* Do Nothing */ } protected virtual void FrameMove() { /* Do Nothing */ } protected virtual void Render() { /* Do Nothing */ } protected virtual void InvalidateDeviceObjects(System.Object sender, System.EventArgs e) { /* Do Nothing */ } protected virtual void DeleteDeviceObjects(System.Object sender, System.EventArgs e) { /* Do Nothing */ } ////// Constructor /// public GraphicsSample() { device = null; active = false; ready = false; hasFocus = false; behavior = 0; ourRenderTarget = this; frameMoving = true; singleStep = false; framePerSecond = 0.0f; deviceStats = null; frameStats = null; this.Text = "Game Engine Sample"; this.ClientSize = new System.Drawing.Size(800,600); this.KeyPreview = true; minDepthBits = 16; minStencilBits = 0; showCursorWhenFullscreen = false; startFullscreen = false; // When clipCursorWhenFullscreen is TRUE, the cursor is limited to // the device window when the app goes fullscreen. This prevents users // from accidentally clicking outside the app window on a multimon system. // This flag is turned off by default for debug builds, since it makes // multimon debugging difficult. #if (DEBUG) clipCursorWhenFullscreen = false; #else clipCursorWhenFullscreen = true; #endif InitializeComponent(); } ////// Picks the best graphics device, and initializes it /// ///true if a good device was found, false otherwise public bool CreateGraphicsSample() { enumerationSettings.ConfirmDeviceCallback = new D3DEnumeration.ConfirmDeviceCallbackType(this.ConfirmDevice); enumerationSettings.Enumerate(); if (ourRenderTarget.Cursor == null) { // Set up a default cursor ourRenderTarget.Cursor = System.Windows.Forms.Cursors.Default; } // if our render target is the main window and we haven't said // ignore the menus, add our menu if ((ourRenderTarget == this) && (isUsingMenus)) this.Menu = mnuMain; try { ChooseInitialSettings(); // Initialize the application timer DXUtil.Timer( TIMER.START ); // Initialize the 3D environment for the app InitializeEnvironment(); // Initialize the app's custom scene stuff OneTimeSceneInitialization(); } catch (SampleException d3de) { HandleSampleException( d3de, ApplicationMessage.ApplicationMustExit ); return false; } catch { HandleSampleException( new SampleException(), ApplicationMessage.ApplicationMustExit ); return false; } // The app is ready to go ready = true; return true; } ////// Sets up graphicsSettings with best available windowed mode, subject to /// the doesRequireHardware and doesRequireReference constraints. /// /// Does the device require hardware support /// Does the device require the ref device ///true if a mode is found, false otherwise public bool FindBestWindowedMode(bool doesRequireHardware, bool doesRequireReference) { // Get display mode of primary adapter (which is assumed to be where the window // will appear) DisplayMode primaryDesktopDisplayMode = Manager.Adapters[0].CurrentDisplayMode; GraphicsAdapterInfo bestAdapterInfo = null; GraphicsDeviceInfo bestDeviceInfo = null; DeviceCombo bestDeviceCombo = null; foreach (GraphicsAdapterInfo adapterInfo in enumerationSettings.AdapterInfoList) { foreach (GraphicsDeviceInfo deviceInfo in adapterInfo.DeviceInfoList) { if (doesRequireHardware && deviceInfo.DevType != DeviceType.Hardware) continue; if (doesRequireReference && deviceInfo.DevType != DeviceType.Reference) continue; foreach (DeviceCombo deviceCombo in deviceInfo.DeviceComboList) { bool adapterMatchesBackBuffer = (deviceCombo.BackBufferFormat == deviceCombo.AdapterFormat); if (!deviceCombo.IsWindowed) continue; if (deviceCombo.AdapterFormat != primaryDesktopDisplayMode.Format) continue; // If we haven't found a compatible DeviceCombo yet, or if this set // is better (because it's a HAL, and/or because formats match better), // save it if (bestDeviceCombo == null || bestDeviceCombo.DevType != DeviceType.Hardware && deviceInfo.DevType == DeviceType.Hardware || deviceCombo.DevType == DeviceType.Hardware && adapterMatchesBackBuffer ) { bestAdapterInfo = adapterInfo; bestDeviceInfo = deviceInfo; bestDeviceCombo = deviceCombo; if (deviceInfo.DevType == DeviceType.Hardware && adapterMatchesBackBuffer) { // This windowed device combo looks great -- take it goto EndWindowedDeviceComboSearch; } // Otherwise keep looking for a better windowed device combo } } } } EndWindowedDeviceComboSearch: if (bestDeviceCombo == null ) return false; graphicsSettings.WindowedAdapterInfo = bestAdapterInfo; graphicsSettings.WindowedDeviceInfo = bestDeviceInfo; graphicsSettings.WindowedDeviceCombo = bestDeviceCombo; graphicsSettings.IsWindowed = true; graphicsSettings.WindowedDisplayMode = primaryDesktopDisplayMode; graphicsSettings.WindowedWidth = ourRenderTarget.ClientRectangle.Right - ourRenderTarget.ClientRectangle.Left; graphicsSettings.WindowedHeight = ourRenderTarget.ClientRectangle.Bottom - ourRenderTarget.ClientRectangle.Top; if (enumerationSettings.AppUsesDepthBuffer) graphicsSettings.WindowedDepthStencilBufferFormat = (DepthFormat)bestDeviceCombo.DepthStencilFormatList[0]; graphicsSettings.WindowedMultisampleType = (MultiSampleType)bestDeviceCombo.MultiSampleTypeList[0]; graphicsSettings.WindowedMultisampleQuality = 0; graphicsSettings.WindowedVertexProcessingType = (VertexProcessingType)bestDeviceCombo.VertexProcessingTypeList[0]; graphicsSettings.WindowedPresentInterval = (PresentInterval)bestDeviceCombo.PresentIntervalList[0]; return true; } ////// Sets up graphicsSettings with best available fullscreen mode, subject to /// the doesRequireHardware and doesRequireReference constraints. /// /// Does the device require hardware support /// Does the device require the ref device ///true if a mode is found, false otherwise public bool FindBestFullscreenMode(bool doesRequireHardware, bool doesRequireReference) { // For fullscreen, default to first HAL DeviceCombo that supports the current desktop // display mode, or any display mode if HAL is not compatible with the desktop mode, or // non-HAL if no HAL is available DisplayMode adapterDesktopDisplayMode = new DisplayMode(); DisplayMode bestAdapterDesktopDisplayMode = new DisplayMode(); DisplayMode bestDisplayMode = new DisplayMode(); bestAdapterDesktopDisplayMode.Width = 0; bestAdapterDesktopDisplayMode.Height = 0; bestAdapterDesktopDisplayMode.Format = 0; bestAdapterDesktopDisplayMode.RefreshRate = 0; GraphicsAdapterInfo bestAdapterInfo = null; GraphicsDeviceInfo bestDeviceInfo = null; DeviceCombo bestDeviceCombo = null; foreach (GraphicsAdapterInfo adapterInfo in enumerationSettings.AdapterInfoList) { adapterDesktopDisplayMode = Manager.Adapters[adapterInfo.AdapterOrdinal].CurrentDisplayMode; foreach (GraphicsDeviceInfo deviceInfo in adapterInfo.DeviceInfoList) { if (doesRequireHardware && deviceInfo.DevType != DeviceType.Hardware) continue; if (doesRequireReference && deviceInfo.DevType != DeviceType.Reference) continue; foreach (DeviceCombo deviceCombo in deviceInfo.DeviceComboList) { bool adapterMatchesBackBuffer = (deviceCombo.BackBufferFormat == deviceCombo.AdapterFormat); bool adapterMatchesDesktop = (deviceCombo.AdapterFormat == adapterDesktopDisplayMode.Format); if (deviceCombo.IsWindowed) continue; // If we haven't found a compatible set yet, or if this set // is better (because it's a HAL, and/or because formats match better), // save it if (bestDeviceCombo == null || bestDeviceCombo.DevType != DeviceType.Hardware && deviceInfo.DevType == DeviceType.Hardware || bestDeviceCombo.DevType == DeviceType.Hardware && bestDeviceCombo.AdapterFormat != adapterDesktopDisplayMode.Format && adapterMatchesDesktop || bestDeviceCombo.DevType == DeviceType.Hardware && adapterMatchesDesktop && adapterMatchesBackBuffer ) { bestAdapterDesktopDisplayMode = adapterDesktopDisplayMode; bestAdapterInfo = adapterInfo; bestDeviceInfo = deviceInfo; bestDeviceCombo = deviceCombo; if (deviceInfo.DevType == DeviceType.Hardware && adapterMatchesDesktop && adapterMatchesBackBuffer) { // This fullscreen device combo looks great -- take it goto EndFullscreenDeviceComboSearch; } // Otherwise keep looking for a better fullscreen device combo } } } } EndFullscreenDeviceComboSearch: if (bestDeviceCombo == null) return false; // Need to find a display mode on the best adapter that uses pBestDeviceCombo->AdapterFormat // and is as close to bestAdapterDesktopDisplayMode's res as possible bestDisplayMode.Width = 0; bestDisplayMode.Height = 0; bestDisplayMode.Format = 0; bestDisplayMode.RefreshRate = 0; foreach( DisplayMode displayMode in bestAdapterInfo.DisplayModeList ) { if( displayMode.Format != bestDeviceCombo.AdapterFormat ) continue; if( displayMode.Width == bestAdapterDesktopDisplayMode.Width && displayMode.Height == bestAdapterDesktopDisplayMode.Height && displayMode.RefreshRate == bestAdapterDesktopDisplayMode.RefreshRate ) { // found a perfect match, so stop bestDisplayMode = displayMode; break; } else if( displayMode.Width == bestAdapterDesktopDisplayMode.Width && displayMode.Height == bestAdapterDesktopDisplayMode.Height && displayMode.RefreshRate > bestDisplayMode.RefreshRate ) { // refresh rate doesn't match, but width/height match, so keep this // and keep looking bestDisplayMode = displayMode; } else if( bestDisplayMode.Width == bestAdapterDesktopDisplayMode.Width ) { // width matches, so keep this and keep looking bestDisplayMode = displayMode; } else if( bestDisplayMode.Width == 0 ) { // we don't have anything better yet, so keep this and keep looking bestDisplayMode = displayMode; } } graphicsSettings.FullscreenAdapterInfo = bestAdapterInfo; graphicsSettings.FullscreenDeviceInfo = bestDeviceInfo; graphicsSettings.FullscreenDeviceCombo = bestDeviceCombo; graphicsSettings.IsWindowed = false; graphicsSettings.FullscreenDisplayMode = bestDisplayMode; if (enumerationSettings.AppUsesDepthBuffer) graphicsSettings.FullscreenDepthStencilBufferFormat = (DepthFormat)bestDeviceCombo.DepthStencilFormatList[0]; graphicsSettings.FullscreenMultisampleType = (MultiSampleType)bestDeviceCombo.MultiSampleTypeList[0]; graphicsSettings.FullscreenMultisampleQuality = 0; graphicsSettings.FullscreenVertexProcessingType = (VertexProcessingType)bestDeviceCombo.VertexProcessingTypeList[0]; graphicsSettings.FullscreenPresentInterval = (PresentInterval)bestDeviceCombo.PresentIntervalList[0]; return true; } ////// Choose the initial settings for the application /// ///true if the settings were initialized public bool ChooseInitialSettings() { bool foundFullscreenMode = FindBestFullscreenMode(false, false); bool foundWindowedMode = FindBestWindowedMode(false, false); if (startFullscreen && foundFullscreenMode) graphicsSettings.IsWindowed = false; if (!foundFullscreenMode && !foundWindowedMode) throw new NoCompatibleDevicesException(); return (foundFullscreenMode || foundWindowedMode); } ////// Build presentation parameters from the current settings /// public void BuildPresentParamsFromSettings() { presentParams.Windowed = graphicsSettings.IsWindowed; presentParams.BackBufferCount = 1; presentParams.MultiSample = graphicsSettings.MultisampleType; presentParams.MultiSampleQuality = graphicsSettings.MultisampleQuality; presentParams.SwapEffect = SwapEffect.Discard; presentParams.EnableAutoDepthStencil = enumerationSettings.AppUsesDepthBuffer; presentParams.AutoDepthStencilFormat = graphicsSettings.DepthStencilBufferFormat; presentParams.PresentFlag = PresentFlag.None; if( windowed ) { presentParams.BackBufferWidth = ourRenderTarget.ClientRectangle.Right - ourRenderTarget.ClientRectangle.Left; presentParams.BackBufferHeight = ourRenderTarget.ClientRectangle.Bottom - ourRenderTarget.ClientRectangle.Top; presentParams.BackBufferFormat = graphicsSettings.DeviceCombo.BackBufferFormat; presentParams.FullScreenRefreshRateInHz = 0; presentParams.PresentationInterval = PresentInterval.Immediate; presentParams.DeviceWindow = ourRenderTarget; } else { presentParams.BackBufferWidth = graphicsSettings.DisplayMode.Width; presentParams.BackBufferHeight = graphicsSettings.DisplayMode.Height; presentParams.BackBufferFormat = graphicsSettings.DeviceCombo.BackBufferFormat; presentParams.FullScreenRefreshRateInHz = graphicsSettings.DisplayMode.RefreshRate; presentParams.PresentationInterval = graphicsSettings.PresentInterval; presentParams.DeviceWindow = this; } } //----------------------------------------------------------------------------- // Name: InitializeEnvironment() // Desc: Initialize the graphics environment //----------------------------------------------------------------------------- public void InitializeEnvironment() { GraphicsAdapterInfo adapterInfo = graphicsSettings.AdapterInfo; GraphicsDeviceInfo deviceInfo = graphicsSettings.DeviceInfo; windowed = graphicsSettings.IsWindowed; // Set up the presentation parameters BuildPresentParamsFromSettings(); if (deviceInfo.Caps.PrimitiveMiscCaps.IsNullReference ) { // Warn user about null ref device that can't render anything HandleSampleException( new NullReferenceDeviceException(), ApplicationMessage.None ); } CreateFlags createFlags = new CreateFlags(); if (graphicsSettings.VertexProcessingType == VertexProcessingType.Software) createFlags = CreateFlags.SoftwareVertexProcessing; else if (graphicsSettings.VertexProcessingType == VertexProcessingType.Mixed) createFlags = CreateFlags.MixedVertexProcessing; else if (graphicsSettings.VertexProcessingType == VertexProcessingType.Hardware) createFlags = CreateFlags.HardwareVertexProcessing; else if (graphicsSettings.VertexProcessingType == VertexProcessingType.PureHardware) { createFlags = CreateFlags.HardwareVertexProcessing | CreateFlags.PureDevice; } else throw new ApplicationException(); createFlags |= CreateFlags.MultiThreaded; try { // Create the device device = new Device(graphicsSettings.AdapterOrdinal, graphicsSettings.DevType, windowed ? ourRenderTarget : this , createFlags, presentParams); // Cache our local objects renderState = device.RenderState; sampleState = device.SamplerState; textureStates = device.TextureState; // When moving from fullscreen to windowed mode, it is important to // adjust the window size after recreating the device rather than // beforehand to ensure that you get the window size you want. For // example, when switching from 640x480 fullscreen to windowed with // a 1000x600 window on a 1024x768 desktop, it is impossible to set // the window size to 1000x600 until after the display mode has // changed to 1024x768, because windows cannot be larger than the // desktop. if( windowed ) { // Make sure main window isn't topmost, so error message is visible System.Drawing.Size currentClientSize = this.ClientSize; this.Size = this.ClientSize; this.ClientSize = currentClientSize; this.SendToBack(); this.BringToFront(); } // Store device Caps graphicsCaps = device.DeviceCaps; behavior = createFlags; // Store device description if( deviceInfo.DevType == DeviceType.Reference ) deviceStats = "REF"; else if( deviceInfo.DevType == DeviceType.Hardware ) deviceStats = "HAL"; else if( deviceInfo.DevType == DeviceType.Software ) deviceStats = "SW"; BehaviorFlags behaviorFlags = new BehaviorFlags(createFlags); if( (behaviorFlags.HardwareVertexProcessing) && (behaviorFlags.PureDevice) ) { if( deviceInfo.DevType == DeviceType.Hardware ) deviceStats += " (pure hw vp)"; else deviceStats += " (simulated pure hw vp)"; } else if( (behaviorFlags.HardwareVertexProcessing) ) { if( deviceInfo.DevType == DeviceType.Hardware ) deviceStats += " (hw vp)"; else deviceStats += " (simulated hw vp)"; } else if( behaviorFlags.MixedVertexProcessing) { if( deviceInfo.DevType == DeviceType.Hardware ) deviceStats += " (mixed vp)"; else deviceStats += " (simulated mixed vp)"; } else if( behaviorFlags.SoftwareVertexProcessing ) { deviceStats += " (sw vp)"; } if( deviceInfo.DevType == DeviceType.Hardware ) { deviceStats += ": "; deviceStats += adapterInfo.AdapterDetails.Description; } // Set up the fullscreen cursor if( showCursorWhenFullscreen && !windowed ) { System.Windows.Forms.Cursor ourCursor = this.Cursor; device.SetCursor(ourCursor, true); device.ShowCursor(true); } // Confine cursor to fullscreen window if( clipCursorWhenFullscreen ) { if (!windowed ) { System.Drawing.Rectangle rcWindow = this.ClientRectangle; } } // Setup the event handlers for our device device.DeviceLost += new System.EventHandler(this.InvalidateDeviceObjects); device.DeviceReset += new System.EventHandler(this.RestoreDeviceObjects); device.Disposing += new System.EventHandler(this.DeleteDeviceObjects); device.DeviceResizing += new System.ComponentModel.CancelEventHandler(this.EnvironmentResized); // Initialize the app's device-dependent objects try { InitializeDeviceObjects(); RestoreDeviceObjects(null, null); active = true; return; } catch { // Cleanup before we try again InvalidateDeviceObjects(null, null); DeleteDeviceObjects(null, null); device.Dispose(); device = null; if (this.Disposing) return; } } catch { // If that failed, fall back to the reference rasterizer if( deviceInfo.DevType == DeviceType.Hardware ) { if (FindBestWindowedMode(false, true)) { windowed = true; // Make sure main window isn't topmost, so error message is visible System.Drawing.Size currentClientSize = this.ClientSize; this.Size = this.ClientSize; this.ClientSize = currentClientSize; this.SendToBack(); this.BringToFront(); // Let the user know we are switching from HAL to the reference rasterizer HandleSampleException( null, ApplicationMessage.WarnSwitchToRef); InitializeEnvironment(); } } } } ////// Displays sample exceptions to the user /// /// The exception that was thrown /// Extra information on how to handle the exception public void HandleSampleException( SampleException e, ApplicationMessage Type ) { // Build a message to display to the user string strMsg = string.Empty; if (e != null) strMsg = e.Message; if( ApplicationMessage.ApplicationMustExit == Type ) { strMsg += "\n\nThis sample will now exit."; System.Windows.Forms.MessageBox.Show(strMsg, this.Text, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); // Close the window, which shuts down the app if( this.IsHandleCreated ) this.Close(); } else { if( ApplicationMessage.WarnSwitchToRef == Type ) strMsg = "\n\nSwitching to the reference rasterizer,\n"; strMsg += "a software device that implements the entire\n"; strMsg += "Direct3D feature set, but runs very slowly."; System.Windows.Forms.MessageBox.Show(strMsg, this.Text, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information); } } ////// Fired when our environment was resized /// /// the device that's resizing our environment /// Set the cancel member to true to turn off automatic device reset public void EnvironmentResized(object sender, System.ComponentModel.CancelEventArgs e) { // Check to see if we're minimizing and our rendering object // is not the form, if so, cancel the resize if ((ourRenderTarget != this) && (this.WindowState == System.Windows.Forms.FormWindowState.Minimized)) e.Cancel = true; // Set up the fullscreen cursor if( showCursorWhenFullscreen && !windowed ) { System.Windows.Forms.Cursor ourCursor = this.Cursor; device.SetCursor(ourCursor, true); device.ShowCursor(true); } // If the app is paused, trigger the rendering of the current frame if( false == frameMoving ) { singleStep = true; DXUtil.Timer( TIMER.START ); DXUtil.Timer( TIMER.STOP ); } } ////// Called when user toggles between fullscreen mode and windowed mode /// public void ToggleFullscreen() { int AdapterOrdinalOld = graphicsSettings.AdapterOrdinal; DeviceType DevTypeOld = graphicsSettings.DevType; isHandlingSizeChanges = false; ready = false; // Toggle the windowed state windowed = !windowed; // Save our maximized settings.. if (!windowed && isMaximized) { this.WindowState = System.Windows.Forms.FormWindowState.Normal; } graphicsSettings.IsWindowed = windowed; // If AdapterOrdinal and DevType are the same, we can just do a Reset(). // If they've changed, we need to do a complete device teardown/rebuild. if (graphicsSettings.AdapterOrdinal == AdapterOrdinalOld && graphicsSettings.DevType == DevTypeOld) { // Resize the 3D device try { BuildPresentParamsFromSettings(); device.Reset(presentParams); EnvironmentResized(device, null); } catch { if( windowed ) ForceWindowed(); else throw new Exception(); } } else { device.Dispose(); device = null; InitializeEnvironment(); } // When moving from fullscreen to windowed mode, it is important to // adjust the window size after resetting the device rather than // beforehand to ensure that you get the window size you want. For // example, when switching from 640x480 fullscreen to windowed with // a 1000x600 window on a 1024x768 desktop, it is impossible to set // the window size to 1000x600 until after the display mode has // changed to 1024x768, because windows cannot be larger than the // desktop. if( windowed ) { // We were maximized, restore that state if (isMaximized) { this.WindowState = System.Windows.Forms.FormWindowState.Maximized; } this.ClientSize = storedSize; this.SendToBack(); this.BringToFront(); } isHandlingSizeChanges = true; ready = true; } ////// Switch to a windowed mode, even if that means picking a new device and/or adapter /// public void ForceWindowed() { if( windowed ) return; if( !FindBestWindowedMode(false, false) ) { return; } windowed = true; // Now destroy the current 3D device objects, then reinitialize ready = false; // Release display objects, so a new device can be created device.Dispose(); device = null; // Create the new device try { InitializeEnvironment(); } catch (SampleException e) { HandleSampleException( e,ApplicationMessage.ApplicationMustExit ); } catch { HandleSampleException( new SampleException(),ApplicationMessage.ApplicationMustExit ); } ready = true; } ////// Called when our sample has nothing else to do, and it's time to render /// private void FullRender() { if ( m_bTerminate ) this.Close(); // Render a frame during idle time (no messages are waiting) if( active && ready ) { try { if (deviceLost) { // Yield some CPU time to other processes System.Threading.Thread.Sleep(100); // 100 milliseconds } // Render a frame during idle time if (active) { Render3DEnvironment(); } } catch (Exception ee) { System.Windows.Forms.MessageBox.Show("An exception has occurred. This sample must exit.\r\n\r\n" + ee.ToString(), "Exception", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information); this.Close(); } } } ////// Run the simulation /// public void Run() { // Now we're ready to recieve and process Windows messages. this.Show(); while (this.Created) { FullRender(); System.Windows.Forms.Application.DoEvents(); } } ////// Draws the scene /// public void Render3DEnvironment() { if (deviceLost) { try { // Test the cooperative level to see if it's okay to render device.TestCooperativeLevel(); } catch (DeviceLostException) { // If the device was lost, do not render until we get it back return; } catch (DeviceNotResetException) { // Check if the device needs to be resized. // If we are windowed, read the desktop mode and use the same format for // the back buffer if( windowed ) { GraphicsAdapterInfo adapterInfo = graphicsSettings.AdapterInfo; graphicsSettings.WindowedDisplayMode = Manager.Adapters[adapterInfo.AdapterOrdinal].CurrentDisplayMode; presentParams.BackBufferFormat = graphicsSettings.WindowedDisplayMode.Format; } // Reset the device and resize it device.Reset(device.PresentationParameters); EnvironmentResized(device, null); } deviceLost = false; } // Get the app's time, in seconds. Skip rendering if no time elapsed float fAppTime = DXUtil.Timer( TIMER.GETAPPTIME ); float fElapsedAppTime = DXUtil.Timer( TIMER.GETELAPSEDTIME ); if( ( 0.0f == fElapsedAppTime ) && frameMoving ) return; // FrameMove (animate) the scene if( frameMoving || singleStep ) { // Store the time for the app appTime = fAppTime; elapsedTime = fElapsedAppTime; // Frame move the scene FrameMove(); singleStep = false; } // Render the scene as normal Render(); UpdateStats(); try { // Show the frame on the primary surface. device.Present(); } catch(DeviceLostException) { deviceLost = true; } } ////// Update the various statistics the simulation keeps track of /// public void UpdateStats() { // Keep track of the frame count float time = DXUtil.Timer( TIMER.GETABSOLUTETIME ); ++frames; // Update the scene stats once per second if( time - lastTime > 1.0f ) { framePerSecond = frames / (time - lastTime); lastTime = time; frames = 0; string strFmt; DisplayMode mode = Manager.Adapters[graphicsSettings.AdapterOrdinal].CurrentDisplayMode; if (mode.Format == device.PresentationParameters.BackBufferFormat) { strFmt = mode.Format.ToString(); } else { strFmt = String.Format("backbuf {0}, adapter {1}", device.PresentationParameters.BackBufferFormat.ToString(), mode.Format.ToString()); } string strDepthFmt; if( enumerationSettings.AppUsesDepthBuffer ) { strDepthFmt = String.Format( " ({0})", graphicsSettings.DepthStencilBufferFormat.ToString()); } else { // No depth buffer strDepthFmt = ""; } string strMultiSample; switch( graphicsSettings.MultisampleType ) { case Direct3D.MultiSampleType.NonMaskable: strMultiSample = " (NonMaskable Multisample)"; break; case Direct3D.MultiSampleType.TwoSamples: strMultiSample = " (2x Multisample)"; break; case Direct3D.MultiSampleType.ThreeSamples: strMultiSample = " (3x Multisample)"; break; case Direct3D.MultiSampleType.FourSamples: strMultiSample = " (4x Multisample)"; break; case Direct3D.MultiSampleType.FiveSamples: strMultiSample = " (5x Multisample)"; break; case Direct3D.MultiSampleType.SixSamples: strMultiSample = " (6x Multisample)"; break; case Direct3D.MultiSampleType.SevenSamples: strMultiSample = " (7x Multisample)"; break; case Direct3D.MultiSampleType.EightSamples: strMultiSample = " (8x Multisample)"; break; case Direct3D.MultiSampleType.NineSamples: strMultiSample = " (9x Multisample)"; break; case Direct3D.MultiSampleType.TenSamples: strMultiSample = " (10x Multisample)"; break; case Direct3D.MultiSampleType.ElevenSamples: strMultiSample = " (11x Multisample)"; break; case Direct3D.MultiSampleType.TwelveSamples: strMultiSample = " (12x Multisample)"; break; case Direct3D.MultiSampleType.ThirteenSamples: strMultiSample = " (13x Multisample)"; break; case Direct3D.MultiSampleType.FourteenSamples: strMultiSample = " (14x Multisample)"; break; case Direct3D.MultiSampleType.FifteenSamples: strMultiSample = " (15x Multisample)"; break; case Direct3D.MultiSampleType.SixteenSamples: strMultiSample = " (16x Multisample)"; break; default: strMultiSample = string.Empty; break; } frameStats = String.Format("{0} fps ({1}x{2}), {3}{4}{5}", framePerSecond.ToString("f2"), device.PresentationParameters.BackBufferWidth, device.PresentationParameters.BackBufferHeight, strFmt, strDepthFmt, strMultiSample); } } ////// Called in to toggle the pause state of the app. /// /// true if the simulation should pause public void Pause( bool pause ) { appPausedCount += (int)( pause ? +1 : -1 ); ready = ( (appPausedCount > 0) ? false : true ); // Handle the first pause request (of many, nestable pause requests) if( pause && ( 1 == appPausedCount ) ) { // Stop the scene from animating if( frameMoving ) DXUtil.Timer( TIMER.STOP ); } if( 0 == appPausedCount ) { // Restart the timers if( frameMoving ) DXUtil.Timer( TIMER.START ); } } ////// Set our variables to not active and not ready /// public void CleanupEnvironment() { active = false; ready = false; if (device != null) device.Dispose(); device = null; } #region Menu EventHandlers ////// Prepares the simulation for a new device being selected /// public void UserSelectNewDevice(object sender, EventArgs e) { // Prompt the user to select a new device or mode if( active && ready ) { Pause(true); DoSelectNewDevice(); Pause(false); } } ////// Displays a dialog so the user can select a new adapter, device, or /// display mode, and then recreates the 3D environment if needed /// private void DoSelectNewDevice() { // Can't display dialogs in fullscreen mode if( windowed == false ) { try { ToggleFullscreen(); } catch { HandleSampleException( new ResetFailedException(), ApplicationMessage.ApplicationMustExit); return; } } // Make sure the main form is in the background this.SendToBack(); D3DSettingsForm settingsForm = new D3DSettingsForm(enumerationSettings, graphicsSettings); System.Windows.Forms.DialogResult result = settingsForm.ShowDialog(null); if( result != System.Windows.Forms.DialogResult.OK) { return; } graphicsSettings = settingsForm.settings; windowed = graphicsSettings.IsWindowed; // Release display objects, so a new device can be created device.Dispose(); device = null; // Inform the display class of the change. It will internally // re-create valid surfaces, a d3ddevice, etc. try { InitializeEnvironment(); } catch(SampleException d3de) { HandleSampleException( d3de, ApplicationMessage.ApplicationMustExit ); } catch { HandleSampleException( new SampleException(), ApplicationMessage.ApplicationMustExit ); } // If the app is paused, trigger the rendering of the current frame if( false == frameMoving ) { singleStep = true; DXUtil.Timer( TIMER.START ); DXUtil.Timer( TIMER.STOP ); } } ////// Will start (or stop) simulation /// private void ToggleStart(object sender, EventArgs e) { //Toggle frame movement frameMoving = !frameMoving; DXUtil.Timer( frameMoving ? TIMER.START : TIMER.STOP); } ////// Will single step the simulation /// private void SingleStep(object sender, EventArgs e) { // Single-step frame movement if( false == frameMoving ) DXUtil.Timer( TIMER.ADVANCE ); else DXUtil.Timer( TIMER.STOP ); frameMoving = false; singleStep = true; } ////// Will end the simulation /// private void ExitSample(object sender, EventArgs e) { this.Close(); } #endregion #region WinForms Overrides ////// Clean up any resources /// protected override void Dispose(bool disposing) { CleanupEnvironment(); mnuMain.Dispose(); base.Dispose(disposing); } ////// Handle any key presses /// protected override void OnKeyPress(System.Windows.Forms.KeyPressEventArgs e) { // Check for our shortcut keys (Escape to quit) if ((byte)e.KeyChar == (byte)(int)System.Windows.Forms.Keys.Escape) { mnuExit.PerformClick(); e.Handled = true; } // Allow the control to handle the keystroke now base.OnKeyPress(e); } ////// Handle system keystrokes (ie, alt-enter) /// protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e) { char tstr = (char)(e.KeyValue); if ( GameEngine.Console.IsVisible && e.KeyData == System.Windows.Forms.Keys.Return ) { GameEngine.Console.ProcessEntry(); } if ( e.KeyData == System.Windows.Forms.Keys.F12 ) { GameEngine.Console.ToggleState(); } else if ( GameEngine.Console.IsVisible && (e.KeyData == System.Windows.Forms.Keys.Space || ( e.KeyData >= System.Windows.Forms.Keys.A && e.KeyData <= System.Windows.Forms.Keys.Z ) || ( e.KeyData >= System.Windows.Forms.Keys.D0 && e.KeyData <= System.Windows.Forms.Keys.D9 ) ) ) { GameEngine.Console.AddCharacterToEntryLine( tstr ); } else if ( GameEngine.Console.IsVisible && e.KeyData == System.Windows.Forms.Keys.OemPeriod ) { GameEngine.Console.AddCharacterToEntryLine( '.' ); } else if ( GameEngine.Console.IsVisible && e.KeyData == System.Windows.Forms.Keys.OemMinus ) { GameEngine.Console.AddCharacterToEntryLine( '-' ); } else if ( GameEngine.Console.IsVisible && e.KeyData == System.Windows.Forms.Keys.Back ) { GameEngine.Console.Backspace(); } if ( (e.Alt) && (e.KeyCode == System.Windows.Forms.Keys.Return)) { // Toggle the fullscreen/window mode if( active && ready ) { Pause( true ); try { ToggleFullscreen(); Pause( false ); return; } catch { HandleSampleException( new ResetFailedException(), ApplicationMessage.ApplicationMustExit); } finally { e.Handled = true; } } } // Allow the control to handle the keystroke now // base.OnKeyDown(e); } ////// Winforms generated code for initializing the form /// private void InitializeComponent() { // // GraphicsSample // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.MinimumSize = new System.Drawing.Size(100, 100); #region MenuCreation/etc this.mnuMain = new System.Windows.Forms.MainMenu(); this.mnuFile = new System.Windows.Forms.MenuItem(); this.mnuGo = new System.Windows.Forms.MenuItem(); this.mnuSingle = new System.Windows.Forms.MenuItem(); this.mnuBreak1 = new System.Windows.Forms.MenuItem(); this.mnuChange = new System.Windows.Forms.MenuItem(); this.mnuBreak2 = new System.Windows.Forms.MenuItem(); this.mnuExit = new System.Windows.Forms.MenuItem(); // // mainMenu1 // this.mnuMain.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.mnuFile}); // // mnuFile // this.mnuFile.Index = 0; this.mnuFile.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.mnuGo, this.mnuSingle, this.mnuBreak1, this.mnuChange, this.mnuBreak2, this.mnuExit}); this.mnuFile.Text = "&File"; // // mnuGo // this.mnuGo.Index = 0; this.mnuGo.Text = "&Go/stop\tEnter"; this.mnuGo.Click += new System.EventHandler(this.ToggleStart); // // mnuSingle // this.mnuSingle.Index = 1; this.mnuSingle.Text = "&Single step\tSpace"; this.mnuSingle.Click += new System.EventHandler(this.SingleStep); // // mnuBreak1 // this.mnuBreak1.Index = 2; this.mnuBreak1.Text = "-"; // // mnuChange // this.mnuChange.Index = 3; this.mnuChange.Shortcut = System.Windows.Forms.Shortcut.F2; this.mnuChange.ShowShortcut = true; this.mnuChange.Text = "&Change Device..."; this.mnuChange.Click += new System.EventHandler(this.UserSelectNewDevice); // // mnuBreak2 // this.mnuBreak2.Index = 4; this.mnuBreak2.Text = "-"; // // mnuExit // this.mnuExit.Index = 5; this.mnuExit.Text = "E&xit\tESC"; this.mnuExit.Click += new System.EventHandler(this.ExitSample); #endregion } ////// When the menu is starting pause our simulation /// protected override void OnMenuStart(System.EventArgs e) { Pause(true); // Pause the simulation while the menu starts } ////// When the menu is complete our simulation can continue /// protected override void OnMenuComplete(System.EventArgs e) { Pause(false); // Unpause the simulation } ////// Make sure our graphics cursor (if available) moves with the cursor /// protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e) { // Move the D3D cursor device.SetCursorPosition(e.X, e.Y, false); // Let the control handle the mouse now base.OnMouseMove(e); } ////// Handle size changed events /// protected override void OnSizeChanged(System.EventArgs e) { this.OnResize(e); base.OnSizeChanged(e); } ////// Handle resize events /// protected override void OnResize(System.EventArgs e) { if (isHandlingSizeChanges) { // Are we maximized? isMaximized = (this.WindowState == System.Windows.Forms.FormWindowState.Maximized); if (!isMaximized) storedSize = this.ClientSize; } active = !( this.WindowState == System.Windows.Forms.FormWindowState.Minimized || this.Visible == false ); base.OnResize(e); } #endregion }