public void Export(IFile outputFile, IModel model) { var outputPath = outputFile.FullName; var outputExtension = outputFile.Extension; var inputFile = outputFile.CloneWithExtension(".glb"); var inputPath = inputFile.FullName; var inputExtension = inputFile.Extension; var ctx = new AssimpContext(); string exportFormatId; { var supportedImportFormatExtensions = ctx.GetSupportedImportFormats(); Asserts.True(supportedImportFormatExtensions.Contains(inputExtension), $"'{inputExtension}' is not a supported import format!"); var supportedExportFormats = ctx.GetSupportedExportFormats(); var exportFormatIds = supportedExportFormats .Where(exportFormat => outputExtension == $".{exportFormat.FileExtension}") .Select(exportFormat => exportFormat.FormatId); Asserts.True(exportFormatIds.Any(), $"'{outputExtension}' is not a supported export format!"); exportFormatId = exportFormatIds.First(); } var sc = ctx.ImportFile(inputPath); var success = ctx.ExportFile(sc, outputPath, exportFormatId); Asserts.True(success, "Failed to export model."); }
// You can bet your ass I'm gonna prefix everything with ass. public void Export(IFile outputFile, IModel model) { var outputPath = outputFile.FullName; var outputExtension = outputFile.Extension; using var ctx = new AssimpContext(); string exportFormatId; { var supportedExportFormats = ctx.GetSupportedExportFormats(); var exportFormatIds = supportedExportFormats .Where(exportFormat => outputExtension == $".{exportFormat.FileExtension}") .Select(exportFormat => exportFormat.FormatId); Asserts.True(exportFormatIds.Any(), $"'{outputExtension}' is not a supported export format!"); exportFormatId = exportFormatIds.First(); } var assScene = new Scene(); assScene.RootNode = new Node("ROOT"); var inputFile = outputFile.CloneWithExtension(".glb"); var inputPath = inputFile.FullName; new GltfExporter().Export(inputFile, model); var sc = ctx.ImportFile(inputPath); ; // [ ]: Get skeleton working // [ ]: Get mesh working // [ ]: Get UVs working // [ ]: Get materials working // [ ]: Get multi-bone-binding working new AssimpSkeletonBuilder().BuildAndBindSkeleton(assScene, model); new AssimpMeshBuilder().BuildAndBindMesh(assScene, model); /*var postProcessSteps = PostProcessSteps.FindInvalidData | * PostProcessSteps.ValidateDataStructure | * PostProcessSteps.GenerateBoundingBoxes | * PostProcessSteps.JoinIdenticalVertices | * PostProcessSteps.OptimizeMeshes | * PostProcessSteps.PreTransformVertices;*/ LogStream.IsVerboseLoggingEnabled = true; var logStream = new FinLogStream(); logStream.Attach(); bool wasSuccessful = false; try { wasSuccessful = ctx.ExportFile(assScene, outputPath, exportFormatId); } catch (Exception e) {} var error = AssimpLibrary.Instance.GetErrorString(); Asserts.True(wasSuccessful, "Failed to export model: " + error); return; /* // Importing the pre-generated GLTF file does most of the hard work off * // the bat: generating the mesh with properly weighted bones. * * // Bone orientation is already correct, you just need to enable * // "Automatic Bone Orientation" if importing in Blender. * * // Fix animations. * var finAnimations = model.AnimationManager.Animations; * var assAnimations = sc.Animations; * for (var a = 0; a < assAnimations.Count; ++a) { * var assAnimation = assAnimations[a]; * var finAnimation = finAnimations[a]; * * // Animations are SUPER slow, we need to speed them way up! * { * // Not entirely sure why this is right... * var animationSpeedup = 2 / assAnimation.DurationInTicks; * * // TODO: Include tangents from the animation file. * foreach (var channel in assAnimation.NodeAnimationChannels) { * this.ScaleKeyTimes_(channel.PositionKeys, animationSpeedup); * this.ScaleKeyTimes_(channel.ScalingKeys, animationSpeedup); * this.ScaleKeyTimes_(channel.RotationKeys, animationSpeedup); * } * } * * var assFps = assAnimation.TicksPerSecond; * var finFps = finAnimation.Fps; * * assAnimation.TicksPerSecond = finFps; * assAnimation.DurationInTicks *= finFps / assFps; * * // TODO: Include animation looping behavior here. * } * * // Export materials. * { * /*var finTextures = new HashSet<string>(); * foreach (var finMaterial in model.MaterialManager.All) { * foreach (var finTexture in finMaterial.Textures) { * finTextures.Add(finTexture); * } * } * * foreach (var finTexture in finTextures) { * var imageData = finTexture.ImageData; * * var imageBytes = new MemoryStream(); * imageData.Save(imageBytes, ImageFormat.Png); * * var assTexture = * new EmbeddedTexture("png", * imageBytes.ToArray(), * finTexture.Name); * assTexture.Filename = finTexture.Name + ".png"; * * finTextureToAssTexture[finTexture] = assTexture; * sc.Textures.Add(assTexture); * } * * // TODO: Need to update the UVs... * * // Fix the UVs. * var finVertices = model.Skin.Vertices; * var assMeshes = sc.Meshes; * * var animations = model.AnimationManager.Animations; * var firstAnimation = * (animations?.Count ?? 0) > 0 ? animations[0] : null; * * var boneTransformManager = new BoneTransformManager(); * /*boneTransformManager.CalculateMatrices( * model.Skeleton.Root, * firstAnimation != null ? (firstAnimation, 0) : null); * boneTransformManager.CalculateMatrices(model.Skeleton.Root, null); * * var finVerticesByMaterial = * model.Skin.Primitives * .GroupBy(primitive => primitive.Material) * .Select(grouping => { * var positionsAndNormals = grouping * .SelectMany(primitive => primitive.Vertices) * .ToHashSet() * .Select(finVertex => { * IPosition position = * new ModelImpl.PositionImpl(); * INormal normal = new ModelImpl.NormalImpl(); * boneTransformManager.ProjectVertex( * finVertex, * position, * normal); * return (position, normal); * }); * * //var uniquePositions = positionsAndNormals. * * return positionsAndNormals; * }) * .ToList(); * * var worldFinVerticesAndNormals = * finVertices.Select(finVertex => { * IPosition position = * new ModelImpl.PositionImpl(); * INormal normal = new ModelImpl.NormalImpl(); * boneTransformManager.ProjectVertex( * finVertex, * position, * normal); * return (position, normal); * }) * .ToArray(); * * foreach (var assMesh in assMeshes) { * var assLocations = assMesh.Vertices; * var assNormals = assMesh.Normals; * * var assUvs = assMesh.TextureCoordinateChannels; * * var oldAssUvs = new List<Vector3D>[8]; * for (var t = 0; t < 8; ++t) { * oldAssUvs[t] = assUvs[t].Select(x => x).ToList(); * assUvs[t].Clear(); * } * * * var hadUv = new bool[8]; * for (var i = 0; i < assLocations.Count; ++i) { * var assLocation = assLocations[i]; * var assNormal = assNormals[i]; * * // TODO: How to find this more efficiently??? * var minIndex = -1; * for (var v = 0; v < worldFinVerticesAndNormals.Length; ++v) { * var (position, normal) = worldFinVerticesAndNormals[v]; * var inNormal = finVertices[v].LocalNormal; * * var tolerance = .005; * * if (i == 131) { * var finUv = finVertices[v].GetUv(0); * * if (Math.Abs(finUv.U - .9375) < tolerance || * Math.Abs((1 - finUv.U) - .9375) < tolerance) { * var u0 = finUv.U; * var v0 = 1 - finUv.V; * ; * } * } * * if (Math.Abs(position.X - assLocation.X) < tolerance && * Math.Abs(position.Y - assLocation.Y) < tolerance && * Math.Abs(position.Z - assLocation.Z) < tolerance) { * if (Math.Abs(normal.X - assNormal.X) < tolerance && * Math.Abs(normal.Y - assNormal.Y) < tolerance && * Math.Abs(normal.Z - assNormal.Z) < tolerance) { * minIndex = v; * break; * } else { * ; * } * } * } * * var minLocation = worldFinVerticesAndNormals[minIndex]; * var minVertex = finVertices[minIndex]; * * for (var t = 0; t < 8; ++t) { * var uv = minVertex.GetUv(t); * * if (uv != null) { * hadUv[t] = true; * assUvs[t].Add(new Vector3D(uv.U, 1 - uv.V, 0)); * } else { * assUvs[t].Add(default); * } * } * } * * for (var t = 0; t < 8; ++t) { * if (!hadUv[t]) { * assUvs[t].Clear(); * } * } * * var lhs = oldAssUvs[0]; * var rhs = assUvs[0]; * for (var i = 0; i < lhs.Count; ++i) { * var oldAssUv = lhs[i]; * var assUv = rhs[i]; * * if (Math.Abs(oldAssUv.X - assUv.X) > .01 || * Math.Abs(oldAssUv.Y - assUv.Y) > .01) { * var assWorld = assLocations[i]; * ; * } * } * * ; * } * * // Re-add the textures. * * sc.Textures.Clear(); * sc.Materials.Clear(); * * foreach (var finMaterial in model.MaterialManager.All) { * var assMaterial = new Material(); * * assMaterial.Shaders.ShaderLanguageType = "HLSL"; * assMaterial.Shaders.FragmentShader = * @" * struct a2v { * float4 Position : POSITION; * float4 Color : COLOR0; * }; * * struct v2p { * float4 Position : POSITION; * float4 Color : COLOR0; * }; * * void main(in a2v IN, out v2p OUT, uniform float4x4 ModelViewMatrix) { * OUT.Position = mul(IN.Position, ModelViewMatrix); * OUT.Color = float4(255, 255, 0, 0); * } * "; * * assMaterial.Name = finMaterial.Name; * // TODO: Set shader * * if (finMaterial is ILayerMaterial layerMaterial) { * var addLayers = * layerMaterial * .Layers * .Where(layer => layer.BlendMode == FinBlendMode.ADD) * .ToArray(); * var multiplyLayers = * layerMaterial * .Layers * .Where(layer => layer.BlendMode == FinBlendMode.MULTIPLY) * .ToArray(); * * if (addLayers.Length == 0) { * throw new NotSupportedException("Expected to find an add layer!"); * } * if (addLayers.Length > 1) { * ; * } * if (addLayers.Length > 2) { * throw new NotSupportedException("Too many add layers for GLTF!"); * } * * for (var i = 0; i < addLayers.Length; ++i) { * var layer = addLayers[i]; * * // TODO: Support flat color layers by generating a 1x1 clamped texture of that color. * if (layer.ColorSource is ITexture finTexture) { * var assTextureSlot = new TextureSlot(); * assTextureSlot.FilePath = finTexture.Name + ".png"; * * // TODO: FBX doesn't support mirror. Blegh * assTextureSlot.WrapModeU = * this.ConvertWrapMode_(finTexture.WrapModeU); * assTextureSlot.WrapModeV = * this.ConvertWrapMode_(finTexture.WrapModeV); * * if (i == 0) { * assTextureSlot.TextureType = TextureType.Diffuse; * } else { * assTextureSlot.TextureType = TextureType.Emissive; * } * * // TODO: Set blend mode * //assTextureSlot.Operation = * * assTextureSlot.UVIndex = layer.TexCoordIndex; * * // TODO: Set texture coord type * * assMaterial.AddMaterialTexture(assTextureSlot); * } * } * * /*foreach (var layer in layerMaterial.Layers) { * // TODO: Support flat color layers by generating a 1x1 clamped texture of that color. * * if (layer.ColorSource is ITexture finTexture) { * var assTextureSlot = new TextureSlot(); * assTextureSlot.FilePath = finTexture.Name + ".png"; * assTextureSlot.TextureType = TextureType.Diffuse; * * // TODO: FBX doesn't support mirror. Blegh * assTextureSlot.WrapModeU = * this.ConvertWrapMode_(finTexture.WrapModeU); * assTextureSlot.WrapModeV = * this.ConvertWrapMode_(finTexture.WrapModeV); * * // TODO: Set blend mode * //assTextureSlot.Operation = * * // TODO: Set texture coord type * * assMaterial.AddMaterialTexture(assTextureSlot); * } * }*/ }
public void Export(IFile outputFile, IModel model) { var outputPath = outputFile.FullName; var outputExtension = outputFile.Extension; using var ctx = new AssimpContext(); string exportFormatId; { var supportedExportFormats = ctx.GetSupportedExportFormats(); var exportFormatIds = supportedExportFormats .Where(exportFormat => outputExtension == $".{exportFormat.FileExtension}") .Select(exportFormat => exportFormat.FormatId); Asserts.True(exportFormatIds.Any(), $"'{outputExtension}' is not a supported export format!"); exportFormatId = exportFormatIds.First(); } var inputFile = !this.LowLevel ? outputFile.CloneWithExtension(".glb") : outputFile.CloneWithExtension(".gltf"); var inputPath = inputFile.FullName; IGltfExporter gltfExporter = !this.LowLevel ? new GltfExporter() : new LowLevelGltfExporter(); Scene?assScene = null; if (!this.LowLevel) { gltfExporter.UvIndices = true; gltfExporter.Embedded = true; gltfExporter.Export(inputFile, model); assScene = ctx.ImportFile(inputPath); File.Delete(inputPath); // Importing the pre-generated GLTF file does most of the hard work off // the bat: generating the mesh with properly weighted bones. // Bone orientation is already correct, you just need to enable // "Automatic Bone Orientation" if importing in Blender. new AssimpIndirectAnimationFixer().Fix(model, assScene); new AssimpIndirectUvFixer().Fix(model, assScene); new AssimpIndirectTextureFixer().Fix(model, assScene); } // Reexports the GLTF version in case the FBX version is screwed up. gltfExporter.UvIndices = false; gltfExporter.Embedded = false; gltfExporter.Export( new FinFile(inputFile.FullName.Replace(".glb", "_gltf.glb")), model); if (assScene != null) { // Finally exports the fbx version. // TODO: Are these all safe to include? var preProcessing = PostProcessSteps.FindInvalidData | PostProcessSteps.JoinIdenticalVertices; var success = ctx.ExportFile(assScene, outputPath, exportFormatId, preProcessing); Asserts.True(success, "Failed to export model."); } }