MainScene.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows.Controls;
  6. using Microsoft.Xna.Framework;
  7. using Microsoft.Xna.Framework.Graphics;
  8. using Microsoft.Xna.Framework.Input;
  9. using MonoGame.WpfCore.MonoGameControls;
  10. using MonoScene.Graphics;
  11. using SharpGLTF.Validation;
  12. namespace MonoGameViewer
  13. {
  14. public class MainScene : MonoGameViewModel
  15. {
  16. #region data
  17. bool _IsAssimp;
  18. SharpGLTF.Schema2.ModelRoot _Model;
  19. DeviceModelCollection _ModelTemplate;
  20. BoundingSphere _ModelSphere;
  21. private bool _UseClassicEffects;
  22. private ModelInstance _ModelInstance;
  23. private Quaternion _Rotation = Quaternion.Identity;
  24. private float _Zoom = 2.5f;
  25. private readonly GlobalLight _GlobalLight = new GlobalLight();
  26. private readonly PunctualLight[] _PunctualLights = new PunctualLight[] { PunctualLight.CreateDefault(0), PunctualLight.CreateDefault(1), PunctualLight.CreateDefault(2) };
  27. #endregion
  28. #region properties
  29. [PropertyTools.DataAnnotations.Description("If enabled, it will use BasicEffect and SkinnedEffect.\r\n⚠ Switching this checkbox will reload the model!")]
  30. public bool UseClassicEffects
  31. {
  32. get => _UseClassicEffects;
  33. set
  34. {
  35. if (value == _UseClassicEffects) return;
  36. _UseClassicEffects = value;
  37. _ProcessModel();
  38. }
  39. }
  40. [PropertyTools.DataAnnotations.Browsable(false)]
  41. public GlobalLight GlobalLight => _GlobalLight;
  42. [PropertyTools.DataAnnotations.Browsable(false)]
  43. public PunctualLight[] PunctualLights => _PunctualLights;
  44. #endregion
  45. #region API
  46. public void LoadModel(string filePath)
  47. {
  48. try
  49. {
  50. var fp = filePath.ToLower();
  51. if (fp.EndsWith(".zip"))
  52. {
  53. _Model = SharpGLTF.IO.ZipReader.LoadModelFromZip(filePath, ValidationMode.TryFix);
  54. _IsAssimp = false;
  55. }
  56. else if (fp.EndsWith(".glb") || fp.EndsWith(".gltf"))
  57. {
  58. _Model = SharpGLTF.Schema2.ModelRoot.Load(filePath, ValidationMode.TryFix);
  59. _IsAssimp = false;
  60. }
  61. else if (fp.EndsWith(".obj") || fp.EndsWith(".blend") || fp.EndsWith(".fbx"))
  62. {
  63. _IsAssimp = true;
  64. var assimpFactory = new MonoScene.Graphics.Pipeline.AssimpModelFactory(GraphicsDevice);
  65. assimpFactory.UseBasicEffects = _UseClassicEffects;
  66. _ModelTemplate = assimpFactory.LoadModel(filePath);
  67. _ModelSphere = _ModelTemplate.DefaultModel.ModelBounds;
  68. if (_ModelSphere.Radius == 0) throw new ArgumentException();
  69. _ModelInstance = null;
  70. }
  71. }
  72. catch(Exception ex) { System.Windows.MessageBox.Show(ex.Message, "Error", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); return; }
  73. _ProcessModel();
  74. }
  75. private void _ProcessModel()
  76. {
  77. if (_IsAssimp) return;
  78. if (_Model == null) return;
  79. if (_ModelTemplate != null) { _ModelTemplate.Dispose(); _ModelTemplate = null; }
  80. var gltfFactory = new MonoScene.Graphics.Pipeline.GltfModelFactory(this.GraphicsDevice);
  81. gltfFactory.UseBasicEffects = _UseClassicEffects;
  82. _ModelTemplate = gltfFactory.ReadModel(_Model);
  83. _ModelSphere = _ModelTemplate.DefaultModel.ModelBounds;
  84. _ModelInstance = null;
  85. GC.Collect();
  86. }
  87. public override void UnloadContent()
  88. {
  89. base.UnloadContent();
  90. _ModelTemplate?.Dispose(); _ModelTemplate = null;
  91. }
  92. public void RotateModel(float x, float y)
  93. {
  94. _Rotation = Quaternion.CreateFromYawPitchRoll(x, y, 0) * _Rotation;
  95. _Rotation.Normalize();
  96. }
  97. public void ZoomModel(float z)
  98. {
  99. _Zoom += z * 0.001f;
  100. if (_Zoom < 0.0001f) _Zoom = 0.0001f;
  101. }
  102. public override void Update(GameTime gameTime)
  103. {
  104. if (_ModelInstance == null && _ModelTemplate != null)
  105. {
  106. _ModelInstance = _ModelTemplate.DefaultModel.CreateInstance();
  107. _Rotation = Quaternion.Identity;
  108. _Zoom = 2.5f;
  109. }
  110. }
  111. public override void Draw(GameTime gameTime)
  112. {
  113. GraphicsDevice.Clear(Color.CornflowerBlue);
  114. if (_ModelInstance == null) return;
  115. _ModelInstance.Armature.SetAnimationFrame(0, (float)gameTime.TotalGameTime.TotalSeconds);
  116. var lookAt = _ModelSphere.Center;
  117. var camPos = _ModelSphere.Center + new Vector3(0, 0, _ModelSphere.Radius * _Zoom);
  118. var camera = Matrix.CreateWorld(camPos, lookAt - camPos, Vector3.UnitY);
  119. var env = new PBREnvironment();
  120. env.SetExposure((float)_GlobalLight.Exposure / 10.0f);
  121. env.SetAmbientLight(_GlobalLight.ToXna());
  122. for(int i=0; i< _PunctualLights.Length; ++i)
  123. {
  124. env.SetDirectLight(i, _PunctualLights[i].Direction, _PunctualLights[i].XnaColor, _PunctualLights[i].Intensity / 10.0f);
  125. }
  126. if (_ModelInstance != null)
  127. {
  128. _ModelInstance.WorldMatrix = Matrix.CreateFromQuaternion(_Rotation);
  129. var ctx = new ModelDrawingContext(this.GraphicsDevice);
  130. ctx.NearPlane = Math.Min(1, _ModelSphere.Radius);
  131. ctx.SetCamera(camera);
  132. ctx.DrawModelInstance(env, _ModelInstance);
  133. }
  134. }
  135. #endregion
  136. }
  137. public class GlobalLight
  138. {
  139. [PropertyTools.DataAnnotations.Slidable(0,100)]
  140. [PropertyTools.DataAnnotations.WideProperty]
  141. public int Exposure { get; set; } = 25;
  142. public System.Windows.Media.Color AmbientColor { get; set; } = System.Windows.Media.Colors.Black;
  143. public Vector3 ToXna() { return new Vector3(AmbientColor.ScR, AmbientColor.ScG, AmbientColor.ScB); }
  144. }
  145. public class PunctualLight
  146. {
  147. public static PunctualLight CreateDefault(int idx)
  148. {
  149. var l = new PunctualLight();
  150. l.Intensity = 15;
  151. l.Color = System.Windows.Media.Colors.White;
  152. switch(idx)
  153. {
  154. case 0:
  155. l.DirectionAngle = 60;
  156. l.ElevationAngle = 30;
  157. l.Intensity = 40;
  158. break;
  159. case 1:
  160. l.DirectionAngle = -70;
  161. l.ElevationAngle = 60;
  162. l.Color = System.Windows.Media.Colors.DeepSkyBlue;
  163. break;
  164. case 2:
  165. l.DirectionAngle = 20;
  166. l.ElevationAngle = -50;
  167. l.Color = System.Windows.Media.Colors.OrangeRed;
  168. break;
  169. }
  170. return l;
  171. }
  172. [PropertyTools.DataAnnotations.Category("Source")]
  173. [PropertyTools.DataAnnotations.Slidable(-180,180)]
  174. // [PropertyTools.DataAnnotations.WideProperty]
  175. [PropertyTools.DataAnnotations.DisplayName("Direction")]
  176. public int DirectionAngle { get; set; }
  177. [PropertyTools.DataAnnotations.Category("Source")]
  178. [PropertyTools.DataAnnotations.Slidable(-90, 90)]
  179. //[PropertyTools.DataAnnotations.WideProperty]
  180. [PropertyTools.DataAnnotations.DisplayName("Elevation")]
  181. public int ElevationAngle { get; set; }
  182. [PropertyTools.DataAnnotations.Category("Properties")]
  183. public System.Windows.Media.Color Color { get; set; }
  184. [PropertyTools.DataAnnotations.Category("Properties")]
  185. [PropertyTools.DataAnnotations.Slidable(0,100)]
  186. [PropertyTools.DataAnnotations.WideProperty]
  187. public int Intensity { get; set; }
  188. [PropertyTools.DataAnnotations.Browsable(false)]
  189. public Vector3 Direction
  190. {
  191. get
  192. {
  193. float yaw = (float)(DirectionAngle * Math.PI) / 180.0f;
  194. float pitch = (float)(ElevationAngle * Math.PI) / 180.0f;
  195. var xform = Matrix.CreateFromYawPitchRoll(yaw + 3.141592f, pitch, 0);
  196. return Vector3.Transform(Vector3.UnitZ, xform);
  197. }
  198. }
  199. [PropertyTools.DataAnnotations.Browsable(false)]
  200. public Color XnaColor => new Color(Color.ScR, Color.ScG, Color.ScB);
  201. }
  202. }