Browse Source

Particle3DSample updated to SDK and MG 3.8.*

CartBlanche 4 weeks ago
parent
commit
bb3ce47f64
69 changed files with 2642 additions and 2263 deletions
  1. 122 0
      Particle3DSample/.vscode/launch.json
  2. 260 0
      Particle3DSample/.vscode/tasks.json
  3. BIN
      Particle3DSample/Content/checker_0.xnb
  4. BIN
      Particle3DSample/Content/explosion.xnb
  5. BIN
      Particle3DSample/Content/fire.xnb
  6. BIN
      Particle3DSample/Content/grid.xnb
  7. BIN
      Particle3DSample/Content/smoke.xnb
  8. 12 0
      Particle3DSample/Core/Content/.mgcontent
  9. 2 0
      Particle3DSample/Core/Content/.mgstats
  10. 0 0
      Particle3DSample/Core/Content/Arial.xnb
  11. 0 0
      Particle3DSample/Core/Content/Content.mgcb
  12. 24 24
      Particle3DSample/Core/Content/ExplosionSettings.xml
  13. 0 0
      Particle3DSample/Core/Content/ExplosionSettings.xnb
  14. 24 24
      Particle3DSample/Core/Content/ExplosionSmokeSettings.xml
  15. 0 0
      Particle3DSample/Core/Content/ExplosionSmokeSettings.xnb
  16. 24 24
      Particle3DSample/Core/Content/FireSettings.xml
  17. 0 0
      Particle3DSample/Core/Content/FireSettings.xnb
  18. BIN
      Particle3DSample/Core/Content/Fonts/Hud.xnb
  19. 0 0
      Particle3DSample/Core/Content/Game.ico
  20. 0 0
      Particle3DSample/Core/Content/Particle3DSample.png
  21. 137 137
      Particle3DSample/Core/Content/Particle3DSampleContent.contentproj
  22. 199 199
      Particle3DSample/Core/Content/ParticleEffect.fx
  23. 14 0
      Particle3DSample/Core/Content/ParticleEffect.mgcontent
  24. BIN
      Particle3DSample/Core/Content/ParticleEffect.xnb
  25. 24 24
      Particle3DSample/Core/Content/ProjectileTrailSettings.xml
  26. 0 0
      Particle3DSample/Core/Content/ProjectileTrailSettings.xnb
  27. 24 24
      Particle3DSample/Core/Content/SmokePlumeSettings.xml
  28. 0 0
      Particle3DSample/Core/Content/SmokePlumeSettings.xnb
  29. 0 0
      Particle3DSample/Core/Content/checker.bmp
  30. BIN
      Particle3DSample/Core/Content/checker_0.xnb
  31. 0 0
      Particle3DSample/Core/Content/explosion.png
  32. BIN
      Particle3DSample/Core/Content/explosion.xnb
  33. 0 0
      Particle3DSample/Core/Content/fire.png
  34. BIN
      Particle3DSample/Core/Content/fire.xnb
  35. 14 14
      Particle3DSample/Core/Content/font.spritefont
  36. 0 0
      Particle3DSample/Core/Content/font.xnb
  37. 71 71
      Particle3DSample/Core/Content/grid.x
  38. BIN
      Particle3DSample/Core/Content/grid.xnb
  39. 0 0
      Particle3DSample/Core/Content/smoke.png
  40. BIN
      Particle3DSample/Core/Content/smoke.xnb
  41. 425 439
      Particle3DSample/Core/Game.cs
  42. 11 0
      Particle3DSample/Core/Particle3DSample.Core.csproj
  43. 105 111
      Particle3DSample/Core/ParticleEmitter.cs
  44. 1 5
      Particle3DSample/Core/ParticleSettings.cs
  45. 523 528
      Particle3DSample/Core/ParticleSystem.cs
  46. 59 63
      Particle3DSample/Core/ParticleVertex.cs
  47. 1 0
      Particle3DSample/Core/Program.cs
  48. 100 108
      Particle3DSample/Core/Projectile.cs
  49. 0 18
      Particle3DSample/Info.plist
  50. 0 136
      Particle3DSample/Particle3DSample.MacOS.csproj
  51. 0 129
      Particle3DSample/Particle3DSample.Windows.csproj
  52. 38 14
      Particle3DSample/Particle3DSample.sln
  53. 0 59
      Particle3DSample/ParticleSettings/ParticleSettings.Windows.csproj
  54. 0 65
      Particle3DSample/ParticleSettings/ParticleSettings.csproj
  55. 31 0
      Particle3DSample/Platforms/Android/AndroidManifest.xml
  56. 1 0
      Particle3DSample/Platforms/Android/Info.plist
  57. 29 0
      Particle3DSample/Platforms/Android/Particle3DSample.Android.csproj
  58. 19 0
      Particle3DSample/Platforms/Android/Program.cs
  59. 26 0
      Particle3DSample/Platforms/Desktop/Particle3DSample.DesktopGL.csproj
  60. 14 0
      Particle3DSample/Platforms/Desktop/Program.cs
  61. 0 0
      Particle3DSample/Platforms/Windows/Game.ico
  62. 28 0
      Particle3DSample/Platforms/Windows/Particle3DSample.Windows.csproj
  63. 0 0
      Particle3DSample/Platforms/Windows/Particle3DSample.png
  64. 15 0
      Particle3DSample/Platforms/Windows/Program.cs
  65. 38 0
      Particle3DSample/Platforms/iOS/Info.plist
  66. 25 0
      Particle3DSample/Platforms/iOS/Particle3DSample.iOS.csproj
  67. 27 0
      Particle3DSample/Platforms/iOS/Program.cs
  68. 0 47
      Particle3DSample/Program.cs
  69. 175 0
      Particle3DSample/README.md

+ 122 - 0
Particle3DSample/.vscode/launch.json

@@ -0,0 +1,122 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Run Windows",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-windows",
+            "program": "${workspaceFolder}/Platforms/Windows/bin/Debug/net8.0-windows/Particle3DSample.Windows.exe",
+            "args": [],
+            "cwd": "${workspaceFolder}/Platforms/Windows",
+            "stopAtEntry": false
+        },
+        {
+            "name": "Run DesktopGL",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-desktopgl",
+            "program": "${workspaceFolder}/Platforms/Desktop/bin/Debug/net8.0/Particle3DSample.DesktopGL.exe",
+            "args": [],
+            "cwd": "${workspaceFolder}/Platforms/Desktop",
+            "stopAtEntry": false
+        },
+        {
+            "name": "Run Android",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-android",
+            "program": "${workspaceFolder}/Platforms/Android/bin/Debug/net8.0-android/Particle3DSample.Android.dll",
+            "args": [],
+            "cwd": "${workspaceFolder}/Platforms/Android",
+            "stopAtEntry": false
+        },
+        {
+            "name": "Run iOS",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-ios",
+            "program": "${workspaceFolder}/Platforms/iOS/bin/Debug/net8.0-ios/Particle3DSample.iOS.dll",
+            "args": [],
+            "cwd": "${workspaceFolder}/Platforms/iOS",
+            "stopAtEntry": false
+        }
+    ]
+}
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Launch Windows",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-windows",
+            "program": "${workspaceFolder}/bin/Debug/net8.0-windows/Particle3DSample.Windows.exe",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "console": "internalConsole",
+            "stopAtEntry": false
+        },
+        {
+            "name": "Launch DesktopGL",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-desktopgl",
+            "program": "${workspaceFolder}/bin/Debug/net8.0/Particle3DSample.DesktopGL.exe",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "console": "internalConsole",
+            "stopAtEntry": false
+        },
+        {
+            "name": "Attach to Process",
+            "type": "coreclr",
+            "request": "attach"
+        }
+    ]
+}
+{
+  "version": "0.2.0",
+  "configurations": [
+    {
+      "name": "Run Windows",
+      "type": "coreclr",
+      "request": "launch",
+      "preLaunchTask": "build-windows",
+      "program": "${workspaceFolder}/Platforms/Windows/bin/Debug/net8.0-windows/Particle3DSample.Windows.exe",
+      "args": [],
+      "cwd": "${workspaceFolder}/Platforms/Windows",
+      "stopAtEntry": false
+    },
+    {
+      "name": "Run DesktopGL",
+      "type": "coreclr",
+      "request": "launch",
+      "preLaunchTask": "build-desktopgl",
+      "program": "${workspaceFolder}/Platforms/Desktop/bin/Debug/net8.0/Particle3DSample.DesktopGL.exe",
+      "args": [],
+      "cwd": "${workspaceFolder}/Platforms/Desktop",
+      "stopAtEntry": false
+    },
+    {
+      "name": "Run Android",
+      "type": "coreclr",
+      "request": "launch",
+      "preLaunchTask": "build-android",
+      "program": "${workspaceFolder}/Platforms/Android/bin/Debug/net8.0-android/Particle3DSample.Android.dll",
+      "args": [],
+      "cwd": "${workspaceFolder}/Platforms/Android",
+      "stopAtEntry": false
+    },
+    {
+      "name": "Run iOS",
+      "type": "coreclr",
+      "request": "launch",
+      "preLaunchTask": "build-ios",
+      "program": "${workspaceFolder}/Platforms/iOS/bin/Debug/net8.0-ios/Particle3DSample.iOS.dll",
+      "args": [],
+      "cwd": "${workspaceFolder}/Platforms/iOS",
+      "stopAtEntry": false
+    }
+  ]
+}

