├── .gitignore
├── MeshBaking.gif
├── README.md
└── SimplestMeshBaker
├── Editor
├── BonesBaker.cs
└── MeshBaker.cs
└── Readme.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | *.meta
--------------------------------------------------------------------------------
/MeshBaking.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kovnir/UnitySimplestMeshBaker/330f26f9d9da1a5e957fa12d41e0890de9a30c18/MeshBaking.gif
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | **Simplest Mesh Baker** is a small plugin that helps you bake several meshes to one in couple clicks.
4 |
5 | [Download from Asset Store](https://assetstore.unity.com/packages/tools/utilities/simplest-mesh-baker-118123)
6 |
7 | 
8 |
9 |
10 |
11 | In addition, it contains a **Bone Baker**. It is a simple wrapper over [SkinnedMeshRenderer.BakeMesh()](https://docs.unity3d.com/ScriptReference/SkinnedMeshRenderer.BakeMesh.html) method, which allows you to convert `SkinnedMeshRendered` to `MeshRenderer` considering transformations and poses.
12 |
13 | **Supported Unity versions:** 5.6.3 or higher
14 |
15 | ## How to use
16 |
17 |
18 | To install it, copy the **Simplest Mesh Baker** folder to your project.
19 |
20 |
21 |
22 | To bake the Meshes, select `GameObjects` with `MeshRenderer` or `SkinnedMeshRenderer` components on it or its children, make a right click and press **Bake Meshes**.
23 |
24 | One Mesh can't contain more than 65,000 vertices, so if source meshes together have more than 65,000 vertices, **Simplest Mesh Baker** will create several objects.
25 |
26 | The plugin will ask you: "*Do you want to separate objects with different materials?*". If you choose "*Yes*" **Simplest Mesh Baker** will bake source objects with different materials to the separated Meshes. If "*No*" **Simplest Mesh Baker** will bake all the meshes together and set one of the source materials for it.
27 |
28 | Also if several but not all meshed use `UVs`, `Vertex Colors`, or `Normals` **Simplest Mesh Baker** will ask how to resolve it. You can remove it from all objects or create with default values where it needed.
29 |
30 | Then **Simplest Mesh Baker** will ask you: "*Do you want to remove sources?*". Click "*Yes*" if so, or "*No*" if you want to leave these objects for now.
31 |
32 | Then the plugin will show a popup with the number of baked objects.
33 |
34 |
35 |
36 | To use **Bones Baker**, select `GameObjects` with `SkinnedMeshRenderer` components on it or its children, make a right click and press **Bake Bones**.
37 | The plugin will ask you: "*Do you want to remove bones after backing?*". Press "*Yes*" if so, or "*No*" if you want to leave bones for now.
38 |
39 |
40 | ## YouTube Tutorials
41 |
42 | [How to Bake Meshes](https://youtu.be/UxkcC0uZ6D8)
43 |
44 | [How to Bake Bones](https://youtu.be/95zAFspba1E)
45 |
--------------------------------------------------------------------------------
/SimplestMeshBaker/Editor/BonesBaker.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEditor;
3 | using UnityEngine;
4 |
5 | namespace SimplestMeshBaker
6 | {
7 | [CanEditMultipleObjects]
8 | public static class BonesBaker
9 | {
10 | private const int YES = 0;
11 | private const int NO = 2;
12 | private const int CANCEL = 1;
13 |
14 | enum QuestionResult
15 | {
16 | Yes = 0,
17 | No = 2,
18 | Cancel = 1,
19 | }
20 |
21 | private static List bonesToRemove;
22 |
23 | [MenuItem("GameObject/Bake Bones", false, 0)]
24 | static void BakeBones(MenuCommand menuCommand)
25 | {
26 | //Prevent executing multiple times
27 | if (Selection.objects.Length > 1)
28 | {
29 | if (menuCommand.context != Selection.objects[0])
30 | {
31 | return;
32 | }
33 | }
34 |
35 | QuestionResult questionResult = (QuestionResult) EditorUtility.DisplayDialogComplex("Simplest Mesh Baker", "Do you want to remove bones after backing?", "Yes", "Cancel", "No");
36 |
37 | if (questionResult == QuestionResult.Cancel)
38 | {
39 | return;
40 | }
41 |
42 | int count = 0;
43 |
44 | bonesToRemove = new List();
45 | foreach (GameObject selected in Selection.gameObjects)
46 | {
47 | count += Bake(selected);
48 | }
49 | if (questionResult == QuestionResult.Yes)
50 | {
51 | foreach (var transform in bonesToRemove)
52 | {
53 | if (transform != null)
54 | {
55 | Undo.DestroyObjectImmediate(transform.gameObject);
56 | }
57 | }
58 | }
59 | EditorUtility.DisplayDialog("Simplest Mesh Baker", "Baked " + count + " objects.",
60 | count == 0 ? "Hm, Ok." : "Great, thanks!");
61 | }
62 |
63 | private static int Bake(GameObject gameObject)
64 | {
65 | int count = 0;
66 | SkinnedMeshRenderer skinnedMeshRenderer = gameObject.GetComponent();
67 | if (skinnedMeshRenderer != null)
68 | {
69 | var newMesh = GetMeshFromSkinnedMeshRenderer(skinnedMeshRenderer);
70 | //get needed data from skinnedMeshRenderer
71 | Material material = skinnedMeshRenderer.sharedMaterial;
72 | Transform rootBone = skinnedMeshRenderer.rootBone;
73 | if (rootBone != null && rootBone.parent != null)
74 | {
75 | bonesToRemove.Add(rootBone.parent);
76 | }
77 | //and remove it
78 | Undo.DestroyObjectImmediate(skinnedMeshRenderer);
79 |
80 | // Generate UV2 for Lightmapping
81 | Unwrapping.GenerateSecondaryUVSet(newMesh);
82 |
83 | //add and setup meshFilter and meshRenderer
84 | MeshFilter meshFilter = Undo.AddComponent(gameObject);
85 | meshFilter.mesh = newMesh;
86 | MeshRenderer meshRenderer = Undo.AddComponent(gameObject);
87 | meshRenderer.sharedMaterial = material;
88 | count++;
89 | }
90 | foreach (Transform tr in gameObject.transform)
91 | {
92 | count += Bake(tr.gameObject);
93 | }
94 | return count;
95 | }
96 |
97 | public static Mesh GetMeshFromSkinnedMeshRenderer(SkinnedMeshRenderer skinnedMeshRenderer)
98 | {
99 | Mesh newMesh = new Mesh();
100 | skinnedMeshRenderer.BakeMesh(newMesh);
101 | //update with scaling scale
102 | Vector3[] verts = newMesh.vertices;
103 | float scaleX = skinnedMeshRenderer.transform.lossyScale.x;
104 | float scaleY = skinnedMeshRenderer.transform.lossyScale.y;
105 | float scaleZ = skinnedMeshRenderer.transform.lossyScale.z;
106 | for (int i = 0; i < verts.Length; i++)
107 | {
108 | verts[i] = new Vector3(verts[i].x / scaleX, verts[i].y / scaleY, verts[i].z / scaleZ);
109 | }
110 | newMesh.vertices = verts;
111 | newMesh.RecalculateBounds();
112 | return newMesh;
113 | }
114 | }
115 | }
--------------------------------------------------------------------------------
/SimplestMeshBaker/Editor/MeshBaker.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using UnityEditor;
4 | using UnityEngine;
5 |
6 | namespace SimplestMeshBaker
7 | {
8 | public class MeshBaker : EditorWindow
9 | {
10 | enum Resolving
11 | {
12 | Not,
13 | Remove,
14 | Create
15 | }
16 |
17 | private const int MAX_VERTEX_COUNT_PER_ONE_OBJECT = 65000;
18 |
19 | private static Resolving colorResolving;
20 | private static Resolving normalsResolving;
21 | private static Resolving tangentsResolving;
22 | private static Resolving uvResolving;
23 |
24 | private static int objectNum;
25 |
26 | [MenuItem("GameObject/Bake Meshes", false, 0)]
27 | private static void BakeMeshes(MenuCommand menuCommand)
28 | {
29 | if (Selection.objects.Length == 0)
30 | {
31 | return;
32 | }
33 | //Prevent executing multiple times
34 | if (Selection.objects.Length > 1)
35 | {
36 | if (menuCommand.context != Selection.objects[0])
37 | {
38 | return;
39 | }
40 | }
41 |
42 | List vertexes = new List();
43 | List normals = new List();
44 | List tangents = new List();
45 | List colors = new List();
46 | List uvs = new List();
47 | List triangles = new List();
48 |
49 | Dictionary> meshesWithMaterials = new Dictionary>();
50 | List meshesWithoutMaterials = new List();
51 |
52 | objectNum = 0;
53 | if (!FillDataAndCheckResolving(meshesWithMaterials, meshesWithoutMaterials))
54 | {
55 | return;
56 | }
57 |
58 | bool separateObjectsWithDifferentMaterials = false;
59 | if (meshesWithMaterials.Count + (meshesWithoutMaterials.Count > 0 ? 1 : 0) > 1)
60 | {
61 | separateObjectsWithDifferentMaterials = EditorUtility.DisplayDialog("Simplest Mesh Baker",
62 | "Do you want to separate objects with different materials?", "Yes", "No");
63 | }
64 |
65 | if (!separateObjectsWithDifferentMaterials)
66 | {
67 | Material material = null;
68 | foreach (var meshToBake in meshesWithMaterials)
69 | {
70 | material = meshToBake.Key;
71 | if (material != null)
72 | {
73 | break;
74 | }
75 | }
76 |
77 | foreach (var mesh in meshesWithoutMaterials)
78 | {
79 | Bake(mesh, vertexes, normals, tangents, colors, uvs, triangles, material);
80 | }
81 | foreach (var meshToBake in meshesWithMaterials)
82 | {
83 | foreach (var mesh in meshToBake.Value)
84 | {
85 | Bake(mesh, vertexes, normals, tangents, colors, uvs, triangles, material);
86 | }
87 | }
88 | if (vertexes.Count > 0)
89 | {
90 | CreateObject(vertexes, normals, tangents, colors, uvs, triangles, material);
91 | vertexes.Clear();
92 | normals.Clear();
93 | tangents.Clear();
94 | colors.Clear();
95 | uvs.Clear();
96 | triangles.Clear();
97 | }
98 | }
99 | else
100 | {
101 | foreach (var mesh in meshesWithoutMaterials)
102 | {
103 | Bake(mesh, vertexes, normals, tangents, colors, uvs, triangles, null);
104 | }
105 | if (vertexes.Count > 0)
106 | {
107 | CreateObject(vertexes, normals, tangents, colors, uvs, triangles, null);
108 | vertexes.Clear();
109 | normals.Clear();
110 | tangents.Clear();
111 | colors.Clear();
112 | uvs.Clear();
113 | triangles.Clear();
114 | }
115 | foreach (var meshToBake in meshesWithMaterials)
116 | {
117 | foreach (var mesh in meshToBake.Value)
118 | {
119 | Bake(mesh, vertexes, normals, tangents, colors, uvs, triangles, meshToBake.Key);
120 | }
121 | CreateObject(vertexes, normals, tangents, colors, uvs, triangles, meshToBake.Key);
122 | vertexes.Clear();
123 | normals.Clear();
124 | tangents.Clear();
125 | colors.Clear();
126 | uvs.Clear();
127 | triangles.Clear();
128 | }
129 | }
130 |
131 | if (EditorUtility.DisplayDialog("Simplest Mesh Baker",
132 | "Do you want to remove sources?", "Yes", "No"))
133 | {
134 | foreach (GameObject selected in Selection.gameObjects)
135 | {
136 | if (selected != null)
137 | {
138 | Undo.DestroyObjectImmediate(selected);
139 | }
140 | }
141 | }
142 |
143 | int meshesCount = meshesWithoutMaterials.Count;
144 | foreach (var bakedMeshes in meshesWithMaterials)
145 | {
146 | meshesCount += bakedMeshes.Value.Count;
147 | }
148 | EditorUtility.DisplayDialog("Simplest Mesh Baker",
149 | "Baked " + meshesCount + " meshes.", "Cool!");
150 | }
151 |
152 | private static bool FillDataAndCheckResolving(Dictionary> meshesWithMaterials, List meshesWithoutMaterials)
153 | {
154 | colorResolving = Resolving.Not;
155 | normalsResolving = Resolving.Not;
156 | uvResolving = Resolving.Not;
157 |
158 | bool anyHasColors = false;
159 | bool anyHasNormals = false;
160 | bool anyHasUVs = false;
161 | bool anyHasNotColors = false;
162 | bool anyHasNotNormals = false;
163 | bool anyHasNotUVs = false;
164 |
165 | HashSet transforms = new HashSet();
166 |
167 | foreach (GameObject selected in Selection.gameObjects)
168 | {
169 | MeshFilter[] meshFilters = selected.GetComponentsInChildren();
170 | foreach (var meshFilter in meshFilters)
171 | {
172 | if (transforms.Contains(meshFilter.transform))
173 | {
174 | continue;
175 | }
176 | Material material = null;
177 | var mr = meshFilter.GetComponent();
178 | if (mr != null)
179 | {
180 | material = mr.sharedMaterial;
181 | }
182 | Mesh mesh = Instantiate(meshFilter.sharedMesh);
183 | HandleMesh(meshesWithMaterials, meshesWithoutMaterials, mesh, meshFilter.transform, material, transforms, ref anyHasNotNormals, ref anyHasNotColors, ref anyHasNotUVs, ref anyHasNormals, ref anyHasColors, ref anyHasUVs);
184 | }
185 |
186 | SkinnedMeshRenderer[] skinnedMeshRenderers = selected.GetComponentsInChildren();
187 | foreach (var skinnedMeshRenderer in skinnedMeshRenderers)
188 | {
189 | if (transforms.Contains(skinnedMeshRenderer.transform))
190 | {
191 | continue;
192 | }
193 | Material material = skinnedMeshRenderer.sharedMaterial;
194 | Mesh mesh = BonesBaker.GetMeshFromSkinnedMeshRenderer(skinnedMeshRenderer);
195 | HandleMesh(meshesWithMaterials, meshesWithoutMaterials, mesh, skinnedMeshRenderer.transform, material, transforms, ref anyHasNotNormals, ref anyHasNotColors, ref anyHasNotUVs, ref anyHasNormals, ref anyHasColors, ref anyHasUVs);
196 | }
197 | }
198 |
199 | return SetResolving(anyHasNormals, anyHasNotNormals, ref normalsResolving, "normals") &&
200 | SetResolving(anyHasColors, anyHasNotColors, ref colorResolving, "colors") &&
201 | SetResolving(anyHasUVs, anyHasNotUVs, ref uvResolving, "uvs");
202 | }
203 |
204 | private static void HandleMesh(Dictionary> meshesWithMaterials, List meshesWithoutMaterials, Mesh mesh,
205 | Transform transform, Material material, HashSet transforms, ref bool anyHasNotNormals,
206 | ref bool anyHasNotColors, ref bool anyHasNotUVs, ref bool anyHasNormals, ref bool anyHasColors, ref bool anyHasUVs)
207 | {
208 | mesh.vertices = mesh.vertices.Select(transform.TransformPoint).ToArray();
209 | mesh.normals = mesh.normals.Select(transform.TransformDirection).ToArray();
210 |
211 | if (material == null)
212 | {
213 | meshesWithoutMaterials.Add(mesh);
214 | }
215 | else
216 | {
217 | if (meshesWithMaterials.ContainsKey(material))
218 | {
219 | meshesWithMaterials[material].Add(mesh);
220 | }
221 | else
222 | {
223 | meshesWithMaterials.Add(material, new List() {mesh});
224 | }
225 | }
226 | transforms.Add(transform);
227 |
228 | CheckMeshAttributes(mesh, ref anyHasNotNormals, ref anyHasNotColors, ref anyHasNotUVs, ref anyHasNormals,
229 | ref anyHasColors, ref anyHasUVs);
230 | }
231 |
232 | private static void CheckMeshAttributes(Mesh mesh, ref bool anyHasNotNormals, ref bool anyHasNotColors, ref bool anyHasNotUVs,
233 | ref bool anyHasNormals, ref bool anyHasColors, ref bool anyHasUVs)
234 | {
235 | bool hasNormals = mesh.vertexCount == mesh.normals.Length;
236 | bool hasColors = mesh.vertexCount == mesh.colors.Length;
237 | bool hasUVs = mesh.vertexCount == mesh.uv.Length;
238 |
239 | anyHasNotNormals |= !hasNormals;
240 | anyHasNotColors |= !hasColors;
241 | anyHasNotUVs |= !hasUVs;
242 |
243 | anyHasNormals |= hasNormals;
244 | anyHasColors |= hasColors;
245 | anyHasUVs |= hasUVs;
246 | }
247 |
248 | private static bool SetResolving(bool has, bool hasNot, ref Resolving resolving, string property)
249 | {
250 | if (has && hasNot)
251 | {
252 | var result = EditorUtility.DisplayDialogComplex("Simplest Mesh Baker",
253 | "Not all objects used " + property + ".",
254 | "Don't use " + property, //0
255 | "Cancel", //1
256 | "Create fake " + property //2
257 | );
258 | if (result == 1)
259 | {
260 | return false;
261 | }
262 | resolving = result == 0 ? Resolving.Remove : Resolving.Create;
263 | }
264 | return true;
265 | }
266 |
267 | private static void CreateObject(List vertexes, List normals, List tangents, List colors,
268 | List uvs, List triangles, Material material)
269 | {
270 | GameObject go = new GameObject();
271 | Undo.RegisterCreatedObjectUndo(go, "Create a new baked gameobject");
272 | objectNum++;
273 | go.name = "Baked Mesh" + (objectNum == 1 ? "" : " " + objectNum);
274 | MeshFilter mf = go.AddComponent();
275 | MeshRenderer mr = go.AddComponent();
276 | Mesh newMesh = new Mesh();
277 |
278 | newMesh.SetVertices(vertexes);
279 | if (normals.Count != 0 && normalsResolving != Resolving.Remove)
280 | {
281 | newMesh.SetNormals(normals);
282 | }
283 | if (tangents.Count != 0 && tangentsResolving != Resolving.Remove)
284 | {
285 | newMesh.SetTangents(tangents);
286 | }
287 | if (colors.Count != 0 && colorResolving != Resolving.Remove)
288 | {
289 | newMesh.SetColors(colors);
290 | }
291 | if (uvs.Count != 0 || uvResolving != Resolving.Remove)
292 | {
293 | newMesh.SetUVs(0, uvs);
294 | }
295 |
296 | newMesh.SetTriangles(triangles, 0);
297 | mf.sharedMesh = newMesh;
298 | mr.material = material;
299 | }
300 |
301 | private static void Bake(Mesh mesh, List vertexes, List normals, List tangents,
302 | List colors, List uvs, List triangles, Material material)
303 | {
304 | //mesh may not have more than 65000 vertices.
305 | if (vertexes.Count + mesh.vertexCount > MAX_VERTEX_COUNT_PER_ONE_OBJECT)
306 | {
307 | CreateObject(vertexes, normals, tangents, colors, uvs, triangles, material);
308 | vertexes.Clear();
309 | normals.Clear();
310 | tangents.Clear();
311 | colors.Clear();
312 | uvs.Clear();
313 | triangles.Clear();
314 | }
315 |
316 | int startCount = vertexes.Count;
317 | foreach (Vector3 vertex in mesh.vertices)
318 | {
319 | vertexes.Add(vertex);
320 | }
321 | foreach (int triangle in mesh.triangles)
322 | {
323 | triangles.Add(triangle + startCount);
324 | }
325 |
326 | FillOrResolve(mesh.normals, normals, mesh.vertices.Length, normalsResolving);
327 | FillOrResolve(mesh.tangents, tangents, mesh.tangents.Length, tangentsResolving);
328 | FillOrResolve(mesh.colors, colors, mesh.vertices.Length, colorResolving);
329 | FillOrResolve(mesh.uv, uvs, mesh.vertices.Length, uvResolving);
330 | }
331 |
332 | private static void FillOrResolve(T[] source, List distanation, int expectedCount,
333 | Resolving resolvingLogic)
334 | {
335 | if (source.Length == 0 && resolvingLogic == Resolving.Create)
336 | {
337 | for (int i = 0; i < expectedCount; i++)
338 | {
339 | distanation.Add(default(T));
340 | }
341 | }
342 | else
343 | {
344 | distanation.AddRange(source);
345 | }
346 | }
347 | }
348 | }
--------------------------------------------------------------------------------
/SimplestMeshBaker/Readme.txt:
--------------------------------------------------------------------------------
1 | Simplest Mesh Baker is a simple plugin which help you to bake several meshesh into one in couple clicks.
2 |
3 | Also it contains Bone Baker. It is a simple wrapper over SkinnedMeshRenderer.BakeMesh() method which let you transform SkinnedMeshRendered to MeshRenderer considering transformations and poses in two clicks.
4 |
5 |
6 |
7 | How to use:
8 |
9 |
10 | To install it copy Simplest Mesh Baker folder into your project.
11 |
12 |
13 | To Bake Meshes select GameObjects with MeshRenderer or SkinnedMeshRenderer components on it or on his childs, make a right click and press Bake Meshes.
14 | One mesh can't contains more than 65000 vertexes, so if models containt. If source meshes together has more than 65000 vertexes Mesh Baker will create several objects.
15 | The plugin will ask you "Do you want to separate objects with different materials?". If you will press "Yes" Simplest Mesh Baker will bake to the separated Meshes source objects with different materials. If "No" Simplest Mesh Baker will bake all meshes together and set him one of the source material.
16 | Also if several but not all meshed uses UVs, Vertex Colors, or Normals, Simplest Mesh Baker will ask how to resolve it. You can create remove it from all objects, all create with default values where it needed.
17 | Than Simplest Mesh Baker will ask you "Do you want to remove sources?". Press "Yes" if so, or "No" if you want to leave this objects for now.
18 | Than the plugin will show popup with count of baked objects.
19 |
20 |
21 |
22 | To Bake Bones select GameObjects with SkinnedMeshRenderer components on it or on his childs, make a right click and press Bake Bones.
23 | The plugin will ask you "Do you want to remove bones after backing?". Press "Yes" if so, or "No" if you want to leave bones for now.
24 |
25 |
26 |
27 | YouTube Tutorials:
28 |
29 | How to Bake Meshes - https://youtu.be/UxkcC0uZ6D8
30 | How to Bake Bones - https://youtu.be/95zAFspba1E
31 |
32 |
33 |
34 | You can report a bug or create a pull request in the GitHub:
35 | https://github.com/Kovnir/UnitySimplestMeshBaker
36 |
37 |
38 | If you have any problems or ideas, please, contact me:
39 | kovnir.alik@gmail.com
40 |
41 |
42 | Thanks for using! =)
--------------------------------------------------------------------------------