{"id":307,"date":"2016-04-22T23:48:51","date_gmt":"2016-04-22T23:48:51","guid":{"rendered":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/?p=307"},"modified":"2016-04-25T21:25:58","modified_gmt":"2016-04-25T21:25:58","slug":"graphics-optimization-3-combining-static-objects-to-single-mesh","status":"publish","type":"post","link":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/2016\/04\/22\/graphics-optimization-3-combining-static-objects-to-single-mesh\/","title":{"rendered":"Graphics &#038; Optimization #3: Combining static objects"},"content":{"rendered":"<p>Rendering multiple meshes and objects is not a good choice for optimization. The starting\u00a0point of optimization should be decreasing number of draw call and batches. For this, we decreased the number of vertices and\u00a0materials as possible as we can. There were two method we used for our application: combining mesh in 3D software Blender and joining meshes through Unity scripting.<\/p>\n<p>Firstly, we could make separated meshes share one single material through\u00a0Blender. The easiest way is \u00a0just joining mesh objects each other. But, If there are the objects \u00a0which are not sharing UV map, we used a technic called &#8220;Texture Atlas&#8221;.<\/p>\n<p>The second method was using Unity scripting. This method is manually copying all meshes vertex information and appending in a single large array. below is the script of that.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-308 size-medium alignnone\" src=\"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/wp-content\/uploads\/2016\/04\/Untitled-7-300x200.png\" alt=\"Untitled-7\" width=\"300\" height=\"200\" srcset=\"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/wp-content\/uploads\/2016\/04\/Untitled-7-300x200.png 300w, https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/wp-content\/uploads\/2016\/04\/Untitled-7.png 720w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><br \/>\n<code><br \/>\nprivate Mesh CombineMeshes_fixed() {<br \/>\nMeshRenderer[] meshRenderers = this.gameObject.GetComponentsInChildren(false);<br \/>\nint totalVertexCount = 0;<br \/>\nint totalMeshCount = 0;<\/code><\/p>\n<p>\/\/<br \/>\nif(meshRenderers != null &amp;&amp; meshRenderers.Length &gt; 0) {<br \/>\nforeach(MeshRenderer meshRenderer in meshRenderers) {<br \/>\nMeshFilter filter = meshRenderer.gameObject.GetComponent();<br \/>\nif(filter != null &amp;&amp; filter.sharedMesh != null) {<br \/>\ntotalVertexCount += filter.sharedMesh.vertexCount;<br \/>\ntotalMeshCount++;<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>if(totalMeshCount == 0) {<br \/>\nDebug.Log(&#8220;SpaceInfo Exporter :: [Error] No meshes found in children.&#8221;);<br \/>\nreturn null;<br \/>\n}<br \/>\nif(totalMeshCount == 1) {<br \/>\nDebug.Log(&#8220;SpaceInfo Exporter :: [Error] Only 1 mesh found in children.&#8221;);<br \/>\nreturn null;<br \/>\n}<br \/>\nif(totalVertexCount &gt; 65535) {<br \/>\nDebug.Log(&#8220;SpaceInfo Exporter :: [Error] There are too many vertices to combine into 1 mesh (&#8220;+totalVertexCount+&#8221;). The max. limit is 65535&#8243;);<br \/>\nreturn null;<br \/>\n}<\/p>\n<p>Mesh mesh = new Mesh();<br \/>\nMatrix4x4 myTransform = this.gameObject.transform.worldToLocalMatrix;<br \/>\nList vertices = new List();<br \/>\nList normals = new List();<br \/>\nList uv1s = new List();<br \/>\nList uv2s = new List();<br \/>\nDictionary&lt;Material, List&gt; subMeshes = new Dictionary&lt;Material, List&gt;();<\/p>\n<p>if(meshRenderers != null &amp;&amp; meshRenderers.Length &gt; 0) {<br \/>\nforeach(MeshRenderer meshRenderer in meshRenderers) {<br \/>\nMeshFilter filter = meshRenderer.gameObject.GetComponent();<br \/>\nif(filter != null &amp;&amp; filter.sharedMesh != null) {<br \/>\nMergeMeshInto(filter.sharedMesh, meshRenderer.sharedMaterials, myTransform * filter.transform.localToWorldMatrix, vertices, normals, uv1s, uv2s, subMeshes);<br \/>\nif(filter.gameObject != this.gameObject) {<br \/>\nfilter.gameObject.SetActive(false);<br \/>\n}<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>mesh.vertices = vertices.ToArray();<br \/>\nif(normals.Count&gt;0) mesh.normals = normals.ToArray();<br \/>\nif(uv1s.Count&gt;0) mesh.uv = uv1s.ToArray();<br \/>\nif(uv2s.Count&gt;0) mesh.uv2 = uv2s.ToArray();<br \/>\nmesh.subMeshCount = subMeshes.Keys.Count;<br \/>\nMaterial[] materials = new Material[subMeshes.Keys.Count];<br \/>\nint mIdx = 0;<br \/>\nforeach(Material m in subMeshes.Keys) {<br \/>\nmaterials[mIdx] = m;<br \/>\nmesh.SetTriangles(subMeshes[m].ToArray(), mIdx++);<br \/>\n}<\/p>\n<p>if(meshRenderers != null &amp;&amp; meshRenderers.Length &gt; 0) {<br \/>\nMeshRenderer meshRend = this.gameObject.GetComponent();<br \/>\nif(meshRend == null) meshRend = this.gameObject.AddComponent();<br \/>\nmeshRend.sharedMaterials = materials;<\/p>\n<p>MeshFilter meshFilter = this.gameObject.GetComponent();<br \/>\nif(meshFilter == null) meshFilter = this.gameObject.AddComponent();<br \/>\nmeshFilter.sharedMesh = mesh;<br \/>\n}<br \/>\nreturn mesh;<br \/>\n}<\/p>\n<p>private static void MergeMeshInto(Mesh meshToMerge, Material[] ms, Matrix4x4 transformMatrix, List vertices, List normals, List uv1s, List uv2s, Dictionary&lt;Material, List&gt; subMeshes) {<br \/>\nif(meshToMerge == null) return;<br \/>\nint vertexOffset = vertices.Count;<br \/>\nVector3[] vs = meshToMerge.vertices;<\/p>\n<p>for(int i=0;i&lt;vs.Length;i++) { vs[i] = transformMatrix.MultiplyPoint3x4(vs[i]); } vertices.AddRange(vs); Quaternion rotation = Quaternion.LookRotation(transformMatrix.GetColumn(2), transformMatrix.GetColumn(1)); Vector3[] ns = meshToMerge.normals; if(ns!=null &amp;&amp; ns.Length&gt;0) {<br \/>\nfor(int i=0;i&lt;ns.Length;i++) ns[i] = rotation * ns[i]; normals.AddRange(ns); } Vector2[] uvs = meshToMerge.uv; if(uvs!=null &amp;&amp; uvs.Length&gt;0) uv1s.AddRange(uvs);<br \/>\nuvs = meshToMerge.uv2;<br \/>\nif(uvs!=null &amp;&amp; uvs.Length&gt;0) uv2s.AddRange(uvs);<\/p>\n<p>for(int i=0;i&lt;ms.Length;i++) {<br \/>\nif(i&lt;meshToMerge.subMeshCount) { int[] ts = meshToMerge.GetTriangles(i); if(ts.Length&gt;0) {<br \/>\nif(ms[i]!=null &amp;&amp; !subMeshes.ContainsKey(ms[i])) {<br \/>\nsubMeshes.Add(ms[i], new List());<br \/>\n}<br \/>\nList subMesh = subMeshes[ms[i]];<br \/>\nfor(int t=0;t&lt;ts.Length;t++) {<br \/>\nts[t] += vertexOffset;<br \/>\n}<br \/>\nsubMesh.AddRange(ts);<br \/>\n}<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Rendering multiple meshes and objects is not a good choice for optimization. The starting\u00a0point of optimization should be decreasing number of draw call and batches. For this, we decreased the number of vertices and\u00a0materials as possible as we can. There were two method we used for our application: combining mesh in 3D software Blender and <a href=\"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/2016\/04\/22\/graphics-optimization-3-combining-static-objects-to-single-mesh\/\" rel=\"nofollow\"><span class=\"sr-only\">Read more about Graphics &#038; Optimization #3: Combining static objects<\/span>[&hellip;]<\/a><\/p>\n","protected":false},"author":3,"featured_media":308,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"pgc_meta":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-307","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-vr-rehearsal"],"_links":{"self":[{"href":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/wp-json\/wp\/v2\/posts\/307"}],"collection":[{"href":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/wp-json\/wp\/v2\/comments?post=307"}],"version-history":[{"count":5,"href":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/wp-json\/wp\/v2\/posts\/307\/revisions"}],"predecessor-version":[{"id":313,"href":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/wp-json\/wp\/v2\/posts\/307\/revisions\/313"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/wp-json\/wp\/v2\/media\/308"}],"wp:attachment":[{"href":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/wp-json\/wp\/v2\/media?parent=307"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/wp-json\/wp\/v2\/categories?post=307"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/projects-old.etc.cmu.edu\/vr-rehearsal\/index.php\/wp-json\/wp\/v2\/tags?post=307"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}