2019-03-28 22:05:49 +01:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using Assimp;
|
2019-07-16 23:35:21 +02:00
|
|
|
|
using Toolbox.Library;
|
|
|
|
|
using Toolbox.Library.Animations;
|
|
|
|
|
using Toolbox.Library.Rendering;
|
2019-03-28 22:05:49 +01:00
|
|
|
|
using System.Windows.Forms;
|
2019-06-06 21:40:32 +02:00
|
|
|
|
using System.Linq;
|
2019-03-28 22:05:49 +01:00
|
|
|
|
|
2019-07-16 23:35:21 +02:00
|
|
|
|
namespace Toolbox.Library
|
2019-03-28 22:05:49 +01:00
|
|
|
|
{
|
|
|
|
|
public class AssimpSaver
|
|
|
|
|
{
|
2019-05-24 21:15:35 +02:00
|
|
|
|
private List<string> ExtractedTextures = new List<string>();
|
|
|
|
|
|
2019-03-28 22:05:49 +01:00
|
|
|
|
public List<string> BoneNames = new List<string>();
|
|
|
|
|
|
2019-05-24 21:15:35 +02:00
|
|
|
|
STProgressBar progressBar;
|
|
|
|
|
|
2019-06-19 20:57:59 +02:00
|
|
|
|
public void SaveFromModel(STGenericModel model, string FileName, List<STGenericTexture> Textures, STSkeleton skeleton = null, List<int> NodeArray = null) {
|
|
|
|
|
SaveFromModel(model.Objects.ToList(), model.Materials.ToList(), FileName, Textures, skeleton, NodeArray);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SaveFromModel(List<STGenericObject> Meshes, List<STGenericMaterial> Materials, string FileName, List<STGenericTexture> Textures, STSkeleton skeleton = null, List<int> NodeArray = null)
|
2019-03-28 22:05:49 +01:00
|
|
|
|
{
|
2019-05-24 21:15:35 +02:00
|
|
|
|
ExtractedTextures.Clear();
|
|
|
|
|
|
2019-03-28 22:05:49 +01:00
|
|
|
|
Scene scene = new Scene();
|
|
|
|
|
scene.RootNode = new Node("RootNode");
|
|
|
|
|
|
2019-05-24 21:15:35 +02:00
|
|
|
|
progressBar = new STProgressBar();
|
2019-08-20 00:28:59 +02:00
|
|
|
|
progressBar.Task = "Exporting Skeleton...";
|
2019-05-24 21:15:35 +02:00
|
|
|
|
progressBar.Value = 0;
|
|
|
|
|
progressBar.StartPosition = FormStartPosition.CenterScreen;
|
|
|
|
|
progressBar.Show();
|
|
|
|
|
progressBar.Refresh();
|
|
|
|
|
|
2019-03-28 22:05:49 +01:00
|
|
|
|
SaveSkeleton(skeleton, scene.RootNode);
|
2019-06-19 20:57:59 +02:00
|
|
|
|
SaveMaterials(scene, Materials, FileName, Textures);
|
2019-05-24 21:15:35 +02:00
|
|
|
|
|
2019-08-20 00:28:59 +02:00
|
|
|
|
progressBar.Task = "Exporting Meshes...";
|
2019-05-24 21:15:35 +02:00
|
|
|
|
progressBar.Value = 50;
|
|
|
|
|
|
2019-06-19 20:57:59 +02:00
|
|
|
|
SaveMeshes(scene, Meshes, skeleton, FileName, NodeArray);
|
2019-05-24 21:15:35 +02:00
|
|
|
|
|
|
|
|
|
progressBar.Task = "Saving File...";
|
|
|
|
|
progressBar.Value = 80;
|
|
|
|
|
|
2019-06-19 20:57:59 +02:00
|
|
|
|
SaveScene(FileName, scene, Meshes);
|
2019-05-24 21:15:35 +02:00
|
|
|
|
|
|
|
|
|
progressBar.Value = 100;
|
|
|
|
|
progressBar.Close();
|
|
|
|
|
progressBar.Dispose();
|
2019-05-03 01:45:55 +02:00
|
|
|
|
}
|
2019-03-28 22:05:49 +01:00
|
|
|
|
|
2019-06-16 15:53:41 +02:00
|
|
|
|
private void SaveScene(string FileName, Scene scene, List<STGenericObject> Meshes)
|
2019-05-03 01:45:55 +02:00
|
|
|
|
{
|
2019-03-28 22:05:49 +01:00
|
|
|
|
using (var v = new AssimpContext())
|
|
|
|
|
{
|
|
|
|
|
string ext = System.IO.Path.GetExtension(FileName);
|
|
|
|
|
|
|
|
|
|
string formatID = "collada";
|
|
|
|
|
if (ext == ".obj")
|
|
|
|
|
formatID = "obj";
|
|
|
|
|
if (ext == ".3ds")
|
|
|
|
|
formatID = "3ds";
|
|
|
|
|
if (ext == ".dae")
|
|
|
|
|
formatID = "collada";
|
|
|
|
|
if (ext == ".ply")
|
|
|
|
|
formatID = "ply";
|
|
|
|
|
|
2019-06-06 21:40:32 +02:00
|
|
|
|
bool ExportSuccessScene = v.ExportFile(scene, FileName, formatID, PostProcessSteps.FlipUVs);
|
|
|
|
|
if (ExportSuccessScene)
|
|
|
|
|
{
|
2019-06-16 15:53:41 +02:00
|
|
|
|
WriteExtraSkinningInfo(FileName, scene, Meshes);
|
2019-03-28 22:05:49 +01:00
|
|
|
|
MessageBox.Show($"Exported {FileName} Successfuly!");
|
2019-06-06 21:40:32 +02:00
|
|
|
|
}
|
2019-03-28 22:05:49 +01:00
|
|
|
|
else
|
|
|
|
|
MessageBox.Show($"Failed to export {FileName}!");
|
|
|
|
|
}
|
2019-06-06 21:40:32 +02:00
|
|
|
|
|
2019-03-28 22:05:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 20:57:59 +02:00
|
|
|
|
private void SaveMeshes(Scene scene, List<STGenericObject> Meshes, STSkeleton skeleton, string FileName, List<int> NodeArray)
|
2019-03-28 22:05:49 +01:00
|
|
|
|
{
|
|
|
|
|
int MeshIndex = 0;
|
2019-06-19 20:57:59 +02:00
|
|
|
|
foreach (var obj in Meshes)
|
2019-03-28 22:05:49 +01:00
|
|
|
|
{
|
2019-07-25 15:36:49 +02:00
|
|
|
|
var mesh = SaveMesh((STGenericObject)obj, scene, MeshIndex++, skeleton, NodeArray);
|
2019-05-03 01:45:55 +02:00
|
|
|
|
scene.Meshes.Add(mesh);
|
|
|
|
|
}
|
|
|
|
|
Node geomNode = new Node(Path.GetFileNameWithoutExtension(FileName), scene.RootNode);
|
2019-03-28 22:05:49 +01:00
|
|
|
|
|
2019-05-03 01:45:55 +02:00
|
|
|
|
for (int ob = 0; ob < scene.MeshCount; ob++)
|
|
|
|
|
{
|
2019-06-15 15:28:44 +02:00
|
|
|
|
geomNode.MeshIndices.Add(ob);
|
2019-07-03 18:39:23 +02:00
|
|
|
|
|
|
|
|
|
// if (!scene.Meshes[ob].HasBones)
|
2019-05-03 01:45:55 +02:00
|
|
|
|
}
|
2019-07-03 18:39:23 +02:00
|
|
|
|
|
2019-05-03 01:45:55 +02:00
|
|
|
|
scene.RootNode.Children.Add(geomNode);
|
|
|
|
|
}
|
2019-03-28 22:05:49 +01:00
|
|
|
|
|
2019-07-25 15:36:49 +02:00
|
|
|
|
private Mesh SaveMesh(STGenericObject genericObj, Scene scene, int index, STSkeleton skeleton, List<int> NodeArray)
|
2019-05-03 01:45:55 +02:00
|
|
|
|
{
|
2019-06-07 22:31:13 +02:00
|
|
|
|
//Assimp is weird so use mesh_# for the name. We'll change it back after save
|
|
|
|
|
Mesh mesh = new Mesh($"mesh_{ index }", PrimitiveType.Triangle);
|
2019-07-25 15:36:49 +02:00
|
|
|
|
|
|
|
|
|
if (genericObj.MaterialIndex < scene.MaterialCount && genericObj.MaterialIndex > 0)
|
|
|
|
|
mesh.MaterialIndex = genericObj.MaterialIndex;
|
|
|
|
|
else
|
|
|
|
|
mesh.MaterialIndex = 0;
|
2019-05-03 01:45:55 +02:00
|
|
|
|
|
|
|
|
|
List<Vector3D> textureCoords0 = new List<Vector3D>();
|
|
|
|
|
List<Vector3D> textureCoords1 = new List<Vector3D>();
|
|
|
|
|
List<Vector3D> textureCoords2 = new List<Vector3D>();
|
|
|
|
|
List<Color4D> vertexColors = new List<Color4D>();
|
2019-03-28 22:05:49 +01:00
|
|
|
|
|
2019-05-03 01:45:55 +02:00
|
|
|
|
int vertexID = 0;
|
|
|
|
|
foreach (Vertex v in genericObj.vertices)
|
|
|
|
|
{
|
|
|
|
|
mesh.Vertices.Add(new Vector3D(v.pos.X, v.pos.Y, v.pos.Z));
|
|
|
|
|
mesh.Normals.Add(new Vector3D(v.nrm.X, v.nrm.Y, v.nrm.Z));
|
|
|
|
|
textureCoords0.Add(new Vector3D(v.uv0.X, v.uv0.Y, 0));
|
|
|
|
|
textureCoords1.Add(new Vector3D(v.uv1.X, v.uv1.Y, 0));
|
|
|
|
|
textureCoords2.Add(new Vector3D(v.uv2.X, v.uv2.Y, 0));
|
|
|
|
|
vertexColors.Add(new Color4D(v.col.X, v.col.Y, v.col.Z, v.col.W));
|
|
|
|
|
|
2019-05-03 01:52:49 +02:00
|
|
|
|
if (skeleton != null)
|
2019-03-28 22:05:49 +01:00
|
|
|
|
{
|
2019-05-03 01:52:49 +02:00
|
|
|
|
for (int j = 0; j < v.boneIds.Count; j++)
|
2019-03-28 22:05:49 +01:00
|
|
|
|
{
|
2019-05-03 01:52:49 +02:00
|
|
|
|
if (j < genericObj.VertexSkinCount)
|
2019-03-28 22:05:49 +01:00
|
|
|
|
{
|
2019-06-19 20:57:59 +02:00
|
|
|
|
STBone STbone = null;
|
|
|
|
|
if (NodeArray != null)
|
|
|
|
|
{
|
|
|
|
|
//Get the bone via the node array and bone index from the vertex
|
|
|
|
|
STbone = skeleton.bones[NodeArray[v.boneIds[j]]];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
STbone = skeleton.bones[v.boneIds[j]];
|
2019-05-03 01:52:49 +02:00
|
|
|
|
|
|
|
|
|
//Find the index of a bone. If it doesn't exist then we add it
|
|
|
|
|
int boneInd = mesh.Bones.FindIndex(x => x.Name == STbone.Text);
|
|
|
|
|
|
|
|
|
|
if (boneInd == -1)
|
|
|
|
|
{
|
2019-07-16 23:35:21 +02:00
|
|
|
|
var matrices = Toolbox.Library.IO.MatrixExenstion.CalculateInverseMatrix(STbone);
|
2019-05-03 01:52:49 +02:00
|
|
|
|
|
|
|
|
|
//Set the inverse matrix
|
|
|
|
|
Matrix4x4 transform = matrices.inverse.FromNumerics();
|
|
|
|
|
|
|
|
|
|
//Create a new assimp bone
|
|
|
|
|
Bone bone = new Bone();
|
|
|
|
|
bone.Name = STbone.Text;
|
2019-06-05 02:17:19 +02:00
|
|
|
|
bone.OffsetMatrix = STbone.invert.ToMatrix4x4();
|
2019-05-03 01:52:49 +02:00
|
|
|
|
mesh.Bones.Add(bone);
|
|
|
|
|
BoneNames.Add(bone.Name);
|
|
|
|
|
|
|
|
|
|
boneInd = mesh.Bones.IndexOf(bone); //Set the index of the bone for the vertex weight
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-31 21:08:14 +02:00
|
|
|
|
int MinWeightAmount = 0;
|
|
|
|
|
|
2019-05-03 01:52:49 +02:00
|
|
|
|
//Check if the max amount of weights is higher than the current bone id
|
2019-05-31 21:08:14 +02:00
|
|
|
|
if (v.boneWeights.Count > j && v.boneWeights[j] > MinWeightAmount)
|
2019-05-03 01:52:49 +02:00
|
|
|
|
{
|
|
|
|
|
if (v.boneWeights[j] <= 1)
|
|
|
|
|
mesh.Bones[boneInd].VertexWeights.Add(new VertexWeight(vertexID, v.boneWeights[j]));
|
|
|
|
|
else
|
|
|
|
|
mesh.Bones[boneInd].VertexWeights.Add(new VertexWeight(vertexID, 1));
|
|
|
|
|
}
|
2019-05-31 21:08:14 +02:00
|
|
|
|
else if (v.boneWeights.Count == 0 || v.boneWeights[j] > MinWeightAmount)
|
2019-03-28 22:05:49 +01:00
|
|
|
|
mesh.Bones[boneInd].VertexWeights.Add(new VertexWeight(vertexID, 1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-03 01:52:49 +02:00
|
|
|
|
|
2019-06-08 20:50:10 +02:00
|
|
|
|
|
2019-05-03 01:45:55 +02:00
|
|
|
|
vertexID++;
|
2019-03-28 22:05:49 +01:00
|
|
|
|
}
|
2019-06-22 04:24:33 +02:00
|
|
|
|
|
|
|
|
|
if (genericObj.lodMeshes.Count != 0)
|
|
|
|
|
{
|
|
|
|
|
List<int> faces = genericObj.lodMeshes[genericObj.DisplayLODIndex].faces;
|
|
|
|
|
for (int f = 0; f < faces.Count; f++)
|
|
|
|
|
mesh.Faces.Add(new Face(new int[] { faces[f++], faces[f++], faces[f] }));
|
|
|
|
|
}
|
|
|
|
|
if (genericObj.PolygonGroups.Count != 0)
|
|
|
|
|
{
|
|
|
|
|
for (int p = 0; p < genericObj.PolygonGroups.Count; p++)
|
|
|
|
|
{
|
|
|
|
|
var polygonGroup = genericObj.PolygonGroups[p];
|
|
|
|
|
for (int f = 0; f < polygonGroup.faces.Count; f++)
|
|
|
|
|
mesh.Faces.Add(new Face(new int[] { polygonGroup.faces[f++], polygonGroup.faces[f++], polygonGroup.faces[f] }));
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-28 22:05:49 +01:00
|
|
|
|
|
2019-05-03 01:45:55 +02:00
|
|
|
|
mesh.TextureCoordinateChannels.SetValue(textureCoords0, 0);
|
2019-06-22 04:24:33 +02:00
|
|
|
|
mesh.TextureCoordinateChannels.SetValue(textureCoords1, 1);
|
|
|
|
|
mesh.TextureCoordinateChannels.SetValue(textureCoords2, 2);
|
|
|
|
|
mesh.VertexColorChannels.SetValue(vertexColors, 0);
|
2019-05-03 01:45:55 +02:00
|
|
|
|
|
|
|
|
|
return mesh;
|
2019-03-28 22:05:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-06 21:40:32 +02:00
|
|
|
|
//Extra skin data based on https://github.com/Sage-of-Mirrors/SuperBMD/blob/ce1061e9b5f57de112f1d12f6459b938594664a0/SuperBMDLib/source/Model.cs#L193
|
|
|
|
|
//Todo this doesn't quite work yet
|
|
|
|
|
//Need to adjust all mesh name IDs so they are correct
|
2019-06-16 15:53:41 +02:00
|
|
|
|
private void WriteExtraSkinningInfo(string FileName, Scene outScene, List<STGenericObject> Meshes)
|
2019-06-06 21:40:32 +02:00
|
|
|
|
{
|
|
|
|
|
StreamWriter test = new StreamWriter(FileName + ".tmp");
|
|
|
|
|
StreamReader dae = File.OpenText(FileName);
|
|
|
|
|
|
2019-06-07 22:31:13 +02:00
|
|
|
|
int geomIndex = 0;
|
2019-06-06 21:40:32 +02:00
|
|
|
|
while (!dae.EndOfStream)
|
|
|
|
|
{
|
|
|
|
|
string line = dae.ReadLine();
|
|
|
|
|
|
2019-06-15 15:24:25 +02:00
|
|
|
|
/* if (line == " <library_visual_scenes>")
|
|
|
|
|
{
|
|
|
|
|
AddControllerLibrary(outScene, test);
|
|
|
|
|
test.WriteLine(line);
|
|
|
|
|
test.Flush();
|
|
|
|
|
}
|
|
|
|
|
else if (line.Contains("<node"))
|
|
|
|
|
{
|
|
|
|
|
// test.WriteLine(line);
|
|
|
|
|
// test.Flush();
|
|
|
|
|
|
|
|
|
|
string[] testLn = line.Split('\"');
|
|
|
|
|
string name = testLn[3];
|
|
|
|
|
|
|
|
|
|
string jointLine = line.Replace(">", $" sid=\"{ name }\" type=\"JOINT\">");
|
|
|
|
|
test.WriteLine(jointLine);
|
|
|
|
|
test.Flush();
|
|
|
|
|
}
|
|
|
|
|
else if (line.Contains("</visual_scene>"))
|
|
|
|
|
{
|
|
|
|
|
foreach (Mesh mesh in outScene.Meshes)
|
|
|
|
|
{
|
|
|
|
|
test.WriteLine($" <node id=\"{ mesh.Name }\" name=\"{ mesh.Name }\" type=\"NODE\">");
|
|
|
|
|
|
|
|
|
|
test.WriteLine($" <instance_controller url=\"#{ mesh.Name }-skin\">");
|
|
|
|
|
test.WriteLine(" <skeleton>#skeleton_root</skeleton>");
|
|
|
|
|
test.WriteLine(" <bind_material>");
|
|
|
|
|
test.WriteLine(" <technique_common>");
|
|
|
|
|
test.WriteLine($" <instance_material symbol=\"theresonlyone\" target=\"#m{ mesh.MaterialIndex }mat\" />");
|
|
|
|
|
test.WriteLine(" </technique_common>");
|
|
|
|
|
test.WriteLine(" </bind_material>");
|
|
|
|
|
test.WriteLine(" </instance_controller>");
|
|
|
|
|
|
|
|
|
|
test.WriteLine(" </node>");
|
|
|
|
|
test.Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test.WriteLine(line);
|
|
|
|
|
test.Flush();
|
|
|
|
|
}*/
|
|
|
|
|
if (line.Contains("<geometry"))
|
2019-06-07 22:31:13 +02:00
|
|
|
|
{
|
2019-06-16 15:53:41 +02:00
|
|
|
|
string RealMeshName = Meshes[geomIndex].Text;
|
2019-06-07 22:31:13 +02:00
|
|
|
|
test.WriteLine($" <geometry id=\"meshId{ geomIndex }\" name=\"{ RealMeshName }\" > ");
|
|
|
|
|
test.Flush();
|
|
|
|
|
|
|
|
|
|
geomIndex++;
|
|
|
|
|
}
|
2019-06-06 21:40:32 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
2019-06-08 20:50:10 +02:00
|
|
|
|
test.WriteLine(line);
|
|
|
|
|
test.Flush();
|
2019-06-06 21:40:32 +02:00
|
|
|
|
}
|
2019-06-15 15:24:25 +02:00
|
|
|
|
|
|
|
|
|
/* else if (line.Contains("<matrix"))
|
|
|
|
|
{
|
|
|
|
|
string matLine = line.Replace("<matrix>", "<matrix sid=\"matrix\">");
|
|
|
|
|
test.WriteLine(matLine);
|
|
|
|
|
test.Flush();
|
|
|
|
|
}*/
|
|
|
|
|
|
2019-06-06 21:40:32 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test.Close();
|
|
|
|
|
dae.Close();
|
|
|
|
|
|
|
|
|
|
File.Copy(FileName + ".tmp", FileName, true);
|
|
|
|
|
File.Delete(FileName + ".tmp");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AddControllerLibrary(Scene scene, StreamWriter writer)
|
|
|
|
|
{
|
|
|
|
|
writer.WriteLine(" <library_controllers>");
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < scene.MeshCount; i++)
|
|
|
|
|
{
|
|
|
|
|
Mesh curMesh = scene.Meshes[i];
|
|
|
|
|
curMesh.Name = curMesh.Name.Replace('_', '-');
|
|
|
|
|
|
|
|
|
|
writer.WriteLine($" <controller id=\"{ curMesh.Name }-skin\" name=\"{ curMesh.Name }Skin\">");
|
|
|
|
|
|
|
|
|
|
writer.WriteLine($" <skin source=\"#meshId{ i }\">");
|
|
|
|
|
|
|
|
|
|
WriteBindShapeMatrixToStream(writer);
|
|
|
|
|
WriteJointNameArrayToStream(curMesh, writer);
|
|
|
|
|
WriteInverseBindMatricesToStream(curMesh, writer);
|
|
|
|
|
WriteSkinWeightsToStream(curMesh, writer);
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" <joints>");
|
|
|
|
|
|
|
|
|
|
writer.WriteLine($" <input semantic=\"JOINT\" source=\"#{ curMesh.Name }-skin-joints-array\"></input>");
|
|
|
|
|
writer.WriteLine($" <input semantic=\"INV_BIND_MATRIX\" source=\"#{ curMesh.Name }-skin-bind_poses-array\"></input>");
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" </joints>");
|
|
|
|
|
writer.Flush();
|
|
|
|
|
|
|
|
|
|
WriteVertexWeightsToStream(curMesh, writer);
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" </skin>");
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" </controller>");
|
|
|
|
|
writer.Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" </library_controllers>");
|
|
|
|
|
writer.Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void WriteJointNameArrayToStream(Mesh mesh, StreamWriter writer)
|
|
|
|
|
{
|
|
|
|
|
writer.WriteLine($" <source id =\"{ mesh.Name }-skin-joints-array\">");
|
|
|
|
|
writer.WriteLine($" <Name_array id=\"{ mesh.Name }-skin-joints-array\" count=\"{ mesh.Bones.Count }\">");
|
|
|
|
|
|
|
|
|
|
writer.Write(" ");
|
|
|
|
|
foreach (Bone bone in mesh.Bones)
|
|
|
|
|
{
|
|
|
|
|
writer.Write($"{ bone.Name }");
|
|
|
|
|
if (bone != mesh.Bones.Last())
|
|
|
|
|
writer.Write(' ');
|
|
|
|
|
else
|
|
|
|
|
writer.Write('\n');
|
|
|
|
|
|
|
|
|
|
writer.Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" </Name_array>");
|
|
|
|
|
writer.Flush();
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" <technique_common>");
|
|
|
|
|
writer.WriteLine($" <accessor source=\"#{ mesh.Name }-skin-joints-array\" count=\"{ mesh.Bones.Count }\" stride=\"1\">");
|
|
|
|
|
writer.WriteLine(" <param name=\"JOINT\" type=\"Name\"></param>");
|
|
|
|
|
writer.WriteLine(" </accessor>");
|
|
|
|
|
writer.WriteLine(" </technique_common>");
|
|
|
|
|
writer.WriteLine(" </source>");
|
|
|
|
|
writer.Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void WriteInverseBindMatricesToStream(Mesh mesh, StreamWriter writer)
|
|
|
|
|
{
|
|
|
|
|
writer.WriteLine($" <source id =\"{ mesh.Name }-skin-bind_poses-array\">");
|
|
|
|
|
writer.WriteLine($" <float_array id=\"{ mesh.Name }-skin-bind_poses-array\" count=\"{ mesh.Bones.Count * 16 }\">");
|
|
|
|
|
|
|
|
|
|
foreach (Bone bone in mesh.Bones)
|
|
|
|
|
{
|
|
|
|
|
Matrix4x4 ibm = bone.OffsetMatrix;
|
|
|
|
|
ibm.Transpose();
|
|
|
|
|
|
|
|
|
|
writer.WriteLine($" {ibm.A1.ToString("F")} {ibm.A2.ToString("F")} {ibm.A3.ToString("F")} {ibm.A4.ToString("F")}");
|
|
|
|
|
writer.WriteLine($" {ibm.B1.ToString("F")} {ibm.B2.ToString("F")} {ibm.B3.ToString("F")} {ibm.B4.ToString("F")}");
|
|
|
|
|
writer.WriteLine($" {ibm.C1.ToString("F")} {ibm.C2.ToString("F")} {ibm.C3.ToString("F")} {ibm.C4.ToString("F")}");
|
|
|
|
|
writer.WriteLine($" {ibm.D1.ToString("F")} {ibm.D2.ToString("F")} {ibm.D3.ToString("F")} {ibm.D4.ToString("F")}");
|
|
|
|
|
|
|
|
|
|
if (bone != mesh.Bones.Last())
|
|
|
|
|
writer.WriteLine("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" </float_array>");
|
|
|
|
|
writer.Flush();
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" <technique_common>");
|
|
|
|
|
writer.WriteLine($" <accessor source=\"#{ mesh.Name }-skin-bind_poses-array\" count=\"{ mesh.Bones.Count }\" stride=\"16\">");
|
|
|
|
|
writer.WriteLine(" <param name=\"TRANSFORM\" type=\"float4x4\"></param>");
|
|
|
|
|
writer.WriteLine(" </accessor>");
|
|
|
|
|
writer.WriteLine(" </technique_common>");
|
|
|
|
|
writer.WriteLine(" </source>");
|
|
|
|
|
writer.Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void WriteSkinWeightsToStream(Mesh mesh, StreamWriter writer)
|
|
|
|
|
{
|
|
|
|
|
int totalWeightCount = 0;
|
|
|
|
|
|
|
|
|
|
foreach (Bone bone in mesh.Bones)
|
|
|
|
|
{
|
|
|
|
|
totalWeightCount += bone.VertexWeightCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.WriteLine($" <source id =\"{ mesh.Name }-skin-weights-array\">");
|
|
|
|
|
writer.WriteLine($" <float_array id=\"{ mesh.Name }-skin-weights-array\" count=\"{ totalWeightCount }\">");
|
|
|
|
|
writer.Write(" ");
|
|
|
|
|
|
|
|
|
|
foreach (Bone bone in mesh.Bones)
|
|
|
|
|
{
|
|
|
|
|
foreach (VertexWeight weight in bone.VertexWeights)
|
|
|
|
|
{
|
|
|
|
|
writer.Write($"{ weight.Weight } ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bone == mesh.Bones.Last())
|
|
|
|
|
writer.WriteLine();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" </float_array>");
|
|
|
|
|
writer.Flush();
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" <technique_common>");
|
|
|
|
|
writer.WriteLine($" <accessor source=\"#{ mesh.Name }-skin-weights-array\" count=\"{ totalWeightCount }\" stride=\"1\">");
|
|
|
|
|
writer.WriteLine(" <param name=\"WEIGHT\" type=\"float\"></param>");
|
|
|
|
|
writer.WriteLine(" </accessor>");
|
|
|
|
|
writer.WriteLine(" </technique_common>");
|
|
|
|
|
writer.WriteLine(" </source>");
|
|
|
|
|
writer.Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class RiggedWeight
|
|
|
|
|
{
|
|
|
|
|
public List<float> Weights { get; private set; }
|
|
|
|
|
public List<int> BoneIndices { get; private set; }
|
|
|
|
|
|
|
|
|
|
public int WeightCount { get; private set; }
|
|
|
|
|
|
|
|
|
|
public RiggedWeight()
|
|
|
|
|
{
|
|
|
|
|
Weights = new List<float>();
|
|
|
|
|
BoneIndices = new List<int>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void AddWeight(float weight, int boneIndex)
|
|
|
|
|
{
|
|
|
|
|
Weights.Add(weight);
|
|
|
|
|
BoneIndices.Add(boneIndex);
|
|
|
|
|
WeightCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void WriteVertexWeightsToStream(Mesh mesh, StreamWriter writer)
|
|
|
|
|
{
|
|
|
|
|
List<float> weights = new List<float>();
|
|
|
|
|
Dictionary<int, RiggedWeight> vertIDWeights = new Dictionary<int, RiggedWeight>();
|
|
|
|
|
|
|
|
|
|
foreach (Bone bone in mesh.Bones)
|
|
|
|
|
{
|
|
|
|
|
foreach (VertexWeight weight in bone.VertexWeights)
|
|
|
|
|
{
|
|
|
|
|
weights.Add(weight.Weight);
|
|
|
|
|
|
|
|
|
|
if (!vertIDWeights.ContainsKey(weight.VertexID))
|
|
|
|
|
vertIDWeights.Add(weight.VertexID, new RiggedWeight());
|
|
|
|
|
|
|
|
|
|
vertIDWeights[weight.VertexID].AddWeight(weight.Weight, mesh.Bones.IndexOf(bone));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.WriteLine($" <vertex_weights count=\"{ vertIDWeights.Count }\">");
|
|
|
|
|
|
|
|
|
|
writer.WriteLine($" <input semantic=\"JOINT\" source=\"#{ mesh.Name }-skin-joints-array\" offset=\"0\"></input>");
|
|
|
|
|
writer.WriteLine($" <input semantic=\"WEIGHT\" source=\"#{ mesh.Name }-skin-weights-array\" offset=\"1\"></input>");
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" <vcount>");
|
|
|
|
|
|
|
|
|
|
writer.Write(" ");
|
|
|
|
|
for (int i = 0; i < vertIDWeights.Count; i++)
|
|
|
|
|
writer.Write($"{ vertIDWeights[i].WeightCount } ");
|
|
|
|
|
|
|
|
|
|
writer.WriteLine("\n </vcount>");
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" <v>");
|
|
|
|
|
writer.Write(" ");
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < vertIDWeights.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
RiggedWeight curWeight = vertIDWeights[i];
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < curWeight.WeightCount; j++)
|
|
|
|
|
{
|
|
|
|
|
writer.Write($"{ curWeight.BoneIndices[j] } { weights.IndexOf(curWeight.Weights[j]) } ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.WriteLine("\n </v>");
|
|
|
|
|
|
|
|
|
|
writer.WriteLine($" </vertex_weights>");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void WriteBindShapeMatrixToStream(StreamWriter writer)
|
|
|
|
|
{
|
|
|
|
|
writer.WriteLine(" <bind_shape_matrix>");
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" 1 0 0 0");
|
|
|
|
|
writer.WriteLine(" 0 1 0 0");
|
|
|
|
|
writer.WriteLine(" 0 0 1 0");
|
|
|
|
|
writer.WriteLine(" 0 0 0 1");
|
|
|
|
|
|
|
|
|
|
writer.WriteLine(" </bind_shape_matrix>");
|
|
|
|
|
writer.Flush();
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 20:57:59 +02:00
|
|
|
|
private void SaveMaterials(Scene scene, List<STGenericMaterial> Materials, string FileName, List<STGenericTexture> Textures)
|
2019-03-28 22:05:49 +01:00
|
|
|
|
{
|
|
|
|
|
string TextureExtension = ".png";
|
|
|
|
|
string TexturePath = System.IO.Path.GetDirectoryName(FileName);
|
|
|
|
|
|
2019-05-24 21:15:35 +02:00
|
|
|
|
for (int i = 0; i < Textures.Count; i++)
|
2019-03-28 22:05:49 +01:00
|
|
|
|
{
|
2019-05-24 21:15:35 +02:00
|
|
|
|
string path = System.IO.Path.Combine(TexturePath, Textures[i].Text + TextureExtension);
|
|
|
|
|
|
|
|
|
|
if (!ExtractedTextures.Contains(path))
|
|
|
|
|
{
|
|
|
|
|
ExtractedTextures.Add(path);
|
|
|
|
|
|
2019-08-20 00:28:59 +02:00
|
|
|
|
progressBar.Task = $"Exporting Texture {Textures[i].Text}";
|
2019-05-24 21:15:35 +02:00
|
|
|
|
progressBar.Value = ((i * 100) / Textures.Count);
|
|
|
|
|
progressBar.Refresh();
|
2019-03-28 22:05:49 +01:00
|
|
|
|
|
2019-05-24 21:15:35 +02:00
|
|
|
|
var bitmap = Textures[i].GetBitmap();
|
|
|
|
|
bitmap.Save(path);
|
|
|
|
|
bitmap.Dispose();
|
2019-04-30 01:50:12 +02:00
|
|
|
|
|
2019-05-24 21:15:35 +02:00
|
|
|
|
GC.Collect();
|
|
|
|
|
}
|
2019-03-28 22:05:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-25 15:36:49 +02:00
|
|
|
|
if (Materials.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
Material material = new Material();
|
|
|
|
|
material.Name = "New Material";
|
|
|
|
|
scene.Materials.Add(material);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 20:57:59 +02:00
|
|
|
|
foreach (var mat in Materials)
|
2019-03-28 22:05:49 +01:00
|
|
|
|
{
|
|
|
|
|
var genericMat = (STGenericMaterial)mat;
|
|
|
|
|
|
|
|
|
|
Material material = new Material();
|
|
|
|
|
material.Name = genericMat.Text;
|
|
|
|
|
|
|
|
|
|
foreach (var tex in genericMat.TextureMaps)
|
|
|
|
|
{
|
2019-03-30 02:05:53 +01:00
|
|
|
|
int index = Textures.FindIndex(r => r.Text.Equals(tex.Name));
|
|
|
|
|
|
2019-03-28 22:05:49 +01:00
|
|
|
|
string path = System.IO.Path.Combine(TexturePath, tex.Name + TextureExtension);
|
2019-03-28 22:28:39 +01:00
|
|
|
|
|
2019-03-30 02:05:53 +01:00
|
|
|
|
if (!File.Exists(path))
|
2019-04-07 23:14:55 +02:00
|
|
|
|
continue;
|
2019-03-30 02:05:53 +01:00
|
|
|
|
|
2019-06-08 20:50:10 +02:00
|
|
|
|
TextureSlot slot2 = new TextureSlot(path, ConvertToAssimpTextureType(tex.Type), 0, TextureMapping.FromUV,
|
2019-08-03 21:23:05 +02:00
|
|
|
|
0, 1.0f, Assimp.TextureOperation.Add, ConvertToAssimpWrapType(tex.WrapModeS), ConvertToAssimpWrapType(tex.WrapModeT), 0);
|
2019-06-08 20:50:10 +02:00
|
|
|
|
|
|
|
|
|
material.AddMaterialTexture(ref slot2);
|
2019-03-28 22:05:49 +01:00
|
|
|
|
}
|
|
|
|
|
scene.Materials.Add(material);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-03 21:23:05 +02:00
|
|
|
|
private static Assimp.TextureWrapMode ConvertToAssimpWrapType(STTextureWrapMode type)
|
2019-06-08 20:50:10 +02:00
|
|
|
|
{
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
2019-08-03 21:23:05 +02:00
|
|
|
|
case STTextureWrapMode.Repeat: return TextureWrapMode.Wrap;
|
|
|
|
|
case STTextureWrapMode.Mirror: return TextureWrapMode.Mirror;
|
|
|
|
|
case STTextureWrapMode.Clamp: return TextureWrapMode.Clamp;
|
2019-06-08 20:50:10 +02:00
|
|
|
|
default:
|
|
|
|
|
return TextureWrapMode.Wrap;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Assimp.TextureType ConvertToAssimpTextureType(STGenericMatTexture.TextureType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case STGenericMatTexture.TextureType.Diffuse: return TextureType.Diffuse;
|
|
|
|
|
case STGenericMatTexture.TextureType.AO: return TextureType.Ambient;
|
|
|
|
|
case STGenericMatTexture.TextureType.Normal: return TextureType.Normals;
|
|
|
|
|
case STGenericMatTexture.TextureType.Light: return TextureType.Lightmap;
|
|
|
|
|
case STGenericMatTexture.TextureType.Emission: return TextureType.Emissive;
|
|
|
|
|
case STGenericMatTexture.TextureType.Specular: return TextureType.Specular;
|
|
|
|
|
default:
|
|
|
|
|
return TextureType.Unknown;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-03 01:45:55 +02:00
|
|
|
|
public void SaveFromObject(STGenericObject genericObject, string FileName)
|
2019-03-28 22:05:49 +01:00
|
|
|
|
{
|
|
|
|
|
Scene scene = new Scene();
|
|
|
|
|
scene.RootNode = new Node("Root");
|
|
|
|
|
|
2019-07-25 15:36:49 +02:00
|
|
|
|
var mesh = SaveMesh(genericObject, scene, 0, null, null);
|
2019-03-28 22:05:49 +01:00
|
|
|
|
mesh.MaterialIndex = 0;
|
|
|
|
|
scene.Meshes.Add(mesh);
|
|
|
|
|
|
|
|
|
|
Material material = new Material();
|
|
|
|
|
material.Name = "NewMaterial";
|
|
|
|
|
scene.Materials.Add(material);
|
|
|
|
|
|
2019-06-16 15:53:41 +02:00
|
|
|
|
SaveScene(FileName, scene, new List<STGenericObject>() { genericObject });
|
2019-03-28 22:05:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SaveSkeleton(STSkeleton skeleton, Node parentNode)
|
|
|
|
|
{
|
|
|
|
|
Node root = new Node("skeleton_root");
|
|
|
|
|
parentNode.Children.Add(root);
|
|
|
|
|
|
|
|
|
|
if (skeleton.bones.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
Node boneNode = new Node(skeleton.bones[0].Text);
|
|
|
|
|
boneNode.Transform = AssimpHelper.GetBoneMatrix(skeleton.bones[0]);
|
|
|
|
|
root.Children.Add(boneNode);
|
|
|
|
|
|
|
|
|
|
foreach (STBone child in skeleton.bones[0].GetChildren())
|
|
|
|
|
SaveBones(boneNode, child, skeleton);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private void SaveBones(Node parentBone, STBone bone, STSkeleton skeleton)
|
|
|
|
|
{
|
|
|
|
|
Node boneNode = new Node(bone.Text);
|
|
|
|
|
parentBone.Children.Add(boneNode);
|
|
|
|
|
|
|
|
|
|
boneNode.Transform = AssimpHelper.GetBoneMatrix(bone);
|
|
|
|
|
|
|
|
|
|
foreach (STBone child in bone.GetChildren())
|
|
|
|
|
SaveBones(boneNode, child, skeleton);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|