/// <summary> /// Initializes a new instance of the <see cref="Model"/> class. /// </summary> /// <param name="availableAnimations"> /// The available animations. /// </param> /// <param name="rootBone"> /// The root bone, or null if there's no skeletal information. /// </param> /// <param name="vertexes"> /// The vertexes associated with this model. /// </param> /// <param name="indices"> /// The indices associated with the model. /// </param> public Model( IModelRenderConfiguration[] modelRenderConfigurations, IRenderBatcher renderBatcher, string name, IAnimationCollection availableAnimations, IMaterial material, IModelBone rootBone, ModelVertex[] vertexes, int[] indices) { Name = name; AvailableAnimations = availableAnimations; Root = rootBone; Vertexes = vertexes; Indices = indices; Material = material; _cachedVertexBuffers = new Dictionary<object, VertexBuffer>(); _modelRenderConfigurations = modelRenderConfigurations; _renderBatcher = renderBatcher; if (Root != null) { _flattenedBones = Root.Flatten(); Bones = _flattenedBones.ToDictionary(k => k.Name, v => v); } }
/// <summary> /// Serializes an array of model vertexes to a binary stream. /// </summary> /// <param name="writer"> /// The binary writer to which the model vertexes will be serialized. /// </param> /// <param name="vertexes"> /// The vertexes to serialize. /// </param> private void SerializeVertexes(BinaryWriter writer, ModelVertex[] vertexes) { writer.Write(vertexes.Length); for (var i = 0; i < vertexes.Length; i++) { var v = vertexes[i]; if (v.Position != null) { writer.Write(true); this.SerializeVector3(writer, v.Position.Value); } else { writer.Write(false); } if (v.Normal != null) { writer.Write(true); this.SerializeVector3(writer, v.Normal.Value); } else { writer.Write(false); } if (v.Tangent != null && v.BiTangent != null) { writer.Write(true); this.SerializeVector3(writer, v.Tangent.Value); this.SerializeVector3(writer, v.BiTangent.Value); } else { writer.Write(false); } writer.Write(v.Colors.Length); for (var c = 0; c < v.Colors.Length; c++) { this.SerializeColor(writer, v.Colors[c]); } writer.Write(v.TexCoordsUV.Length); for (var c = 0; c < v.TexCoordsUV.Length; c++) { this.SerializeVector2(writer, v.TexCoordsUV[c]); } writer.Write(v.TexCoordsUVW.Length); for (var c = 0; c < v.TexCoordsUVW.Length; c++) { this.SerializeVector3(writer, v.TexCoordsUVW[c]); } if (v.BoneIndices != null) { writer.Write(true); this.SerializeVector4(writer, v.BoneIndices.Value.ToVector4()); } else { writer.Write(false); } if (v.BoneWeights != null) { writer.Write(true); this.SerializeVector4(writer, v.BoneWeights.Value); } else { writer.Write(false); } } }
/// <summary> /// Load an FBX file from a file on disk. /// </summary> /// <param name="filename"> /// The filename of the FBX file to load. /// </param> /// <param name="additionalAnimationFiles"> /// A dictionary mapping of animation names to filenames for additional FBX files to load. /// </param> /// <param name="options">Additional options for the import.</param> /// <returns> /// The loaded <see cref="IModel"/>. /// </returns> public IModel Load(string filename, string name, Dictionary <string, string> additionalAnimationFiles, string[] options) { this.LoadAssimpLibrary(); // Import the scene via AssImp. var importer = new AssimpContext(); PostProcessSteps ProcessFlags = 0; if (options == null) { ProcessFlags |= PostProcessSteps.FlipUVs | PostProcessSteps.Triangulate | PostProcessSteps.FlipWindingOrder; } else { foreach (var v in options) { PostProcessSteps flag; if (Enum.TryParse(v, true, out flag)) { ProcessFlags |= flag; Console.Write("(on: " + flag + ") "); } } } var scene = importer.ImportFile(filename, ProcessFlags); ModelVertex[] vertexes; int[] indices; IModelBone boneHierarchy; Material material = null; if (scene.MeshCount >= 1) { var boneWeightingMap = this.BuildBoneWeightingMap(scene); var staticTransformMap = this.BuildStaticTransformMap(scene); if (options?.Contains("!NoBoneHierarchy") ?? false) { boneHierarchy = null; } else { boneHierarchy = this.ImportBoneHierarchy(scene.RootNode, scene.Meshes[0]); } vertexes = this.ImportVertexes(scene, boneWeightingMap, staticTransformMap); indices = this.ImportIndices(scene); } else { boneHierarchy = this.ImportBoneHierarchy(scene.RootNode, null); vertexes = new ModelVertex[0]; indices = new int[0]; } // If the scene has materials associated with it, and the mesh has // a material associated with it, read in the material information. if (scene.MeshCount >= 1 && scene.MaterialCount > scene.Meshes[0].MaterialIndex) { var assimpMaterial = scene.Materials[scene.Meshes[0].MaterialIndex]; material = ConvertMaterial(assimpMaterial); } // Create the list of animations, including the null animation. var animations = new List <IAnimation>(); // Import the basic animation. if (scene.AnimationCount > 0) { animations.AddRange( scene.Animations.Select( assimpAnimation => this.ImportAnimation(assimpAnimation.Name, assimpAnimation))); } // For each additional animation file, import that and add the animation to the existing scene. animations.AddRange( from kv in additionalAnimationFiles let animationImporter = new AssimpContext() let additionalScene = animationImporter.ImportFile(kv.Value, ProcessFlags) where additionalScene.AnimationCount == 1 select this.ImportAnimation(kv.Key, additionalScene.Animations[0])); // Return the resulting model. return(new Model( _modelRenderConfigurations, _renderBatcher, name, new AnimationCollection(animations), material, boneHierarchy, vertexes, indices)); }
/// <summary> /// Serializes an array of model vertexes to a binary stream. /// </summary> /// <param name="writer"> /// The binary writer to which the model vertexes will be serialized. /// </param> /// <param name="vertexes"> /// The vertexes to serialize. /// </param> private void SerializeVertexes(BinaryWriter writer, ModelVertex[] vertexes) { writer.Write(vertexes.Length); for (var i = 0; i < vertexes.Length; i++) { this.SerializeVector3(writer, vertexes[i].Position ?? Vector3.Zero); this.SerializeVector3(writer, vertexes[i].Normal ?? Vector3.Zero); this.SerializeVector2(writer, vertexes[i].TexCoordsUV[0]); this.SerializeVector4(writer, vertexes[i].BoneWeights ?? Vector4.Zero); this.SerializeVector4(writer, (vertexes[i].BoneIndices ?? new Byte4()).ToVector4()); } }
/// <summary> /// Load an FBX file from a file on disk. /// </summary> /// <param name="filename"> /// The filename of the FBX file to load. /// </param> /// <param name="additionalAnimationFiles"> /// A dictionary mapping of animation names to filenames for additional FBX files to load. /// </param> /// <param name="options">Additional options for the import.</param> /// <returns> /// The loaded <see cref="IModel"/>. /// </returns> public IModel Load(string filename, string name, Dictionary<string, string> additionalAnimationFiles, string[] options) { this.LoadAssimpLibrary(); // Import the scene via AssImp. var importer = new AssimpContext(); PostProcessSteps ProcessFlags = 0; if (options == null) { ProcessFlags |= PostProcessSteps.FlipUVs | PostProcessSteps.Triangulate | PostProcessSteps.FlipWindingOrder; } else { foreach (var v in options) { PostProcessSteps flag; if (Enum.TryParse(v, true, out flag)) { ProcessFlags |= flag; Console.Write("(on: " + flag + ") "); } } } var scene = importer.ImportFile(filename, ProcessFlags); ModelVertex[] vertexes; int[] indices; IModelBone boneHierarchy; Material material = null; if (scene.MeshCount >= 1) { var boneWeightingMap = this.BuildBoneWeightingMap(scene); var staticTransformMap = this.BuildStaticTransformMap(scene); if (options?.Contains("!NoBoneHierarchy") ?? false) { boneHierarchy = null; } else { boneHierarchy = this.ImportBoneHierarchy(scene.RootNode, scene.Meshes[0]); } vertexes = this.ImportVertexes(scene, boneWeightingMap, staticTransformMap); indices = this.ImportIndices(scene); } else { boneHierarchy = this.ImportBoneHierarchy(scene.RootNode, null); vertexes = new ModelVertex[0]; indices = new int[0]; } // If the scene has materials associated with it, and the mesh has // a material associated with it, read in the material information. if (scene.MeshCount >= 1 && scene.MaterialCount > scene.Meshes[0].MaterialIndex) { var assimpMaterial = scene.Materials[scene.Meshes[0].MaterialIndex]; material = ConvertMaterial(assimpMaterial); } // Create the list of animations, including the null animation. var animations = new List<IAnimation>(); // Import the basic animation. if (scene.AnimationCount > 0) { animations.AddRange( scene.Animations.Select( assimpAnimation => this.ImportAnimation(assimpAnimation.Name, assimpAnimation))); } // For each additional animation file, import that and add the animation to the existing scene. animations.AddRange( from kv in additionalAnimationFiles let animationImporter = new AssimpContext() let additionalScene = animationImporter.ImportFile(kv.Value, ProcessFlags) where additionalScene.AnimationCount == 1 select this.ImportAnimation(kv.Key, additionalScene.Animations[0])); // Return the resulting model. return new Model( _modelRenderConfigurations, _renderBatcher, name, new AnimationCollection(animations), material, boneHierarchy, vertexes, indices); }