+ 260 - 0
Particle3DSample/.vscode/tasks.json

@@ -0,0 +1,260 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "build-windows",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "Platforms/Windows/Particle3DSample.Windows.csproj"
+            ],
+            "group": "build"
+        },
+        {
+            "label": "build-desktopgl",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "Platforms/Desktop/Particle3DSample.DesktopGL.csproj"
+            ],
+            "group": "build"
+        },
+        {
+            "label": "build-android",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "Platforms/Android/Particle3DSample.Android.csproj"
+            ],
+            "group": "build"
+        },
+        {
+            "label": "build-ios",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "Platforms/iOS/Particle3DSample.iOS.csproj"
+            ],
+            "group": "build"
+        },
+        {
+            "label": "clean-all",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "clean",
+                "Particle3DSample.sln"
+            ],
+            "group": "build"
+        },
+        {
+            "label": "restore-all",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "restore",
+                "Particle3DSample.sln"
+            ],
+            "group": "build"
+        }
+    ]
+}
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "build-windows",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "${workspaceFolder}/Particle3DSample.Windows.csproj"
+            ],
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "silent",
+                "focus": false,
+                "panel": "shared",
+                "showReuseMessage": true,
+                "clear": false
+            },
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "build-desktopgl",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "${workspaceFolder}/Particle3DSample.DesktopGL.csproj"
+            ],
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "silent",
+                "focus": false,
+                "panel": "shared",
+                "showReuseMessage": true,
+                "clear": false
+            },
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "build-android",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "${workspaceFolder}/Particle3DSample.Android.csproj"
+            ],
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "silent",
+                "focus": false,
+                "panel": "shared",
+                "showReuseMessage": true,
+                "clear": false
+            },
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "run-windows",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "run",
+                "--project",
+                "${workspaceFolder}/Particle3DSample.Windows.csproj"
+            ],
+            "group": "test",
+            "dependsOn": "build-windows",
+            "presentation": {
+                "echo": true,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared"
+            }
+        },
+        {
+            "label": "run-desktopgl",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "run",
+                "--project",
+                "${workspaceFolder}/Particle3DSample.DesktopGL.csproj"
+            ],
+            "group": "test",
+            "dependsOn": "build-desktopgl",
+            "presentation": {
+                "echo": true,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared"
+            }
+        },
+        {
+            "label": "clean-all",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "clean",
+                "${workspaceFolder}/Particle3DSample.sln"
+            ],
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared"
+            }
+        },
+        {
+            "label": "restore-all",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "restore",
+                "${workspaceFolder}/Particle3DSample.sln"
+            ],
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared"
+            }
+        }
+    ]
+}
+{
+  "version": "2.0.0",
+  "tasks": [
+    {
+      "label": "build-windows",
+      "command": "dotnet",
+      "type": "process",
+      "args": [
+        "build",
+        "Platforms/Windows/Particle3DSample.Windows.csproj"
+      ],
+      "group": "build"
+    },
+    {
+      "label": "build-desktopgl",
+      "command": "dotnet",
+      "type": "process",
+      "args": [
+        "build",
+        "Platforms/Desktop/Particle3DSample.DesktopGL.csproj"
+      ],
+      "group": "build"
+    },
+    {
+      "label": "build-android",
+      "command": "dotnet",
+      "type": "process",
+      "args": [
+        "build",
+        "Platforms/Android/Particle3DSample.Android.csproj"
+      ],
+      "group": "build"
+    },
+    {
+      "label": "build-ios",
+      "command": "dotnet",
+      "type": "process",
+      "args": [
+        "build",
+        "Platforms/iOS/Particle3DSample.iOS.csproj"
+      ],
+      "group": "build"
+    },
+    {
+      "label": "clean-all",
+      "command": "dotnet",
+      "type": "process",
+      "args": [
+        "clean",
+        "Particle3DSample.sln"
+      ],
+      "group": "build"
+    },
+    {
+      "label": "restore-all",
+      "command": "dotnet",
+      "type": "process",
+      "args": [
+        "restore",
+        "Particle3DSample.sln"
+      ],
+      "group": "build"
+    }
+  ]
+}

BIN
Particle3DSample/Content/checker_0.xnb


BIN
Particle3DSample/Content/explosion.xnb


BIN
Particle3DSample/Content/fire.xnb


BIN
Particle3DSample/Content/grid.xnb


BIN
Particle3DSample/Content/smoke.xnb


+ 12 - 0
Particle3DSample/Core/Content/.mgcontent

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<SourceFileCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <Profile>HiDef</Profile>
+  <Platform>DesktopGL</Platform>
+  <Config />
+  <SourceFiles>
+    <File>C:/Users/savag/source/repos/CartBlanche/MonoGame/MonoGame-Samples/Particle3DSample/Content/ParticleEffect.fx</File>
+  </SourceFiles>
+  <DestFiles>
+    <File xsi:nil="true" />
+  </DestFiles>
+</SourceFileCollection>

+ 2 - 0
Particle3DSample/Core/Content/.mgstats

@@ -0,0 +1,2 @@
+Source File,Dest File,Processor Type,Content Type,Source File Size,Dest File Size,Build Seconds
+"C:/Users/savag/source/repos/CartBlanche/MonoGame/MonoGame-Samples/Particle3DSample/Content/ParticleEffect.fx","C:/Users/savag/source/repos/CartBlanche/MonoGame/MonoGame-Samples/Particle3DSample/Content/bin/DesktopGL/ParticleEffect.xnb","EffectProcessor","CompiledEffectContent",6534,4942,0.5059261

+ 0 - 0
Particle3DSample/Content/Arial.xnb → Particle3DSample/Core/Content/Arial.xnb


+ 0 - 0
Particle3DSample/Core/Content/Content.mgcb


+ 24 - 24
Particle3DSample/Content/ExplosionSettings.xml → Particle3DSample/Core/Content/ExplosionSettings.xml

@@ -1,25 +1,25 @@
-<?xml version="1.0" encoding="utf-8"?>
-<XnaContent>
-  <Asset Type="Particle3DSample.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>
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent>
+  <Asset Type="Particle3DSample.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>
 </XnaContent>

+ 0 - 0
Particle3DSample/Content/ExplosionSettings.xnb → Particle3DSample/Core/Content/ExplosionSettings.xnb


+ 24 - 24
Particle3DSample/Content/ExplosionSmokeSettings.xml → Particle3DSample/Core/Content/ExplosionSmokeSettings.xml

@@ -1,25 +1,25 @@
-<?xml version="1.0" encoding="utf-8"?>
-<XnaContent>
-  <Asset Type="Particle3DSample.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>
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent>
+  <Asset Type="Particle3DSample.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>
 </XnaContent>

+ 0 - 0
Particle3DSample/Content/ExplosionSmokeSettings.xnb → Particle3DSample/Core/Content/ExplosionSmokeSettings.xnb


+ 24 - 24
Particle3DSample/Content/FireSettings.xml → Particle3DSample/Core/Content/FireSettings.xml

@@ -1,25 +1,25 @@
-<?xml version="1.0" encoding="utf-8"?>
-<XnaContent>
-  <Asset Type="Particle3DSample.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>
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent>
+  <Asset Type="Particle3DSample.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>
 </XnaContent>

+ 0 - 0
Particle3DSample/Content/FireSettings.xnb → Particle3DSample/Core/Content/FireSettings.xnb


BIN
Particle3DSample/Core/Content/Fonts/Hud.xnb


