Skip to content

Commit 2d5548e

Browse files
committed
Refactoring monogame loader
1 parent d372441 commit 2d5548e

11 files changed

Lines changed: 568 additions & 432 deletions

examples/MonoGameIntegrationDemo/Models/MonoGameContext.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,13 @@ protected override void Update(GameTime gameTime)
7272

7373
protected override void Draw(GameTime gameTime)
7474
{
75-
GraphicsDevice.Clear(Color.CornflowerBlue);
75+
GraphicsDevice.Clear(Color.CornflowerBlue);
76+
77+
// setup lights and fog for the effects in the template
78+
79+
_CurrentModelInstance?.Template?.ConfigureLightsAndFog(null, null);
80+
81+
// rendering
7682

7783
if (_CurrentModelInstance != null)
7884
{

examples/SharpGLTF.Runtime.MonoGame/MonoGameModelInstance.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
using Microsoft.Xna.Framework.Graphics;
77

88
using SharpGLTF.Runtime.Template;
9+
using SharpGLTF.Schema2;
10+
using SharpGLTF.Transforms;
11+
12+
913

1014

1115
#if USINGMONOGAMEMODEL
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Numerics;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
using Microsoft.Xna.Framework.Graphics;
9+
using Microsoft.Xna.Framework.Input;
10+
11+
using SharpGLTF.Schema2;
12+
13+
using GLTFMATERIAL = SharpGLTF.Schema2.Material;
14+
15+
namespace SharpGLTF.Runtime.Pipeline
16+
{
17+
/// <summary>
18+
/// Converts glTF materials into MonoGame effects
19+
/// </summary>
20+
public abstract class EffectsFactory
21+
{
22+
#region lifecycle
23+
24+
/// <summary>
25+
/// Register here your own <see cref="EffectsFactory"/> derived class to override effects creation
26+
/// </summary>
27+
public static Func<GraphicsDevice, GraphicsResourceTracker, TextureFactory, EffectsFactory> InstanceBuilder { get; set; }
28+
29+
public static EffectsFactory Create(GraphicsDevice device, GraphicsResourceTracker disposables, TextureFactory textureFactory = null)
30+
{
31+
ArgumentNullException.ThrowIfNull(device);
32+
ArgumentNullException.ThrowIfNull(disposables);
33+
34+
textureFactory ??= TextureFactory.Create(device, disposables);
35+
36+
var ef = InstanceBuilder?.Invoke(device, disposables, textureFactory);
37+
ef ??= new DefaultEffectsFactory(device, disposables, textureFactory);
38+
return ef;
39+
}
40+
41+
protected EffectsFactory(GraphicsDevice device, GraphicsResourceTracker disposables, TextureFactory textureFactory)
42+
{
43+
_Device = device;
44+
_TexFactory = textureFactory;
45+
_Disposables = disposables;
46+
}
47+
48+
#endregion
49+
50+
#region data
51+
52+
private readonly GraphicsDevice _Device;
53+
private readonly TextureFactory _TexFactory;
54+
private readonly GraphicsResourceTracker _Disposables;
55+
56+
// effects cache
57+
private readonly Dictionary<Object, Effect> _RigidEffects = new Dictionary<Object, Effect>();
58+
private readonly Dictionary<Object, SkinnedEffect> _SkinnedEffects = new Dictionary<Object, SkinnedEffect>();
59+
60+
#endregion
61+
62+
#region API - Schema
63+
64+
protected GraphicsDevice Device => _Device;
65+
66+
public Effect UseEffect(GLTFMATERIAL srcMaterial, bool isSkinned)
67+
{
68+
ArgumentNullException.ThrowIfNull(srcMaterial);
69+
70+
var effect = _GetMaterialOrDefault(srcMaterial, isSkinned);
71+
if (effect != null) return effect;
72+
73+
effect = CreateEffect(srcMaterial, isSkinned);
74+
75+
if (isSkinned && effect is SkinnedEffect skEffect) { _SkinnedEffects[srcMaterial] = skEffect; }
76+
else { _RigidEffects[srcMaterial] = effect; }
77+
78+
return effect;
79+
}
80+
81+
private Effect _GetMaterialOrDefault(GLTFMATERIAL srcMaterial, bool isSkinned)
82+
{
83+
if (isSkinned)
84+
{
85+
if (_SkinnedEffects.TryGetValue(srcMaterial, out SkinnedEffect dstMaterial)) return dstMaterial;
86+
}
87+
else
88+
{
89+
if (_RigidEffects.TryGetValue(srcMaterial, out Effect dstMaterial)) return dstMaterial;
90+
}
91+
92+
return null;
93+
}
94+
95+
protected abstract Effect CreateEffect(GLTFMATERIAL srcMaterial, bool isSkinned);
96+
97+
protected Texture2D UseTexture(MaterialChannel? channel, string name)
98+
{
99+
if (!channel.HasValue) return _TexFactory.UseWhiteImage();
100+
101+
if (channel.HasValue && name == null)
102+
{
103+
name = channel.Value.LogicalParent.Name;
104+
if (name == null) name = "null";
105+
name += $"-{channel.Value.Key}";
106+
}
107+
108+
return _TexFactory.UseTexture(channel.Value.Texture?.PrimaryImage?.Content ?? default, name);
109+
}
110+
111+
protected Texture2D UseWhiteTexture()
112+
{
113+
return _TexFactory.UseWhiteImage();
114+
}
115+
116+
#endregion
117+
}
118+
119+
/// <summary>
120+
/// Converts glTF materials into MonoGame effects
121+
/// </summary>
122+
/// <remarks>
123+
/// This will use default effects:<br/>
124+
/// <see cref="BasicEffect"/><br/>
125+
/// <see cref="AlphaTestEffect"/><br/>
126+
/// <see cref="SkinnedEffect"/><br/>
127+
/// Notice that these effects are very basic and limiting, so this factory does a best effort to try advanced glTF materials to fit in.
128+
/// </remarks>
129+
public class DefaultEffectsFactory : EffectsFactory
130+
{
131+
#region lifecycle
132+
public DefaultEffectsFactory(GraphicsDevice device, GraphicsResourceTracker disposables, TextureFactory textureFactory)
133+
: base(device, disposables, textureFactory) { }
134+
135+
#endregion
136+
137+
#region API
138+
139+
// Monogame's BasicEffect uses Phong's shading, while glTF uses PBR shading,
140+
// so given monogame's limitations, we try to guess the most appropiate values
141+
// to have acceptable looking renders.
142+
143+
protected override Effect CreateEffect(GLTFMATERIAL srcMaterial, bool isSkinned)
144+
{
145+
return isSkinned
146+
? CreateSkinnedEffect(srcMaterial)
147+
: CreateRigidEffect(srcMaterial);
148+
}
149+
150+
protected virtual Effect CreateRigidEffect(GLTFMATERIAL srcMaterial)
151+
{
152+
var dstMaterial = srcMaterial.Alpha == Schema2.AlphaMode.MASK
153+
? CreateAlphaTestEffect(srcMaterial)
154+
: CreateBasicEffect(srcMaterial);
155+
156+
return dstMaterial;
157+
}
158+
159+
protected virtual Effect CreateBasicEffect(GLTFMATERIAL srcMaterial)
160+
{
161+
var dstMaterial = new BasicEffect(Device);
162+
163+
dstMaterial.Name = srcMaterial.Name;
164+
165+
dstMaterial.Alpha = _GltfMaterialBasicProperties.GetAlphaLevel(srcMaterial);
166+
dstMaterial.DiffuseColor = _GltfMaterialBasicProperties.GetDiffuseColor(srcMaterial);
167+
dstMaterial.SpecularColor = _GltfMaterialBasicProperties.GetSpecularColor(srcMaterial);
168+
dstMaterial.SpecularPower = _GltfMaterialBasicProperties.GetSpecularPower(srcMaterial);
169+
dstMaterial.EmissiveColor = _GltfMaterialBasicProperties.GeEmissiveColor(srcMaterial);
170+
dstMaterial.Texture = UseDiffuseTexture(srcMaterial);
171+
172+
if (srcMaterial.Unlit)
173+
{
174+
dstMaterial.EmissiveColor = dstMaterial.DiffuseColor;
175+
dstMaterial.SpecularColor = Vector3.Zero;
176+
dstMaterial.SpecularPower = 16;
177+
}
178+
179+
dstMaterial.PreferPerPixelLighting = true;
180+
dstMaterial.TextureEnabled = dstMaterial.Texture != null;
181+
182+
return dstMaterial;
183+
}
184+
185+
protected virtual Effect CreateAlphaTestEffect(GLTFMATERIAL srcMaterial)
186+
{
187+
var dstMaterial = new AlphaTestEffect(Device);
188+
189+
dstMaterial.Name = srcMaterial.Name;
190+
191+
dstMaterial.Alpha = _GltfMaterialBasicProperties.GetAlphaLevel(srcMaterial);
192+
//dstMaterial.AlphaFunction = CompareFunction.GreaterEqual;
193+
dstMaterial.ReferenceAlpha = (int)(srcMaterial.AlphaCutoff * 255);
194+
195+
dstMaterial.DiffuseColor = _GltfMaterialBasicProperties.GetDiffuseColor(srcMaterial);
196+
197+
dstMaterial.Texture = UseDiffuseTexture(srcMaterial);
198+
199+
return dstMaterial;
200+
}
201+
202+
protected virtual Effect CreateSkinnedEffect(GLTFMATERIAL srcMaterial)
203+
{
204+
var dstMaterial = new SkinnedEffect(Device);
205+
206+
dstMaterial.Name = srcMaterial.Name;
207+
208+
dstMaterial.Alpha = _GltfMaterialBasicProperties.GetAlphaLevel(srcMaterial);
209+
dstMaterial.DiffuseColor = _GltfMaterialBasicProperties.GetDiffuseColor(srcMaterial);
210+
dstMaterial.SpecularColor = _GltfMaterialBasicProperties.GetSpecularColor(srcMaterial);
211+
dstMaterial.SpecularPower = _GltfMaterialBasicProperties.GetSpecularPower(srcMaterial);
212+
dstMaterial.EmissiveColor = _GltfMaterialBasicProperties.GeEmissiveColor(srcMaterial);
213+
dstMaterial.Texture = UseDiffuseTexture(srcMaterial);
214+
215+
dstMaterial.WeightsPerVertex = 4;
216+
dstMaterial.PreferPerPixelLighting = true;
217+
218+
// apparently, SkinnedEffect does not support disabling textures, so we set a white texture here.
219+
dstMaterial.Texture ??= UseWhiteTexture(); // creates a dummy white texture.
220+
221+
return dstMaterial;
222+
}
223+
224+
protected virtual Texture2D UseDiffuseTexture(GLTFMATERIAL srcMaterial)
225+
{
226+
var diffuse = srcMaterial.FindChannel("Diffuse")
227+
?? srcMaterial.FindChannel("BaseColor");
228+
229+
return diffuse == null
230+
? null
231+
: UseTexture(diffuse, null);
232+
}
233+
234+
#endregion
235+
}
236+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
using Microsoft.Xna.Framework.Graphics;
5+
6+
namespace SharpGLTF.Runtime.Pipeline
7+
{
8+
/// <summary>
9+
/// tracks all the disposable objects of a model during loading.
10+
/// </summary>
11+
/// <remarks>
12+
/// During the process of loading a model, resources like textures, effects and device buffers<br/>
13+
/// are gathered as a collection of disposables, so when the whole model is disposed, we can also
14+
/// dispose of the device resources.
15+
/// </remarks>
16+
public class GraphicsResourceTracker
17+
{
18+
#region data
19+
20+
private readonly List<GraphicsResource> _Disposables = new List<GraphicsResource>();
21+
22+
#endregion
23+
24+
#region properties
25+
26+
public IReadOnlyList<GraphicsResource> Disposables => _Disposables;
27+
28+
#endregion
29+
30+
#region API
31+
public void AddDisposable(GraphicsResource resource)
32+
{
33+
if (resource == null) throw new ArgumentNullException();
34+
if (_Disposables.Contains(resource)) throw new ArgumentException();
35+
_Disposables.Add(resource);
36+
}
37+
38+
#endregion
39+
}
40+
}

0 commit comments

Comments
 (0)