Konstantin Koch d9e752b3b1 Fix AssimpImporter and that Rendersystems can be loaded when creating content files, also allows Reference and Null Device for DX
If a model was imported with TargetRealTimeMaximumQuality and then
drawn, it causes a blue screen on my system (Nvidia Geforce GTC 650,
Driver version 353.62)
Also disabled Content recreation if a content project is launched with
the visual studio debugger.
2015-11-22 14:12:43 +01:00

423 lines
16 KiB
C#

#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ANX.Framework.Content.Pipeline.Graphics;
using System.IO;
using ANX.Framework.Graphics;
using ANX.Framework.NonXNA.Development;
using ANX.Framework.Content.Pipeline.Helpers;
using ANX.Framework.Content.Pipeline.Processors;
using System.Collections;
using Assimp;
using System.Diagnostics;
#endregion
// This file is part of the ANX.Framework created by the
// "ANX.Framework developer group" and released under the Ms-PL license.
// For details see: http://anxframework.codeplex.com/license
namespace ANX.Framework.Content.Pipeline
{
[Developer("Konstantin Koch")]
[AssimpImporter(Category = "Model Files", DefaultProcessor = "ModelProcessor", DisplayName = "Assimp Importer - ANX Framework", CacheImportedData = true)]
public class AssimpImporter : ContentImporter<NodeContent>
{
ContentImporterContext context;
ContentIdentity identity;
public AssimpImporter()
{
}
public override NodeContent Import(string filename, ContentImporterContext context)
{
if (!File.Exists(filename))
throw new FileNotFoundException("filename");
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
Debugger.Break();
this.identity = new ContentIdentity(filename, this.GetType().Name);
AssimpDeploy.DeployLibraries();
using (Assimp.AssimpContext assimpContext = new AssimpContext())
{
//Don't use the preset for maximumQuality, on some sytems it causes the graphics driver to break down, which creates a blue screen when we try to draw it :(
//Everything works fine with reference device. The blue screen was experienced on a system with a Nvidia GeForce GTX 650.
Scene scene = assimpContext.ImportFile(filename, PostProcessPreset.ConvertToLeftHanded | PostProcessSteps.Triangulate | PostProcessSteps.ValidateDataStructure | PostProcessSteps.ImproveCacheLocality | PostProcessSteps.FixInFacingNormals);
return ConvertScene(scene);
}
}
private NodeContent ConvertScene(Scene scene)
{
var rootNode = Convert(scene, scene.RootNode);
//var allNodes = WalkNodes(rootNode).ToDictionary((x) => x.Name);
/*foreach (var camera in scene.Cameras)
{
ReplaceNode(rootNode, allNodes, Convert(camera));
}
foreach (var light in scene.Lights)
{
ReplaceNode(rootNode, allNodes, Convert(light));
}*/
//TODO: Support ANimation
/*foreach (var animation in scene.Animations)
{
if (animation.TicksPerSecond == 0)
{
this.context.Logger.LogWarning(null, null, string.Format("Animation \"{0}\" has no ticks per seconds specified. Unable to process animation.", animation.NodeAnimationChannels));
continue;
}
AnimationContent animationContent = new AnimationContent();
animationContent.Duration = TimeSpan.FromSeconds(animation.DurationInTicks / animation.TicksPerSecond);
foreach (var channel in animation.NodeAnimationChannels)
{
AnimationChannel animationChannelContent = new AnimationChannel();
animationChannelContent.Add(new AnimationKeyframe())
animationContent.Channels.Add();
}
}*/
//return rootNode;
return CollapseNodes(rootNode);
}
private NodeContent CollapseNodes(NodeContent content)
{
//Find all the single nodes and eat them.
if (content.Children.Count == 1)
{
var nodes = WalkCollapsableChildren(content, (x) => !(x is MeshContent) && x.Children.Count == 1).ToArray();
if (nodes.Length > 0)
{
foreach (var nodeToEat in nodes.Take(nodes.Length - 1))
{
content.Transform *= nodeToEat.Transform;
content.OpaqueData.AddRange(nodeToEat.OpaqueData, overwrite: true);
}
content.Children.Clear();
var lastNode = nodes.Last();
lastNode.Transform = content.Transform * lastNode.Transform;
lastNode.OpaqueData.AddRange(content.OpaqueData, overwrite: true);
content = lastNode;
}
}
else if (content.Children.Count > 1)
{
List<NodeContent> newChildren = new List<NodeContent>();
foreach (var child in content.Children)
{
var newChild = CollapseNodes(child);
newChild.Parent = null;
newChildren.Add(newChild);
}
content.Children.Clear();
content.Children.AddRange(newChildren);
}
return content;
}
private NodeContent Convert(Camera camera)
{
NodeContent cameraNode = new NodeContent()
{
Name = camera.Name,
Transform = Matrix.CreateLookAt(camera.Position.ToAnx(), (camera.Position + camera.Direction).ToAnx(), camera.Up.ToAnx()),
};
cameraNode.OpaqueData.Add("AspectRatio", camera.AspectRatio);
cameraNode.OpaqueData.Add("ClipPlaneFar", camera.ClipPlaneFar);
cameraNode.OpaqueData.Add("ClipPlaneNear", camera.ClipPlaneNear);
cameraNode.OpaqueData.Add("FieldOfView", camera.FieldOfview);
cameraNode.OpaqueData.Add("Up", camera.Up);
cameraNode.OpaqueData.Add("Position", camera.Position);
cameraNode.OpaqueData.Add("Direction", camera.Direction);
return cameraNode;
}
private NodeContent Convert(Light light)
{
NodeContent node = new NodeContent()
{
Name = light.Name,
Transform = Matrix.CreateFromYawPitchRoll(light.Direction.X, light.Direction.Y, light.Direction.Z) * Matrix.CreateTranslation(light.Position.ToAnx())
};
node.OpaqueData.Add("InnerConeAngle", light.AngleInnerCone);
node.OpaqueData.Add("OuterConeAngle", light.AngleOuterCone);
if (light.LightType != LightSourceType.Directional)
{
node.OpaqueData.Add("ConstantAttentuation", light.AttenuationConstant);
node.OpaqueData.Add("LinearAttentuation", light.AttenuationLinear);
node.OpaqueData.Add("QuadraticAttentuation", light.AttenuationQuadratic);
}
node.OpaqueData.Add("AmbientColor", light.ColorAmbient);
node.OpaqueData.Add("DiffuseColor", light.ColorDiffuse);
node.OpaqueData.Add("SpecularColor", light.ColorSpecular);
node.OpaqueData.Add("Direction", light.Direction);
node.OpaqueData.Add("Position", light.Position);
return node;
}
public GeometryContent ConvertAndAdd(MeshContent meshContent, Mesh mesh, Scene scene)
{
GeometryContent geometry = new GeometryContent();
geometry.Indices.AddRange(mesh.GetIndices());
int positionOffset = meshContent.Positions.Count;
meshContent.Positions.AddRange(mesh.Vertices.Select((x) => x.ToAnx()));
geometry.Vertices.AddRange(Enumerable.Range(positionOffset, mesh.VertexCount));
if (mesh.HasNormals)
geometry.Vertices.Channels.Add(VertexChannelNames.Normal(), mesh.Normals.Select((x) => x.ToAnx()));
/*if (mesh.HasTangentBasis)
{
geometry.Vertices.Channels.Add(VertexChannelNames.Tangent(0), mesh.Tangents.Select((x) => x.ToAnx()));
geometry.Vertices.Channels.Add(VertexChannelNames.Binormal(0), mesh.BiTangents.Select((x) => x.ToAnx()));
}
*/
for (int i = 0; i < mesh.TextureCoordinateChannelCount; i++)
{
var componentCount = mesh.UVComponentCount[i];
geometry.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(i), GetUvVertexType(componentCount), mesh.TextureCoordinateChannels[i].Select((x) => ExtractUvVector(x.ToAnx(), componentCount)));
}
for (int i = 0; i < mesh.VertexColorChannelCount; i++)
{
geometry.Vertices.Channels.Add(VertexChannelNames.Color(i), mesh.VertexColorChannels[i].Select((x) => x.ToAnx().ToVector4()));
}
List<BoneContent> bones = new List<BoneContent>();
if (mesh.HasBones)
{
var boneWeights = new Dictionary<int, BoneWeight>();
foreach (var assimpBone in mesh.Bones)
{
BoneContent bone = new BoneContent();
bone.Name = assimpBone.Name;
bone.Transform = assimpBone.OffsetMatrix.ToAnx();
foreach (var vertexWeight in assimpBone.VertexWeights)
{
boneWeights.Add(vertexWeight.VertexID, new BoneWeight(assimpBone.Name, vertexWeight.Weight));
}
bones.Add(bone);
}
var weightlessVertices = new List<int>();
var weightChannel = new BoneWeightCollection();
for (int i = 0; i < boneWeights.Count; i++)
{
BoneWeight weight;
if (!boneWeights.TryGetValue(i, out weight))
{
weight = new BoneWeight(null, 0);
weightlessVertices.Add(i);
}
weightChannel.Add(weight);
}
if (weightlessVertices.Count > 0)
{
this.context.Logger.LogImportantMessage(string.Format("The following vertices on the mesh \"{0}\" didn't contain bone weights: {1}", mesh.Name, string.Join(",", weightlessVertices)));
}
geometry.Vertices.Channels.Add(VertexChannelNames.Weights(), weightChannel);
}
var material = Convert(scene.Materials[mesh.MaterialIndex]);
if (mesh.VertexColorChannelCount > 0)
material.VertexColorEnabled = true;
geometry.Material = material;
meshContent.Geometry.Add(geometry);
return geometry;
}
private Type GetUvVertexType(int uvComponentCount)
{
switch (uvComponentCount)
{
case 1:
return typeof(float);
case 2:
return typeof(Vector2);
case 3:
return typeof(Vector3);
default:
throw new NotSupportedException(String.Format("An UV component count of {0} is not supported.", uvComponentCount));
}
}
private object ExtractUvVector(Vector3 vector, int componentCount)
{
switch (componentCount)
{
case 1:
return vector.X;
case 2:
return new Vector2(vector.X, vector.Y);
case 3:
return vector;
default:
throw new NotSupportedException(String.Format("An UV component count of {0} is not supported.", componentCount));
}
}
private AssimpMaterialContent Convert(Material assimpMaterial)
{
AssimpMaterialContent material = new AssimpMaterialContent();
material.Name = assimpMaterial.Name;
foreach (var texture in assimpMaterial.GetAllMaterialTextures())
{
if (string.IsNullOrEmpty(texture.FilePath))
continue;
var sourcePath = new Uri(texture.FilePath, UriKind.RelativeOrAbsolute);
if (!sourcePath.IsAbsoluteUri)
sourcePath = new Uri(new Uri(this.identity.SourceFilename, UriKind.Absolute), sourcePath);
material.Textures.Add(Path.GetFileNameWithoutExtension(texture.FilePath) + " " + texture.TextureIndex, new ExternalReference<TextureContent>(sourcePath.LocalPath));
}
if (assimpMaterial.HasBlendMode)
material.IsAdditive = assimpMaterial.BlendMode == BlendMode.Additive;
if (assimpMaterial.HasBumpScaling)
material.BumpScaling = assimpMaterial.BumpScaling;
if (assimpMaterial.HasColorAmbient)
material.AmbientColor = assimpMaterial.ColorAmbient.ToAnx().ToVector3();
if (assimpMaterial.HasColorDiffuse)
material.DiffuseColor = assimpMaterial.ColorDiffuse.ToAnx().ToVector3();
if (assimpMaterial.HasColorEmissive)
material.EmissiveColor = assimpMaterial.ColorEmissive.ToAnx().ToVector3();
if (assimpMaterial.HasColorReflective)
material.ReflectiveColor = assimpMaterial.ColorReflective.ToAnx().ToVector3();
if (assimpMaterial.HasColorSpecular)
{
var specularColor = assimpMaterial.ColorSpecular.ToAnx().ToVector3();
if (assimpMaterial.HasShininessStrength)
specularColor *= assimpMaterial.ShininessStrength;
material.SpecularColor = specularColor;
}
if (assimpMaterial.HasOpacity)
material.Alpha = 1 - assimpMaterial.Opacity;
if (assimpMaterial.HasReflectivity)
material.Reflectivity = assimpMaterial.Reflectivity;
if (assimpMaterial.HasShadingMode)
material.ShadingMode = Enum.GetName(typeof(ShadingMode), assimpMaterial.ShadingMode);
if (assimpMaterial.HasShininess)
material.SpecularPower = assimpMaterial.Shininess;
if (assimpMaterial.HasTwoSided)
material.IsTwoSided = assimpMaterial.IsTwoSided;
if (assimpMaterial.HasWireFrame)
material.IsWireframe = assimpMaterial.IsWireFrameEnabled;
return material;
}
private NodeContent Convert(Scene scene, Node node)
{
NodeContent content;
if (node.HasMeshes)
{
content = new MeshContent();
foreach (var mesh in node.EnumerateMeshes(scene))
{
ConvertAndAdd((MeshContent)content, mesh, scene);
}
}
else
{
content = new NodeContent();
}
content.Name = node.Name;
content.Transform = node.Transform.ToAnx();
content.OpaqueData.AddRange(node.Metadata);
foreach (var child in node.Children)
{
var childContent = Convert(scene, child);
content.Children.Add(childContent);
}
return content;
}
private IEnumerable<NodeContent> WalkCollapsableChildren(NodeContent node, Predicate<NodeContent> predicate)
{
foreach (var child in node.Children)
{
if (!predicate(child))
{
yield return child;
yield break;
}
yield return child;
foreach (var subChild in WalkCollapsableChildren(child, predicate))
{
yield return subChild;
}
}
}
private IEnumerable<NodeContent> FindChildren(NodeContentCollection collection, Predicate<NodeContent> predicate)
{
foreach (var child in collection)
{
if (predicate(child))
yield return child;
else
foreach (var subChild in FindChildren(child.Children, predicate))
{
yield return subChild;
}
}
}
}
}