2
0
Эх сурвалжийг харах

ShatterEffectSample added Content.mgcb and Processor project.

CartBlanche 2 өдөр өмнө
parent
commit
9ee411314f
23 өөрчлөгдсөн 614 нэмэгдсэн , 98 устгасан
  1. 36 0
      ShatterEffectSample/.config/dotnet-tools.json
  2. 84 0
      ShatterEffectSample/Core/Content/Content.mgcb
  3. 77 0
      ShatterEffectSample/Core/Content/Macros.hlsl
  4. 25 41
      ShatterEffectSample/Core/Content/ShatterEffect.fx
  5. BIN
      ShatterEffectSample/Core/Content/ShatterEffect.xnb
  6. BIN
      ShatterEffectSample/Core/Content/engine_diff_tex_0.xnb
  7. 1 1
      ShatterEffectSample/Core/Content/font.spritefont
  8. BIN
      ShatterEffectSample/Core/Content/font.xnb
  9. BIN
      ShatterEffectSample/Core/Content/model/tank.fbx
  10. BIN
      ShatterEffectSample/Core/Content/tank.xnb
  11. 0 0
      ShatterEffectSample/Core/Content/tank1.fbx
  12. BIN
      ShatterEffectSample/Core/Content/turret_alt_diff_tex_0.xnb
  13. 1 1
      ShatterEffectSample/Core/ShatterEffectGame.cs
  14. 2 3
      ShatterEffectSample/Core/ShatterEffectSample.Core.csproj
  15. 4 4
      ShatterEffectSample/Platforms/Android/ShatterEffectSample.Android.csproj
  16. 3 3
      ShatterEffectSample/Platforms/DesktopGL/ShatterEffectSample.DesktopGL.csproj
  17. 5 3
      ShatterEffectSample/Platforms/Windows/ShatterEffectSample.Windows.csproj
  18. 4 3
      ShatterEffectSample/Platforms/iOS/ShatterEffectSample.iOS.csproj
  19. 77 0
      ShatterEffectSample/Processor/HelperClasses.cs
  20. 12 0
      ShatterEffectSample/Processor/ShatterEffectSample.Processor.csproj
  21. 186 0
      ShatterEffectSample/Processor/ShatterProcessor.cs
  22. 55 6
      ShatterEffectSample/README.md
  23. 42 33
      ShatterEffectSample/ShatterEffectSample.sln

+ 36 - 0
ShatterEffectSample/.config/dotnet-tools.json

@@ -0,0 +1,36 @@
+{
+  "version": 1,
+  "isRoot": true,
+  "tools": {
+    "dotnet-mgcb": {
+      "version": "3.8.5-develop.4",
+      "commands": [
+        "mgcb"
+      ]
+    },
+    "dotnet-mgcb-editor": {
+      "version": "3.8.5-develop.4",
+      "commands": [
+        "mgcb-editor"
+      ]
+    },
+    "dotnet-mgcb-editor-linux": {
+      "version": "3.8.5-develop.4",
+      "commands": [
+        "mgcb-editor-linux"
+      ]
+    },
+    "dotnet-mgcb-editor-windows": {
+      "version": "3.8.5-develop.4",
+      "commands": [
+        "mgcb-editor-windows"
+      ]
+    },
+    "dotnet-mgcb-editor-mac": {
+      "version": "3.8.5-develop.4",
+      "commands": [
+        "mgcb-editor-mac"
+      ]
+    }
+  }
+}

+ 84 - 0
ShatterEffectSample/Core/Content/Content.mgcb

@@ -0,0 +1,84 @@
+
+#----------------------------- Global Properties ----------------------------#
+
+/outputDir:bin/$(Platform)
+/intermediateDir:obj/$(Platform)
+/platform:DesktopGL
+/config:$(Configuration)
+/profile:HiDef
+/compress:False
+
+#-------------------------------- References --------------------------------#
+
+/reference:..\..\Processor\bin\Release\net8.0\ShatterEffectSample.Processor.dll
+
+#---------------------------------- Content ---------------------------------#
+
+#begin engine_diff_tex.tga
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:engine_diff_tex.tga
+
+#begin font.spritefont
+/importer:FontDescriptionImporter
+/processor:FontDescriptionProcessor
+/processorParam:PremultiplyAlpha=True
+/processorParam:TextureFormat=Compressed
+/build:font.spritefont
+
+#begin ShatterEffect.fx
+/importer:EffectImporter
+/processor:EffectProcessor
+/processorParam:DebugMode=Auto
+/build:ShatterEffect.fx
+
+#begin ShatterEffect.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:ShatterEffect.png
+
+#begin model\tank.fbx
+/importer:FbxImporter
+/processor:ShatterProcessor
+/processorParam:ColorKeyColor=0,0,0,0
+/processorParam:ColorKeyEnabled=True
+/processorParam:DefaultEffect=BasicEffect
+/processorParam:GenerateMipmaps=True
+/processorParam:GenerateTangentFrames=False
+/processorParam:PremultiplyTextureAlpha=True
+/processorParam:PremultiplyVertexColors=True
+/processorParam:ResizeTexturesToPowerOfTwo=False
+/processorParam:RotationX=0
+/processorParam:RotationY=0
+/processorParam:RotationZ=0
+/processorParam:Scale=0.1
+/processorParam:SwapWindingOrder=False
+/processorParam:TextureFormat=Compressed
+/processorParam:MeshCollisionEnabled=true
+/build:model\tank.fbx
+
+#begin turret_alt_diff_tex.tga
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:turret_alt_diff_tex.tga

