Browse Source

Initial commit. Particle shader has issues with DX/SM4

Simon Jackson 8 years ago
parent
commit
79559532c8
41 changed files with 3303 additions and 48 deletions
  1. 53 0
      .gitattributes
  2. 282 46
      .gitignore
  3. 121 0
      Content/Content.mgcb
  4. 25 0
      Content/ExplosionSettings.xml
  5. 25 0
      Content/ExplosionSmokeSettings.xml
  6. 25 0
      Content/FireSettings.xml
  7. 204 0
      Content/ParticleEffect.fx
  8. 25 0
      Content/ProjectileTrailSettings.xml
  9. 25 0
      Content/SmokePlumeSettings.xml
  10. BIN
      Content/checker.bmp
  11. BIN
      Content/explosion.png
  12. BIN
      Content/fire.png
  13. 15 0
      Content/font.spritefont
  14. 72 0
      Content/grid.x
  15. BIN
      Content/smoke.png
  16. 48 0
      ParticleSystem/ParticleModel.csproj
  17. 131 0
      ParticleSystem/ParticleSettings.cs
  18. 368 0
      ParticleSystem/ParticleSystem.cs
  19. 30 0
      ParticleSystem/Properties/AssemblyInfo.cs
  20. 4 0
      ParticleSystem/packages.config
  21. BIN
      Platforms/DesktopGL/Icon.bmp
  22. BIN
      Platforms/DesktopGL/Icon.ico
  23. 20 0
      Platforms/DesktopGL/Program.cs
  24. 36 0
      Platforms/DesktopGL/Properties/AssemblyInfo.cs
  25. 124 0
      Platforms/DesktopGL/XMLContentLoadingSample.DesktopGL.csproj
  26. 42 0
      Platforms/DesktopGL/app.manifest
  27. BIN
      Platforms/WindowsDX/Icon.ico
  28. 22 0
      Platforms/WindowsDX/Program.cs
  29. 36 0
      Platforms/WindowsDX/Properties/AssemblyInfo.cs
  30. 78 0
      Platforms/WindowsDX/XMLContentLoadingSample.WindowsDX.csproj
  31. 42 0
      Platforms/WindowsDX/app.manifest
  32. 17 2
      README.md
  33. 62 0
      XMLContentLoadingSample.sln
  34. 453 0
      XMLContentLoadingSample/Game.cs
  35. 112 0
      XMLContentLoadingSample/ParticleEmitter.cs
  36. 62 0
      XMLContentLoadingSample/ParticleVertex.cs
  37. 108 0
      XMLContentLoadingSample/Projectile.cs
  38. 30 0
      XMLContentLoadingSample/Properties/AssemblyInfo.cs
  39. 58 0
      XMLContentLoadingSample/XMLContentLoadingSample.csproj
  40. 544 0
      XMLContentLoadingSample/XMLParticleSystem.cs
  41. 4 0
      XMLContentLoadingSample/packages.config

+ 53 - 0
.gitattributes

@@ -0,0 +1,53 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following 
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just comment the entries below and
+# uncomment the group further below
+###############################################################################
+
+*.sln        text eol=crlf
+*.csproj     text eol=crlf
+*.vbproj     text eol=crlf
+*.vcxproj    text eol=crlf
+*.vcproj     text eol=crlf
+*.dbproj     text eol=crlf
+*.fsproj     text eol=crlf
+*.lsproj     text eol=crlf
+*.wixproj    text eol=crlf
+*.modelproj  text eol=crlf
+*.sqlproj    text eol=crlf
+*.wmaproj    text eol=crlf
+
+*.xproj      text eol=crlf
+*.props      text eol=crlf
+*.filters    text eol=crlf
+*.vcxitems   text eol=crlf
+
+
+#*.sln       merge=binary
+#*.csproj    merge=binary
+#*.vbproj    merge=binary
+#*.vcxproj   merge=binary
+#*.vcproj    merge=binary
+#*.dbproj    merge=binary
+#*.fsproj    merge=binary
+#*.lsproj    merge=binary
+#*.wixproj   merge=binary
+#*.modelproj merge=binary
+#*.sqlproj   merge=binary
+#*.wwaproj   merge=binary
+
+#*.xproj     merge=binary
+#*.props     merge=binary
+#*.filters   merge=binary
+#*.vcxitems  merge=binary

+ 282 - 46
.gitignore

@@ -1,52 +1,288 @@
-# Prerequisites
-*.d
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
 
-# Object files
-*.o
-*.ko
-*.obj
-*.elf
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
 
-# Linker output
-*.ilk
-*.map
-*.exp
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
 
-# Precompiled Headers
-*.gch
-*.pch
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
 
-# Libraries
-*.lib
-*.a
-*.la
-*.lo
-
-# Shared objects (inc. Windows DLLs)
-*.dll
-*.so
-*.so.*
-*.dylib
-
-# Executables
-*.exe
-*.out
-*.app
-*.i*86
-*.x86_64
-*.hex
-
-# Debug files
-*.dSYM/
-*.su
-*.idb
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
 *.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Typescript v1 declaration files
+typings/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Telerik's JustMock configuration file
+*.jmconfig
 
-# Kernel Module Compile Results
-*.mod*
-*.cmd
-.tmp_versions/
-modules.order
-Module.symvers
-Mkfile.old
-dkms.conf
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs

+ 121 - 0
Content/Content.mgcb

@@ -0,0 +1,121 @@
+
+#----------------------------- Global Properties ----------------------------#
+
+/outputDir:bin
+/intermediateDir:obj
+/platform:DesktopGL
+/config:
+/profile:Reach
+/compress:False
+
+#-------------------------------- References --------------------------------#
+
+/reference:..\ParticleSystem\bin\Debug\XMLContentLoadingSample.ParticleModel.dll
+
+#---------------------------------- Content ---------------------------------#
+
+#begin checker.bmp
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:checker.bmp
+
+#begin ExplosionSettings.xml
+/importer:XmlImporter
+/processor:PassThroughProcessor
+/build:ExplosionSettings.xml
+
+#begin ExplosionSmokeSettings.xml
+/importer:XmlImporter
+/processor:PassThroughProcessor
+/build:ExplosionSmokeSettings.xml
+
+#begin fire.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:fire.png
+
+#begin FireSettings.xml
+/importer:XmlImporter
+/processor:PassThroughProcessor
+/build:FireSettings.xml
+
+#begin font.spritefont
+/importer:FontDescriptionImporter
+/processor:FontDescriptionProcessor
+/processorParam:PremultiplyAlpha=True
+/processorParam:TextureFormat=Compressed
+/build:font.spritefont
+
+#begin grid.x
+/importer:XImporter
+/processor:ModelProcessor
+/processorParam:ColorKeyColor=0,0,0,0
+/processorParam:ColorKeyEnabled=True
+/processorParam:DefaultEffect=BasicEffect
+/processorParam:GenerateMipmaps=True
+/processorParam:GenerateTangentFrames=False
+/processorParam:PremultiplyTextureAlpha=True
+/processorParam:PremultiplyVertexColors=True
+/processorParam:ResizeTexturesToPowerOfTwo=False
+/processorParam:RotationX=0
+/processorParam:RotationY=0
+/processorParam:RotationZ=0
+/processorParam:Scale=1
+/processorParam:SwapWindingOrder=False
+/processorParam:TextureFormat=Compressed
+/build:grid.x
+
+#begin ParticleEffect.fx
+/importer:EffectImporter
+/processor:EffectProcessor
+/processorParam:DebugMode=Auto
+/build:ParticleEffect.fx
+
+#begin ProjectileTrailSettings.xml
+/importer:XmlImporter
+/processor:PassThroughProcessor
+/build:ProjectileTrailSettings.xml
+
+#begin smoke.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:smoke.png
+
+#begin SmokePlumeSettings.xml
+/importer:XmlImporter
+/processor:PassThroughProcessor
+/build:SmokePlumeSettings.xml
+
+#begin explosion.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:explosion.png
+

+ 25 - 0
Content/ExplosionSettings.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent>
+  <Asset Type="XMLContentLoadingSample.ParticleModel.ParticleSettings">
+    <BlendState>Additive</BlendState>
+    <TextureName>explosion</TextureName>
+    <MaxParticles>100</MaxParticles>
+    <Duration>PT2S</Duration>
+    <DurationRandomness>1</DurationRandomness>
+    <EmitterVelocitySensitivity>1</EmitterVelocitySensitivity>
+    <MinHorizontalVelocity>20</MinHorizontalVelocity>
+    <MaxHorizontalVelocity>30</MaxHorizontalVelocity>
+    <MinVerticalVelocity>-20</MinVerticalVelocity>
+    <MaxVerticalVelocity>20</MaxVerticalVelocity>
+    <Gravity>0 0 0</Gravity>
+    <EndVelocity>0</EndVelocity>
+    <MinColor>FF808080</MinColor>
+    <MaxColor>FFA9A9A9</MaxColor>
+    <MinRotateSpeed>-1</MinRotateSpeed>
+    <MaxRotateSpeed>1</MaxRotateSpeed>
+    <MinStartSize>10</MinStartSize>
+    <MaxStartSize>10</MaxStartSize>
+    <MinEndSize>100</MinEndSize>
+    <MaxEndSize>200</MaxEndSize>
+  </Asset>
+</XnaContent>

+ 25 - 0
Content/ExplosionSmokeSettings.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent>
+  <Asset Type="XMLContentLoadingSample.ParticleModel.ParticleSettings">
+    <BlendState>NonPremultiplied</BlendState>
+    <TextureName>smoke</TextureName>
+    <MaxParticles>200</MaxParticles>
+    <Duration>PT4S</Duration>
+    <DurationRandomness>0</DurationRandomness>
+    <EmitterVelocitySensitivity>1</EmitterVelocitySensitivity>
+    <MinHorizontalVelocity>0</MinHorizontalVelocity>
+    <MaxHorizontalVelocity>50</MaxHorizontalVelocity>
+    <MinVerticalVelocity>-10</MinVerticalVelocity>
+    <MaxVerticalVelocity>50</MaxVerticalVelocity>
+    <Gravity>0 -20 0</Gravity>
+    <EndVelocity>0</EndVelocity>
+    <MinColor>FFD3D3D3</MinColor>
+    <MaxColor>FFFFFFFF</MaxColor>
+    <MinRotateSpeed>-2</MinRotateSpeed>
+    <MaxRotateSpeed>2</MaxRotateSpeed>
+    <MinStartSize>10</MinStartSize>
+    <MaxStartSize>10</MaxStartSize>
+    <MinEndSize>100</MinEndSize>
+    <MaxEndSize>200</MaxEndSize>
+  </Asset>
+</XnaContent>

+ 25 - 0
Content/FireSettings.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent>
+  <Asset Type="XMLContentLoadingSample.ParticleModel.ParticleSettings">
+    <BlendState>Additive</BlendState>
+    <TextureName>fire</TextureName>
+    <MaxParticles>2400</MaxParticles>
+    <Duration>PT2S</Duration>
+    <DurationRandomness>1</DurationRandomness>
+    <EmitterVelocitySensitivity>1</EmitterVelocitySensitivity>
+    <MinHorizontalVelocity>0</MinHorizontalVelocity>
+    <MaxHorizontalVelocity>15</MaxHorizontalVelocity>
+    <MinVerticalVelocity>-10</MinVerticalVelocity>
+    <MaxVerticalVelocity>10</MaxVerticalVelocity>
+    <Gravity>0 15 0</Gravity>
+    <EndVelocity>1</EndVelocity>
+    <MinColor>0AFFFFFF</MinColor>
+    <MaxColor>28FFFFFF</MaxColor>
+    <MinRotateSpeed>0</MinRotateSpeed>
+    <MaxRotateSpeed>0</MaxRotateSpeed>
+    <MinStartSize>5</MinStartSize>
+    <MaxStartSize>10</MaxStartSize>
+    <MinEndSize>10</MinEndSize>
+    <MaxEndSize>40</MaxEndSize>
+  </Asset>
+</XnaContent>

