Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ModelAssetImporter.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 
7 using SiliconStudio.Assets;
8 using SiliconStudio.Core.Diagnostics;
9 using SiliconStudio.Core.Extensions;
10 using SiliconStudio.Core.IO;
11 using SiliconStudio.Core.Mathematics;
12 using SiliconStudio.Core.Serialization;
13 using SiliconStudio.Paradox.Assets.Materials;
14 using SiliconStudio.Paradox.Assets.Materials.Nodes;
15 using SiliconStudio.Paradox.Assets.Materials.Processor.Visitors;
16 using SiliconStudio.Paradox.Assets.Texture;
17 using SiliconStudio.Paradox.Data;
18 using SiliconStudio.Paradox.Effects;
19 using SiliconStudio.Paradox.Effects.Data;
20 using SiliconStudio.Paradox.Engine;
21 using SiliconStudio.Paradox.Engine.Data;
22 using SiliconStudio.Paradox.EntityModel.Data;
23 using SiliconStudio.Paradox.Importer.Common;
24 
25 namespace SiliconStudio.Paradox.Assets.Model
26 {
27  public abstract class ModelAssetImporter : AssetImporterBase
28  {
29  private static readonly Type[] supportedTypes = { typeof(EntityAsset), typeof(ModelAsset), typeof(TextureAsset), typeof(MaterialAsset), typeof(AnimationAsset), typeof(CameraAsset), typeof(LightAsset) };
30 
31  public override AssetImporterParameters GetDefaultParameters(bool isForReImport)
32  {
33  var parameters = new AssetImporterParameters(supportedTypes);
34 
35  // When we are reimporting, we don't want the asset to be reimported by default
36  if (isForReImport)
37  {
38  parameters.SelectedOutputTypes[typeof(EntityAsset)] = false;
39  }
40  return parameters;
41  }
42 
43  /// <summary>
44  /// Get the entity information.
45  /// </summary>
46  /// <param name="localPath">The path of the asset.</param>
47  /// <param name="logger">The logger to use to log import message.</param>
48  /// <returns>The EntityInfo.</returns>
49  public abstract EntityInfo GetEntityInfo(UFile localPath, Logger logger);
50 
51  /// <summary>
52  /// Imports the model.
53  /// </summary>
54  /// <param name="localPath">The path of the asset.</param>
55  /// <param name="importParameters">The parameters used to import the model.</param>
56  /// <returns>A collection of assets.</returns>
57  public override IEnumerable<AssetItem> Import(UFile localPath, AssetImporterParameters importParameters)
58  {
59  var assetReferences = new List<AssetItem>();
60 
61  var entityInfo = GetEntityInfo(localPath, importParameters.Logger);
62 
63  var isImportingEntity = importParameters.IsTypeSelectedForOutput<EntityAsset>();
64 
65  var isImportingModel = importParameters.IsTypeSelectedForOutput<ModelAsset>() ||
66  isImportingEntity;
67 
68  var isImportingMaterial = importParameters.IsTypeSelectedForOutput<MaterialAsset>() ||
69  isImportingModel;
70 
71  var isImportingTexture = importParameters.IsTypeSelectedForOutput<TextureAsset>() ||
72  isImportingMaterial;
73 
74  var isImportingCamera = importParameters.IsTypeSelectedForOutput<CameraAsset>();
75 
76  var isImportingLight = importParameters.IsTypeSelectedForOutput<LightAsset>();
77 
78 
79  // 1. Textures
80  if (isImportingTexture)
81  {
82  ImportTextures(assetReferences, localPath, entityInfo.TextureDependencies);
83  }
84 
85  // 2. Animation
86  if (importParameters.IsTypeSelectedForOutput<AnimationAsset>())
87  {
88  ImportAnimation(assetReferences, localPath, entityInfo.AnimationNodes);
89  }
90 
91  // 3. Materials
92  if (isImportingMaterial)
93  {
94  ImportMaterials(assetReferences, localPath, entityInfo.Materials);
95  }
96 
97  // 4. Model
98  if (isImportingModel)
99  {
100  var modelItem = ImportModel(assetReferences, localPath, localPath, entityInfo);
101 
102  // 4. Entity
103  if (isImportingEntity)
104  {
105  var entityAsset = ImportEntity(assetReferences, localPath, modelItem, entityInfo);
106 
107  // 5. Camera
108  if (isImportingCamera)
109  ImportCameras(assetReferences, localPath, entityInfo, entityAsset, modelItem);
110 
111  // 6. Lights
112  if (isImportingLight)
113  ImportLights(assetReferences, localPath, entityInfo, entityAsset, modelItem);
114  }
115  }
116 
117  return assetReferences;
118  }
119 
120  private static void ImportLights(List<AssetItem> assetReferences, UFile localPath, EntityInfo entityInfo, AssetItem entityAsset, AssetItem modelAsset)
121  {
122  if (entityInfo.Lights == null)
123  return;
124 
125  foreach (var light in entityInfo.Lights)
126  {
127  var lightUrl = new UFile(localPath.GetFileName() + "_light_" + light.NodeName, null);
128 
129  var cameraEntityAsset = CreateTrackingEntity(entityAsset, modelAsset, lightUrl, light.NodeName);
130  ((EntityAsset)cameraEntityAsset.Asset).Data.Components.Add(LightComponent.Key, light.Data);
131 
132  assetReferences.Add(cameraEntityAsset);
133  }
134  }
135 
136  private static void ImportCameras(List<AssetItem> assetReferences, UFile localPath, EntityInfo entityInfo, AssetItem entityAsset, AssetItem modelAsset)
137  {
138  if (entityInfo.Cameras == null)
139  return;
140 
141  foreach (var camera in entityInfo.Cameras)
142  {
143  var cameraUrl = new UFile(localPath.GetFileName() + "_camera_" + camera.NodeName, null);
144 
145  var cameraEntityAsset = CreateTrackingEntity(entityAsset, modelAsset, cameraUrl, camera.NodeName);
146  ((EntityAsset)cameraEntityAsset.Asset).Data.Components.Add(CameraComponent.Key, camera.Data);
147 
148  if (camera.TargetNodeName != null)
149  {
150  // We have a target, create an entity for it
151  var cameraTargetUrl = new UFile(localPath.GetFileName() + "_cameratarget_" + camera.TargetNodeName, null);
152  var cameraTargetEntityAsset = CreateTrackingEntity(entityAsset, modelAsset, cameraTargetUrl, camera.TargetNodeName);
153 
154  // Update target
155  camera.Data.Target = new ContentReference<EntityData>(cameraTargetEntityAsset.Id, cameraTargetEntityAsset.Location);
156 
157  assetReferences.Add(cameraTargetEntityAsset);
158  }
159 
160  assetReferences.Add(cameraEntityAsset);
161  }
162  }
163 
164  private static AssetItem CreateTrackingEntity(AssetItem rootEntityAsset, AssetItem modelAsset, UFile cameraUrl, string nodeName)
165  {
166  var entity = new EntityData { Name = nodeName };
167 
168  // Add TransformationComponent
169  entity.Components.Add(TransformationComponent.Key, new TransformationComponentData());
170 
171 
172  // Add ModelNodeLinkComponent
173  entity.Components.Add(ModelNodeLinkComponent.Key, new ModelNodeLinkComponentData
174  {
175  NodeName = nodeName,
176  Target = EntityComponentReference.New(rootEntityAsset.Id, rootEntityAsset.Location, ModelComponent.Key),
177  });
178 
179  var asset = new EntityAsset { Data = entity };
180  var entityAsset = new AssetItem(cameraUrl, asset);
181 
182  var parentEntity = (EntityAsset)rootEntityAsset.Asset;
183 
184  // Get or create transformation component
185  EntityComponentData entityComponentData;
186  if (!parentEntity.Data.Components.TryGetValue(TransformationComponent.Key, out entityComponentData))
187  {
188  entityComponentData = new TransformationComponentData();
189  parentEntity.Data.Components.Add(TransformationComponent.Key, entityComponentData);
190  }
191 
192  // Mark node as preserved
193  ((ModelAsset)modelAsset.Asset).PreserveNodes(new List<string> { nodeName });
194 
195  // Add as children of model entity
196  ((TransformationComponentData)entityComponentData).Children.Add(
197  EntityComponentReference.New(entityAsset.Id, entityAsset.Location, TransformationComponent.Key));
198 
199 
200  return entityAsset;
201  }
202 
203  private static AssetItem ImportEntity(List<AssetItem> assetReferences, UFile localPath, AssetItem modelItem, EntityInfo entityInfo)
204  {
205  var entityUrl = new UFile(localPath.GetFileName(), null);
206 
207  var asset = new EntityAsset();
208  asset.Data.Name = entityUrl;
209  // Use modelUrl.Path to get the url without the extension
210  asset.Data.Components.Add(ModelComponent.Key, new ModelComponentData { Model = new ContentReference<ModelData>(modelItem.Id, modelItem.Location), Enabled = true });
211 
212  var assetReference = new AssetItem(entityUrl, asset);
213  assetReferences.Add(assetReference);
214 
215  return assetReference;
216  }
217 
218  private static void ImportAnimation(List<AssetItem> assetReferences, UFile localPath, List<string> animationNodes)
219  {
220  if (animationNodes != null && animationNodes.Count > 0)
221  {
222  var assetSource = localPath;
223  var animUrl = new UFile(localPath.GetFileName() + "_anim", null);
224 
225  var asset = new AnimationAsset { Source = assetSource };
226 
227  assetReferences.Add(new AssetItem(animUrl, asset));
228  }
229  }
230 
231  private static AssetItem ImportModel(List<AssetItem> assetReferences, UFile assetSource, UFile localPath, EntityInfo entityInfo)
232  {
233  var frontAxis = Vector3.Cross(entityInfo.UpAxis, Vector3.UnitZ).Length() < MathUtil.ZeroTolerance ? Vector3.UnitY : Vector3.UnitZ;
234  var asset = new ModelAsset { Source = assetSource, UpAxis = entityInfo.UpAxis, FrontAxis = frontAxis };
235 
236  if (entityInfo.Models != null)
237  {
238  var loadedMaterials = assetReferences.Where(x => x.Asset is MaterialAsset).ToList();
239  foreach (var model in entityInfo.Models)
240  {
241  var meshParams = new MeshMaterialParameters { Parameters = model.Parameters, NodeName = model.NodeName };
242 
243  var matId = model.MaterialName;
244  var matName = GenerateFinalMaterialName(localPath, matId);
245  var foundMaterial = loadedMaterials.FirstOrDefault(x => x.Location == new UFile(matName, null));
246  if (foundMaterial != null)
247  {
248  var matReference = new AssetReference<MaterialAsset>(foundMaterial.Id, foundMaterial.Location);
249  meshParams.Material = matReference;
250  }
251  asset.MeshParameters.Add(model.MeshName, meshParams);
252  }
253  }
254 
255  if (entityInfo.Nodes != null)
256  {
257  foreach (var node in entityInfo.Nodes)
258  asset.Nodes.Add(new NodeInformation(node.Name, node.Depth, node.Preserve));
259  }
260 
261  if (entityInfo.AnimationNodes != null && entityInfo.AnimationNodes.Count > 0)
262  asset.PreserveNodes(entityInfo.AnimationNodes);
263 
264  var modelUrl = new UFile(localPath.GetFileName() + "_model", null);
265  var assetItem = new AssetItem(modelUrl, asset);
266  assetReferences.Add(assetItem);
267  return assetItem;
268  }
269 
270  private static void ImportMaterials(List<AssetItem> assetReferences, UFile localPath, Dictionary<string, MaterialDescription> materials)
271  {
272  if (materials != null)
273  {
274  var assetSource = localPath;
275  var loadedTextures = assetReferences.Where(x => x.Asset is TextureAsset).ToList();
276 
277  foreach (var materialKeyValue in materials)
278  {
279  AdjustForTransparency(materialKeyValue.Value);
280  var material = materialKeyValue.Value;
281  var materialUrl = new UFile(GenerateFinalMaterialName(localPath, materialKeyValue.Key), null);
282  var asset = new MaterialAsset { Material = material };
283 
284  // patch texture name and ids
285  var textureVisitor = new MaterialTextureVisitor(material);
286  var textures = textureVisitor.GetAllTextureValues();
287  foreach (var texture in textures)
288  {
289  // texture location is #nameOfTheModel_#nameOfTheTexture at this point in the material
290  var foundTexture = loadedTextures.FirstOrDefault(x => x.Location == GenerateFinalTextureUrl(localPath, texture.TextureReference.Location));
291  if (foundTexture != null)
292  texture.TextureReference = new AssetReference<TextureAsset>(foundTexture.Id, foundTexture.Location);
293  }
294 
295  var assetReference = new AssetItem(materialUrl, asset);
296  assetReferences.Add(assetReference);
297  }
298  }
299  }
300 
301  /// <summary>
302  /// Modify the material to comply with its transparency parameters.
303  /// </summary>
304  /// <param name="material">The material/</param>
305  private static void AdjustForTransparency(MaterialDescription material)
306  {
307  // Note: at this point, there is no other nodes than diffuse, specular, transparent, normal and displacement
308  if (material.ColorNodes.ContainsKey(MaterialParameters.AlbedoDiffuse))
309  {
310  var diffuseNode = material.GetMaterialNode(MaterialParameters.AlbedoDiffuse);
311  if (material.ColorNodes.ContainsKey(MaterialParameters.TransparencyMap))
312  {
313  var diffuseNodeName = material.ColorNodes[MaterialParameters.AlbedoDiffuse];
314  var transparentNodeName = material.ColorNodes[MaterialParameters.TransparencyMap];
315 
316  var transparentNode = material.GetMaterialNode(MaterialParameters.TransparencyMap);
317 
318  if (diffuseNode == null || transparentNode == null)
319  return;
320 
321  var foundTextureDiffuse = FindTextureNode(material, diffuseNodeName);
322  var foundTextureTransparent = FindTextureNode(material, transparentNodeName);
323 
324  if (foundTextureDiffuse != null && foundTextureTransparent != null)
325  {
326  if (foundTextureDiffuse != foundTextureTransparent)
327  {
328  var alphaMixNode = new MaterialBinaryNode(diffuseNode, transparentNode, MaterialBinaryOperand.SubstituteAlpha);
329  material.AddColorNode(MaterialParameters.AlbedoDiffuse, "pdx_diffuseWithAlpha", alphaMixNode);
330  }
331  }
332 
333  // set the key if it was missing
334  material.Parameters.Set(MaterialParameters.UseTransparent, true);
335  }
336  else
337  {
338  // NOTE: MaterialParameters.UseTransparent is mostly runtime
339  var isTransparent = false;
340  if (material.Parameters.ContainsKey(MaterialParameters.UseTransparent))
341  isTransparent = (bool)material.Parameters[MaterialParameters.UseTransparent];
342 
343  if (!isTransparent)
344  {
345  // remove the diffuse node
346  var diffuseName = material.ColorNodes[MaterialParameters.AlbedoDiffuse];
347  material.Nodes.Remove(diffuseName);
348 
349  // add the new one
350  var opaqueNode = new MaterialBinaryNode(diffuseNode, null, MaterialBinaryOperand.Opaque);
351  material.AddColorNode(MaterialParameters.AlbedoDiffuse, "pdx_diffuseOpaque", opaqueNode);
352  }
353  }
354  }
355  }
356 
357  /// <summary>
358  /// Explore the material to find a MaterialTextureNode behind a name.
359  /// </summary>
360  /// <param name="material">The material.</param>
361  /// <param name="startNode">The name of the stating node.</param>
362  /// <returns>The MaterialTextureNode if found.</returns>
363  private static MaterialTextureNode FindTextureNode(MaterialDescription material, string startNode)
364  {
365  var currentNode = material.FindNode(startNode);
366  while (currentNode is MaterialReferenceNode)
367  {
368  var currentReferenceNode = (MaterialReferenceNode)currentNode;
369  currentNode = material.FindNode(currentReferenceNode.Name);
370  }
371 
372  return currentNode as MaterialTextureNode;
373  }
374 
375  private static string GenerateFinalMaterialName(UFile localPath, string materialId)
376  {
377  return localPath.GetFileName() + "_material_" + materialId;
378  }
379 
380  private static UFile GenerateFinalTextureUrl(UFile localPath, string textureName)
381  {
382  return new UFile(localPath.GetFileName() + '_' + textureName, null);
383  }
384 
385  private static void ImportTextures(List<AssetItem> assetReferences, UFile localPath, List<string> dependentTextures)
386  {
387  if (dependentTextures != null)
388  {
389  // Import each texture
390  foreach (var textureFullPath in dependentTextures.Distinct(x => x))
391  {
392  ImportTexture(assetReferences, localPath, textureFullPath);
393  }
394  }
395  }
396 
397  private static void ImportTexture(List<AssetItem> assetReferences, UFile localPath, string textureFullPath)
398  {
399  var texturePath = new UFile(textureFullPath);
400 
401  var source = texturePath;
402  var texture = new TextureAsset { Source = source, PremultiplyAlpha = false };
403 
404  // Creates the url to the texture
405  var textureUrl = GenerateFinalTextureUrl(localPath, texturePath.GetFileName());
406 
407  // Create asset reference
408  assetReferences.Add(new AssetItem(textureUrl, texture));
409  }
410 
411  /// <summary>
412  /// Used only for category purpose, there is no such thing as a Camera asset (it will be a CameraComponent inside an EntityAsset).
413  /// </summary>
414  [AssetDescription("Camera", "A camera")]
415  class CameraAsset : Asset
416  {
417 
418  }
419 
420  /// <summary>
421  /// Used only for category purpose, there is no such thing as a Light asset (it will be a LightComponent inside an EntityAsset).
422  /// </summary>
423  [AssetDescription("Light", "A light")]
424  class LightAsset : Asset
425  {
426 
427  }
428  }
429 }
Data type for SiliconStudio.Paradox.Engine.TransformationComponent.
Definition: EngineData.cs:701
override AssetImporterParameters GetDefaultParameters(bool isForReImport)
Gets the default parameters for this importer.
A node that describe a binary operation between two IMaterialNode
Base class for Asset.
Definition: Asset.cs:14
static readonly ParameterKey< ShaderMixinSource > AlbedoDiffuse
Defines Position, Rotation and Scale of its Entity.
bool IsTypeSelectedForOutput(Type type)
Determines whether the specified type is type selected for output by this importer.
Parameters used by IAssetImporter.Import
Logger Logger
Gets or sets the logger to use during the import.
An asset item part of a Package accessible through SiliconStudio.Assets.Package.Assets.
Definition: AssetItem.cs:17
override IEnumerable< AssetItem > Import(UFile localPath, AssetImporterParameters importParameters)
Imports the model.
Base implementation for ILogger.
Definition: Logger.cs:10
Dictionary< ParameterKey< ShaderMixinSource >, string > ColorNodes
The tree used in this model.
MaterialBinaryOperand
Operands of the MaterialNode.
string GetFileName()
Gets the name of the file without its extension. Can be null.
Definition: UFile.cs:75
Data type for SiliconStudio.Paradox.EntityModel.Entity.
Definition: EngineData.cs:739
Data type for SiliconStudio.Paradox.Engine.ModelComponent.
Definition: EngineData.cs:674
static readonly ParameterKey< bool > UseTransparent
Data type for SiliconStudio.Paradox.EntityModel.EntityComponent.
Definition: EngineData.cs:342
ParameterCollectionData Parameters
The parameters of this model.
Contains user-friendly names and descriptions of an asset type.
static readonly ParameterKey< ShaderMixinSource > TransparencyMap
static PropertyKey< TransformationComponent > Key
Asset Asset
Gets or sets the asset.
Definition: AssetItem.cs:197
Collection of Mesh, each one usually being a different LOD of the same Model. The effect system will ...
Definition: Model.cs:23
HRESULT PremultiplyAlpha(_In_ const Image &srcImage, _In_ DWORD flags, _Out_ ScratchImage &image)
Defines a normalized file path. See UPath for details. This class cannot be inherited.
Definition: UFile.cs:13