+ 77 - 0
ShatterEffectSample/Core/Content/Macros.hlsl

@@ -0,0 +1,77 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+#if !defined(MACROS_H)
+#define MACROS_H
+
+
+#if defined(SM4)
+
+// Macros for targetting shader model 4.0 (DX11)
+#define VS_SHADERMODEL vs_4_0
+#define PS_SHADERMODEL ps_4_0
+
+#define TECHNIQUE(name, vsname, psname ) \
+	technique name { pass { VertexShader = compile vs_4_0_level_9_1 vsname (); PixelShader = compile ps_4_0_level_9_1 psname(); } }
+
+#define BEGIN_CONSTANTS     cbuffer Parameters : register(b0) {
+#define MATRIX_CONSTANTS
+#define END_CONSTANTS       };
+
+#define _vs(r)
+#define _ps(r)
+#define _cb(r)
+
+#define DECLARE_TEXTURE_FORMAT(Name, Format, Index) \
+    Texture2D<Format> Name : register(t##Index); \
+    sampler Name##Sampler : register(s##Index)
+
+#define DECLARE_TEXTURE(Name, Index) \
+    Texture2D<float4> Name : register(t##Index); \
+    sampler Name##Sampler : register(s##Index)
+
+#define DECLARE_CUBEMAP(Name, Index) \
+    TextureCube<float4> Name : register(t##Index); \
+    sampler Name##Sampler : register(s##Index)
+
+#define LOAD_TEXTURE(Name, texCoord)  Name.Load(texCoord)
+#define SAMPLE_TEXTURE(Name, texCoord)  Name.Sample(Name##Sampler, texCoord)
+#define SAMPLE_CUBEMAP(Name, texCoord)  Name.Sample(Name##Sampler, texCoord)
+
+#define UNROLL [unroll]
+
+#else // !defined(SM4)
+
+#define SV_POSITION POSITION
+#define SV_TARGET0 COLOR
+#define VS_SHADERMODEL vs_3_0
+#define PS_SHADERMODEL ps_3_0
+
+#define BEGIN_CONSTANTS
+#define MATRIX_CONSTANTS
+#define END_CONSTANTS
+
+#define _vs(r)  : register(vs, r)
+#define _ps(r)  : register(ps, r)
+#define _cb(r)
+
+#define DECLARE_TEXTURE(Name, Index) \
+    sampler2D Name : register(s##Index)
+
+#define DECLARE_CUBEMAP(Name, Index) \
+    samplerCUBE Name : register(s##Index)
+
+#define DECLARE_TEXTURE_FORMAT(Name, Format, Index) \
+    sampler2D Name : register(s##Index)
+
+#define SAMPLE_TEXTURE(Name, texCoord)  tex2D(Name, texCoord)
+#define SAMPLE_CUBEMAP(Name, texCoord)  texCUBE(Name, texCoord)
+#define LOAD_TEXTURE(Name, texCoord)  tex2D(Name, texCoord.xy)
+
+#define UNROLL [unroll]
+
+#endif
+
+
+#endif // MACROS_H

+ 25 - 41
ShatterEffectSample/Core/Content/ShatterEffect.fx

@@ -1,17 +1,16 @@
-//-----------------------------------------------------------------------------
-// ShatterEffect.fx
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
 
+#include "Macros.hlsl"
 
 float4x4 WorldViewProjection;
 float4x4 World : World;
 float RotationAmount;
 float TranslationAmount;
-texture modelTexture;
 float time;
+Texture2D modelTexture : register(t0);
+SamplerState TextureSampler : register(s0);
 
 // Lighting Data
 float3 eyePosition;
@@ -21,21 +20,9 @@ float4 diffuseColor;
 float4 specularColor;
 float specularPower;
 
-
-sampler TextureSampler = sampler_state
-{
-    Texture = <modelTexture>;
-    MinFilter = Anisotropic;
-    MagFilter = LINEAR;
-    MipFilter = LINEAR;
-    MaxAnisotropy = 8;
-    AddressU  = WRAP;
-    AddressV  = WRAP;
-};
-
 struct VertexShaderOutput
 {
-     float4 Position : POSITION;
+     float4 Position : SV_POSITION;
      float3 WorldNormal : TEXCOORD0;
      float3 WorldPosition : TEXCOORD1;
      float2 texCoords : TEXCOORD2;
@@ -47,7 +34,7 @@ struct PixelShaderInput
      float3 WorldNormal : TEXCOORD0;
      float3 WorldPosition : TEXCOORD1;
      float2 TexCoords : TEXCOORD2;
-     float4 Color: COLOR0;
+     float4 Color: TEXCOORD3;
 };
 
 struct InputVS
@@ -110,8 +97,7 @@ VertexShaderOutput ShatterVS(InputVS input)
                                                   input.RotationalVelocity.z);
     
     // Rotate the vertex around its triangle's center
-    float3 position = input.TriangleCenter + mul(input.Position - input.TriangleCenter, 
-                                            rotMatrix);
+    float3 position = input.TriangleCenter + mul(input.Position - input.TriangleCenter, rotMatrix);
     
     // Displace the vertex along its normal
     position += input.Normal * TranslationAmount;    
@@ -140,30 +126,28 @@ VertexShaderOutput ShatterVS(InputVS input)
     return output;
 }
 
-float4 PhongPS(PixelShaderInput input) : COLOR
+float4 PhongPS(PixelShaderInput input) : SV_Target
 {
-     float3 directionToLight = normalize(lightPosition - input.WorldPosition);
-     float3 reflectionVector = normalize(reflect(-directionToLight, input.WorldNormal));
-     float3 directionToCamera = normalize(eyePosition - input.WorldPosition);
-     
-     //calculate specular component
-     float4 specular = specularColor *  
-                       pow( saturate(dot(reflectionVector, directionToCamera)), 
-                       specularPower);
-     
-     float4 TextureColor   = tex2D(TextureSampler, input.TexCoords);
-     
-     float4 color = TextureColor * input.Color + specular;
-     color.a = 1.0;                   
-          
-     return color;
+    float3 directionToLight = normalize(lightPosition - input.WorldPosition);
+    float3 reflectionVector = normalize(reflect(-directionToLight, input.WorldNormal));
+    float3 directionToCamera = normalize(eyePosition - input.WorldPosition);
+
+    float4 specular = specularColor *  
+                      pow(saturate(dot(reflectionVector, directionToCamera)), specularPower);
+
+    float4 TextureColor = modelTexture.Sample(TextureSampler, input.TexCoords);
+
+    float4 color = TextureColor * input.Color + specular;
+    color.a = 1.0;
+
+    return color;
 }
 
 technique
 {
     pass
     {        
-        VertexShader = compile vs_2_0 ShatterVS();
-        PixelShader = compile ps_2_0 PhongPS();
+        VertexShader = compile VS_SHADERMODEL ShatterVS();
+        PixelShader = compile PS_SHADERMODEL PhongPS();
     }
 }

BIN
ShatterEffectSample/Core/Content/ShatterEffect.xnb


BIN
ShatterEffectSample/Core/Content/engine_diff_tex_0.xnb


+ 1 - 1
ShatterEffectSample/Core/Content/font.spritefont

@@ -11,7 +11,7 @@ with.
     <!--
     Modify this string to change the font that will be imported.
     -->
-    <FontName>Segoe UI Mono</FontName>
+    <FontName>Arial</FontName>
 
     <!--
     Size is a float value, measured in points. Modify this value to change

BIN
ShatterEffectSample/Core/Content/font.xnb


BIN
ShatterEffectSample/Core/Content/model/tank.fbx


BIN
ShatterEffectSample/Core/Content/tank.xnb


+ 0 - 0
ShatterEffectSample/Core/Content/tank.fbx → ShatterEffectSample/Core/Content/tank1.fbx


BIN
ShatterEffectSample/Core/Content/turret_alt_diff_tex_0.xnb


+ 1 - 1
ShatterEffectSample/Core/ShatterEffectGame.cs

@@ -55,7 +55,7 @@ namespace ShatterEffect
             spriteBatch = new SpriteBatch(this.graphics.GraphicsDevice);
             
             font = Content.Load<SpriteFont>("font");
-            model = Content.Load<Model>("tank");
+            model = Content.Load<Model>("model/tank");
 
             // Calculate View/Projection Matrices.
             view = Matrix.CreateLookAt(cameraPosition, targetPosition, Vector3.Up);

+ 2 - 3
ShatterEffectSample/Core/ShatterEffectSample.Core.csproj

@@ -1,9 +1,8 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <TargetFramework>net8.0</TargetFramework>
-    <RootNamespace>ShatterEffectSample.Core</RootNamespace>
-    <AssemblyTitle>Shatter Effect</AssemblyTitle>
-    <AssemblyProduct>Shatter Effect</AssemblyProduct>
+    <RootNamespace>ShatterEffect.Core</RootNamespace>
+    <AssemblyProduct>ShatterEffect.Core</AssemblyProduct>
     <Description>This sample shows how you can apply an effect on any model in your game to shatter it apart. The effect is simulated with a vertex shader.</Description>
     <Company>Microsoft</Company>
     <Copyright>Copyright © Microsoft</Copyright>

+ 4 - 4
ShatterEffectSample/Platforms/Android/ShatterEffectSample.Android.csproj

@@ -9,16 +9,16 @@
     <Company>Microsoft</Company>
     <Copyright>Copyright © Microsoft</Copyright>
     <Version>1.0.0.0</Version>
+    <MonoGamePlaform>Android</MonoGamePlaform>
   </PropertyGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\..\Core\ShatterEffectSample.Core.csproj" />
     <PackageReference Include="MonoGame.Framework.Android" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
   </ItemGroup>
-  
+
   <ItemGroup>
-    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
+    <MonoGameContentReference Include="..\..\Core\Content\Content.mgcb" />
   </ItemGroup>
 </Project>

+ 3 - 3
ShatterEffectSample/Platforms/DesktopGL/ShatterEffectSample.DesktopGL.csproj

@@ -9,16 +9,16 @@
     <Company>Microsoft</Company>
     <Copyright>Copyright © Microsoft</Copyright>
     <Version>1.0.0.0</Version>
+    <MonoGamePlaform>DesktopGL</MonoGamePlaform>
   </PropertyGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\..\Core\ShatterEffectSample.Core.csproj" />
     <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
   </ItemGroup>
 
   <ItemGroup>
-    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
+    <MonoGameContentReference Include="..\..\Core\Content\Content.mgcb" />
   </ItemGroup>
 </Project>

+ 5 - 3
ShatterEffectSample/Platforms/Windows/ShatterEffectSample.Windows.csproj

@@ -10,17 +10,19 @@
     <Copyright>Copyright © Microsoft</Copyright>
     <Version>1.0.0.0</Version>
     <UseWindowsForms>true</UseWindowsForms>
+    <MonoGamePlaform>Windows</MonoGamePlaform>
   </PropertyGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\..\Core\ShatterEffectSample.Core.csproj" />
     <PackageReference Include="MonoGame.Framework.WindowsDX" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
   </ItemGroup>
 
   <ItemGroup>
-    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
+    <MonoGameContentReference Include="..\..\Core\Content\Content.mgcb" />
   </ItemGroup>
+
+  
   
 </Project>

+ 4 - 3
ShatterEffectSample/Platforms/iOS/ShatterEffectSample.iOS.csproj

@@ -9,16 +9,17 @@
     <Company>Microsoft</Company>
     <Copyright>Copyright © Microsoft</Copyright>
     <Version>1.0.0.0</Version>
+    <MonoGamePlaform>iOS</MonoGamePlaform>
   </PropertyGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\..\Core\ShatterEffectSample.Core.csproj" />
     <PackageReference Include="MonoGame.Framework.iOS" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
   </ItemGroup>
 
   <ItemGroup>
-    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
+    <MonoGameContentReference Include="..\..\Core\Content\Content.mgcb" />
   </ItemGroup>
+  
 </Project>

+ 77 - 0
ShatterEffectSample/Processor/HelperClasses.cs

@@ -0,0 +1,77 @@
+//-----------------------------------------------------------------------------
+// HelperClasses.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+
+namespace ShatterEffectProcessor
+{
+    /// <summary>
+    /// Enumerates each element 3 times, once for each vertex in a triangle
+    /// </summary>    
+    internal class ReplicateTriangleDataToEachVertex<T> : IEnumerable<T>
+    {
+        private IEnumerable<T> perTriangleData;
+
+        public ReplicateTriangleDataToEachVertex(IEnumerable<T> perTriangleData)
+        {
+            this.perTriangleData = perTriangleData;
+        }
+
+        public IEnumerator<T> GetEnumerator()
+        {
+            foreach (T item in perTriangleData)
+            {
+                for (int i = 0; i < 3; i++)
+                {
+                    // Return the same center value for every 3 vertices.
+                    yield return item;
+                }
+            }
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+    }
+
+    /// <summary>
+    /// Enumerates a set of random vectors such that each element of those vectors
+    /// are in the range [-1,1]
+    /// </summary>
+    internal class RandomVectorEnumerable : IEnumerable<Vector3>
+    {
+        private Random random = new Random();
+        private int count;
+
+        public RandomVectorEnumerable(int count)
+        {
+            this.count = count;
+        }
+
+        public IEnumerator<Vector3> GetEnumerator()
+        {
+            for (int i = 0; i < count; i++)
+            {
+                Vector3 vector = new Vector3((float)random.NextDouble(),
+                    (float)random.NextDouble(), (float)random.NextDouble());
+                vector *= 2;
+                vector -= Vector3.One;
+
+                yield return vector;
+            }
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+    }
+}

+ 12 - 0
ShatterEffectSample/Processor/ShatterEffectSample.Processor.csproj

@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <RootNamespace>ShatterEffect.Processor</RootNamespace>
+    <AssemblyProduct>ShatterEffect.Processor</AssemblyProduct>
+  </PropertyGroup>
+  <!-- Let SDK include all .cs files by default -->
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Framework.Content.Pipeline" Version="3.8.*" />
+  </ItemGroup>
+</Project>

+ 186 - 0
ShatterEffectSample/Processor/ShatterProcessor.cs

@@ -0,0 +1,186 @@
+//-----------------------------------------------------------------------------
+// ShatterProcessor.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Content.Pipeline;
+using Microsoft.Xna.Framework.Content.Pipeline.Processors;
+using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
+
+namespace ShatterEffectProcessor
+{
+    [ContentProcessor]
+    public class ShatterProcessor : ModelProcessor
+    {
+        private string triangleCenterChannel = VertexChannelNames.TextureCoordinate(1);
+        private string rotationalVelocityChannel =
+            VertexChannelNames.TextureCoordinate(2);
+
+        public override ModelContent Process(NodeContent input,
+                                             ContentProcessorContext context)
+        {
+            // Break up the mesh to separate triangles.           
+            NodeContent processedNode = ProcessMesh(input);
+            return base.Process(processedNode, context);
+        }
+
+        /// <summary>
+        /// Breaks the input mesh into separate un-indexed triangles.
+        /// </summary>
+        /// <param name="input">Input MeshContent node.</param>
+        /// <returns>Broken MeshContent</returns>
+        private MeshContent ProcessMesh(NodeContent input)
+        {
+            MeshBuilder builder = MeshBuilder.StartMesh("model");
+
+            MeshContent mesh = input as MeshContent;
+            List<Vector3> normalList = new List<Vector3>();
+            List<Vector2> texCoordList = new List<Vector2>();
+
+            if (mesh != null)
+            {
+                int normalChannel = builder.CreateVertexChannel<Vector3>(
+                                               VertexChannelNames.Normal());
+                int texChannel = builder.CreateVertexChannel<Vector2>(
+                                               VertexChannelNames.TextureCoordinate(0));
+
+                foreach (GeometryContent geometry in mesh.Geometry)
+                {
+                    IndirectPositionCollection positions = geometry.Vertices.Positions;
+
+                    VertexChannel<Vector3> normals =
+                        geometry.Vertices.Channels.Get<Vector3>(
+                                                        VertexChannelNames.Normal());
+
+                    VertexChannel<Vector2> texCoords =
+                        geometry.Vertices.Channels.Get<Vector2>(
+                                            VertexChannelNames.TextureCoordinate(0));
+
+                    // Copy the positions over     
+                    // To do that, we traverse the indices and grab the indexed 
+                    // position  and add it to the new mesh. This in effect will 
+                    // duplicate positions in the mesh reversing the compacting
+                    // effect of using index buffers.
+                    foreach (int i in geometry.Indices)
+                    {
+                        builder.CreatePosition(positions[i]);
+
+                        // Save the normals and the texture coordinates for additon to
+                        // the mesh later.
+                        normalList.Add(normals[i]);
+                        texCoordList.Add(texCoords[i]);
+                    }
+                }
+
+                int index = 0;
+
+                foreach (GeometryContent geometry in mesh.Geometry)
+                {
+                    // Save the material to the new mesh.
+                    builder.SetMaterial(geometry.Material);
+
+                    // Now we create the Triangles. 
+                    // To do that, we simply generate an index list that is sequential
+                    // from 0 to geometry.Indices.Count
+                    // This will create an index buffer that looks like: 0,1,2,3,4,5,...
+                    for (int i = 0; i < geometry.Indices.Count; i++)
+                    {
+                        // Set the normal for the current vertex
+                        builder.SetVertexChannelData(normalChannel, normalList[index]);
+                        // Set the texture coordinates for the current vertex
+                        builder.SetVertexChannelData(texChannel, texCoordList[index]);
+                        builder.AddTriangleVertex(index);
+                        index++;
+                    }
+                }
+            }
+
+            MeshContent finalMesh = builder.FinishMesh();
+            // Copy the transform over from the source mesh to retain parent/child 
+            // relative transforms.
+            finalMesh.Transform = input.Transform;
+
+            // Now we take the new MeshContent and calculate the centers of all the
+            // triangles. The centers are needed so that we can rotate the triangles
+            // around them as we shatter the model.
+            foreach (GeometryContent geometry in finalMesh.Geometry)
+            {
+                Vector3[] triangleCenters = new Vector3[geometry.Indices.Count / 3];
+                Vector3[] trianglePoints = new Vector3[2];
+
+                IndirectPositionCollection positions = geometry.Vertices.Positions;
+
+                for (int i = 0; i < positions.Count; i++)
+                {
+                    Vector3 position = positions[i];
+
+                    if (i % 3 == 2)
+                    {
+                        // Calculate the center of the triangle.
+                        triangleCenters[i / 3] = (trianglePoints[0] + trianglePoints[1]
+                                                 + position) / 3;
+                    }
+                    else
+                    {
+                        trianglePoints[i % 3] = position;
+                    }
+                }
+
+                // Add two new channels to the MeshContent:
+                // triangleCenterChannel: This is the channel that will store the center
+                // of the triangle that this vertex belongs to.
+                // rotationalVelocityChannel: This channel has randomly generated values
+                // for x,y and z rotational angles. This information will be used to
+                // randomly rotate the triangles as they shatter from the model.
+                geometry.Vertices.Channels.Add<Vector3>(
+                    triangleCenterChannel,
+                    new ReplicateTriangleDataToEachVertex<Vector3>(triangleCenters));
+                geometry.Vertices.Channels.Add<Vector3>(
+                    rotationalVelocityChannel,
+                    new ReplicateTriangleDataToEachVertex<Vector3>(
+                    new RandomVectorEnumerable(triangleCenters.Length)));
+            }
+
+            foreach (NodeContent child in input.Children)
+            {
+                finalMesh.Children.Add(ProcessMesh(child));
+            }
+
+            return finalMesh;
+        }
+
+
+
+        /// <summary>
+        ///Overriding the ConvertMaterial function of ModelProcessor so that we can
+        ///replace the BasicEffect with our own ShatterEffect.   
+        /// </summary>
+        /// <param name="material">input Material</param>
+        /// <param name="context">Content processor context</param>
+        /// <returns></returns>
+        protected override MaterialContent ConvertMaterial(MaterialContent material,
+                                                    ContentProcessorContext context)
+        {
+            EffectMaterialContent effect = new EffectMaterialContent();
+            // Use our own ShatterEffect.fx instead of BasicEffect.
+            effect.Effect =
+                new ExternalReference<EffectContent>("shatterEffect.fx");
+
+            foreach (ExternalReference<TextureContent> texture in
+                                                            material.Textures.Values)
+            {
+                // Add the textures in the source Material to our effect.
+                effect.Textures.Add("modelTexture", texture);
+            }
+            return base.ConvertMaterial(effect, context);
+        }
+    }
+}

+ 55 - 6
ShatterEffectSample/README.md

@@ -1,6 +1,59 @@
 # Shatter Effect Sample
 
-This project demonstrates how to apply a shatter effect to any model in your game using MonoGame 3.8.*. The effect is simulated with a vertex shader. The codebase is organized for cross-platform support with shared logic in the `Core` project and platform-specific entry points for Windows, DesktopGL, Android, and iOS.
+This sample shows how you can apply an effect on any model in your game to shatter it apart. The effect is simulated with a vertex shader.
+
+## Sample Overview
+
+The shatter effect operates independently on every triangle in the model. For every triangle in the model, the vertex shader rotates the vertices of the triangle around the x, y, and z axes by random velocities. At the same time, the triangle is translated along its normal. This creates the appearance of the entire model shattering outwards into small pieces. To get this effect to work properly, the model must be processed beforehand with a custom processor that derives from [ModelProcessor](http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.pipeline.processors.modelprocessor.aspx).
+
+### Sample Controls
+
+This sample uses the following keyboard and gamepad controls:
+
+| Action                     | Keyboard control | Gamepad control |
+|----------------------------|------------------|-----------------|
+| Shatter the model          | UP ARROW         | **A**           |
+| Reverse the shatter effect | DOWN ARROW       | **B**           |
+| Exit the sample            | ESC or ALT+F4    | **BACK**        |
+
+## How the Sample Works
+
+The shatter effect is achieved with the help of both a processor and an effect.
+
+### Build Time Processing
+
+The processor, `ShatterProcessor`, is derived from **ModelProcessor** and overrides the **Process** method. The processor manipulates the model’s data in three ways before passing it to **ModelProcessor** to produce the processor’s final output.
+
+#### 1.  Break down the model’s triangles
+
+This step reverses the effect of indexing a model’s vertices to break up the triangles. While it will increase the number of vertices for the model on the GPU, this is required so that we can independently manipulate every triangle without affecting nearby ones. An indexed model does not give us such freedom.
+
+#### 2.  Calculate the center point of every triangle in the model
+
+Once the model’s triangles have been broken down, the processor calculates the center point of every triangle using the formula:
+
+```
+TriangleCenter = (Vertex_1 + Vertex_2 + Vertex_3) / 3
+```
+
+The triangle centers are then saved in a `Vector3[]` array. For every triangle in the model, the triangle center is used in the vertex shader as the point to rotate the triangle around.
+
+#### 3.  Create two vertex channels for the model
+
+Now that the triangle centers have been calculated, the processor creates a per-vertex channel to store them. For every three vertices, we store the triangle center that corresponds to the triangle they form. The channel is called `triangleCenterChannel`. This channel adds a `float3 TEXCOORD1` field to the vertex declaration for the vertex shader to use. The channel is created with the help of a helper function called `ReplicateTriangleDataToEachVertex`. This function uses the `Vector3[]` array created earlier and returns the corresponding center for every vertex in the model.
+
+The second channel the processor creates is called `rotationalVelocityChannel`. This channel creates a `float3 TEXCOORD2` field in the vertex declaration for the model. For every vertex, the processor generates a random set of x, y, and z velocities in the range of –1 to 1. The vertex shader uses these values to determine how to rotate each vertex as the model is shattered.
+
+The processor then passes the newly created model to the parent processor, **ModelProcessor**, to complete processing it.
+
+## Extending the Sample
+
+The sample can be extended in a variety of ways:
+
+- The effect could simulate wind drag for every triangle based on its size, so that bigger triangles fall a bit slower than smaller ones. Create another vertex channel containing information about triangle size and precalculate the channel at processing time.
+- Group together chunks of triangles that are close to each other and rotate/translate them as a single entity. This gives the illusion of chunks of the model breaking versus smaller pieces.
+
+---
 
 ## Project Structure
 - `Core/` — Shared game logic and content pipeline files
@@ -34,8 +87,4 @@ This project demonstrates how to apply a shatter effect to any model in your gam
 - Platform-specific code is separated to avoid `#if` blocks.
 - Only Windows, DesktopGL, Android, and iOS are supported. Linux, MacOS, and PSMobile are not supported in MonoGame 3.8.*.
 
-## License
-Copyright © Microsoft
-
----
-For more details, see the source code and comments in each project.
+---

+ 42 - 33
ShatterEffectSample/ShatterEffectSample.sln

@@ -1,4 +1,3 @@
-
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio Version 17
 VisualStudioVersion = 17.5.2.0
@@ -13,37 +12,47 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShatterEffectSample.Android
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShatterEffectSample.iOS", "Platforms\iOS\ShatterEffectSample.iOS.csproj", "{55555555-5555-5555-5555-555555555555}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShatterEffectSample.Processor", "Processor\ShatterEffectSample.Processor.csproj", "{9B372090-F91A-E91A-DC78-F892816ED2CC}"
+EndProject
 Global
-	   GlobalSection(SolutionConfigurationPlatforms) = preSolution
-			   Debug|Any CPU = Debug|Any CPU
-			   Release|Any CPU = Release|Any CPU
-	   EndGlobalSection
-	   GlobalSection(ProjectConfigurationPlatforms) = postSolution
-			   {11111111-1111-1111-1111-111111111111}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-			   {11111111-1111-1111-1111-111111111111}.Debug|Any CPU.Build.0 = Debug|Any CPU
-			   {11111111-1111-1111-1111-111111111111}.Release|Any CPU.ActiveCfg = Release|Any CPU
-			   {11111111-1111-1111-1111-111111111111}.Release|Any CPU.Build.0 = Release|Any CPU
-			   {22222222-2222-2222-2222-222222222222}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-			   {22222222-2222-2222-2222-222222222222}.Debug|Any CPU.Build.0 = Debug|Any CPU
-			   {22222222-2222-2222-2222-222222222222}.Release|Any CPU.ActiveCfg = Release|Any CPU
-			   {22222222-2222-2222-2222-222222222222}.Release|Any CPU.Build.0 = Release|Any CPU
-			   {33333333-3333-3333-3333-333333333333}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-			   {33333333-3333-3333-3333-333333333333}.Debug|Any CPU.Build.0 = Debug|Any CPU
-			   {33333333-3333-3333-3333-333333333333}.Release|Any CPU.ActiveCfg = Release|Any CPU
-			   {33333333-3333-3333-3333-333333333333}.Release|Any CPU.Build.0 = Release|Any CPU
-			   {44444444-4444-4444-4444-444444444444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-			   {44444444-4444-4444-4444-444444444444}.Debug|Any CPU.Build.0 = Debug|Any CPU
-			   {44444444-4444-4444-4444-444444444444}.Release|Any CPU.ActiveCfg = Release|Any CPU
-			   {44444444-4444-4444-4444-444444444444}.Release|Any CPU.Build.0 = Release|Any CPU
-			   {55555555-5555-5555-5555-555555555555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-			   {55555555-5555-5555-5555-555555555555}.Debug|Any CPU.Build.0 = Debug|Any CPU
-			   {55555555-5555-5555-5555-555555555555}.Release|Any CPU.ActiveCfg = Release|Any CPU
-			   {55555555-5555-5555-5555-555555555555}.Release|Any CPU.Build.0 = Release|Any CPU
-	   EndGlobalSection
-	   GlobalSection(SolutionProperties) = preSolution
-			   HideSolutionNode = FALSE
-	   EndGlobalSection
-	   GlobalSection(ExtensibilityGlobals) = postSolution
-			   SolutionGuid = {9CC4CC96-C4DD-4CB8-8DFC-CB19A85CA4A2}
-	   EndGlobalSection
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{11111111-1111-1111-1111-111111111111}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{11111111-1111-1111-1111-111111111111}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{11111111-1111-1111-1111-111111111111}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{11111111-1111-1111-1111-111111111111}.Release|Any CPU.Build.0 = Release|Any CPU
+		{22222222-2222-2222-2222-222222222222}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{22222222-2222-2222-2222-222222222222}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{22222222-2222-2222-2222-222222222222}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{22222222-2222-2222-2222-222222222222}.Release|Any CPU.Build.0 = Release|Any CPU
+		{33333333-3333-3333-3333-333333333333}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{33333333-3333-3333-3333-333333333333}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{33333333-3333-3333-3333-333333333333}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{33333333-3333-3333-3333-333333333333}.Release|Any CPU.Build.0 = Release|Any CPU
+		{44444444-4444-4444-4444-444444444444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{44444444-4444-4444-4444-444444444444}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{44444444-4444-4444-4444-444444444444}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{44444444-4444-4444-4444-444444444444}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{44444444-4444-4444-4444-444444444444}.Release|Any CPU.Build.0 = Release|Any CPU
+		{44444444-4444-4444-4444-444444444444}.Release|Any CPU.Deploy.0 = Release|Any CPU
+		{55555555-5555-5555-5555-555555555555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{55555555-5555-5555-5555-555555555555}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{55555555-5555-5555-5555-555555555555}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{55555555-5555-5555-5555-555555555555}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{55555555-5555-5555-5555-555555555555}.Release|Any CPU.Build.0 = Release|Any CPU
+		{55555555-5555-5555-5555-555555555555}.Release|Any CPU.Deploy.0 = Release|Any CPU
+		{9B372090-F91A-E91A-DC78-F892816ED2CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9B372090-F91A-E91A-DC78-F892816ED2CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9B372090-F91A-E91A-DC78-F892816ED2CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9B372090-F91A-E91A-DC78-F892816ED2CC}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		   SolutionGuid = {9CC4CC96-C4DD-4CB8-8DFC-CB19A85CA4A2}
+	EndGlobalSection
 EndGlobal