Files
UmaViewer/Assets/Scripts/Exporters/ModelExporter.cs
T
2023-07-11 02:09:28 +08:00

285 lines
12 KiB
C#

using LibMMD.Material;
using LibMMD.Model;
using LibMMD.Reader;
using LibMMD.Unity3D;
using LibMMD.Writer;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using static LibMMD.Model.SkinningOperator;
//using UnityGLTF;
public class ModelExporter
{
static string modelInfo = $"Generated by UmaViewer {Application.version}, Copyright Cygames \nLink : https://github.com/katboi01/UmaViewer";
public static void ExportModel(UmaContainer container, string path)
{
container.SetDynamicBoneEnable(false);
container.UmaFaceAnimator?.Rebind();
container.UmaAnimator?.Rebind();
container.EnableEyeTracking = false;
container.FaceDrivenKeyTarget?.FacialResetAll();
var textures = TextureExporter.ExportAllTexture(Path.GetDirectoryName(path), container.gameObject);
var model = ReadPMXModel(container.CharaEntry, container, textures);
FileStream fileStream = new FileStream(path, FileMode.Create);
BinaryWriter writer = new BinaryWriter(fileStream);
var config = new ModelConfig() { GlobalToonPath = "Toon" };
PMXWriter.Write(writer, model, config);
writer.Close();
fileStream.Close();
UmaViewerUI.Instance.ShowMessage($"PMX Save at {path}", UIMessageType.Success);
}
public static RawMMDModel ReadPMXModel(CharaEntry entry, UmaContainer container, string[] textures)
{
RawMMDModel model = new RawMMDModel();
model.Name = entry.Name;
model.NameEn = entry.GetName();
model.Description = model.DescriptionEn = $"{entry.GetName()}\n{modelInfo}";
//Rean Bones
var rootBone = container.transform.Find("Position");
List<Transform> bones = new List<Transform>(rootBone.GetComponentsInChildren<Transform>());
bones.RemoveAll(o => o.name.Contains("Col_"));
//Read vertices And triangles
List<Renderer> renderers = new List<Renderer>(container.GetComponentsInChildren<Renderer>());
List<int> triangles = new List<int>();
model.Vertices = ReadVerticesAndTriangles(renderers, bones, ref triangles);
model.TriangleIndexes = triangles.ToArray();
//Read Texture reference
foreach (var texture in textures)
{
model.TextureList.Add(new MMDTexture(texture));
}
model.Parts = ReadPartMaterials(renderers, model);
model.Bones = ReadBones(bones);
model.Morphs = new Morph[0];
model.Rigidbodies = new MMDRigidBody[0];
model.Joints = new MMDJoint[0];
return model;
}
private static Bone[] ReadBones(List<Transform> bonelist)
{
List<Bone> pmxbones = new List<Bone>();
foreach(var bone in bonelist)
{
Bone pmxbone = new Bone();
pmxbone.Name = pmxbone.NameEn = bone.name;
pmxbone.Position = bone.position;
pmxbone.ParentIndex = bonelist.IndexOf(bone.parent);
pmxbone.TransformLevel = 0;
pmxbone.Visible = true;
pmxbone.Movable = true;
pmxbone.Rotatable = true;
pmxbone.Controllable = true;
pmxbone.ChildBoneVal = new Bone.ChildBone()
{
ChildUseId = true,
Index = (bone.childCount > 0 ? bonelist.IndexOf(bone.GetChild(0)) : -1)
};
pmxbones.Add(pmxbone);
}
return pmxbones.ToArray();
}
private static Part[] ReadPartMaterials(List<Renderer> renderers, RawMMDModel model)
{
List<Part> parts = new List<Part>();
int baseShift = 0;
foreach (Renderer renderer in renderers)
{
Mesh mesh;
if (renderer is MeshRenderer mr)
{
var meshfilter = mr.GetComponent<MeshFilter>();
mesh = meshfilter.mesh;
}
else
{
mesh = ((SkinnedMeshRenderer)renderer).sharedMesh;
}
var materials = new List<Material>(renderer.sharedMaterials);
for (int i = 0; i < mesh.subMeshCount; i++)
{
var material = (i < materials.Count ? materials[i] : materials[materials.Count - 1]);
var part = new Part();
var mat = new MMDMaterial();
part.Material = mat;
mat.Name = mat.NameEn = material.name;
mat.DiffuseColor = Color.white;
mat.SpecularColor = Color.clear;
mat.AmbientColor = Color.white * 0.5f;
mat.Shiness = 5;
mat.CastSelfShadow = true;
mat.DrawGroundShadow = true;
mat.DrawSelfShadow = true;
mat.EdgeColor = Color.black;
mat.EdgeSize = 0.4f;
var tex = model.TextureList.Find(t => t.TexturePath.Contains($"/{material.mainTexture.name}.png"));
mat.Texture = (tex ?? model.TextureList[0]);
mat.MetaInfo = "";
part.BaseShift = baseShift + mesh.GetSubMesh(i).indexStart;
part.TriangleIndexNum = mesh.GetSubMesh(i).indexCount;
parts.Add(part);
}
baseShift += mesh.triangles.Length;
}
return parts.ToArray();
}
private static Vertex[] ReadVerticesAndTriangles(List<Renderer> renderers, List<Transform> bones, ref List<int> triangleList)
{
List<Vertex> verticesList = new List<Vertex>();
int vertexOffset = 0;
foreach (Renderer renderer in renderers)
{
if (renderer is MeshRenderer mr)
{
var meshfilter = mr.GetComponent<MeshFilter>();
//Cache this data to avoid copying arrays.
var mesh = meshfilter.mesh;
var vertices = mesh.vertices;
var normals = mesh.normals;
var uv = mesh.uv;
var uv1 = mesh.uv2;
var uv2 = mesh.uv3;
var colors = mesh.colors;
var triangles = mesh.triangles;
for (int i = 0; i < vertices.Length; i++)
{
Vertex vertex = new Vertex();
vertex.Coordinate = renderer.transform.TransformPoint(vertices[i]);
vertex.Normal = normals[i];
vertex.UvCoordinate = uv[i];
vertex.ExtraUvCoordinate = new Vector4[3]
{
uv1.Length > 0 ? uv1[i] : Vector2.zero,
uv2.Length > 0 ? uv2[i] : Vector2.zero,
colors.Length > 0 ? colors[i] : Color.clear
};
vertex.SkinningOperator = new SkinningOperator() { Type = SkinningType.SkinningBdef1 };
vertex.SkinningOperator.Param = new Bdef1() { BoneId = bones.IndexOf(renderer.transform) };
vertex.EdgeScale = 1;
verticesList.Add(vertex);
}
foreach (var triangle in mesh.triangles)
{
triangleList.Add(triangle + vertexOffset);
}
vertexOffset += vertices.Length;
}
else if (renderer is SkinnedMeshRenderer smr)
{
var mesh = smr.sharedMesh;
var vertices = mesh.vertices;
var normals = mesh.normals;
var uv = mesh.uv;
var uv1 = mesh.uv2;
var uv2 = mesh.uv3;
var colors = mesh.colors;
var weights = mesh.boneWeights;
var bindpose = mesh.bindposes;
var boneCounts = mesh.GetBonesPerVertex();
var skinbone = smr.bones;
var isBody = smr.name.Equals("M_Body")|| smr.name.Equals("M_Mayu");
for (int i = 0; i < vertices.Length; i++)
{
Vertex vertex = new Vertex();
var rootbone = isBody ? smr.rootBone.parent : smr.rootBone;
vertex.Coordinate = rootbone.TransformPoint(vertices[i]);
vertex.Normal = normals[i];
vertex.UvCoordinate = new Vector2(uv[i].x, 1 - uv[i].y);
vertex.ExtraUvCoordinate = new Vector4[3]
{
uv1.Length > 0 ? uv1[i] : Vector2.zero,
uv2.Length > 0 ? uv2[i] : Vector2.zero,
colors.Length > 0 ? colors[i] : Color.clear
};
var boneWeight = weights[i];
var boneCount = boneCounts[i];
switch (boneCount)
{
case 0:
vertex.SkinningOperator = new SkinningOperator() { Type = SkinningType.SkinningBdef1 };
vertex.SkinningOperator.Param = new Bdef1() { BoneId = GetBoneIndex(bones,renderer.transform) };
break;
default:
case 1:
vertex.SkinningOperator = new SkinningOperator() { Type = SkinningType.SkinningBdef1 };
vertex.SkinningOperator.Param = new Bdef1() { BoneId = GetBoneIndex(bones, skinbone[boneWeight.boneIndex0]) };
break;
case 2:
vertex.SkinningOperator = new SkinningOperator() { Type = SkinningType.SkinningBdef2 };
vertex.SkinningOperator.Param = new Bdef2() {
BoneId = new int []{
GetBoneIndex(bones, skinbone[boneWeight.boneIndex0]),
GetBoneIndex(bones, skinbone[boneWeight.boneIndex1]),
},
BoneWeight = boneWeight.weight0
};
break;
case 3:
case 4:
vertex.SkinningOperator = new SkinningOperator() { Type = SkinningType.SkinningBdef4 };
vertex.SkinningOperator.Param = new Bdef4()
{
BoneId = new int[]{
GetBoneIndex(bones, skinbone[boneWeight.boneIndex0]),
GetBoneIndex(bones, skinbone[boneWeight.boneIndex1]),
GetBoneIndex(bones, skinbone[boneWeight.boneIndex2]),
GetBoneIndex(bones, skinbone[boneWeight.boneIndex3]),
},
BoneWeight = new float[]
{
boneWeight.weight0,
boneWeight.weight1,
boneWeight.weight2,
boneWeight.weight3
}
};
break;
}
vertex.EdgeScale = 1;
verticesList.Add(vertex);
}
foreach (var triangle in mesh.triangles)
{
triangleList.Add(triangle + vertexOffset);
}
vertexOffset += vertices.Length;
}
}
return verticesList.ToArray();
}
private static int GetBoneIndex(List<Transform> bones, Transform bone)
{
return bones.Contains(bone) ? bones.IndexOf(bone) : 0;
}
}