+ 204 - 0
Content/ParticleEffect.fx

@@ -0,0 +1,204 @@
+//-----------------------------------------------------------------------------
+// ParticleEffect.fx
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+
+// Camera parameters.
+float4x4 View;
+float4x4 Projection;
+float2 ViewportScale;
+
+
+// The current time, in seconds.
+float CurrentTime;
+
+
+// Parameters describing how the particles animate.
+float Duration;
+float DurationRandomness;
+float3 Gravity;
+float EndVelocity;
+float4 MinColor;
+float4 MaxColor;
+
+
+// These float2 parameters describe the min and max of a range.
+// The actual value is chosen differently for each particle,
+// interpolating between x and y by some random amount.
+float2 RotateSpeed;
+float2 StartSize;
+float2 EndSize;
+
+
+// Particle texture and sampler.
+texture Texture;
+
+sampler Sampler = sampler_state
+{
+    Texture = (Texture);
+    
+    MinFilter = Linear;
+    MagFilter = Linear;
+    MipFilter = Point;
+    
+    AddressU = Clamp;
+    AddressV = Clamp;
+};
+
+
+// Vertex shader input structure describes the start position and
+// velocity of the particle, and the time at which it was created,
+// along with some random values that affect its size and rotation.
+struct VertexShaderInput
+{
+    float2 Corner : POSITION0;
+    float3 Position : POSITION1;
+    float3 Velocity : NORMAL0;
+    float4 Random : COLOR0;
+    float Time : TEXCOORD0;
+};
+
+
+// Vertex shader output structure specifies the position and color of the particle.
+struct VertexShaderOutput
+{
+    float4 Position : SV_POSITION;
+    float4 Color : COLOR0;
+    float2 TextureCoordinate : COLOR1;
+};
+
+
+// Vertex shader helper for computing the position of a particle.
+float4 ComputeParticlePosition(float3 position, float3 velocity,
+                               float age, float normalizedAge)
+{
+    float startVelocity = length(velocity);
+
+    // Work out how fast the particle should be moving at the end of its life,
+    // by applying a constant scaling factor to its starting velocity.
+    float endVelocity = startVelocity * EndVelocity;
+    
+    // Our particles have constant acceleration, so given a starting velocity
+    // S and ending velocity E, at time T their velocity should be S + (E-S)*T.
+    // The particle position is the sum of this velocity over the range 0 to T.
+    // To compute the position directly, we must integrate the velocity
+    // equation. Integrating S + (E-S)*T for T produces S*T + (E-S)*T*T/2.
+
+    float velocityIntegral = startVelocity * normalizedAge +
+                             (endVelocity - startVelocity) * normalizedAge *
+                                                             normalizedAge / 2;
+     
+    position += normalize(velocity) * velocityIntegral * Duration;
+    
+    // Apply the gravitational force.
+    position += Gravity * age * normalizedAge;
+    
+    // Apply the camera view and projection transforms.
+    return mul(mul(float4(position, 1), View), Projection);
+}
+
+
+// Vertex shader helper for computing the size of a particle.
+float ComputeParticleSize(float randomValue, float normalizedAge)
+{
+    // Apply a random factor to make each particle a slightly different size.
+    float startSize = lerp(StartSize.x, StartSize.y, randomValue);
+    float endSize = lerp(EndSize.x, EndSize.y, randomValue);
+    
+    // Compute the actual size based on the age of the particle.
+    float size = lerp(startSize, endSize, normalizedAge);
+    
+    // Project the size into screen coordinates.
+    return size * Projection._m11;
+}
+
+
+// Vertex shader helper for computing the color of a particle.
+float4 ComputeParticleColor(float4 projectedPosition,
+                            float randomValue, float normalizedAge)
+{
+    // Apply a random factor to make each particle a slightly different color.
+    float4 color = lerp(MinColor, MaxColor, randomValue);
+    
+    // Fade the alpha based on the age of the particle. This curve is hard coded
+    // to make the particle fade in fairly quickly, then fade out more slowly:
+    // plot x*(1-x)*(1-x) for x=0:1 in a graphing program if you want to see what
+    // this looks like. The 6.7 scaling factor normalizes the curve so the alpha
+    // will reach all the way up to fully solid.
+    
+    color.a *= normalizedAge * (1-normalizedAge) * (1-normalizedAge) * 6.7;
+   
+    return color;
+}
+
+
+// Vertex shader helper for computing the rotation of a particle.
+float2x2 ComputeParticleRotation(float randomValue, float age)
+{    
+    // Apply a random factor to make each particle rotate at a different speed.
+    float rotateSpeed = lerp(RotateSpeed.x, RotateSpeed.y, randomValue);
+    
+    float rotation = rotateSpeed * age;
+
+    // Compute a 2x2 rotation matrix.
+    float c = cos(rotation);
+    float s = sin(rotation);
+    
+    return float2x2(c, -s, s, c);
+}
+
+
+// Custom vertex shader animates particles entirely on the GPU.
+VertexShaderOutput ParticleVertexShader(VertexShaderInput input)
+{
+    VertexShaderOutput output;
+    
+    // Compute the age of the particle.
+    float age = CurrentTime - input.Time;
+    
+    // Apply a random factor to make different particles age at different rates.
+    age *= 1 + input.Random.x * DurationRandomness;
+    
+    // Normalize the age into the range zero to one.
+    float normalizedAge = saturate(age / Duration);
+
+    // Compute the particle position, size, color, and rotation.
+    output.Position = ComputeParticlePosition(input.Position, input.Velocity,
+                                              age, normalizedAge);
+
+    float size = ComputeParticleSize(input.Random.y, normalizedAge);
+    float2x2 rotation = ComputeParticleRotation(input.Random.w, age);
+
+    output.Position.xy += mul(input.Corner, rotation) * size * ViewportScale;
+    
+    output.Color = ComputeParticleColor(output.Position, input.Random.z, normalizedAge);
+    output.TextureCoordinate = (input.Corner + 1) / 2;
+    
+    return output;
+}
+
+
+// Pixel shader for drawing particles.
+float4 ParticlePixelShader(VertexShaderOutput input) : SV_Target
+{
+    return tex2D(Sampler, input.TextureCoordinate) * input.Color;
+}
+
+
+// Effect technique for drawing particles.
+technique Particles
+{
+    pass P0
+    {
+#if SM4	
+        VertexShader = compile vs_4_0 ParticleVertexShader();
+        PixelShader = compile ps_4_0 ParticlePixelShader();
+#else
+         VertexShader = compile vs_3_0 ParticleVertexShader();
+        PixelShader = compile ps_3_0 ParticlePixelShader();
+#endif
+    }
+}

+ 25 - 0
Content/ProjectileTrailSettings.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent>
+  <Asset Type="XMLContentLoadingSample.ParticleModel.ParticleSettings">
+    <BlendState>NonPremultiplied</BlendState>
+    <TextureName>smoke</TextureName>
+    <MaxParticles>1000</MaxParticles>
+    <Duration>PT3S</Duration>
+    <DurationRandomness>1.5</DurationRandomness>
+    <EmitterVelocitySensitivity>0.1</EmitterVelocitySensitivity>
+    <MinHorizontalVelocity>0</MinHorizontalVelocity>
+    <MaxHorizontalVelocity>1</MaxHorizontalVelocity>
+    <MinVerticalVelocity>-1</MinVerticalVelocity>
+    <MaxVerticalVelocity>1</MaxVerticalVelocity>
+    <Gravity>0 0 0</Gravity>
+    <EndVelocity>1</EndVelocity>
+    <MinColor>FF406080</MinColor>
+    <MaxColor>80FFFFFF</MaxColor>
+    <MinRotateSpeed>-4</MinRotateSpeed>
+    <MaxRotateSpeed>4</MaxRotateSpeed>
+    <MinStartSize>2</MinStartSize>
+    <MaxStartSize>4</MaxStartSize>
+    <MinEndSize>5</MinEndSize>
+    <MaxEndSize>15</MaxEndSize>
+  </Asset>
+</XnaContent>

+ 25 - 0
Content/SmokePlumeSettings.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent>
+  <Asset Type="XMLContentLoadingSample.ParticleModel.ParticleSettings">
+    <BlendState>NonPremultiplied</BlendState>
+    <TextureName>smoke</TextureName>
+    <MaxParticles>600</MaxParticles>
+    <Duration>PT10S</Duration>
+    <DurationRandomness>0</DurationRandomness>
+    <EmitterVelocitySensitivity>1</EmitterVelocitySensitivity>
+    <MinHorizontalVelocity>0</MinHorizontalVelocity>
+    <MaxHorizontalVelocity>15</MaxHorizontalVelocity>
+    <MinVerticalVelocity>10</MinVerticalVelocity>
+    <MaxVerticalVelocity>20</MaxVerticalVelocity>
+    <Gravity>-20 -5 0</Gravity>
+    <EndVelocity>0.75</EndVelocity>
+    <MinColor>FFFFFFFF</MinColor>
+    <MaxColor>FFFFFFFF</MaxColor>
+    <MinRotateSpeed>-1</MinRotateSpeed>
+    <MaxRotateSpeed>1</MaxRotateSpeed>
+    <MinStartSize>5</MinStartSize>
+    <MaxStartSize>10</MaxStartSize>
+    <MinEndSize>50</MinEndSize>
+    <MaxEndSize>200</MaxEndSize>
+  </Asset>
+</XnaContent>

BIN
Content/checker.bmp


BIN
Content/explosion.png


BIN
Content/fire.png


+ 15 - 0
Content/font.spritefont

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
+  <Asset Type="Graphics:FontDescription">
+    <FontName>Segoe UI Mono</FontName>
+    <Size>14</Size>
+    <Spacing>2</Spacing>
+    <Style>Regular</Style>
+    <CharacterRegions>
+      <CharacterRegion>
+        <Start>&#32;</Start>
+        <End>&#126;</End>
+      </CharacterRegion>
+    </CharacterRegions>
+  </Asset>
+</XnaContent>

+ 72 - 0
Content/grid.x

@@ -0,0 +1,72 @@
+xof 0303txt 0032
+template Vector {
+ <3d82ab5e-62da-11cf-ab39-0020af71e433>
+ FLOAT x;
+ FLOAT y;
+ FLOAT z;
+}
+
+template MeshFace {
+ <3d82ab5f-62da-11cf-ab39-0020af71e433>
+ DWORD nFaceVertexIndices;
+ array DWORD faceVertexIndices[nFaceVertexIndices];
+}
+
+template Mesh {
+ <3d82ab44-62da-11cf-ab39-0020af71e433>
+ DWORD nVertices;
+ array Vector vertices[nVertices];
+ DWORD nFaces;
+ array MeshFace faces[nFaces];
+ [...]
+}
+
+template Coords2d {
+ <f6f23f44-7686-11cf-8f52-0040333594a3>
+ FLOAT u;
+ FLOAT v;
+}
+
+template MeshTextureCoords {
+ <f6f23f40-7686-11cf-8f52-0040333594a3>
+ DWORD nTextureCoords;
+ array Coords2d textureCoords[nTextureCoords];
+}
+
+
+Mesh {
+ 4;
+ 640.00000;0.000000;-640.00000;,
+ -640.00000;0.000000;-640.00000;,
+ -640.00000;0.000000;640.00000;,
+ 640.00000;0.000000;640.00000;;
+ 2;
+ 3;0,1,2;,
+ 3;0,2,3;;
+
+ MeshTextureCoords {
+  4;
+  0.000000;0.000000;,
+  0.000000;32.000000;,
+  32.000000;32.000000;,
+  32.000000;0.000000;;
+ }
+
+ MeshMaterialList {
+  1;
+  2;
+  0,
+  0;
+
+  Material {
+   0.800000;0.800000;0.800000;1.000000;;
+   1.000000;
+   0.000000;0.000000;0.000000;;
+   0.000000;0.000000;0.000000;;
+
+   TextureFilename {
+    "checker.bmp";
+   }
+  }
+ }
+}

