Skip to content

Commit 049adc9

Browse files
Continued work on exporting BMD to a common format using AssImp.
1 parent 5ac03e8 commit 049adc9

File tree

10 files changed

+197
-9
lines changed

10 files changed

+197
-9
lines changed

SuperBMD/SuperBMD.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
<Compile Include="source\BMD\INF1.cs" />
8484
<Compile Include="source\BMD\JNT1.cs" />
8585
<Compile Include="source\BMD\MAT3.cs" />
86+
<Compile Include="source\ExportSettings.cs" />
8687
<Compile Include="source\Model.cs" />
8788
<Compile Include="source\BMD\SHP1.cs" />
8889
<Compile Include="source\BMD\TEX1.cs" />

SuperBMD/source/BMD/INF1.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using SuperBMD.Scenegraph.Enums;
88
using GameFormatReader.Common;
99
using Assimp;
10+
using SuperBMD.Util;
1011

1112
namespace SuperBMD.BMD
1213
{
@@ -111,9 +112,36 @@ private void GetNodesRecursive(Rigging.Bone bone, List<Rigging.Bone> skeleton, S
111112
}
112113
}
113114

114-
public void FillScene(Scene scene, List<Rigging.Bone> flatSkeleton)
115+
public void FillScene(Scene scene, List<Rigging.Bone> flatSkeleton, bool useSkeletonRoot)
115116
{
117+
Node root = scene.RootNode;
116118

119+
if (useSkeletonRoot)
120+
root = new Node("skeleton_root");
121+
122+
ProcessNodesRecursive(root, Root, flatSkeleton);
123+
124+
if (useSkeletonRoot)
125+
scene.RootNode.Children.Add(root);
126+
}
127+
128+
private void ProcessNodesRecursive(Node rootAssNode, SceneNode rootSceneNode, List<Rigging.Bone> flatSkeleton)
129+
{
130+
foreach (SceneNode sceneNode in rootSceneNode.Children)
131+
{
132+
if (sceneNode.Type == NodeType.Joint)
133+
{
134+
Rigging.Bone joint = flatSkeleton[sceneNode.Index];
135+
136+
Node newAssNode = new Node(joint.Name);
137+
newAssNode.Transform = joint.TransformationMatrix.ToMatrix4x4();
138+
139+
rootAssNode.Children.Add(newAssNode);
140+
ProcessNodesRecursive(newAssNode, sceneNode, flatSkeleton);
141+
}
142+
else
143+
ProcessNodesRecursive(rootAssNode, sceneNode, flatSkeleton);
144+
}
117145
}
118146

119147
public void Write(EndianBinaryWriter writer, int packetCount, int vertexCount)

SuperBMD/source/BMD/SHP1.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using SuperBMD.Geometry.Enums;
1010
using SuperBMD.Util;
1111
using SuperBMD.Rigging;
12+
using OpenTK;
1213

