Improve DAE exporting.
A brand new DAE exporter is now used built off the one used by Ploaj and Crossmod devs. This replaces the old assimp exporter which was very buggy. The DAE exporter has many improvments such as multiple materials per mesh, node tree fixes, material improvements, and lots more. Single binded bfres (only using a bone index) will now be rigged if exported as DAE. More progress on CMB saving. It's almost done, still needs some adjustments. Fixed GFMDL rigging on certain models (thanks to RTB's script to reference what controlled the bone type).
This commit is contained in:
parent
762ec86bf0
commit
9c3ac9ae9f
@ -29,17 +29,21 @@ namespace FirstPlugin
|
||||
|
||||
public override void Export(string FileName)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
ExportModelSettings exportDlg = new ExportModelSettings();
|
||||
if (exportDlg.ShowDialog() == DialogResult.OK)
|
||||
ExportModel(FileName, exportDlg.Settings);
|
||||
}
|
||||
|
||||
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
var model = new STGenericModel();
|
||||
model.Materials = Materials;
|
||||
model.Objects = Shapes;
|
||||
|
||||
|
||||
assimp.SaveFromModel(model, FileName, BcresParent.GetTextures(), Skeleton.Renderable);
|
||||
DAE.Export(fileName, settings, model, BcresParent.GetTextures(), Skeleton.Renderable);
|
||||
}
|
||||
|
||||
|
||||
public override string ExportFilter => FileFilters.CMDL;
|
||||
|
||||
public CMDLWrapper(Model model, BCRES bcres) : base()
|
||||
|
@ -545,22 +545,11 @@ namespace Bfres.Structs
|
||||
OBJ.ExportModel(FileName, this, GetTextures());
|
||||
break;
|
||||
default:
|
||||
if(Runtime.DEVELOPER_DEBUG_MODE)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
if (settings.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
if (settings.ExportTextures)
|
||||
assimp.SaveFromModel(this, FileName, GetTextures(), Skeleton, Skeleton.Node_Array.ToList());
|
||||
else
|
||||
assimp.SaveFromModel(this, FileName, new List<STGenericTexture>(), Skeleton, Skeleton.Node_Array.ToList());
|
||||
}
|
||||
}
|
||||
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
if (settings.ShowDialog() == DialogResult.OK)
|
||||
DAE.Export(FileName, settings.Settings, this, GetTextures(),
|
||||
Skeleton, Skeleton.Node_Array.ToList());
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -819,8 +819,7 @@ namespace Bfres.Structs
|
||||
OBJ.ExportMesh(sfd.FileName, this);
|
||||
break;
|
||||
default:
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
assimp.SaveFromObject(this, sfd.FileName);
|
||||
DAE.Export(sfd.FileName, new DAE.ExportSettings(), this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -99,33 +99,32 @@ namespace FirstPlugin
|
||||
header.Read(new FileReader(stream), this);
|
||||
|
||||
ContextMenuStrip = new STContextMenuStrip();
|
||||
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Export Model", null, ExportModelAction, Keys.Control | Keys.E));
|
||||
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Export Model", null, ExportAction, Keys.Control | Keys.E));
|
||||
}
|
||||
|
||||
private void ExportModelAction(object sender, EventArgs args) {
|
||||
ExportModel();
|
||||
}
|
||||
|
||||
private void ExportModel()
|
||||
private void ExportAction(object sender, EventArgs args)
|
||||
{
|
||||
SaveFileDialog sfd = new SaveFileDialog();
|
||||
sfd.Filter = "Supported Formats|*.dae;";
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
ExportModel(sfd.FileName);
|
||||
ExportModelSettings exportDlg = new ExportModelSettings();
|
||||
if (exportDlg.ShowDialog() == DialogResult.OK)
|
||||
ExportModel(sfd.FileName, exportDlg.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportModel(string FileName)
|
||||
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
|
||||
var model = new STGenericModel();
|
||||
model.Materials = header.GenericMaterials;
|
||||
model.Objects = Renderer.Meshes;
|
||||
var textures = new List<STGenericTexture>();
|
||||
foreach (var bntx in PluginRuntime.bntxContainers)
|
||||
foreach (var tex in bntx.Textures.Values)
|
||||
textures.Add(tex);
|
||||
|
||||
assimp.SaveFromModel(model, FileName, new List<STGenericTexture>(), ((STSkeleton)DrawableContainer.Drawables[0]));
|
||||
DAE.Export(fileName, settings, model, textures, ((STSkeleton)DrawableContainer.Drawables[0]));
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
@ -139,6 +138,8 @@ namespace FirstPlugin
|
||||
}
|
||||
|
||||
//Todo replace tedius offset handling with a class to store necessary data and methods to execute
|
||||
//Structure thanks to RTB's script https://www.vg-resource.com/thread-29836.html
|
||||
//And https://github.com/dragonation/pokemon-switch-model-resolver/blob/master/src/parse.js
|
||||
public class Header
|
||||
{
|
||||
public STSkeleton Skeleton { get; set; }
|
||||
@ -156,6 +157,8 @@ namespace FirstPlugin
|
||||
|
||||
public bool IsV2 = false;
|
||||
|
||||
public List<int> SkinningIndices = new List<int>();
|
||||
|
||||
public void Read(FileReader reader, GFBMDL Root)
|
||||
{
|
||||
Skeleton = new STSkeleton();
|
||||
@ -165,29 +168,14 @@ namespace FirstPlugin
|
||||
reader.SetByteOrder(false);
|
||||
|
||||
Version = reader.ReadUInt32();
|
||||
Boundings = reader.ReadSingles(9);
|
||||
|
||||
//Temp check for valid bone header size
|
||||
using (reader.TemporarySeek(72, SeekOrigin.Begin))
|
||||
{
|
||||
long boneData = reader.ReadOffset(true, typeof(uint));
|
||||
if (boneData < reader.BaseStream.Length - 4)
|
||||
{
|
||||
reader.SeekBegin(boneData);
|
||||
reader.ReadUInt32(); //count
|
||||
long offset = reader.ReadOffset(true, typeof(uint));
|
||||
if (offset < reader.BaseStream.Length - 4)
|
||||
{
|
||||
reader.SeekBegin(offset);
|
||||
uint infoOffset = reader.ReadUInt32();
|
||||
if (infoOffset == 26 || infoOffset == 24)
|
||||
IsV2 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
ushort v2Check = reader.ReadUInt16();
|
||||
if (v2Check == 0)
|
||||
IsV2 = true;
|
||||
|
||||
if (IsV2)
|
||||
reader.ReadUInt32();
|
||||
reader.SeekBegin(0x28);
|
||||
else
|
||||
reader.SeekBegin(0x2C);
|
||||
|
||||
long TextureOffset = reader.ReadOffset(true, typeof(uint));
|
||||
long ShaderNameOffset = reader.ReadOffset(true, typeof(uint));
|
||||
@ -273,6 +261,9 @@ namespace FirstPlugin
|
||||
bone.parentIndex = 0;
|
||||
|
||||
Skeleton.bones.Add(bone);
|
||||
|
||||
if (bone.HasSkinning)
|
||||
SkinningIndices.Add(Skeleton.bones.IndexOf(bone));
|
||||
}
|
||||
|
||||
foreach (var bone in Skeleton.bones)
|
||||
@ -349,8 +340,12 @@ namespace FirstPlugin
|
||||
if (Buffer.Weights.Count > 0)
|
||||
vertex.boneWeights = new List<float>(Buffer.Weights[v]);
|
||||
if (Buffer.BoneIndex.Count > 0)
|
||||
vertex.boneIds = new List<int>(Buffer.BoneIndex[v]);
|
||||
// if (Buffer.Colors1.Count > 0)
|
||||
{
|
||||
var boneIndexList = Buffer.BoneIndex[v];
|
||||
for (int j = 0; j < boneIndexList.Length; j++)
|
||||
vertex.boneIds.Add(SkinningIndices[boneIndexList[j]]);
|
||||
}
|
||||
// if (Buffer.Colors1.Count > 0)
|
||||
// vertex.col = Buffer.Colors1[v] / 255f;
|
||||
if (Buffer.Binormals.Count > 0)
|
||||
vertex.bitan = Buffer.Binormals[v];
|
||||
@ -1249,6 +1244,8 @@ namespace FirstPlugin
|
||||
|
||||
public Bone(STSkeleton skeleton) : base(skeleton) { }
|
||||
|
||||
public bool HasSkinning = false;
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
long DataPosition = reader.Position;
|
||||
@ -1265,6 +1262,8 @@ namespace FirstPlugin
|
||||
BoneInfo = new BoneInfo();
|
||||
BoneInfo.Read(reader);
|
||||
|
||||
HasSkinning = BoneInfo.BoneRigFlag1 == 4;
|
||||
|
||||
RotationType = BoneRotationType.Euler;
|
||||
Checked = true;
|
||||
|
||||
@ -1326,8 +1325,8 @@ namespace FirstPlugin
|
||||
internal ushort ScalePosition { get; set; }
|
||||
internal ushort RotationPosition { get; set; }
|
||||
internal ushort TranslationPosition { get; set; }
|
||||
internal ushort Unknown4Position { get; set; }
|
||||
internal ushort Unknown5Position { get; set; }
|
||||
internal ushort BoneRigFlag2 { get; set; }
|
||||
internal ushort BoneRigFlag1 { get; set; }
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
@ -1341,8 +1340,8 @@ namespace FirstPlugin
|
||||
ScalePosition = reader.ReadUInt16();
|
||||
RotationPosition = reader.ReadUInt16();
|
||||
TranslationPosition = reader.ReadUInt16();
|
||||
Unknown4Position = reader.ReadUInt16(); //Padding
|
||||
Unknown5Position = reader.ReadUInt16(); //Padding
|
||||
BoneRigFlag2 = reader.ReadUInt16();
|
||||
BoneRigFlag1 = reader.ReadUInt16();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,20 +91,19 @@ namespace FirstPlugin
|
||||
sfd.Filter = "Supported Formats|*.dae;";
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
ExportModel(sfd.FileName);
|
||||
ExportModelSettings exportDlg = new ExportModelSettings();
|
||||
if (exportDlg.ShowDialog() == DialogResult.OK)
|
||||
ExportModel(sfd.FileName, exportDlg.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportModel(string FileName)
|
||||
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
|
||||
var model = new STGenericModel();
|
||||
model.Materials = new List<STGenericMaterial>();
|
||||
model.Objects = Renderer.Meshes;
|
||||
model.Materials = new List<STGenericMaterial>();
|
||||
model.Objects = Renderer.Meshes;
|
||||
|
||||
assimp.SaveFromModel(model, FileName, new List<STGenericTexture>(), new STSkeleton());
|
||||
DAE.Export(fileName, settings, model, new List<STGenericTexture>(), new STSkeleton());
|
||||
}
|
||||
|
||||
public Header GMXHeader;
|
||||
|
@ -58,29 +58,45 @@ namespace FirstPlugin
|
||||
public ToolStripItem[] GetContextMenuItems()
|
||||
{
|
||||
List<ToolStripItem> Items = new List<ToolStripItem>();
|
||||
Items.Add(new ToolStripMenuItem("Save", null, SaveAction, Keys.Control | Keys.S));
|
||||
Items.Add(new ToolStripMenuItem("Export", null, ExportAction, Keys.Control | Keys.E));
|
||||
return Items.ToArray();
|
||||
}
|
||||
|
||||
private void SaveAction(object sender, EventArgs e)
|
||||
{
|
||||
SaveFileDialog sfd = new SaveFileDialog();
|
||||
sfd.Filter = Utils.GetAllFilters(this);
|
||||
sfd.FileName = FileName;
|
||||
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
STFileSaver.SaveFileFormat(this, sfd.FileName);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportAction(object sender, EventArgs e)
|
||||
{
|
||||
SaveFileDialog sfd = new SaveFileDialog();
|
||||
sfd.Filter = "Supported Formats|*.dae;";
|
||||
if (sfd.ShowDialog() == DialogResult.OK) {
|
||||
ExportModel(sfd.FileName);
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
ExportModelSettings exportDlg = new ExportModelSettings();
|
||||
if (exportDlg.ShowDialog() == DialogResult.OK)
|
||||
ExportModel(sfd.FileName, exportDlg.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportModel(string FileName)
|
||||
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
|
||||
var model = new STGenericModel();
|
||||
model.Materials = Materials;
|
||||
model.Objects = Renderer.Meshes;
|
||||
var textures = new List<STGenericTexture>();
|
||||
foreach (var tex in Renderer.TextureList)
|
||||
textures.Add(tex);
|
||||
|
||||
assimp.SaveFromModel(model, FileName, new List<STGenericTexture>(), new STSkeleton());
|
||||
DAE.Export(fileName, settings, model, textures, Skeleton);
|
||||
}
|
||||
|
||||
bool DrawablesLoaded = false;
|
||||
@ -790,11 +806,25 @@ namespace FirstPlugin
|
||||
}
|
||||
}
|
||||
|
||||
private uint GetTotalIndexCount()
|
||||
{
|
||||
uint total = 0;
|
||||
foreach (var shape in SkeletalMeshChunk.ShapeChunk.SeperateShapes)
|
||||
{
|
||||
foreach (var prim in shape.Primatives)
|
||||
{
|
||||
foreach (var subprim in prim.Primatives) //Note 3DS usually only has one sub primative
|
||||
total += (uint)subprim.Indices.Length;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
public void Write(FileWriter writer, Header header)
|
||||
{
|
||||
long pos = writer.Position;
|
||||
|
||||
writer.Write(Indices != null ? Indices.Length : 0);
|
||||
writer.Write(GetTotalIndexCount());
|
||||
//Reserve space for all the offses
|
||||
writer.Write(0); //SkeletonChunk
|
||||
if (header.Version >= CMBVersion.MM3DS)
|
||||
@ -857,7 +887,38 @@ namespace FirstPlugin
|
||||
if (Indices != null && Indices.Length > 0)
|
||||
{
|
||||
writer.WriteUint32Offset(pos + (_offsetPos += 4));
|
||||
writer.Write(Indices);
|
||||
|
||||
long indexBufferPos = writer.Position;
|
||||
foreach (var shape in SkeletalMeshChunk.ShapeChunk.SeperateShapes)
|
||||
{
|
||||
foreach (var prim in shape.Primatives)
|
||||
{
|
||||
foreach (var subprim in prim.Primatives) //Note 3DS usually only has one sub primative
|
||||
{
|
||||
subprim.Indices = new uint[subprim.IndexCount];
|
||||
|
||||
writer.SeekBegin(indexBufferPos + subprim.Offset);
|
||||
|
||||
switch (subprim.IndexType)
|
||||
{
|
||||
case CmbDataType.UByte:
|
||||
for (int i = 0; i < subprim.IndexCount; i++)
|
||||
writer.Write((byte)subprim.Indices[i]);
|
||||
break;
|
||||
case CmbDataType.UShort:
|
||||
for (int i = 0; i < subprim.IndexCount; i++)
|
||||
writer.Write((ushort)subprim.Indices[i]);
|
||||
break;
|
||||
case CmbDataType.UInt:
|
||||
for (int i = 0; i < subprim.IndexCount; i++)
|
||||
writer.Write((uint)subprim.Indices[i]);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unsupported index type! " + subprim.IndexType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TextureChunk != null && TextureChunk.Textures.Count > 0)
|
||||
@ -894,8 +955,31 @@ namespace FirstPlugin
|
||||
|
||||
public void Write(FileWriter writer, Header header)
|
||||
{
|
||||
long pos = writer.Position;
|
||||
|
||||
writer.WriteSignature(Magic);
|
||||
writer.Write(uint.MaxValue);//SectionSize
|
||||
long _offsetPos = writer.Position;
|
||||
writer.Write(uint.MaxValue);//MeshChunk
|
||||
writer.Write(uint.MaxValue);//ShapeChunk
|
||||
|
||||
if (MeshChunk != null)
|
||||
{
|
||||
writer.WriteUint32Offset(_offsetPos);
|
||||
MeshChunk.Write(writer, header);
|
||||
}
|
||||
|
||||
if (ShapeChunk != null)
|
||||
{
|
||||
writer.WriteUint32Offset(_offsetPos + 4);
|
||||
ShapeChunk.Write(writer, header);
|
||||
}
|
||||
|
||||
|
||||
long endPos = writer.Position;
|
||||
using (writer.TemporarySeek(pos + 4, System.IO.SeekOrigin.Begin)) {
|
||||
writer.Write((uint)(endPos - pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -905,28 +989,31 @@ namespace FirstPlugin
|
||||
|
||||
public List<Mesh> Meshes = new List<Mesh>();
|
||||
|
||||
public uint Unknown;
|
||||
|
||||
public void Read(FileReader reader, Header header)
|
||||
{
|
||||
reader.ReadSignature(4, Magic);
|
||||
uint sectionSize = reader.ReadUInt32();
|
||||
uint meshCount = reader.ReadUInt32();
|
||||
uint unknown = reader.ReadUInt32();
|
||||
Unknown = reader.ReadUInt32();
|
||||
|
||||
long meshPos = reader.Position;
|
||||
for (int i = 0; i < meshCount; i++)
|
||||
{
|
||||
if (header.Version == CMBVersion.OOT3DS)
|
||||
reader.SeekBegin(meshPos + (i * 4));
|
||||
else if (header.Version == CMBVersion.MM3DS)
|
||||
reader.SeekBegin(meshPos + (i * 0x0C));
|
||||
else if (header.Version >= CMBVersion.LM3DS)
|
||||
reader.SeekBegin(meshPos + (i * 0x58));
|
||||
|
||||
Mesh mesh = new Mesh();
|
||||
|
||||
mesh.SepdIndex = reader.ReadUInt16();
|
||||
mesh.MaterialIndex = reader.ReadByte();
|
||||
Meshes.Add(mesh);
|
||||
|
||||
if (header.Version == CMBVersion.OOT3DS)
|
||||
mesh.unks = reader.ReadBytes(4);
|
||||
else if (header.Version == CMBVersion.MM3DS)
|
||||
mesh.unks = reader.ReadBytes(0x0C);
|
||||
else if (header.Version >= CMBVersion.LM3DS)
|
||||
mesh.unks = reader.ReadBytes(0x58);
|
||||
|
||||
Console.WriteLine($"SepdIndex {mesh.SepdIndex}");
|
||||
Console.WriteLine($"MaterialIndex { mesh.MaterialIndex}");
|
||||
}
|
||||
@ -934,17 +1021,29 @@ namespace FirstPlugin
|
||||
|
||||
public void Write(FileWriter writer, Header header)
|
||||
{
|
||||
long pos = writer.Position;
|
||||
|
||||
writer.WriteSignature(Magic);
|
||||
writer.Write(uint.MaxValue);//SectionSize
|
||||
writer.Write(Meshes.Count);
|
||||
for (int i = 0; i < Meshes.Count; i++)
|
||||
{
|
||||
writer.Write(Unknown);
|
||||
|
||||
for (int i = 0; i < Meshes.Count; i++) {
|
||||
writer.Write(Meshes[i].unks);
|
||||
writer.Write(Meshes[i].SepdIndex);
|
||||
writer.Write(Meshes[i].MaterialIndex);
|
||||
}
|
||||
|
||||
long endPos = writer.Position;
|
||||
using (writer.TemporarySeek(pos + 4, System.IO.SeekOrigin.Begin)) {
|
||||
writer.Write((uint)(endPos - pos));
|
||||
}
|
||||
}
|
||||
|
||||
public class Mesh
|
||||
{
|
||||
public byte[] unks;
|
||||
|
||||
public ushort SepdIndex { get; set; }
|
||||
public byte MaterialIndex { get; set; }
|
||||
}
|
||||
@ -970,7 +1069,7 @@ namespace FirstPlugin
|
||||
for (int i = 0; i < sepdCount; i++)
|
||||
{
|
||||
reader.SeekBegin(pos + offsets[i]);
|
||||
var sepd= new SeperateShape();
|
||||
var sepd = new SeperateShape();
|
||||
sepd.Read(reader, header);
|
||||
SeperateShapes.Add(sepd);
|
||||
}
|
||||
@ -978,9 +1077,24 @@ namespace FirstPlugin
|
||||
|
||||
public void Write(FileWriter writer, Header header)
|
||||
{
|
||||
long pos = writer.Position;
|
||||
|
||||
writer.WriteSignature(Magic);
|
||||
writer.Write(uint.MaxValue);//SectionSize
|
||||
writer.Write(SeperateShapes.Count);
|
||||
writer.Write(Unknown);
|
||||
long offsetPos = writer.Position;
|
||||
writer.Write(new ushort[SeperateShapes.Count]);
|
||||
for (int i = 0; i < SeperateShapes.Count; i++)
|
||||
{
|
||||
writer.WriteUint16Offset(offsetPos, pos);
|
||||
SeperateShapes[i].Write(writer, header);
|
||||
}
|
||||
|
||||
long endPos = writer.Position;
|
||||
using (writer.TemporarySeek(pos + 4, System.IO.SeekOrigin.Begin)) {
|
||||
writer.Write((uint)(endPos - pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1045,6 +1159,8 @@ namespace FirstPlugin
|
||||
|
||||
public void Write(FileWriter writer, Header header)
|
||||
{
|
||||
long pos = writer.Position;
|
||||
|
||||
writer.WriteSignature(Magic);
|
||||
writer.Write(uint.MaxValue); //section size
|
||||
writer.Write(Primatives.Count);
|
||||
@ -1061,6 +1177,11 @@ namespace FirstPlugin
|
||||
WriteVertexAttrib(writer, BoneIndices);
|
||||
WriteVertexAttrib(writer, BoneWeights);
|
||||
WriteVertexAttrib(writer, Tangent);
|
||||
|
||||
long endPos = writer.Position;
|
||||
using (writer.TemporarySeek(pos + 4, System.IO.SeekOrigin.Begin)) {
|
||||
writer.Write((uint)(endPos - pos));
|
||||
}
|
||||
}
|
||||
|
||||
private SepdVertexAttribute ReadVertexAttrib(FileReader reader)
|
||||
@ -1083,11 +1204,10 @@ namespace FirstPlugin
|
||||
return att;
|
||||
}
|
||||
|
||||
private void WriteVertexAttrib(FileWriter writer, SepdVertexAttribute attribute)
|
||||
private void WriteVertexAttrib(FileWriter writer, SepdVertexAttribute att)
|
||||
{
|
||||
long pos = writer.Position;
|
||||
|
||||
SepdVertexAttribute att = new SepdVertexAttribute();
|
||||
writer.Write(att.StartPosition);
|
||||
writer.Write(att.Scale);
|
||||
writer.Write(att.Type, true);
|
||||
@ -1149,8 +1269,32 @@ namespace FirstPlugin
|
||||
|
||||
public void Write(FileWriter writer, Header header)
|
||||
{
|
||||
long pos = writer.Position;
|
||||
|
||||
writer.WriteSignature(Magic);
|
||||
writer.Write(uint.MaxValue);//SectionSize
|
||||
writer.Write(Primatives.Count);
|
||||
writer.Write(SkinningMode, true);
|
||||
writer.Write((ushort)BoneIndexTable.Length);
|
||||
|
||||
long boneIndexOfsPos = writer.Position;
|
||||
writer.Write(uint.MaxValue); //bone index offset
|
||||
|
||||
long primativeOfsPos = writer.Position;
|
||||
writer.Write(uint.MaxValue); //primative offset
|
||||
|
||||
writer.WriteUint32Offset(boneIndexOfsPos, pos);
|
||||
writer.Write(BoneIndexTable);
|
||||
writer.Align(4);
|
||||
|
||||
writer.WriteUint32Offset(primativeOfsPos, pos);
|
||||
for (int i = 0; i < Primatives.Count; i++)
|
||||
Primatives[i].Write(writer, header);
|
||||
|
||||
long endPos = writer.Position;
|
||||
using (writer.TemporarySeek(pos + 4, System.IO.SeekOrigin.Begin)) {
|
||||
writer.Write((uint)(endPos - pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1179,14 +1323,17 @@ namespace FirstPlugin
|
||||
}
|
||||
}
|
||||
|
||||
public uint Unknown;
|
||||
public uint Unknown2;
|
||||
|
||||
public void Read(FileReader reader, Header header)
|
||||
{
|
||||
long pos = reader.Position;
|
||||
|
||||
reader.ReadSignature(4, Magic);
|
||||
uint sectionSize = reader.ReadUInt32();
|
||||
uint unknown = reader.ReadUInt32();
|
||||
uint unknown2 = reader.ReadUInt32();
|
||||
Unknown = reader.ReadUInt32();
|
||||
Unknown2 = reader.ReadUInt32();
|
||||
IndexType = reader.ReadEnum<CmbDataType>(true);
|
||||
reader.Seek(2); //padding
|
||||
|
||||
@ -1199,8 +1346,21 @@ namespace FirstPlugin
|
||||
|
||||
public void Write(FileWriter writer, Header header)
|
||||
{
|
||||
long pos = writer.Position;
|
||||
|
||||
writer.WriteSignature(Magic);
|
||||
writer.Write(uint.MaxValue);//SectionSize
|
||||
writer.Write(Unknown);
|
||||
writer.Write(Unknown2);
|
||||
writer.Write(IndexType, true);
|
||||
writer.Seek(2);
|
||||
writer.Write(IndexCount);
|
||||
writer.Write((ushort)(Offset / sizeof(ushort)));
|
||||
|
||||
long endPos = writer.Position;
|
||||
using (writer.TemporarySeek(pos + 4, System.IO.SeekOrigin.Begin)) {
|
||||
writer.Write((uint)(endPos - pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1220,9 +1380,16 @@ namespace FirstPlugin
|
||||
|
||||
public void Write(FileWriter writer, Header header)
|
||||
{
|
||||
long pos = writer.Position;
|
||||
|
||||
writer.WriteSignature(Magic);
|
||||
writer.Write(uint.MaxValue);//SectionSize
|
||||
writer.Write(data);
|
||||
|
||||
long endPos = writer.Position;
|
||||
using (writer.TemporarySeek(pos + 4, System.IO.SeekOrigin.Begin)) {
|
||||
writer.Write((uint)(endPos - pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1376,16 +1543,29 @@ namespace FirstPlugin
|
||||
{
|
||||
private const string Magic = "qtrs";
|
||||
|
||||
byte[] data;
|
||||
|
||||
public void Read(FileReader reader, Header header)
|
||||
{
|
||||
reader.ReadSignature(4, Magic);
|
||||
uint sectionSize = reader.ReadUInt32();
|
||||
|
||||
data = reader.getSection((uint)reader.Position, sectionSize);
|
||||
}
|
||||
|
||||
public void Write(FileWriter writer, Header header)
|
||||
{
|
||||
long pos = writer.Position;
|
||||
|
||||
writer.WriteSignature(Magic);
|
||||
writer.Write(uint.MaxValue);//SectionSize
|
||||
writer.Write(data);
|
||||
|
||||
long endPos = writer.Position;
|
||||
using (writer.TemporarySeek(pos + 4, System.IO.SeekOrigin.Begin))
|
||||
{
|
||||
writer.Write((uint)(endPos - pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
60
File_Format_Library/FileFormats/LM1/LM1_MDL.cs
Normal file
60
File_Format_Library/FileFormats/LM1/LM1_MDL.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox;
|
||||
using System.Windows.Forms;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public class LM1_MDL : IFileFormat
|
||||
{
|
||||
public FileType FileType { get; set; } = FileType.Model;
|
||||
|
||||
public bool CanSave { get; set; }
|
||||
public string[] Description { get; set; } = new string[] { "MDL" };
|
||||
public string[] Extension { get; set; } = new string[] { "*.mdl" };
|
||||
public string FileName { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public IFileInfo IFileInfo { get; set; }
|
||||
|
||||
public bool Identify(System.IO.Stream stream)
|
||||
{
|
||||
using (var reader = new Toolbox.Library.IO.FileReader(stream, true))
|
||||
{
|
||||
return reader.ReadUInt32() == 0x04B40000;
|
||||
}
|
||||
}
|
||||
|
||||
public Type[] Types
|
||||
{
|
||||
get
|
||||
{
|
||||
List<Type> types = new List<Type>();
|
||||
return types.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public void Load(System.IO.Stream stream)
|
||||
{
|
||||
using (var reader = new FileReader(stream))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Save(System.IO.Stream stream)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -100,9 +100,9 @@ namespace FirstPlugin
|
||||
model.Unload();
|
||||
}
|
||||
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
assimp.SaveFromModel(Objects, Materials, sfd.FileName, new List<STGenericTexture>(), Skeleton);
|
||||
if (settings.ShowDialog() == DialogResult.OK)
|
||||
DAE.Export(sfd.FileName, settings.Settings, Objects, Materials, new List<STGenericTexture>(), Skeleton);
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ namespace FirstPlugin
|
||||
string Path = System.IO.Path.Combine(folderDlg.SelectedPath,
|
||||
System.IO.Path.GetFileNameWithoutExtension(file) + ".dae");
|
||||
|
||||
model.ExportModel(Path);
|
||||
model.ExportModel(Path, new DAE.ExportSettings());
|
||||
model.Unload();
|
||||
}
|
||||
}
|
||||
@ -146,28 +146,23 @@ namespace FirstPlugin
|
||||
header.Read(new FileReader(stream), this);
|
||||
|
||||
ContextMenuStrip = new STContextMenuStrip();
|
||||
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Export Model", null, ExportModelAction, Keys.Control | Keys.E));
|
||||
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Export Model", null, ExportAction, Keys.Control | Keys.E));
|
||||
}
|
||||
|
||||
private void ExportModelAction(object sender, EventArgs args) {
|
||||
ExportModel();
|
||||
}
|
||||
|
||||
private void ExportModel()
|
||||
private void ExportAction(object sender, EventArgs args)
|
||||
{
|
||||
SaveFileDialog sfd = new SaveFileDialog();
|
||||
sfd.Filter = "Supported Formats|*.dae;";
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
ExportModel(sfd.FileName);
|
||||
ExportModelSettings exportDlg = new ExportModelSettings();
|
||||
if (exportDlg.ShowDialog() == DialogResult.OK)
|
||||
ExportModel(sfd.FileName, exportDlg.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportModel(string FileName)
|
||||
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
|
||||
List<STGenericMaterial> Materials = new List<STGenericMaterial>();
|
||||
foreach (STGenericMaterial mat in Nodes[0].Nodes)
|
||||
Materials.Add(mat);
|
||||
@ -176,7 +171,7 @@ namespace FirstPlugin
|
||||
model.Materials = Materials;
|
||||
model.Objects = ((GenericModelRenderer)DrawableContainer.Drawables[1]).Meshes;
|
||||
|
||||
assimp.SaveFromModel(model, FileName, new List<STGenericTexture>(), ((STSkeleton)DrawableContainer.Drawables[0]));
|
||||
DAE.Export(fileName, settings, model, new List<STGenericTexture>(), ((STSkeleton)DrawableContainer.Drawables[0]));
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
|
@ -100,9 +100,9 @@ namespace FirstPlugin
|
||||
model.Unload();
|
||||
}
|
||||
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
assimp.SaveFromModel(Objects, Materials, sfd.FileName, new List<STGenericTexture>(), Skeleton);
|
||||
if (settings.ShowDialog() == DialogResult.OK)
|
||||
DAE.Export(sfd.FileName, settings.Settings, Objects, Materials, new List<STGenericTexture>(), Skeleton);
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ namespace FirstPlugin
|
||||
string Path = System.IO.Path.Combine(folderDlg.SelectedPath,
|
||||
System.IO.Path.GetFileNameWithoutExtension(file) + ".dae");
|
||||
|
||||
model.ExportModel(Path);
|
||||
model.ExportModel(Path, new DAE.ExportSettings());
|
||||
model.Unload();
|
||||
}
|
||||
}
|
||||
@ -146,28 +146,23 @@ namespace FirstPlugin
|
||||
header.Read(new FileReader(stream), this);
|
||||
|
||||
ContextMenuStrip = new STContextMenuStrip();
|
||||
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Export Model", null, ExportModelAction, Keys.Control | Keys.E));
|
||||
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Export Model", null, ExportAction, Keys.Control | Keys.E));
|
||||
}
|
||||
|
||||
private void ExportModelAction(object sender, EventArgs args) {
|
||||
ExportModel();
|
||||
}
|
||||
|
||||
private void ExportModel()
|
||||
private void ExportAction(object sender, EventArgs args)
|
||||
{
|
||||
SaveFileDialog sfd = new SaveFileDialog();
|
||||
sfd.Filter = "Supported Formats|*.dae;";
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
ExportModel(sfd.FileName);
|
||||
ExportModelSettings exportDlg = new ExportModelSettings();
|
||||
if (exportDlg.ShowDialog() == DialogResult.OK)
|
||||
ExportModel(sfd.FileName, exportDlg.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportModel(string FileName)
|
||||
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
|
||||
List<STGenericMaterial> Materials = new List<STGenericMaterial>();
|
||||
foreach (STGenericMaterial mat in Nodes[0].Nodes)
|
||||
Materials.Add(mat);
|
||||
@ -176,9 +171,10 @@ namespace FirstPlugin
|
||||
model.Materials = Materials;
|
||||
model.Objects = ((GenericModelRenderer)DrawableContainer.Drawables[1]).Meshes;
|
||||
|
||||
assimp.SaveFromModel(model, FileName, new List<STGenericTexture>(), ((STSkeleton)DrawableContainer.Drawables[0]));
|
||||
DAE.Export(fileName, settings, model, new List<STGenericTexture>(), ((STSkeleton)DrawableContainer.Drawables[0]));
|
||||
}
|
||||
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
foreach (var mesh in ((GenericModelRenderer)DrawableContainer.Drawables[1]).Meshes)
|
||||
|
63
File_Format_Library/FileFormats/MarioParty/HSF.cs
Normal file
63
File_Format_Library/FileFormats/MarioParty/HSF.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox;
|
||||
using System.Windows.Forms;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public class HSF : IFileFormat
|
||||
{
|
||||
public FileType FileType { get; set; } = FileType.Resource;
|
||||
|
||||
public bool CanSave { get; set; }
|
||||
public string[] Description { get; set; } = new string[] { "Mario Party " };
|
||||
public string[] Extension { get; set; } = new string[] { "*.hsf" };
|
||||
public string FileName { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public IFileInfo IFileInfo { get; set; }
|
||||
|
||||
public bool Identify(System.IO.Stream stream)
|
||||
{
|
||||
using (var reader = new Toolbox.Library.IO.FileReader(stream, true))
|
||||
{
|
||||
return reader.CheckSignature(4, "HSFV");
|
||||
}
|
||||
}
|
||||
|
||||
public Type[] Types
|
||||
{
|
||||
get
|
||||
{
|
||||
List<Type> types = new List<Type>();
|
||||
return types.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public string Version;
|
||||
|
||||
public void Load(System.IO.Stream stream)
|
||||
{
|
||||
using (var reader = new FileReader(stream))
|
||||
{
|
||||
reader.SetByteOrder(true);
|
||||
reader.ReadSignature(3, "HSF");
|
||||
Version = reader.ReadString(4, Encoding.ASCII);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Save(System.IO.Stream stream)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -46,18 +46,13 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
|
||||
|
||||
private void ExportModel(string FileName)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
|
||||
List<STGenericMaterial> Materials = new List<STGenericMaterial>();
|
||||
// foreach (var msh in DataDictionary.Renderer.Meshes)
|
||||
// Materials.Add(msh.GetMaterial());
|
||||
|
||||
var model = new STGenericModel();
|
||||
model.Materials = Materials;
|
||||
model.Materials = new List<STGenericMaterial>();
|
||||
model.Objects = DataDictionary.Renderer.Meshes;
|
||||
|
||||
assimp.SaveFromModel(model, FileName, new List<STGenericTexture>(), new STSkeleton());
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
if (settings.ShowDialog() == DialogResult.OK)
|
||||
DAE.Export(FileName, settings.Settings, model, new List<STGenericTexture>());
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,34 +104,34 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
|
||||
public ToolStripItem[] GetContextMenuItems()
|
||||
{
|
||||
List<ToolStripItem> Items = new List<ToolStripItem>();
|
||||
Items.Add(new ToolStripMenuItem("Export", null, ExportModelAction, Keys.Control | Keys.E));
|
||||
Items.Add(new ToolStripMenuItem("Export", null, ExportAction, Keys.Control | Keys.E));
|
||||
return Items.ToArray();
|
||||
}
|
||||
|
||||
private void ExportModelAction(object sender, EventArgs args)
|
||||
|
||||
private void ExportAction(object sender, EventArgs args)
|
||||
{
|
||||
SaveFileDialog sfd = new SaveFileDialog();
|
||||
sfd.Filter = "Supported Formats|*.dae;";
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
ExportModel(sfd.FileName);
|
||||
ExportModelSettings exportDlg = new ExportModelSettings();
|
||||
if (exportDlg.ShowDialog() == DialogResult.OK)
|
||||
ExportModel(sfd.FileName, exportDlg.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportModel(string FileName)
|
||||
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
|
||||
List<STGenericMaterial> Materials = new List<STGenericMaterial>();
|
||||
// foreach (var msh in DataDictionary.Renderer.Meshes)
|
||||
// Materials.Add(msh.GetMaterial());
|
||||
foreach (STGenericMaterial mat in Nodes[0].Nodes)
|
||||
Materials.Add(mat);
|
||||
|
||||
var model = new STGenericModel();
|
||||
model.Materials = Materials;
|
||||
model.Objects = RenderedMeshes;
|
||||
|
||||
assimp.SaveFromModel(model, FileName, new List<STGenericTexture>(), new STSkeleton());
|
||||
DAE.Export(fileName, settings, model, new List<STGenericTexture>());
|
||||
}
|
||||
|
||||
|
||||
|
@ -40,24 +40,23 @@ namespace FirstPlugin.LuigisMansion3
|
||||
sfd.Filter = "Supported Formats|*.dae;";
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
ExportModel(sfd.FileName);
|
||||
ExportModelSettings exportDlg = new ExportModelSettings();
|
||||
if (exportDlg.ShowDialog() == DialogResult.OK)
|
||||
ExportModel(sfd.FileName, exportDlg.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportModel(string FileName)
|
||||
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
|
||||
List<STGenericMaterial> Materials = new List<STGenericMaterial>();
|
||||
// foreach (var msh in DataDictionary.Renderer.Meshes)
|
||||
// Materials.Add(msh.GetMaterial());
|
||||
foreach (STGenericMaterial mat in Nodes[0].Nodes)
|
||||
Materials.Add(mat);
|
||||
|
||||
var model = new STGenericModel();
|
||||
model.Materials = Materials;
|
||||
model.Objects = DataDictionary.Renderer.Meshes;
|
||||
|
||||
assimp.SaveFromModel(model, FileName, new List<STGenericTexture>(), new STSkeleton());
|
||||
DAE.Export(fileName, settings, model, new List<STGenericTexture>());
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,27 +156,25 @@ namespace FirstPlugin.LuigisMansion3
|
||||
sfd.Filter = "Supported Formats|*.dae;";
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
ExportModel(sfd.FileName);
|
||||
ExportModelSettings exportDlg = new ExportModelSettings();
|
||||
if (exportDlg.ShowDialog() == DialogResult.OK)
|
||||
ExportModel(sfd.FileName, exportDlg.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportModel(string FileName)
|
||||
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
|
||||
List<STGenericMaterial> Materials = new List<STGenericMaterial>();
|
||||
// foreach (var msh in DataDictionary.Renderer.Meshes)
|
||||
// Materials.Add(msh.GetMaterial());
|
||||
foreach (STGenericMaterial mat in Nodes[0].Nodes)
|
||||
Materials.Add(mat);
|
||||
|
||||
var model = new STGenericModel();
|
||||
model.Materials = Materials;
|
||||
model.Objects = RenderedMeshes;
|
||||
|
||||
assimp.SaveFromModel(model, FileName, new List<STGenericTexture>(), new STSkeleton());
|
||||
DAE.Export(fileName, settings, model, new List<STGenericTexture>());
|
||||
}
|
||||
|
||||
|
||||
public LM3_Model(LM3_DICT dict)
|
||||
{
|
||||
DataDictionary = dict;
|
||||
|
@ -102,24 +102,28 @@ namespace FirstPlugin.NLG
|
||||
sfd.Filter = "Supported Formats|*.dae;";
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
ExportModel(sfd.FileName);
|
||||
ExportModelSettings exportDlg = new ExportModelSettings();
|
||||
if (exportDlg.ShowDialog() == DialogResult.OK)
|
||||
ExportModel(sfd.FileName, exportDlg.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportModel(string FileName)
|
||||
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
|
||||
List<STGenericMaterial> Materials = new List<STGenericMaterial>();
|
||||
// foreach (var msh in DataDictionary.Renderer.Meshes)
|
||||
// Materials.Add(msh.GetMaterial());
|
||||
foreach (var mesh in Renderer.Meshes)
|
||||
if (mesh.GetMaterial() != null)
|
||||
Materials.Add(mesh.GetMaterial());
|
||||
|
||||
var textures = new List<STGenericTexture>();
|
||||
foreach (var tex in PluginRuntime.stikersTextures)
|
||||
textures.Add(tex);
|
||||
|
||||
var model = new STGenericModel();
|
||||
model.Materials = Materials;
|
||||
model.Objects = Renderer.Meshes;
|
||||
|
||||
assimp.SaveFromModel(model, FileName, new List<STGenericTexture>(), new STSkeleton());
|
||||
DAE.Export(fileName, settings, model, textures);
|
||||
}
|
||||
|
||||
public enum SectionMagic : uint
|
||||
|
@ -21,7 +21,7 @@ namespace FirstPlugin.NLG
|
||||
|
||||
public bool Identify(System.IO.Stream stream)
|
||||
{
|
||||
return Utils.GetExtension(FileName) == ".sanim";
|
||||
return Utils.GetExtension(FileName) == ".sanim" || FileName.Contains(".sanim.zlib");
|
||||
}
|
||||
|
||||
public Type[] Types
|
||||
|
@ -88,15 +88,14 @@ namespace FirstPlugin.PunchOutWii
|
||||
sfd.Filter = "Supported Formats|*.dae;";
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
ExportModel(sfd.FileName);
|
||||
ExportModelSettings exportDlg = new ExportModelSettings();
|
||||
if (exportDlg.ShowDialog() == DialogResult.OK)
|
||||
ExportModel(sfd.FileName, exportDlg.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportModel(string FileName)
|
||||
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
AssimpSaver assimp = new AssimpSaver();
|
||||
ExportModelSettings settings = new ExportModelSettings();
|
||||
|
||||
List<STGenericMaterial> Materials = new List<STGenericMaterial>();
|
||||
// foreach (var msh in DataDictionary.Renderer.Meshes)
|
||||
// Materials.Add(msh.GetMaterial());
|
||||
@ -105,7 +104,7 @@ namespace FirstPlugin.PunchOutWii
|
||||
model.Materials = Materials;
|
||||
model.Objects = RenderedMeshes;
|
||||
|
||||
assimp.SaveFromModel(model, FileName, new List<STGenericTexture>(), new STSkeleton());
|
||||
DAE.Export(fileName, settings, model, new List<STGenericTexture>(), new STSkeleton());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ namespace FirstPlugin
|
||||
public static string FTEX = GetFilter(".bftex", ".dds", ".dds2", ".png", ".bmp", ".tga", ".jpg", ".tiff", ".tif", ".gif");
|
||||
public static string GTX = GetFilter(".dds", ".dds2", ".png", ".bmp", ".tga", ".jpg", ".tiff", ".tif", ".gif");
|
||||
|
||||
public static string FMDL = GetFilter(".bfmdl", ".dae", ".fbx", ".obj", ".csv");
|
||||
public static string FMDL_EXPORT = GetFilter(".bfmdl", ".dae");
|
||||
public static string FMDL = GetFilter(".dae", ".bfmdl", ".fbx", ".obj", ".csv");
|
||||
public static string FMDL_EXPORT = GetFilter(".dae", ".bfmdl");
|
||||
public static string FSKL = GetFilter(".bfskl");
|
||||
public static string FSHP = GetFilter(".bfobj", ".dae");
|
||||
public static string BONE = GetFilter(".bfbon");
|
||||
|
@ -111,6 +111,7 @@ In the event that the tool cannot compile, check references. All the libraries a
|
||||
## Credits
|
||||
|
||||
- Smash Forge Devs (SMG, Ploaj, jam1garner, smb123w64gb, etc) for some code ported over. Specifically animation stuff and some rendering.
|
||||
- Ploaj for a base on the DAE writer.
|
||||
- Assimp devs for their massive asset library!
|
||||
- Wexos (helped figure out a few things, ie format list to assign each attribute)
|
||||
- JuPaHe64 for the base 3D renderer.
|
||||
|
@ -1,649 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Assimp;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.Animations;
|
||||
using Toolbox.Library.Rendering;
|
||||
using System.Windows.Forms;
|
||||
using System.Linq;
|
||||
|
||||
namespace Toolbox.Library
|
||||
{
|
||||
public class AssimpSaver
|
||||
{
|
||||
private List<string> ExtractedTextures = new List<string>();
|
||||
|
||||
public List<string> BoneNames = new List<string>();
|
||||
|
||||
STProgressBar progressBar;
|
||||
|
||||
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)
|
||||
{
|
||||
ExtractedTextures.Clear();
|
||||
|
||||
Scene scene = new Scene();
|
||||
scene.RootNode = new Node("RootNode");
|
||||
|
||||
progressBar = new STProgressBar();
|
||||
progressBar.Task = "Exporting Skeleton...";
|
||||
progressBar.Value = 0;
|
||||
progressBar.StartPosition = FormStartPosition.CenterScreen;
|
||||
progressBar.Show();
|
||||
progressBar.Refresh();
|
||||
|
||||
SaveSkeleton(skeleton, scene.RootNode);
|
||||
SaveMaterials(scene, Materials, FileName, Textures);
|
||||
|
||||
progressBar.Task = "Exporting Meshes...";
|
||||
progressBar.Value = 50;
|
||||
|
||||
SaveMeshes(scene, Meshes, skeleton, FileName, NodeArray);
|
||||
|
||||
progressBar.Task = "Saving File...";
|
||||
progressBar.Value = 80;
|
||||
|
||||
SaveScene(FileName, scene, Meshes);
|
||||
|
||||
progressBar.Value = 100;
|
||||
progressBar.Close();
|
||||
progressBar.Dispose();
|
||||
}
|
||||
|
||||
private void SaveScene(string FileName, Scene scene, List<STGenericObject> Meshes)
|
||||
{
|
||||
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";
|
||||
|
||||
bool ExportSuccessScene = v.ExportFile(scene, FileName, formatID, PostProcessSteps.FlipUVs);
|
||||
if (ExportSuccessScene)
|
||||
{
|
||||
if (ext == ".dae")
|
||||
WriteExtraSkinningInfo(FileName, scene, Meshes);
|
||||
|
||||
MessageBox.Show($"Exported {FileName} Successfuly!");
|
||||
}
|
||||
else
|
||||
MessageBox.Show($"Failed to export {FileName}!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void SaveMeshes(Scene scene, List<STGenericObject> Meshes, STSkeleton skeleton, string FileName, List<int> NodeArray)
|
||||
{
|
||||
int MeshIndex = 0;
|
||||
foreach (var obj in Meshes)
|
||||
{
|
||||
var mesh = SaveMesh((STGenericObject)obj, scene, MeshIndex++, skeleton, NodeArray);
|
||||
scene.Meshes.Add(mesh);
|
||||
}
|
||||
Node geomNode = new Node(Path.GetFileNameWithoutExtension(FileName), scene.RootNode);
|
||||
|
||||
for (int ob = 0; ob < scene.MeshCount; ob++)
|
||||
{
|
||||
geomNode.MeshIndices.Add(ob);
|
||||
|
||||
// if (!scene.Meshes[ob].HasBones)
|
||||
}
|
||||
|
||||
scene.RootNode.Children.Add(geomNode);
|
||||
}
|
||||
|
||||
private Mesh SaveMesh(STGenericObject genericObj, Scene scene, int index, STSkeleton skeleton, List<int> NodeArray)
|
||||
{
|
||||
//Assimp is weird so use mesh_# for the name. We'll change it back after save
|
||||
Mesh mesh = new Mesh($"mesh_{ index }", PrimitiveType.Triangle);
|
||||
|
||||
if (genericObj.MaterialIndex < scene.MaterialCount && genericObj.MaterialIndex > 0)
|
||||
mesh.MaterialIndex = genericObj.MaterialIndex;
|
||||
else
|
||||
mesh.MaterialIndex = 0;
|
||||
|
||||
List<Vector3D> textureCoords0 = new List<Vector3D>();
|
||||
List<Vector3D> textureCoords1 = new List<Vector3D>();
|
||||
List<Vector3D> textureCoords2 = new List<Vector3D>();
|
||||
List<Color4D> vertexColors = new List<Color4D>();
|
||||
|
||||
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));
|
||||
|
||||
if (skeleton != null)
|
||||
{
|
||||
for (int j = 0; j < v.boneIds.Count; j++)
|
||||
{
|
||||
if (j < genericObj.VertexSkinCount)
|
||||
{
|
||||
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]];
|
||||
|
||||
//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)
|
||||
{
|
||||
var matrices = Toolbox.Library.IO.MatrixExenstion.CalculateInverseMatrix(STbone);
|
||||
|
||||
//Set the inverse matrix
|
||||
Matrix4x4 transform = matrices.inverse.FromNumerics();
|
||||
|
||||
//Create a new assimp bone
|
||||
Bone bone = new Bone();
|
||||
bone.Name = STbone.Text;
|
||||
bone.OffsetMatrix = STbone.invert.ToMatrix4x4();
|
||||
mesh.Bones.Add(bone);
|
||||
BoneNames.Add(bone.Name);
|
||||
|
||||
boneInd = mesh.Bones.IndexOf(bone); //Set the index of the bone for the vertex weight
|
||||
}
|
||||
|
||||
int MinWeightAmount = 0;
|
||||
|
||||
//Check if the max amount of weights is higher than the current bone id
|
||||
if (v.boneWeights.Count > j && v.boneWeights[j] > MinWeightAmount)
|
||||
{
|
||||
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));
|
||||
}
|
||||
else if (v.boneWeights.Count == 0 || v.boneWeights[j] > MinWeightAmount)
|
||||
mesh.Bones[boneInd].VertexWeights.Add(new VertexWeight(vertexID, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
vertexID++;
|
||||
}
|
||||
|
||||
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++)
|
||||
if (f < polygonGroup.faces.Count - 2)
|
||||
mesh.Faces.Add(new Face(new int[] { polygonGroup.faces[f++], polygonGroup.faces[f++], polygonGroup.faces[f] }));
|
||||
}
|
||||
}
|
||||
|
||||
mesh.TextureCoordinateChannels.SetValue(textureCoords0, 0);
|
||||
mesh.TextureCoordinateChannels.SetValue(textureCoords1, 1);
|
||||
mesh.TextureCoordinateChannels.SetValue(textureCoords2, 2);
|
||||
mesh.VertexColorChannels.SetValue(vertexColors, 0);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
//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
|
||||
private void WriteExtraSkinningInfo(string FileName, Scene outScene, List<STGenericObject> Meshes)
|
||||
{
|
||||
StreamWriter test = new StreamWriter(FileName + ".tmp");
|
||||
StreamReader dae = File.OpenText(FileName);
|
||||
|
||||
int geomIndex = 0;
|
||||
while (!dae.EndOfStream)
|
||||
{
|
||||
string line = dae.ReadLine();
|
||||
|
||||
/* 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"))
|
||||
{
|
||||
string RealMeshName = Meshes[geomIndex].Text;
|
||||
test.WriteLine($" <geometry id=\"meshId{ geomIndex }\" name=\"{ RealMeshName }\" > ");
|
||||
test.Flush();
|
||||
|
||||
geomIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
test.WriteLine(line);
|
||||
test.Flush();
|
||||
}
|
||||
|
||||
/* else if (line.Contains("<matrix"))
|
||||
{
|
||||
string matLine = line.Replace("<matrix>", "<matrix sid=\"matrix\">");
|
||||
test.WriteLine(matLine);
|
||||
test.Flush();
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
private void SaveMaterials(Scene scene, List<STGenericMaterial> Materials, string FileName, List<STGenericTexture> Textures)
|
||||
{
|
||||
string TextureExtension = ".png";
|
||||
string TexturePath = System.IO.Path.GetDirectoryName(FileName);
|
||||
|
||||
for (int i = 0; i < Textures.Count; i++)
|
||||
{
|
||||
string path = System.IO.Path.Combine(TexturePath, Textures[i].Text + TextureExtension);
|
||||
|
||||
if (!ExtractedTextures.Contains(path))
|
||||
{
|
||||
ExtractedTextures.Add(path);
|
||||
|
||||
progressBar.Task = $"Exporting Texture {Textures[i].Text}";
|
||||
progressBar.Value = ((i * 100) / Textures.Count);
|
||||
progressBar.Refresh();
|
||||
|
||||
var bitmap = Textures[i].GetBitmap();
|
||||
bitmap.Save(path);
|
||||
bitmap.Dispose();
|
||||
|
||||
GC.Collect();
|
||||
}
|
||||
}
|
||||
|
||||
if (Materials.Count == 0)
|
||||
{
|
||||
Material material = new Material();
|
||||
material.Name = "New Material";
|
||||
scene.Materials.Add(material);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var mat in Materials)
|
||||
{
|
||||
var genericMat = (STGenericMaterial)mat;
|
||||
|
||||
Material material = new Material();
|
||||
material.Name = genericMat.Text;
|
||||
|
||||
foreach (var tex in genericMat.TextureMaps)
|
||||
{
|
||||
int index = Textures.FindIndex(r => r.Text.Equals(tex.Name));
|
||||
|
||||
string path = System.IO.Path.Combine(TexturePath, tex.Name + TextureExtension);
|
||||
|
||||
if (!File.Exists(path))
|
||||
continue;
|
||||
|
||||
TextureSlot slot2 = new TextureSlot(path, ConvertToAssimpTextureType(tex.Type), 0, TextureMapping.FromUV,
|
||||
0, 1.0f, Assimp.TextureOperation.Add, ConvertToAssimpWrapType(tex.WrapModeS), ConvertToAssimpWrapType(tex.WrapModeT), 0);
|
||||
|
||||
material.AddMaterialTexture(ref slot2);
|
||||
}
|
||||
scene.Materials.Add(material);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static Assimp.TextureWrapMode ConvertToAssimpWrapType(STTextureWrapMode type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case STTextureWrapMode.Repeat: return TextureWrapMode.Wrap;
|
||||
case STTextureWrapMode.Mirror: return TextureWrapMode.Mirror;
|
||||
case STTextureWrapMode.Clamp: return TextureWrapMode.Clamp;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveFromObject(STGenericObject genericObject, string FileName)
|
||||
{
|
||||
Scene scene = new Scene();
|
||||
scene.RootNode = new Node("Root");
|
||||
|
||||
var mesh = SaveMesh(genericObject, scene, 0, null, null);
|
||||
mesh.MaterialIndex = 0;
|
||||
scene.Meshes.Add(mesh);
|
||||
|
||||
Material material = new Material();
|
||||
material.Name = "NewMaterial";
|
||||
scene.Materials.Add(material);
|
||||
|
||||
SaveScene(FileName, scene, new List<STGenericObject>() { genericObject });
|
||||
}
|
||||
|
||||
private void SaveSkeleton(STSkeleton skeleton, Node parentNode)
|
||||
{
|
||||
Node root = new Node("skeleton_root");
|
||||
parentNode.Children.Add(root);
|
||||
|
||||
Console.WriteLine($"bones {skeleton.bones.Count}");
|
||||
|
||||
if (skeleton.bones.Count > 0)
|
||||
{
|
||||
foreach (var bone in skeleton.bones)
|
||||
{
|
||||
//Get each root bone and find children
|
||||
if (bone.parentIndex == -1)
|
||||
{
|
||||
Node boneNode = new Node(bone.Text);
|
||||
boneNode.Transform = AssimpHelper.GetBoneMatrix(bone);
|
||||
root.Children.Add(boneNode);
|
||||
|
||||
foreach (STBone child in bone.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);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -10,11 +10,31 @@ namespace Toolbox.Library.Collada
|
||||
{
|
||||
public class ColladaWriter : IDisposable
|
||||
{
|
||||
//Based around this nice Writer https://raw.githubusercontent.com/Ploaj/SSBHLib/master/CrossMod/IO/DAEWriter.cs
|
||||
//With adjustments and expanded usage.
|
||||
|
||||
private XmlTextWriter Writer;
|
||||
private DAE.ExportSettings Settings;
|
||||
private DAE.Version Version;
|
||||
|
||||
private Dictionary<string, int> AttriubteIdList = new Dictionary<string, int>();
|
||||
private List<Tuple<string, SemanticType, TriangleList[], int>> GeometrySources = new List<Tuple<string, SemanticType, TriangleList[], int>>();
|
||||
|
||||
private Dictionary<string, int> AttributeIdList = new Dictionary<string, int>();
|
||||
|
||||
private Dictionary<string, Tuple<List<int[]>, List<float[]>>> GeometryControllers = new Dictionary<string, Tuple<List<int[]>, List<float[]>>>();
|
||||
|
||||
//Keep a dictionary of names and assigned id names
|
||||
private Dictionary<string, string> MeshIdList = new Dictionary<string, string>();
|
||||
private Dictionary<string, List<string>> MaterialIdList = new Dictionary<string, List<string>>();
|
||||
|
||||
private Dictionary<string, string> MeshSkinIdList = new Dictionary<string, string>();
|
||||
|
||||
private List<Joint> Joints = new List<Joint>();
|
||||
|
||||
public string CurrentGeometryID;
|
||||
public string CurrentMaterial;
|
||||
|
||||
public bool Optimize = false;
|
||||
|
||||
public ColladaWriter(string fileName, DAE.ExportSettings settings)
|
||||
{
|
||||
@ -25,6 +45,8 @@ namespace Toolbox.Library.Collada
|
||||
Formatting = Formatting.Indented,
|
||||
Indentation = 2,
|
||||
};
|
||||
|
||||
WriteDAEHeader();
|
||||
}
|
||||
|
||||
public void WriteDAEHeader()
|
||||
@ -58,8 +80,16 @@ namespace Toolbox.Library.Collada
|
||||
Writer.WriteEndElement();
|
||||
|
||||
Writer.WriteStartElement("unit");
|
||||
Writer.WriteAttributeString("meter", "0.01");
|
||||
Writer.WriteAttributeString("name", "centimeter");
|
||||
if (Settings.Preset == ProgramPreset.MAX)
|
||||
{
|
||||
Writer.WriteAttributeString("meter", "1");
|
||||
Writer.WriteAttributeString("name", "centimeter");
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteAttributeString("meter", "0.01");
|
||||
Writer.WriteAttributeString("name", "centimeter");
|
||||
}
|
||||
Writer.WriteEndElement();
|
||||
|
||||
Writer.WriteStartElement("up_axis");
|
||||
@ -69,7 +99,7 @@ namespace Toolbox.Library.Collada
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public void WriteLibraryImages(string[] textureNames)
|
||||
public void WriteLibraryImages(string[] textureNames = null)
|
||||
{
|
||||
Writer.WriteStartElement("library_images");
|
||||
for (int i = 0; i < textureNames?.Length; i++)
|
||||
@ -77,7 +107,7 @@ namespace Toolbox.Library.Collada
|
||||
Writer.WriteStartElement("image");
|
||||
Writer.WriteAttributeString("id", textureNames[i]);
|
||||
Writer.WriteStartElement("init_from");
|
||||
Writer.WriteString($"{Settings.ImageFolder}{textureNames[i]}.{Settings.ImageExtension}");
|
||||
Writer.WriteString($"{Settings.ImageFolder}{textureNames[i]}{Settings.ImageExtension}");
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
@ -89,7 +119,11 @@ namespace Toolbox.Library.Collada
|
||||
Writer.WriteStartElement("library_materials");
|
||||
for (int i = 0; i < materials?.Count; i++)
|
||||
{
|
||||
Writer.WriteStartElement("library_effects");
|
||||
Writer.WriteStartElement("material");
|
||||
Writer.WriteAttributeString("id", materials[i].Name);
|
||||
Writer.WriteStartElement("instance_effect");
|
||||
Writer.WriteAttributeString("url", "#Effect_" + materials[i].Name);
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
Writer.WriteEndElement();
|
||||
@ -100,20 +134,842 @@ namespace Toolbox.Library.Collada
|
||||
Writer.WriteStartElement("library_effects");
|
||||
for (int i = 0; i < materials?.Count; i++)
|
||||
{
|
||||
Writer.WriteStartElement("library_effects");
|
||||
Writer.WriteStartElement("effect");
|
||||
Writer.WriteAttributeString("id", $"Effect_{materials[i].Name}");
|
||||
Writer.WriteStartElement("profile_COMMON");
|
||||
{
|
||||
for (int t = 0; t < materials[i].Textures.Count; t++)
|
||||
{
|
||||
var tex = materials[i].Textures[t];
|
||||
//Here we write 2 things. Surface then sampler info
|
||||
|
||||
//Surface info
|
||||
Writer.WriteStartElement("newparam");
|
||||
Writer.WriteAttributeString("sid", $"surface_{materials[i].Name}-{tex.Type}");
|
||||
Writer.WriteStartElement("surface");
|
||||
Writer.WriteAttributeString("type", "2D");
|
||||
{
|
||||
Writer.WriteStartElement("init_from");
|
||||
Writer.WriteString(tex.Name);
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteStartElement("format");
|
||||
//Formats are uncompressed types.
|
||||
//Todo these should be adjusted for DDS
|
||||
Writer.WriteString("A8R8G8B8");
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
|
||||
{
|
||||
//Sampler info
|
||||
Writer.WriteStartElement("newparam");
|
||||
Writer.WriteAttributeString("sid", $"sampler_{materials[i].Name}-{tex.Type}");
|
||||
Writer.WriteStartElement("sampler2D");
|
||||
|
||||
Writer.WriteStartElement("source");
|
||||
Writer.WriteString($"surface_{materials[i].Name}-{tex.Type}");
|
||||
Writer.WriteEndElement();
|
||||
|
||||
//Add sampler wrap modes
|
||||
Writer.WriteStartElement("wrap_s");
|
||||
Writer.WriteString(tex.WrapModeS.ToString());
|
||||
Writer.WriteEndElement();
|
||||
|
||||
Writer.WriteStartElement("wrap_t");
|
||||
Writer.WriteString(tex.WrapModeT.ToString());
|
||||
Writer.WriteEndElement();
|
||||
|
||||
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Now write texture info
|
||||
//Todo support PBR workflow
|
||||
|
||||
Writer.WriteStartElement("technique");
|
||||
Writer.WriteAttributeString("sid", "common");
|
||||
Writer.WriteStartElement("phong");
|
||||
|
||||
bool HasSpecular = false;
|
||||
bool HasEmission = false;
|
||||
bool HasDiffuse = false;
|
||||
|
||||
for (int t = 0; t < materials[i].Textures.Count; t++)
|
||||
{
|
||||
var tex = materials[i].Textures[t];
|
||||
|
||||
if (tex.Type == PhongTextureType.diffuse)
|
||||
HasDiffuse = true;
|
||||
if (tex.Type == PhongTextureType.emission)
|
||||
HasEmission = true;
|
||||
if (tex.Type == PhongTextureType.specular)
|
||||
HasSpecular = true;
|
||||
|
||||
Writer.WriteStartElement($"{tex.Type}");
|
||||
Writer.WriteStartElement("texture");
|
||||
Writer.WriteAttributeString("texture", $"sampler_{materials[i].Name}-{tex.Type}");
|
||||
Writer.WriteAttributeString("texcoord", $"{tex.TextureChannel}");
|
||||
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
if (!HasDiffuse)
|
||||
WriteMaterialColor("diffuse", new float[] { 1, 1, 1, 1 });
|
||||
if (!HasEmission)
|
||||
WriteMaterialColor("emission", new float[] { 0, 0, 0, 1 });
|
||||
if (!HasSpecular)
|
||||
WriteMaterialColor("specular", new float[] { 0, 0, 0, 1 });
|
||||
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public void WriteMaterialColor(string type, float[] color)
|
||||
{
|
||||
Writer.WriteStartElement(type);
|
||||
Writer.WriteStartElement("color");
|
||||
Writer.WriteAttributeString("sid", type);
|
||||
Writer.WriteString(string.Join(" ", color));
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public void StartLibraryGeometries() {
|
||||
Writer.WriteStartElement("library_geometries");
|
||||
}
|
||||
|
||||
public void StartGeometry(string name)
|
||||
{
|
||||
CurrentGeometryID = GetUniqueID($"{name}-mesh");
|
||||
MeshSkinIdList.Add(CurrentGeometryID, "");
|
||||
MeshIdList.Add(CurrentGeometryID, name);
|
||||
Writer.WriteStartElement("geometry");
|
||||
Writer.WriteAttributeString("id", CurrentGeometryID);
|
||||
Writer.WriteAttributeString("name", name);
|
||||
Writer.WriteStartElement("mesh");
|
||||
}
|
||||
|
||||
private void OptimizeSource<T>(T[] values, TriangleList[] triangleLists, int stride, out T[] newValues, out TriangleList[] newIndices) where T : struct
|
||||
{
|
||||
var optimizedIndices = new List<uint>();
|
||||
var optimizedValues = new List<T>();
|
||||
|
||||
// Use a special class to store values, so we can have the performance benefits of a dictionary.
|
||||
var vertexBank = new Dictionary<ValueContainer<T>, uint>();
|
||||
|
||||
for (int t = 0; t < triangleLists.Length; t++)
|
||||
{
|
||||
for (int i = 0; i < triangleLists[t].Indices.Count; i++)
|
||||
{
|
||||
var vertexValues = new ValueContainer<T>(GetVertexValues(values, triangleLists[t].Indices.ToArray(), stride, i));
|
||||
|
||||
if (!vertexBank.ContainsKey(vertexValues))
|
||||
{
|
||||
uint index = (uint)vertexBank.Count;
|
||||
optimizedIndices.Add(index);
|
||||
vertexBank.Add(vertexValues, index);
|
||||
optimizedValues.AddRange(vertexValues.Values);
|
||||
}
|
||||
else
|
||||
{
|
||||
optimizedIndices.Add(vertexBank[vertexValues]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
triangleLists[t].Indices = optimizedIndices;
|
||||
}
|
||||
|
||||
newIndices = triangleLists;
|
||||
newValues = optimizedValues.ToArray();
|
||||
}
|
||||
|
||||
private static T[] GetVertexValues<T>(T[] values, uint[] indices, int stride, int i)
|
||||
{
|
||||
var vertexValues = new T[stride];
|
||||
for (int j = 0; j < stride; j++)
|
||||
vertexValues[j] = values[indices[i] * stride + j];
|
||||
|
||||
return vertexValues;
|
||||
}
|
||||
|
||||
public void WriteGeometrySource(string name, SemanticType semantic, float[] values, TriangleList[] indices, int set = -1)
|
||||
{
|
||||
int stride = GetStride(semantic);
|
||||
|
||||
if (Optimize)
|
||||
OptimizeSource(values, indices, stride, out values, out indices);
|
||||
|
||||
string sourceid = GetUniqueID(name + "-" + semantic.ToString().ToLower());
|
||||
Writer.WriteStartElement("source");
|
||||
Writer.WriteAttributeString("id", sourceid);
|
||||
|
||||
Writer.WriteStartElement("float_array");
|
||||
string FloatArrayID = GetUniqueID(name + "-" + semantic.ToString().ToLower() + "-array");
|
||||
Writer.WriteAttributeString("id", FloatArrayID);
|
||||
Writer.WriteAttributeString("count", values.Length.ToString());
|
||||
Writer.WriteString(string.Join(" ", values));
|
||||
Writer.WriteEndElement();
|
||||
|
||||
Writer.WriteStartElement("technique_common");
|
||||
{
|
||||
Writer.WriteStartElement("accessor");
|
||||
Writer.WriteAttributeString("source", $"#{FloatArrayID}");
|
||||
Writer.WriteAttributeString("count", (values.Length / stride).ToString());
|
||||
Writer.WriteAttributeString("stride", stride.ToString());
|
||||
if (semantic == SemanticType.NORMAL || semantic == SemanticType.POSITION)
|
||||
{
|
||||
WriteParam("X", "float");
|
||||
WriteParam("Y", "float");
|
||||
WriteParam("Z", "float");
|
||||
}
|
||||
if (semantic == SemanticType.TEXCOORD)
|
||||
{
|
||||
WriteParam("S", "float");
|
||||
WriteParam("T", "float");
|
||||
}
|
||||
if (semantic == SemanticType.COLOR)
|
||||
{
|
||||
WriteParam("R", "float");
|
||||
WriteParam("G", "float");
|
||||
WriteParam("B", "float");
|
||||
WriteParam("A", "float");
|
||||
}
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
|
||||
GeometrySources.Add(new Tuple<string, SemanticType, TriangleList[], int>(sourceid, semantic, indices, set));
|
||||
}
|
||||
|
||||
private int GetStride(SemanticType semantic) {
|
||||
if (semantic == SemanticType.COLOR)
|
||||
return 4;
|
||||
else if (semantic == SemanticType.TEXCOORD)
|
||||
return 2;
|
||||
else if (semantic == SemanticType.JOINT)
|
||||
return 1;
|
||||
else if (semantic == SemanticType.INV_BIND_MATRIX)
|
||||
return 16;
|
||||
else if (semantic == SemanticType.WEIGHT)
|
||||
return 1;
|
||||
else
|
||||
return 3;
|
||||
}
|
||||
|
||||
private void WriteParam(string Name, string Type)
|
||||
{
|
||||
Writer.WriteStartElement("param");
|
||||
Writer.WriteAttributeString("name", Name);
|
||||
Writer.WriteAttributeString("type", Type);
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public void EndGeometryMesh()
|
||||
{
|
||||
Tuple<string, SemanticType, TriangleList[], int> Position = null;
|
||||
foreach (var v in GeometrySources)
|
||||
{
|
||||
if (v.Item2 == SemanticType.POSITION)
|
||||
{
|
||||
Position = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// can only write vertex information if there is at least position data
|
||||
if (Position != null)
|
||||
{
|
||||
// vertices
|
||||
string verticesid = GetUniqueID(Position.Item1.Replace("position", "vertex"));
|
||||
Writer.WriteStartElement("vertices");
|
||||
Writer.WriteAttributeString("id", verticesid);
|
||||
WriteInput(Position.Item2.ToString(), Position.Item1);
|
||||
Writer.WriteEndElement();
|
||||
|
||||
List<string> triangleMaterials = new List<string>();
|
||||
|
||||
// triangles
|
||||
int triIndex = 0;
|
||||
foreach (TriangleList triangleList in Position.Item3)
|
||||
{
|
||||
Writer.WriteStartElement("triangles");
|
||||
if (triangleList.Material != "")
|
||||
{
|
||||
Writer.WriteAttributeString("material", $"{triangleList.Material}");
|
||||
triangleMaterials.Add(triangleList.Material);
|
||||
}
|
||||
else if (CurrentMaterial != "")
|
||||
{
|
||||
Writer.WriteAttributeString("material", $"{CurrentMaterial}");
|
||||
triangleMaterials.Add(CurrentMaterial);
|
||||
CurrentMaterial = "";
|
||||
}
|
||||
|
||||
Writer.WriteAttributeString("count", (triangleList.Indices.Count / 3).ToString());
|
||||
WriteInput("VERTEX", $"{verticesid}", 0);
|
||||
int offset = 1;
|
||||
foreach (var v in GeometrySources)
|
||||
if (v.Item2 != SemanticType.POSITION)
|
||||
{
|
||||
WriteInput(v.Item2.ToString(), $"{v.Item1}", offset++, v.Item4);
|
||||
}
|
||||
// write p
|
||||
StringBuilder p = new StringBuilder();
|
||||
for (int i = 0; i < triangleList.Indices.Count; i++)
|
||||
{
|
||||
p.Append(triangleList.Indices[i] + " ");
|
||||
foreach (var v in GeometrySources)
|
||||
if (v.Item2 != SemanticType.POSITION)
|
||||
p.Append(v.Item3[triIndex].Indices[i] + " ");
|
||||
}
|
||||
Writer.WriteStartElement("p");
|
||||
Writer.WriteString(p.ToString());
|
||||
Writer.WriteEndElement();
|
||||
|
||||
Writer.WriteEndElement();
|
||||
|
||||
triIndex++;
|
||||
}
|
||||
|
||||
MaterialIdList.Add(CurrentGeometryID, triangleMaterials);
|
||||
}
|
||||
|
||||
GeometrySources.Clear();
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public void WriteInput(string semantic, string sourceid, int offset = -1, int set = -1)
|
||||
{
|
||||
Writer.WriteStartElement("input");
|
||||
Writer.WriteAttributeString("semantic", semantic);
|
||||
Writer.WriteAttributeString("source", $"#{sourceid}");
|
||||
if (offset != -1)
|
||||
Writer.WriteAttributeString("offset", offset.ToString());
|
||||
if (set != -1)
|
||||
Writer.WriteAttributeString("set", set.ToString());
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A function for automatically creating and handling the controllers
|
||||
/// The Joints must be added before this function is used
|
||||
/// </summary>
|
||||
/// <param name="BoneIndices">A list of arrays of bone indices for each vertex</param>
|
||||
/// <param name="Weights">A list that contains an array of weights per vertex</param>
|
||||
public void AttachGeometryController(List<int[]> BoneIndices, List<float[]> Weights)
|
||||
{
|
||||
GeometryControllers.Add(CurrentGeometryID, new Tuple<List<int[]>, List<float[]>>(BoneIndices, Weights));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends Writing the Geometry Section
|
||||
/// </summary>
|
||||
public void EndGeometrySection()
|
||||
{
|
||||
Writer.WriteEndElement();
|
||||
|
||||
CreateControllerSection();
|
||||
|
||||
CreateVisualNodeSection();
|
||||
}
|
||||
|
||||
private void RecursivlyWriteJoints(Joint joint)
|
||||
{
|
||||
Writer.WriteStartElement("node");
|
||||
Writer.WriteAttributeString("id", $"Armature_{joint.Name}");
|
||||
Writer.WriteAttributeString("name", joint.Name);
|
||||
Writer.WriteAttributeString("sid", joint.Name);
|
||||
Writer.WriteAttributeString("type", "JOINT");
|
||||
|
||||
Writer.WriteStartElement("matrix");
|
||||
Writer.WriteAttributeString("sid", "transform");
|
||||
Writer.WriteString(string.Join(" ", joint.Transform));
|
||||
Writer.WriteEndElement();
|
||||
|
||||
foreach (var child in GetChildren(joint))
|
||||
{
|
||||
RecursivlyWriteJoints(child);
|
||||
}
|
||||
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
private Joint[] GetChildren(Joint j)
|
||||
{
|
||||
int parentindex = Joints.IndexOf(j);
|
||||
List<Joint> Children = new List<Joint>();
|
||||
foreach (var child in Joints)
|
||||
{
|
||||
if (child.ParentIndex == parentindex)
|
||||
Children.Add(child);
|
||||
}
|
||||
return Children.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the library controller section
|
||||
/// </summary>
|
||||
public void BeginLibraryControllers()
|
||||
{
|
||||
Writer.WriteStartElement("library_controllers");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// automatically generates the controller nodes
|
||||
/// </summary>
|
||||
private void CreateControllerSection()
|
||||
{
|
||||
BeginLibraryControllers();
|
||||
|
||||
foreach (var v in GeometryControllers.Keys)
|
||||
{
|
||||
WriteLibraryController(v);
|
||||
}
|
||||
|
||||
EndLibraryControllers();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new joint to the default skeletal tree
|
||||
/// </summary>
|
||||
public void AddJoint(string name, string parentName, float[] Transform, float[] InvWorldTransform)
|
||||
{
|
||||
Joint j = new Joint();
|
||||
j.Name = name;
|
||||
j.Transform = Transform;
|
||||
j.BindPose = InvWorldTransform;
|
||||
foreach (var joint in Joints)
|
||||
if (joint.Name.Equals(parentName))
|
||||
j.ParentIndex = Joints.IndexOf(joint);
|
||||
Joints.Add(j);
|
||||
}
|
||||
|
||||
public void WriteLibraryController(string Name)
|
||||
{
|
||||
if (!GeometryControllers.ContainsKey(Name)) return;
|
||||
|
||||
var BoneWeight = GeometryControllers[Name];
|
||||
string SkinID = $"Armature_{Name}";
|
||||
MeshSkinIdList[Name] = SkinID;
|
||||
|
||||
Writer.WriteStartElement("controller");
|
||||
Writer.WriteAttributeString("id", SkinID);
|
||||
|
||||
Writer.WriteStartElement("skin");
|
||||
Writer.WriteAttributeString("source", $"#{Name}");
|
||||
{
|
||||
Writer.WriteStartElement("bind_shape_matrix");
|
||||
Writer.WriteString("1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1");
|
||||
Writer.WriteEndElement();
|
||||
|
||||
object[] BoneNames = new string[Joints.Count];
|
||||
object[] InvBinds = new object[Joints.Count * 16];
|
||||
for (int i = 0; i < BoneNames.Length; i++)
|
||||
{
|
||||
BoneNames[i] = Joints[i].Name;
|
||||
for (int j = 0; j < 16; j++)
|
||||
{
|
||||
InvBinds[i * 16 + j] = Joints[i].BindPose[j];
|
||||
}
|
||||
}
|
||||
var Weights = new List<object>();
|
||||
var WeightIndices = new List<int>();
|
||||
foreach (var v in BoneWeight.Item2)
|
||||
{
|
||||
foreach (var w in v)
|
||||
{
|
||||
int index = Weights.IndexOf(w);
|
||||
if (index == -1)
|
||||
{
|
||||
WeightIndices.Add(Weights.Count);
|
||||
Weights.Add(w);
|
||||
}
|
||||
else
|
||||
{
|
||||
WeightIndices.Add(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
string Jointid = WriteSkinSource(Name, SemanticType.JOINT, BoneNames);
|
||||
string Bindid = WriteSkinSource(Name, SemanticType.INV_BIND_MATRIX, InvBinds);
|
||||
string Weightid = WriteSkinSource(Name, SemanticType.WEIGHT, Weights.ToArray());
|
||||
|
||||
|
||||
Writer.WriteStartElement("joints");
|
||||
WriteInput(SemanticType.JOINT.ToString(), Jointid);
|
||||
WriteInput(SemanticType.INV_BIND_MATRIX.ToString(), Bindid);
|
||||
Writer.WriteEndElement();
|
||||
|
||||
Writer.WriteStartElement("vertex_weights");
|
||||
Writer.WriteAttributeString("count", BoneWeight.Item1.Count.ToString());
|
||||
WriteInput(SemanticType.JOINT.ToString(), Jointid, 0);
|
||||
WriteInput(SemanticType.WEIGHT.ToString(), Weightid, 1);
|
||||
|
||||
// now writing out the counts and such...
|
||||
//vcount
|
||||
{
|
||||
StringBuilder values = new StringBuilder();
|
||||
foreach (var v in BoneWeight.Item1)
|
||||
{
|
||||
values.Append($"{v.Length} ");
|
||||
}
|
||||
Writer.WriteStartElement("vcount");
|
||||
Writer.WriteString(values.ToString());
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
//v
|
||||
{
|
||||
StringBuilder values = new StringBuilder();
|
||||
int weightindexcount = 0;
|
||||
for (int i = 0; i < BoneWeight.Item1.Count; i++)
|
||||
{
|
||||
for (int j = 0; j < BoneWeight.Item1[i].Length; j++)
|
||||
{
|
||||
values.Append($"{BoneWeight.Item1[i][j]} {WeightIndices[weightindexcount++]} ");
|
||||
}
|
||||
}
|
||||
Writer.WriteStartElement("v");
|
||||
Writer.WriteString(values.ToString());
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a skin source accessor for the current skin controller
|
||||
/// </summary>
|
||||
/// <param name="Name"></param>
|
||||
/// <param name="Semantic"></param>
|
||||
/// <param name="Values"></param>
|
||||
/// <returns>the id of the newly created skin source</returns>
|
||||
public string WriteSkinSource(string Name, SemanticType Semantic, object[] Values)
|
||||
{
|
||||
int Stride = GetStride(Semantic);
|
||||
|
||||
string sourceid = GetUniqueID(Name + "-" + Semantic.ToString().ToLower());
|
||||
Writer.WriteStartElement("source");
|
||||
Writer.WriteAttributeString("id", sourceid);
|
||||
|
||||
Writer.WriteStartElement(Semantic == SemanticType.JOINT ? "Name_array" : "float_array");
|
||||
string FloatArrayID = GetUniqueID(Name + "-" + Semantic.ToString().ToLower() + "-array");
|
||||
Writer.WriteAttributeString("id", FloatArrayID);
|
||||
Writer.WriteAttributeString("count", Values.Length.ToString());
|
||||
Writer.WriteString(string.Join(" ", Values));
|
||||
Writer.WriteEndElement();
|
||||
|
||||
Writer.WriteStartElement("technique_common");
|
||||
{
|
||||
Writer.WriteStartElement("accessor");
|
||||
Writer.WriteAttributeString("source", $"#{FloatArrayID}");
|
||||
Writer.WriteAttributeString("count", (Values.Length / Stride).ToString());
|
||||
Writer.WriteAttributeString("stride", Stride.ToString());
|
||||
if (Semantic == SemanticType.JOINT)
|
||||
{
|
||||
WriteParam("JOINT", "name");
|
||||
}
|
||||
if (Semantic == SemanticType.INV_BIND_MATRIX)
|
||||
{
|
||||
WriteParam("TRANSFORM", "float4x4");
|
||||
}
|
||||
if (Semantic == SemanticType.WEIGHT)
|
||||
{
|
||||
WriteParam("WEIGHT", "float");
|
||||
}
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
Writer.WriteEndElement();
|
||||
|
||||
Writer.WriteEndElement();
|
||||
|
||||
return sourceid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the Library Controller Section
|
||||
/// </summary>
|
||||
public void EndLibraryControllers()
|
||||
{
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void BeginVisualNodeSection()
|
||||
{
|
||||
Writer.WriteStartElement("library_visual_scenes");
|
||||
Writer.WriteStartElement("visual_scene");
|
||||
Writer.WriteAttributeString("id", "Scene");
|
||||
Writer.WriteAttributeString("name", "Scene");
|
||||
}
|
||||
|
||||
public void CreateVisualNodeSection()
|
||||
{
|
||||
BeginVisualNodeSection();
|
||||
Writer.WriteStartElement("node");
|
||||
Writer.WriteAttributeString("id", "Armature");
|
||||
Writer.WriteAttributeString("name", "Armature");
|
||||
Writer.WriteAttributeString("type", "NODE");
|
||||
|
||||
Writer.WriteStartElement("matrix");
|
||||
Writer.WriteAttributeString("sid", "transform");
|
||||
Writer.WriteString("1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1");
|
||||
Writer.WriteEndElement();
|
||||
|
||||
foreach (var joint in Joints)
|
||||
if (joint.ParentIndex == -1)
|
||||
RecursivlyWriteJoints(joint);
|
||||
|
||||
Writer.WriteEndElement();
|
||||
|
||||
// write geometry nodes
|
||||
|
||||
foreach (var m in MeshSkinIdList)
|
||||
{
|
||||
Writer.WriteStartElement("node");
|
||||
Writer.WriteAttributeString("id", MeshIdList[m.Key]);
|
||||
Writer.WriteAttributeString("name", MeshIdList[m.Key]);
|
||||
Writer.WriteAttributeString("type", "NODE");
|
||||
|
||||
if (m.Value.Equals(""))
|
||||
{
|
||||
Writer.WriteStartElement("instance_geometry");
|
||||
Writer.WriteAttributeString("url", $"#{m.Key}");
|
||||
Writer.WriteAttributeString("name", MeshIdList[m.Key]);
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteStartElement("instance_controller");
|
||||
Writer.WriteAttributeString("url", $"#{m.Value}");
|
||||
Writer.WriteStartElement("skeleton");
|
||||
Writer.WriteString("#Armature_" + Joints[0].Name);
|
||||
Writer.WriteEndElement();
|
||||
if (MaterialIdList.ContainsKey(m.Key))
|
||||
{
|
||||
Writer.WriteStartElement("bind_material");
|
||||
Writer.WriteStartElement("technique_common");
|
||||
foreach (var mat in MaterialIdList[m.Key])
|
||||
{
|
||||
Writer.WriteStartElement("instance_material");
|
||||
Writer.WriteAttributeString("symbol", mat);
|
||||
Writer.WriteAttributeString("target", "#" + mat);
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
EndVisualNodeSection();
|
||||
}
|
||||
|
||||
public void WriteNode(string Name)
|
||||
{
|
||||
Writer.WriteStartElement("node");
|
||||
Writer.WriteAttributeString("id", Name);
|
||||
Writer.WriteAttributeString("name", Name);
|
||||
Writer.WriteAttributeString("type", "NODE");
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public void EndVisualNodeSection()
|
||||
{
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
|
||||
Writer.WriteStartElement("scene");
|
||||
Writer.WriteStartElement("instance_visual_scene");
|
||||
Writer.WriteAttributeString("url", "#Scene");
|
||||
Writer.WriteEndElement();
|
||||
Writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public string GetUniqueID(string id)
|
||||
{
|
||||
if (AttributeIdList.ContainsKey(id))
|
||||
{
|
||||
AttributeIdList[id]++;
|
||||
return $"{id}_{AttributeIdList[id]}";//
|
||||
}
|
||||
else
|
||||
{
|
||||
AttributeIdList.Add(id, 0);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Writer.Close();
|
||||
Writer?.WriteEndElement();
|
||||
Writer?.WriteEndDocument();
|
||||
Writer?.Close();
|
||||
}
|
||||
|
||||
|
||||
private class ValueContainer<T> : IEquatable<ValueContainer<T>> where T : struct
|
||||
{
|
||||
public T[] Values { get; }
|
||||
private readonly int hashCode;
|
||||
|
||||
public ValueContainer(T[] values)
|
||||
{
|
||||
Values = values;
|
||||
hashCode = ((System.Collections.IStructuralEquatable)Values).GetHashCode(EqualityComparer<T>.Default);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as ValueContainer<T>);
|
||||
}
|
||||
|
||||
public bool Equals(ValueContainer<T> other)
|
||||
{
|
||||
// Compare precalculated hash first for performance reasons.
|
||||
// The entire sequence needs to be compared to resolve collisions.
|
||||
return other != null && hashCode == other.hashCode && Enumerable.SequenceEqual(Values, other.Values);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Material
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public List<TextureMap> Textures = new List<TextureMap>();
|
||||
}
|
||||
|
||||
public class Geometry
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public Material Material { get; set; }
|
||||
|
||||
public List<float> Position = new List<float>();
|
||||
public List<float> Normal = new List<float>();
|
||||
public List<float> UV0 = new List<float>();
|
||||
public List<float> UV1 = new List<float>();
|
||||
public List<float> UV2 = new List<float>();
|
||||
public List<float> UV3 = new List<float>();
|
||||
public List<float> Color = new List<float>();
|
||||
public List<int[]> BoneIndices = new List<int[]>();
|
||||
public List<float[]> BoneWeights = new List<float[]>();
|
||||
|
||||
public bool HasNormals = false;
|
||||
public bool HasUV0 = false;
|
||||
public bool HasUV1 = false;
|
||||
public bool HasUV2 = false;
|
||||
public bool HasUV3 = false;
|
||||
public bool HasColors = false;
|
||||
public bool HasBoneIndices = false;
|
||||
public bool HasWeights = false;
|
||||
}
|
||||
|
||||
public enum PhongTextureType
|
||||
{
|
||||
diffuse,
|
||||
specular,
|
||||
emission,
|
||||
bump,
|
||||
}
|
||||
|
||||
public enum TEXCOORD
|
||||
{
|
||||
CHANNEL0,
|
||||
CHANNEL1,
|
||||
CHANNEL2,
|
||||
CHANNEL3,
|
||||
}
|
||||
|
||||
public enum SemanticType
|
||||
{
|
||||
None,
|
||||
POSITION,
|
||||
VERTEX,
|
||||
NORMAL,
|
||||
TEXCOORD,
|
||||
COLOR,
|
||||
WEIGHT,
|
||||
JOINT,
|
||||
INV_BIND_MATRIX,
|
||||
TEXTANGENT,
|
||||
TEXBINORMAL
|
||||
}
|
||||
|
||||
public enum ProgramPreset
|
||||
{
|
||||
NONE,
|
||||
MAX,
|
||||
MAYA,
|
||||
BLENDER,
|
||||
}
|
||||
|
||||
public enum SamplerWrapMode
|
||||
{
|
||||
NONE,
|
||||
WRAP,
|
||||
MIRROR,
|
||||
CLAMP,
|
||||
BORDER,
|
||||
}
|
||||
|
||||
public class TextureMap
|
||||
{
|
||||
public TEXCOORD TextureChannel { get; set; }
|
||||
public string Name { get; set; }
|
||||
public PhongTextureType Type { get; set; }
|
||||
|
||||
public SamplerWrapMode WrapModeS = SamplerWrapMode.WRAP;
|
||||
public SamplerWrapMode WrapModeT = SamplerWrapMode.WRAP;
|
||||
|
||||
public TextureMap()
|
||||
{
|
||||
TextureChannel = TEXCOORD.CHANNEL0;
|
||||
Type = PhongTextureType.diffuse;
|
||||
Name = "";
|
||||
}
|
||||
}
|
||||
|
||||
public class TriangleList
|
||||
{
|
||||
public string Material = "";
|
||||
public List<uint> Indices = new List<uint>();
|
||||
}
|
||||
|
||||
public class Joint
|
||||
{
|
||||
public string Name;
|
||||
public int ParentIndex = -1;
|
||||
public float[] Transform;
|
||||
public float[] BindPose;
|
||||
}
|
||||
}
|
||||
|
@ -8,29 +8,28 @@ using System.Runtime.InteropServices;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Xml;
|
||||
using ColladaHelper;
|
||||
using OpenTK;
|
||||
using Toolbox.Library.Rendering;
|
||||
using Toolbox.Library.Collada;
|
||||
|
||||
namespace Toolbox.Library
|
||||
{
|
||||
public class DAE : DAEHelper
|
||||
public class DAE
|
||||
{
|
||||
public class ExportSettings
|
||||
{
|
||||
public bool FlipTexCoordsVertical = true;
|
||||
|
||||
public Version FileVersion = new Version();
|
||||
|
||||
public ProgramPreset Preset = ProgramPreset.NONE;
|
||||
|
||||
public bool ExportTextures = true;
|
||||
|
||||
public string ImageExtension = ".png";
|
||||
public string ImageFolder = "";
|
||||
}
|
||||
|
||||
public class TextureMap
|
||||
{
|
||||
public uint TextureChannel = 0;
|
||||
public string Type = "diffuse";
|
||||
}
|
||||
|
||||
public class Version
|
||||
{
|
||||
public int Major = 1;
|
||||
@ -38,11 +37,269 @@ namespace Toolbox.Library
|
||||
public int Micro = 1;
|
||||
}
|
||||
|
||||
public static void Export(string fileName, ExportSettings exportSettings)
|
||||
{
|
||||
using (ColladaWriter writer = new ColladaWriter(fileName, exportSettings))
|
||||
{
|
||||
|
||||
public static void Export(string FileName, ExportSettings settings, STGenericObject mesh)
|
||||
{
|
||||
Export(FileName, settings, new List<STGenericObject>() { mesh },
|
||||
new List<STGenericMaterial>(), new List<STGenericTexture>());
|
||||
}
|
||||
|
||||
public static void Export(string FileName, ExportSettings settings, STGenericModel model, List<STGenericTexture> Textures, STSkeleton skeleton = null, List<int> NodeArray = null) {
|
||||
Export(FileName, settings, model.Objects.ToList(), model.Materials.ToList(), Textures, skeleton, NodeArray);
|
||||
}
|
||||
|
||||
public static void Export(string FileName, ExportSettings settings,
|
||||
List<STGenericObject> Meshes, List<STGenericMaterial> Materials,
|
||||
List<STGenericTexture> Textures, STSkeleton skeleton = null, List<int> NodeArray = null)
|
||||
{
|
||||
if (Materials == null)
|
||||
Materials = new List<STGenericMaterial>();
|
||||
|
||||
string TexturePath = System.IO.Path.GetDirectoryName(FileName);
|
||||
|
||||
using (ColladaWriter writer = new ColladaWriter(FileName, settings))
|
||||
{
|
||||
writer.WriteAsset();
|
||||
|
||||
if (Materials.Count > 0)
|
||||
{
|
||||
List<string> textureNames = new List<string>();
|
||||
for (int i = 0; i < Textures?.Count; i++)
|
||||
{
|
||||
textureNames.Add(Textures[i].Text);
|
||||
|
||||
if (settings.ExportTextures) {
|
||||
var bitmap = Textures[i].GetBitmap();
|
||||
bitmap.Save($"{TexturePath}/{Textures[i].Text}.png");
|
||||
bitmap.Dispose();
|
||||
|
||||
GC.Collect();
|
||||
}
|
||||
}
|
||||
|
||||
List<Material> materials = new List<Material>();
|
||||
foreach (var mat in Materials)
|
||||
{
|
||||
Material material = new Material();
|
||||
material.Name = mat.Text;
|
||||
materials.Add(material);
|
||||
|
||||
foreach (var tex in mat.TextureMaps)
|
||||
{
|
||||
TextureMap texMap = new TextureMap();
|
||||
texMap.Name = tex.Name;
|
||||
if (tex.Type == STGenericMatTexture.TextureType.Diffuse)
|
||||
texMap.Type = PhongTextureType.diffuse;
|
||||
else if (tex.Type == STGenericMatTexture.TextureType.Normal)
|
||||
texMap.Type = PhongTextureType.bump;
|
||||
else if (tex.Type == STGenericMatTexture.TextureType.Specular)
|
||||
texMap.Type = PhongTextureType.specular;
|
||||
else if (tex.Type == STGenericMatTexture.TextureType.Emission)
|
||||
texMap.Type = PhongTextureType.emission;
|
||||
else
|
||||
continue; //Skip adding unknown types
|
||||
|
||||
if (tex.WrapModeS == STTextureWrapMode.Repeat)
|
||||
texMap.WrapModeS = SamplerWrapMode.WRAP;
|
||||
else if (tex.WrapModeS == STTextureWrapMode.Mirror)
|
||||
texMap.WrapModeS = SamplerWrapMode.MIRROR;
|
||||
else if (tex.WrapModeS == STTextureWrapMode.Clamp)
|
||||
texMap.WrapModeS = SamplerWrapMode.CLAMP;
|
||||
|
||||
|
||||
if (tex.WrapModeT == STTextureWrapMode.Repeat)
|
||||
texMap.WrapModeT = SamplerWrapMode.WRAP;
|
||||
else if (tex.WrapModeT == STTextureWrapMode.Mirror)
|
||||
texMap.WrapModeT = SamplerWrapMode.MIRROR;
|
||||
else if(tex.WrapModeT == STTextureWrapMode.Clamp)
|
||||
texMap.WrapModeT = SamplerWrapMode.CLAMP;
|
||||
|
||||
|
||||
//If no textures are saved, still keep images references
|
||||
//So the user can still dump textures after
|
||||
if (Textures?.Count == 0)
|
||||
textureNames.Add($"{texMap.Name}");
|
||||
|
||||
material.Textures.Add(texMap);
|
||||
}
|
||||
}
|
||||
|
||||
writer.WriteLibraryImages(textureNames.ToArray());
|
||||
|
||||
writer.WriteLibraryMaterials(materials);
|
||||
writer.WriteLibraryEffects(materials);
|
||||
}
|
||||
else
|
||||
writer.WriteLibraryImages();
|
||||
|
||||
if (skeleton != null) {
|
||||
foreach (var bone in skeleton.bones)
|
||||
{
|
||||
//Set the inverse matrix
|
||||
// var inverse = skeleton.GetBoneTransform(bone).Inverted();
|
||||
var transform = bone.GetTransform();
|
||||
|
||||
var inverse = Toolbox.Library.IO.MatrixExenstion.CalculateInverseMatrix(bone).inverse;
|
||||
|
||||
float[] Transform = new float[] {
|
||||
transform.M11, transform.M21, transform.M31, transform.M41,
|
||||
transform.M12, transform.M22, transform.M32, transform.M42,
|
||||
transform.M13, transform.M23, transform.M33, transform.M43,
|
||||
transform.M14, transform.M24, transform.M34, transform.M44
|
||||
};
|
||||
|
||||
float[] InvTransform = new float[] {
|
||||
inverse.M11, inverse.M21, inverse.M31, inverse.M41,
|
||||
inverse.M12, inverse.M22, inverse.M32, inverse.M42,
|
||||
inverse.M13, inverse.M23, inverse.M33, inverse.M43,
|
||||
inverse.M14, inverse.M24, inverse.M34, inverse.M44
|
||||
};
|
||||
|
||||
writer.AddJoint(bone.Text, bone.parentIndex == -1 ? "" :
|
||||
skeleton.bones[bone.parentIndex].Text, Transform, InvTransform);
|
||||
}
|
||||
}
|
||||
|
||||
writer.StartLibraryGeometries();
|
||||
foreach (var mesh in Meshes)
|
||||
{
|
||||
int[] IndexTable = null;
|
||||
if (NodeArray != null)
|
||||
IndexTable = NodeArray.ToArray();
|
||||
|
||||
writer.StartGeometry(mesh.Text);
|
||||
|
||||
if (mesh.MaterialIndex != -1)
|
||||
{
|
||||
writer.CurrentMaterial = Materials[mesh.MaterialIndex].Text;
|
||||
}
|
||||
|
||||
// collect sources
|
||||
List<float> Position = new List<float>();
|
||||
List<float> Normal = new List<float>();
|
||||
List<float> UV0 = new List<float>();
|
||||
List<float> UV1 = new List<float>();
|
||||
List<float> UV2 = new List<float>();
|
||||
List<float> UV3 = new List<float>();
|
||||
List<float> Color = new List<float>();
|
||||
List<int[]> BoneIndices = new List<int[]>();
|
||||
List<float[]> BoneWeights = new List<float[]>();
|
||||
|
||||
bool HasNormals = false;
|
||||
bool HasColors = false;
|
||||
bool HasUV0 = false;
|
||||
bool HasUV1 = false;
|
||||
bool HasUV2 = false;
|
||||
bool HasBoneIds = false;
|
||||
|
||||
foreach (var vertex in mesh.vertices)
|
||||
{
|
||||
if (vertex.nrm != Vector3.Zero) HasNormals = true;
|
||||
if (vertex.col != Vector4.One) HasColors = true;
|
||||
if (vertex.uv0 != Vector2.Zero) HasUV0 = true;
|
||||
if (vertex.uv1 != Vector2.Zero) HasUV1 = true;
|
||||
if (vertex.uv2 != Vector2.Zero) HasUV2 = true;
|
||||
if (vertex.boneIds.Count > 0) HasBoneIds = true;
|
||||
|
||||
Position.Add(vertex.pos.X); Position.Add(vertex.pos.Y); Position.Add(vertex.pos.Z);
|
||||
Normal.Add(vertex.nrm.X); Normal.Add(vertex.nrm.Y); Normal.Add(vertex.nrm.Z);
|
||||
|
||||
if (settings.FlipTexCoordsVertical)
|
||||
{
|
||||
UV0.Add(vertex.uv0.X); UV0.Add(1 - vertex.uv0.Y);
|
||||
UV1.Add(vertex.uv1.X); UV1.Add(1 - vertex.uv1.Y);
|
||||
UV2.Add(vertex.uv2.X); UV2.Add(1 - vertex.uv2.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
UV0.Add(vertex.uv0.X); UV0.Add(vertex.uv0.Y);
|
||||
UV1.Add(vertex.uv1.X); UV1.Add(vertex.uv1.Y);
|
||||
UV2.Add(vertex.uv2.X); UV2.Add(vertex.uv2.Y);
|
||||
}
|
||||
|
||||
Color.AddRange(new float[] { vertex.col.X, vertex.col.Y, vertex.col.Z, vertex.col.W });
|
||||
|
||||
List<int> bIndices = new List<int>();
|
||||
List<float> bWeights = new List<float>();
|
||||
for (int b = 0; b < vertex.boneIds.Count; b++)
|
||||
{
|
||||
if (vertex.boneWeights.Count > b) {
|
||||
if (vertex.boneWeights[b] == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IndexTable != null)
|
||||
bIndices.Add((int)IndexTable[vertex.boneIds[b]]);
|
||||
else
|
||||
bIndices.Add((int)vertex.boneIds[b]);
|
||||
|
||||
//Some models may only use indices (single bind, rigid skin)
|
||||
if (vertex.boneWeights.Count > b)
|
||||
bWeights.Add(vertex.boneWeights[b]);
|
||||
else
|
||||
bWeights.Add(1);
|
||||
}
|
||||
|
||||
if (bIndices.Count == 0 && mesh.BoneIndex != -1) {
|
||||
HasBoneIds = true;
|
||||
bIndices.Add(mesh.BoneIndex);
|
||||
bWeights.Add(1);
|
||||
}
|
||||
|
||||
BoneIndices.Add(bIndices.ToArray());
|
||||
BoneWeights.Add(bWeights.ToArray());
|
||||
}
|
||||
|
||||
List<TriangleList> triangleLists = new List<TriangleList>();
|
||||
if (mesh.lodMeshes.Count > 0)
|
||||
{
|
||||
TriangleList triangleList = new TriangleList();
|
||||
triangleLists.Add(triangleList);
|
||||
|
||||
var lodMesh = mesh.lodMeshes[mesh.DisplayLODIndex];
|
||||
for (int i = 0; i < lodMesh.faces.Count; i++)
|
||||
triangleList.Indices.Add((uint)lodMesh.faces[i]);
|
||||
}
|
||||
if (mesh.PolygonGroups.Count > 0)
|
||||
{
|
||||
foreach (var group in mesh.PolygonGroups)
|
||||
{
|
||||
TriangleList triangleList = new TriangleList();
|
||||
|
||||
triangleLists.Add(triangleList);
|
||||
|
||||
if (group.MaterialIndex != -1)
|
||||
triangleList.Material = Materials[group.MaterialIndex].Text;
|
||||
|
||||
for (int i = 0; i < group.faces.Count; i++)
|
||||
triangleList.Indices.Add((uint)group.faces[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// write sources
|
||||
writer.WriteGeometrySource(mesh.Text, SemanticType.POSITION, Position.ToArray(), triangleLists.ToArray());
|
||||
|
||||
if (HasNormals)
|
||||
writer.WriteGeometrySource(mesh.Text, SemanticType.NORMAL, Normal.ToArray(), triangleLists.ToArray());
|
||||
|
||||
if (HasColors)
|
||||
writer.WriteGeometrySource(mesh.Text, SemanticType.COLOR, Color.ToArray(), triangleLists.ToArray());
|
||||
|
||||
if (HasUV0)
|
||||
writer.WriteGeometrySource(mesh.Text, SemanticType.TEXCOORD, UV0.ToArray(), triangleLists.ToArray(), 0);
|
||||
|
||||
if (HasUV1)
|
||||
writer.WriteGeometrySource(mesh.Text, SemanticType.TEXCOORD, UV1.ToArray(), triangleLists.ToArray(), 1);
|
||||
|
||||
if (HasUV2)
|
||||
writer.WriteGeometrySource(mesh.Text, SemanticType.TEXCOORD, UV2.ToArray(), triangleLists.ToArray(), 2);
|
||||
|
||||
if (HasBoneIds)
|
||||
writer.AttachGeometryController(BoneIndices, BoneWeights);
|
||||
|
||||
writer.EndGeometryMesh();
|
||||
}
|
||||
writer.EndGeometrySection();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,18 +31,21 @@
|
||||
this.exportTexturesChkBox = new Toolbox.Library.Forms.STCheckBox();
|
||||
this.stButton1 = new Toolbox.Library.Forms.STButton();
|
||||
this.stButton2 = new Toolbox.Library.Forms.STButton();
|
||||
this.chkFlipUvsVertical = new Toolbox.Library.Forms.STCheckBox();
|
||||
this.contentContainer.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// contentContainer
|
||||
//
|
||||
this.contentContainer.Controls.Add(this.chkFlipUvsVertical);
|
||||
this.contentContainer.Controls.Add(this.stButton2);
|
||||
this.contentContainer.Controls.Add(this.stButton1);
|
||||
this.contentContainer.Controls.Add(this.exportTexturesChkBox);
|
||||
this.contentContainer.Size = new System.Drawing.Size(210, 156);
|
||||
this.contentContainer.Size = new System.Drawing.Size(329, 173);
|
||||
this.contentContainer.Controls.SetChildIndex(this.exportTexturesChkBox, 0);
|
||||
this.contentContainer.Controls.SetChildIndex(this.stButton1, 0);
|
||||
this.contentContainer.Controls.SetChildIndex(this.stButton2, 0);
|
||||
this.contentContainer.Controls.SetChildIndex(this.chkFlipUvsVertical, 0);
|
||||
//
|
||||
// exportTexturesChkBox
|
||||
//
|
||||
@ -55,12 +58,13 @@
|
||||
this.exportTexturesChkBox.TabIndex = 11;
|
||||
this.exportTexturesChkBox.Text = "Export Textures";
|
||||
this.exportTexturesChkBox.UseVisualStyleBackColor = true;
|
||||
this.exportTexturesChkBox.CheckedChanged += new System.EventHandler(this.exportTexturesChkBox_CheckedChanged);
|
||||
//
|
||||
// stButton1
|
||||
//
|
||||
this.stButton1.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.stButton1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.stButton1.Location = new System.Drawing.Point(126, 124);
|
||||
this.stButton1.Location = new System.Drawing.Point(244, 141);
|
||||
this.stButton1.Name = "stButton1";
|
||||
this.stButton1.Size = new System.Drawing.Size(75, 23);
|
||||
this.stButton1.TabIndex = 12;
|
||||
@ -71,18 +75,29 @@
|
||||
//
|
||||
this.stButton2.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.stButton2.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.stButton2.Location = new System.Drawing.Point(45, 124);
|
||||
this.stButton2.Location = new System.Drawing.Point(163, 141);
|
||||
this.stButton2.Name = "stButton2";
|
||||
this.stButton2.Size = new System.Drawing.Size(75, 23);
|
||||
this.stButton2.TabIndex = 13;
|
||||
this.stButton2.Text = "Ok";
|
||||
this.stButton2.UseVisualStyleBackColor = false;
|
||||
//
|
||||
// chkFlipUvsVertical
|
||||
//
|
||||
this.chkFlipUvsVertical.AutoSize = true;
|
||||
this.chkFlipUvsVertical.Location = new System.Drawing.Point(23, 70);
|
||||
this.chkFlipUvsVertical.Name = "chkFlipUvsVertical";
|
||||
this.chkFlipUvsVertical.Size = new System.Drawing.Size(101, 17);
|
||||
this.chkFlipUvsVertical.TabIndex = 14;
|
||||
this.chkFlipUvsVertical.Text = "Flp UVs Vertical";
|
||||
this.chkFlipUvsVertical.UseVisualStyleBackColor = true;
|
||||
this.chkFlipUvsVertical.CheckedChanged += new System.EventHandler(this.chkFlipUvsVertical_CheckedChanged);
|
||||
//
|
||||
// ExportModelSettings
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(216, 161);
|
||||
this.ClientSize = new System.Drawing.Size(335, 178);
|
||||
this.Name = "ExportModelSettings";
|
||||
this.Text = "Export Settings";
|
||||
this.contentContainer.ResumeLayout(false);
|
||||
@ -96,5 +111,6 @@
|
||||
private STCheckBox exportTexturesChkBox;
|
||||
private STButton stButton2;
|
||||
private STButton stButton1;
|
||||
private STCheckBox chkFlipUvsVertical;
|
||||
}
|
||||
}
|
@ -12,17 +12,22 @@ namespace Toolbox.Library.Forms
|
||||
{
|
||||
public partial class ExportModelSettings : STForm
|
||||
{
|
||||
public bool ExportTextures
|
||||
{
|
||||
get
|
||||
{
|
||||
return exportTexturesChkBox.Checked;
|
||||
}
|
||||
}
|
||||
public DAE.ExportSettings Settings = new DAE.ExportSettings();
|
||||
|
||||
public ExportModelSettings()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
chkFlipUvsVertical.Checked = Settings.FlipTexCoordsVertical;
|
||||
exportTexturesChkBox.Checked = Settings.ExportTextures;
|
||||
}
|
||||
|
||||
private void exportTexturesChkBox_CheckedChanged(object sender, EventArgs e) {
|
||||
Settings.ExportTextures = exportTexturesChkBox.Checked;
|
||||
}
|
||||
|
||||
private void chkFlipUvsVertical_CheckedChanged(object sender, EventArgs e) {
|
||||
Settings.FlipTexCoordsVertical = chkFlipUvsVertical.Checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,22 @@ namespace Toolbox.Library
|
||||
public Quaternion rot = Quaternion.FromMatrix(Matrix3.Zero);
|
||||
public Matrix4 Transform, invert;
|
||||
|
||||
|
||||
public Matrix4 GetTransform()
|
||||
{
|
||||
Vector3 mPos = new Vector3(position[0], position[1],position[2]);
|
||||
|
||||
Quaternion mRot;
|
||||
if (RotationType == STBone.BoneRotationType.Quaternion)
|
||||
mRot = (STSkeleton.FromQuaternionAngles(rotation[2], rotation[1],rotation[0], rotation[3]));
|
||||
else
|
||||
mRot = (STSkeleton.FromEulerAngles(rotation[2],rotation[1],rotation[0]));
|
||||
|
||||
Vector3 mSca = new Vector3(scale[0], scale[1],scale[2]);
|
||||
|
||||
return Matrix4.CreateScale(mSca) * Matrix4.CreateFromQuaternion(mRot) * Matrix4.CreateTranslation(mPos);
|
||||
}
|
||||
|
||||
//Used for shifting models with the bones in the shader
|
||||
public Matrix4 ModelMatrix = Matrix4.Identity;
|
||||
|
||||
|
@ -287,6 +287,16 @@ namespace Toolbox.Library
|
||||
return bone;
|
||||
}
|
||||
|
||||
public Matrix4 GetBoneTransform(STBone Bone)
|
||||
{
|
||||
if (Bone == null)
|
||||
return Matrix4.Identity;
|
||||
if (Bone.parentIndex == -1)
|
||||
return Bone.GetTransform();
|
||||
else
|
||||
return Bone.GetTransform() * GetBoneTransform(bones[Bone.parentIndex]);
|
||||
}
|
||||
|
||||
public int boneIndex(string name)
|
||||
{
|
||||
for (int i = 0; i < bones.Count; i++)
|
||||
|
@ -126,12 +126,21 @@ namespace Toolbox.Library.IO
|
||||
//
|
||||
// RelativeOffsetPosition controls the relative position the offset starts at
|
||||
//
|
||||
public void WriteUint32Offset(long target, long RelativeOffsetPosition = 0)
|
||||
public void WriteUint32Offset(long target, long relativePosition = 0)
|
||||
{
|
||||
long pos = Position;
|
||||
using (TemporarySeek(target, SeekOrigin.Begin))
|
||||
{
|
||||
Write((uint)(pos - RelativeOffsetPosition));
|
||||
Write((uint)(pos - relativePosition));
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteUint16Offset(long target, long relativePosition)
|
||||
{
|
||||
long pos = Position;
|
||||
using (TemporarySeek(target, SeekOrigin.Begin))
|
||||
{
|
||||
Write((ushort)(pos - relativePosition));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,11 +416,9 @@
|
||||
<Compile Include="FileFormats\Animation\SMD.cs" />
|
||||
<Compile Include="FileFormats\APNG\APNG.cs" />
|
||||
<Compile Include="FileFormats\APNG\CRC.cs" />
|
||||
<Compile Include="FileFormats\Assimp\AssimpSaver.cs" />
|
||||
<Compile Include="FileFormats\Animation\SEANIM.cs" />
|
||||
<Compile Include="FileFormats\CSharpImageLibrary\DDS_BlockHelpers.cs" />
|
||||
<Compile Include="FileFormats\CSharpImageLibrary\ImageEngine.cs" />
|
||||
<Compile Include="FileFormats\DAE\ColladaHelper.cs" />
|
||||
<Compile Include="FileFormats\DAE\collada_schema_1_4.cs" />
|
||||
<Compile Include="FileFormats\DAE\DAE.cs" />
|
||||
<Compile Include="FileFormats\OBJ.cs" />
|
||||
|
@ -118,16 +118,17 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="richTextBox1.Text" xml:space="preserve">
|
||||
<value>- Smash Forge Devs (SMG, Ploaj, jam1garner, smb123w64gb, etc) for some code ported over. Specifically animation stuff and some rendering.
|
||||
- Assimp devs for their massive asset library!
|
||||
- Wexos (helped figure out a few things, ie format list to assign each attribute)
|
||||
- JuPaHe64 for the base 3D renderer and timeline control.
|
||||
- Every File Explorer devs (Gericom) for Yaz0 stuff
|
||||
- Exelix for Byaml, Sarc and KCL library
|
||||
- Syroot for helpful IO extensions and libraies
|
||||
- GDK Chan for some DDS decode methods
|
||||
- AboodXD for BNTX texture swizzling
|
||||
- MelonSpeedruns for logo.
|
||||
- Skyth and Radfordhound for PAC documentation</value>
|
||||
<value>- Smash Forge Devs (SMG, Ploaj, jam1garner, smb123w64gb, etc) for some code ported over. Specifically animation stuff and some rendering.
|
||||
- Assimp devs for their massive asset library!
|
||||
- Wexos (helped figure out a few things, ie format list to assign each attribute)
|
||||
- JuPaHe64 for the base 3D renderer and timeline control.
|
||||
- Every File Explorer devs (Gericom) for Yaz0 stuff
|
||||
- Exelix for Byaml, Sarc and KCL library
|
||||
- Syroot for helpful IO extensions and libraies
|
||||
- GDK Chan for some DDS decode methods
|
||||
- AboodXD for BNTX texture swizzling
|
||||
- MelonSpeedruns for logo.
|
||||
- Skyth and Radfordhound for PAC documentation
|
||||
- Ploaj and CrossMod devs for base on DAE writer/exporter</value>
|
||||
</data>
|
||||
</root>
|
Loading…
Reference in New Issue
Block a user