Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
MaterialTreeReducer.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 using System.Linq;
6 using SiliconStudio.Core.Mathematics;
7 using SiliconStudio.Paradox.Assets.Materials.Nodes;
8 using SiliconStudio.Paradox.Graphics;
9 
10 namespace SiliconStudio.Paradox.Assets.Materials.Processor.Visitors
11 {
13  {
14  #region Private members
15 
16  /// <summary>
17  /// Reduction status of each tree.
18  /// </summary>
19  private readonly Dictionary<string, ReductionStatus> treeReductionStatus;
20 
21  #endregion
22 
23  #region Publice members
24 
25  /// <summary>
26  /// Reduced form of each tree.
27  /// </summary>
28  public readonly Dictionary<string, IMaterialNode> ReducedTrees;
29 
30  #endregion
31 
32  #region Public methods
33 
34  public MaterialTreeReducer(MaterialDescription mat) : base(mat)
35  {
36  treeReductionStatus = Material.Nodes.ToDictionary(x => x.Key, x => ReductionStatus.None);
37  ReducedTrees = new Dictionary<string, IMaterialNode>();
38  }
39 
40  /// <summary>
41  /// Reduce all the trees.
42  /// </summary>
43  public void ReduceTrees()
44  {
45  foreach (var tree in Material.Nodes)
46  {
47  BeginReduction(tree.Key);
48  }
49  }
50 
51  /// <summary>
52  /// Get the list of all the reducible sub-trees for this tree. If a tree is entirely reducible, only the rootnode will appear
53  /// </summary>
54  /// <returns>The list of all the reducible sub-trees</returns>
55  public List<IMaterialNode> GetReducibleSubTrees(IMaterialNode node)
56  {
57  var resultList = new List<IMaterialNode>();
58  var criterion = new ReductionCriteria { TexcoordIndex = TextureCoordinate.TexcoordNone };
59  if (BuildReducibleSubTreesList(node, resultList, out criterion))
60  AddNodeToReduceList(node, resultList);
61  return resultList;
62  }
63 
64  /// <summary>
65  /// Checks if the node can be reduced and adds to the list the ones that cannot be reduced further.
66  /// </summary>
67  /// <param name="node">The node to visit.</param>
68  /// <param name="resultList">The list of node to reduce.</param>
69  /// <param name="criterion">The current criteria to reduce the node.</param>
70  /// <returns>True if the node can be reduced, false otherwise.</returns>
71  private bool BuildReducibleSubTreesList(IMaterialNode node, List<IMaterialNode> resultList, out ReductionCriteria criterion)
72  {
73  if (node is IMaterialValueNode)
74  {
75  criterion.TexcoordIndex = node is MaterialTextureNode ? ((MaterialTextureNode)node).TexcoordIndex : TextureCoordinate.TexcoordNone;
76  criterion.AddressModeU = node is MaterialTextureNode ? ((MaterialTextureNode)node).Sampler.AddressModeU : (TextureAddressMode?)null;
77  criterion.AddressModeV = node is MaterialTextureNode ? ((MaterialTextureNode)node).Sampler.AddressModeV : (TextureAddressMode?)null;
78 
79  var isSingleReducible = node.IsReducible;
80  if (node is MaterialFloatNode)
81  isSingleReducible &= (((MaterialFloatNode)node).Key == null);
82  if (node is MaterialFloat4Node)
83  isSingleReducible &= (((MaterialFloat4Node)node).Key == null);
84  if (node is MaterialColorNode)
85  isSingleReducible &= (((MaterialColorNode)node).Key == null);
86 
87  return isSingleReducible;
88  }
89 
90  var refNode = node as MaterialReferenceNode;
91  if (refNode != null)
92  {
93  return BuildReducibleSubTreesList(Material.FindNode(refNode.Name), resultList, out criterion);
94  }
95 
96  criterion.TexcoordIndex = TextureCoordinate.TexcoordNone;
97  criterion.AddressModeU = (TextureAddressMode?)null;
98  criterion.AddressModeV = (TextureAddressMode?)null;
99 
100  bool? checkIsReducible = null;
101  ReductionCriteria? checkCriterion = null;
102  var reducibleNodes = new List<IMaterialNode>();
103 
104  foreach (var subNodeIt in node.GetChildren(Material))
105  {
106  var subNode = subNodeIt.Node;
107  ReductionCriteria childCriterion;
108  var childIsReducible = BuildReducibleSubTreesList(subNode, resultList, out childCriterion);
109  if (!checkIsReducible.HasValue)
110  {
111  checkIsReducible = childIsReducible;
112  checkCriterion = childCriterion;
113  }
114  else
115  {
116  checkIsReducible = checkIsReducible.Value && childIsReducible && CompatibleCriteria(childCriterion, checkCriterion.Value);
117  checkCriterion = MergeCriteria(checkCriterion.Value, childCriterion);
118  }
119 
120  if (childIsReducible)
121  reducibleNodes.Add(subNode);
122  }
123 
124  if (checkCriterion.HasValue)
125  {
126  criterion = checkCriterion.Value;
127  }
128 
129  if (checkIsReducible.HasValue && !checkIsReducible.Value)
130  {
131  // if one child is not reducible, add all the reducible ones to the list
132  foreach (var reducibleNode in reducibleNodes)
133  AddNodeToReduceList(reducibleNode, resultList);
134 
135  return false;
136  }
137 
138  return node.IsReducible;
139  }
140 
141  #endregion
142 
143  #region Private methods
144 
145  /// <summary>
146  /// Starts the reduction of this reference.
147  /// </summary>
148  /// <param name="referenceName">The name of the reference.</param>
149  private void BeginReduction(string referenceName)
150  {
151  if (referenceName == null)
152  return;
153 
154  if (!treeReductionStatus.ContainsKey(referenceName))
155  return;
156 
157  var status = treeReductionStatus[referenceName];
158 
159  if (status == ReductionStatus.None)
160  {
161  var node = Material.Nodes[referenceName];
162  treeReductionStatus[referenceName] = ReductionStatus.InProgress;
163  var computedNode = Reduce(node);
164  if (computedNode != null)
165  ReducedTrees[referenceName] = computedNode;
166  treeReductionStatus[referenceName] = ReductionStatus.Completed;
167  }
168  else if (status == ReductionStatus.InProgress)
169  {
170  treeReductionStatus[referenceName] = ReductionStatus.Completed;
171  // TODO: cycle - log error
172  }
173  }
174 
175  /// <summary>
176  /// Reduce the binaryNode to its most compact form.
177  /// </summary>
178  /// <param name="node">The IMaterialNode binaryNode to reduce.</param>
179  /// <returns>The reduced binaryNode.</returns>
180  private IMaterialNode Reduce(IMaterialNode node)
181  {
182  if (node is MaterialBinaryNode)
183  return Reduce(node as MaterialBinaryNode);
184  if (node is MaterialReferenceNode)
185  {
186  var referenceName = (node as MaterialReferenceNode).Name;
187  if (referenceName != null)
188  {
189  BeginReduction(referenceName);
190 
191  if (ReducedTrees.ContainsKey(referenceName))
192  return ReducedTrees[referenceName];
193 
194  return null;
195  }
196  }
197  return node;
198  }
199 
200  /// <summary>
201  /// Reduce the binaryNode to its most compact form.
202  /// </summary>
203  /// <param name="binaryNode">The MaterialBinaryNode binaryNode to reduce.</param>
204  /// <returns>The reduced binaryNode.</returns>
205  private IMaterialNode Reduce(MaterialBinaryNode binaryNode)
206  {
207  var left = Reduce(binaryNode.LeftChild);
208  var right = Reduce(binaryNode.RightChild);
209 
210  bool canReduce = true;
211  //if (binaryNode.LeftChild is IMaterialValueNode)
212  // canReduce &= !(binaryNode.LeftChild as IMaterialValueNode).IsParameter;
213  //if (binaryNode.RightChild is IMaterialValueNode)
214  // canReduce &= !(binaryNode.RightChild as IMaterialValueNode).IsParameter;
215 
216  if (canReduce)
217  {
218  if (left is MaterialFloatNode)
219  {
220  if (right is MaterialFloatNode)
221  {
222  (left as MaterialFloatNode).Value = MaterialReductionUtils.MixFloat((left as MaterialFloatNode).Value, (right as MaterialFloatNode).Value, binaryNode.Operand);
223  return left;
224  }
225  if (right is MaterialFloat4Node)
226  {
227  var value = (left as MaterialFloatNode).Value;
228  (right as MaterialFloat4Node).Value = MaterialReductionUtils.MixFloat4(new Vector4(value, value, value, value), (right as MaterialFloat4Node).Value, binaryNode.Operand);
229  return right;
230  }
231  if (right is MaterialColorNode)
232  {
233  var value = (left as MaterialFloatNode).Value;
234  (right as MaterialColorNode).Value = MaterialReductionUtils.MixColor(new Color4(value, value, value, value), (right as MaterialColorNode).Value, binaryNode.Operand);
235  return right;
236  }
237  }
238  else if (left is MaterialFloat4Node)
239  {
240  if (right is MaterialFloatNode)
241  {
242  var value = (right as MaterialFloatNode).Value;
243  (left as MaterialFloat4Node).Value = MaterialReductionUtils.MixFloat4((left as MaterialFloat4Node).Value, new Vector4(value, value, value, value), binaryNode.Operand);
244  return left;
245  }
246  if (right is MaterialFloat4Node)
247  {
248  (left as MaterialFloat4Node).Value = MaterialReductionUtils.MixFloat4((left as MaterialFloat4Node).Value, (right as MaterialFloat4Node).Value, binaryNode.Operand);
249  return left;
250  }
251  if (right is MaterialColorNode)
252  {
253  var value = (left as MaterialFloat4Node).Value;
254  (left as MaterialFloat4Node).Value = MaterialReductionUtils.MixFloat4(value, (right as MaterialColorNode).Value.ToVector4(), binaryNode.Operand);
255  return left;
256  }
257  }
258  else if (left is MaterialColorNode)
259  {
260  if (right is MaterialFloatNode)
261  {
262  var value = (right as MaterialFloatNode).Value;
263  (left as MaterialColorNode).Value = MaterialReductionUtils.MixColor((left as MaterialColorNode).Value, new Color4(value, value, value, value), binaryNode.Operand);
264  return left;
265  }
266  if (right is MaterialFloat4Node)
267  {
268  (right as MaterialFloat4Node).Value = MaterialReductionUtils.MixFloat4((left as MaterialColorNode).Value.ToVector4(), (right as MaterialFloat4Node).Value, binaryNode.Operand);
269  return right;
270  }
271  if (right is MaterialColorNode)
272  {
273  var value = (left as MaterialColorNode).Value;
274  (left as MaterialColorNode).Value = MaterialReductionUtils.MixColor((left as MaterialColorNode).Value, (right as MaterialColorNode).Value, binaryNode.Operand);
275  return left;
276  }
277  }
278  }
279 
280  binaryNode.LeftChild = left;
281  binaryNode.RightChild = right;
282  return binaryNode;
283  }
284 
285  #endregion
286 
287  #region Private static methods
288 
289  /// <summary>
290  /// Add a node to the list of of nodes to reduce.
291  /// </summary>
292  /// <param name="node">The node to add.</param>
293  /// <param name="reduceList">The list of node to reduce.</param>
294  private static void AddNodeToReduceList(IMaterialNode node, List<IMaterialNode> reduceList)
295  {
296  if (!(node is IMaterialValueNode || node is MaterialTextureNode || node is MaterialShaderClassNode))
297  reduceList.Add(node);
298  }
299 
300  /// <summary>
301  /// Check if the two criteria are compatible.
302  /// </summary>
303  /// <param name="baseCriteria">The base criteria.</param>
304  /// <param name="newCriteria">The new criteria.</param>
305  /// <returns>True if they are compatible.</returns>
306  private static bool CompatibleCriteria(ReductionCriteria baseCriteria, ReductionCriteria newCriteria)
307  {
308  var result = baseCriteria.TexcoordIndex == TextureCoordinate.TexcoordNone || newCriteria.TexcoordIndex == TextureCoordinate.TexcoordNone || baseCriteria.TexcoordIndex == newCriteria.TexcoordIndex;
309  result = result && (!baseCriteria.AddressModeU.HasValue || !newCriteria.AddressModeU.HasValue || baseCriteria.AddressModeU.Value == newCriteria.AddressModeU.Value);
310  result = result && (!baseCriteria.AddressModeV.HasValue || !newCriteria.AddressModeV.HasValue || baseCriteria.AddressModeV.Value == newCriteria.AddressModeV.Value);
311  return result;
312  }
313 
314  /// <summary>
315  /// Merge the info of the two compatible criteria.
316  /// </summary>
317  /// <param name="baseCriteria">The base criteria.</param>
318  /// <param name="newCriteria">The new criteria.</param>
319  /// <returns>The merged criteria.</returns>
320  private static ReductionCriteria MergeCriteria(ReductionCriteria baseCriteria, ReductionCriteria newCriteria)
321  {
322  var result = new ReductionCriteria
323  {
324  TexcoordIndex = baseCriteria.TexcoordIndex == TextureCoordinate.TexcoordNone ? newCriteria.TexcoordIndex : baseCriteria.TexcoordIndex,
325  AddressModeU = baseCriteria.AddressModeU.HasValue ? baseCriteria.AddressModeU : (newCriteria.AddressModeU.HasValue ? newCriteria.AddressModeU.Value : (TextureAddressMode?)null),
326  AddressModeV = baseCriteria.AddressModeV.HasValue ? baseCriteria.AddressModeV : (newCriteria.AddressModeV.HasValue ? newCriteria.AddressModeV.Value : (TextureAddressMode?)null)
327  };
328  return result;
329  }
330 
331  #endregion
332 
333  private enum ReductionStatus
334  {
335  None,
336  InProgress,
337  Completed
338  }
339 
340  private struct ReductionCriteria
341  {
342  public TextureCoordinate TexcoordIndex;
343  public TextureAddressMode? AddressModeU;
344  public TextureAddressMode? AddressModeV;
345  }
346  }
347 }
A node that describe a binary operation between two IMaterialNode
IMaterialNode LeftChild
The left (background) child node.
TextureAddressMode
Identify a technique for resolving texture coordinates that are outside of the boundaries of a textur...
TextureCoordinate
The texture coordinate.
Represents a color in the form of rgba.
Definition: Color4.cs:42
Represents a four dimensional mathematical vector.
Definition: Vector4.cs:42
List< IMaterialNode > GetReducibleSubTrees(IMaterialNode node)
Get the list of all the reducible sub-trees for this tree. If a tree is entirely reducible, only the rootnode will appear
readonly Dictionary< string, IMaterialNode > ReducedTrees
Reduced form of each tree.
Base interface for all nodes in the material tree
IMaterialNode RightChild
The right (foreground) child node.
MaterialBinaryOperand Operand
The operation to blend the nodes.