Axiom/Zalążek programu AXIOM
Informacje ogólne
[edytuj]By ułatwić pierwsze kroki w systemie i nie wymagać wprowadzania dużej ilości własnego kodu, utworzyliśmy klasę zawierającą metody, które muszą wystąpić w programie oraz zalążek programu korzystający z tej klasy, w którym będziemy wprowadzać nasze kolejne line kodu.
Klasa ExampleApplication
[edytuj]Klasa ta zawiera metody, które będziemy nadpisywać przy tworzeniu własnego programu pokazowego. Prosimy przekopiować zawartość klasy do pliku tekstowego i nazwać go ExampleApplication.cs. Plik proszę odłożyć w bezpieczne miejsce a w czasie tworzenia programu korzystać z jego kopii.
// This file was created from ExampleApplication.h (Ogle v1.2.3) by trejs
// for use with Axiom's tutorials.
//
// It still has some flaws. See the "// TODO" lines to begin with.
// But better that than no tutorials at all :)
//
// 2006-12-26: Fixed the mouse button issue, code now compatible with 0.7.1.0-RC2. / trejs
// 2007-08-17: Updated for compatibility with 0.7.2.0 / borrillis
#region Namespace Declarations
using System;
using System.Data;
using System.Collections.Generic;
using Axiom;
using Axiom.Core;
using Axiom.Graphics;
using Axiom.Configuration;
using Axiom.Math;
using Axiom.Overlays;
using Axiom.Input;
#endregion Namespace Declarations
namespace ExampleApplication
{
public class ExampleApplication
{
#region ConfigureConsole
// Code originally from the command-line demo launcher. Mostly rewritten.
//
// If you only want the configuration menu, just copy this class into your application.
// See how it's used in ExampleApplication.Configure().
private class ConfigureConsole
{
#region Types
public enum DialogResult
{
Continue,
Run,
Exit
}
#endregion
#region Private fields
// Currently selected system
private RenderSystem m_currentSystem;
// Holds the list of possible rendersystems
private ConfigOption m_renderSystems;
// Menu items
private List<ConfigOption> m_currentMenuItems = new List<ConfigOption>();
// Currently selected item
private ConfigOption m_currentOption;
// Options for the currently selected system
private List<ConfigOption> m_currentSystemOptions = new List<ConfigOption>();
#endregion
#region Properties
/// <summary>
/// Gets the selected rendersystem (and all the values of options set for it)
/// </summary>
public RenderSystem RenderSystem
{
get
{
return m_currentSystem;
}
}
#endregion
#region Constructor
public ConfigureConsole()
{
// Set current RenderSystem to first in list
m_currentSystem = Root.Instance.RenderSystems[ 0 ];
// Create rendersystem option and get possible values
m_renderSystems = new ConfigOption( "Render System", m_currentSystem.Name, false );
foreach ( RenderSystem r in Root.Instance.RenderSystems )
m_renderSystems.PossibleValues.Add( r.ToString() );
// Build list of options for the currently selected rendersystem
BuildCurrentSystemOptions();
}
#endregion
#region Public methods
public DialogResult Show()
{
while ( true )
{
// Clear menu buffer
m_currentMenuItems.Clear();
// Build menu
if ( m_currentOption == null ) // Main-menu
{
// Build menu from m_currentOptions
foreach ( ConfigOption c in m_currentSystemOptions )
m_currentMenuItems.Add( c );
}
else // Option menu
{
// Add possible values for this option
foreach ( object value in m_currentOption.PossibleValues )
m_currentMenuItems.Add( new ConfigOption( value.ToString(), "", false ) );
}
// Display menu
DisplayOptions();
// Handle next keypress (waits for user input)
DialogResult result = HandleNextKeyPress();
// Check if we're exiting the configure console
if ( result != DialogResult.Continue )
{
Console.Clear();
return result;
}
}
}
#endregion
#region Private methods
private void BuildCurrentSystemOptions()
{
// Make shure it's empty
m_currentSystemOptions.Clear();
// Add rendersystem options to the list
m_currentSystemOptions.Add( m_renderSystems );
// Browse and add options of current rendersystem
foreach ( ConfigOption c in m_currentSystem.ConfigOptions )
m_currentSystemOptions.Add( c );
}
private bool IsDigit( ref int digit, ConsoleKey key )
{
string str = key.ToString();
if ( str.Length == 2 && char.IsDigit( str[ 1 ] ) )
{
digit = int.Parse( str.Substring( 1 ) ); // Numbers are returned like "D5"
return true;
}
else
return false;
}
private DialogResult HandleNextKeyPress()
{
// Wait for key and then read it without echo to the console
ConsoleKey key = Console.ReadKey( true ).Key;
// If the currentOption.Name is null, then we're in the main-menu
if ( m_currentOption == null )
{
// Escape exits, enter runs
if ( key == ConsoleKey.Escape )
return DialogResult.Exit;
else if ( key == ConsoleKey.Enter )
{
// Save options for current system
for ( int i = 0; i < m_currentSystemOptions.Count; i++ )
{
ConfigOption opt = m_currentSystemOptions[ i ];
m_currentSystem.ConfigOptions[ opt.Name ] = opt;
}
return DialogResult.Run;
}
else
{
// Check for selection
int selection = 0;
if ( IsDigit( ref selection, key ) && selection < m_currentMenuItems.Count )
m_currentOption = (ConfigOption)m_currentMenuItems[ selection ];
}
}
else
{
// Esc: Return to main-menu
if ( key == ConsoleKey.Escape )
m_currentOption = null;
// Check if the current key is a digit, and if that digit is within the possible values
int selection = 0;
if ( IsDigit( ref selection, key ) && selection < m_currentOption.PossibleValues.Count )
{
// Check if we're about to change render system
if ( m_currentOption.Name == "Render System" )
{
m_currentSystem = Root.Instance.RenderSystems[ selection ];
m_renderSystems = m_currentOption;
BuildCurrentSystemOptions();
m_currentOption = null; // Reset current option (to show main-menu)
}
else
{
m_currentOption.Value = (string)m_currentOption.PossibleValues[ selection ];
// Set the selected value
for ( int i = 0; i < m_currentSystemOptions.Count; i++ )
if ( m_currentSystemOptions[ i ].Name == m_currentOption.Name )
m_currentSystemOptions[ i ] = m_currentOption;
m_currentOption = null; // Reset current option (to show main-menu)
}
}
}
return DialogResult.Continue;
}
private void DisplayOptions()
{
Console.Clear();
Console.WriteLine( "Axiom Engine Configuration" );
Console.WriteLine( "==========================" );
if ( m_currentOption != null && m_currentOption.Name != null )
{
Console.WriteLine( "Available settings for {0}.\n", m_currentOption.Name );
}
// Load Render Subsystem Options
int i = 0;
foreach ( object o in m_currentMenuItems )
{
// If this is a possible-value of an option (and not a list of options in the main-menu)
// we need to display it's name but not the current value (it doesn't have any).
if ( m_currentOption == null || m_currentOption.Name == null )
Console.WriteLine( "{0} | {1}", i++, o );
else
Console.WriteLine( "{0} | {1}", i++, ( (ConfigOption)o ).Name );
}
if ( m_currentOption == null || m_currentOption.Name == null )
{
Console.WriteLine();
Console.WriteLine( "Enter | Saves changes." );
Console.WriteLine( "ESC | Exits." );
}
Console.Write( "\nSelect option: " );
}
#endregion
}
#endregion
#region Private fields
private string m_configFile = "EngineConfig.xml";
private string m_logFile = "AxiomExample.log";
private long m_lastOverlayUpdate = -1000;
private float m_moveScale = 0, m_rotScale = 0, m_moveSpeed = 100, m_rotateSpeed = 36;
private Vector2 m_rotateVector = new Vector2( 0, 0 );
private Vector3 m_translateVector = new Vector3( 0, 0, 0 );
private Root m_root;
private Camera m_camera;
private SceneManager m_sceneManager;
private RenderWindow m_renderWindow;
private InputReader m_inputReader;
#endregion Private fields
#region Protected properties
/// <summary>
/// Root (Axiom.Core.Root)
/// </summary>
protected Root Root
{
get
{
return m_root;
}
set
{
m_root = value;
}
}
/// <summary>
/// Camera (Axiom.Core.Camera)
/// </summary>
protected Camera Camera
{
get
{
return m_camera;
}
set
{
m_camera = value;
}
}
/// <summary>
/// SceneManager (Axiom.Core.SceneManager)
/// </summary>
protected SceneManager SceneManager
{
get
{
return m_sceneManager;
}
set
{
m_sceneManager = value;
}
}
/// <summary>
/// RenderWindow (Axiom.Graphics.RenderWindow)
/// </summary>
protected RenderWindow RenderWindow
{
get
{
return m_renderWindow;
}
set
{
m_renderWindow = value;
}
}
/// <summary>
/// InputReader (Axiom.Input.InputReader)
/// </summary>
protected InputReader InputReader
{
get
{
return m_inputReader;
}
set
{
m_inputReader = value;
}
}
#endregion Protected fields
#region Public properties
/// <summary>
/// Gets or set the config file name and path
/// </summary>
public string ConfigFile
{
get
{
return m_configFile;
}
set
{
m_configFile = value;
}
}
/// <summary>
/// Gets or sets the config file name and path
/// </summary>
public string LogFile
{
get
{
return m_logFile;
}
set
{
m_logFile = value;
}
}
#endregion
#region Init methods
/// <summary>
/// Starts the example
/// </summary>
public virtual void Run()
{
try
{
if ( Setup() )
m_root.StartRendering();
}
catch ( System.Reflection.ReflectionTypeLoadException ex )
{
// This catches directx missing (or too old) to log :)
for ( int i = 0; i < ex.LoaderExceptions.Length; i++ )
if ( LogManager.Instance != null )
LogManager.Instance.Write( ex.LoaderExceptions[ i ].Message );
}
catch ( Exception ex )
{
if ( LogManager.Instance != null )
LogManager.Instance.Write( ex.ToString() );
}
// TODO: Memory cleanup here..
}
/// <summary>
/// Initalizes the application
/// </summary>
/// <returns>True if successfull, False to exit the application</returns>
protected virtual bool Setup()
{
m_root = new Root( ConfigFile, LogFile );
SetupResources();
// Run config utility, exit program if it returns false
if ( !Configure() )
return false;
else
m_renderWindow = Root.Instance.Initialize( true );
// Initalize input
m_inputReader = PlatformManager.Instance.CreateInputReader();
m_inputReader.Initialize( m_renderWindow, true, true, false, true );
ChooseSceneManager();
CreateCamera();
CreateViewports();
// Set default mipmap level (NB some APIs ignore this)
TextureManager.Instance.DefaultNumMipMaps = 5;
// Create any resource listeners (for loading screens)
CreateResourceListener();
// Add some event handlers
RegisterEventHandlers();
// Lastly, create the scene
CreateScene();
return true;
}
/// <summary>
/// Adds the searchpaths from "EngineConfig.xml" to resources
/// </summary>
protected virtual void SetupResources()
{
EngineConfig config = new EngineConfig();
config.ReadXml( ConfigFile );
foreach ( EngineConfig.FilePathRow row in config.FilePath )
ResourceManager.AddCommonArchive( row.src, row.type );
}
/// <summary>
/// Configures the application
/// </summary>
/// <returns>True if successfull, False to exit the application</returns>
protected virtual bool Configure()
{
ConfigureConsole cc = new ConfigureConsole();
if ( cc.Show() == ConfigureConsole.DialogResult.Exit )
return false;
// Set selected rendersystem
m_root.RenderSystem = cc.RenderSystem;
return true;
}
/// <summary>
/// Chooses scene manager (SceneType.Generic)
/// </summary>
protected virtual void ChooseSceneManager()
{
// Create a generic scene manager
m_sceneManager = m_root.SceneManagers.GetSceneManager( SceneType.Generic );
}
/// <summary>
/// Creates a camera at 500 in the Z direction that looks at -300 in the Z direction
/// </summary>
protected virtual void CreateCamera()
{
// Create the camera
m_camera = m_sceneManager.CreateCamera( "PlayerCam" );
// Position it at 500 in the Z direction
m_camera.Position = new Vector3( 0, 0, 500 );
// Look back along -Z
m_camera.LookAt( new Vector3( 0, 0, -300 ) );
m_camera.Near = 5;
}
/// <summary>
/// Creates a viewport using mCamera
/// </summary>
protected virtual void CreateViewports()
{
// Create one viewport, entire window
Viewport vp = m_renderWindow.AddViewport( m_camera );
vp.BackgroundColor = ColorEx.Black;
// Alter the camera aspect ratio to match the viewport
m_camera.AspectRatio = vp.ActualWidth / vp.ActualHeight;
}
/// <summary>
/// Optional override method where you can create resource listeners (e.g. for loading screens)
/// </summary>
protected virtual void CreateResourceListener()
{
}
/// <summary>
/// Registers event handlers and calls InitOverlay()
/// </summary>
protected virtual void RegisterEventHandlers()
{
m_root.FrameStarted += UpdateInput;
m_root.FrameStarted += UpdateOverlay;
m_root.FrameStarted += FrameStarted;
m_root.FrameEnded += FrameEnded;
// Create debug overlay
InitOverlay();
}
/// <summary>
/// Initalizes the debug overlay (fps, etc..)
/// </summary>
protected virtual void InitOverlay()
{
Overlay o = OverlayManager.Instance.GetByName( "Core/DebugOverlay" );
if ( o == null )
throw new Exception( "Could not find overlay named 'Core/DebugOverlay'." );
o.Show();
}
/// <summary>
/// Creates the scene
/// </summary>
protected virtual void CreateScene()
{
}
#endregion Init methods
#region Event handlers
/// <summary>
/// This is run before each frame
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
protected virtual void FrameStarted( object source, FrameEventArgs e )
{
}
/// <summary>
/// This is run after each frame
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
protected virtual void FrameEnded( object source, FrameEventArgs e )
{
}
/// <summary>
/// Checks for input and handles it
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
protected virtual void UpdateInput( object source, FrameEventArgs e )
{
m_inputReader.Capture();
#region Camera movement
// Reset vectors
m_rotateVector.x = m_translateVector.x = 0;
m_rotateVector.y = m_translateVector.y = 0;
m_translateVector.z = 0;
// Move
m_moveScale = m_moveSpeed * e.TimeSinceLastFrame;
// Rotate
m_rotScale = m_rotateSpeed * e.TimeSinceLastFrame;
// Move forward and back
if ( m_inputReader.IsKeyPressed( KeyCodes.W ) || m_inputReader.IsKeyPressed( KeyCodes.Up ) )
m_translateVector.z = -m_moveScale;
else if ( m_inputReader.IsKeyPressed( KeyCodes.S ) || m_inputReader.IsKeyPressed( KeyCodes.Down ) )
m_translateVector.z = m_moveScale;
// Move left and right
if ( m_inputReader.IsKeyPressed( KeyCodes.A ) )
m_translateVector.x = -m_moveScale;
else if ( m_inputReader.IsKeyPressed( KeyCodes.D ) )
m_translateVector.x = m_moveScale;
// Move up and down
if ( m_inputReader.IsKeyPressed( KeyCodes.PageUp ) )
m_translateVector.y = m_moveScale;
else if ( m_inputReader.IsKeyPressed( KeyCodes.PageDown ) )
m_translateVector.y = -m_moveScale;
// Rotate left and right
if ( m_inputReader.IsKeyPressed( KeyCodes.Left ) )
m_rotateVector.x = -m_rotScale;
else if ( m_inputReader.IsKeyPressed( KeyCodes.Right ) )
m_rotateVector.x = m_rotScale;
// Right mouse button pressed
if ( m_inputReader.IsMousePressed( MouseButtons.Right ) )
{
// Translate
m_translateVector.x += m_inputReader.RelativeMouseX * 0.13f;
m_translateVector.y -= m_inputReader.RelativeMouseY * 0.13f;
}
else
{
// Apply mouse rotation
m_rotateVector.x += m_inputReader.RelativeMouseX * 0.13f;
m_rotateVector.y += m_inputReader.RelativeMouseY * 0.13f;
}
// Apply changes
m_camera.Yaw( -m_rotateVector.x );
m_camera.Pitch( -m_rotateVector.y );
m_camera.MoveRelative( m_translateVector );
#endregion Camera movement
// TODO: what about window-closing-event?
if ( m_inputReader.IsKeyPressed( KeyCodes.Escape ) )
{
Root.Instance.QueueEndRendering();
// TODO: Find a better way
if ( m_root != null )
{
// remove event handlers
// engine.FrameStarted -= new FrameEvent( OnFrameStarted );
// engine.FrameEnded -= new FrameEvent( OnFrameEnded );
m_root.Dispose();
}
m_sceneManager.RemoveAllCameras();
m_sceneManager.RemoveCamera( m_camera );
m_camera = null;
Root.Instance.RenderSystem.DetachRenderTarget( m_renderWindow );
m_renderWindow.Dispose();
return;
}
}
/// <summary>
/// Updates the debug overlay
/// </summary>
protected virtual void UpdateOverlay( object source, FrameEventArgs e )
{
if ( Root.Instance.Timer.Milliseconds - m_lastOverlayUpdate >= 1000 )
{
m_lastOverlayUpdate = Root.Instance.Timer.Milliseconds;
OverlayElement element =
OverlayElementManager.Instance.GetElement( "Core/DebugText" );
element.Text = m_renderWindow.DebugText;
element = OverlayElementManager.Instance.GetElement( "Core/CurrFps" );
element.Text = string.Format( "Current FPS: {0}", Root.Instance.CurrentFPS );
element = OverlayElementManager.Instance.GetElement( "Core/BestFps" );
element.Text = string.Format( "Best FPS: {0}", Root.Instance.BestFPS );
element = OverlayElementManager.Instance.GetElement( "Core/WorstFps" );
element.Text = string.Format( "Worst FPS: {0}", Root.Instance.WorstFPS );
element = OverlayElementManager.Instance.GetElement( "Core/AverageFps" );
element.Text = string.Format( "Average FPS: {0}", Root.Instance.AverageFPS );
element = OverlayElementManager.Instance.GetElement( "Core/NumTris" );
element.Text = string.Format( "Triangle Count: {0}", m_sceneManager.TargetRenderSystem.FacesRendered );
}
}
#endregion Events
}
}
Zalążek programu w środowisku programistycznym
[edytuj]Środowiska programistyczne, takie jak Visual Studio, podczas tworzenia nowego projektu wprowadzają własne zalążki programu. Należy je zamienić na następujące jądro programu:
using System;
using Axiom;
using Axiom.Core;
using Axiom.Math;
namespace ExampleApplication
{
class Program
{
static void Main(string[] args)
{
AxiomTutorial app = new AxiomTutorial ();
app.Run();
}
}
class AxiomTutorial : ExampleApplication
{
protected override void CreateScene()
{
}
}
}
Jądro programu ma bardzo prostą definicję. W zasadzie robi dwie rzeczy:
- tworzy obiekt aplikacji
- uruchamia obiekt aplikacji
AxiomTutorial app = new AxiomTutorial ();
app.Run();
W jądrze musimy co najmniej nadpisać metodę createScene() z ExampleApplication.
Nasza praca w głównej mierze polega na nadpisywaniu kolejnych metod różnych klas definiowanych przez AXIOM.