1
0
mirror of synced 2024-11-12 02:00:50 +01:00

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:
KillzXGaming 2019-11-15 19:27:03 -05:00
parent 762ec86bf0
commit 9c3ac9ae9f
28 changed files with 1670 additions and 1919 deletions

View File

@ -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()

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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));
}
}
}

View 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)
{
}
}
}

View File

@ -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()

View File

@ -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)

View 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)
{
}
}
}

View File

@ -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>());
}

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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());
}
}
}

View File

@ -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");

View File

@ -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.

View File

@ -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

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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++)

View File

@ -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));
}
}

View File

@ -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" />

View File

@ -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>