+ 0 - 0
Particle3DSample/Game.ico → Particle3DSample/Core/Content/Game.ico


+ 0 - 0
Particle3DSample/Particle3DSample.png → Particle3DSample/Core/Content/Particle3DSample.png


+ 137 - 137
Particle3DSample/Content/Particle3DSampleContent.contentproj → Particle3DSample/Core/Content/Particle3DSampleContent.contentproj

@@ -1,138 +1,138 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
-  <PropertyGroup>
-    <ProjectGuid>{12A1407B-BACE-4F2A-9413-A4C8DD9E4922}</ProjectGuid>
-    <ProjectTypeGuids>{96E2B04D-8817-42c6-938A-82C39BA4D311};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
-    <OutputType>Library</OutputType>
-    <AppDesignerFolder>Properties</AppDesignerFolder>
-    <RootNamespace>Particle3DSample</RootNamespace>
-    <AssemblyName>Particle3DSample</AssemblyName>
-    <XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
-    <ApplicationIcon>Game.ico</ApplicationIcon>
-    <OutputPath>bin\$(Platform)\$(Configuration)</OutputPath>
-    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
-    <ContentRootDirectory>Content</ContentRootDirectory>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
-    <XnaPlatform>Windows</XnaPlatform>
-    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
-    <XnaPlatform>Windows</XnaPlatform>
-    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
-  </PropertyGroup>
-  <ItemGroup>
-    <Compile Include="explosion.png">
-      <Importer>TextureImporter</Importer>
-      <Processor>TextureProcessor</Processor>
-      <Name>explosion</Name>
-      <ProcessorParameters_GenerateMipmaps>True</ProcessorParameters_GenerateMipmaps>
-      <ProcessorParameters_TextureFormat>DxtCompressed</ProcessorParameters_TextureFormat>
-      <ProcessorParameters_PremultiplyAlpha>False</ProcessorParameters_PremultiplyAlpha>
-    </Compile>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="font.spritefont">
-      <Importer>FontDescriptionImporter</Importer>
-      <Processor>FontDescriptionProcessor</Processor>
-      <Name>font</Name>
-    </Compile>
-    <Compile Include="smoke.png">
-      <Importer>TextureImporter</Importer>
-      <Processor>TextureProcessor</Processor>
-      <Name>smoke</Name>
-      <ProcessorParameters_GenerateMipmaps>True</ProcessorParameters_GenerateMipmaps>
-      <ProcessorParameters_TextureFormat>DxtCompressed</ProcessorParameters_TextureFormat>
-      <ProcessorParameters_PremultiplyAlpha>False</ProcessorParameters_PremultiplyAlpha>
-    </Compile>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="grid.x">
-      <Importer>XImporter</Importer>
-      <Processor>ModelProcessor</Processor>
-      <Name>grid</Name>
-    </Compile>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="fire.png">
-      <Importer>TextureImporter</Importer>
-      <Processor>TextureProcessor</Processor>
-      <Name>fire</Name>
-      <ProcessorParameters_GenerateMipmaps>True</ProcessorParameters_GenerateMipmaps>
-      <ProcessorParameters_TextureFormat>DxtCompressed</ProcessorParameters_TextureFormat>
-      <ProcessorParameters_PremultiplyAlpha>False</ProcessorParameters_PremultiplyAlpha>
-    </Compile>
-    <Compile Include="ParticleEffect.fx">
-      <Importer>EffectImporter</Importer>
-      <Processor>EffectProcessor</Processor>
-      <Name>ParticleEffect</Name>
-    </Compile>
-  </ItemGroup>
-  <ItemGroup>
-    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.EffectImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.FBXImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.TextureImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.XImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.AudioImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.VideoImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553">
-      <Private>False</Private>
-    </Reference>
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\..\ParticleSettings\ParticleSettingsWindows.csproj">
-      <Project>{0043A4D6-575A-4781-99C0-830387D0432E}</Project>
-      <Name>ParticleSettingsWindows</Name>
-    </ProjectReference>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="ExplosionSettings.xml">
-      <Name>ExplosionSettings</Name>
-      <Importer>XmlImporter</Importer>
-      <Processor>PassThroughProcessor</Processor>
-    </Compile>
-    <Compile Include="ExplosionSmokeSettings.xml">
-      <Name>ExplosionSmokeSettings</Name>
-      <Importer>XmlImporter</Importer>
-      <Processor>PassThroughProcessor</Processor>
-    </Compile>
-    <Compile Include="FireSettings.xml">
-      <Name>FireSettings</Name>
-      <Importer>XmlImporter</Importer>
-      <Processor>PassThroughProcessor</Processor>
-    </Compile>
-    <Compile Include="ProjectileTrailSettings.xml">
-      <Name>ProjectileTrailSettings</Name>
-      <Importer>XmlImporter</Importer>
-      <Processor>PassThroughProcessor</Processor>
-    </Compile>
-    <Compile Include="SmokePlumeSettings.xml">
-      <Name>SmokePlumeSettings</Name>
-      <Importer>XmlImporter</Importer>
-      <Processor>PassThroughProcessor</Processor>
-    </Compile>
-  </ItemGroup>
-  <!-- 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>
-  -->
-  <ProjectExtensions>
-    <VisualStudio>
-    </VisualStudio>
-  </ProjectExtensions>
-  <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\$(XnaFrameworkVersion)\Microsoft.Xna.GameStudio.ContentPipeline.targets" />
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
+  <PropertyGroup>
+    <ProjectGuid>{12A1407B-BACE-4F2A-9413-A4C8DD9E4922}</ProjectGuid>
+    <ProjectTypeGuids>{96E2B04D-8817-42c6-938A-82C39BA4D311};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Particle3DSample</RootNamespace>
+    <AssemblyName>Particle3DSample</AssemblyName>
+    <XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
+    <ApplicationIcon>Game.ico</ApplicationIcon>
+    <OutputPath>bin\$(Platform)\$(Configuration)</OutputPath>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <ContentRootDirectory>Content</ContentRootDirectory>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <XnaPlatform>Windows</XnaPlatform>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <XnaPlatform>Windows</XnaPlatform>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="explosion.png">
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+      <Name>explosion</Name>
+      <ProcessorParameters_GenerateMipmaps>True</ProcessorParameters_GenerateMipmaps>
+      <ProcessorParameters_TextureFormat>DxtCompressed</ProcessorParameters_TextureFormat>
+      <ProcessorParameters_PremultiplyAlpha>False</ProcessorParameters_PremultiplyAlpha>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="font.spritefont">
+      <Importer>FontDescriptionImporter</Importer>
+      <Processor>FontDescriptionProcessor</Processor>
+      <Name>font</Name>
+    </Compile>
+    <Compile Include="smoke.png">
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+      <Name>smoke</Name>
+      <ProcessorParameters_GenerateMipmaps>True</ProcessorParameters_GenerateMipmaps>
+      <ProcessorParameters_TextureFormat>DxtCompressed</ProcessorParameters_TextureFormat>
+      <ProcessorParameters_PremultiplyAlpha>False</ProcessorParameters_PremultiplyAlpha>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="grid.x">
+      <Importer>XImporter</Importer>
+      <Processor>ModelProcessor</Processor>
+      <Name>grid</Name>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="fire.png">
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+      <Name>fire</Name>
+      <ProcessorParameters_GenerateMipmaps>True</ProcessorParameters_GenerateMipmaps>
+      <ProcessorParameters_TextureFormat>DxtCompressed</ProcessorParameters_TextureFormat>
+      <ProcessorParameters_PremultiplyAlpha>False</ProcessorParameters_PremultiplyAlpha>
+    </Compile>
+    <Compile Include="ParticleEffect.fx">
+      <Importer>EffectImporter</Importer>
+      <Processor>EffectProcessor</Processor>
+      <Name>ParticleEffect</Name>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.EffectImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.FBXImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.TextureImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.XImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.AudioImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.VideoImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553">
+      <Private>False</Private>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\ParticleSettings\ParticleSettingsWindows.csproj">
+      <Project>{0043A4D6-575A-4781-99C0-830387D0432E}</Project>
+      <Name>ParticleSettingsWindows</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ExplosionSettings.xml">
+      <Name>ExplosionSettings</Name>
+      <Importer>XmlImporter</Importer>
+      <Processor>PassThroughProcessor</Processor>
+    </Compile>
+    <Compile Include="ExplosionSmokeSettings.xml">
+      <Name>ExplosionSmokeSettings</Name>
+      <Importer>XmlImporter</Importer>
+      <Processor>PassThroughProcessor</Processor>
+    </Compile>
+    <Compile Include="FireSettings.xml">
+      <Name>FireSettings</Name>
+      <Importer>XmlImporter</Importer>
+      <Processor>PassThroughProcessor</Processor>
+    </Compile>
+    <Compile Include="ProjectileTrailSettings.xml">
+      <Name>ProjectileTrailSettings</Name>
+      <Importer>XmlImporter</Importer>
+      <Processor>PassThroughProcessor</Processor>
+    </Compile>
+    <Compile Include="SmokePlumeSettings.xml">
+      <Name>SmokePlumeSettings</Name>
+      <Importer>XmlImporter</Importer>
+      <Processor>PassThroughProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <!-- 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>
+  -->
+  <ProjectExtensions>
+    <VisualStudio>
+    </VisualStudio>
+  </ProjectExtensions>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\$(XnaFrameworkVersion)\Microsoft.Xna.GameStudio.ContentPipeline.targets" />
 </Project>
 </Project>

