//-----------------------------------------------------------------------------
// DemoGame.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
namespace Graphics3DSample
{
///
/// This is the main type for your game
///
public class Graphics3DSampleGame : Game
{
const int buttonHeight = 70;
const int buttonWidth = 70;
const int buttonMargin = 15;
GraphicsDeviceManager graphics;
Spaceship spaceship;
Checkbox[] lightEnablingButtons;
Checkbox perpixelLightingButton;
Checkbox animationButton;
Checkbox backgroundTextureEnablingButton;
float cameraFOV = 45; // Initial camera FOV (serves as a zoom level)
float rotationXAmount = 0.0f;
float rotationYAmount = 0.0f;
float? prevLength;
Texture2D background;
Animation animation;
Vector2 animationPosition;
// Mouse input tracking
private MouseState prevMouseState;
private float? prevMouseDragLength = null;
///
/// Applies zoom delta to cameraFOV and clamps the value
///
private void ApplyZoomDelta(float delta)
{
cameraFOV -= delta;
cameraFOV = MathHelper.Clamp(cameraFOV, maxFOV, minFOV);
}
///
/// Resets gesture/mouse drag state for zoom
///
private void ResetZoomGestureState()
{
prevLength = null;
prevMouseDragLength = null;
}
///
/// Applies rotation deltas to the camera
///
private void ApplyRotationDelta(float deltaX, float deltaY)
{
rotationXAmount += deltaX;
rotationYAmount -= deltaY;
}
///
/// Toggles checkboxes at a given screen position
///
private void ToggleCheckboxesAt(Point pos)
{
foreach (var checkbox in lightEnablingButtons)
{
if (checkbox.Bounds.Contains(pos))
checkbox.IsChecked = !checkbox.IsChecked;
}
if (perpixelLightingButton.Bounds.Contains(pos))
perpixelLightingButton.IsChecked = !perpixelLightingButton.IsChecked;
if (animationButton.Bounds.Contains(pos))
animationButton.IsChecked = !animationButton.IsChecked;
if (backgroundTextureEnablingButton.Bounds.Contains(pos))
backgroundTextureEnablingButton.IsChecked = !backgroundTextureEnablingButton.IsChecked;
}
///
/// Provides SporiteBatch to components that draw sprites
///
public SpriteBatch SpriteBatch { get; private set; }
const float minFOV = 60;
const float maxFOV = 30;
///
/// Initialization that does not depend on GraphicsDevice
///
public Graphics3DSampleGame()
{
Content.RootDirectory = "Content";
graphics = new GraphicsDeviceManager(this);
graphics.IsFullScreen = false;
graphics.SupportedOrientations =
DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
graphics.ApplyChanges();
IsMouseVisible = true;
}
///
/// Initialization that depends on GraphicsDevice but does not depend on Content
///
protected override void Initialize()
{
GraphicsDevice.DepthStencilState = new DepthStencilState() { DepthBufferEnable = true };
SpriteBatch = new SpriteBatch(GraphicsDevice);
CreateSpaceship();
CreateLightEnablingButtons();
CreateBackgroundTextureEnablingButton();
CreatePerPixelLightingButton();
CreateAnimationButton();
//Initialize gestures support - Pinch for Zoom and horizontal drag for rotate
TouchPanel.EnabledGestures = GestureType.FreeDrag | GestureType.Pinch | GestureType.PinchComplete;
base.Initialize();
}
///
/// Loads content and creates graphics resources.
///
protected override void LoadContent()
{
background = Content.Load("Textures/spaceBG");
animation = CreateAnimation();
spaceship.Load(this.Content);
base.LoadContent();
}
///
/// Creates animation
///
///
private Animation CreateAnimation()
{
XDocument doc = null;
using (var stream = TitleContainer.OpenStream("Content/AnimationDef.xml"))
{
doc = XDocument.Load(stream);
}
// Load multiple animations form XML definition
XName name = XName.Get("Definition");
var definitions = doc.Document.Descendants(name);
// Get the first (and only in this case) animation from the XML definition
var definition = definitions.First();
Texture2D texture = Content.Load(definition.Attribute("SheetName").Value);
Point frameSize = new Point();
frameSize.X = int.Parse(definition.Attribute("FrameWidth").Value);
frameSize.Y = int.Parse(definition.Attribute("FrameHeight").Value);
Point sheetSize = new Point();
sheetSize.X = int.Parse(definition.Attribute("SheetColumns").Value);
sheetSize.Y = int.Parse(definition.Attribute("SheetRows").Value);
TimeSpan frameInterval = TimeSpan.FromSeconds((float)1 / int.Parse(definition.Attribute("Speed").Value));
//Calculate the animation position (in the middle fot he screen)
animationPosition = new Vector2((graphics.PreferredBackBufferWidth / 2 - frameSize.X),
(graphics.PreferredBackBufferHeight / 2 - frameSize.Y));
return new Animation(texture, frameSize, sheetSize, frameInterval);
}
///
/// Creates spaceship
///
private void CreateSpaceship()
{
spaceship = new Spaceship();
spaceship.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(cameraFOV),
GraphicsDevice.Viewport.AspectRatio, 10, 20000);
}
///
/// Creates light enabling buttons
///
private void CreateLightEnablingButtons()
{
lightEnablingButtons = new Checkbox[3];
for (int n = 0; n < lightEnablingButtons.Length; n++)
{
lightEnablingButtons[n] = new Checkbox(this, "Buttons/lamp_60x60",
new Rectangle(GraphicsDevice.Viewport.Width - (n + 1) * (buttonWidth + buttonMargin),
buttonMargin, buttonWidth, buttonHeight), true);
this.Components.Add(lightEnablingButtons[n]);
}
}
///
/// Creates per-pixel lighting button
///
private void CreatePerPixelLightingButton()
{
perpixelLightingButton = new Checkbox(this, "Buttons/perPixelLight_60x60",
new Rectangle(GraphicsDevice.Viewport.Width - (buttonWidth + buttonMargin),
GraphicsDevice.Viewport.Height - (buttonHeight + buttonMargin),
buttonWidth, buttonHeight), false);
this.Components.Add(perpixelLightingButton);
}
///
/// Creates animation button
///
private void CreateAnimationButton()
{
animationButton = new Checkbox(this, "Buttons/animation_60x60",
new Rectangle(buttonMargin,
GraphicsDevice.Viewport.Height - (buttonHeight + buttonMargin),
buttonWidth, buttonHeight), false);
this.Components.Add(animationButton);
}
///
/// Create texture enabling button
///
private void CreateBackgroundTextureEnablingButton()
{
backgroundTextureEnablingButton = new Checkbox(this, "Buttons/textureOnOff",
new Rectangle(buttonMargin, buttonMargin, buttonWidth, buttonHeight), false);
this.Components.Add(backgroundTextureEnablingButton);
}
///
/// Updates spaceship rendering properties
///
protected override void Update(GameTime gameTime)
{
// Handle touch/mouse input first
HandleInput();
// Handle keyboard input
KeyboardState keyboardState = Keyboard.GetState();
// Allows the game to exit
if (keyboardState.IsKeyDown(Keys.Escape)
|| GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
spaceship.Rotation = GetRotationMatrix();
spaceship.View = GetViewMatrix();
spaceship.Lights = lightEnablingButtons.Select(e => e.IsChecked).ToArray();
spaceship.IsTextureEnabled = true;
spaceship.IsPerPixelLightingEnabled = perpixelLightingButton.IsChecked;
spaceship.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(cameraFOV),
GraphicsDevice.Viewport.AspectRatio, 10, 20000);
if (animationButton.IsChecked)
{
animation.Update(gameTime);
}
base.Update(gameTime);
}
private void HandleInput()
{
// Touch input
while (TouchPanel.IsGestureAvailable)
{
GestureSample gestureSample = TouchPanel.ReadGesture();
switch (gestureSample.GestureType)
{
case GestureType.FreeDrag:
ApplyRotationDelta(gestureSample.Delta.X, gestureSample.Delta.Y);
break;
case GestureType.Pinch:
float gestureValue = 0;
float gestureLengthToZoomScale = 10;
Vector2 gestureDiff = gestureSample.Position - gestureSample.Position2;
gestureValue = gestureDiff.Length() / gestureLengthToZoomScale;
if (prevLength != null) // Skip the first pinch event
ApplyZoomDelta(gestureValue - prevLength.Value);
prevLength = gestureValue;
break;
case GestureType.Tap:
Point tapPos = new Point((int)gestureSample.Position.X, (int)gestureSample.Position.Y);
ToggleCheckboxesAt(tapPos);
break;
case GestureType.PinchComplete:
ResetZoomGestureState();
break;
default:
break;
}
}
// Mouse input parity
MouseState mouseState = Mouse.GetState();
// Mouse click for UI elements
if (prevMouseState.LeftButton == ButtonState.Released && mouseState.LeftButton == ButtonState.Pressed)
{
Point mousePos = new Point(mouseState.X, mouseState.Y);
ToggleCheckboxesAt(mousePos);
}
// Mouse drag for rotation (left button)
if (mouseState.LeftButton == ButtonState.Pressed && prevMouseState.LeftButton == ButtonState.Pressed)
{
int deltaX = mouseState.X - prevMouseState.X;
int deltaY = mouseState.Y - prevMouseState.Y;
ApplyRotationDelta(deltaX, deltaY);
}
// Mouse wheel for zoom (FOV)
if (mouseState.ScrollWheelValue != prevMouseState.ScrollWheelValue)
{
float wheelDelta = mouseState.ScrollWheelValue - prevMouseState.ScrollWheelValue;
float zoomScale = 0.05f; // Adjust sensitivity as needed
ApplyZoomDelta(wheelDelta * zoomScale);
}
// Mouse drag with right button for pinch-like zoom (optional)
if (mouseState.RightButton == ButtonState.Pressed && prevMouseState.RightButton == ButtonState.Pressed)
{
float gestureLengthToZoomScale = 10f;
float gestureValue = Vector2.Distance(new Vector2(mouseState.X, mouseState.Y), new Vector2(prevMouseState.X, prevMouseState.Y)) / gestureLengthToZoomScale;
if (prevMouseDragLength != null)
ApplyZoomDelta(gestureValue - prevMouseDragLength.Value);
prevMouseDragLength = gestureValue;
}
else if (mouseState.RightButton == ButtonState.Released)
{
ResetZoomGestureState();
}
prevMouseState = mouseState;
}
///
/// Gets spaceship rotation matrix
///
///
private Matrix GetRotationMatrix()
{
Matrix matrix = Matrix.CreateWorld(new Vector3(0, 250, 0), Vector3.Forward, Vector3.Up) *
Matrix.CreateFromYawPitchRoll((float)Math.PI + MathHelper.PiOver2 + rotationXAmount / 100, rotationYAmount / 100, 0);
return matrix;
}
///
/// Gets spaceship view matrix
///
private Matrix GetViewMatrix()
{
return Matrix.CreateLookAt(
new Vector3(3500, 400, 0) + new Vector3(0, 250, 0),
new Vector3(0, 250, 0),
Vector3.Up);
}
///
/// Draws the game
///
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.MonoGameOrange);
if (backgroundTextureEnablingButton.IsChecked)
{
SpriteBatch.Begin();
SpriteBatch.Draw(background, Vector2.Zero, Color.White);
SpriteBatch.End();
}
var lights = lightEnablingButtons.Select(e => e.IsChecked).ToArray();
// Set render states.
GraphicsDevice.BlendState = BlendState.Opaque;
GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
// This draws game components, including the currently active menu screen.
// Draw the spaceship model
spaceship.Draw();
if (animationButton.IsChecked)
{
DrawAnimation();
}
base.Draw(gameTime);
}
///
/// Draws animation
///
private void DrawAnimation()
{
float screenHeight = graphics.PreferredBackBufferHeight;
float scale = (float)(graphics.PreferredBackBufferWidth / 480.0);
SpriteBatch.Begin();
animation.Draw(SpriteBatch, animationPosition, 2.0f, SpriteEffects.None);
SpriteBatch.End();
}
}
}