Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ModelRenderer.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 
6 using SiliconStudio.Core.Collections;
7 using SiliconStudio.Core.Extensions;
8 
10 
11 namespace SiliconStudio.Paradox.Effects
12 {
13  /// <summary>
14  /// This <see cref="Renderer"/> is responsible to prepare and render meshes for a specific pass.
15  /// </summary>
16  public class ModelRenderer : Renderer
17  {
18  private int meshPassSlot;
19 
20  private readonly FastList<RenderMesh> meshesToRender;
21 
22  private readonly DynamicEffectCompiler dynamicEffectCompiler;
23  private readonly string effectName;
24 
25  private readonly SafeDelegateList<AcceptModelDelegate> acceptModels;
26 
27  private readonly SafeDelegateList<AcceptRenderModelDelegate> acceptRenderModels;
28 
29  private readonly SafeDelegateList<AcceptMeshForRenderingDelegate> acceptPrepareMeshForRenderings;
30 
31  private readonly SafeDelegateList<AcceptRenderMeshDelegate> acceptRenderMeshes;
32 
33  private readonly SafeDelegateList<UpdateMeshesDelegate> updateMeshes;
34 
35  private readonly SafeDelegateList<PreRenderDelegate> preRenders;
36 
37  private readonly SafeDelegateList<PostRenderDelegate> postRenders;
38 
39  private readonly SafeDelegateList<PreEffectUpdateDelegate> preEffectUpdates;
40 
41  private readonly SafeDelegateList<PostEffectUpdateDelegate> postEffectUpdates;
42 
43  /// <summary>
44  /// An accept model callback to test whether a model will be handled by this instance.
45  /// </summary>
46  /// <param name="modelInstance">The model instance</param>
47  /// <returns><c>true</c> if the model instance is going to be handled by this renderer, <c>false</c> otherwise.</returns>
48  public delegate bool AcceptModelDelegate(IModelInstance modelInstance);
49 
50  public delegate bool AcceptMeshForRenderingDelegate(RenderModel renderModel, Mesh mesh);
51 
52  public delegate bool AcceptRenderMeshDelegate(RenderContext context, RenderMesh renderMesh);
53 
54  public delegate bool AcceptRenderModelDelegate(RenderModel renderModel);
55 
56  public delegate void UpdateMeshesDelegate(RenderContext context, FastList<RenderMesh> meshes);
57 
58  public delegate void PreRenderDelegate(RenderContext context);
59 
60  public delegate void PostRenderDelegate(RenderContext context);
61 
62  public delegate void PreEffectUpdateDelegate(RenderContext context, RenderMesh renderMesh);
63 
64  public delegate void PostEffectUpdateDelegate(RenderContext context, RenderMesh renderMesh);
65 
66  /// <summary>
67  /// Initializes a new instance of the <see cref="ModelRenderer"/> class.
68  /// </summary>
69  /// <param name="services">The services.</param>
70  /// <param name="effectName">Name of the effect.</param>
71  public ModelRenderer(IServiceRegistry services, string effectName) : base(services)
72  {
73  if (effectName == null) throw new ArgumentNullException("effectName");
74  this.effectName = effectName;
75  DebugName = string.Format("ModelRenderer [{0}]", effectName);
76 
77  dynamicEffectCompiler = new DynamicEffectCompiler(services, effectName);
78 
79  meshesToRender = new FastList<RenderMesh>();
80 
81  acceptModels = new SafeDelegateList<AcceptModelDelegate>(this);
82  acceptRenderModels = new SafeDelegateList<AcceptRenderModelDelegate>(this);
83  acceptPrepareMeshForRenderings = new SafeDelegateList<AcceptMeshForRenderingDelegate>(this);
84  acceptRenderMeshes = new SafeDelegateList<AcceptRenderMeshDelegate>(this);
85  updateMeshes = new SafeDelegateList<UpdateMeshesDelegate>(this) { UpdateMeshesDefault };
86  SortMeshes = DefaultSort;
87  preRenders = new SafeDelegateList<PreRenderDelegate>(this);
88  postRenders = new SafeDelegateList<PostRenderDelegate>(this);
89  preEffectUpdates = new SafeDelegateList<PreEffectUpdateDelegate>(this);
90  postEffectUpdates = new SafeDelegateList<PostEffectUpdateDelegate>(this);
91  }
92 
93  public string EffectName
94  {
95  get
96  {
97  return effectName;
98  }
99  }
100 
101  public SafeDelegateList<AcceptModelDelegate> AcceptModel
102  {
103  get
104  {
105  return acceptModels;
106  }
107  }
108 
109  public SafeDelegateList<AcceptRenderModelDelegate> AcceptRenderModel
110  {
111  get
112  {
113  return acceptRenderModels;
114  }
115  }
116 
117  public SafeDelegateList<AcceptMeshForRenderingDelegate> AcceptPrepareMeshForRendering
118  {
119  get
120  {
121  return acceptPrepareMeshForRenderings;
122  }
123  }
124 
125  public SafeDelegateList<AcceptRenderMeshDelegate> AcceptRenderMesh
126  {
127  get
128  {
129  return acceptRenderMeshes;
130  }
131  }
132 
133  public SafeDelegateList<UpdateMeshesDelegate> UpdateMeshes
134  {
135  get
136  {
137  return updateMeshes;
138  }
139  }
140 
141  public UpdateMeshesDelegate SortMeshes { get; set; }
142 
143  public SafeDelegateList<PreRenderDelegate> PreRender
144  {
145  get
146  {
147  return preRenders;
148  }
149  }
150 
151  public SafeDelegateList<PostRenderDelegate> PostRender
152  {
153  get
154  {
155  return postRenders;
156  }
157  }
158 
159  public SafeDelegateList<PreEffectUpdateDelegate> PreEffectUpdate
160  {
161  get
162  {
163  return preEffectUpdates;
164  }
165  }
166 
167  public SafeDelegateList<PostEffectUpdateDelegate> PostEffectUpdate
168  {
169  get
170  {
171  return postEffectUpdates;
172  }
173  }
174 
175  public override void Load()
176  {
177  base.Load();
178 
179  var pipelineModelState = Pass.GetOrCreateModelRendererState();
180 
181  // Get the slot for the pass of this processor
182  meshPassSlot = pipelineModelState.GetModelSlot(Pass);
183 
184  // Register callbacks used by the MeshProcessor
185  pipelineModelState.AcceptModel += OnAcceptModel;
186  pipelineModelState.PrepareRenderModel += PrepareModelForRendering;
187  pipelineModelState.AcceptRenderModel += OnAcceptRenderModel;
188  }
189 
190  public override void Unload()
191  {
192  base.Unload();
193 
194  var pipelineModelState = Pass.GetOrCreateModelRendererState();
195 
196  // Unregister callbacks
197  pipelineModelState.AcceptModel -= OnAcceptModel;
198  pipelineModelState.PrepareRenderModel -= PrepareModelForRendering;
199  pipelineModelState.AcceptRenderModel -= OnAcceptRenderModel;
200  }
201 
202  protected override void OnRendering(RenderContext context)
203  {
204  var state = Pass.GetModelRendererState();
205 
206  // Get all meshes from render models
207  meshesToRender.Clear();
208  foreach (var renderModel in state.RenderModels)
209  {
210  var meshes = renderModel.RenderMeshes[meshPassSlot];
211  if (meshes != null)
212  meshesToRender.AddRange(meshes);
213  }
214 
215  // Update meshes
216  foreach (var updateMeshesToRender in updateMeshes)
217  {
218  updateMeshesToRender(context, meshesToRender);
219  }
220 
221  // Sort meshes
222  if (SortMeshes != null)
223  {
224  SortMeshes(context, meshesToRender);
225  }
226 
227  // PreRender callbacks
228  foreach (var preRender in preRenders)
229  {
230  preRender(context);
231  }
232 
233  // TODO: separate update effect and render to tightly batch render calls vs 1 cache-friendly loop on meshToRender
234  foreach (var mesh in meshesToRender)
235  {
236  // PreEffectUpdate callbacks
237  foreach (var preEffectUpdate in preEffectUpdates)
238  {
239  preEffectUpdate(context, mesh);
240  }
241 
242  // Update Effect and mesh
243  UpdateEffect(mesh);
244 
245  // PostEffectUpdate callbacks
246  foreach (var postEffectUpdate in postEffectUpdates)
247  {
248  postEffectUpdate(context, mesh);
249  }
250 
251  mesh.Draw(context);
252  }
253 
254  // PostRender callbacks
255  foreach (var postRender in postRenders)
256  {
257  postRender(context);
258  }
259  }
260 
261  private void PrepareModelForRendering(RenderModel renderModel)
262  {
263  foreach (var mesh in renderModel.Model.Meshes)
264  {
265  if (acceptPrepareMeshForRenderings.Count > 0 && !OnAcceptPrepareMeshForRendering(renderModel, mesh))
266  {
267  continue;
268  }
269 
270  var effectMesh = new RenderMesh(renderModel, mesh);
271  UpdateEffect(effectMesh);
272 
273  // Register mesh for rendering
274  if (renderModel.RenderMeshes[meshPassSlot] == null)
275  {
276  renderModel.RenderMeshes[meshPassSlot] = new List<RenderMesh>();
277  }
278  renderModel.RenderMeshes[meshPassSlot].Add(effectMesh);
279  }
280  }
281 
282  private void UpdateMeshesDefault(RenderContext context, FastList<RenderMesh> meshes)
283  {
284  for (var i = 0; i < meshes.Count; ++i)
285  {
286  var mesh = meshes[i];
287 
288  if (!mesh.Enabled || (acceptRenderMeshes.Count > 0 && !OnAcceptRenderMesh(context, mesh)))
289  {
290  meshes.SwapRemoveAt(i--);
291  }
292  }
293  }
294 
295  private void DefaultSort(RenderContext context, FastList<RenderMesh> meshes)
296  {
297  // Sort based on ModelComponent.DrawOrder
298  meshes.Sort(ModelComponentSorter.Default);
299  }
300 
301  private bool OnAcceptRenderModel(RenderModel renderModel)
302  {
303  // NOTICE: We don't use Linq, as It would allocated objects and triggers GC
304  foreach (var acceptRenderModel in AcceptRenderModel)
305  {
306  if (!acceptRenderModel(renderModel))
307  {
308  return false;
309  }
310  }
311  return true;
312  }
313 
314  private bool OnAcceptModel(IModelInstance modelInstance)
315  {
316  // NOTICE: We don't use Linq, as It would allocated objects and triggers GC
317  foreach (var test in acceptModels)
318  {
319  if (!test(modelInstance))
320  {
321  return false;
322  }
323  }
324  return true;
325  }
326 
327  private bool OnAcceptPrepareMeshForRendering(RenderModel renderModel, Mesh mesh)
328  {
329  // NOTICE: Don't use Linq, as It would allocated objects and triggers GC
330  foreach (var test in acceptPrepareMeshForRenderings)
331  {
332  if (!test(renderModel, mesh))
333  {
334  return false;
335  }
336  }
337  return true;
338  }
339 
340  private bool OnAcceptRenderMesh(RenderContext context, RenderMesh renderMesh)
341  {
342  // NOTICE: Don't use Linq, as It would allocated objects and triggers GC
343  foreach (var test in acceptRenderMeshes)
344  {
345  if (!test(context, renderMesh))
346  {
347  return false;
348  }
349  }
350  return true;
351  }
352 
353  /// <summary>
354  /// Create or update the Effect of the effect mesh.
355  /// </summary>
356  protected void UpdateEffect(RenderMesh renderMesh)
357  {
358  if (dynamicEffectCompiler.Update(renderMesh))
359  {
360  renderMesh.Initialize(GraphicsDevice);
361  }
362  }
363 
364  /// <summary>
365  /// A list to ensure that all delegates are not null.
366  /// </summary>
367  /// <typeparam name="T">A delegate</typeparam>
368  public class SafeDelegateList<T> : ConstrainedList<T> where T : class
369  {
370  private const string ExceptionError = "The delegate added to the list cannot be null";
371  private readonly ModelRenderer renderer;
372 
373  internal SafeDelegateList(ModelRenderer renderer)
374  : base(Constraint, true, ExceptionError)
375  {
376  this.renderer = renderer;
377  }
378 
379  public new ModelRenderer Add(T item)
380  {
381  base.Add(item);
382  return renderer;
383  }
384 
385  public new ModelRenderer Insert(int index, T item)
386  {
387  base.Insert(index, item);
388  return renderer;
389  }
390 
391  private static bool Constraint(ConstrainedList<T> constrainedList, T arg2)
392  {
393  return arg2 != null;
394  }
395  }
396 
397  #region Helper class
398 
399  private class ModelComponentSorter : IComparer<RenderMesh>
400  {
401  #region Constants and Fields
402 
403  public static readonly ModelComponentSorter Default = new ModelComponentSorter();
404 
405  #endregion
406 
407  public int Compare(RenderMesh left, RenderMesh right)
408  {
409  var xModelComponent = left.RenderModel.ModelInstance;
410  var yModelComponent = right.RenderModel.ModelInstance;
411 
412  // Ignore if no associated mesh component
413  if (xModelComponent == null || yModelComponent == null)
414  return 0;
415 
416  // TODO: Add a kind of associated data to an effect mesh to speed up this test?
417  var leftMaterial = left.Mesh.Material;
418  var isLeftTransparent = (leftMaterial != null && leftMaterial.Parameters.Get(MaterialParameters.UseTransparent));
419 
420  var rightMaterial = right.Mesh.Material;
421  var isRightTransparent = (rightMaterial != null && rightMaterial.Parameters.Get(MaterialParameters.UseTransparent));
422 
423  if (isLeftTransparent && !isRightTransparent)
424  return 1;
425 
426  if (!isLeftTransparent && isRightTransparent)
427  return -1;
428 
429  // Use draw order
430  return Math.Sign(xModelComponent.DrawOrder - yModelComponent.DrawOrder);
431  }
432  }
433 
434  #endregion
435  }
436 }
SiliconStudio.Core.IServiceRegistry IServiceRegistry
Definition: ModelRenderer.cs:9
Performs render pipeline transformations attached to a specific RenderPass.
Definition: Renderer.cs:13
ModelRenderer(IServiceRegistry services, string effectName)
Initializes a new instance of the ModelRenderer class.
void UpdateEffect(RenderMesh renderMesh)
Create or update the Effect of the effect mesh.
Provides a dynamic compiler for an effect based on parameters changed.
Thread-local storage context used during rendering.
override void Unload()
Unloads this instance. This method is called when a RenderPass is de-attached (directly or indirectly...
delegate void UpdateMeshesDelegate(RenderPass currentRenderPass, ref FastList< RenderMesh > meshes)
Represent a collection associated with a constraint. When an item is added to this collection...
Instance of a model with its parameters.
Instantiation of a Model through a RenderPipeline.
Definition: RenderModel.cs:11
override void Load()
Loads this instance. This method is called when a RenderPass is attached (directly or indirectly) to ...
List< Mesh > Meshes
Gets the meshes.
Definition: Model.cs:50
override void OnRendering(RenderContext context)
This Renderer is responsible to prepare and render meshes for a specific pass.
readonly List< RenderMesh >[] RenderMeshes
Gets the meshes instantiated for this view.
Definition: RenderModel.cs:59
readonly Model Model
Gets the underlying model.
Definition: RenderModel.cs:72