+ 199 - 199
Particle3DSample/Content/ParticleEffect.fx → Particle3DSample/Core/Content/ParticleEffect.fx

@@ -1,199 +1,199 @@
-//-----------------------------------------------------------------------------
-// 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 : POSITION0;
-    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) : COLOR0
-{
-    return tex2D(Sampler, input.TextureCoordinate) * input.Color;
-}
-
-
-// Effect technique for drawing particles.
-technique Particles
-{
-    pass P0
-    {
-        VertexShader = compile vs_2_0 ParticleVertexShader();
-        PixelShader = compile ps_2_0 ParticlePixelShader();
-    }
-}
+//-----------------------------------------------------------------------------
+// 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 : POSITION0;
+    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) : COLOR0
+{
+    return tex2D(Sampler, input.TextureCoordinate) * input.Color;
+}
+
+
+// Effect technique for drawing particles.
+technique Particles
+{
+    pass P0
+    {
+        VertexShader = compile vs_2_0 ParticleVertexShader();
+        PixelShader = compile ps_2_0 ParticlePixelShader();
+    }
+}

+ 14 - 0
Particle3DSample/Core/Content/ParticleEffect.mgcontent

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PipelineBuildEvent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <SourceFile>C:/Users/savag/source/repos/CartBlanche/MonoGame/MonoGame-Samples/Particle3DSample/Content/ParticleEffect.fx</SourceFile>
+  <SourceTime>2024-09-18T16:04:30.4252028+01:00</SourceTime>
+  <DestFile>C:/Users/savag/source/repos/CartBlanche/MonoGame/MonoGame-Samples/Particle3DSample/Content/bin/DesktopGL/ParticleEffect.xnb</DestFile>
+  <DestTime>2025-07-07T19:02:01.6811055+01:00</DestTime>
+  <Importer>EffectImporter</Importer>
+  <ImporterTime>2025-06-02T11:31:02+01:00</ImporterTime>
+  <Processor>EffectProcessor</Processor>
+  <ProcessorTime>2025-06-02T11:31:02+01:00</ProcessorTime>
+  <Dependencies />
+  <BuildAsset />
+  <BuildOutput />
+</PipelineBuildEvent>

BIN
Particle3DSample/Core/Content/ParticleEffect.xnb


+ 24 - 24
Particle3DSample/Content/ProjectileTrailSettings.xml → Particle3DSample/Core/Content/ProjectileTrailSettings.xml

@@ -1,25 +1,25 @@
-<?xml version="1.0" encoding="utf-8"?>
-<XnaContent>
-  <Asset Type="Particle3DSample.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>
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent>
+  <Asset Type="Particle3DSample.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>
 </XnaContent>

+ 0 - 0
Particle3DSample/Content/ProjectileTrailSettings.xnb → Particle3DSample/Core/Content/ProjectileTrailSettings.xnb


+ 24 - 24
Particle3DSample/Content/SmokePlumeSettings.xml → Particle3DSample/Core/Content/SmokePlumeSettings.xml

@@ -1,25 +1,25 @@
-<?xml version="1.0" encoding="utf-8"?>
-<XnaContent>
-  <Asset Type="Particle3DSample.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>
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent>
+  <Asset Type="Particle3DSample.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>
 </XnaContent>

+ 0 - 0
Particle3DSample/Content/SmokePlumeSettings.xnb → Particle3DSample/Core/Content/SmokePlumeSettings.xnb


+ 0 - 0
Particle3DSample/Content/checker.bmp → Particle3DSample/Core/Content/checker.bmp


BIN
Particle3DSample/Core/Content/checker_0.xnb


+ 0 - 0
Particle3DSample/Content/explosion.png → Particle3DSample/Core/Content/explosion.png


BIN
Particle3DSample/Core/Content/explosion.xnb


+ 0 - 0
Particle3DSample/Content/fire.png → Particle3DSample/Core/Content/fire.png


BIN
Particle3DSample/Core/Content/fire.xnb


+ 14 - 14
Particle3DSample/Content/font.spritefont → Particle3DSample/Core/Content/font.spritefont

@@ -1,15 +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>
+<?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>
 </XnaContent>

+ 0 - 0
Particle3DSample/Content/font.xnb → Particle3DSample/Core/Content/font.xnb


+ 71 - 71
Particle3DSample/Content/grid.x → Particle3DSample/Core/Content/grid.x

@@ -1,72 +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";
-   }
-  }
- }
+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
Particle3DSample/Core/Content/grid.xnb


+ 0 - 0
Particle3DSample/Content/smoke.png → Particle3DSample/Core/Content/smoke.png


BIN
Particle3DSample/Core/Content/smoke.xnb


+ 425 - 439
Particle3DSample/Game.cs → Particle3DSample/Core/Game.cs

@@ -1,439 +1,425 @@
-#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 Particle3DSample
-{
-	/// <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);
-			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> ("Arial");
-			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
-}
+//-----------------------------------------------------------------------------
+// Game.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+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;
+
+
+namespace Particle3DSample
+{
+	/// <summary>
+	/// Sample showing how to implement a particle system entirely
+	/// on the GPU, using the vertex shader to animate particles.
+	/// </summary>
+	public class Particle3DSampleGame : Game
+	{
+
+
+		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;
+
+
+
+
+
+		/// <summary>
+		/// Constructor.
+		/// </summary>
+		public Particle3DSampleGame ()
+		{
+			graphics = new GraphicsDeviceManager (this);
+			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> ("Arial");
+			font = Content.Load<SpriteFont> ("font");
+			//grid = Content.Load<Model> ("grid");
+		}
+
+
+
+
+
+		/// <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 ();
+		}
+
+
+
+
+
+		/// <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;
+			}
+		}
+
+
+	}
+
+
+
+	//    /// <summary>
+	//    /// The main entry point for the application.
+	//    /// </summary>
+	//    static class Program
+	//    {
+	//        static void Main()
+	//        {
+	//            using (Particle3DSampleGame game = new Particle3DSampleGame())
+	//            {
+	//                game.Run();
+	//            }
+	//        }
+	//    }
+
+}

+ 11 - 0
Particle3DSample/Core/Particle3DSample.Core.csproj

@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <AssemblyName>Particle3DSample.Core</AssemblyName>
+    <RootNamespace>Particle3DSample.Core</RootNamespace>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*" />
+  </ItemGroup>
+</Project>

+ 105 - 111
Particle3DSample/ParticleEmitter.cs → Particle3DSample/Core/ParticleEmitter.cs

@@ -1,111 +1,105 @@
-#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 Particle3DSample
-{
-	/// <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;
-		}
-	}
-}
+//-----------------------------------------------------------------------------
+// ParticleEmitter.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using Microsoft.Xna.Framework;
+
+
+namespace Particle3DSample
+{
+	/// <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
+	{
+
+		ParticleSystem particleSystem;
+		float timeBetweenParticles;
+		Vector3 previousPosition;
+		float timeLeftOver;
+
+
+
+		/// <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;
+		}
+	}
+}

+ 1 - 5
Particle3DSample/ParticleSettings/ParticleSettings.cs → Particle3DSample/Core/ParticleSettings.cs