BIN
Content/smoke.png


+ 48 - 0
ParticleSystem/ParticleModel.csproj

@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{3994E064-F047-433B-9388-7F1748B87AC0}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>XMLContentLoadingSample.ParticleModel</RootNamespace>
+    <AssemblyName>XMLContentLoadingSample.ParticleModel</AssemblyName>
+    <DefaultLanguage>en-US</DefaultLanguage>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="ParticleSettings.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="MonoGame.Framework, Version=3.6.0.1625, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\MonoGame.Framework.Portable.3.6.0.1625\lib\portable-net45+win8+wpa81\MonoGame.Framework.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
+</Project>

+ 131 - 0
ParticleSystem/ParticleSettings.cs

@@ -0,0 +1,131 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ParticleSettings.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Content;
+#endregion
+
+namespace XMLContentLoadingSample.ParticleModel
+{
+    /// <summary>
+    /// Settings class describes all the tweakable options used
+    /// to control the appearance of a particle system.
+    /// </summary>
+    public class ParticleSettings
+    {
+        // Name of the texture used by this particle system.
+        public string TextureName = null;
+
+
+        // Maximum number of particles that can be displayed at one time.
+        public int MaxParticles = 100;
+
+
+        // How long these particles will last.
+        public TimeSpan Duration = TimeSpan.FromSeconds(1);
+
+
+        // If greater than zero, some particles will last a shorter time than others.
+        public float DurationRandomness = 0;
+
+
+        // Controls how much particles are influenced by the velocity of the object
+        // which created them. You can see this in action with the explosion effect,
+        // where the flames continue to move in the same direction as the source
+        // projectile. The projectile trail particles, on the other hand, set this
+        // value very low so they are less affected by the velocity of the projectile.
+        public float EmitterVelocitySensitivity = 1;
+
+
+        // Range of values controlling how much X and Z axis velocity to give each
+        // particle. Values for individual particles are randomly chosen from somewhere
+        // between these limits.
+        public float MinHorizontalVelocity = 0;
+        public float MaxHorizontalVelocity = 0;
+
+
+        // Range of values controlling how much Y axis velocity to give each particle.
+        // Values for individual particles are randomly chosen from somewhere between
+        // these limits.
+        public float MinVerticalVelocity = 0;
+        public float MaxVerticalVelocity = 0;
+
+
+        // Direction and strength of the gravity effect. Note that this can point in any
+        // direction, not just down! The fire effect points it upward to make the flames
+        // rise, and the smoke plume points it sideways to simulate wind.
+        public Vector3 Gravity = Vector3.Zero;
+
+
+        // Controls how the particle velocity will change over their lifetime. If set
+        // to 1, particles will keep going at the same speed as when they were created.
+        // If set to 0, particles will come to a complete stop right before they die.
+        // Values greater than 1 make the particles speed up over time.
+        public float EndVelocity = 1;
+
+
+        // Range of values controlling the particle color and alpha. Values for
+        // individual particles are randomly chosen from somewhere between these limits.
+        public Color MinColor = Color.White;
+        public Color MaxColor = Color.White;
+
+
+        // Range of values controlling how fast the particles rotate. Values for
+        // individual particles are randomly chosen from somewhere between these
+        // limits. If both these values are set to 0, the particle system will
+        // automatically switch to an alternative shader technique that does not
+        // support rotation, and thus requires significantly less GPU power. This
+        // means if you don't need the rotation effect, you may get a performance
+        // boost from leaving these values at 0.
+        public float MinRotateSpeed = 0;
+        public float MaxRotateSpeed = 0;
+
+
+        // Range of values controlling how big the particles are when first created.
+        // Values for individual particles are randomly chosen from somewhere between
+        // these limits.
+        public float MinStartSize = 100;
+        public float MaxStartSize = 100;
+
+
+        // Range of values controlling how big particles become at the end of their
+        // life. Values for individual particles are randomly chosen from somewhere
+        // between these limits.
+        public float MinEndSize = 100;
+        public float MaxEndSize = 100;
+
+
+        // Alpha blending settings.
+        [ContentSerializerIgnore]
+        public BlendState BlendState = BlendState.NonPremultiplied;
+
+
+        [ContentSerializer(ElementName = "BlendState")]
+        private string BlendStateSerializationHelper
+        {
+            get { return BlendState.Name.Replace("BlendState.", string.Empty); }
+
+            set
+            {
+                switch (value)
+                {
+                    case "AlphaBlend":       BlendState = BlendState.AlphaBlend;       break;
+                    case "Additive":         BlendState = BlendState.Additive;         break;
+                    case "NonPremultiplied": BlendState = BlendState.NonPremultiplied; break;
+
+                    default:
+                        throw new ArgumentException("Unknown blend state " + value);
+                }
+            }
+        }
+    }
+}

+ 368 - 0
ParticleSystem/ParticleSystem.cs

@@ -0,0 +1,368 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// SmokePlumeParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace ParticleSample
+{
+    /// <summary>
+    /// ParticleSystem is an abstract class that provides the basic functionality to
+    /// create a particle effect. Different subclasses will have different effects,
+    /// such as fire, explosions, and plumes of smoke. To use these subclasses, 
+    /// simply call AddParticles, and pass in where the particles should exist
+    /// </summary>
+    public abstract class ParticleSystem : DrawableGameComponent
+    {
+        // these two values control the order that particle systems are drawn in.
+        // typically, particles that use additive blending should be drawn on top of
+        // particles that use regular alpha blending. ParticleSystems should therefore
+        // set their DrawOrder to the appropriate value in InitializeConstants, though
+        // it is possible to use other values for more advanced effects.
+        public const int AlphaBlendDrawOrder = 100;
+        public const int AdditiveDrawOrder = 200;
+
+        // a reference to the main game; we'll keep this around because it exposes a
+        // content manager and a sprite batch for us to use.
+        private ParticleSampleGame game;
+
+        // the texture this particle system will use.
+        private Texture2D texture;
+
+        // the origin when we're drawing textures. this will be the middle of the
+        // texture.
+        private Vector2 origin;
+
+        // this number represents the maximum number of effects this particle system
+        // will be expected to draw at one time. this is set in the constructor and is
+        // used to calculate how many particles we will need.
+        private int howManyEffects;
+        
+        // the array of particles used by this system. these are reused, so that calling
+        // AddParticles will not cause any allocations.
+        Particle[] particles;
+
+        // the queue of free particles keeps track of particles that are not curently
+        // being used by an effect. when a new effect is requested, particles are taken
+        // from this queue. when particles are finished they are put onto this queue.
+        Queue<Particle> freeParticles;
+        /// <summary>
+        /// returns the number of particles that are available for a new effect.
+        /// </summary>
+        public int FreeParticleCount
+        {
+            get { return freeParticles.Count; }
+        }
+
+
+        // This region of values control the "look" of the particle system, and should 
+        // be set by deriving particle systems in the InitializeConstants method. The
+        // values are then used by the virtual function InitializeParticle. Subclasses
+        // can override InitializeParticle for further
+        // customization.
+        #region constants to be set by subclasses
+
+        /// <summary>
+        /// minNumParticles and maxNumParticles control the number of particles that are
+        /// added when AddParticles is called. The number of particles will be a random
+        /// number between minNumParticles and maxNumParticles.
+        /// </summary>
+        protected int minNumParticles;
+        protected int maxNumParticles;
+       
+        /// <summary>
+        /// this controls the texture that the particle system uses. It will be used as
+        /// an argument to ContentManager.Load.
+        /// </summary>
+        protected string textureFilename;
+
+        /// <summary>
+        /// minInitialSpeed and maxInitialSpeed are used to control the initial velocity
+        /// of the particles. The particle's initial speed will be a random number 
+        /// between these two. The direction is determined by the function 
+        /// PickRandomDirection, which can be overriden.
+        /// </summary>
+        protected float minInitialSpeed;
+        protected float maxInitialSpeed;
+
+        /// <summary>
+        /// minAcceleration and maxAcceleration are used to control the acceleration of
+        /// the particles. The particle's acceleration will be a random number between
+        /// these two. By default, the direction of acceleration is the same as the
+        /// direction of the initial velocity.
+        /// </summary>
+        protected float minAcceleration;
+        protected float maxAcceleration;
+
+        /// <summary>
+        /// minRotationSpeed and maxRotationSpeed control the particles' angular
+        /// velocity: the speed at which particles will rotate. Each particle's rotation
+        /// speed will be a random number between minRotationSpeed and maxRotationSpeed.
+        /// Use smaller numbers to make particle systems look calm and wispy, and large 
+        /// numbers for more violent effects.
+        /// </summary>
+        protected float minRotationSpeed;
+        protected float maxRotationSpeed;
+
+        /// <summary>
+        /// minLifetime and maxLifetime are used to control the lifetime. Each
+        /// particle's lifetime will be a random number between these two. Lifetime
+        /// is used to determine how long a particle "lasts." Also, in the base
+        /// implementation of Draw, lifetime is also used to calculate alpha and scale
+        /// values to avoid particles suddenly "popping" into view
+        /// </summary>
+        protected float minLifetime;
+        protected float maxLifetime;
+
+        /// <summary>
+        /// to get some additional variance in the appearance of the particles, we give
+        /// them all random scales. the scale is a value between minScale and maxScale,
+        /// and is additionally affected by the particle's lifetime to avoid particles
+        /// "popping" into view.
+        /// </summary>
+        protected float minScale;
+        protected float maxScale;
+
+        /// <summary>
+        /// different effects can use different blend states. fire and explosions work
+        /// well with additive blending, for example.
+        /// </summary>
+		protected BlendState blendState;
+
+        #endregion
+        
+        /// <summary>
+        /// Constructs a new ParticleSystem.
+        /// </summary>
+        /// <param name="game">The host for this particle system. The game keeps the 
+        /// content manager and sprite batch for us.</param>
+        /// <param name="howManyEffects">the maximum number of particle effects that
+        /// are expected on screen at once.</param>
+        /// <remarks>it is tempting to set the value of howManyEffects very high.
+        /// However, this value should be set to the minimum possible, because
+        /// it has a large impact on the amount of memory required, and slows down the
+        /// Update and Draw functions.</remarks>
+        protected ParticleSystem(ParticleSampleGame game, int howManyEffects)
+            : base(game)
+        {            
+            this.game = game;
+            this.howManyEffects = howManyEffects;
+        }
+
+        /// <summary>
+        /// override the base class's Initialize to do some additional work; we want to
+        /// call InitializeConstants to let subclasses set the constants that we'll use.
+        /// 
+        /// also, the particle array and freeParticles queue are set up here.
+        /// </summary>
+        public override void Initialize()
+        {
+            InitializeConstants();
+            
+            // calculate the total number of particles we will ever need, using the
+            // max number of effects and the max number of particles per effect.
+            // once these particles are allocated, they will be reused, so that
+            // we don't put any pressure on the garbage collector.
+            particles = new Particle[howManyEffects * maxNumParticles];
+            freeParticles = new Queue<Particle>(howManyEffects * maxNumParticles);
+            for (int i = 0; i < particles.Length; i++)
+            {
+                particles[i] = new Particle();
+                freeParticles.Enqueue(particles[i]);
+            }
+            base.Initialize();
+        }
+
+        /// <summary>
+        /// this abstract function must be overriden by subclasses of ParticleSystem.
+        /// It's here that they should set all the constants marked in the region
+        /// "constants to be set by subclasses", which give each ParticleSystem its
+        /// specific flavor.
+        /// </summary>
+        protected abstract void InitializeConstants();
+
+        /// <summary>
+        /// Override the base class LoadContent to load the texture. once it's
+        /// loaded, calculate the origin.
+        /// </summary>
+        protected override void LoadContent()
+        {
+            // make sure sub classes properly set textureFilename.
+            if (string.IsNullOrEmpty(textureFilename))
+            {
+                string message = "textureFilename wasn't set properly, so the " +
+                    "particle system doesn't know what texture to load. Make " +
+                    "sure your particle system's InitializeConstants function " +
+                    "properly sets textureFilename.";
+                throw new InvalidOperationException(message);
+            }
+            // load the texture....
+            texture = game.Content.Load<Texture2D>(textureFilename);
+
+            // ... and calculate the center. this'll be used in the draw call, we
+            // always want to rotate and scale around this point.
+            origin.X = texture.Width / 2;
+            origin.Y = texture.Height / 2;
+
+            base.LoadContent();
+        }
+
+        /// <summary>
+        /// AddParticles's job is to add an effect somewhere on the screen. If there 
+        /// aren't enough particles in the freeParticles queue, it will use as many as 
+        /// it can. This means that if there not enough particles available, calling
+        /// AddParticles will have no effect.
+        /// </summary>
+        /// <param name="where">where the particle effect should be created</param>
+        public void AddParticles(Vector2 where)
+        {
+            // the number of particles we want for this effect is a random number
+            // somewhere between the two constants specified by the subclasses.
+            int numParticles = 
+                ParticleSampleGame.Random.Next(minNumParticles, maxNumParticles);
+
+            // create that many particles, if you can.
+            for (int i = 0; i < numParticles && freeParticles.Count > 0; i++)
+            {
+                // grab a particle from the freeParticles queue, and Initialize it.
+                Particle p = freeParticles.Dequeue();
+                InitializeParticle(p, where);               
+            }
+        }
+
+        /// <summary>
+        /// InitializeParticle randomizes some properties for a particle, then
+        /// calls initialize on it. It can be overriden by subclasses if they 
+        /// want to modify the way particles are created. For example, 
+        /// SmokePlumeParticleSystem overrides this function make all particles
+        /// accelerate to the right, simulating wind.
+        /// </summary>
+        /// <param name="p">the particle to initialize</param>
+        /// <param name="where">the position on the screen that the particle should be
+        /// </param>
+        protected virtual void InitializeParticle(Particle p, Vector2 where)
+        {
+            // first, call PickRandomDirection to figure out which way the particle
+            // will be moving. velocity and acceleration's values will come from this.
+            Vector2 direction = PickRandomDirection();
+
+            // pick some random values for our particle
+            float velocity = 
+                ParticleSampleGame.RandomBetween(minInitialSpeed, maxInitialSpeed);
+            float acceleration = 
+                ParticleSampleGame.RandomBetween(minAcceleration, maxAcceleration);
+            float lifetime =
+                ParticleSampleGame.RandomBetween(minLifetime, maxLifetime);
+            float scale =
+                ParticleSampleGame.RandomBetween(minScale, maxScale);
+            float rotationSpeed =
+                ParticleSampleGame.RandomBetween(minRotationSpeed, maxRotationSpeed);
+
+            // then initialize it with those random values. initialize will save those,
+            // and make sure it is marked as active.
+            p.Initialize(
+                where, velocity * direction, acceleration * direction,
+                lifetime, scale, rotationSpeed);
+        }
+
+        /// <summary>
+        /// PickRandomDirection is used by InitializeParticles to decide which direction
+        /// particles will move. The default implementation is a random vector in a
+        /// circular pattern.
+        /// </summary>
+        protected virtual Vector2 PickRandomDirection()
+        {
+            float angle = ParticleSampleGame.RandomBetween(0, MathHelper.TwoPi);
+            return new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
+        }
+
+        /// <summary>
+        /// overriden from DrawableGameComponent, Update will update all of the active
+        /// particles.
+        /// </summary>
+        public override void Update(GameTime gameTime)
+        {
+            // calculate dt, the change in the since the last frame. the particle
+            // updates will use this value.
+            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+            // go through all of the particles...
+            foreach (Particle p in particles)
+            {
+                
+                if (p.Active)
+                {
+                    // ... and if they're active, update them.
+                    p.Update(dt);
+                    // if that update finishes them, put them onto the free particles
+                    // queue.
+                    if (!p.Active)
+                    {
+                        freeParticles.Enqueue(p);
+                    }
+                }   
+            }
+
+            base.Update(gameTime);
+        }
+
+        /// <summary>
+        /// overriden from DrawableGameComponent, Draw will use ParticleSampleGame's 
+        /// sprite batch to render all of the active particles.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            // tell sprite batch to begin, using the spriteBlendMode specified in
+            // initializeConstants
+			game.SpriteBatch.Begin(SpriteSortMode.Deferred, blendState);
+            
+            foreach (Particle p in particles)
+            {
+                // skip inactive particles
+                if (!p.Active)
+                    continue;
+
+                // normalized lifetime is a value from 0 to 1 and represents how far
+                // a particle is through its life. 0 means it just started, .5 is half
+                // way through, and 1.0 means it's just about to be finished.
+                // this value will be used to calculate alpha and scale, to avoid 
+                // having particles suddenly appear or disappear.
+                float normalizedLifetime = p.TimeSinceStart / p.Lifetime;
+
+                // we want particles to fade in and fade out, so we'll calculate alpha
+                // to be (normalizedLifetime) * (1-normalizedLifetime). this way, when
+                // normalizedLifetime is 0 or 1, alpha is 0. the maximum value is at
+                // normalizedLifetime = .5, and is
+                // (normalizedLifetime) * (1-normalizedLifetime)
+                // (.5)                 * (1-.5)
+                // .25
+                // since we want the maximum alpha to be 1, not .25, we'll scale the 
+                // entire equation by 4.
+                float alpha = 4 * normalizedLifetime * (1 - normalizedLifetime);
+				Color color = Color.White * alpha;
+
+                // make particles grow as they age. they'll start at 75% of their size,
+                // and increase to 100% once they're finished.
+                float scale = p.Scale * (.75f + .25f * normalizedLifetime);
+
+                game.SpriteBatch.Draw(texture, p.Position, null, color,
+                    p.Rotation, origin, scale, SpriteEffects.None, 0.0f);
+            }
+
+            game.SpriteBatch.End();
+
+            base.Draw(gameTime);
+        }
+    }
+}

