自定义数据
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Custom
{
[Serializable]
public class CustomAvatarData
{
[Serializable]
public class MeshData
{
public Vector3[] meshVertices;
public Vector2[] meshUV;
public int[] meshTriangles;
public BoneWeight[] meshBoneWeight;
public int subMeshCount;
public List<string> meshBonesName = new List<string>();
public int meshMaterialCount;
public Matrix4x4[] meshBindPoses;
public Bounds meshBounds;
public Vector3[] meshNormals;
public Vector4[] meshTangents;
}
[Serializable]
public class SkeletonData
{
public CustomTransform current;
public List<string> children = new List<string>();
}
[Serializable]
public class CustomTransform
{
public string nodeName;
public Vector3 position;
public Quaternion rotation;
}
[Serializable]
public enum Gender
{
male,
female,
}
[Serializable]
public class CustomSkindMeshData
{
public Gender gender;
public MeshData meshData = new MeshData();
public string SkeletonRoot;
public List<SkeletonData> skeletonDatas = new List<SkeletonData>();
public string mainTextureBase64;
public string normalTextureBase64;
}
}
}
保存模型数据
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
namespace Custom
{
public class SaveAvatar : MonoBehaviour
{
public GameObject root;
public Material matTemp;
public string avatarName;
public CustomAvatarData.Gender gender;
//public GameObject[] bon;
// Start is called before the first frame update
void Start()
{
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.S))
{
SaveFunc();
}
}
[ContextMenu("SaveFunc")]
public void SaveFunc()
{
var skMesh = CombineSuitsToSkeleton(root, root.GetComponentsInChildren<SkinnedMeshRenderer>());
CustomAvatarData.CustomSkindMeshData customSkindMeshData = new CustomAvatarData.CustomSkindMeshData();
customSkindMeshData.gender = this.gender;
CustomAvatarData.MeshData meshData = new CustomAvatarData.MeshData();
meshData.meshVertices = skMesh.sharedMesh.vertices;
meshData.meshTriangles = skMesh.sharedMesh.triangles;
meshData.meshUV = skMesh.sharedMesh.uv;
meshData.meshBoneWeight = skMesh.sharedMesh.boneWeights;
meshData.subMeshCount = skMesh.sharedMesh.subMeshCount;
foreach (var item in skMesh.bones)
{
meshData.meshBonesName.Add(item.gameObject.name);
}
meshData.meshMaterialCount = skMesh.sharedMaterials.Length;
meshData.meshBindPoses = skMesh.sharedMesh.bindposes;
meshData.meshBounds = skMesh.sharedMesh.bounds;
meshData.meshNormals = skMesh.sharedMesh.normals;
meshData.meshTangents = skMesh.sharedMesh.tangents;
customSkindMeshData.meshData = meshData;
Texture2D tex = new Texture2D(skMesh.material.mainTexture.width, skMesh.material.mainTexture.height);
tex = skMesh.material.mainTexture as Texture2D;
customSkindMeshData.mainTextureBase64 = System.Convert.ToBase64String(tex.EncodeToJPG());
customSkindMeshData.normalTextureBase64 = System.Convert.ToBase64String((skMesh.material.GetTexture("_BumpMap")as Texture2D).EncodeToJPG());
for (int i = 0; i < root.transform.childCount; i++)
{
if (root.transform.GetChild(i).GetComponent<SkinnedMeshRenderer>())
{
continue;
}
var trans = root.transform.GetChild(i).GetComponent<Transform>();
customSkindMeshData.SkeletonRoot = trans.gameObject.name;
GetChildInfo(trans, customSkindMeshData);
}
//customSkindMeshData.skeletonData.tree.Traverse(customSkindMeshData.skeletonData.tree.Root);
var json = JsonUtility.ToJson(customSkindMeshData);
print(Application.dataPath + "/" + avatarName + ".json");
//File.WriteAllText(Application.dataPath + "/AvatarData/" + avatarName + ".json", json);
File.WriteAllBytes(Application.dataPath + "/AvatarData/" + avatarName + ".customModel", Encoding.UTF8.GetBytes(json));
print("Save");
var skr = gameObject.GetComponent<SkinnedMeshRenderer>();
if (skr)
{
Destroy(skr);
}
}
private void GetChildInfo(Transform trans, CustomAvatarData.CustomSkindMeshData _customSkindMeshData)
{
CustomAvatarData.SkeletonData skeleton = new CustomAvatarData.SkeletonData();
CustomAvatarData.CustomTransform custom = new CustomAvatarData.CustomTransform();
custom.nodeName = trans.gameObject.name;
custom.position = trans.localPosition;
custom.rotation = trans.localRotation;
skeleton.current = custom;
for (int i = 0; i < trans.childCount; i++)
{
var t = trans.GetChild(i);
skeleton.children.Add(t.gameObject.name);
GetChildInfo(t, _customSkindMeshData);
}
_customSkindMeshData.skeletonDatas.Add(skeleton);
}
SkinnedMeshRenderer CombineSuitsToSkeleton(GameObject skeleton, SkinnedMeshRenderer[] meshes)
{
var transforms = new List<Transform>(skeleton.GetComponentsInChildren<Transform>(true));
var materials = new List<Material>();
var combineInstances = new List<CombineInstance>();
var bones = new List<Transform>();
foreach (SkinnedMeshRenderer smr in meshes)
{
materials.AddRange(smr.materials);
for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++)
{
CombineInstance ci = new CombineInstance();
ci.mesh = smr.sharedMesh;
ci.subMeshIndex = sub;
combineInstances.Add(ci);
}
foreach (Transform b in smr.bones)
{
var t = transforms.Find(t => b.name.Equals(t.name));
if (t != null)
{
bones.Add(t);
}
}
}
var newMaterial = Instantiate(matTemp);
var oldUV = new List<Vector2[]>();
// merge the texture
var Textures = new List<Texture2D>();
var normals = new List<Texture2D>();
foreach (Material m in materials)
{
var _tex = m.GetTexture("baseColorTexture") as Texture2D;
var _text2 = ConvertTexture(_tex);
Textures.Add(_text2);
var _tex_n = m.GetTexture("normalTexture") as Texture2D;
if (_tex_n)
{
Texture2D texture2D = new Texture2D(_tex_n.width, _tex_n.height, _tex_n.format, false);
//texture2D.SetPixels(normal.GetPixels());
Graphics.CopyTexture(_tex_n, texture2D);
//File.WriteAllBytes(Application.dataPath + "/" + texture2D.GetInstanceID() + ".png", texture2D.EncodeToPNG());
normals.Add(texture2D);
}
}
Texture2D newDiffuseTex = new Texture2D(512, 512, TextureFormat.RGBA32, true);
Rect[] uvs = newDiffuseTex.PackTextures(Textures.ToArray(), 0);
newMaterial.mainTexture = newDiffuseTex;
newMaterial.EnableKeyword("_SPECULARHIGHLIGHTS_OFF");
newMaterial.SetFloat("_SpecularHighlights", 0f);
Texture2D newNormal = new Texture2D(512, 512, TextureFormat.RGBA32, true);
newNormal.PackTextures(normals.ToArray(), 0);
newMaterial.SetTexture("_BumpMap", newNormal);
// reset uv
Vector2[] uva, uvb;
for (int j = 0; j < combineInstances.Count; j++)
{
uva = (Vector2[])(combineInstances[j].mesh.uv);
uvb = new Vector2[uva.Length];
for (int k = 0; k < uva.Length; k++)
{
uvb[k] = new Vector2((uva[k].x * uvs[j].width) + uvs[j].x, (uva[k].y * uvs[j].height) + uvs[j].y);
}
oldUV.Add(combineInstances[j].mesh.uv);
combineInstances[j].mesh.uv = uvb;
}
var r = gameObject.AddComponent<SkinnedMeshRenderer>() ?? gameObject.GetComponent<SkinnedMeshRenderer>();
r.bones = bones.ToArray();
r.sharedMesh = new Mesh();
r.sharedMesh.CombineMeshes(combineInstances.ToArray(), true, false);
r.material = newMaterial;
return r;
}
private Texture2D ConvertTexture(Texture2D source)
{
byte[] pix = source.GetRawTextureData();
Texture2D readableText = new Texture2D(source.width, source.height, source.format, false);
readableText.LoadRawTextureData(pix);
readableText.Apply();
return readableText;
}
}
}
加载模型数据
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
namespace Custom
{
public class LoadAvatar : MonoBehaviour
{
public RuntimeAnimatorController animatorController;
public Avatar avatarMale;
public Avatar avatarFemale;
public Material matTemp;
public string avatarName;
// Start is called before the first frame update
void Start()
{
StartCoroutine(GetRequest("http://192.168.1.112:9666/MyAvatar3.customModel"));
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.L))
{
List<CustomAvatarData.MeshData> ml = new List<CustomAvatarData.MeshData>();
//var json = File.ReadAllText(Application.dataPath + "/AvatarData/" + avatarName + ".json");
var json = File.ReadAllBytes(Application.dataPath + "/AvatarData/" + avatarName + ".customModel");
var jObject = JsonUtility.FromJson<CustomAvatarData.CustomSkindMeshData>(Encoding.UTF8.GetString(json));
GenerateChildren(jObject.SkeletonRoot, jObject.skeletonDatas, transform);
var anim = gameObject.AddComponent<Animator>();
anim.runtimeAnimatorController = animatorController;
if (jObject.gender.Equals(CustomAvatarData.Gender.male))
{
anim.avatar = avatarMale;
}
else
{
anim.avatar = avatarFemale;
}
ml.Add(jObject.meshData);
CombineSuitsToSkeleton(this.gameObject, ml.ToArray(), jObject.mainTextureBase64, jObject.normalTextureBase64);
}
}
IEnumerator GetRequest(string uri)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
// Request and wait for the desired page.
yield return webRequest.SendWebRequest();
string[] pages = uri.Split('/');
int page = pages.Length - 1;
switch (webRequest.result)
{
case UnityWebRequest.Result.ConnectionError:
case UnityWebRequest.Result.DataProcessingError:
Debug.LogError(pages[page] + ": Error: " + webRequest.error);
break;
case UnityWebRequest.Result.ProtocolError:
Debug.LogError(pages[page] + ": HTTP Error: " + webRequest.error);
break;
case UnityWebRequest.Result.Success:
Debug.Log(pages[page] + ":\nReceived: " + webRequest.downloadHandler.data);
List<CustomAvatarData.MeshData> ml = new List<CustomAvatarData.MeshData>();
var jObject = JsonUtility.FromJson<CustomAvatarData.CustomSkindMeshData>(Encoding.UTF8.GetString(webRequest.downloadHandler.data));
GenerateChildren(jObject.SkeletonRoot, jObject.skeletonDatas, transform);
var anim = gameObject.AddComponent<Animator>();
anim.runtimeAnimatorController = animatorController;
if (jObject.gender.Equals(CustomAvatarData.Gender.male))
{
anim.avatar = avatarMale;
}
else
{
anim.avatar = avatarFemale;
}
ml.Add(jObject.meshData);
CombineSuitsToSkeleton(this.gameObject, ml.ToArray(), jObject.mainTextureBase64, jObject.normalTextureBase64);
break;
}
}
}
private void GenerateChildren(string currentName, List<CustomAvatarData.SkeletonData> _skeletonDatas, Transform _parent)
{
foreach (var skeleton in _skeletonDatas)
{
if (skeleton.current.nodeName.Equals(currentName))
{
GameObject newGameObject = new GameObject(currentName);
newGameObject.transform.SetParent(_parent);
newGameObject.transform.localPosition = skeleton.current.position;
newGameObject.transform.localRotation = skeleton.current.rotation;
foreach (var child in skeleton.children)
{
foreach (var skeleton2 in _skeletonDatas)
{
if (skeleton2.current.nodeName.Equals(child))
{
GenerateChildren(skeleton2.current.nodeName, _skeletonDatas, newGameObject.transform);
}
}
}
break;
}
}
}
private SkinnedMeshRenderer CombineSuitsToSkeleton(GameObject skeleton, CustomAvatarData.MeshData[] meshes, string txtureBase64,string nomalBase64)
{
var transforms = new List<Transform>(skeleton.GetComponentsInChildren<Transform>(true));
var materials = new List<Material>();
var combineInstances = new List<CombineInstance>();
var bones = new List<Transform>();
foreach (var smr in meshes)
{
List<Material> mats = new List<Material>();
for (int i = 0; i < smr.meshMaterialCount; i++)
{
Material mat = Instantiate(matTemp);
Texture2D texture = new Texture2D(2, 2);
texture.LoadImage(Convert.FromBase64String(txtureBase64));
Texture2D normal = new Texture2D(2, 2);
print(nomalBase64);
normal.LoadImage(Convert.FromBase64String(nomalBase64));
mat.mainTexture = texture;
mat.SetTexture("_BumpMap", normal);
mats.Add(mat);
}
materials.AddRange(mats);
for (int sub = 0; sub < smr.subMeshCount; sub++)
{
Mesh ms = new Mesh();
ms.vertices = smr.meshVertices;
ms.triangles = smr.meshTriangles;
ms.boneWeights = smr.meshBoneWeight;
ms.uv = smr.meshUV;
ms.bindposes = smr.meshBindPoses;
ms.bounds = smr.meshBounds;
ms.normals = smr.meshNormals;
ms.tangents = smr.meshTangents;
CombineInstance ci = new CombineInstance();
ci.mesh = ms;
ci.subMeshIndex = sub;
combineInstances.Add(ci);
}
foreach (var b in smr.meshBonesName)
{
var t = transforms.Find(t => b.Equals(t.name));
if (t != null)
{
bones.Add(t);
}
}
}
print(materials.Count);
var newMaterial = Instantiate(matTemp);
var oldUV = new List<Vector2[]>();
// merge the texture
var Textures = new List<Texture2D>();
var normals = new List<Texture2D>();
foreach (Material m in materials)
{
Textures.Add(m.GetTexture("_MainTex") as Texture2D);
normals.Add(m.GetTexture("_BumpMap") as Texture2D);
}
Texture2D newDiffuseTex = new Texture2D(512, 512, TextureFormat.RGBA32, true);
Rect[] uvs = newDiffuseTex.PackTextures(Textures.ToArray(), 0);
newMaterial.mainTexture = newDiffuseTex;
newMaterial.EnableKeyword("_SPECULARHIGHLIGHTS_OFF");
newMaterial.SetFloat("_SpecularHighlights", 0f);
Texture2D normalTex = new Texture2D(512, 512, TextureFormat.RGBA32, true);
normalTex.PackTextures(normals.ToArray(), 0);
newMaterial.SetTexture("_BumpMap", normalTex);
// reset uv
Vector2[] uva, uvb;
for (int j = 0; j < combineInstances.Count; j++)
{
uva = (Vector2[])(combineInstances[j].mesh.uv);
uvb = new Vector2[uva.Length];
for (int k = 0; k < uva.Length; k++)
{
uvb[k] = new Vector2((uva[k].x * uvs[j].width) + uvs[j].x, (uva[k].y * uvs[j].height) + uvs[j].y);
}
oldUV.Add(combineInstances[j].mesh.uv);
combineInstances[j].mesh.uv = uvb;
}
var r = skeleton.AddComponent<SkinnedMeshRenderer>() ?? skeleton.GetComponent<SkinnedMeshRenderer>();
r.bones = bones.ToArray();
r.sharedMesh = new Mesh();
r.sharedMesh.CombineMeshes(combineInstances.ToArray(), true, false);
r.material = newMaterial;
r.rootBone = transform.GetChild(0).GetChild(0);
return r;
}
}
}