@@ -1,18 +1,14 @@
-#region File Description
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 // ParticleSettings.cs
 // ParticleSettings.cs
 //
 //
 // Microsoft XNA Community Game Platform
 // Microsoft XNA Community Game Platform
 // Copyright (C) Microsoft Corporation. All rights reserved.
 // Copyright (C) Microsoft Corporation. All rights reserved.
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
-#endregion
 
 
-#region Using Statements
 using System;
 using System;
 using Microsoft.Xna.Framework;
 using Microsoft.Xna.Framework;
 using Microsoft.Xna.Framework.Graphics;
 using Microsoft.Xna.Framework.Graphics;
 using Microsoft.Xna.Framework.Content;
 using Microsoft.Xna.Framework.Content;
-#endregion
 
 
 namespace Particle3DSample
 namespace Particle3DSample
 {
 {
@@ -23,7 +19,7 @@ namespace Particle3DSample
     public class ParticleSettings
     public class ParticleSettings
     {
     {
         // Name of the texture used by this particle system.
         // Name of the texture used by this particle system.
-        public string TextureName = null;
+        public string? TextureName = null;
 
 
 
 
         // Maximum number of particles that can be displayed at one time.
         // Maximum number of particles that can be displayed at one time.

+ 523 - 528
Particle3DSample/ParticleSystem.cs → Particle3DSample/Core/ParticleSystem.cs

@@ -1,528 +1,523 @@
-#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;
-
-#endregion
-
-namespace Particle3DSample
-{
-	/// <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 ()
-		{
-			//return;
-			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 ();
-			// No cloning for now so we will just create a new effect for now
-			particleEffect = effect;
-			
-			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 ["TextureSampler"].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
-	}
-}
+//-----------------------------------------------------------------------------
+// ParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Graphics.PackedVector;
+
+
+namespace Particle3DSample
+{
+	/// <summary>
+	/// The main component in charge of displaying particles.
+	/// </summary>
+	public class ParticleSystem : DrawableGameComponent
+	{
+
+
+		// 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 ();
+
+
+
+
+
+		/// <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 ()
+		{
+			//return;
+			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 ();
+			// No cloning for now so we will just create a new effect for now
+			particleEffect = effect;
+			
+			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.
+			if (settings.TextureName != null) 
+			{
+				Texture2D texture = content.Load<Texture2D> (settings.TextureName);
+
+				var textureParam = parameters ["Texture"];
+				if (textureParam != null)
+				{
+					textureParam.SetValue (texture);
+				}
+			}
+		}
+
+
+
+
+
+		/// <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;
+		}
+
+
+
+
+
+		/// <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;
+		}
+
+
+	}
+}

+ 59 - 63
Particle3DSample/ParticleVertex.cs → Particle3DSample/Core/ParticleVertex.cs

@@ -1,63 +1,59 @@
-#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 Particle3DSample
-{
-	/// <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;
-	}
-}
+//-----------------------------------------------------------------------------
+// ParticleVertex.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Graphics.PackedVector;
+
+
+namespace Particle3DSample
+{
+	/// <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;
+	}
+}

+ 1 - 0
Particle3DSample/Core/Program.cs

@@ -0,0 +1 @@
+// Entry point is now platform-specific. This file is intentionally left blank.

+ 100 - 108
Particle3DSample/Projectile.cs → Particle3DSample/Core/Projectile.cs

@@ -1,108 +1,100 @@
-#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 Particle3DSample
-{
-    /// <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;
-        }
-    }
-}
+//-----------------------------------------------------------------------------
+// Projectile.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using Microsoft.Xna.Framework;
+
+namespace Particle3DSample
+{
+    /// <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
+    {
+
+        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;
+
+
+
+        ParticleSystem explosionParticles;
+        ParticleSystem explosionSmokeParticles;
+        ParticleEmitter trailEmitter;
+
+        Vector3 position;
+        Vector3 velocity;
+        float age;
+
+        static Random random = new Random();
+
+
+
+        /// <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;
+        }
+    }
+}

+ 0 - 18
Particle3DSample/Info.plist

@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleIconFile</key>
-	<string>Game.ico</string>
-	<key>CFBundleIdentifier</key>
-	<string>com.yourcompany.Particle3DSample</string>
-	<key>CFBundleName</key>
-	<string>Particle3DSample</string>
-	<key>CFBundleVersion</key>
-	<string>1</string>
-	<key>LSMinimumSystemVersion</key>
-	<string>10.6</string>
-	<key>NSPrincipalClass</key>
-	<string>NSApplication</string>
-</dict>
-</plist>

+ 0 - 136
Particle3DSample/Particle3DSample.MacOS.csproj

@@ -1,136 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>10.0.0</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{1230A19A-FA76-4D12-85E5-68A452315299}</ProjectGuid>
-    <ProjectTypeGuids>{948B3504-5B70-4649-8FE4-BDE1FB46EC69};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
-    <OutputType>Exe</OutputType>
-    <RootNamespace>Particle3DSample</RootNamespace>
-    <AssemblyName>Particle3DSample</AssemblyName>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>True</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG;MONOMAC</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>False</ConsolePause>
-    <EnableCodeSigning>False</EnableCodeSigning>
-    <CreatePackage>False</CreatePackage>
-    <EnablePackageSigning>False</EnablePackageSigning>
-    <IncludeMonoRuntime>False</IncludeMonoRuntime>
-    <UseSGen>False</UseSGen>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>none</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>False</ConsolePause>
-    <EnableCodeSigning>False</EnableCodeSigning>
-    <CreatePackage>False</CreatePackage>
-    <EnablePackageSigning>False</EnablePackageSigning>
-    <IncludeMonoRuntime>False</IncludeMonoRuntime>
-    <UseSGen>False</UseSGen>
-    <DefineConstants>MONOMAC</DefineConstants>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="System.Xml" />
-    <Reference Include="System.Core" />
-    <Reference Include="System.Xml.Linq" />
-    <Reference Include="System.Drawing" />
-    <Reference Include="MonoMac" />
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="Info.plist">
-    </None>
-    <None Include="Content\Particle3DSampleContent.contentproj" />
-    <None Include="Content\ParticleEffect.fx" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <Import Project="$(MSBuildExtensionsPath)\Mono\MonoMac\v0.0\Mono.MonoMac.targets" />
-  <ItemGroup>
-    <ProjectReference Include="ParticleSettings\ParticleSettings.csproj">
-      <Project>{3388FF68-6F15-4F36-B226-7823E5309E92}</Project>
-      <Name>ParticleSettings</Name>
-    </ProjectReference>
-    <ProjectReference Include="..\..\MonoGame.Framework\MonoGame.Framework.MacOS.csproj">
-      <Project>{36C538E6-C32A-4A8D-A39C-566173D7118E}</Project>
-      <Name>MonoGame.Framework.MacOS</Name>
-    </ProjectReference>
-    <ProjectReference Include="..\..\ThirdParty\Lidgren.Network\Lidgren.Network.MacOS.csproj">
-      <Project>{AE483C29-042E-4226-BA52-D247CE7676DA}</Project>
-      <Name>Lidgren.Network.MacOS</Name>
-    </ProjectReference>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="Program.cs" />
-    <Compile Include="Game.cs" />
-    <Compile Include="ParticleEmitter.cs" />
-    <Compile Include="ParticleSystem.cs" />
-    <Compile Include="ParticleVertex.cs" />
-    <Compile Include="Projectile.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <Content Include="Content\font.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\ExplosionSettings.xml" />
-    <Content Include="Content\ExplosionSmokeSettings.xml" />
-    <Content Include="Content\FireSettings.xml" />
-    <Content Include="Content\ProjectileTrailSettings.xml" />
-    <Content Include="Content\SmokePlumeSettings.xml" />
-    <Content Include="Content\checker.bmp" />
-    <Content Include="Content\explosion.png" />
-    <Content Include="Content\fire.png" />
-    <Content Include="Content\font.spritefont" />
-    <Content Include="Content\grid.x" />
-    <Content Include="Content\smoke.png" />
-    <Content Include="Game.ico" />
-    <Content Include="Particle3DSample.png" />
-    <Content Include="Content\Arial.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\ExplosionSettings.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\checker_0.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\ExplosionSmokeSettings.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\fire.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\FireSettings.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\grid.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\ProjectileTrailSettings.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\smoke.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\SmokePlumeSettings.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\explosion.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="..\CompiledContent\OSX\Content\Effects\ParticleEffect.xnb">
-      <Link>Content\ParticleEffect.xnb</Link>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-  </ItemGroup>
-</Project>

