MeshCombiner.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using LunarCatsStudio.SuperCombiner;
  5. #if UNITY_EDITOR
  6. using UnityEditor;
  7. #endif
  8. /// <summary>
  9. /// This class manage the combine process of meshes and skinned meshes
  10. /// </summary>
  11. namespace LunarCatsStudio.SuperCombiner
  12. {
  13. public class MeshCombiner
  14. {
  15. /// <summary>
  16. /// The session name
  17. /// </summary>
  18. private string _sessionName = "";
  19. /// <summary>
  20. /// List of Blendshape frames
  21. /// </summary>
  22. private Dictionary<string, BlendShapeFrame> blendShapes = new Dictionary<string, BlendShapeFrame>();
  23. private int _vertexOffset = 0;
  24. /// <summary>
  25. /// Flag to specify if UV2 have to be generated or not
  26. /// </summary>
  27. private bool _generateUv2 = true;
  28. /// <summary>
  29. /// The reference to the _combinedResult
  30. /// </summary>
  31. private CombinedResult _combinedResult;
  32. public CombinedResult CombinedResult
  33. {
  34. set
  35. {
  36. _combinedResult = value;
  37. }
  38. }
  39. /// <summary>
  40. /// Set the different parameters of MeshCombiner
  41. /// </summary>
  42. /// <param name="sessionName_p"></param>
  43. /// <param name="generateUv2_p"></param>
  44. public void SetParameters(string sessionName_p, bool generateUv2_p)
  45. {
  46. _sessionName = sessionName_p;
  47. _generateUv2 = generateUv2_p;
  48. }
  49. public void Clear()
  50. {
  51. blendShapes.Clear();
  52. _vertexOffset = 0;
  53. }
  54. public List<GameObject> CombineToMeshes(List<MeshRenderer> meshRenderers, List<SkinnedMeshRenderer> skinnedMeshRenderers, Transform parent, int combinedIndex)
  55. {
  56. // The list of Meshes created
  57. List<GameObject> combinedMeshes = new List<GameObject>();
  58. // The list of _combineInstances
  59. CombineInstanceID combineInstances = new CombineInstanceID();
  60. int verticesCount = 0;
  61. int combinedGameObjectCount = 0;
  62. // Process meshes for combine process
  63. for (int i = 0; i < meshRenderers.Count; i++)
  64. {
  65. // Loop over all submeshes
  66. for (int j = 0; j < meshRenderers[i].GetComponent<MeshFilter>().sharedMesh.subMeshCount; j++)
  67. {
  68. // Get a copy of the submesh at index j
  69. Mesh newMesh = SubmeshSplitter.ExtractSubmesh(meshRenderers[i].GetComponent<MeshFilter>().sharedMesh, j);
  70. verticesCount += newMesh.vertexCount;
  71. // Create the list of CombineInstance for this mesh
  72. Matrix4x4 matrix = parent.transform.worldToLocalMatrix * meshRenderers[i].transform.localToWorldMatrix;
  73. combineInstances.AddRange(CreateCombinedInstances(newMesh, new Material[] { meshRenderers[i].sharedMaterials[j] }, meshRenderers[i].gameObject.GetInstanceID(), meshRenderers[i].gameObject.name, matrix, combinedIndex));
  74. }
  75. }
  76. for (int i = 0; i < skinnedMeshRenderers.Count; i++)
  77. {
  78. // Loop over all submeshes
  79. for (int j = 0; j < meshRenderers[i].GetComponent<MeshFilter>().sharedMesh.subMeshCount; j++)
  80. {
  81. // Get a snapshot of the sub skinnedMesh renderer at index j
  82. Mesh newMesh = SubmeshSplitter.ExtractSubmesh(skinnedMeshRenderers[i].sharedMesh, j);
  83. _vertexOffset += newMesh.vertexCount;
  84. verticesCount += newMesh.vertexCount;
  85. // Create the list of CombineInstance for this skinnedMesh
  86. Matrix4x4 matrix = parent.transform.worldToLocalMatrix * skinnedMeshRenderers[i].transform.localToWorldMatrix;
  87. combineInstances.AddRange(CreateCombinedInstances(newMesh, new Material[] { skinnedMeshRenderers[i].sharedMaterials[j] }, skinnedMeshRenderers[i].GetInstanceID(), skinnedMeshRenderers[i].gameObject.name, matrix, combinedIndex));
  88. }
  89. }
  90. if (combineInstances.Count() > 0)
  91. {
  92. // Create the combined GameObject which contains the combined meshes
  93. combinedMeshes.Add(CreateCombinedMeshGameObject(combineInstances, parent, combinedGameObjectCount, combinedIndex));
  94. }
  95. return combinedMeshes;
  96. }
  97. public List<GameObject> CombineToSkinnedMeshes(List<MeshRenderer> meshRenderers, List<SkinnedMeshRenderer> skinnedMeshRenderers, Transform parent, int combinedIndex)
  98. {
  99. // The list of Meshes created
  100. List<GameObject> combinedMeshes = new List<GameObject>();
  101. // The list of _combineInstances
  102. CombineInstanceID combineInstances = new CombineInstanceID();
  103. int verticesCount = 0;
  104. int combinedGameObjectCount = 0;
  105. /*
  106. / Skinned mesh parameters
  107. */
  108. // List of bone weight
  109. List<BoneWeight> boneWeights = new List<BoneWeight>();
  110. // List of bones
  111. List<Transform> bones = new List<Transform>();
  112. // List of bindposes
  113. List<Matrix4x4> bindposes = new List<Matrix4x4>();
  114. // List of original bones mapped to their instanceId
  115. Dictionary<int, Transform> originalBones = new Dictionary<int, Transform>();
  116. // Link original bone instanceId to the new created bones
  117. Dictionary<int, Transform> originToNewBoneMap = new Dictionary<int, Transform>();
  118. // The vertices count
  119. int boneOffset = 0;
  120. // Get bones hierarchies from all skinned mesh
  121. for (int i = 0; i < skinnedMeshRenderers.Count; i++)
  122. {
  123. foreach (Transform t in skinnedMeshRenderers[i].bones)
  124. {
  125. if (!originalBones.ContainsKey(t.GetInstanceID()))
  126. {
  127. originalBones.Add(t.GetInstanceID(), t);
  128. }
  129. }
  130. }
  131. // Find the root bones
  132. Transform[] rootBones = FindRootBone(originalBones);
  133. for (int i = 0; i < rootBones.Length; i++)
  134. {
  135. // Instantiate the GameObject parent for this rootBone
  136. GameObject rootBoneParent = new GameObject("rootBone" + i);
  137. rootBoneParent.transform.position = rootBones[i].position;
  138. rootBoneParent.transform.parent = parent;
  139. rootBoneParent.transform.localPosition -= rootBones[i].localPosition;
  140. rootBoneParent.transform.localRotation = Quaternion.identity;
  141. // Instanciate a copy of the root bone
  142. GameObject newRootBone = InstantiateCopy(rootBones[i].gameObject);
  143. newRootBone.transform.position = rootBones[i].position;
  144. newRootBone.transform.rotation = rootBones[i].rotation;
  145. newRootBone.transform.parent = rootBoneParent.transform;
  146. newRootBone.AddComponent<MeshRenderer>();
  147. // Get the correspondancy map between original bones and new bones
  148. GetOrignialToNewBonesCorrespondancy(rootBones[i], newRootBone.transform, originToNewBoneMap);
  149. }
  150. // Copy Animator Controllers to new Combined GameObject
  151. foreach (Animator anim in parent.parent.GetComponentsInChildren<Animator>())
  152. {
  153. Transform[] children = anim.GetComponentsInChildren<Transform>();
  154. // Find the transform into which a copy of the Animator component will be added
  155. Transform t = FindTransformForAnimator(children, rootBones, anim);
  156. if (t != null)
  157. {
  158. CopyAnimator(anim, originToNewBoneMap[t.GetInstanceID()].parent.gameObject);
  159. }
  160. }
  161. for (int i = 0; i < skinnedMeshRenderers.Count; i++)
  162. {
  163. // Get a snapshot of the skinnedMesh renderer
  164. Mesh mesh = copyMesh(skinnedMeshRenderers[i].sharedMesh, skinnedMeshRenderers[i].GetInstanceID().ToString());
  165. _vertexOffset += mesh.vertexCount;
  166. verticesCount += skinnedMeshRenderers[i].sharedMesh.vertexCount;
  167. // Copy bone weights
  168. BoneWeight[] meshBoneweight = skinnedMeshRenderers[i].sharedMesh.boneWeights;
  169. foreach (BoneWeight bw in meshBoneweight)
  170. {
  171. BoneWeight bWeight = bw;
  172. bWeight.boneIndex0 += boneOffset;
  173. bWeight.boneIndex1 += boneOffset;
  174. bWeight.boneIndex2 += boneOffset;
  175. bWeight.boneIndex3 += boneOffset;
  176. boneWeights.Add(bWeight);
  177. }
  178. boneOffset += skinnedMeshRenderers[i].bones.Length;
  179. // Copy bones and bindposes
  180. Transform[] meshBones = skinnedMeshRenderers[i].bones;
  181. foreach (Transform bone in meshBones)
  182. {
  183. bones.Add(originToNewBoneMap[bone.GetInstanceID()]);
  184. bindposes.Add(bone.worldToLocalMatrix * parent.transform.localToWorldMatrix);
  185. }
  186. // Create the list of CombineInstance for this skinnedMesh
  187. Matrix4x4 matrix = parent.transform.worldToLocalMatrix * skinnedMeshRenderers[i].transform.localToWorldMatrix;
  188. combineInstances.AddRange(CreateCombinedInstances(mesh, skinnedMeshRenderers[i].sharedMaterials, skinnedMeshRenderers[i].GetInstanceID(), skinnedMeshRenderers[i].gameObject.name, matrix, combinedIndex));
  189. }
  190. if (combineInstances.Count() > 0)
  191. {
  192. // Create the combined GameObject which contains the combined meshes
  193. // Create the new GameObject
  194. GameObject combinedGameObject = CreateCombinedSkinnedMeshGameObject(combineInstances, parent, combinedGameObjectCount, combinedIndex);
  195. // Assign skinnedMesh parameters values
  196. SkinnedMeshRenderer sk = combinedGameObject.GetComponent<SkinnedMeshRenderer>();
  197. AssignParametersToSkinnedMesh(sk, bones, boneWeights, bindposes);
  198. combinedMeshes.Add(combinedGameObject);
  199. }
  200. return combinedMeshes;
  201. }
  202. // Assign the parameters of the new skinnedMesh
  203. private void AssignParametersToSkinnedMesh(SkinnedMeshRenderer skin, List<Transform> bones, List<BoneWeight> boneWeights, List<Matrix4x4> bindposes)
  204. {
  205. // Complete bone weights list if some are missing. BoneWeight is either empty or has the same quantity of elements than vertexCount
  206. if (boneWeights.Count > 0)
  207. {
  208. for (int i = boneWeights.Count; i < skin.sharedMesh.vertexCount; i++)
  209. {
  210. boneWeights.Add(boneWeights[0]);
  211. }
  212. }
  213. skin.bones = bones.ToArray();
  214. //sk.rootBone = newRootBone.transform;
  215. skin.sharedMesh.boneWeights = boneWeights.ToArray();
  216. skin.sharedMesh.bindposes = bindposes.ToArray();
  217. skin.sharedMesh.RecalculateBounds();
  218. skin.sharedMesh.RecalculateNormals();
  219. bones.Clear();
  220. boneWeights.Clear();
  221. bindposes.Clear();
  222. _vertexOffset = 0;
  223. }
  224. // Copy the animator component to the transform
  225. private void CopyAnimator(Animator anim, GameObject target)
  226. {
  227. if (target.GetComponentsInChildren<Animator>().Length == 0)
  228. {
  229. Animator newAnimator = target.AddComponent(typeof(Animator)) as Animator;
  230. if (newAnimator != null)
  231. {
  232. newAnimator.applyRootMotion = anim.applyRootMotion;
  233. newAnimator.avatar = anim.avatar;
  234. newAnimator.updateMode = anim.updateMode;
  235. newAnimator.cullingMode = anim.cullingMode;
  236. newAnimator.runtimeAnimatorController = anim.runtimeAnimatorController;
  237. }
  238. }
  239. }
  240. // Find the transform in which to instanciate the animator component
  241. private Transform FindTransformForAnimator(Transform[] children, Transform[] rootBones, Animator anim)
  242. {
  243. foreach (Transform t in children)
  244. {
  245. for (int i = 0; i < rootBones.Length; i++)
  246. {
  247. if (t.Equals(rootBones[i]))
  248. {
  249. return rootBones[i];
  250. }
  251. }
  252. }
  253. return null;
  254. }
  255. // Return a correspondancy map between original bones and the new bones
  256. private void GetOrignialToNewBonesCorrespondancy(Transform rootBone, Transform newRootBone, Dictionary<int, Transform> originToNewBoneMap)
  257. {
  258. Transform[] rootBoneTransforms = rootBone.GetComponentsInChildren<Transform>();
  259. Transform[] newRootBoneTransforms = newRootBone.GetComponentsInChildren<Transform>();
  260. // Get correspondancy between original bones and the new ones recently created
  261. for (int i = 0; i < newRootBoneTransforms.Length; i++)
  262. {
  263. if (!originToNewBoneMap.ContainsKey(rootBoneTransforms[i].GetInstanceID()))
  264. {
  265. originToNewBoneMap.Add(rootBoneTransforms[i].GetInstanceID(), newRootBoneTransforms[i]);
  266. }
  267. else
  268. {
  269. Logger.Instance.AddLog("SuperCombiner", " Found duplicated root bone: " + rootBoneTransforms[i], Logger.LogLevel.LOG_WARNING);
  270. }
  271. }
  272. }
  273. // Find the list of root bone from a hierachy of bones
  274. private Transform[] FindRootBone(Dictionary<int, Transform> bones)
  275. {
  276. List<Transform> rootBones = new List<Transform>();
  277. List<Transform> bonesList = new List<Transform>(bones.Values);
  278. if (bonesList.Count == 0)
  279. {
  280. return rootBones.ToArray();
  281. }
  282. Transform rootBone = bonesList.ToArray()[0];
  283. while (rootBone.parent != null)
  284. {
  285. if (bones.ContainsKey(rootBone.parent.GetInstanceID()))
  286. {
  287. rootBone = rootBone.parent;
  288. }
  289. else
  290. {
  291. rootBones.Add(rootBone.parent);
  292. Transform[] children = rootBone.parent.GetComponentsInChildren<Transform>();
  293. foreach (Transform t in children)
  294. {
  295. bones.Remove(t.GetInstanceID());
  296. if (t != rootBone.parent && rootBones.Contains(t))
  297. {
  298. rootBones.Remove(t);
  299. }
  300. }
  301. Transform[] otherBones = (new List<Transform>(bones.Values)).ToArray();
  302. if (otherBones.Length > 0)
  303. {
  304. rootBone = otherBones[0];
  305. }
  306. else
  307. {
  308. break;
  309. }
  310. }
  311. }
  312. return rootBones.ToArray();
  313. }
  314. // Instantiate a copy of the GameObject, keeping it's transform values identical
  315. private GameObject InstantiateCopy(GameObject original)
  316. {
  317. GameObject copy = GameObject.Instantiate(original) as GameObject;
  318. copy.transform.parent = original.transform.parent;
  319. copy.transform.localPosition = original.transform.localPosition;
  320. copy.transform.localRotation = original.transform.localRotation;
  321. copy.transform.localScale = original.transform.localScale;
  322. copy.name = original.name;
  323. // Remove all SkinnedMeshRenderes that may be inside root hierarchy
  324. foreach (SkinnedMeshRenderer skin in copy.GetComponentsInChildren<SkinnedMeshRenderer>())
  325. {
  326. GameObject.DestroyImmediate(skin);
  327. }
  328. return copy;
  329. }
  330. /// <summary>
  331. /// Create a new combineInstance based on a new mesh
  332. /// </summary>
  333. /// <param name="mesh"></param>
  334. /// <param name="sharedMaterials"></param>
  335. /// <param name="instanceID"></param>
  336. /// <param name="name"></param>
  337. /// <param name="matrix"></param>
  338. /// <param name="combinedIndex"></param>
  339. /// <returns></returns>
  340. private CombineInstanceID CreateCombinedInstances(Mesh mesh, Material[] sharedMaterials, int instanceID, string name, Matrix4x4 matrix, int combinedIndex)
  341. {
  342. CombineInstanceID instances = new CombineInstanceID();
  343. int[] textureIndexes = new int[mesh.subMeshCount];
  344. for (int k = 0; k < mesh.subMeshCount; k++)
  345. {
  346. // Find corresponding _material for each submesh
  347. if (k < sharedMaterials.Length)
  348. {
  349. Material mat = sharedMaterials[k];
  350. textureIndexes[k] = _combinedResult.FindCorrespondingMaterialIndex(mat, combinedIndex);
  351. }
  352. else
  353. {
  354. Logger.Instance.AddLog("SuperCombiner", " Mesh '" + mesh.name + "' has " + mesh.subMeshCount + " submeshes but only " + sharedMaterials.Length + " _material(s) assigned", Logger.LogLevel.LOG_WARNING);
  355. break;
  356. }
  357. }
  358. // Update submesh count
  359. _combinedResult._subMeshCount += (mesh.subMeshCount - 1);
  360. // Generate new UVs only if there are more than 1 _material combined
  361. if (_combinedResult._originalMaterialList[combinedIndex].Count > 1)
  362. {
  363. GenerateUV(mesh, textureIndexes, _combinedResult._combinedMaterials[combinedIndex].scaleFactors.ToArray(), name, combinedIndex);
  364. }
  365. for (int k = 0; k < mesh.subMeshCount; k++)
  366. {
  367. instances.AddCombineInstance(k, mesh, matrix, instanceID, name);
  368. }
  369. return instances;
  370. }
  371. /// <summary>
  372. /// Create a new GameObject based on the CombineInstance list
  373. /// </summary>
  374. /// <param name="instances"></param>
  375. /// <param name="parent"></param>
  376. /// <param name="number"></param>
  377. /// <returns></returns>
  378. private GameObject CreateCombinedSkinnedMeshGameObject(CombineInstanceID instances, Transform parent, int number, int combinedIndex)
  379. {
  380. GameObject combined = new GameObject(_sessionName + number.ToString());
  381. SkinnedMeshRenderer skinnedMeshRenderer = combined.AddComponent<SkinnedMeshRenderer>();
  382. skinnedMeshRenderer.sharedMaterial = _combinedResult._combinedMaterials[combinedIndex].material;
  383. skinnedMeshRenderer.sharedMesh = new Mesh();
  384. skinnedMeshRenderer.sharedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
  385. skinnedMeshRenderer.sharedMesh.name = _sessionName + "_" + _combinedResult._combinedMaterials[combinedIndex].displayedIndex + "_mesh" + number;
  386. skinnedMeshRenderer.sharedMesh.CombineMeshes(instances._combineInstances.ToArray(), true, true);
  387. #if UNITY_5_3_OR_NEWER
  388. // Add blendShapes to new skinnedMesh renderer if needed
  389. foreach (BlendShapeFrame blendShape in blendShapes.Values)
  390. {
  391. Vector3[] detlaVertices = new Vector3[skinnedMeshRenderer.sharedMesh.vertexCount];
  392. Vector3[] detlaNormals = new Vector3[skinnedMeshRenderer.sharedMesh.vertexCount];
  393. Vector3[] detlaTangents = new Vector3[skinnedMeshRenderer.sharedMesh.vertexCount];
  394. for (int p = 0; p < blendShape._deltaVertices.Length; p++)
  395. {
  396. detlaVertices.SetValue(blendShape._deltaVertices[p], p + blendShape._vertexOffset);
  397. detlaNormals.SetValue(blendShape._deltaNormals[p], p + blendShape._vertexOffset);
  398. detlaTangents.SetValue(blendShape._deltaTangents[p], p + blendShape._vertexOffset);
  399. }
  400. skinnedMeshRenderer.sharedMesh.AddBlendShapeFrame(blendShape._shapeName, blendShape._frameWeight, detlaVertices, detlaNormals, detlaTangents);
  401. }
  402. #endif
  403. #if UNITY_EDITOR
  404. MeshUtility.Optimize(skinnedMeshRenderer.sharedMesh);
  405. #endif
  406. combined.transform.SetParent(parent);
  407. combined.transform.localPosition = Vector3.zero;
  408. _combinedResult._totalVertexCount += skinnedMeshRenderer.sharedMesh.vertexCount;
  409. _combinedResult.AddCombinedMesh(skinnedMeshRenderer.sharedMesh, instances, combinedIndex);
  410. return combined;
  411. }
  412. /// <summary>
  413. /// Create a new GameObject based on the CombineInstance list.
  414. /// Set its MeshFilter and MeshRenderer to the new combined Meshe/Material
  415. /// </summary>
  416. /// <param name="instances"></param>
  417. /// <param name="parent"></param>
  418. /// <param name="number"></param>
  419. /// <returns></returns>
  420. public GameObject CreateCombinedMeshGameObject(CombineInstanceID instances, Transform parent, int number, int combinedIndex)
  421. {
  422. GameObject combined;
  423. MeshFilter meshFilter;
  424. MeshRenderer meshRenderer;
  425. // If parent has components MeshFilters and MeshRenderers, replace meshes and materials
  426. if (number == 0 && parent.GetComponent<MeshFilter>() != null && parent.GetComponent<MeshRenderer>() != null)
  427. {
  428. combined = parent.gameObject;
  429. meshFilter = parent.GetComponent<MeshFilter>();
  430. meshRenderer = parent.GetComponent<MeshRenderer>();
  431. }
  432. else
  433. {
  434. combined = new GameObject(_sessionName + "_" + _combinedResult._combinedMaterials[combinedIndex].displayedIndex + "_" + number.ToString());
  435. meshFilter = combined.AddComponent<MeshFilter>();
  436. meshRenderer = combined.AddComponent<MeshRenderer>();
  437. combined.transform.SetParent(parent);
  438. combined.transform.localPosition = Vector3.zero;
  439. }
  440. meshRenderer.sharedMaterial = _combinedResult._combinedMaterials[combinedIndex].material;
  441. meshFilter.mesh = new Mesh();
  442. meshFilter.sharedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
  443. meshFilter.sharedMesh.name = _sessionName + "_" + _combinedResult._combinedMaterials[combinedIndex].displayedIndex + "_mesh" + number;
  444. meshFilter.sharedMesh.CombineMeshes(instances._combineInstances.ToArray());
  445. #if UNITY_EDITOR
  446. MeshUtility.Optimize(meshFilter.sharedMesh);
  447. if (_generateUv2)
  448. {
  449. Unwrapping.GenerateSecondaryUVSet(meshFilter.sharedMesh);
  450. }
  451. #endif
  452. _combinedResult._totalVertexCount += meshFilter.sharedMesh.vertexCount;
  453. _combinedResult.AddCombinedMesh(meshFilter.sharedMesh, instances, combinedIndex);
  454. return combined;
  455. }
  456. // Generate the new transformed gameobjects and apply new materials to them
  457. public bool GenerateUV(Mesh targetMesh, int[] textureIndex, float[] scaleFactors, string objectName, int combinedIndex)
  458. {
  459. int subMeshCount = targetMesh.subMeshCount;
  460. if (subMeshCount > textureIndex.Length)
  461. {
  462. Logger.Instance.AddLog("SuperCombiner", "GameObject '" + objectName + "' has submeshes with no _material assigned", Logger.LogLevel.LOG_WARNING);
  463. subMeshCount = textureIndex.Length;
  464. }
  465. Logger.Instance.AddLog("SuperCombiner", "Processing '" + objectName + "'...", Logger.LogLevel.LOG_DEBUG, false);
  466. Vector2[] uv = (Vector2[])(targetMesh.uv);
  467. if (uv.Length <= 0)
  468. {
  469. #if UNITY_EDITOR
  470. // The mesh does not have UVs, so we try to unwrap the UV's
  471. Logger.Instance.AddLog("SuperCombiner", "Object " + objectName + " doesn't have uv, SuperCombiner will try to unwrap it's uvs but the result may be incorrect. In order to avoid this potential issue, add uv map to this mesh with a 3d modeler tool.", Logger.LogLevel.LOG_WARNING);
  472. Vector2[] uvTemp = Unwrapping.GeneratePerTriangleUV(targetMesh);
  473. uv = new Vector2[targetMesh.vertexCount];
  474. for (int i = 0; i < targetMesh.vertexCount; i++)
  475. {
  476. uv[i] = uvTemp[i];
  477. }
  478. targetMesh.uv = new Vector2[uv.Length];
  479. #endif
  480. }
  481. Vector2[] uv2 = (Vector2[])(targetMesh.uv2);
  482. Vector2[] new_uv = new Vector2[uv.Length];
  483. Vector2[] new_uv2 = new Vector2[uv2.Length];
  484. Rect[] uvsInAtlasTexture = new Rect[subMeshCount];
  485. if (new_uv.Length > 0)
  486. {
  487. for (int i = 0; i < subMeshCount; i++)
  488. {
  489. // Get the list of triangles for the current submesh
  490. int[] subMeshTriangles = targetMesh.GetTriangles(i);
  491. if (textureIndex[i] < _combinedResult._combinedMaterials[combinedIndex].uvs.Length)
  492. {
  493. uvsInAtlasTexture[i] = _combinedResult._combinedMaterials[combinedIndex].uvs[textureIndex[i]];
  494. // Target UV calculation, taking into account main map's scale and offset of the original _material
  495. Rect targetUV = new Rect(uvsInAtlasTexture[i].position, uvsInAtlasTexture[i].size);
  496. float factor = scaleFactors[textureIndex[i]];
  497. if (factor > 1)
  498. {
  499. targetUV.size = Vector2.Scale(targetUV.size, Vector2.one / factor);
  500. targetUV.position += new Vector2(uvsInAtlasTexture[i].width * (1 - 1 / factor) / 2f, uvsInAtlasTexture[i].height * (1 - 1 / factor) / 2f);
  501. }
  502. float xMin = _combinedResult._combinedMaterials[combinedIndex].meshUVBounds[textureIndex[i]].xMin;
  503. float yMin = _combinedResult._combinedMaterials[combinedIndex].meshUVBounds[textureIndex[i]].yMin;
  504. float width = _combinedResult._combinedMaterials[combinedIndex].meshUVBounds[textureIndex[i]].width;
  505. float height = _combinedResult._combinedMaterials[combinedIndex].meshUVBounds[textureIndex[i]].height;
  506. for (int j = 0; j < subMeshTriangles.Length; j++)
  507. {
  508. int uvIndex = subMeshTriangles[j];
  509. new_uv[uvIndex] = uv[uvIndex];
  510. // Translate new mesh's uvs so that minimun is at coordinates (0, 0)
  511. new_uv[uvIndex].x -= xMin;
  512. new_uv[uvIndex].y -= yMin;
  513. // Scale (if necessary) new mesh's uvs so that it fits in a (1, 1) square
  514. if (width != 0 && width != 1)
  515. {
  516. new_uv[uvIndex].Scale(new Vector2(1 / width, 1));
  517. }
  518. if (height != 0 && height != 1)
  519. {
  520. new_uv[uvIndex].Scale(new Vector2(1, 1 / height));
  521. }
  522. // Scale and translate new uvs to fit the correct texture in the atlas
  523. new_uv[uvIndex].Scale(targetUV.size);
  524. new_uv[uvIndex] += targetUV.position;
  525. }
  526. }
  527. else
  528. {
  529. Logger.Instance.AddLog("SuperCombiner", "Texture _index exceed packed texture size", Logger.LogLevel.LOG_ERROR);
  530. }
  531. }
  532. }
  533. else
  534. {
  535. Logger.Instance.AddLog("SuperCombiner", "Object " + objectName + " doesn't have uv, combine process may be incorrect. Add uv map with a 3d modeler tool.", Logger.LogLevel.LOG_WARNING);
  536. }
  537. // Assign new uv
  538. targetMesh.uv = new_uv;
  539. // Lightmap
  540. if (_generateUv2)
  541. {
  542. if (uv2 != null && uv2.Length > 0 && _combinedResult._combinedMaterials[combinedIndex].uvs2 != null && _combinedResult._combinedMaterials[combinedIndex].uvs2.Length > 0)
  543. {
  544. for (int l = 0; l < uv2.Length; l++)
  545. {
  546. new_uv2[uv2.Length + l] = new Vector2((uv2[l].x * _combinedResult._combinedMaterials[combinedIndex].uvs2[textureIndex[0]].width) + _combinedResult._combinedMaterials[combinedIndex].uvs2[textureIndex[0]].x, (uv2[l].y * _combinedResult._combinedMaterials[combinedIndex].uvs2[textureIndex[0]].height) + _combinedResult._combinedMaterials[combinedIndex].uvs2[textureIndex[0]].y);
  547. }
  548. targetMesh.uv2 = new_uv2;
  549. }
  550. else
  551. {
  552. // target mesh doesn't have uv2
  553. }
  554. }
  555. return true;
  556. }
  557. // Copy a Mesh into a new instance
  558. public Mesh copyMesh(Mesh mesh, string id = "")
  559. {
  560. Mesh copy = new Mesh();
  561. copy.indexFormat = mesh.indexFormat; // Carefull to set indexFormat before setting subMeshCount!
  562. copy.subMeshCount = mesh.subMeshCount;
  563. copy.vertices = mesh.vertices;
  564. copy.normals = mesh.normals;
  565. copy.uv = mesh.uv;
  566. copy.uv2 = mesh.uv2;
  567. copy.uv3 = mesh.uv3;
  568. copy.uv4 = mesh.uv4;
  569. copy.tangents = mesh.tangents;
  570. copy.bindposes = mesh.bindposes;
  571. copy.boneWeights = mesh.boneWeights;
  572. copy.bounds = mesh.bounds;
  573. copy.colors32 = mesh.colors32;
  574. copy.name = mesh.name;
  575. copy.subMeshCount = mesh.subMeshCount;
  576. for (int i = 0; i < mesh.subMeshCount; i++)
  577. {
  578. copy.SetIndices(mesh.GetIndices(i), mesh.GetTopology(i), i);
  579. copy.SetTriangles(mesh.GetTriangles(i), i);
  580. }
  581. #if UNITY_5_3_OR_NEWER
  582. // Blendshape management
  583. if (mesh.blendShapeCount > 0)
  584. {
  585. Vector3[] deltaVertices = new Vector3[mesh.vertexCount];
  586. Vector3[] deltaNormals = new Vector3[mesh.vertexCount];
  587. Vector3[] deltaTangents = new Vector3[mesh.vertexCount];
  588. for (int s = 0; s < mesh.blendShapeCount; s++)
  589. {
  590. for (int f = 0; f < mesh.GetBlendShapeFrameCount(s); f++)
  591. {
  592. if (!blendShapes.ContainsKey(mesh.GetBlendShapeName(s) + id))
  593. {
  594. // Copy blendShape to the new mesh
  595. mesh.GetBlendShapeFrameVertices(s, f, deltaVertices, deltaNormals, deltaTangents);
  596. copy.AddBlendShapeFrame(
  597. mesh.GetBlendShapeName(s),
  598. mesh.GetBlendShapeFrameWeight(s, f),
  599. deltaVertices, deltaNormals, deltaTangents
  600. );
  601. // Add this blendShape to the list
  602. blendShapes.Add(mesh.GetBlendShapeName(s) + id, new BlendShapeFrame(mesh.GetBlendShapeName(s) + id, mesh.GetBlendShapeFrameWeight(s, f), deltaVertices, deltaNormals, deltaTangents, _vertexOffset));
  603. }
  604. }
  605. }
  606. }
  607. #endif
  608. return copy;
  609. }
  610. // Copy a new mesh and assign it to destination
  611. private void CopyNewMeshesByCombine(Mesh original, Mesh destination)
  612. {
  613. int subMeshCount = original.subMeshCount;
  614. CombineInstance[] combineInstances = new CombineInstance[subMeshCount];
  615. for (int j = 0; j < subMeshCount; j++)
  616. {
  617. combineInstances[j] = new CombineInstance();
  618. combineInstances[j].subMeshIndex = j;
  619. combineInstances[j].mesh = original;
  620. combineInstances[j].transform = Matrix4x4.identity;
  621. }
  622. destination.CombineMeshes(combineInstances, false);
  623. }
  624. public GameObject CombineMeshToSubmeshes(List<Mesh> meshes, MeshOutput output)
  625. {
  626. int vertexOffset = 0;
  627. List<int[]> indices = new List<int[]>();
  628. List<int[]> triangles = new List<int[]>();
  629. List<Vector3> vertices = new List<Vector3>();
  630. List<Vector3[]> verticesArray = new List<Vector3[]>();
  631. List<Vector3> normals = new List<Vector3>();
  632. List<Vector4> tangents = new List<Vector4>();
  633. List<Color32> colors = new List<Color32>();
  634. List<Vector2> uv0 = new List<Vector2>();
  635. List<Vector2> uv2 = new List<Vector2>();
  636. for (int i = 0; i < meshes.Count; i++)
  637. {
  638. // Indices
  639. indices.Add(meshes[i].GetIndices(0));
  640. // Triangles
  641. int[] trianglesTmp = meshes[i].GetTriangles(0);
  642. for (int j = 0; j < trianglesTmp.Length; j++)
  643. {
  644. trianglesTmp[j] = trianglesTmp[j] + vertexOffset;
  645. }
  646. triangles.Add(trianglesTmp);
  647. // Vertices
  648. List<Vector3> vertexTmp = new List<Vector3>();
  649. meshes[i].GetVertices(vertexTmp);
  650. vertices.AddRange(vertexTmp);
  651. vertexOffset += vertexTmp.Count;
  652. // Normals
  653. List<Vector3> normalsTmp = new List<Vector3>();
  654. meshes[i].GetNormals(normalsTmp);
  655. normals.AddRange(normalsTmp);
  656. // Tangents
  657. List<Vector4> tangentsTmp = new List<Vector4>();
  658. meshes[i].GetTangents(tangentsTmp);
  659. tangents.AddRange(tangentsTmp);
  660. // Colors
  661. List<Color32> colorsTmp = new List<Color32>();
  662. meshes[i].GetColors(colorsTmp);
  663. colors.AddRange(colorsTmp);
  664. // UVs
  665. List<Vector2> uvTmp = new List<Vector2>();
  666. meshes[i].GetUVs(0, uvTmp);
  667. uv0.AddRange(uvTmp);
  668. meshes[i].GetUVs(1, uvTmp); // channel 1 is uv2
  669. uv2.AddRange(uvTmp);
  670. }
  671. GameObject go = new GameObject(_sessionName);
  672. Mesh mesh = new Mesh();
  673. if (output == LunarCatsStudio.SuperCombiner.MeshOutput.Mesh)
  674. {
  675. MeshFilter meshFilter = go.AddComponent<MeshFilter>();
  676. meshFilter.sharedMesh = mesh;
  677. }
  678. else
  679. {
  680. SkinnedMeshRenderer skinnedMeshRenderer = go.AddComponent<SkinnedMeshRenderer>();
  681. skinnedMeshRenderer.sharedMesh = mesh;
  682. }
  683. mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
  684. mesh.name = _sessionName + "_mesh";
  685. mesh.subMeshCount = _combinedResult.GetMaterialGroupCount();
  686. mesh.SetVertices(vertices);
  687. mesh.SetNormals(normals);
  688. mesh.SetTangents(tangents);
  689. mesh.SetColors(colors);
  690. mesh.SetUVs(0, uv0);
  691. mesh.SetUVs(1, uv2);
  692. for (int i = 0; i < indices.Count; i++)
  693. {
  694. mesh.SetIndices(indices[i], mesh.GetTopology(0), i);
  695. mesh.SetTriangles(triangles[i], i);
  696. }
  697. mesh.RecalculateBounds();
  698. #if UNITY_EDITOR
  699. MeshUtility.Optimize(mesh);
  700. #endif
  701. if (output == LunarCatsStudio.SuperCombiner.MeshOutput.Mesh)
  702. {
  703. MeshRenderer meshRenderer = go.AddComponent<MeshRenderer>();
  704. meshRenderer.sharedMaterials = _combinedResult.GetCombinedMaterials().ToArray();
  705. }
  706. else
  707. {
  708. SkinnedMeshRenderer skinnedmeshRenderer = go.GetComponent<SkinnedMeshRenderer>();
  709. skinnedmeshRenderer.sharedMaterials = _combinedResult.GetCombinedMaterials().ToArray();
  710. }
  711. return go;
  712. }
  713. }
  714. }