diff --git a/Assets/Scripts/Exporters/BillboardBuilder.cs b/Assets/Scripts/Exporters/BillboardBuilder.cs new file mode 100644 index 0000000..de107ad --- /dev/null +++ b/Assets/Scripts/Exporters/BillboardBuilder.cs @@ -0,0 +1,182 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class BillboardBuilder : MonoBehaviour +{ + public static Mesh CreateSphereMesh(float radius, float uvRadius, Vector2 uvCenter, int longitude = 6, int latitude = 6) + { + Mesh mesh = new Mesh(); + Vector3[] vertices = new Vector3[(longitude + 1) * (latitude + 1)]; + Vector2[] uvs = new Vector2[vertices.Length]; + int[] triangles = new int[longitude * latitude * 6]; + + int vert = 0, tri = 0; + for (int lat = 0; lat <= latitude; lat++) + { + float a1 = Mathf.PI * lat / latitude; + float y = Mathf.Cos(a1); + float sin1 = Mathf.Sin(a1); + + for (int lon = 0; lon <= longitude; lon++) + { + float a2 = 2 * Mathf.PI * lon / longitude; + float x = sin1 * Mathf.Cos(a2); + float z = sin1 * Mathf.Sin(a2); + + vertices[vert] = new Vector3(x, y, z) * radius; + //uvs[vert] = new Vector2((float)lon / longitude, (float)lat / latitude); + + Vector3 p = vertices[vert].normalized; + uvs[vert] = new Vector2(p.x, p.z) * uvRadius + uvCenter; + + //uvs[vert] = new Vector2(p.x, p.z) * 0.5f + new Vector2(0.5f, 0.5f); + + if (lat < latitude && lon < longitude) + { + int current = vert; + int next = vert + longitude + 1; + + triangles[tri++] = current; + triangles[tri++] = current + 1; + triangles[tri++] = next; + + triangles[tri++] = current + 1; + triangles[tri++] = next + 1; + triangles[tri++] = next; + } + + vert++; + } + } + + mesh.vertices = vertices; + mesh.uv = uvs; + mesh.triangles = triangles; + mesh.RecalculateNormals(); + return mesh; + } + + public static void BuildBillboard(UmaContainerCharacter container) + { + int groupSize = 4; + + foreach (SkinnedMeshRenderer s in container.GetComponentsInChildren()) + { + if (s.name.Equals("M_Hair_Billboard") || s.name.Equals("M_Body_Billboard")) + { + Mesh sourceMesh = s.sharedMesh; + Vector3[] vertices = sourceMesh.vertices; + Vector2[] uv1s = sourceMesh.uv; + Vector2[] uv2s = sourceMesh.uv2; + + BoneWeight[] boneWeights = sourceMesh.boneWeights; + Matrix4x4[] bindposes = sourceMesh.bindposes; + + if (uv2s.Length != vertices.Length) + { + Debug.LogError("Unmatch vertices number."); + return; + } + + // List + List finalVertices = new List(); + List finalTriangles = new List(); + List finalNormals = new List(); + List finalUVs = new List(); + List finalBoneWeights = new List(); + + int sphereCount = 0; + + for (int i = 0; i < vertices.Length; i += groupSize) + { + Vector3 center = vertices[i]; + float radius = Mathf.Abs(uv2s[i].x); + + + if (radius <= 0.0001f) + continue; + + //Calc + float min_x = 1; + float max_x = 0; + float min_y = 1; + float max_y = 0; + for (int j = 0; j < 4; j++) + { + if (uv1s[i + j].x > max_x) max_x = uv1s[i + j].x; + if (uv1s[i + j].x < min_x) min_x = uv1s[i + j].x; + if (uv1s[i + j].y > max_y) max_y = uv1s[i + j].y; + if (uv1s[i + j].y < min_y) min_y = uv1s[i + j].y; + } + + float center_x = (min_x + max_x) / 2; + float center_y = (min_y + max_y) / 2; + Vector2 uv_center = new Vector2(center_x, center_y); + float uv_radius = (max_x - min_x) / 2 * 0.25f; + + + // Create + Mesh sphereMesh; + + if (radius < 0.003) + { + sphereMesh = CreateSphereMesh(radius, uv_radius, uv_center); + } + else + { + sphereMesh = CreateSphereMesh(radius, uv_radius, uv_center, 12, 9); + } + + + int vertexOffset = finalVertices.Count; + + foreach (Vector3 v in sphereMesh.vertices) + finalVertices.Add(v + center); + + finalTriangles.AddRange(System.Array.ConvertAll(sphereMesh.triangles, t => t + vertexOffset)); + finalNormals.AddRange(sphereMesh.normals); + finalUVs.AddRange(sphereMesh.uv); + + BoneWeight bw = boneWeights[i]; + for (int j = 0; j < sphereMesh.vertexCount; j++) + finalBoneWeights.Add(bw); + + sphereCount++; + } + + // Merge + Mesh finalMesh = new Mesh(); + finalMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; + finalMesh.SetVertices(finalVertices); + finalMesh.SetTriangles(finalTriangles, 0); + finalMesh.SetNormals(finalNormals); + finalMesh.SetUVs(0, finalUVs); + finalMesh.boneWeights = finalBoneWeights.ToArray(); + finalMesh.bindposes = bindposes; + + // Create + GameObject resultGO = new GameObject("BoundSpheres"); + resultGO.transform.SetParent(s.transform, false); + SkinnedMeshRenderer newSMR = resultGO.AddComponent(); + newSMR.sharedMesh = finalMesh; + newSMR.bones = s.bones; + newSMR.rootBone = s.rootBone; + newSMR.material = s.sharedMaterial; + + Debug.Log($"Generated {sphereCount} small spheres and combined them into a single mesh."); + } + } + } + + public static void RemoveBillboard(UmaContainerCharacter container) + { + foreach (SkinnedMeshRenderer s in container.GetComponentsInChildren()) + { + if (s.name.Equals("BoundSpheres")) + { + GameObject.Destroy(s.gameObject); + } + } + } +} diff --git a/Assets/Scripts/Exporters/BillboardBuilder.cs.meta b/Assets/Scripts/Exporters/BillboardBuilder.cs.meta new file mode 100644 index 0000000..b2130c4 --- /dev/null +++ b/Assets/Scripts/Exporters/BillboardBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8657757b99bdfed4cafd242f24fe25f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Exporters/ModelExporter.cs b/Assets/Scripts/Exporters/ModelExporter.cs index e701750..c4c6126 100644 --- a/Assets/Scripts/Exporters/ModelExporter.cs +++ b/Assets/Scripts/Exporters/ModelExporter.cs @@ -10,6 +10,8 @@ using System.IO; using UnityEngine; using static LibMMD.Model.Morph; using static LibMMD.Model.SkinningOperator; + +using static BillboardBuilder; //using UnityGLTF; @@ -26,6 +28,7 @@ public class ModelExporter container.EnableEyeTracking = false; container.FaceDrivenKeyTarget?.FacialResetAll(); AddBlendShape(container); + BuildBillboard(container); var textures = TextureExporter.ExportAllTexture(Path.GetDirectoryName(path), container.gameObject); var model = ReadPMXModel(container.CharaEntry, container, textures); @@ -38,6 +41,7 @@ public class ModelExporter writer.Close(); fileStream.Close(); + RemoveBillboard(container); ClearBlendShape(container); UmaViewerUI.Instance.ShowMessage($"PMX Save at {path}", UIMessageType.Success); } @@ -326,12 +330,12 @@ public class ModelExporter Vertex vertex = new Vertex(); vertex.Coordinate = renderer.transform.TransformPoint(vertices[i]); vertex.Normal = normals[i]; - vertex.UvCoordinate = uv[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, + uv1.Length > 0 ? new Vector2(uv1[i].x, 1 - uv1[i].y) : Vector2.zero, + uv2.Length > 0 ? new Vector2(uv2[i].x, 1 - uv2[i].y) : Vector2.zero, colors.Length > 0 ? colors[i] : Color.clear }; diff --git a/Assets/Scripts/Exporters/UnityPMXRuntimeLoader/LibMMD/Util/MMDReaderWriteUtil.cs b/Assets/Scripts/Exporters/UnityPMXRuntimeLoader/LibMMD/Util/MMDReaderWriteUtil.cs index 511864e..6131f87 100644 --- a/Assets/Scripts/Exporters/UnityPMXRuntimeLoader/LibMMD/Util/MMDReaderWriteUtil.cs +++ b/Assets/Scripts/Exporters/UnityPMXRuntimeLoader/LibMMD/Util/MMDReaderWriteUtil.cs @@ -109,9 +109,9 @@ namespace LibMMD.Util return ret; } - public static void WriteVector3(BinaryWriter writer, Vector3 vector) + public static void WriteVector3(BinaryWriter writer, Vector3 vector, bool useSizeAmplifier = true) { - WriteAmpVector3(writer, vector, WorldSizeAmplifier); + WriteAmpVector3(writer, vector, useSizeAmplifier ? WorldSizeAmplifier : 1); } public static void WriteAmpVector3(BinaryWriter writer, Vector3 vector, float amp) diff --git a/Assets/Scripts/Exporters/UnityPMXRuntimeLoader/LibMMD/Writer/PMXWriter.cs b/Assets/Scripts/Exporters/UnityPMXRuntimeLoader/LibMMD/Writer/PMXWriter.cs index 77c737d..33b4223 100644 --- a/Assets/Scripts/Exporters/UnityPMXRuntimeLoader/LibMMD/Writer/PMXWriter.cs +++ b/Assets/Scripts/Exporters/UnityPMXRuntimeLoader/LibMMD/Writer/PMXWriter.cs @@ -78,10 +78,10 @@ namespace LibMMD.Writer MMDReaderWriteUtil.WriteAmpVector3(writer, joint.Rotation, Mathf.Rad2Deg); MMDReaderWriteUtil.WriteVector3(writer, joint.PositionLowLimit); MMDReaderWriteUtil.WriteVector3(writer, joint.PositionHiLimit); - MMDReaderWriteUtil.WriteVector3(writer, joint.RotationLowLimit); - MMDReaderWriteUtil.WriteVector3(writer, joint.RotationHiLimit); + MMDReaderWriteUtil.WriteVector3(writer, joint.RotationLowLimit, false); + MMDReaderWriteUtil.WriteVector3(writer, joint.RotationHiLimit, false); MMDReaderWriteUtil.WriteVector3(writer, joint.SpringTranslate); - MMDReaderWriteUtil.WriteVector3(writer, joint.SpringRotate); + MMDReaderWriteUtil.WriteVector3(writer, joint.SpringRotate, false); } } @@ -286,7 +286,7 @@ namespace LibMMD.Writer if (bone.RotAxisFixed) { - MMDReaderWriteUtil.WriteVector3(writer, bone.RotAxis); + MMDReaderWriteUtil.WriteVector3(writer, bone.RotAxis, false); } if (bone.AppendRotate || bone.AppendTranslate) @@ -297,8 +297,8 @@ namespace LibMMD.Writer if (bone.UseLocalAxis) { - MMDReaderWriteUtil.WriteVector3(writer, bone.LocalAxisVal.AxisX); - MMDReaderWriteUtil.WriteVector3(writer, bone.LocalAxisVal.AxisZ); + MMDReaderWriteUtil.WriteVector3(writer, bone.LocalAxisVal.AxisX, false); + MMDReaderWriteUtil.WriteVector3(writer, bone.LocalAxisVal.AxisZ, false); } if (bone.ReceiveTransform) @@ -328,8 +328,8 @@ namespace LibMMD.Writer writer.Write(link.HasLimit ? (byte)1 : (byte)0); if (link.HasLimit) { - MMDReaderWriteUtil.WriteVector3(writer, link.LoLimit); - MMDReaderWriteUtil.WriteVector3(writer, link.HiLimit); + MMDReaderWriteUtil.WriteVector3(writer, link.LoLimit, false); + MMDReaderWriteUtil.WriteVector3(writer, link.HiLimit, false); } } } @@ -529,7 +529,7 @@ namespace LibMMD.Writer private static void WriteVertexBasic(BinaryWriter writer, Vector3 coordinate, Vector3 normal, Vector2 uvCoordinate) { MMDReaderWriteUtil.WriteVector3(writer, coordinate); - MMDReaderWriteUtil.WriteVector3(writer, normal); + MMDReaderWriteUtil.WriteVector3(writer, normal, false); MMDReaderWriteUtil.WriteVector2(writer, uvCoordinate); } diff --git a/Assets/Scripts/UmaContainerCharacter.cs b/Assets/Scripts/UmaContainerCharacter.cs index b29eb8b..d58b853 100644 --- a/Assets/Scripts/UmaContainerCharacter.cs +++ b/Assets/Scripts/UmaContainerCharacter.cs @@ -1683,6 +1683,6 @@ public class UmaContainerCharacter : UmaContainer } } } - return Instantiate(entry.Get()); + return Instantiate(entry.Get(), parent.transform); } }