+ 0 - 129
Particle3DSample/Particle3DSample.Windows.csproj

@@ -1,129 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>10.0.0</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <OutputType>WinExe</OutputType>
-    <RootNamespace>Particle3DSample</RootNamespace>
-    <AssemblyName>Particle3DSample</AssemblyName>
-    <ProjectGuid>{4E34D15A-4FDD-42D4-9645-83298049035C}</ProjectGuid>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>True</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>False</ConsolePause>
-    <EnableCodeSigning>False</EnableCodeSigning>
-    <CreatePackage>False</CreatePackage>
-    <EnablePackageSigning>False</EnablePackageSigning>
-    <IncludeMonoRuntime>False</IncludeMonoRuntime>
-    <UseSGen>False</UseSGen>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>none</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>False</ConsolePause>
-    <EnableCodeSigning>False</EnableCodeSigning>
-    <CreatePackage>False</CreatePackage>
-    <EnablePackageSigning>False</EnablePackageSigning>
-    <IncludeMonoRuntime>False</IncludeMonoRuntime>
-    <UseSGen>False</UseSGen>
-  </PropertyGroup>
-  <PropertyGroup>
-    <StartupObject />
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="System.Xml" />
-    <Reference Include="System.Core" />
-    <Reference Include="System.Xml.Linq" />
-    <Reference Include="System.Drawing" />
-  </ItemGroup>
-  <ItemGroup>
-    <Content Include="..\CompiledContent\Windows\Content\Effects\ParticleEffect.xnb">
-      <Link>Content\ParticleEffect.xnb</Link>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <None Include="Info.plist">
-    </None>
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <ItemGroup>
-    <Compile Include="Program.cs" />
-    <Compile Include="Game.cs" />
-    <Compile Include="ParticleEmitter.cs" />
-    <Compile Include="ParticleSystem.cs" />
-    <Compile Include="ParticleVertex.cs" />
-    <Compile Include="Projectile.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <Content Include="Content\font.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\ExplosionSettings.xml" />
-    <Content Include="Content\ExplosionSmokeSettings.xml" />
-    <Content Include="Content\FireSettings.xml" />
-    <Content Include="Content\ProjectileTrailSettings.xml" />
-    <Content Include="Content\SmokePlumeSettings.xml" />
-    <Content Include="Content\checker.bmp" />
-    <Content Include="Content\explosion.png" />
-    <Content Include="Content\fire.png" />
-    <Content Include="Content\font.spritefont" />
-    <Content Include="Content\grid.x" />
-    <Content Include="Content\smoke.png" />
-    <Content Include="Game.ico" />
-    <Content Include="Particle3DSample.png" />
-    <Content Include="Content\Arial.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\ExplosionSettings.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\checker_0.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\ExplosionSmokeSettings.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\fire.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\FireSettings.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\grid.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\ProjectileTrailSettings.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\smoke.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\SmokePlumeSettings.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\explosion.xnb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\..\MonoGame.Framework\MonoGame.Framework.WindowsGL.csproj">
-      <Project>{7DE47032-A904-4C29-BD22-2D235E8D91BA}</Project>
-      <Name>MonoGame.Framework.WindowsGL</Name>
-    </ProjectReference>
-    <ProjectReference Include="ParticleSettings\ParticleSettings.Windows.csproj">
-      <Project>{3388FF68-6F15-4F36-B226-7823E5309E92}</Project>
-      <Name>ParticleSettings.Windows</Name>
-    </ProjectReference>
-  </ItemGroup>
-</Project>

+ 38 - 14
Particle3DSample/Particle3DSample.sln

@@ -1,26 +1,50 @@
 
 
-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual Studio 2010
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Particle3DSample", "Particle3DSample.csproj", "{1230A19A-FA76-4D12-85E5-68A452315299}"
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Particle3DSample.Windows", "Platforms\Windows\Particle3DSample.Windows.csproj", "{4E34D15A-4FDD-42D4-9645-83298049035C}"
 EndProject
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame.Framework.MacOS", "..\..\..\..\..\..\..\Users\Jimmy\Public\Share\MonoMacSource\kjpgit\MonoGame\MonoGame.Framework\MonoGame.Framework.MacOS.csproj", "{36C538E6-C32A-4A8D-A39C-566173D7118E}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Particle3DSample.DesktopGL", "Platforms\Desktop\Particle3DSample.DesktopGL.csproj", "{5A2F42F1-5B25-4E88-9B47-F8E93A5D8E92}"
 EndProject
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Particle3DSample.Android", "Platforms\Android\Particle3DSample.Android.csproj", "{6B73D2C9-8C4D-4F35-B5A1-F1E4A3C8D5E0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Particle3DSample.iOS", "Platforms\iOS\Particle3DSample.iOS.csproj", "{7C84E3D2-9D5E-4F46-C6B2-A2F5B4C7D6F1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Particle3DSample.Core", "Core\Particle3DSample.Core.csproj", "{A1B2C3D4-1234-5678-ABCD-1234567890AB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParticleSettings", "ParticleSettings\ParticleSettings.csproj", "{3388FF68-6F15-4F36-B226-7823E5309E92}"
+EndProject
+
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Debug|Any CPU = Debug|Any CPU
 		Release|Any CPU = Release|Any CPU
 		Release|Any CPU = Release|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{1230A19A-FA76-4D12-85E5-68A452315299}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{1230A19A-FA76-4D12-85E5-68A452315299}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{1230A19A-FA76-4D12-85E5-68A452315299}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{1230A19A-FA76-4D12-85E5-68A452315299}.Release|Any CPU.Build.0 = Release|Any CPU
-		{36C538E6-C32A-4A8D-A39C-566173D7118E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{36C538E6-C32A-4A8D-A39C-566173D7118E}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{36C538E6-C32A-4A8D-A39C-566173D7118E}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{36C538E6-C32A-4A8D-A39C-566173D7118E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4E34D15A-4FDD-42D4-9645-83298049035C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4E34D15A-4FDD-42D4-9645-83298049035C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4E34D15A-4FDD-42D4-9645-83298049035C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4E34D15A-4FDD-42D4-9645-83298049035C}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5A2F42F1-5B25-4E88-9B47-F8E93A5D8E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5A2F42F1-5B25-4E88-9B47-F8E93A5D8E92}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5A2F42F1-5B25-4E88-9B47-F8E93A5D8E92}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5A2F42F1-5B25-4E88-9B47-F8E93A5D8E92}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6B73D2C9-8C4D-4F35-B5A1-F1E4A3C8D5E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6B73D2C9-8C4D-4F35-B5A1-F1E4A3C8D5E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6B73D2C9-8C4D-4F35-B5A1-F1E4A3C8D5E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6B73D2C9-8C4D-4F35-B5A1-F1E4A3C8D5E0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7C84E3D2-9D5E-4F46-C6B2-A2F5B4C7D6F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7C84E3D2-9D5E-4F46-C6B2-A2F5B4C7D6F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7C84E3D2-9D5E-4F46-C6B2-A2F5B4C7D6F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7C84E3D2-9D5E-4F46-C6B2-A2F5B4C7D6F1}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3388FF68-6F15-4F36-B226-7823E5309E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3388FF68-6F15-4F36-B226-7823E5309E92}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3388FF68-6F15-4F36-B226-7823E5309E92}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3388FF68-6F15-4F36-B226-7823E5309E92}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	EndGlobalSection
-	GlobalSection(MonoDevelopProperties) = preSolution
-		StartupItem = Particle3DSample.csproj
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
 	EndGlobalSection
 	EndGlobalSection
 EndGlobal
 EndGlobal

+ 0 - 59
Particle3DSample/ParticleSettings/ParticleSettings.Windows.csproj

