Files
test/Assets/ModelRenderer/Scripts/SkinnedMesh/SkinnedMeshRenderFromData.cs
2025-11-24 15:37:31 +07:00

295 lines
13 KiB
C#

using System.IO;
using System.Threading.Tasks;
using ModelRenderer.Scripts;
using UnityEditor;
using UnityEngine;
namespace BrewMonster.Scripts
{
public class SkinnedMeshRenderFromData : MonoBehaviour
{
public static string baseTexturePath = "ModelRenderer/Art/Textures/";
public static string baseMeshPath = "ModelRenderer/Art/Models";
private int BaseMapHash = Shader.PropertyToID("_BaseMap");
public SkeletonBuilder _skeletonBuilder;
public MeshFilter _meshFilter;
public SkinnedMeshRenderer _skinnedMeshRenderer;
private Vector3[] vertices = new Vector3[]
{
new Vector3(0, 0, 0),
new Vector3(1, 0, 0),
new Vector3(0, 1, 0),
new Vector3(0, 0, 1)
};
private int[] indices = new int[]
{
0, 1, 2,
0, 1, 3,
1, 2, 3,
2, 0, 3
};
private Vector2[] uv1 = new Vector2[]
{
new Vector2(0, 0),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(1, 1)
};
// normals
private Vector3[] normals = new Vector3[]
{
new Vector3(0, 0, -1),
new Vector3(0, 0, -1),
new Vector3(0, 0, -1),
new Vector3(0, 0, -1)
};
public string[] BoneNames;
#if MODEL_RENDERER_PROJECT
public async Task Render(A3DSkin skin, int index, Material material, SkeletonBuilder skeletonBuilder = null)
{
A3DSkinMesh skinMesh = skin._skinMeshes[index];
BoneNames = skin._boneNames;
// Set up all paths
var relativeFilePath = skin.m_strAbsoluteFilePath.Substring(Application.dataPath.Length + 1);
relativeFilePath = relativeFilePath.Substring(0, relativeFilePath.LastIndexOf('.'));
var relativeMeshPath = relativeFilePath;
var absoluteMeshFolderPath = Path.Combine(Application.dataPath, baseMeshPath, relativeMeshPath);
var skinName = skin.m_strSkinName;
relativeMeshPath = Path.Combine(relativeMeshPath, $"{skinMesh.m_strMeshName}.mesh");
var absoluteMeshPath = Path.Combine(Application.dataPath, baseMeshPath, relativeMeshPath);
var savedMeshPath = Path.Combine("Assets", baseMeshPath, relativeMeshPath);
var relativeMaterialPath = Path.Combine(relativeFilePath, $"{skinMesh.m_strMeshName}.mat");
var absoluteMaterialPath = Path.Combine(Application.dataPath, baseMeshPath, relativeMaterialPath);
var savedMaterialPath = Path.Combine("Assets", baseMeshPath, relativeMaterialPath);
// Prefab paths
string prefabRelativePath = Path.Combine(relativeFilePath, $"{skinMesh.m_strMeshName}.prefab");
string prefabAssetPath = Path.Combine("Assets", baseMeshPath, prefabRelativePath);
string prefabAbsolutePath = Path.Combine(Application.dataPath, baseMeshPath, prefabRelativePath);
gameObject.name = skinMesh.m_strMeshName;
#if UNITY_EDITOR
// Check if prefab already exists at the beginning
GameObject existingPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabAssetPath);
if (existingPrefab != null)
{
// Prefab exists - load it and assign bones for animation
PrefabUtility.ConvertToPrefabInstance(gameObject, existingPrefab, new ConvertToPrefabInstanceSettings(), InteractionMode.AutomatedAction);
// Get the SkinnedMeshRenderer from the prefab instance
_skinnedMeshRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
// Assign bones and root bone so it works with animation
if (_skinnedMeshRenderer != null)
{
_skeletonBuilder = skeletonBuilder ?? _skeletonBuilder;
_skinnedMeshRenderer.bones = _skeletonBuilder.GetBones(skin._boneNames);
_skinnedMeshRenderer.rootBone = _skinnedMeshRenderer.bones[^1];
}
return; // Early exit - no need to create mesh/material
}
#endif
// Prefab doesn't exist - proceed with normal rendering
if (!Directory.Exists(absoluteMeshFolderPath)) Directory.CreateDirectory(absoluteMeshFolderPath);
if (_meshFilter == null)
{
_meshFilter = gameObject.GetComponent<MeshFilter>();
if (_meshFilter == null)
_meshFilter = gameObject.AddComponent<MeshFilter>();
}
Mesh mesh;
if (File.Exists(absoluteMeshPath))
{
// If mesh already exists, we just load it from the asset database
mesh = AssetDatabase.LoadAssetAtPath<Mesh>(savedMeshPath);
_meshFilter.mesh = mesh;
_skeletonBuilder = skeletonBuilder ?? _skeletonBuilder;
_skinnedMeshRenderer.bones = _skeletonBuilder.GetBones(skin._boneNames);
_skinnedMeshRenderer.rootBone = _skinnedMeshRenderer.bones[^1];
_skinnedMeshRenderer.sharedMesh = mesh;
}
else
{
// If mesh does not exist, we create a new one. Then save it to the asset database.
vertices = new Vector3[skinMesh.iNumVert];
for (int i = 0; i < skinMesh.iNumVert; i++)
{
vertices[i] = new Vector3(skinMesh._vertices[i].vPos[0], skinMesh._vertices[i].vPos[1], skinMesh._vertices[i].vPos[2]);
}
indices = new int[skinMesh.iNumIdx];
for (int i = 0; i < skinMesh.iNumIdx; i++)
{
indices[i] = skinMesh._indices[i];
}
uv1 = new Vector2[skinMesh.iNumVert];
for (int i = 0; i < skinMesh.iNumVert; i++)
{
uv1[i] = new Vector2(skinMesh._vertices[i].tu, skinMesh._vertices[i].tv);
}
normals = new Vector3[skinMesh.iNumVert];
for (int i = 0; i < skinMesh.iNumVert; i++)
{
normals[i] = new Vector3(skinMesh._vertices[i].vNormal[0], skinMesh._vertices[i].vNormal[1], skinMesh._vertices[i].vNormal[2]);
}
// Create a new Mesh
mesh = new Mesh();
// Set the vertices and triangles (indices)
mesh.vertices = vertices;
mesh.uv = uv1;
mesh.triangles = indices;
var boneWeights = new BoneWeight[skinMesh.iNumVert];
for (int i = 0; i < skinMesh.iNumVert; i++)
{
var src = skinMesh._vertices[i];
BoneWeight boneWeight = new BoneWeight
{
boneIndex0 = (int)(src.dwMatIndices & 0x000000ff),
boneIndex1 = (int)((src.dwMatIndices >> 8) & 0x000000ff),
boneIndex2 = (int)((src.dwMatIndices >> 16) & 0x000000ff),
boneIndex3 = (int)((src.dwMatIndices >> 24) & 0x000000ff),
weight0 = src.aWeights.Length > 0 ? src.aWeights[0] : 0.0f,
weight1 = src.aWeights.Length > 1 ? src.aWeights[1] : 0.0f,
weight2 = src.aWeights.Length > 2 ? src.aWeights[2] : 0.0f,
weight3 = src.aWeights.Length > 3 ? src.aWeights[3] : 0.0f
};
boneWeights[i] = boneWeight;
}
mesh.boneWeights = boneWeights;
mesh.bindposes = skeletonBuilder != null ? skeletonBuilder.GetBindPoses(skin._boneNames) : _skeletonBuilder.GetBindPoses(skin._boneNames);
mesh.RecalculateNormals();
mesh.RecalculateBounds();
if (_skinnedMeshRenderer == null)
{
_skinnedMeshRenderer = gameObject.AddComponent<SkinnedMeshRenderer>();
}
_skinnedMeshRenderer.bones = skeletonBuilder != null ? skeletonBuilder.GetBones(skin._boneNames) : _skeletonBuilder.GetBones(skin._boneNames);
_skinnedMeshRenderer.rootBone = _skinnedMeshRenderer.bones[^1];
// now we save the mesh to asset
AssetDatabase.CreateAsset(mesh, savedMeshPath);
AssetDatabase.Refresh();
// await Task.Delay(100);
// then set the mesh to the skinned mesh renderer
mesh = AssetDatabase.LoadAssetAtPath<Mesh>(savedMeshPath);
_skinnedMeshRenderer.sharedMesh = mesh;
_meshFilter.mesh = mesh;
}
Material newMaterial = null;
if (File.Exists(absoluteMaterialPath))
{
newMaterial = AssetDatabase.LoadAssetAtPath<Material>(savedMaterialPath);
}
else
{
newMaterial = new Material(material);
}
var absoluteTexturePath = Path.Combine(skin.m_strTextureDirPath, skin.textureNames[skinMesh.iTexture]);
var relativeTexturePath = absoluteTexturePath.Substring(Application.dataPath.Length + 1);
string texturePathWithoutExtension = relativeTexturePath.Substring(0, relativeTexturePath.Length - 4);
// this is absolute path of the png file
string pngTexturePath = Path.Combine(Application.dataPath, baseTexturePath, texturePathWithoutExtension + ".png");
// check if png file exists
if (File.Exists(pngTexturePath))
{
if (pngTexturePath.Contains(Application.dataPath))
{
pngTexturePath = pngTexturePath.Substring(Application.dataPath.Length);
pngTexturePath = "Assets" + pngTexturePath;
}
newMaterial.SetTexture(BaseMapHash, AssetDatabase.LoadAssetAtPath<Texture2D>(pngTexturePath));
}
else if (File.Exists(absoluteTexturePath))
{
bool isTGATexture = absoluteTexturePath.Contains(".tga");
Texture2D texture;
if (isTGATexture)
{
texture = AAssit.LoadTextureTGA(absoluteTexturePath);
}
else
{
texture = AAssit.LoadTextureDXT(File.ReadAllBytes(absoluteTexturePath));
}
// save the texture to png
FolderAndFileUltilities.CreateFolderStructure(pngTexturePath.Substring(0, pngTexturePath.LastIndexOf('/')));
AAssit.SaveTexture2DToPNG(texture, pngTexturePath, true);
AssetDatabase.Refresh();
if (pngTexturePath.Contains(Application.dataPath))
{
pngTexturePath = pngTexturePath.Substring(Application.dataPath.Length);
pngTexturePath = "Assets" + pngTexturePath;
}
newMaterial.SetTexture(BaseMapHash, AssetDatabase.LoadAssetAtPath<Texture2D>(pngTexturePath));
}
else
{
newMaterial.SetTexture(BaseMapHash, Texture2D.whiteTexture);
}
if (!File.Exists(absoluteMaterialPath))
{
AssetDatabase.CreateAsset(newMaterial, savedMaterialPath);
AssetDatabase.Refresh();
newMaterial = AssetDatabase.LoadAssetAtPath<Material>(savedMaterialPath);
}
_skinnedMeshRenderer.material = newMaterial;
#if UNITY_EDITOR
// Save the GameObject as a new prefab (we already checked it doesn't exist at the beginning)
// Ensure target folder exists
string prefabFolderAbsolute = Path.GetDirectoryName(prefabAbsolutePath);
if (!Directory.Exists(prefabFolderAbsolute))
{
Directory.CreateDirectory(prefabFolderAbsolute);
}
// Create prefab and connect the current object to it
PrefabUtility.SaveAsPrefabAssetAndConnect(gameObject, prefabAssetPath, InteractionMode.AutomatedAction);
#endif
}
// each skinned mesh should have a skeleton builder
// no skeleton => no animation
public void BuildSkeleton(A3DSkeleton skeleton)
{
_skeletonBuilder.BuildSkeleton(skeleton);
}
#endif
}
}