f51dd17f94
Add in nutexb file format. While not finished, it can preview and export them. Batch exporting for nuteb (tools menu). Rework GTX code. This is WIP and not finished. Add XTX code. Unifnished atm. Add saving for wii u and include fmat exporting. Proper error handling for assimp and texture swizzling.
573 lines
22 KiB
C#
573 lines
22 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Assimp;
|
|
using Assimp.Configs;
|
|
using OpenTK;
|
|
using Switch_Toolbox.Library.Rendering;
|
|
using System.Windows.Forms;
|
|
|
|
namespace Switch_Toolbox.Library
|
|
{
|
|
public class AssimpData
|
|
{
|
|
public Scene scene;
|
|
private Vector3 m_sceneCenter, m_sceneMin, m_sceneMax;
|
|
|
|
public List<STGenericObject> objects = new List<STGenericObject>();
|
|
public List<STGenericMaterial> materials = new List<STGenericMaterial>();
|
|
|
|
public AssimpContext Importer = new AssimpContext();
|
|
|
|
public string[] GetSupportedImportFormats()
|
|
{
|
|
return Importer.GetSupportedImportFormats();
|
|
}
|
|
|
|
public AssimpData()
|
|
{
|
|
}
|
|
public void LoadFile(string FileName)
|
|
{
|
|
try
|
|
{
|
|
AssimpContext Importer = new AssimpContext();
|
|
|
|
scene = Importer.ImportFile(FileName, PostProcessSteps.Triangulate | PostProcessSteps.JoinIdenticalVertices
|
|
| PostProcessSteps.FlipUVs | PostProcessSteps.LimitBoneWeights |
|
|
PostProcessSteps.CalculateTangentSpace | PostProcessSteps.GenerateNormals);
|
|
LoadMeshes();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (e.ToString().Contains("Error loading unmanaged library from path"))
|
|
{
|
|
MessageBox.Show($"Failed to load assimp! Make sure you have Assimp32.dll next to the program!");
|
|
}
|
|
Console.WriteLine(e);
|
|
}
|
|
}
|
|
public void processNode()
|
|
{
|
|
Matrix4x4 identity = Matrix4x4.Identity;
|
|
if (scene.RootNode != null)
|
|
{
|
|
BuildNode(scene.RootNode, ref identity);
|
|
}
|
|
else
|
|
{
|
|
int Index = 0;
|
|
foreach (Mesh msh in scene.Meshes)
|
|
{
|
|
objects.Add(CreateGenericObject(msh, Index, Matrix4.Identity));
|
|
Index++;
|
|
}
|
|
}
|
|
}
|
|
private void BuildNode(Node parent, ref Matrix4x4 rootTransform)
|
|
{
|
|
Matrix4x4 trafo = parent.Transform;
|
|
Matrix4x4 world = trafo * rootTransform;
|
|
Matrix4 worldTK = AssimpHelper.TKMatrix(world);
|
|
|
|
foreach (int index in parent.MeshIndices)
|
|
objects.Add(CreateGenericObject(scene.Meshes[index], index, worldTK));
|
|
|
|
foreach (Node child in parent.Children)
|
|
BuildNode(child, ref rootTransform);
|
|
}
|
|
public void LoadMeshes()
|
|
{
|
|
objects.Clear();
|
|
processNode();
|
|
if (scene.HasMaterials)
|
|
{
|
|
foreach (Material mat in scene.Materials)
|
|
{
|
|
materials.Add(CreateGenericMaterial(mat));
|
|
}
|
|
}
|
|
foreach (Assimp.Animation animation in scene.Animations)
|
|
{
|
|
|
|
}
|
|
}
|
|
public Animation CreateGenericAnimation(Assimp.Animation animation)
|
|
{
|
|
Animation STanim = new Animation();
|
|
STanim.Text = animation.Name;
|
|
STanim.FrameCount = (int)animation.DurationInTicks;
|
|
|
|
//Load node animations
|
|
if (animation.HasNodeAnimations)
|
|
{
|
|
var _channels = new NodeAnimationChannel[animation.NodeAnimationChannelCount];
|
|
for (int i = 0; i < _channels.Length; i++)
|
|
{
|
|
_channels[i] = new NodeAnimationChannel();
|
|
}
|
|
}
|
|
|
|
//Load mesh animations
|
|
if (animation.HasMeshAnimations)
|
|
{
|
|
var _meshChannels = new MeshAnimationChannel[animation.MeshAnimationChannelCount];
|
|
for (int i = 0; i < _meshChannels.Length; i++)
|
|
{
|
|
_meshChannels[i] = new MeshAnimationChannel();
|
|
}
|
|
}
|
|
|
|
return STanim;
|
|
}
|
|
public STGenericMaterial CreateGenericMaterial(Material material)
|
|
{
|
|
STGenericMaterial mat = new STGenericMaterial();
|
|
mat.Text = material.Name;
|
|
|
|
TextureSlot tex;
|
|
if (material.GetMaterialTexture(TextureType.Diffuse, 0, out tex))
|
|
mat.TextureMaps.Add(CreateTextureSlot(tex, TextureType.Diffuse));
|
|
if (material.GetMaterialTexture(TextureType.Normals, 1, out tex))
|
|
mat.TextureMaps.Add(CreateTextureSlot(tex, TextureType.Normals));
|
|
if (material.GetMaterialTexture(TextureType.Specular, 1, out tex))
|
|
mat.TextureMaps.Add(CreateTextureSlot(tex, TextureType.Specular));
|
|
return mat;
|
|
}
|
|
private STGenericMatTexture CreateTextureSlot(TextureSlot tex, TextureType type)
|
|
{
|
|
var matTex = new STGenericMatTexture();
|
|
|
|
switch (type)
|
|
{
|
|
case TextureType.Diffuse:
|
|
matTex.Type = STGenericMatTexture.TextureType.Diffuse;
|
|
break;
|
|
case TextureType.Normals:
|
|
matTex.Type = STGenericMatTexture.TextureType.Normal;
|
|
break;
|
|
case TextureType.Lightmap:
|
|
matTex.Type = STGenericMatTexture.TextureType.Light;
|
|
break;
|
|
case TextureType.Emissive:
|
|
matTex.Type = STGenericMatTexture.TextureType.Emission;
|
|
break;
|
|
case TextureType.Specular:
|
|
matTex.Type = STGenericMatTexture.TextureType.Specular;
|
|
break;
|
|
case TextureType.Shininess:
|
|
matTex.Type = STGenericMatTexture.TextureType.Metalness;
|
|
break;
|
|
case TextureType.Opacity:
|
|
matTex.Type = STGenericMatTexture.TextureType.Transparency;
|
|
break;
|
|
case TextureType.Displacement:
|
|
break;
|
|
default:
|
|
matTex.Type = STGenericMatTexture.TextureType.Unknown;
|
|
break;
|
|
}
|
|
|
|
matTex.Name = System.IO.Path.GetFileNameWithoutExtension(tex.FilePath);
|
|
matTex.wrapModeS = SetWrapMode(tex.WrapModeU);
|
|
matTex.wrapModeT = SetWrapMode(tex.WrapModeV);
|
|
|
|
return matTex;
|
|
}
|
|
private int SetWrapMode(TextureWrapMode wrap)
|
|
{
|
|
switch (wrap)
|
|
{
|
|
case TextureWrapMode.Wrap:
|
|
return 0;
|
|
case TextureWrapMode.Mirror:
|
|
return 1;
|
|
case TextureWrapMode.Clamp:
|
|
return 2;
|
|
case TextureWrapMode.Decal:
|
|
return 0;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
public STGenericObject CreateGenericObject(Mesh msh, int Index, Matrix4 transform)
|
|
{
|
|
STGenericObject obj = new STGenericObject();
|
|
|
|
Console.WriteLine(msh.MaterialIndex);
|
|
if (msh.MaterialIndex != -1)
|
|
obj.MaterialIndex = msh.MaterialIndex;
|
|
else
|
|
scene.Materials.Add(new Material() { Name = msh.Name });
|
|
|
|
if (scene.Materials[msh.MaterialIndex].Name == "")
|
|
scene.Materials[msh.MaterialIndex].Name = msh.Name;
|
|
|
|
obj.HasPos = msh.HasVertices;
|
|
obj.HasNrm = msh.HasNormals;
|
|
obj.HasUv0 = msh.HasTextureCoords(0);
|
|
obj.HasUv1 = msh.HasTextureCoords(1);
|
|
obj.HasUv2 = msh.HasTextureCoords(2);
|
|
obj.HasIndices = msh.HasBones;
|
|
if (msh.HasBones)
|
|
obj.HasWeights = msh.Bones[0].HasVertexWeights;
|
|
|
|
obj.HasTans = msh.HasTangentBasis;
|
|
obj.HasBitans = msh.HasTangentBasis;
|
|
obj.HasVertColors = msh.HasVertexColors(0);
|
|
obj.ObjectName = msh.Name;
|
|
obj.boneList = GetBoneList(msh);
|
|
obj.MaxSkinInfluenceCount = GetVertexSkinCount(msh);
|
|
|
|
GenericObject.LOD_Mesh lod = new GenericObject.LOD_Mesh();
|
|
lod.faces = GetFaces(msh);
|
|
lod.IndexFormat = STIndexFormat.UInt16;
|
|
lod.PrimitiveType = STPolygonType.Triangle;
|
|
lod.GenerateSubMesh();
|
|
obj.lodMeshes.Add(lod);
|
|
obj.vertices = GetVertices(msh, transform, obj);
|
|
obj.VertexBufferIndex = Index;
|
|
|
|
return obj;
|
|
}
|
|
int NodeIndex;
|
|
public void SaveFromModel(GenericModel model, string FileName)
|
|
{
|
|
Scene scene = new Scene();
|
|
scene.RootNode = new Node("Root");
|
|
|
|
Node modelNode = new Node(model.Text);
|
|
scene.RootNode.Children.Add(modelNode);
|
|
|
|
int MeshIndex = 0;
|
|
foreach (var obj in model.Nodes[0].Nodes)
|
|
{
|
|
var genericObj = (STGenericObject)obj;
|
|
|
|
Node groupNode = new Node(genericObj.Text);
|
|
|
|
|
|
Mesh mesh = new Mesh(genericObj.Text, PrimitiveType.Triangle);
|
|
|
|
List<Vector3D> textureCoords0 = new List<Vector3D>();
|
|
List<Vector3D> textureCoords1 = new List<Vector3D>();
|
|
List<Vector3D> textureCoords2 = new List<Vector3D>();
|
|
List<Color4D> vertexColors = new List<Color4D>();
|
|
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));
|
|
mesh.TextureCoordinateChannels[0] = textureCoords0;
|
|
mesh.TextureCoordinateChannels[1] = textureCoords1;
|
|
mesh.TextureCoordinateChannels[2] = textureCoords2;
|
|
mesh.VertexColorChannels[0] = vertexColors;
|
|
}
|
|
|
|
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] }));
|
|
|
|
Console.WriteLine(genericObj.faces.Count);
|
|
Console.WriteLine(genericObj.vertices.Count);
|
|
Console.WriteLine(genericObj.Text);
|
|
|
|
mesh.MaterialIndex = genericObj.MaterialIndex;
|
|
|
|
mesh.TextureCoordinateChannels.SetValue(textureCoords0, 0);
|
|
groupNode.MeshIndices.Add(scene.Meshes.Count);
|
|
|
|
|
|
scene.Meshes.Add(mesh);
|
|
|
|
MeshIndex++;
|
|
modelNode.Children.Add(groupNode);
|
|
}
|
|
foreach (var mat in model.Nodes[1].Nodes)
|
|
{
|
|
var genericMat = (STGenericMaterial)mat;
|
|
|
|
Material material = new Material();
|
|
material.Name = genericMat.Name;
|
|
|
|
foreach (var tex in genericMat.TextureMaps)
|
|
{
|
|
TextureSlot slot = new TextureSlot();
|
|
slot.FilePath = tex.Name;
|
|
|
|
if (tex.Type == STGenericMatTexture.TextureType.Diffuse)
|
|
slot.TextureType = TextureType.Diffuse;
|
|
else if (tex.Type == STGenericMatTexture.TextureType.Normal)
|
|
slot.TextureType = TextureType.Normals;
|
|
else if (tex.Type == STGenericMatTexture.TextureType.Specular)
|
|
slot.TextureType = TextureType.Specular;
|
|
else if (tex.Type == STGenericMatTexture.TextureType.Emission)
|
|
slot.TextureType = TextureType.Emissive;
|
|
else if (tex.Type == STGenericMatTexture.TextureType.Light)
|
|
{
|
|
slot.Mapping = TextureMapping.FromUV - 1;
|
|
slot.TextureType = TextureType.Lightmap;
|
|
}
|
|
else if (tex.Type == STGenericMatTexture.TextureType.Shadow)
|
|
{
|
|
slot.Mapping = TextureMapping.FromUV - 1;
|
|
slot.TextureType = TextureType.Ambient;
|
|
}
|
|
else
|
|
slot.TextureType = TextureType.Unknown;
|
|
|
|
if (tex.wrapModeS == 0)
|
|
slot.WrapModeU = TextureWrapMode.Wrap;
|
|
if (tex.wrapModeS == 1)
|
|
slot.WrapModeU = TextureWrapMode.Mirror;
|
|
if (tex.wrapModeS == 2)
|
|
slot.WrapModeU = TextureWrapMode.Clamp;
|
|
if (tex.wrapModeT == 0)
|
|
slot.WrapModeU = TextureWrapMode.Wrap;
|
|
if (tex.wrapModeT == 1)
|
|
slot.WrapModeU = TextureWrapMode.Mirror;
|
|
if (tex.wrapModeT == 2)
|
|
slot.WrapModeU = TextureWrapMode.Clamp;
|
|
|
|
material.AddMaterialTexture(ref slot);
|
|
}
|
|
scene.Materials.Add(material);
|
|
}
|
|
|
|
using (var v = new AssimpContext())
|
|
{
|
|
string ext = System.IO.Path.GetExtension(FileName);
|
|
|
|
string formatID = "obj";
|
|
if (ext == ".obj")
|
|
formatID = "obj";
|
|
if (ext == ".fbx")
|
|
formatID = "fbx";
|
|
if (ext == ".dae")
|
|
formatID = "dae";
|
|
|
|
Console.WriteLine(ext);
|
|
Console.WriteLine(formatID);
|
|
|
|
bool Exported = v.ExportFile(scene, FileName, formatID);
|
|
|
|
if (Exported)
|
|
System.Windows.Forms.MessageBox.Show($"Exported {FileName} Successfuly!");
|
|
else
|
|
System.Windows.Forms.MessageBox.Show($"Failed to export {FileName}!");
|
|
}
|
|
}
|
|
public void SaveFromObject(List<Vertex> vertices, List<int> faces, string MeshName, string FileName)
|
|
{
|
|
Scene scene = new Scene();
|
|
scene.RootNode = new Node("Root");
|
|
|
|
Mesh mesh = new Mesh(MeshName, PrimitiveType.Triangle);
|
|
|
|
List<Vector3D> textureCoords0 = new List<Vector3D>();
|
|
List<Vector3D> textureCoords1 = new List<Vector3D>();
|
|
List<Vector3D> textureCoords2 = new List<Vector3D>();
|
|
List<Color4D> vertexColors = new List<Color4D>();
|
|
|
|
foreach (Vertex v in 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));
|
|
mesh.TextureCoordinateChannels[0] = textureCoords0;
|
|
mesh.TextureCoordinateChannels[1] = textureCoords1;
|
|
mesh.TextureCoordinateChannels[2] = textureCoords2;
|
|
mesh.VertexColorChannels[0] = vertexColors;
|
|
}
|
|
for (int f = 0; f < faces.Count; f++)
|
|
{
|
|
mesh.Faces.Add(new Face(new int[] { faces[f++], faces[f++], faces[f] }));
|
|
}
|
|
mesh.MaterialIndex = 0;
|
|
|
|
mesh.TextureCoordinateChannels.SetValue(textureCoords0, 0);
|
|
scene.Meshes.Add(mesh);
|
|
scene.RootNode.MeshIndices.Add(0);
|
|
|
|
Material material = new Material();
|
|
material.Name = "NewMaterial";
|
|
scene.Materials.Add(material);
|
|
|
|
using (var v = new AssimpContext())
|
|
{
|
|
v.ExportFile(scene, FileName, "obj");
|
|
}
|
|
}
|
|
public List<int> GetFaces(Mesh msh)
|
|
{
|
|
List<int> faces = new List<int>();
|
|
|
|
if (msh.HasFaces)
|
|
{
|
|
foreach (Face f in msh.Faces)
|
|
{
|
|
if (f.HasIndices)
|
|
{
|
|
foreach (int indx in f.Indices)
|
|
faces.Add(indx);
|
|
}
|
|
}
|
|
}
|
|
|
|
return faces;
|
|
}
|
|
public List<string> GetBoneList(Mesh msh)
|
|
{
|
|
List<string> bones = new List<string>();
|
|
foreach (Bone b in msh.Bones)
|
|
{
|
|
if (!bones.Contains(b.Name))
|
|
bones.Add(b.Name);
|
|
}
|
|
return bones;
|
|
}
|
|
public int GetVertexSkinCount(Mesh msh)
|
|
{
|
|
|
|
List<int> indciesTotal = new List<int>();
|
|
|
|
var blendIndexes = new List<List<int>>();
|
|
var blendWeights = new List<List<float>>();
|
|
|
|
int i;
|
|
for (i = 0; i < msh.VertexCount; i++)
|
|
{
|
|
blendIndexes.Add(new List<int>());
|
|
blendWeights.Add(new List<float>());
|
|
}
|
|
|
|
foreach (var bone in msh.Bones)
|
|
{
|
|
var bi = msh.Bones.IndexOf(bone);
|
|
foreach (var vw in bone.VertexWeights)
|
|
{
|
|
blendIndexes[vw.VertexID].Add(bi);
|
|
blendWeights[vw.VertexID].Add(vw.Weight);
|
|
}
|
|
}
|
|
|
|
foreach (Bone b in msh.Bones)
|
|
Console.WriteLine(b.VertexWeights.Count);
|
|
|
|
if (msh.HasBones)
|
|
return msh.Bones.Max(b => b.VertexWeightCount);
|
|
|
|
return 0;
|
|
}
|
|
public List<Vertex> GetVertices(Mesh msh, Matrix4 transform, STGenericObject STobj)
|
|
{
|
|
|
|
List<Vertex> vertices = new List<Vertex>();
|
|
for (int v = 0; v < msh.VertexCount; v++)
|
|
{
|
|
Vertex vert = new Vertex();
|
|
|
|
if (msh.HasVertices)
|
|
vert.pos = Vector3.TransformPosition(FromVector(msh.Vertices[v]), transform);
|
|
if (msh.HasNormals)
|
|
vert.nrm = Vector3.TransformNormal(FromVector(msh.Normals[v]), transform);
|
|
if (msh.HasTextureCoords(0))
|
|
vert.uv0 = new Vector2(msh.TextureCoordinateChannels[0][v].X, msh.TextureCoordinateChannels[0][v].Y);
|
|
if (msh.HasTextureCoords(1))
|
|
vert.uv1 = new Vector2(msh.TextureCoordinateChannels[1][v].X, msh.TextureCoordinateChannels[1][v].Y);
|
|
if (msh.HasTextureCoords(2))
|
|
vert.uv2 = new Vector2(msh.TextureCoordinateChannels[2][v].X, msh.TextureCoordinateChannels[2][v].Y);
|
|
if (msh.HasTangentBasis)
|
|
vert.tan = new Vector4(msh.Tangents[v].X, msh.Tangents[v].Y, msh.Tangents[v].Z, 1);
|
|
if (msh.HasVertexColors(0))
|
|
vert.col = new Vector4(msh.VertexColorChannels[0][v].R, msh.VertexColorChannels[0][v].G, msh.VertexColorChannels[0][v].B, msh.VertexColorChannels[0][v].A);
|
|
if (msh.HasTangentBasis)
|
|
vert.bitan = new Vector4(msh.BiTangents[v].X, msh.BiTangents[v].Y, msh.BiTangents[v].Z, 1);
|
|
vertices.Add(vert);
|
|
}
|
|
if (msh.HasBones)
|
|
{
|
|
for (int i = 0; i < msh.BoneCount; i++)
|
|
{
|
|
Bone bn = msh.Bones[i];
|
|
if (bn.HasVertexWeights)
|
|
{
|
|
foreach (VertexWeight w in bn.VertexWeights)
|
|
{
|
|
// vertices[w.VertexID].pos = Vector3.TransformPosition(vertices[w.VertexID].pos, AssimpHelper.TKMatrix(bn.OffsetMatrix));
|
|
vertices[w.VertexID].boneWeights.Add(w.Weight);
|
|
vertices[w.VertexID].boneNames.Add(bn.Name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return vertices;
|
|
}
|
|
private Vector3 FromVector(Vector3D vec)
|
|
{
|
|
Vector3 v;
|
|
v.X = vec.X;
|
|
v.Y = vec.Y;
|
|
v.Z = vec.Z;
|
|
return v;
|
|
}
|
|
public static OpenTK.Matrix4 TKMatrix2(Assimp.Matrix4x4 matOut)
|
|
{
|
|
var matIn = new OpenTK.Matrix4();
|
|
|
|
matOut.A1 = matIn.M11;
|
|
matOut.B1 = matIn.M12;
|
|
matOut.C1 = matIn.M13;
|
|
matOut.D1 = matIn.M14;
|
|
|
|
//Y
|
|
matOut.A2 = matIn.M21;
|
|
matOut.B2 = matIn.M22;
|
|
matOut.C2 = matIn.M23;
|
|
matOut.D2 = matIn.M24;
|
|
|
|
//Z
|
|
matOut.A3 = matIn.M31;
|
|
matOut.B3 = matIn.M32;
|
|
matOut.C3 = matIn.M33;
|
|
matOut.D3 = matIn.M34;
|
|
|
|
//Translation
|
|
matOut.A4 = matIn.M41;
|
|
matOut.B4 = matIn.M42;
|
|
matOut.C4 = matIn.M43;
|
|
matOut.D4 = matIn.M44;
|
|
|
|
return matIn;
|
|
}
|
|
|
|
private Matrix4 FromAssimpMatrix(Matrix4x4 mat)
|
|
{
|
|
Vector3D scaling;
|
|
Vector3D tranlation;
|
|
Assimp.Quaternion rot;
|
|
mat.Decompose(out scaling, out rot, out tranlation);
|
|
|
|
Console.WriteLine($"rotQ " + rot);
|
|
|
|
Matrix4 positionMat = Matrix4.CreateTranslation(FromVector(tranlation));
|
|
Matrix4 rotQ = Matrix4.CreateFromQuaternion(AssimpHelper.TKQuaternion(rot));
|
|
Matrix4 scaleMat = Matrix4.CreateScale(FromVector(scaling));
|
|
Matrix4 matrixFinal = scaleMat * rotQ * positionMat;
|
|
|
|
return matrixFinal;
|
|
}
|
|
}
|
|
}
|