@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>10.0.0</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{3388FF68-6F15-4F36-B226-7823E5309E92}</ProjectGuid>
-    <ProjectTypeGuids>{948B3504-5B70-4649-8FE4-BDE1FB46EC69};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
-    <OutputType>Library</OutputType>
-    <RootNamespace>ParticleSettingsWindows</RootNamespace>
-    <AssemblyName>ParticleSettings</AssemblyName>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>True</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>False</ConsolePause>
-    <EnableCodeSigning>False</EnableCodeSigning>
-    <CreatePackage>False</CreatePackage>
-    <EnablePackageSigning>False</EnablePackageSigning>
-    <IncludeMonoRuntime>False</IncludeMonoRuntime>
-    <UseSGen>False</UseSGen>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>none</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>False</ConsolePause>
-    <EnableCodeSigning>False</EnableCodeSigning>
-    <CreatePackage>False</CreatePackage>
-    <EnablePackageSigning>False</EnablePackageSigning>
-    <IncludeMonoRuntime>False</IncludeMonoRuntime>
-    <UseSGen>False</UseSGen>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="System.Xml" />
-    <Reference Include="System.Core" />
-    <Reference Include="System.Xml.Linq" />
-    <Reference Include="System.Drawing" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <ItemGroup>
-    <Compile Include="ParticleSettings.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\..\..\MonoGame.Framework\MonoGame.Framework.WindowsGL.csproj">
-      <Project>{7DE47032-A904-4C29-BD22-2D235E8D91BA}</Project>
-      <Name>MonoGame.Framework.WindowsGL</Name>
-    </ProjectReference>
-  </ItemGroup>
-</Project>

+ 0 - 65
Particle3DSample/ParticleSettings/ParticleSettings.csproj

@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>10.0.0</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{3388FF68-6F15-4F36-B226-7823E5309E92}</ProjectGuid>
-    <ProjectTypeGuids>{948B3504-5B70-4649-8FE4-BDE1FB46EC69};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
-    <OutputType>Library</OutputType>
-    <RootNamespace>ParticleSettingsWindows</RootNamespace>
-    <AssemblyName>ParticleSettings</AssemblyName>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>True</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>False</ConsolePause>
-    <EnableCodeSigning>False</EnableCodeSigning>
-    <CreatePackage>False</CreatePackage>
-    <EnablePackageSigning>False</EnablePackageSigning>
-    <IncludeMonoRuntime>False</IncludeMonoRuntime>
-    <UseSGen>False</UseSGen>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>none</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>False</ConsolePause>
-    <EnableCodeSigning>False</EnableCodeSigning>
-    <CreatePackage>False</CreatePackage>
-    <EnablePackageSigning>False</EnablePackageSigning>
-    <IncludeMonoRuntime>False</IncludeMonoRuntime>
-    <UseSGen>False</UseSGen>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="System.Xml" />
-    <Reference Include="System.Core" />
-    <Reference Include="System.Xml.Linq" />
-    <Reference Include="System.Drawing" />
-    <Reference Include="MonoMac" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <Import Project="$(MSBuildExtensionsPath)\Mono\MonoMac\v0.0\Mono.MonoMac.targets" />
-  <ItemGroup>
-    <Compile Include="ParticleSettings.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\..\..\MonoGame.Framework\MonoGame.Framework.MacOS.csproj">
-      <Project>{36C538E6-C32A-4A8D-A39C-566173D7118E}</Project>
-      <Name>MonoGame.Framework.MacOS</Name>
-    </ProjectReference>
-    <ProjectReference Include="..\..\..\ThirdParty\Lidgren.Network\Lidgren.Network.MacOS.csproj">
-      <Project>{AE483C29-042E-4226-BA52-D247CE7676DA}</Project>
-      <Name>Lidgren.Network.MacOS</Name>
-    </ProjectReference>
-  </ItemGroup>
-</Project>

+ 31 - 0
Particle3DSample/Platforms/Android/AndroidManifest.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          android:versionCode="1"
+          android:versionName="1.0"
+          package="com.particle3dsample.android">
+
+  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
+
+  <uses-permission android:name="android.permission.INTERNET" />
+  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+
+  <application android:allowBackup="true"
+               android:icon="@mipmap/ic_launcher"
+               android:label="@string/app_name"
+               android:theme="@style/AppTheme">
+
+    <activity android:name="crc640ec207abc449b2ca.MainActivity"
+              android:exported="true"
+              android:label="@string/app_name"
+              android:screenOrientation="landscape"
+              android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|uiMode"
+              android:theme="@style/AppTheme.Fullscreen">
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+      </intent-filter>
+    </activity>
+
+  </application>
+
+</manifest>

+ 1 - 0
Particle3DSample/Platforms/Android/Info.plist

@@ -0,0 +1 @@
+// ...existing Info.plist content if needed for Android...

+ 29 - 0
Particle3DSample/Platforms/Android/Particle3DSample.Android.csproj

@@ -0,0 +1,29 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net8.0-android</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <AssemblyName>Particle3DSample.Android</AssemblyName>
+    <RootNamespace>Particle3DSample.Android</RootNamespace>
+    <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
+    <Nullable>enable</Nullable>
+    <ApplicationId>com.particle3dsample.android</ApplicationId>
+    <ApplicationDisplayName>Particle3D Sample</ApplicationDisplayName>
+    <ApplicationVersion>1</ApplicationVersion>
+    <AndroidVersionCode>1</AndroidVersionCode>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.Android" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\Particle3DSample.Core.csproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+</Project>

+ 19 - 0
Particle3DSample/Platforms/Android/Program.cs

@@ -0,0 +1,19 @@
+using System;
+using Android.App;
+using Android.OS;
+using Microsoft.Xna.Framework;
+
+namespace Particle3DSample.Android
+{
+    [Activity(Label = "Particle3DSample", MainLauncher = true, Icon = "@mipmap/icon")]
+    public class MainActivity : AndroidGameActivity
+    {
+        protected override void OnCreate(Bundle bundle)
+        {
+            base.OnCreate(bundle);
+            var game = new Particle3DSampleGame();
+            SetContentView((game.Services.GetService(typeof(View)) as View));
+            game.Run();
+        }
+    }
+}

+ 26 - 0
Particle3DSample/Platforms/Desktop/Particle3DSample.DesktopGL.csproj

@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <AssemblyName>Particle3DSample.DesktopGL</AssemblyName>
+    <RootNamespace>Particle3DSample.DesktopGL</RootNamespace>
+    <Nullable>enable</Nullable>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\Particle3DSample.Core.csproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+</Project>

+ 14 - 0
Particle3DSample/Platforms/Desktop/Program.cs

@@ -0,0 +1,14 @@
+using System;
+using Microsoft.Xna.Framework;
+
+namespace Particle3DSample.DesktopGL
+{
+    public static class Program
+    {
+        static void Main()
+        {
+            using (var game = new Particle3DSampleGame())
+                game.Run();
+        }
+    }
+}

+ 0 - 0
Particle3DSample/Platforms/Windows/Game.ico


+ 28 - 0
Particle3DSample/Platforms/Windows/Particle3DSample.Windows.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net8.0-windows</TargetFramework>
+    <OutputType>WinExe</OutputType>
+    <UseWindowsForms>true</UseWindowsForms>
+    <ApplicationIcon>..\..\Core\Content\Game.ico</ApplicationIcon>
+    <AssemblyName>Particle3DSample.Windows</AssemblyName>
+    <RootNamespace>Particle3DSample.Windows</RootNamespace>
+    <Nullable>enable</Nullable>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.WindowsDX" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\Particle3DSample.Core.csproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+</Project>

+ 0 - 0
Particle3DSample/Platforms/Windows/Particle3DSample.png


+ 15 - 0
Particle3DSample/Platforms/Windows/Program.cs

@@ -0,0 +1,15 @@
+using System;
+using Microsoft.Xna.Framework;
+
+namespace Particle3DSample.Windows
+{
+    public static class Program
+    {
+        [STAThread]
+        static void Main()
+        {
+            using (var game = new Particle3DSampleGame())
+                game.Run();
+        }
+    }
+}

+ 38 - 0
Particle3DSample/Platforms/iOS/Info.plist

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDisplayName</key>
+	<string>Particle3D Sample</string>
+	<key>CFBundleIdentifier</key>
+	<string>com.particle3dsample.ios</string>
+	<key>CFBundleName</key>
+	<string>Particle3DSample</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>MinimumOSVersion</key>
+	<string>11.0</string>
+	<key>UIDeviceFamily</key>
+	<array>
+		<integer>1</integer>
+		<integer>2</integer>
+	</array>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UIStatusBarHidden</key>
+	<true/>
+</dict>
+</plist>

+ 25 - 0
Particle3DSample/Platforms/iOS/Particle3DSample.iOS.csproj

@@ -0,0 +1,25 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net8.0-ios</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <AssemblyName>Particle3DSample.iOS</AssemblyName>
+    <RootNamespace>Particle3DSample.iOS</RootNamespace>
+    <SupportedOSPlatformVersion>11.0</SupportedOSPlatformVersion>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.iOS" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\Particle3DSample.Core.csproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+</Project>