1314
namespace SuperBMD.BMD
1415
{
@@ -164,6 +165,68 @@ public static SHP1 Create(Scene scene, Dictionary<string, int> boneNames, Vertex
164165
return shp1;
165166
}
166167

168+
public void FillScene(Scene scene, VertexData vertData, List<Rigging.Bone> flatSkeleton, List<Matrix4> inverseBindMatrices)
169+
{
170+
for (int i = 0; i < Shapes.Count; i++)
171+
{
172+
Mesh mesh = new Mesh($"mesh_{ i }", PrimitiveType.Triangle);
173+
mesh.MaterialIndex = i;
174+
175+
int vertexID = 0;
176+
Shape curShape = Shapes[i];
177+
foreach (Packet pack in curShape.Packets)
178+
{
179+
foreach (Primitive prim in pack.Primitives)
180+
{
181+
List<Vertex> triVertices = J3DUtility.PrimitiveToTriangles(prim);
182+
183+
for (int triIndex = 0; triIndex < triVertices.Count / 3; triIndex += 3)
184+
{
185+
Face newFace = new Face(new int[] { vertexID, vertexID + 1, vertexID + 2 });
186+
mesh.Faces.Add(newFace);
187+
188+
for (int triVertIndex = 0; triVertIndex < 3; triVertIndex++)
189+
{
190+
Vertex vert = triVertices[triIndex + triVertIndex];
191+
192+
for (int j = 0; j < vert.VertexWeight.WeightCount; j++)
193+
{
194+
Rigging.Bone curWeightBone = flatSkeleton[vert.VertexWeight.BoneIndices[j]];
195+
196+
int assBoneIndex = mesh.Bones.FindIndex(x => x.Name == curWeightBone.Name);
197+
198+
if (assBoneIndex == -1)
199+
{
200+
Assimp.Bone newBone = new Assimp.Bone();
201+
newBone.Name = curWeightBone.Name;
202+
mesh.Bones.Add(newBone);
203+
assBoneIndex = mesh.Bones.IndexOf(newBone);
204+
}
205+
206+
mesh.Bones[assBoneIndex].VertexWeights.Add(new VertexWeight(vertexID, vert.VertexWeight.Weights[j]));
207+
}
208+
209+
OpenTK.Vector4 openTKVec = new Vector4(vertData.Positions[(int)vert.GetAttributeIndex(GXVertexAttribute.Position)], 1);
210+
Vector3D vertVec = new Vector3D(openTKVec.X, openTKVec.Y, openTKVec.Z);
211+
212+
if (vert.VertexWeight.WeightCount == 1)
213+
{
214+
Vector4 trans = OpenTK.Vector4.Transform(openTKVec, inverseBindMatrices[vert.VertexWeight.BoneIndices[0]].Inverted());
215+
vertVec = new Vector3D(trans.X, trans.Y, trans.Z);
216+
}
217+
218+
mesh.Vertices.Add(vertVec);
219+
220+
vertexID++;
221+
}
222+
}
223+
}
224+
}
225+
226+
scene.Meshes.Add(mesh);
227+
}
228+
}
229+
167230
public void Write(EndianBinaryWriter writer)
168231
{
169232
List<Tuple<ShapeVertexDescriptor, int>> descriptorOffsets; // Contains the offsets for each unique vertex descriptor

SuperBMD/source/ExportSettings.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace SuperBMD
8+
{
9+
public class ExportSettings
10+
{
11+
public bool UseSkeletonRoot;
12+
13+
public ExportSettings()
14+
{
15+
UseSkeletonRoot = true;
16+
}
17+
}
18+
}

SuperBMD/source/Geometry/Shape.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ public void ProcessVerticesWithoutWeights(Mesh mesh, VertexData vertData)
8282
for (int i = 0; i < 3; i++)
8383
{
8484
Vertex vert = new Vertex();
85+
86+
Weight rootWeight = new Weight();
87+
rootWeight.AddWeight(1.0f, 0);
88+
89+
vert.SetWeight(rootWeight);
8590
int vertIndex = face.Indices[i];
8691

8792
foreach (Enums.GXVertexAttribute attrib in activeAttribs)
@@ -254,11 +259,12 @@ public void ProcessVerticesWithWeights(Mesh mesh, VertexData vertData, Dictionar
254259
{
255260
Vertex vert = new Vertex();
256261
int vertIndex = vertexIndexArray[i];
262+
Weight curWeight = vertWeightArray[i];
263+
264+
vert.SetWeight(curWeight);
257265

258266
foreach (Enums.GXVertexAttribute attrib in activeAttribs)
259267
{
260-
Weight curWeight = vertWeightArray[i];
261-
262268
switch (attrib)
263269
{
264270
case Enums.GXVertexAttribute.PositionMatrixIdx:

SuperBMD/source/Model.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public Model(Scene scene, string modelDirectory)
9797
VertexData = new VTX1(scene);
9898
Joints = new JNT1(scene, VertexData);
9999
Scenegraph = new INF1(scene, Joints);
100-
Textures = new TEX1(scene, modelDirectory);
100+
Textures = new TEX1(scene, Path.GetDirectoryName(modelDirectory));
101101

102102
SkinningEnvelopes = new EVP1();
103103
SkinningEnvelopes.SetInverseBindMatrices(scene, Joints.FlatSkeleton);
@@ -142,11 +142,14 @@ public void ExportBMD(string fileName)
142142
}
143143
}
144144

145-
public void ExportAssImp(string fileName, string modelType)
145+
public void ExportAssImp(string fileName, string modelType, ExportSettings settings)
146146
{
147+
throw new NotImplementedException();
148+
147149
string outDir = Path.GetDirectoryName(fileName);
148150

149151
Scene outScene = new Scene();
152+
150153
outScene.RootNode = new Node("RootNode");
151154
Assimp.Node geomNode = new Node(Path.GetFileNameWithoutExtension(fileName), outScene.RootNode);
152155

@@ -157,11 +160,12 @@ public void ExportAssImp(string fileName, string modelType)
157160

158161
outScene.RootNode.Children.Add(geomNode);
159162

163+
Scenegraph.FillScene(outScene, Joints.FlatSkeleton, settings.UseSkeletonRoot);
160164
Materials.FillScene(outScene, Textures, outDir);
161-
165+
Shapes.FillScene(outScene, VertexData.Attributes, Joints.FlatSkeleton, SkinningEnvelopes.InverseBindMatrices);
162166

163167
AssimpContext cont = new AssimpContext();
164-
cont.ExportFile(outScene, fileName, modelType, PostProcessSteps.ValidateDataStructure);
168+
cont.ExportFile(outScene, fileName, "collada", PostProcessSteps.ValidateDataStructure);
165169
}
166170
}
167171
}

SuperBMD/source/Rigging/Bone.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ public Bone(EndianBinaryReader reader, string name)
6262

6363
m_Translation = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
6464

65+
TransformationMatrix = Matrix4.CreateScale(m_Scale) *
66+
Matrix4.CreateFromQuaternion(m_Rotation) *
67+
Matrix4.CreateTranslation(m_Translation);
68+
6569
Bounds = new BoundingVolume(reader);
6670
}
6771

SuperBMD/source/Util/J3DUtility.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using SuperBMD.Materials.Enums;
77
using GameFormatReader.Common;
88
using OpenTK;
9+
using SuperBMD.Geometry;
910

1011
namespace SuperBMD.Util
1112
{
@@ -156,5 +157,50 @@ public static Vector3 ToEulerAngles(this Quaternion q)
156157

157158
return vec;
158159
}
160+
161+
public static List<Vertex> PrimitiveToTriangles(Primitive prim)
162+
{
163+
List<Vertex> vertList = new List<Vertex>();
164+
165+
if (prim.PrimitiveType == Geometry.Enums.GXPrimitiveType.Triangles)
166+
return prim.Vertices;
167+
if (prim.PrimitiveType == Geometry.Enums.GXPrimitiveType.TriangleStrip)
168+
{
169+
for (int v = 2; v < prim.Vertices.Count; v++)
170+
{
171+
bool isEven = v % 2 != 0;
172+
Vertex[] newTri = new Vertex[3];
173+
174+
newTri[0] = prim.Vertices[v - 2];
175+
newTri[1] = isEven ? prim.Vertices[v] : prim.Vertices[v - 1];
176+
newTri[2] = isEven ? prim.Vertices[v - 1] : prim.Vertices[v];
177+
178+
// Check against degenerate triangles (a triangle which shares indexes)
179+
if (newTri[0] != newTri[1] && newTri[1] != newTri[2] && newTri[2] != newTri[0])
180+
vertList.AddRange(newTri);
181+
else
182+
System.Console.WriteLine("Degenerate triangle detected, skipping TriangleStrip conversion to triangle.");
183+
}
184+
}
185+
else if (prim.PrimitiveType == Geometry.Enums.GXPrimitiveType.TriangleFan)
186+
{
187+
for (int v = 1; v < prim.Vertices.Count - 1; v++)
188+
{
189+
// Triangle is always, v, v+1, and index[0]?
190+
Vertex[] newTri = new Vertex[3];
191+
newTri[0] = prim.Vertices[v];
192+
newTri[1] = prim.Vertices[v + 1];
193+
newTri[2] = prim.Vertices[0];
194+
195+
// Check against degenerate triangles (a triangle which shares indexes)
196+
if (newTri[0] != newTri[1] && newTri[1] != newTri[2] && newTri[2] != newTri[0])
197+
vertList.AddRange(newTri);
198+
else
199+
System.Console.WriteLine("Degenerate triangle detected, skipping TriangleFan conversion to triangle.");
200+
}
201+
}
202+
203+
return vertList;
204+
}
159205
}
160206
}

