void GenerateMaterialTexture2D(VoxModel model) { var texture = new Texture2D(256, 1, TextureFormat.RGBA32, false) { name = "Color", wrapMode = TextureWrapMode.Clamp, filterMode = FilterMode.Point }; texture.SetPixels(model.palette); var materials = VoxMesher.MaterialChunkToVector4(model.materialChunks); var material = new Texture2D(256, 1, TextureFormat.RGBA32, false) { name = "Material", wrapMode = TextureWrapMode.Clamp, filterMode = FilterMode.Point }; material.SetPixels(materials.Select(x => new Color(x.x, x.y, x.z, x.w)).ToArray()); Merge(model, texture, material); }
/// <summary> /// Clears model data /// </summary> /// <param name="model"></param> private void ResetModel(VoxModel model) { model.palette = null; if (model.voxelFrames != null) { model.voxelFrames.Clear(); } else { model.voxelFrames = new List <VoxelData>(); } if (model.meshes != null) { model.meshes.Clear(); } else { model.meshes = new List <MeshLODs>(); } model.materialChunks.Clear(); model.transformNodeChunks.Clear(); model.groupNodeChunks.Clear(); model.shapeNodeChunks.Clear(); model.layerChunks.Clear(); model.rendererSettingChunks.Clear(); }
public Mesh MeshVoxelData(VoxModel data, VoxelData frame, Mesh result) { var scale = data.Settings.modelScale; var grid = frame; var colorList = data.palette; var materialList = MaterialChunkToVector4(data.materialChunks); var origin = data.Settings.origin; return(MeshVoxelData(scale * Vector3.one, grid, colorList, materialList, origin, result)); }
public override void OnInspectorGUI() { var model = target as VoxModel; if (targetModel == null || model != targetModel) { //filePath = model.modelSource; targetModel = model; } GUILayout.BeginHorizontal(); GUILayout.Label("Input File", GUILayout.ExpandWidth(false)); GUI.enabled = false; GUILayout.TextArea(filePath.Replace(Application.dataPath, "")); GUI.enabled = true; if (GUILayout.Button("…", GUILayout.ExpandWidth(false))) { filePath = EditorUtility.OpenFilePanel("Open model", EditorPrefs.GetString("LastVoxPath", Application.dataPath), "vox"); if (File.Exists(filePath)) { EditorPrefs.SetString("LastVoxPath", filePath); } } GUILayout.EndHorizontal(); if (filePath != string.Empty) { if (GUILayout.Button("➡Generate Model", GUILayout.ExpandWidth(false))) { try { EditorUtility.DisplayProgressBar("Vox", "", 0); GenerateModel(filePath, model, targetModel); } catch (System.Exception ex) { Debug.LogException(ex); } finally { EditorUtility.ClearProgressBar(); } } if (GUILayout.Button("Gen Texture 3D", GUILayout.ExpandWidth(false))) { GenerateTex3D(model); } if (GUILayout.Button("Gen Color/Material Texture 2D", GUILayout.ExpandWidth(false))) { GenerateMaterialTexture2D(model); } } EditorGUILayout.Separator(); base.OnInspectorGUI(); }
void BuildLODGroups(VoxModel model, List <GameObject> targets) { for (int i = 0; i < model.meshes.Count; i++) { var modelGO = targets[i]; var frame = model.meshes[i]; if (frame.LODs.Any(m => m.opaque != null && m.opaque.triangles.Length > 0)) { BuildLODGroup(frame, m => m.opaque, model, modelGO, $"Model[{i}].opaque", model.Settings.materialOpaque); } if (frame.LODs .Any(m => m.transparent != null && m.transparent.triangles.Length > 0)) { BuildLODGroup(frame, m => m.transparent, model, modelGO, $"Model[{i}].transparent", model.Settings.materialTransparent); } } }
void BuildContentNode(VoxModel model, GameObject parent, int node, NodeType type, List <GameObject> models, GameObject[] gos) { switch (type) { case NodeType.Group: foreach (var childId in model.groupNodeChunks.First(c => c.id == node).childIds) { gos[childId].transform.SetParent(parent.transform); } break; case NodeType.Shape: foreach (var sm in model.shapeNodeChunks.First(c => c.id == node).models) { var child = Instantiate(models[sm.modelId], parent.transform); child.name = $"Model[{sm.modelId}]"; } break; } }
/// <summary> /// Clears model data /// </summary> /// <param name="model"></param> private void resetModel(VoxModel model) { model.pallete = null; if (model.meshData != null) { model.meshData.Clear(); } else { model.meshData = new List <VoxelData>(); } if (model.meshes != null) { model.meshes.Clear(); } else { model.meshes = new List <Mesh>(); } }
void Merge(VoxModel model, IEnumerable <Texture3D> textures) { var subAssets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(model)); var assetTextures = subAssets .Select(s => s as Texture3D) .Where(s => s != null) .ToArray(); foreach (var go in assetTextures) { DestroyImmediate(go, true); } foreach (var texture in textures) { AssetDatabase.AddObjectToAsset(texture, targetModel); } EditorUtility.SetDirty(targetModel); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); }
public bool LoadModel(string absolutePath, VoxModel output) { //Load the whole file BinaryReader reader = new BinaryReader(new MemoryStream(File.ReadAllBytes(absolutePath))); string head = new string(reader.ReadChars(4)); if (!head.Equals(HEADER)) { Debug.LogError("Not a MagicaVoxel File!"); return(false); } int version = reader.ReadInt32(); if (version != VERSION) { Debug.LogWarning("Version number:" + version + " Was designed for " + VERSION); } resetModel(output); childCount = 0; while (reader.BaseStream.Position != reader.BaseStream.Length) { ReadChunk(reader, output); } reader.Close(); if (output.pallete == null) { output.pallete = LoadDefaultPallete(); } VoxMesher mesher = new VoxMesher(); for (int i = 0; i < output.meshData.Count; i++) { Mesh m = new Mesh(); mesher.MeshVoxelData(output, i, m); output.meshes.Add(m); } return(true); }
GameObject BuildLODGroup(MeshLODs list, MeshSelector selector, VoxModel model, GameObject modelGO, string name, Material material) { var go = new GameObject(name); go.transform.SetParent(modelGO.transform); var g = go.AddComponent <LODGroup>(); g.SetLODs(list.LODs.Select((m, l) => selector(list.LODs[l])).Select((m, l) => { var goLOD = new GameObject(l.ToString(), typeof(MeshFilter), typeof(MeshRenderer)); goLOD.transform.SetParent(go.transform); var h = Mathf.Pow(2 - (model.Settings.LODBias / 16f), -l); //Debug.Log(h); var r = new[] { goLOD.GetComponent <MeshRenderer>(), }; r[0].material = material; var mf = goLOD.GetComponent <MeshFilter>(); mf.mesh = m; return(new LOD(h, r)); }).ToArray()); g.RecalculateBounds(); return(go); }
private void ReadChunk(BinaryReader reader, VoxModel output) { string chunkName = new string(reader.ReadChars(4)); int chunkSize = reader.ReadInt32(); int childChunkSize = reader.ReadInt32(); //get current chunk bytes and process byte[] chunk = reader.ReadBytes(chunkSize); byte[] children = reader.ReadBytes(childChunkSize); BinaryReader chunkReader = new BinaryReader(new MemoryStream(chunk)); switch (chunkName) { case MAIN: break; case SIZE: int w = chunkReader.ReadInt32(); int h = chunkReader.ReadInt32(); int d = chunkReader.ReadInt32(); output.meshData[childCount].Resize(w, d, h); childCount++; break; case XYZ: Vector3 pos = new Vector3(); int voxelCount = chunkReader.ReadInt32(); VoxelData frame = output.meshData[childCount - 1]; byte x, y, z; for (int i = 0; i < voxelCount; i++) { x = chunkReader.ReadByte(); y = chunkReader.ReadByte(); z = chunkReader.ReadByte(); pos = toUnity * pos; frame.Set(x, z, y, (byte)(chunkReader.ReadByte())); } break; case RGBA: output.pallete = LoadPallete(chunkReader); break; case MATT: break; case PACK: int frameCount = chunkReader.ReadInt32(); for (int i = 0; i < frameCount; i++) { output.meshData.Add(new VoxelData()); } break; default: Debug.LogError("Unknown chunk id \"" + chunkName + "\""); break; } //read child chunks chunkReader.Close(); BinaryReader childReader = new BinaryReader(new MemoryStream(children)); while (childReader.BaseStream.Position != childReader.BaseStream.Length) { ReadChunk(childReader, output); } childReader.Close(); }
GameObject BuildSceneGraph(VoxModel model, List <GameObject> models) { if (!model.transformNodeChunks.Any()) { return(null); } var chunks = model.transformNodeChunks.Cast <NodeChunk>() .Concat(model.groupNodeChunks) .Concat(model.shapeNodeChunks) .ToDictionary(x => x.id, x => x); var gos = Enumerable.Range(0, chunks.Count).Select(i => { switch (chunks[i].Type) { default: case NodeType.Transform: return(new GameObject()); case NodeType.Group: return(null); case NodeType.Shape: return(null); } }).ToArray(); var toVox = Quaternion.Inverse(MagicaVoxelParser.toUnity); foreach (var t in model.transformNodeChunks) { Vector3 scale; var rotation = GetTransform(t.RotationAt(0), out scale); rotation.x *= -1; rotation.z *= -1; //rotation.w *= -1; gos[t.id].transform.localScale = scale; var translation = t.TranslationAt(0) * model.Settings.modelScale; translation.z = -translation.z; gos[t.id].transform.localPosition = translation; gos[t.id].transform.localRotation = rotation * toVox; //Debug.Log($"t{t.id}→{t.childId}"); BuildContentNode(model, gos[t.id], t.childId, chunks[t.childId].Type, models, gos); var go = gos[t.id]; go.name = t.Name; if (go.name == string.Empty) { go.name = $"Transform[{t.id}]"; } go.SetActive(!t.Hidden && !(t.layerId >= 0 && model.layerChunks.ElementAt(t.layerId).Hidden)); } if (gos.Any()) { var root = gos.First(); root.name = model.name; var translation = MagicaVoxelParser.toUnity * root.transform.localPosition; var rotation = root.transform.localRotation * MagicaVoxelParser.toUnity * MagicaVoxelParser.toUnity; translation.z = -translation.z; rotation.w *= -1; root.transform.localPosition = translation; root.transform.localScale = new Vector3( -root.transform.localScale.x, root.transform.localScale.y, -root.transform.localScale.z); root.transform.localRotation = rotation; return(root); } return(null); }
GameObject GenerateModel(VoxModel model, AssetImportContext ctx) { var parser = new MagicaVoxelParser(); parser.LoadModel(ctx.assetPath, model, s => EditorUtility.DisplayProgressBar("vox", s, 0)); var subAssets = new List <Object>(); ctx.GetObjects(subAssets); var assetMeshes = subAssets .Select(s => s as Mesh) .Where(s => s != null) .ToArray(); var updateOpaque = false; var updateTransparent = false; var uv3 = new List <Vector4>(); var assetIndex = 0; for (int i = 0; i < model.meshes.Count; i++) { for (int l = 0; l < model.meshes[i].LODs.Count; l++) { MeshSet m; //get mesh m.opaque = assetIndex < assetMeshes.Length ? assetMeshes[assetIndex++] : null; m.transparent = assetIndex < assetMeshes.Length ? assetMeshes[assetIndex++] : null; updateOpaque = m.opaque == null; if (updateOpaque) { m.opaque = new Mesh(); } updateTransparent = m.transparent == null; if (updateTransparent) { m.transparent = new Mesh(); } //turn temp meshes into assets EditorUtility.DisplayProgressBar("vox", $"asset: frame={i}/{model.meshes.Count}, lod={l}/{model.meshes[i].LODs.Count}", .5f + (.5f * i) / model.voxelFrames.Count); var frame = model.meshes[i].LODs[l]; BuildMesh(m.opaque, frame.opaque, $"{name}.{i}.{l}.opaque", uv3, model.Settings.OmitsUVUnwrapping); BuildMesh(m.transparent, frame.transparent, $"{name}.{i}.{l}.transparent", uv3, model.Settings.OmitsUVUnwrapping); //new mesh if (updateOpaque) { ctx.AddObjectToAsset(m.opaque.name, m.opaque); } if (updateTransparent) { ctx.AddObjectToAsset(m.transparent.name, m.transparent); } model.meshes[i].LODs[l] = m; } } var baseGO = ctx.mainObject as GameObject; if (baseGO == null) { baseGO = new GameObject(); ctx.AddObjectToAsset("main", baseGO); ctx.SetMainObject(baseGO); } var gos = Enumerable.Range(0, baseGO.transform.childCount) .Select(i => baseGO.transform.GetChild(i)) .Select(t => t.gameObject) .ToList(); var gosCount = gos.Count; for (int i = gosCount; i < model.meshes.Count; i++) { var target = new GameObject(); target.transform.SetParent(baseGO.transform); gos.Add(target.gameObject); } BuildLODGroups(model, gos); if (model.Settings.asSceneGraph) { var root = BuildSceneGraph(model, gos); DestroyImmediate(baseGO); baseGO = root; ctx.AddObjectToAsset("main", root); ctx.SetMainObject(root); } //destroy unneeded meshes var meshSetContained = model.meshes.SelectMany(x => x.LODs).ToArray(); foreach (var go in subAssets .Where(s => (s as Mesh) != null) .Where(s => !meshSetContained.Any(x => x.Contains(s as Mesh)))) { DestroyImmediate(go, true); } //DestroyImmediate(baseGO); return(baseGO); }
public bool LoadModel(string absolutePath, VoxModel output, Logger logger) { var name = Path.GetFileNameWithoutExtension(absolutePath); logger?.Invoke("load: " + name); //Load the whole file using (var reader = new BinaryReader(new MemoryStream(File.ReadAllBytes(absolutePath)))) { var head = new string(reader.ReadChars(4)); if (!head.Equals(HEADER)) { Debug.LogError("Not a MagicaVoxel File!", output); return(false); } int version = reader.ReadInt32(); if (version != VERSION) { Debug.LogWarning("Version number:" + version + " Was designed for " + VERSION); } ResetModel(output); childCount = 0; while (reader.BaseStream.Position != reader.BaseStream.Length) { ReadChunk(reader, output); } } if (output.palette == null) { output.palette = LoadDefaultPalette(); } output.SetAlphaFromTranparency(); var mesher = new VoxMesher(); for (int i = 0; i < output.voxelFrames.Count; i++) { var frame = output.voxelFrames[i]; logger?.Invoke($"frame={i}/{output.voxelFrames.Count}: {name}"); if (!output.Settings.EnablesTransparent || !frame.Any(output.palette, c => c.a < 1)) { var list = new MeshLODs(new Mesh { name = $"{i}.opaque" }); mesher.MeshVoxelData(output, frame, list.LODs[0].opaque); output.meshes.Add(list); } else { var list = new MeshLODs( new Mesh { name = $"{i}.opaque", }, new Mesh { name = $"{i}.tranparent" }); mesher.MeshVoxelData(output, frame.Where(output.palette, c => c.a >= 1), list.LODs[0].opaque); mesher.MeshVoxelData(output, frame.Where(output.palette, c => c.a < 1), list.LODs[0].transparent); output.meshes.Add(list); } } #region LOD supports: added by XELF var dataList = output.voxelFrames.ToArray(); var sizeList = output.voxelFrames.Select(d => new Int3(d.VoxelsWide, d.VoxelsTall, d.VoxelsDeep)).ToArray(); for (int l = 1; l < output.Settings.maxLOD; l++) { for (int i = 0; i < output.voxelFrames.Count; i++) { logger?.Invoke($"frame={i}/{output.voxelFrames.Count}, lod={l}/{output.Settings.maxLOD}: {name}"); var previous = dataList[i]; var size = sizeList[i]; size.X = (size.X + 1) >> 1; size.Y = (size.Y + 1) >> 1; size.Z = (size.Z + 1) >> 1; sizeList[i] = size; var scale = new Vector3( output.Settings.modelScale * output.voxelFrames[i].VoxelsWide / size.X, output.Settings.modelScale * output.voxelFrames[i].VoxelsTall / size.Y, output.Settings.modelScale * output.voxelFrames[i].VoxelsDeep / size.Z); var current = previous.ToSmaller(); if (!output.Settings.EnablesTransparent || !current.Any(output.palette, c => c.a < 1)) { var m = new MeshSet { opaque = new Mesh(), }; var materials = VoxMesher.MaterialChunkToVector4(output.materialChunks); mesher.MeshVoxelData(scale, current, output.palette, materials, output.Settings.origin, m.opaque); output.meshes[i].LODs.Add(m); } else { var m = new MeshSet { opaque = new Mesh(), transparent = new Mesh(), }; var materials = VoxMesher.MaterialChunkToVector4(output.materialChunks); mesher.MeshVoxelData(scale, current.Where(output.palette, c => c.a >= 1), output.palette, materials, output.Settings.origin, m.opaque); mesher.MeshVoxelData(scale, current.Where(output.palette, c => c.a < 1), output.palette, materials, output.Settings.origin, m.transparent); output.meshes[i].LODs.Add(m); } dataList[i] = current; } } #endregion return(true); }
private void ReadChunk(BinaryReader reader, VoxModel output) { var chunkName = new string(reader.ReadChars(4)); var chunkSize = reader.ReadInt32(); var childChunkSize = reader.ReadInt32(); //get current chunk bytes and process var chunk = reader.ReadBytes(chunkSize); var children = reader.ReadBytes(childChunkSize); using (var chunkReader = new BinaryReader(new MemoryStream(chunk))) { //Debug.Log(chunkName); switch (chunkName) { case MAIN: break; case SIZE: int w = chunkReader.ReadInt32(); int h = chunkReader.ReadInt32(); int d = chunkReader.ReadInt32(); if (childCount >= output.voxelFrames.Count) { output.voxelFrames.Add(new VoxelData()); } output.voxelFrames[childCount].Resize(w, d, h); childCount++; break; case XYZI: var voxelCount = chunkReader.ReadInt32(); var frame = output.voxelFrames[childCount - 1]; byte x, y, z; for (int i = 0; i < voxelCount; i++) { x = chunkReader.ReadByte(); y = chunkReader.ReadByte(); z = chunkReader.ReadByte(); frame.Set(x, z, y, chunkReader.ReadByte()); } break; case RGBA: output.palette = LoadPalette(chunkReader); break; case MATT: break; case PACK: int frameCount = chunkReader.ReadInt32(); for (int i = 0; i < frameCount; i++) { output.voxelFrames.Add(new VoxelData()); } break; #region Vox extension: added by XELF case nTRN: output.transformNodeChunks.Add(ReadTransformNodeChunk(chunkReader)); break; case nGRP: output.groupNodeChunks.Add(ReadGroupNodeChunk(chunkReader)); break; case nSHP: output.shapeNodeChunks.Add(ReadShapeNodeChunk(chunkReader)); break; case LAYR: output.layerChunks.Add(ReadLayerChunk(chunkReader)); break; case MATL: output.materialChunks.Add(ReadMaterialChunk(chunkReader)); break; case rOBJ: output.rendererSettingChunks.Add(ReadRObjectChunk(chunkReader)); break; #endregion default: Debug.LogError($"Unknown chunk: \"{chunkName}\""); break; } } //read child chunks using (var childReader = new BinaryReader(new MemoryStream(children))) { while (childReader.BaseStream.Position != childReader.BaseStream.Length) { ReadChunk(childReader, output); } } }
void GenerateModel(string filePath, VoxModel model, VoxModel targetModel) { var parser = new MagicaVoxelParser(); //model.modelSource = filePath; parser.LoadModel(filePath, model, s => EditorUtility.DisplayProgressBar("vox", s, 0)); EditorUtility.SetDirty(model); var path = AssetDatabase.GetAssetPath(model); var name = Path.GetFileNameWithoutExtension(path); var subAssets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(model)); //load asset meshes var assetMeshes = subAssets .Select(s => s as Mesh) .Where(s => s != null) .ToArray(); var updateOpaque = false; var updateTransparent = false; var uv3 = new List <Vector4>(); var assetIndex = 0; for (int i = 0; i < model.meshes.Count; i++) { for (int l = 0; l < model.meshes[i].LODs.Count; l++) { MeshSet m; //get mesh m.opaque = assetIndex < assetMeshes.Length ? assetMeshes[assetIndex++] : null; m.transparent = assetIndex < assetMeshes.Length ? assetMeshes[assetIndex++] : null; updateOpaque = m.opaque == null; if (updateOpaque) { m.opaque = new Mesh(); } updateTransparent = m.transparent == null; if (updateTransparent) { m.transparent = new Mesh(); } //turn temp meshes into assets EditorUtility.DisplayProgressBar("vox", $"asset: frame={i}/{model.meshes.Count}, lod={l}/{model.meshes[i].LODs.Count}", .5f + (.5f * i) / model.voxelFrames.Count); var frame = model.meshes[i].LODs[l]; BuildMesh(m.opaque, frame.opaque, $"{name}.{i}.{l}.opaque", uv3, model.Settings.OmitsUVUnwrapping); BuildMesh(m.transparent, frame.transparent, $"{name}.{i}.{l}.transparent", uv3, model.Settings.OmitsUVUnwrapping); //new mesh if (updateOpaque) { AssetDatabase.AddObjectToAsset(m.opaque, targetModel); } if (updateTransparent) { AssetDatabase.AddObjectToAsset(m.transparent, targetModel); } model.meshes[i].LODs[l] = m; } } var baseGO = new GameObject(); var gos = Enumerable.Range(0, baseGO.transform.childCount) .Select(i => baseGO.transform.GetChild(i)) .Select(t => t.gameObject) .ToList(); var gosCount = gos.Count; for (int i = gosCount; i < model.meshes.Count; i++) { var target = new GameObject(); target.transform.SetParent(baseGO.transform); gos.Add(target.gameObject); } BuildLODGroups(model, gos); if (model.Settings.asSceneGraph) { var root = BuildSceneGraph(model, gos); DestroyImmediate(baseGO); baseGO = root; } //destroy unneeded meshes foreach (var go in subAssets .Where(s => (s as Mesh) != null) .Where(s => !model.meshes.SelectMany(x => x.LODs).Any(x => x.Contains(s as Mesh)))) { DestroyImmediate(go, true); } if (targetModel.prefab == null) { targetModel.prefab = PrefabUtility.CreatePrefab(path + ".prefab", new GameObject()); } PrefabUtility.ReplacePrefab(baseGO, targetModel.prefab, ReplacePrefabOptions.ReplaceNameBased); DestroyImmediate(baseGO); EditorUtility.SetDirty(targetModel); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); }
public override void OnInspectorGUI() { base.OnInspectorGUI(); VoxModel model = target as VoxModel; if (targetModel == null || model != targetModel) { filePath = model.modelSource; targetModel = model; } GUILayout.BeginHorizontal(); GUILayout.Label("File:", GUILayout.ExpandWidth(false)); GUILayout.TextArea(filePath.Replace(Application.dataPath, "")); if (GUILayout.Button("Open file", GUILayout.ExpandWidth(false))) { filePath = EditorUtility.OpenFilePanel("Open model", EditorPrefs.GetString("LastVoxPath", Application.dataPath), "vox"); if (File.Exists(filePath)) { EditorPrefs.SetString("LastVoxPath", filePath); } } GUILayout.EndHorizontal(); if (filePath != string.Empty) { if (GUILayout.Button("Generate Model", GUILayout.ExpandWidth(false))) { MagicaVoxelParser parser = new MagicaVoxelParser(); model.modelSource = filePath; parser.LoadModel(filePath, model); EditorUtility.SetDirty(model); string path = AssetDatabase.GetAssetPath(model); string name = Path.GetFileNameWithoutExtension(path); Object[] subAssets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(model)); //load asset meshes List <Mesh> assetMeshes = new List <Mesh>(); for (int i = 0; i < subAssets.Length; i++) { if (subAssets[i] is Mesh) { assetMeshes.Add(subAssets[i] as Mesh); } } int max = model.meshes.Count; bool update = false; Mesh m, temp; for (int i = 0; i < max; i++) { //get mesh if (i < assetMeshes.Count) { update = false; m = assetMeshes[i]; if (m == null) { update = true; m = new Mesh(); } } else { m = new Mesh(); update = true; } //turn temp meshes into assets if (i < model.meshes.Count) { m.name = name + "_frame_" + i; temp = model.meshes[i]; m.Clear(); m.vertices = temp.vertices; m.colors = temp.colors; m.normals = temp.normals; m.triangles = temp.triangles; m.uv = Unwrapping.GeneratePerTriangleUV(m); Unwrapping.GenerateSecondaryUVSet(m); //new mesh if (update) { AssetDatabase.AddObjectToAsset(m, targetModel); } model.meshes[i] = m; } } //destroy un needed meshes for (int i = 0; i < subAssets.Length; i++) { m = subAssets[i] as Mesh; if (m != null) { if (model.meshes.Contains(m) == false) { DestroyImmediate(m, true); } } } EditorUtility.SetDirty(targetModel); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } } }
void GenerateTex3D(VoxModel model) { Merge(model, model.voxelFrames .Select((f, i) => CreateTexture3D(f, $"{i}"))); }