+ 30 - 0
ParticleSystem/Properties/AssemblyInfo.cs

@@ -0,0 +1,30 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ParticleSystem")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ParticleSystem")]
+[assembly: AssemblyCopyright("Copyright ©  2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: NeutralResourcesLanguage("en")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 4 - 0
ParticleSystem/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="MonoGame.Framework.Portable" version="3.6.0.1625" targetFramework="portable45-net45+win8" />
+</packages>

BIN
Platforms/DesktopGL/Icon.bmp


BIN
Platforms/DesktopGL/Icon.ico


+ 20 - 0
Platforms/DesktopGL/Program.cs

@@ -0,0 +1,20 @@
+using System;
+
+namespace XMLContentLoadingSample.DesktopGL
+{
+    /// <summary>
+    /// The main class.
+    /// </summary>
+    public static class Program
+    {
+        /// <summary>
+        /// The main entry point for the application.
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            using (var game = new Particle3DSampleGame())
+                game.Run();
+        }
+    }
+}

+ 36 - 0
Platforms/DesktopGL/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DesktopGL")]
+[assembly: AssemblyProduct("DesktopGL")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyCopyright("Copyright ©  2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("93c483e6-3025-45ec-ae0c-adf464559ecc")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 124 - 0
Platforms/DesktopGL/XMLContentLoadingSample.DesktopGL.csproj

@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{E606F782-2258-49B2-8BD6-A6DE3ACFD01E}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>XMLContentLoadingSample.DesktopGL</RootNamespace>
+    <AssemblyName>XMLContentLoadingSample.DesktopGL</AssemblyName>
+    <FileAlignment>512</FileAlignment>
+    <MonoGamePlatform>DesktopGL</MonoGamePlatform>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath>
+    <DefineConstants>DEBUG;TRACE;LINUX</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <Prefer32Bit>false</Prefer32Bit>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
+    <OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath>
+    <DefineConstants>TRACE;LINUX</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <Prefer32Bit>false</Prefer32Bit>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationIcon>Icon.ico</ApplicationIcon>
+  </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="MonoGame.Framework">
+      <HintPath>$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\MonoGame.Framework.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Icon.ico" />
+    <EmbeddedResource Include="Icon.bmp" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x86\SDL2.dll">
+      <Link>x86\SDL2.dll</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x64\SDL2.dll">
+      <Link>x64\SDL2.dll</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x86\soft_oal.dll">
+      <Link>x86\soft_oal.dll</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x64\soft_oal.dll">
+      <Link>x64\soft_oal.dll</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x86\libSDL2-2.0.so.0">
+      <Link>x86\libSDL2-2.0.so.0</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x64\libSDL2-2.0.so.0">
+      <Link>x64\libSDL2-2.0.so.0</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x86\libopenal.so.1">
+      <Link>x86\libopenal.so.1</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x64\libopenal.so.1">
+      <Link>x64\libopenal.so.1</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\libSDL2-2.0.0.dylib">
+      <Link>libSDL2-2.0.0.dylib</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\libopenal.1.dylib">
+      <Link>libopenal.1.dylib</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\MonoGame.Framework.dll.config">
+      <Link>MonoGame.Framework.dll.config</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <MonoGameContentReference Include="..\..\Content\Content.mgcb">
+      <Link>Content\Content.mgcb</Link>
+    </MonoGameContentReference>
+    <None Include="app.manifest" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\XMLContentLoadingSample\XMLContentLoadingSample.csproj">
+      <Project>{18a0f682-c28f-43e9-be04-38e188b3379b}</Project>
+      <Name>XMLContentLoadingSample</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 42 - 0
Platforms/DesktopGL/app.manifest

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity version="1.0.0.0" name="DesktopGL"/>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+    <security>
+      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- A list of the Windows versions that this application has been tested on and is
+           is designed to work with. Uncomment the appropriate elements and Windows will 
+           automatically selected the most compatible environment. -->
+
+      <!-- Windows Vista -->
+      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
+
+      <!-- Windows 7 -->
+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+
+      <!-- Windows 8 -->
+      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+
+      <!-- Windows 8.1 -->
+      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+
+    </application>
+  </compatibility>
+
+  <application xmlns="urn:schemas-microsoft-com:asm.v3">
+    <windowsSettings>
+      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
+    </windowsSettings>
+  </application>
+
+</assembly>

BIN
Platforms/WindowsDX/Icon.ico


+ 22 - 0
Platforms/WindowsDX/Program.cs

@@ -0,0 +1,22 @@
+using System;
+
+namespace XMLContentLoadingSample.WindowsDX
+{
+#if WINDOWS || LINUX
+    /// <summary>
+    /// The main class.
+    /// </summary>
+    public static class Program
+    {
+        /// <summary>
+        /// The main entry point for the application.
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            using (var game = new Particle3DSampleGame())
+                game.Run();
+        }
+    }
+#endif
+}

+ 36 - 0
Platforms/WindowsDX/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("WindowsDX")]
+[assembly: AssemblyProduct("WindowsDX")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyCopyright("Copyright ©  2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("4032116e-4b51-41e0-9a0a-3e0981de318b")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 78 - 0
Platforms/WindowsDX/XMLContentLoadingSample.WindowsDX.csproj

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{65C24C9F-7E44-4ED8-9250-B04EB691EB96}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>WindowsDX</RootNamespace>
+    <AssemblyName>WindowsDX</AssemblyName>
+    <FileAlignment>512</FileAlignment>
+    <MonoGamePlatform>Windows</MonoGamePlatform>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath>
+    <DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath>
+    <DefineConstants>TRACE;WINDOWS</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationIcon>Icon.ico</ApplicationIcon>
+  </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="MonoGame.Framework">
+      <HintPath>$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\Windows\MonoGame.Framework.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Icon.ico" />
+  </ItemGroup>
+  <ItemGroup>
+    <MonoGameContentReference Include="..\..\Content\Content.mgcb">
+      <Link>Content\Content.mgcb</Link>
+    </MonoGameContentReference>
+    <None Include="app.manifest" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\XMLContentLoadingSample\XMLContentLoadingSample.csproj">
+      <Project>{18a0f682-c28f-43e9-be04-38e188b3379b}</Project>
+      <Name>XMLContentLoadingSample</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 42 - 0
Platforms/WindowsDX/app.manifest

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity version="1.0.0.0" name="WindowsDX"/>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+    <security>
+      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- A list of the Windows versions that this application has been tested on and is
+           is designed to work with. Uncomment the appropriate elements and Windows will 
+           automatically selected the most compatible environment. -->
+
+      <!-- Windows Vista -->
+      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
+
+      <!-- Windows 7 -->
+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+
+      <!-- Windows 8 -->
+      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+
+      <!-- Windows 8.1 -->
+      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+
+    </application>
+  </compatibility>
+
+  <application xmlns="urn:schemas-microsoft-com:asm.v3">
+    <windowsSettings>
+      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
+    </windowsSettings>
+  </application>
+
+</assembly>

+ 17 - 2
README.md

@@ -1,2 +1,17 @@
-# XMLContentLoading
-XML Content pipeline sample based off the XNA XML Particles 3D source upgraded to MonoGame
+#Authoring Particle Systems Using XML and the Content Pipeline#
+
+Description:
+
+This tutorial teaches you how to extend the [Particles 3D](https://github.com/DDReaper/XNAGameStudio/wiki/Particles 3D) sample so the particle systems are defined by XML files loaded with the Content Pipeline.
+
+
+Sample Overview
+
+In the original version of the sample, each particle system was defined by using a separate class that derived from the abstract ParticleSystem base class, and overloaded the InitializeSettings method to specify how the particle system should be displayed. That meant you had to create a new class any time you wanted to add a new type of particle, and had to recompile your game any time you modified one of these classes to alter the particle behavior. Using external XML files allows you to create and tweak any number of different particle systems without needing to change the main game executable.
+
+
+All content and source code downloaded from this page are bound to the Microsoft Permissive License (Ms-PL).
+
+![](https://github.com/DDReaper/XNAGameStudio/blob/master/Images/XNA_Particle3D_01_small.jpg)![](https://github.com/DDReaper/XNAGameStudio/blob/master/Images/XNA_Particle3D_02_small.jpg)![](https://github.com/DDReaper/XNAGameStudio/blob/master/Images/XNA_Particle3D_03_small.jpg)
+
+		

+ 62 - 0
XMLContentLoadingSample.sln

@@ -0,0 +1,62 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26430.6
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XMLContentLoadingSample", "XMLContentLoadingSample\XMLContentLoadingSample.csproj", "{18A0F682-C28F-43E9-BE04-38E188B3379B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Platforms", "Platforms", "{FE76C17D-1849-4E9F-8C66-520B9A146F5B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParticleModel", "ParticleSystem\ParticleModel.csproj", "{3994E064-F047-433B-9388-7F1748B87AC0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XMLContentLoadingSample.DesktopGL", "Platforms\DesktopGL\XMLContentLoadingSample.DesktopGL.csproj", "{E606F782-2258-49B2-8BD6-A6DE3ACFD01E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XMLContentLoadingSample.WindowsDX", "Platforms\WindowsDX\XMLContentLoadingSample.WindowsDX.csproj", "{65C24C9F-7E44-4ED8-9250-B04EB691EB96}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{18A0F682-C28F-43E9-BE04-38E188B3379B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{18A0F682-C28F-43E9-BE04-38E188B3379B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{18A0F682-C28F-43E9-BE04-38E188B3379B}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{18A0F682-C28F-43E9-BE04-38E188B3379B}.Debug|x86.Build.0 = Debug|Any CPU
+		{18A0F682-C28F-43E9-BE04-38E188B3379B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{18A0F682-C28F-43E9-BE04-38E188B3379B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{18A0F682-C28F-43E9-BE04-38E188B3379B}.Release|x86.ActiveCfg = Release|Any CPU
+		{18A0F682-C28F-43E9-BE04-38E188B3379B}.Release|x86.Build.0 = Release|Any CPU
+		{3994E064-F047-433B-9388-7F1748B87AC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3994E064-F047-433B-9388-7F1748B87AC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3994E064-F047-433B-9388-7F1748B87AC0}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{3994E064-F047-433B-9388-7F1748B87AC0}.Debug|x86.Build.0 = Debug|Any CPU
+		{3994E064-F047-433B-9388-7F1748B87AC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3994E064-F047-433B-9388-7F1748B87AC0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3994E064-F047-433B-9388-7F1748B87AC0}.Release|x86.ActiveCfg = Release|Any CPU
+		{3994E064-F047-433B-9388-7F1748B87AC0}.Release|x86.Build.0 = Release|Any CPU
+		{E606F782-2258-49B2-8BD6-A6DE3ACFD01E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E606F782-2258-49B2-8BD6-A6DE3ACFD01E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E606F782-2258-49B2-8BD6-A6DE3ACFD01E}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{E606F782-2258-49B2-8BD6-A6DE3ACFD01E}.Debug|x86.Build.0 = Debug|Any CPU
+		{E606F782-2258-49B2-8BD6-A6DE3ACFD01E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E606F782-2258-49B2-8BD6-A6DE3ACFD01E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E606F782-2258-49B2-8BD6-A6DE3ACFD01E}.Release|x86.ActiveCfg = Release|Any CPU
+		{E606F782-2258-49B2-8BD6-A6DE3ACFD01E}.Release|x86.Build.0 = Release|Any CPU
+		{65C24C9F-7E44-4ED8-9250-B04EB691EB96}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{65C24C9F-7E44-4ED8-9250-B04EB691EB96}.Debug|x86.ActiveCfg = Debug|x86
+		{65C24C9F-7E44-4ED8-9250-B04EB691EB96}.Debug|x86.Build.0 = Debug|x86
+		{65C24C9F-7E44-4ED8-9250-B04EB691EB96}.Release|Any CPU.ActiveCfg = Release|x86
+		{65C24C9F-7E44-4ED8-9250-B04EB691EB96}.Release|x86.ActiveCfg = Release|x86
+		{65C24C9F-7E44-4ED8-9250-B04EB691EB96}.Release|x86.Build.0 = Release|x86
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{E606F782-2258-49B2-8BD6-A6DE3ACFD01E} = {FE76C17D-1849-4E9F-8C66-520B9A146F5B}
+		{65C24C9F-7E44-4ED8-9250-B04EB691EB96} = {FE76C17D-1849-4E9F-8C66-520B9A146F5B}
+	EndGlobalSection
+EndGlobal

+ 453 - 0
XMLContentLoadingSample/Game.cs

@@ -0,0 +1,453 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Game.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+#endregion
+
+namespace XMLContentLoadingSample
+{
+    /// <summary>
+    /// Sample showing how to implement a particle system entirely
+    /// on the GPU, using the vertex shader to animate particles.
+    /// </summary>
+    public class Particle3DSampleGame : Microsoft.Xna.Framework.Game
+    {
+        #region Fields
+
+
+        GraphicsDeviceManager graphics;
+
+        SpriteBatch spriteBatch;
+        SpriteFont font;
+        Model grid;
+
+
+        // This sample uses five different particle systems.
+        ParticleSystem explosionParticles;
+        ParticleSystem explosionSmokeParticles;
+        ParticleSystem projectileTrailParticles;
+        ParticleSystem smokePlumeParticles;
+        ParticleSystem fireParticles;
+
+
+        // The sample can switch between three different visual effects.
+        enum ParticleState
+        {
+            Explosions,
+            SmokePlume,
+            RingOfFire,
+        };
+
+        ParticleState currentState = ParticleState.Explosions;
+
+
+        // The explosions effect works by firing projectiles up into the
+        // air, so we need to keep track of all the active projectiles.
+        List<Projectile> projectiles = new List<Projectile>();
+
+        TimeSpan timeToNextProjectile = TimeSpan.Zero;
+
+
+        // Random number generator for the fire effect.
+        Random random = new Random();
+
+
+        // Input state.
+        KeyboardState currentKeyboardState;
+        GamePadState currentGamePadState;
+
+        KeyboardState lastKeyboardState;
+        GamePadState lastGamePadState;
+
+
+        // Camera state.
+        float cameraArc = -5;
+        float cameraRotation = 0;
+        float cameraDistance = 200;
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public Particle3DSampleGame()
+        {
+            graphics = new GraphicsDeviceManager(this);
+            graphics.GraphicsProfile = GraphicsProfile.HiDef;
+            Content.RootDirectory = "Content";
+
+            // Construct our particle system components.
+            explosionParticles = new ParticleSystem(this, Content, "ExplosionSettings");
+            explosionSmokeParticles = new ParticleSystem(this, Content, "ExplosionSmokeSettings");
+            projectileTrailParticles = new ParticleSystem(this, Content, "ProjectileTrailSettings");
+            smokePlumeParticles = new ParticleSystem(this, Content, "SmokePlumeSettings");
+            fireParticles = new ParticleSystem(this, Content, "FireSettings");
+
+            // Set the draw order so the explosions and fire
+            // will appear over the top of the smoke.
+            smokePlumeParticles.DrawOrder = 100;
+            explosionSmokeParticles.DrawOrder = 200;
+            projectileTrailParticles.DrawOrder = 300;
+            explosionParticles.DrawOrder = 400;
+            fireParticles.DrawOrder = 500;
+
+            // Register the particle system components.
+            Components.Add(explosionParticles);
+            Components.Add(explosionSmokeParticles);
+            Components.Add(projectileTrailParticles);
+            Components.Add(smokePlumeParticles);
+            Components.Add(fireParticles);
+        }
+
+
+        /// <summary>
+        /// Load your graphics content.
+        /// </summary>
+        protected override void LoadContent()
+        {
+            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
+
+            font = Content.Load<SpriteFont>("font");
+            grid = Content.Load<Model>("grid");
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Allows the game to run logic.
+        /// </summary>
+        protected override void Update(GameTime gameTime)
+        {
+            HandleInput();
+
+            UpdateCamera(gameTime);
+
+            switch (currentState)
+            {
+                case ParticleState.Explosions:
+                    UpdateExplosions(gameTime);
+                    break;
+
+                case ParticleState.SmokePlume:
+                    UpdateSmokePlume();
+                    break;
+
+                case ParticleState.RingOfFire:
+                    UpdateFire();
+                    break;
+            }
+
+            UpdateProjectiles(gameTime);
+
+            base.Update(gameTime);
+        }
+
+
+        /// <summary>
+        /// Helper for updating the explosions effect.
+        /// </summary>
+        void UpdateExplosions(GameTime gameTime)
+        {
+            timeToNextProjectile -= gameTime.ElapsedGameTime;
+
+            if (timeToNextProjectile <= TimeSpan.Zero)
+            {
+                // Create a new projectile once per second. The real work of moving
+                // and creating particles is handled inside the Projectile class.
+                projectiles.Add(new Projectile(explosionParticles,
+                                               explosionSmokeParticles,
+                                               projectileTrailParticles));
+
+                timeToNextProjectile += TimeSpan.FromSeconds(1);
+            }
+        }
+
+
+        /// <summary>
+        /// Helper for updating the list of active projectiles.
+        /// </summary>
+        void UpdateProjectiles(GameTime gameTime)
+        {
+            int i = 0;
+
+            while (i < projectiles.Count)
+            {
+                if (!projectiles[i].Update(gameTime))
+                {
+                    // Remove projectiles at the end of their life.
+                    projectiles.RemoveAt(i);
+                }
+                else
+                {
+                    // Advance to the next projectile.
+                    i++;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Helper for updating the smoke plume effect.
+        /// </summary>
+        void UpdateSmokePlume()
+        {
+            // This is trivial: we just create one new smoke particle per frame.
+            smokePlumeParticles.AddParticle(Vector3.Zero, Vector3.Zero);
+        }
+
+
+        /// <summary>
+        /// Helper for updating the fire effect.
+        /// </summary>
+        void UpdateFire()
+        {
+            const int fireParticlesPerFrame = 20;
+
+            // Create a number of fire particles, randomly positioned around a circle.
+            for (int i = 0; i < fireParticlesPerFrame; i++)
+            {
+                fireParticles.AddParticle(RandomPointOnCircle(), Vector3.Zero);
+            }
+
+            // Create one smoke particle per frmae, too.
+            smokePlumeParticles.AddParticle(RandomPointOnCircle(), Vector3.Zero);
+        }
+
+
+        /// <summary>
+        /// Helper used by the UpdateFire method. Chooses a random location
+        /// around a circle, at which a fire particle will be created.
+        /// </summary>
+        Vector3 RandomPointOnCircle()
+        {
+            const float radius = 30;
+            const float height = 40;
+
+            double angle = random.NextDouble() * Math.PI * 2;
+
+            float x = (float)Math.Cos(angle);
+            float y = (float)Math.Sin(angle);
+
+            return new Vector3(x * radius, y * radius + height, 0);
+        }
+
+
+        /// <summary>
+        /// This is called when the game should draw itself.
+        /// </summary>
+        protected override void Draw(GameTime gameTime)
+        {
+            GraphicsDevice device = graphics.GraphicsDevice;
+
+            device.Clear(Color.CornflowerBlue);
+
+            // Compute camera matrices.
+            float aspectRatio = (float)device.Viewport.Width /
+                                (float)device.Viewport.Height;
+
+            Matrix view = Matrix.CreateTranslation(0, -25, 0) *
+                          Matrix.CreateRotationY(MathHelper.ToRadians(cameraRotation)) *
+                          Matrix.CreateRotationX(MathHelper.ToRadians(cameraArc)) *
+                          Matrix.CreateLookAt(new Vector3(0, 0, -cameraDistance),
+                                              new Vector3(0, 0, 0), Vector3.Up);
+
+            Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
+                                                                    aspectRatio,
+                                                                    1, 10000);
+
+            // Pass camera matrices through to the particle system components.
+            explosionParticles.SetCamera(view, projection);
+            explosionSmokeParticles.SetCamera(view, projection);
+            projectileTrailParticles.SetCamera(view, projection);
+            smokePlumeParticles.SetCamera(view, projection);
+            fireParticles.SetCamera(view, projection);
+
+            // Draw our background grid and message text.
+            DrawGrid(view, projection);
+
+            DrawMessage();
+
+            // This will draw the particle system components.
+            base.Draw(gameTime);
+        }
+
+
+        /// <summary>
+        /// Helper for drawing the background grid model.
+        /// </summary>
+        void DrawGrid(Matrix view, Matrix projection)
+        {
+            GraphicsDevice device = graphics.GraphicsDevice;
+
+            device.BlendState = BlendState.Opaque;
+            device.DepthStencilState = DepthStencilState.Default;
+            device.SamplerStates[0] = SamplerState.LinearWrap;
+
+            grid.Draw(Matrix.Identity, view, projection);
+        }
+
+
+        /// <summary>
+        /// Helper for drawing our message text.
+        /// </summary>
+        void DrawMessage()
+        {
+            string message = string.Format("Current effect: {0}!!!\n" +
+                                           "Hit the A button or space bar to switch.",
+                                           currentState);
+
+            spriteBatch.Begin();
+            spriteBatch.DrawString(font, message, new Vector2(50, 50), Color.White);
+            spriteBatch.End();
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Handles input for quitting the game and cycling
+        /// through the different particle effects.
+        /// </summary>
+        void HandleInput()
+        {
+            lastKeyboardState = currentKeyboardState;
+            lastGamePadState = currentGamePadState;
+
+            currentKeyboardState = Keyboard.GetState();
+            currentGamePadState = GamePad.GetState(PlayerIndex.One);
+
+            // Check for exit.
+            if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
+                currentGamePadState.Buttons.Back == ButtonState.Pressed)
+            {
+                Exit();
+            }
+
+            // Check for changing the active particle effect.
+            if (((currentKeyboardState.IsKeyDown(Keys.Space) &&
+                 (lastKeyboardState.IsKeyUp(Keys.Space))) ||
+                ((currentGamePadState.Buttons.A == ButtonState.Pressed)) &&
+                 (lastGamePadState.Buttons.A == ButtonState.Released)))
+            {
+                currentState++;
+
+                if (currentState > ParticleState.RingOfFire)
+                    currentState = 0;
+            }
+        }
+
+
+        /// <summary>
+        /// Handles input for moving the camera.
+        /// </summary>
+        void UpdateCamera(GameTime gameTime)
+        {
+            float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
+
+            // Check for input to rotate the camera up and down around the model.
+            if (currentKeyboardState.IsKeyDown(Keys.Up) ||
+                currentKeyboardState.IsKeyDown(Keys.W))
+            {
+                cameraArc += time * 0.025f;
+            }
+
+            if (currentKeyboardState.IsKeyDown(Keys.Down) ||
+                currentKeyboardState.IsKeyDown(Keys.S))
+            {
+                cameraArc -= time * 0.025f;
+            }
+
+            cameraArc += currentGamePadState.ThumbSticks.Right.Y * time * 0.05f;
+
+            // Limit the arc movement.
+            if (cameraArc > 90.0f)
+                cameraArc = 90.0f;
+            else if (cameraArc < -90.0f)
+                cameraArc = -90.0f;
+
+            // Check for input to rotate the camera around the model.
+            if (currentKeyboardState.IsKeyDown(Keys.Right) ||
+                currentKeyboardState.IsKeyDown(Keys.D))
+            {
+                cameraRotation += time * 0.05f;
+            }
+
+            if (currentKeyboardState.IsKeyDown(Keys.Left) ||
+                currentKeyboardState.IsKeyDown(Keys.A))
+            {
+                cameraRotation -= time * 0.05f;
+            }
+
+            cameraRotation += currentGamePadState.ThumbSticks.Right.X * time * 0.1f;
+
+            // Check for input to zoom camera in and out.
+            if (currentKeyboardState.IsKeyDown(Keys.Z))
+                cameraDistance += time * 0.25f;
+
+            if (currentKeyboardState.IsKeyDown(Keys.X))
+                cameraDistance -= time * 0.25f;
+
+            cameraDistance += currentGamePadState.Triggers.Left * time * 0.5f;
+            cameraDistance -= currentGamePadState.Triggers.Right * time * 0.5f;
+
+            // Limit the camera distance.
+            if (cameraDistance > 500)
+                cameraDistance = 500;
+            else if (cameraDistance < 10)
+                cameraDistance = 10;
+
+            if (currentGamePadState.Buttons.RightStick == ButtonState.Pressed ||
+                currentKeyboardState.IsKeyDown(Keys.R))
+            {
+                cameraArc = -5;
+                cameraRotation = 0;
+                cameraDistance = 200;
+            }
+        }
+
+
+        #endregion
+    }
+
+
+    #region Entry Point
+
+    /// <summary>
+    /// The main entry point for the application.
+    /// </summary>
+    static class Program
+    {
+        static void Main()
+        {
+            using (Particle3DSampleGame game = new Particle3DSampleGame())
+            {
+                game.Run();
+            }
+        }
+    }
+
+    #endregion
+}

+ 112 - 0
XMLContentLoadingSample/ParticleEmitter.cs

@@ -0,0 +1,112 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ParticleEmitter.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace XMLContentLoadingSample
+{
+    /// <summary>
+    /// Helper for objects that want to leave particles behind them as they
+    /// move around the world. This emitter implementation solves two related
+    /// problems:
+    /// 
+    /// If an object wants to create particles very slowly, less than once per
+    /// frame, it can be a pain to keep track of which updates ought to create
+    /// a new particle versus which should not.
+    /// 
+    /// If an object is moving quickly and is creating many particles per frame,
+    /// it will look ugly if these particles are all bunched up together. Much
+    /// better if they can be spread out along a line between where the object
+    /// is now and where it was on the previous frame. This is particularly
+    /// important for leaving trails behind fast moving objects such as rockets.
+    /// 
+    /// This emitter class keeps track of a moving object, remembering its
+    /// previous position so it can calculate the velocity of the object. It
+    /// works out the perfect locations for creating particles at any frequency
+    /// you specify, regardless of whether this is faster or slower than the
+    /// game update rate.
+    /// </summary>
+    public class ParticleEmitter
+    {
+        #region Fields
+
+        ParticleSystem particleSystem;
+        float timeBetweenParticles;
+        Vector3 previousPosition;
+        float timeLeftOver;
+
+        #endregion
+
+
+        /// <summary>
+        /// Constructs a new particle emitter object.
+        /// </summary>
+        public ParticleEmitter(ParticleSystem particleSystem,
+                               float particlesPerSecond, Vector3 initialPosition)
+        {
+            this.particleSystem = particleSystem;
+
+            timeBetweenParticles = 1.0f / particlesPerSecond;
+            
+            previousPosition = initialPosition;
+        }
+
+
+        /// <summary>
+        /// Updates the emitter, creating the appropriate number of particles
+        /// in the appropriate positions.
+        /// </summary>
+        public void Update(GameTime gameTime, Vector3 newPosition)
+        {
+            if (gameTime == null)
+                throw new ArgumentNullException("gameTime");
+
+            // Work out how much time has passed since the previous update.
+            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+            if (elapsedTime > 0)
+            {
+                // Work out how fast we are moving.
+                Vector3 velocity = (newPosition - previousPosition) / elapsedTime;
+
+                // If we had any time left over that we didn't use during the
+                // previous update, add that to the current elapsed time.
+                float timeToSpend = timeLeftOver + elapsedTime;
+                
+                // Counter for looping over the time interval.
+                float currentTime = -timeLeftOver;
+
+                // Create particles as long as we have a big enough time interval.
+                while (timeToSpend > timeBetweenParticles)
+                {
+                    currentTime += timeBetweenParticles;
+                    timeToSpend -= timeBetweenParticles;
+
+                    // Work out the optimal position for this particle. This will produce
+                    // evenly spaced particles regardless of the object speed, particle
+                    // creation frequency, or game update rate.
+                    float mu = currentTime / elapsedTime;
+
+                    Vector3 position = Vector3.Lerp(previousPosition, newPosition, mu);
+
+                    // Create the particle.
+                    particleSystem.AddParticle(position, velocity);
+                }
+
+                // Store any time we didn't use, so it can be part of the next update.
+                timeLeftOver = timeToSpend;
+            }
+
+            previousPosition = newPosition;
+        }
+    }
+}

+ 62 - 0
XMLContentLoadingSample/ParticleVertex.cs

@@ -0,0 +1,62 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ParticleVertex.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Graphics.PackedVector;
+#endregion
+
+namespace XMLContentLoadingSample
+{
+    /// <summary>
+    /// Custom vertex structure for drawing particles.
+    /// </summary>
+    struct ParticleVertex
+    {
+        // Stores which corner of the particle quad this vertex represents.
+        public Short2 Corner;
+
+        // Stores the starting position of the particle.
+        public Vector3 Position;
+
+        // Stores the starting velocity of the particle.
+        public Vector3 Velocity;
+
+        // Four random values, used to make each particle look slightly different.
+        public Color Random;
+
+        // The time (in seconds) at which this particle was created.
+        public float Time;
+
+
+        // Describe the layout of this vertex structure.
+        public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration
+        (
+            new VertexElement(0, VertexElementFormat.Short2,
+                                 VertexElementUsage.Position, 0),
+
+            new VertexElement(4, VertexElementFormat.Vector3,
+                                 VertexElementUsage.Position, 1),
+
+            new VertexElement(16, VertexElementFormat.Vector3,
+                                  VertexElementUsage.Normal, 0),
+
+            new VertexElement(28, VertexElementFormat.Color,
+                                  VertexElementUsage.Color, 0),
+
+            new VertexElement(32, VertexElementFormat.Single,
+                                  VertexElementUsage.TextureCoordinate, 0)
+        );
+
+
+        // Describe the size of this vertex structure.
+        public const int SizeInBytes = 36;
+    }
+}

+ 108 - 0
XMLContentLoadingSample/Projectile.cs

@@ -0,0 +1,108 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Projectile.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace XMLContentLoadingSample
+{
+    /// <summary>
+    /// This class demonstrates how to combine several different particle systems
+    /// to build up a more sophisticated composite effect. It implements a rocket
+    /// projectile, which arcs up into the sky using a ParticleEmitter to leave a
+    /// steady stream of trail particles behind it. After a while it explodes,
+    /// creating a sudden burst of explosion and smoke particles.
+    /// </summary>
+    class Projectile
+    {
+        #region Constants
+
+        const float trailParticlesPerSecond = 200;
+        const int numExplosionParticles = 30;
+        const int numExplosionSmokeParticles = 50;
+        const float projectileLifespan = 1.5f;
+        const float sidewaysVelocityRange = 60;
+        const float verticalVelocityRange = 40;
+        const float gravity = 15;
+
+        #endregion
+
+        #region Fields
+
+        ParticleSystem explosionParticles;
+        ParticleSystem explosionSmokeParticles;
+        ParticleEmitter trailEmitter;
+
+        Vector3 position;
+        Vector3 velocity;
+        float age;
+
+        static Random random = new Random();
+
+        #endregion
+
+
+        /// <summary>
+        /// Constructs a new projectile.
+        /// </summary>
+        public Projectile(ParticleSystem explosionParticles,
+                          ParticleSystem explosionSmokeParticles,
+                          ParticleSystem projectileTrailParticles)
+        {
+            this.explosionParticles = explosionParticles;
+            this.explosionSmokeParticles = explosionSmokeParticles;
+
+            // Start at the origin, firing in a random (but roughly upward) direction.
+            position = Vector3.Zero;
+
+            velocity.X = (float)(random.NextDouble() - 0.5) * sidewaysVelocityRange;
+            velocity.Y = (float)(random.NextDouble() + 0.5) * verticalVelocityRange;
+            velocity.Z = (float)(random.NextDouble() - 0.5) * sidewaysVelocityRange;
+
+            // Use the particle emitter helper to output our trail particles.
+            trailEmitter = new ParticleEmitter(projectileTrailParticles,
+                                               trailParticlesPerSecond, position);
+        }
+
+
+        /// <summary>
+        /// Updates the projectile.
+        /// </summary>
+        public bool Update(GameTime gameTime)
+        {
+            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+            // Simple projectile physics.
+            position += velocity * elapsedTime;
+            velocity.Y -= elapsedTime * gravity;
+            age += elapsedTime;
+
+            // Update the particle emitter, which will create our particle trail.
+            trailEmitter.Update(gameTime, position);
+
+            // If enough time has passed, explode! Note how we pass our velocity
+            // in to the AddParticle method: this lets the explosion be influenced
+            // by the speed and direction of the projectile which created it.
+            if (age > projectileLifespan)
+            {
+                for (int i = 0; i < numExplosionParticles; i++)
+                    explosionParticles.AddParticle(position, velocity);
+
+                for (int i = 0; i < numExplosionSmokeParticles; i++)
+                    explosionSmokeParticles.AddParticle(position, velocity);
+
+                return false;
+            }
+                
+            return true;
+        }
+    }
+}

+ 30 - 0
XMLContentLoadingSample/Properties/AssemblyInfo.cs

@@ -0,0 +1,30 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("XMLContentLoadingSample")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("XMLContentLoadingSample")]
+[assembly: AssemblyCopyright("Copyright ©  2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: NeutralResourcesLanguage("en")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 58 - 0
XMLContentLoadingSample/XMLContentLoadingSample.csproj

@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{18A0F682-C28F-43E9-BE04-38E188B3379B}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>XMLContentLoadingSample</RootNamespace>
+    <AssemblyName>XMLContentLoadingSample</AssemblyName>
+    <DefaultLanguage>en-US</DefaultLanguage>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="Game.cs" />
+    <Compile Include="ParticleEmitter.cs" />
+    <Compile Include="ParticleVertex.cs" />
+    <Compile Include="Projectile.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="XMLParticleSystem.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="MonoGame.Framework, Version=3.6.0.1625, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\MonoGame.Framework.Portable.3.6.0.1625\lib\portable-net45+win8+wpa81\MonoGame.Framework.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\ParticleSystem\ParticleModel.csproj">
+      <Project>{3994e064-f047-433b-9388-7f1748b87ac0}</Project>
+      <Name>ParticleModel</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
+</Project>

+ 544 - 0
XMLContentLoadingSample/XMLParticleSystem.cs

@@ -0,0 +1,544 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Graphics.PackedVector;
+using XMLContentLoadingSample.ParticleModel;
+#endregion
+
+namespace XMLContentLoadingSample
+{
+    /// <summary>
+    /// The main component in charge of displaying particles.
+    /// </summary>
+    public class ParticleSystem : DrawableGameComponent
+    {
+        #region Fields
+
+
+        // Name of the XML settings file describing this particle system.
+        string settingsName;
+        
+        
+        // Settings class controls the appearance and animation of this particle system.
+        ParticleSettings settings;
+
+
+        // For loading the effect and particle texture.
+        ContentManager content;
+
+
+        // Custom effect for drawing particles. This computes the particle
+        // animation entirely in the vertex shader: no per-particle CPU work required!
+        Effect particleEffect;
+
+
+        // Shortcuts for accessing frequently changed effect parameters.
+        EffectParameter effectViewParameter;
+        EffectParameter effectProjectionParameter;
+        EffectParameter effectViewportScaleParameter;
+        EffectParameter effectTimeParameter;
+
+
+        // An array of particles, treated as a circular queue.
+        ParticleVertex[] particles;
+
+
+        // A vertex buffer holding our particles. This contains the same data as
+        // the particles array, but copied across to where the GPU can access it.
+        DynamicVertexBuffer vertexBuffer;
+
+
+        // Index buffer turns sets of four vertices into particle quads (pairs of triangles).
+        IndexBuffer indexBuffer;
+
+
+        // The particles array and vertex buffer are treated as a circular queue.
+        // Initially, the entire contents of the array are free, because no particles
+        // are in use. When a new particle is created, this is allocated from the
+        // beginning of the array. If more than one particle is created, these will
+        // always be stored in a consecutive block of array elements. Because all
+        // particles last for the same amount of time, old particles will always be
+        // removed in order from the start of this active particle region, so the
+        // active and free regions will never be intermingled. Because the queue is
+        // circular, there can be times when the active particle region wraps from the
+        // end of the array back to the start. The queue uses modulo arithmetic to
+        // handle these cases. For instance with a four entry queue we could have:
+        //
+        //      0
+        //      1 - first active particle
+        //      2 
+        //      3 - first free particle
+        //
+        // In this case, particles 1 and 2 are active, while 3 and 4 are free.
+        // Using modulo arithmetic we could also have:
+        //
+        //      0
+        //      1 - first free particle
+        //      2 
+        //      3 - first active particle
+        //
+        // Here, 3 and 0 are active, while 1 and 2 are free.
+        //
+        // But wait! The full story is even more complex.
+        //
+        // When we create a new particle, we add them to our managed particles array.
+        // We also need to copy this new data into the GPU vertex buffer, but we don't
+        // want to do that straight away, because setting new data into a vertex buffer
+        // can be an expensive operation. If we are going to be adding several particles
+        // in a single frame, it is faster to initially just store them in our managed
+        // array, and then later upload them all to the GPU in one single call. So our
+        // queue also needs a region for storing new particles that have been added to
+        // the managed array but not yet uploaded to the vertex buffer.
+        //
+        // Another issue occurs when old particles are retired. The CPU and GPU run
+        // asynchronously, so the GPU will often still be busy drawing the previous
+        // frame while the CPU is working on the next frame. This can cause a
+        // synchronization problem if an old particle is retired, and then immediately
+        // overwritten by a new one, because the CPU might try to change the contents
+        // of the vertex buffer while the GPU is still busy drawing the old data from
+        // it. Normally the graphics driver will take care of this by waiting until
+        // the GPU has finished drawing inside the VertexBuffer.SetData call, but we
+        // don't want to waste time waiting around every time we try to add a new
+        // particle! To avoid this delay, we can specify the SetDataOptions.NoOverwrite
+        // flag when we write to the vertex buffer. This basically means "I promise I
+        // will never try to overwrite any data that the GPU might still be using, so
+        // you can just go ahead and update the buffer straight away". To keep this
+        // promise, we must avoid reusing vertices immediately after they are drawn.
+        //
+        // So in total, our queue contains four different regions:
+        //
+        // Vertices between firstActiveParticle and firstNewParticle are actively
+        // being drawn, and exist in both the managed particles array and the GPU
+        // vertex buffer.
+        //
+        // Vertices between firstNewParticle and firstFreeParticle are newly created,
+        // and exist only in the managed particles array. These need to be uploaded
+        // to the GPU at the start of the next draw call.
+        //
+        // Vertices between firstFreeParticle and firstRetiredParticle are free and
+        // waiting to be allocated.
+        //
+        // Vertices between firstRetiredParticle and firstActiveParticle are no longer
+        // being drawn, but were drawn recently enough that the GPU could still be
+        // using them. These need to be kept around for a few more frames before they
+        // can be reallocated.
+
+        int firstActiveParticle;
+        int firstNewParticle;
+        int firstFreeParticle;
+        int firstRetiredParticle;
+
+
+        // Store the current time, in seconds.
+        float currentTime;
+
+
+        // Count how many times Draw has been called. This is used to know
+        // when it is safe to retire old particles back into the free list.
+        int drawCounter;
+
+
+        // Shared random number generator.
+        static Random random = new Random();
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public ParticleSystem(Game game, ContentManager content, string settingsName)
+            : base(game)
+        {
+            this.content = content;
+            this.settingsName = settingsName;
+        }
+
+
+        /// <summary>
+        /// Loads graphics for the particle system.
+        /// </summary>
+        protected override void LoadContent()
+        {
+            settings = content.Load<ParticleSettings>(settingsName);
+            
+            // Allocate the particle array, and fill in the corner fields (which never change).
+            particles = new ParticleVertex[settings.MaxParticles * 4];
+
+            for (int i = 0; i < settings.MaxParticles; i++)
+            {
+                particles[i * 4 + 0].Corner = new Short2(-1, -1);
+                particles[i * 4 + 1].Corner = new Short2(1, -1);
+                particles[i * 4 + 2].Corner = new Short2(1, 1);
+                particles[i * 4 + 3].Corner = new Short2(-1, 1);
+            }
+
+            LoadParticleEffect();
+
+            // Create a dynamic vertex buffer.
+            vertexBuffer = new DynamicVertexBuffer(GraphicsDevice, ParticleVertex.VertexDeclaration,
+                                                   settings.MaxParticles * 4, BufferUsage.WriteOnly);
+
+            // Create and populate the index buffer.
+            ushort[] indices = new ushort[settings.MaxParticles * 6];
+
+            for (int i = 0; i < settings.MaxParticles; i++)
+            {
+                indices[i * 6 + 0] = (ushort)(i * 4 + 0);
+                indices[i * 6 + 1] = (ushort)(i * 4 + 1);
+                indices[i * 6 + 2] = (ushort)(i * 4 + 2);
+
+                indices[i * 6 + 3] = (ushort)(i * 4 + 0);
+                indices[i * 6 + 4] = (ushort)(i * 4 + 2);
+                indices[i * 6 + 5] = (ushort)(i * 4 + 3);
+            }
+
+            indexBuffer = new IndexBuffer(GraphicsDevice, typeof(ushort), indices.Length, BufferUsage.WriteOnly);
+
+            indexBuffer.SetData(indices);
+        }
+
+
+        /// <summary>
+        /// Helper for loading and initializing the particle effect.
+        /// </summary>
+        void LoadParticleEffect()
+        {
+            Effect effect = content.Load<Effect>("ParticleEffect");
+
+            // If we have several particle systems, the content manager will return
+            // a single shared effect instance to them all. But we want to preconfigure
+            // the effect with parameters that are specific to this particular
+            // particle system. By cloning the effect, we prevent one particle system
+            // from stomping over the parameter settings of another.
+            
+            particleEffect = effect.Clone();
+
+            EffectParameterCollection parameters = particleEffect.Parameters;
+
+            // Look up shortcuts for parameters that change every frame.
+            effectViewParameter = parameters["View"];
+            effectProjectionParameter = parameters["Projection"];
+            effectViewportScaleParameter = parameters["ViewportScale"];
+            effectTimeParameter = parameters["CurrentTime"];
+
+            // Set the values of parameters that do not change.
+            parameters["Duration"].SetValue((float)settings.Duration.TotalSeconds);
+            parameters["DurationRandomness"].SetValue(settings.DurationRandomness);
+            parameters["Gravity"].SetValue(settings.Gravity);
+            parameters["EndVelocity"].SetValue(settings.EndVelocity);
+            parameters["MinColor"].SetValue(settings.MinColor.ToVector4());
+            parameters["MaxColor"].SetValue(settings.MaxColor.ToVector4());
+
+            parameters["RotateSpeed"].SetValue(
+                new Vector2(settings.MinRotateSpeed, settings.MaxRotateSpeed));
+            
+            parameters["StartSize"].SetValue(
+                new Vector2(settings.MinStartSize, settings.MaxStartSize));
+            
+            parameters["EndSize"].SetValue(
+                new Vector2(settings.MinEndSize, settings.MaxEndSize));
+
+            // Load the particle texture, and set it onto the effect.
+            Texture2D texture = content.Load<Texture2D>(settings.TextureName);
+
+            parameters["Texture"].SetValue(texture);
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Updates the particle system.
+        /// </summary>
+        public override void Update(GameTime gameTime)
+        {
+            if (gameTime == null)
+                throw new ArgumentNullException("gameTime");
+
+            currentTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+            RetireActiveParticles();
+            FreeRetiredParticles();
+
+            // If we let our timer go on increasing for ever, it would eventually
+            // run out of floating point precision, at which point the particles
+            // would render incorrectly. An easy way to prevent this is to notice
+            // that the time value doesn't matter when no particles are being drawn,
+            // so we can reset it back to zero any time the active queue is empty.
+
+            if (firstActiveParticle == firstFreeParticle)
+                currentTime = 0;
+
+            if (firstRetiredParticle == firstActiveParticle)
+                drawCounter = 0;
+        }
+
+
+        /// <summary>
+        /// Helper for checking when active particles have reached the end of
+        /// their life. It moves old particles from the active area of the queue
+        /// to the retired section.
+        /// </summary>
+        void RetireActiveParticles()
+        {
+            float particleDuration = (float)settings.Duration.TotalSeconds;
+
+            while (firstActiveParticle != firstNewParticle)
+            {
+                // Is this particle old enough to retire?
+                // We multiply the active particle index by four, because each
+                // particle consists of a quad that is made up of four vertices.
+                float particleAge = currentTime - particles[firstActiveParticle * 4].Time;
+
+                if (particleAge < particleDuration)
+                    break;
+
+                // Remember the time at which we retired this particle.
+                particles[firstActiveParticle * 4].Time = drawCounter;
+
+                // Move the particle from the active to the retired queue.
+                firstActiveParticle++;
+
+                if (firstActiveParticle >= settings.MaxParticles)
+                    firstActiveParticle = 0;
+            }
+        }
+
+
+        /// <summary>
+        /// Helper for checking when retired particles have been kept around long
+        /// enough that we can be sure the GPU is no longer using them. It moves
+        /// old particles from the retired area of the queue to the free section.
+        /// </summary>
+        void FreeRetiredParticles()
+        {
+            while (firstRetiredParticle != firstActiveParticle)
+            {
+                // Has this particle been unused long enough that
+                // the GPU is sure to be finished with it?
+                // We multiply the retired particle index by four, because each
+                // particle consists of a quad that is made up of four vertices.
+                int age = drawCounter - (int)particles[firstRetiredParticle * 4].Time;
+
+                // The GPU is never supposed to get more than 2 frames behind the CPU.
+                // We add 1 to that, just to be safe in case of buggy drivers that
+                // might bend the rules and let the GPU get further behind.
+                if (age < 3)
+                    break;
+
+                // Move the particle from the retired to the free queue.
+                firstRetiredParticle++;
+
+                if (firstRetiredParticle >= settings.MaxParticles)
+                    firstRetiredParticle = 0;
+            }
+        }
+
+        
+        /// <summary>
+        /// Draws the particle system.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            GraphicsDevice device = GraphicsDevice;
+
+            // Restore the vertex buffer contents if the graphics device was lost.
+            if (vertexBuffer.IsContentLost)
+            {
+                vertexBuffer.SetData(particles);
+            }
+
+            // If there are any particles waiting in the newly added queue,
+            // we'd better upload them to the GPU ready for drawing.
+            if (firstNewParticle != firstFreeParticle)
+            {
+                AddNewParticlesToVertexBuffer();
+            }
+
+            // If there are any active particles, draw them now!
+            if (firstActiveParticle != firstFreeParticle)
+            {
+                device.BlendState = settings.BlendState;
+                device.DepthStencilState = DepthStencilState.DepthRead;
+
+                // Set an effect parameter describing the viewport size. This is
+                // needed to convert particle sizes into screen space point sizes.
+                effectViewportScaleParameter.SetValue(new Vector2(0.5f / device.Viewport.AspectRatio, -0.5f));
+
+                // Set an effect parameter describing the current time. All the vertex
+                // shader particle animation is keyed off this value.
+                effectTimeParameter.SetValue(currentTime);
+
+                // Set the particle vertex and index buffer.
+                device.SetVertexBuffer(vertexBuffer);
+                device.Indices = indexBuffer;
+
+                // Activate the particle effect.
+                foreach (EffectPass pass in particleEffect.CurrentTechnique.Passes)
+                {
+                    pass.Apply();
+
+                    if (firstActiveParticle < firstFreeParticle)
+                    {
+                        // If the active particles are all in one consecutive range,
+                        // we can draw them all in a single call.
+                        device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0,
+                                                     firstActiveParticle * 4, (firstFreeParticle - firstActiveParticle) * 4,
+                                                     firstActiveParticle * 6, (firstFreeParticle - firstActiveParticle) * 2);
+                    }
+                    else
+                    {
+                        // If the active particle range wraps past the end of the queue
+                        // back to the start, we must split them over two draw calls.
+                        device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0,
+                                                     firstActiveParticle * 4, (settings.MaxParticles - firstActiveParticle) * 4,
+                                                     firstActiveParticle * 6, (settings.MaxParticles - firstActiveParticle) * 2);
+
+                        if (firstFreeParticle > 0)
+                        {
+                            device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0,
+                                                         0, firstFreeParticle * 4,
+                                                         0, firstFreeParticle * 2);
+                        }
+                    }
+                }
+
+                // Reset some of the renderstates that we changed,
+                // so as not to mess up any other subsequent drawing.
+                device.DepthStencilState = DepthStencilState.Default;
+            }
+
+            drawCounter++;
+        }
+
+
+        /// <summary>
+        /// Helper for uploading new particles from our managed
+        /// array to the GPU vertex buffer.
+        /// </summary>
+        void AddNewParticlesToVertexBuffer()
+        {
+            int stride = ParticleVertex.SizeInBytes;
+
+            if (firstNewParticle < firstFreeParticle)
+            {
+                // If the new particles are all in one consecutive range,
+                // we can upload them all in a single call.
+                vertexBuffer.SetData(firstNewParticle * stride * 4, particles,
+                                     firstNewParticle * 4,
+                                     (firstFreeParticle - firstNewParticle) * 4,
+                                     stride, SetDataOptions.NoOverwrite);
+            }
+            else
+            {
+                // If the new particle range wraps past the end of the queue
+                // back to the start, we must split them over two upload calls.
+                vertexBuffer.SetData(firstNewParticle * stride * 4, particles,
+                                     firstNewParticle * 4,
+                                     (settings.MaxParticles - firstNewParticle) * 4,
+                                     stride, SetDataOptions.NoOverwrite);
+
+                if (firstFreeParticle > 0)
+                {
+                    vertexBuffer.SetData(0, particles,
+                                         0, firstFreeParticle * 4,
+                                         stride, SetDataOptions.NoOverwrite);
+                }
+            }
+
+            // Move the particles we just uploaded from the new to the active queue.
+            firstNewParticle = firstFreeParticle;
+        }
+
+
+        #endregion
+
+        #region Public Methods
+
+
+        /// <summary>
+        /// Sets the camera view and projection matrices
+        /// that will be used to draw this particle system.
+        /// </summary>
+        public void SetCamera(Matrix view, Matrix projection)
+        {
+            effectViewParameter.SetValue(view);
+            effectProjectionParameter.SetValue(projection);
+        }
+
+
+        /// <summary>
+        /// Adds a new particle to the system.
+        /// </summary>
+        public void AddParticle(Vector3 position, Vector3 velocity)
+        {
+            // Figure out where in the circular queue to allocate the new particle.
+            int nextFreeParticle = firstFreeParticle + 1;
+
+            if (nextFreeParticle >= settings.MaxParticles)
+                nextFreeParticle = 0;
+
+            // If there are no free particles, we just have to give up.
+            if (nextFreeParticle == firstRetiredParticle)
+                return;
+
+            // Adjust the input velocity based on how much
+            // this particle system wants to be affected by it.
+            velocity *= settings.EmitterVelocitySensitivity;
+
+            // Add in some random amount of horizontal velocity.
+            float horizontalVelocity = MathHelper.Lerp(settings.MinHorizontalVelocity,
+                                                       settings.MaxHorizontalVelocity,
+                                                       (float)random.NextDouble());
+
+            double horizontalAngle = random.NextDouble() * MathHelper.TwoPi;
+
+            velocity.X += horizontalVelocity * (float)Math.Cos(horizontalAngle);
+            velocity.Z += horizontalVelocity * (float)Math.Sin(horizontalAngle);
+
+            // Add in some random amount of vertical velocity.
+            velocity.Y += MathHelper.Lerp(settings.MinVerticalVelocity,
+                                          settings.MaxVerticalVelocity,
+                                          (float)random.NextDouble());
+
+            // Choose four random control values. These will be used by the vertex
+            // shader to give each particle a different size, rotation, and color.
+            Color randomValues = new Color((byte)random.Next(255),
+                                           (byte)random.Next(255),
+                                           (byte)random.Next(255),
+                                           (byte)random.Next(255));
+
+            // Fill in the particle vertex structure.
+            for (int i = 0; i < 4; i++)
+            {
+                particles[firstFreeParticle * 4 + i].Position = position;
+                particles[firstFreeParticle * 4 + i].Velocity = velocity;
+                particles[firstFreeParticle * 4 + i].Random = randomValues;
+                particles[firstFreeParticle * 4 + i].Time = currentTime;
+            }
+
+            firstFreeParticle = nextFreeParticle;
+        }
+
+
+        #endregion
+    }
+}

+ 4 - 0
XMLContentLoadingSample/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="MonoGame.Framework.Portable" version="3.6.0.1625" targetFramework="portable45-net45+win8" />
+</packages>