285 lines
12 KiB
C#
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;
|
|
}
|
|
}
|