Unity-AI-Deepseek生成的生成模型代码
结果
能用,不是很理想,从左到右,分别是body,眼睛,演睫毛,手指套(如果你知道这是什么)结果不是很理想
(下面代码已包含,修复的切线只能传Vector3参数,Unity2022测试)
你们帮我看看,到底这个代码的质量如何,是否能用
明显它是知道的
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using UnityEngine.UIElements;
public class SkinnedMeshSplitterWindow : EditorWindow
{
private GameObject targetObject;
private Vector2 scrollPos;
[MenuItem("Tools/Skinned--Mesh Splitter")]
public static void ShowWindow()
{
GetWindow<SkinnedMeshSplitterWindow>("Mesh Splitter");
}
void OnGUI()
{
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
// 目标对象选择
targetObject = (GameObject)EditorGUILayout.ObjectField("Target Object",
targetObject, typeof(GameObject), true);
EditorGUILayout.Space(10);
if (targetObject != null)
{
DrawMeshInfo();
EditorGUILayout.Space(10);
if (GUILayout.Button("Split SubMeshes", GUILayout.Height(30)))
{
SplitMeshes(targetObject);
}
}
else
{
EditorGUILayout.HelpBox("Select a GameObject with MeshFilter or SkinnedMeshRenderer",
MessageType.Info);
}
EditorGUILayout.EndScrollView();
}
void DrawMeshInfo()
{
var smr = targetObject.GetComponent<SkinnedMeshRenderer>();
var mf = targetObject.GetComponent<MeshFilter>();
if (smr != null && smr.sharedMesh != null)
{
EditorGUILayout.LabelField("Skinned Mesh Info:", EditorStyles.boldLabel);
EditorGUILayout.LabelField($"SubMeshes: {smr.sharedMesh.subMeshCount}");
EditorGUILayout.LabelField($"Materials: {smr.sharedMaterials.Length}");
}
else if (mf != null && mf.sharedMesh != null)
{
EditorGUILayout.LabelField("Static Mesh Info:", EditorStyles.boldLabel);
EditorGUILayout.LabelField($"SubMeshes: {mf.sharedMesh.subMeshCount}");
EditorGUILayout.LabelField($"Materials: {targetObject.GetComponent<MeshRenderer>()?.sharedMaterials.Length ?? 0}");
}
else
{
EditorGUILayout.HelpBox("No valid mesh component found", MessageType.Warning);
}
}
void SplitMeshes(GameObject originalObject)
{
// 处理SkinnedMeshRenderer
var skinnedRenderer = originalObject.GetComponent<SkinnedMeshRenderer>();
if (skinnedRenderer != null)
{
SplitSkinnedMesh(skinnedRenderer);
}
// 处理MeshFilter
var meshFilter = originalObject.GetComponent<MeshFilter>();
if (meshFilter != null)
{
SplitStaticMesh(meshFilter);
}
}
void SplitSkinnedMesh(SkinnedMeshRenderer originalRenderer)
{
Mesh originalMesh = originalRenderer.sharedMesh;
Transform rootBone = originalRenderer.rootBone;
Transform[] bones = originalRenderer.bones;
Material[] materials = originalRenderer.sharedMaterials;
for (int subMeshIndex = 0; subMeshIndex < originalMesh.subMeshCount; subMeshIndex++)
{
GameObject newGo = CreateNewObject(originalRenderer.gameObject, subMeshIndex, "Skinned");
SkinnedMeshRenderer newRenderer = newGo.AddComponent<SkinnedMeshRenderer>();
newRenderer.sharedMesh = ProcessSubMesh(originalMesh, subMeshIndex);
newRenderer.bones = bones;
newRenderer.rootBone = rootBone;
newRenderer.sharedMaterials = GetSubMaterials(materials, subMeshIndex);
}
}
void SplitStaticMesh(MeshFilter originalFilter)
{
Mesh originalMesh = originalFilter.sharedMesh;
MeshRenderer originalRenderer = originalFilter.GetComponent<MeshRenderer>();
Material[] materials = originalRenderer?.sharedMaterials;
for (int subMeshIndex = 0; subMeshIndex < originalMesh.subMeshCount; subMeshIndex++)
{
GameObject newGo = CreateNewObject(originalFilter.gameObject, subMeshIndex, "Static");
MeshFilter newFilter = newGo.AddComponent<MeshFilter>();
newFilter.sharedMesh = ProcessSubMesh(originalMesh, subMeshIndex);
MeshRenderer newRenderer = newGo.AddComponent<MeshRenderer>();
newRenderer.sharedMaterials = GetSubMaterials(materials, subMeshIndex);
}
}
GameObject CreateNewObject(GameObject original, int index, string typePrefix)
{
GameObject newGo = new GameObject($"{original.name}_{typePrefix}_submesh_{index}");
newGo.transform.SetParent(original.transform.parent);
newGo.transform.localPosition = original.transform.localPosition;
newGo.transform.localRotation = original.transform.localRotation;
newGo.transform.localScale = original.transform.localScale;
return newGo;
}
Mesh ProcessSubMesh(Mesh originalMesh, int subMeshIndex)
{
Mesh newMesh = new Mesh();
// 获取三角形数据
int[] triangles = originalMesh.GetTriangles(subMeshIndex);
HashSet<int> vertexSet = new HashSet<int>(triangles);
// 创建顶点映射表
Dictionary<int, int> vertexMap = new Dictionary<int, int>();
List<Vector3> newVertices = new List<Vector3>();
// 收集所有顶点属性
List<Vector3> newNormals = new List<Vector3>();
List<Vector4> newTangents = new List<Vector4>();
List<Vector2> newUV = new List<Vector2>();
List<BoneWeight> newBoneWeights = new List<BoneWeight>();
foreach (int index in vertexSet)
{
vertexMap[index] = newVertices.Count;
newVertices.Add(originalMesh.vertices[index]);
if (originalMesh.normals.Length > index)
newNormals.Add(originalMesh.normals[index]);
if (originalMesh.tangents.Length > index)
newTangents.Add(originalMesh.tangents[index]);
if (originalMesh.uv.Length > index)
newUV.Add(originalMesh.uv[index]);
if (originalMesh.boneWeights.Length > index)
newBoneWeights.Add(originalMesh.boneWeights[index]);
}
// 重新映射三角形
int[] newTriangles = new int[triangles.Length];
for (int i = 0; i < triangles.Length; i++)
{
newTriangles[i] = vertexMap[triangles[i]];
}
// 设置网格数据
newMesh.SetVertices(newVertices);
if (newNormals.Count > 0) newMesh.SetNormals(newNormals);
if (newTangents.Count > 0) newMesh.SetTangents(newTangents);
if (newUV.Count > 0) newMesh.SetUVs(0, newUV);
if (newBoneWeights.Count > 0) newMesh.boneWeights = newBoneWeights.ToArray();
newMesh.triangles = newTriangles;
newMesh.RecalculateBounds();
// 复制混合形状(如果需要)
if (originalMesh.blendShapeCount > 0)
{
for (int i = 0; i < originalMesh.blendShapeCount; i++)
{
Vector3[] deltaVertices = new Vector3[originalMesh.vertexCount];
Vector3[] deltaNormals = new Vector3[originalMesh.vertexCount];
Vector3[] deltaTangents = new Vector3[originalMesh.vertexCount];
originalMesh.GetBlendShapeFrameVertices(i, 0, deltaVertices, deltaNormals, deltaTangents);
List<Vector3> newDeltaVerts = new List<Vector3>();
foreach (int index in vertexSet)
{
newDeltaVerts.Add(deltaVertices[index]);
}
//做一个转换,不要最后切线的 w;第四个分量(w)是一个符号值(通常为1或-1),用于确定副切线(Bitangent)的方向。
List<Vector3> temps = new List<Vector3>();
for (int j =0; j< newTangents.Count; j++) {
Vector3 t = newTangents[i];
temps.Add(t);
}
newMesh.AddBlendShapeFrame(
originalMesh.GetBlendShapeName(i),
originalMesh.GetBlendShapeFrameWeight(i, 0),
newDeltaVerts.ToArray(),
deltaNormals.Length > 0 ? newNormals.ToArray() : null,
deltaTangents.Length > 0 ? temps.ToArray() : null
);
}
}
return newMesh;
}
Material[] GetSubMaterials(Material[] originalMaterials, int subMeshIndex)
{
if (originalMaterials == null || originalMaterials.Length == 0)
return new Material[0];
int materialIndex = Mathf.Clamp(subMeshIndex, 0, originalMaterials.Length - 1);
return new Material[] { originalMaterials[materialIndex] };
}
}
结果,我之后再截图吧
<最终生成结果在倒装,放文章最上面了。。。>
修复切线,切线对不上的。。。
指的是mesh 读取的是 tangents:Vector4,但是add mesh 只能 tangetns:Vector3// unity 的skinnedmesh 万年没有更新,想要对上,全靠“蒙”
1. 为什么有时是Vector3
,有时是Vector4
?
-
Vector4
的普遍性
在Unity的网格数据(Mesh.tangents
)中,切线默认存储为Vector4
数组。这是因为法线贴图(Normal Mapping)需要完整的切线空间信息,而第四个分量(w
)用于控制副切线(Bitangent)的方向。 -
Vector3
的简化场景
如果你在某些代码中看到Vector3
的切线,可能是因为:-
在不需要法线贴图的场景中(例如仅使用顶点光照),开发者可能忽略第四个分量。
-
旧版Unity或特定插件未正确处理切线数据。
-
2. Vector4
的第四个分量(w
)是什么?
-
方向符号(Sign of Direction)
第四个分量(w
)是一个符号值(通常为1
或-1
),用于确定副切线(Bitangent)的方向。
副切线的计算公式为:cpp
复制
bitangent = cross(normal, tangent.xyz) * tangent.w;