SuperBMD/source/Util/VectorUtility.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ public static OpenTK.Vector3 ToOpenTKVector3(this Assimp.Vector3D vec3)
1616
return new OpenTK.Vector3(vec3.X, vec3.Y, vec3.Z);
1717
}
1818

19+
public static Assimp.Vector3D ToVector3D(this OpenTK.Vector3 vec3)
20+
{
21+
return new Vector3D(vec3.X, vec3.Y, vec3.Z);
22+
}
23+
1924
public static OpenTK.Vector2 ToOpenTKVector2(this Assimp.Vector3D vec3)
2025
{
2126
return new OpenTK.Vector2(vec3.X, vec3.Y);
@@ -35,5 +40,16 @@ public static Color ToSuperBMDColorRGBA(this Assimp.Color4D color4)
3540
{
3641
return new Color(color4.R, color4.G, color4.B, color4.A);
3742
}
43+
44+
public static Matrix4x4 ToMatrix4x4(this OpenTK.Matrix4 mat4)
45+
{
46+
Matrix4x4 outMat = new Matrix4x4(mat4.M11, mat4.M12, mat4.M13, mat4.M14,
47+
mat4.M21, mat4.M22, mat4.M23, mat4.M24,
48+
mat4.M31, mat4.M32, mat4.M33, mat4.M34,
49+
mat4.M41, mat4.M42, mat4.M43, mat4.M44);
50+
51+
outMat.Transpose();
52+
return outMat;
53+
}
3854
}
3955
}

SuperBMD_UnitTest/Program.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ class Program
1212
static void Main(string[] args)
1313
{
1414
Model mod = Model.Load(args[0]);
15-
//mod.ExportBMD(args[0] + ".bmd");
16-
mod.ExportAssImp(args[2], args[1]);
15+
mod.ExportBMD(args[0] + ".bmd");
16+
//ExportSettings exSet = new ExportSettings();
17+
18+
//mod.ExportAssImp(args[2], args[1], exSet);
1719
}
1820
}
1921
}

0 commit comments

Comments
 (0)