Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
MaterialTextureLayerFlattener.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.IO;
6 using System.Linq;
7 using SiliconStudio.Core.IO;
8 using SiliconStudio.Core.Mathematics;
9 using SiliconStudio.Core.Serialization.Assets;
10 using SiliconStudio.Paradox.Assets.Materials.Nodes;
11 using SiliconStudio.Paradox.Assets.Materials.Processor.Visitors;
12 using SiliconStudio.Paradox.Effects;
13 using SiliconStudio.Paradox.Graphics;
14 using SiliconStudio.Paradox.Graphics.Data;
15 using SiliconStudio.Paradox.Shaders;
16 using SiliconStudio.Paradox.Shaders.Compiler;
17 
18 namespace SiliconStudio.Paradox.Assets.Materials.Processor.Flattener
19 {
20  public class MaterialTextureLayerFlattener : IDisposable
21  {
22  #region Private members
23 
24  /// <summary>
25  /// Generated texture index.
26  /// </summary>
27  private static int textureIndex = 0;
28 
29  /// <summary>
30  /// The GraphicsDevice used to perform GPU commands
31  /// </summary>
32  private GraphicsDevice graphicsDevice;
33 
34  /// <summary>
35  /// Plane used to draw on screen
36  /// </summary>
37  private GeometricPrimitive plane;
38 
39  /// <summary>
40  /// List of commands to reduce the trees.
41  /// </summary>
42  private readonly List<MaterialReductionInfo> commandList = new List<MaterialReductionInfo>();
43 
44  #endregion
45 
46  #region Public properties
47 
48  /// <summary>
49  /// Current material.
50  /// </summary>
51  public MaterialDescription Material { get; private set; }
52 
53  /// <summary>
54  /// States if there are commands to execute.
55  /// </summary>
56  public bool HasCommands
57  {
58  get
59  {
60  return commandList.Count > 0;
61  }
62  }
63 
64  #endregion
65 
66  #region Public methods
67 
68  /// <summary>
69  /// Constructor.
70  /// </summary>
71  /// <param name="mat">The material to reduce.</param>
73  {
74  graphicsDevice = device;
75  Material = mat.Clone();
76  }
77 
78  /// <summary>
79  /// Release the allocated data.
80  /// </summary>
81  public void Dispose()
82  {
83  if (plane != null)
84  plane.Dispose();
85  }
86 
87  /// <summary>
88  /// Performs the maximal reduction.
89  /// </summary>
90  //public Dictionary<UFile, Image> Run(EffectCompilerBase compiler)
91  public bool Run(EffectCompilerBase compiler)
92  {
93  var result = true;
94 
95  if (commandList.Count > 0)
96  {
97  if (plane == null)
98  plane = GeometricPrimitive.Plane.New(graphicsDevice, 2.0f, 2.0f);
99 
100  var assetManager = new AssetManager();
101  assetManager.Serializer.RegisterSerializer(new GpuTextureSerializer2<Graphics.Texture2D>(graphicsDevice));
102 
103  var textures = new Dictionary<string, Texture2D>();
104  var materialTreeShaderCreator = new MaterialTreeShaderCreator(Material);
105  var textureVisitor = new MaterialTextureVisitor(Material);
106  var compilerParameters = new CompilerParameters { Platform = GraphicsPlatform.Direct3D11, Profile = GraphicsProfile.Level_11_0 };
107 
108  foreach (var command in commandList)
109  {
110  var computeColorShader = materialTreeShaderCreator.GenerateShaderForReduction(command.OldNode);
111  if (computeColorShader == null)
112  continue;
113 
114  var finalShader = new ShaderMixinSource();
115  finalShader.Mixins.Add(new ShaderClassSource("FlattenLayers"));
116  finalShader.Compositions.Add("outColor", computeColorShader);
117  var results = compiler.Compile(finalShader, compilerParameters, null, null);
118 
119  if (results.HasErrors)
120  continue;
121 
122  command.TreeEffect = new Graphics.Effect(graphicsDevice, results.MainBytecode, results.MainUsedParameters);
123  var maxWidth = 0;
124  var maxHeight = 0;
125  var allTextures = textureVisitor.GetAllTextureValues(command.OldNode);
126  foreach (var texSlot in allTextures)
127  {
128  Texture2D tex;
129  if (!textures.TryGetValue(texSlot.TextureName, out tex))
130  {
131  //TODO: change load so that texture can be unloaded.
132  tex = assetManager.Load<Texture2D>(texSlot.TextureName);
133  textures.Add(texSlot.TextureName, tex);
134  }
135 
136  if (tex == null)
137  throw new FileNotFoundException("Texture " + texSlot.TextureName + " not found");
138 
139  command.TreeEffect.Parameters.Set(texSlot.UsedParameterKey, tex);
140  maxWidth = Math.Max(maxWidth, tex.Width);
141  maxHeight = Math.Max(maxHeight, tex.Height);
142  // can take min, a user-defined size, or clamp the min/max
143  // exclude mask?
144  }
145 
146  command.RenderTarget = Texture2D.New(graphicsDevice, maxWidth, maxHeight, PixelFormat.R8G8B8A8_UNorm, TextureFlags.ShaderResource | TextureFlags.RenderTarget).ToRenderTarget();
147  command.ToExecute = true;
148  }
149 
150  // remove wrong commands
151  commandList.RemoveAll(x => !x.ToExecute);
152 
153  var nodeReplacer = new MaterialNodeReplacer(Material);
154 
155  foreach (var command in commandList.Where(x => x.ToExecute))
156  {
157  lock (graphicsDevice)
158  {
159  graphicsDevice.Clear(command.RenderTarget, Color4.Black);
160  graphicsDevice.SetRenderTarget(command.RenderTarget);
161 
162  graphicsDevice.SetRasterizerState(graphicsDevice.RasterizerStates.CullNone);
163  graphicsDevice.SetDepthStencilState(graphicsDevice.DepthStencilStates.None);
164 
165  command.TreeEffect.Apply();
166  plane.Draw();
167 
168  // save texture
169  SaveTexture(command.RenderTarget.Texture, command.TextureUrl, assetManager);
170  }
171  // make new tree
172  var newNode = new MaterialTextureNode(command.TextureUrl.FullPath, command.TexcoordIndex, Vector2.One, Vector2.Zero);
173 
174  nodeReplacer.Replace(command.OldNode, newNode);
175 
176  // save new material?
177  command.ToExecute = false;
178  }
179 
180  foreach (var command in commandList)
181  {
182  command.TreeEffect.Dispose();
183  command.RenderTarget.Dispose();
184  }
185 
186  foreach (var texture in textures)
187  {
188  texture.Value.Dispose();
189  }
190  textures.Clear();
191 
192  foreach (var tex in textures)
193  {
194  assetManager.Unload(tex);
195  }
196 
197  textures.Clear();
198  result = commandList.All(x => !x.ToExecute);
199  commandList.Clear();
200  }
201 
202  return result;
203  }
204 
205  /// <summary>
206  /// Create the structures to perform the reductions.
207  /// </summary>
208  public void PrepareForFlattening(UDirectory baseDir = null)
209  {
210  var reducer = new MaterialTreeReducer(Material);
211  reducer.ReduceTrees();
212  var reducedTrees = reducer.ReducedTrees;
213  var textureVisitor = new MaterialTextureVisitor(Material);
214 
215  foreach (var materialReference in reducedTrees)
216  {
217  Material.Nodes[materialReference.Key] = materialReference.Value;
218  textureVisitor.AssignDefaultTextureKeys(materialReference.Value);
219  }
220 
221  commandList.Clear();
222  textureVisitor.ResetTextureIndices();
223 
224  var basePath = (baseDir == null || baseDir.GetDirectory() == "" ? "" : baseDir.ToString() + "/") + "__reduced_textures__";
225 
226  foreach (var materialReferenceKey in Material.Nodes)
227  {
228  if (materialReferenceKey.Value == null)
229  continue;
230 
231  var materialReferenceName = materialReferenceKey.Key;
232  var materialNode = materialReferenceKey.Value;
233  textureVisitor.AssignDefaultTextureKeys(materialNode);
234  var nodesToReduce = reducer.GetReducibleSubTrees(materialNode);
235 
236  for (var i = 0; i < nodesToReduce.Count; ++i)
237  {
238  var nodeToReduce = nodesToReduce[i];
239  var finalTextureUrl = new UFile(basePath, materialReferenceName + "_Texture" + (nodesToReduce.Count > 1 ? i.ToString() : ""), null);
240  var infos = new MaterialReductionInfo { ToExecute = false, TextureUrl = finalTextureUrl };
241  var canBeReduced = textureVisitor.HasUniqueTexcoord(nodeToReduce, out infos.TexcoordIndex);
242  infos.OldNode = nodeToReduce;
243 
244  if (!canBeReduced)
245  continue; // throw new Exception("Unsolvable tree");
246 
247  commandList.Add(infos);
248  }
249  }
250  }
251 
252  #endregion
253 
254  #region Private methods
255 
256  /// <summary>
257  /// Saves the texture in a png file and in the database.
258  /// </summary>
259  /// <param name="texture">The Texture.</param>
260  /// <param name="filename">The filename.</param>
261  private void SaveTexture(Paradox.Graphics.Texture texture, string filename, AssetManager assetManager)
262  {
263 #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP
264  using (var image = texture.GetDataAsImage())
265  {
266  using (var resultFileStream = File.OpenWrite("tex" + textureIndex + ".png"))
267  {
268  textureIndex++;
269  image.Save(resultFileStream, ImageFileType.Png);
270  }
271 
272  assetManager.Save(filename, image);
273  }
274 #endif
275  }
276 
277  #endregion
278 
279  #region Helper class
280 
281  /// <summary>
282  /// Class storing rendering commands information.
283  /// </summary>
284  private class MaterialReductionInfo
285  {
286  public RenderTarget RenderTarget;
287  public bool ToExecute;
288  public Graphics.Effect TreeEffect;
289  public TextureCoordinate TexcoordIndex;
290  public UFile TextureUrl;
291  public IMaterialNode OldNode;
292  }
293 
294  #endregion
295  }
296 }
static readonly Vector2 Zero
A SiliconStudio.Core.Mathematics.Vector2 with all of its components set to zero.
Definition: Vector2.cs:52
Represents a two dimensional mathematical vector.
Definition: Vector2.cs:42
A mixin performing a combination of ShaderClassSource and other mixins.
Base class for implementations of IEffectCompiler, providing some helper functions.
A geometric primitive. Use Cube, Cylinder, GeoSphere, Plane, Sphere, Teapot, Torus. See Draw+vertices to learn how to use it.
TextureCoordinate
The texture coordinate.
Performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, and creates shaders. See The+GraphicsDevice+class to learn more about the class.
static readonly Vector2 One
A SiliconStudio.Core.Mathematics.Vector2 with all of its components set to one.
Definition: Vector2.cs:67
Defines a normalized directory path. See UPath for details. This class cannot be inherited.
Definition: UDirectory.cs:13
void PrepareForFlattening(UDirectory baseDir=null)
Create the structures to perform the reductions.
A Texture 2D frontend to SharpDX.Direct3D11.Texture2D.
Definition: Texture2D.cs:37
MaterialTextureLayerFlattener(MaterialDescription mat, GraphicsDevice device)
Constructor.
Defines a normalized file path. See UPath for details. This class cannot be inherited.
Definition: UFile.cs:13