+ 27 - 0
Particle3DSample/Platforms/iOS/Program.cs

@@ -0,0 +1,27 @@
+using System;
+using Foundation;
+using UIKit;
+using Microsoft.Xna.Framework;
+
+namespace Particle3DSample.iOS
+{
+    [Register("AppDelegate")]
+    public class AppDelegate : UIApplicationDelegate
+    {
+        public override UIWindow Window { get; set; }
+        public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
+        {
+            var game = new Particle3DSampleGame();
+            game.Run();
+            return true;
+        }
+    }
+
+    public class Application
+    {
+        static void Main(string[] args)
+        {
+            UIApplication.Main(args, null, typeof(AppDelegate));
+        }
+    }
+}

+ 0 - 47
Particle3DSample/Program.cs

@@ -1,47 +0,0 @@
-#if MONOMAC
-using MonoMac.AppKit;
-using MonoMac.Foundation;
-#endif
-
-namespace Particle3DSample
-{
-	static class Program
-	{
-		/// <summary>
-		/// The main entry point for the application.
-		/// </summary>
-		static void Main (string[] args)
-		{
-#if MONOMAC
-			NSApplication.Init ();
-
-			using (var p = new NSAutoreleasePool ()) {
-				NSApplication.SharedApplication.Delegate = new AppDelegate ();
-				NSApplication.Main (args);
-			}
-#else
-			using (var game = new Particle3DSampleGame()) {
-				game.Run();
-			}
-#endif
-
-		}
-	}
-#if MONOMAC
-	class AppDelegate : NSApplicationDelegate
-	{
-        Particle3DSampleGame game;
-		public override void FinishedLaunching (MonoMac.Foundation.NSObject notification)
-		{
-			game = new Particle3DSampleGame();
-			game.Run();
-		}
-
-		public override bool ApplicationShouldTerminateAfterLastWindowClosed (NSApplication sender)
-		{
-			return true;
-		}
-	}		
-#endif
-}
-

+ 175 - 0
Particle3DSample/README.md

@@ -0,0 +1,175 @@
+# Particle3D Sample - MonoGame 3.8
+
+This is a modernized version of the MonoGame Particle3D sample, demonstrating advanced 3D particle effects using MonoGame 3.8.4 framework.
+
+## Project Overview
+
+The Particle3D Sample showcases various particle systems including:
+- Explosions with smoke
+- Fire effects
+- Projectile trails
+- Smoke plumes
+- Various particle emitters and systems
+
+The project demonstrates how to create custom particle effects using vertex buffers, custom shaders, and particle settings that can be configured via XML files.
+
+## Project Structure
+
+- **Game.cs** - Main game class handling initialization, updates, and rendering
+- **ParticleSystem.cs** - Core particle system implementation
+- **ParticleEmitter.cs** - Particle emission logic
+- **ParticleVertex.cs** - Custom vertex structure for particles
+- **Projectile.cs** - Projectile implementation with trail effects
+- **ParticleSettings/** - Library containing particle configuration classes
+
+## Supported Platforms
+
+This modernized version supports the following platforms using .NET 8.0:
+
+
+### Using Visual Studio Code
+
+1. Open the workspace in VS Code.
+2. Use the build/run tasks for your platform (see `.vscode/tasks.json` and `.vscode/launch.json`).
+3. Select the desired launch configuration and press F5 to run.
+
+### Project Structure (Post-Refactor)
+
+- `/Core` - Shared game and particle system code
+- `/Platforms/Windows` - Windows-specific entry point and project
+- `/Platforms/Desktop` - DesktopGL (OpenGL) entry point and project
+- `/Platforms/Android` - Android entry point and project
+- `/Platforms/iOS` - iOS entry point and project
+
+All platform projects reference `/Core` for shared logic. Platform-specific code and entry points are separated to minimize `#if/#endif` usage.
+```bash
+dotnet build Platforms/Windows/Particle3DSample.Windows.csproj
+dotnet run --project Platforms/Windows/Particle3DSample.Windows.csproj
+```
+
+#### DesktopGL (Cross-platform OpenGL)
+```bash
+dotnet build Platforms/Desktop/Particle3DSample.DesktopGL.csproj
+dotnet run --project Platforms/Desktop/Particle3DSample.DesktopGL.csproj
+```
+
+#### Android
+```bash
+dotnet build Platforms/Android/Particle3DSample.Android.csproj
+dotnet run --project Platforms/Android/Particle3DSample.Android.csproj
+```
+
+#### iOS
+```bash
+dotnet build Platforms/iOS/Particle3DSample.iOS.csproj
+dotnet run --project Platforms/iOS/Particle3DSample.iOS.csproj
+```
+
+### Using Visual Studio Code
+
+1. Open the workspace in VS Code.
+2. Use the build/run tasks for your platform (see `.vscode/tasks.json` and `.vscode/launch.json`).
+3. Select the desired launch configuration and press F5 to run.
+
+### Project Structure (Post-Refactor)
+
+- `/Core` - Shared game and particle system code
+- `/Platforms/Windows` - Windows-specific entry point and project
+- `/Platforms/Desktop` - DesktopGL (OpenGL) entry point and project
+- `/Platforms/Android` - Android entry point and project
+- `/Platforms/iOS` - iOS entry point and project
+
+All platform projects reference `/Core` for shared logic. Platform-specific code and entry points are separated to minimize `#if/#endif` usage.
+dotnet build Particle3DSample.DesktopGL.csproj
+dotnet run --project Particle3DSample.DesktopGL.csproj
+```
+
+#### Android
+```bash
+dotnet build Particle3DSample.Android.csproj
+# Deploy to connected device or emulator
+dotnet build Particle3DSample.Android.csproj -t:Run
+```
+
+### Using Visual Studio Code
+
+1. Open the project folder in VS Code
+2. Use the integrated terminal or Command Palette
+3. Run tasks:
+   - `Ctrl+Shift+P` → "Tasks: Run Task"
+   - Select "build-windows", "build-desktopgl", or "build-android"
+   - Or use "run-windows" or "run-desktopgl" to build and run
+
+#### Available VS Code Tasks
+- `build-windows` - Build Windows version
+- `build-desktopgl` - Build DesktopGL version
+- `build-android` - Build Android version
+- `run-windows` - Build and run Windows version
+- `run-desktopgl` - Build and run DesktopGL version
+- `clean-all` - Clean all projects
+- `restore-all` - Restore NuGet packages
+
+## Controls
+
+- **Mouse** - Look around
+- **Left Click** - Fire projectiles with particle trails
+- **Right Click** - Create explosion effects
+- **WASD** - Move camera
+- **Space/Shift** - Move up/down
+- **Escape** - Exit application
+
+## Technical Details
+
+### Modern .NET Features
+- Uses SDK-style project files for simplified configuration
+- Targets .NET 8.0 for best performance and modern language features
+- Uses MonoGame 3.8.* NuGet packages instead of project references
+- Nullable reference types enabled for better code safety
+
+### Content Pipeline
+The project uses pre-built .xnb content files located in the `Content/` directory:
+- Particle textures (explosion.xnb, fire.xnb, smoke.xnb)
+- Particle settings XML files and compiled .xnb versions
+- Font files for UI text
+- 3D model files
+
+### Particle System Features
+- Custom vertex structures optimized for particle rendering
+- Hardware-accelerated particle rendering using vertex buffers
+- XML-based particle system configuration
+- Multiple particle emitter types
+- Physics-based particle movement and effects
+
+## Troubleshooting
+
+### Common Issues
+
+1. **Missing MonoGame Dependencies**
+   ```bash
+   dotnet restore
+   ```
+
+2. **Content not found errors**
+   - Ensure all .xnb files are in the Content directory
+   - Check that content files are set to "Copy to Output Directory"
+
+3. **Graphics/Shader Issues**
+   - Try the DesktopGL version for better cross-platform compatibility
+   - Ensure graphics drivers are up to date
+
+4. **Android Build Issues**
+   - Install Android workload: `dotnet workload install android`
+   - Ensure Android SDK is properly configured
+
+### Performance Tips
+- The sample is optimized for desktop performance
+- On mobile devices, consider reducing particle counts in settings XML files
+- Use the DesktopGL version for better cross-platform compatibility
+
+## Contributing
+
+This is a sample project demonstrating MonoGame 3.8 features. Feel free to use it as a reference for your own particle system implementations.
+
+## License
+
+This sample is provided as-is for educational and reference purposes.