294 lines
13 KiB
C#
294 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;
|
|
_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
|
|
}
|
|
} |