Merge pull request #115 from croakfang/master

fix missing mesh in Cheval Grand swimsuit export
This commit is contained in:
katboi01
2025-08-03 21:23:17 +02:00
committed by GitHub
6 changed files with 212 additions and 15 deletions
@@ -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<SkinnedMeshRenderer>())
{
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<Vector3> finalVertices = new List<Vector3>();
List<int> finalTriangles = new List<int>();
List<Vector3> finalNormals = new List<Vector3>();
List<Vector2> finalUVs = new List<Vector2>();
List<BoneWeight> finalBoneWeights = new List<BoneWeight>();
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<SkinnedMeshRenderer>();
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<SkinnedMeshRenderer>())
{
if (s.name.Equals("BoundSpheres"))
{
GameObject.Destroy(s.gameObject);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8657757b99bdfed4cafd242f24fe25f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+7 -3
View File
@@ -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
};
@@ -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)
@@ -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);
}
+1 -1
View File
@@ -1683,6 +1683,6 @@ public class UmaContainerCharacter : UmaContainer
}
}
}
return Instantiate(entry.Get<GameObject>());
return Instantiate(entry.Get<GameObject>(), parent.transform);
}
}