Skip to content

Commit 19aacc1

Browse files
committed
Added the ability to load a secondary vox model to use as an alpha mask for the main model.
Alpha is determined by the color index of each voxel (ie index 1 = fully transparent, index 255 = fully opaque)
1 parent ec8eb29 commit 19aacc1

File tree

9 files changed

+193
-48
lines changed

9 files changed

+193
-48
lines changed
0 Bytes
Binary file not shown.

Assets/MagicaVoxel/Scripts/Editor/MVEditorUtilities.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,25 @@ static void Load() {
2323
"vox"
2424
);
2525

26-
if (path != null && path.Length > 0) {
26+
if (path != null && path.Length > 0)
27+
{
28+
string alphaMaskPath = string.Empty;
29+
if (EditorUtility.DisplayDialog("Question", "Do you want to load an alpha mask model?", "yes", "no"))
30+
{
31+
alphaMaskPath = EditorUtility.OpenFilePanel(
32+
"Open VOX model for alpha mask",
33+
"Assets/MagicaVoxel/Vox",
34+
"vox"
35+
);
36+
}
2737

2838
GameObject go = new GameObject ();
2939
go.name = System.IO.Path.GetFileNameWithoutExtension (path);
3040

3141
MVVoxModel voxModel = go.AddComponent<MVVoxModel> ();
3242
voxModel.ed_filePath = path;
33-
voxModel.LoadVOXFile (path, voxModel.ed_importAsIndividualVoxels);
43+
voxModel.ed_alphaMaskFilePath = alphaMaskPath;
44+
voxModel.LoadVOXFile (path, alphaMaskPath, voxModel.ed_importAsIndividualVoxels);
3445
}
3546
}
3647
}

Assets/MagicaVoxel/Scripts/Editor/MVVoxModelInspector.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,24 @@ public override void OnInspectorGUI ()
2828
"vox"
2929
);
3030

31+
string alphaMaskPath = string.Empty;
32+
if(EditorUtility.DisplayDialog("Question", "Do you want to load an alpha mask model file?", "yes", "no"))
33+
{
34+
alphaMaskPath = EditorUtility.OpenFilePanel(
35+
"Open VOX model for alpha mask",
36+
"Assets/MagicaVoxel/Vox",
37+
"vox"
38+
);
39+
}
40+
3141
voxModel.ed_filePath = path;
32-
voxModel.LoadVOXFile (path, voxModel.ed_importAsIndividualVoxels);
42+
voxModel.ed_alphaMaskFilePath = alphaMaskPath;
43+
voxModel.LoadVOXFile (path, alphaMaskPath, voxModel.ed_importAsIndividualVoxels);
3344

3445
}
3546
if (GUILayout.Button ("Reimport")) {
3647

37-
voxModel.LoadVOXFile (voxModel.ed_filePath, voxModel.ed_importAsIndividualVoxels);
48+
voxModel.LoadVOXFile (voxModel.ed_filePath, voxModel.ed_alphaMaskFilePath, voxModel.ed_importAsIndividualVoxels);
3849
}
3950

4051
if (GUILayout.Button ("Clear")) {

Assets/MagicaVoxel/Scripts/Editor/MVVoxModelVoxelInspector.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ public static void CombineVoxels(MVVoxModelVoxel[] voxels) {
4040
chunk.voxels [v.voxel.x, v.voxel.y, v.voxel.z] = v.voxel.colorIndex;
4141
}
4242

43-
MVImporter.GenerateFaces (chunk);
44-
Mesh[] meshes = MVImporter.CreateMeshesFromChunk (chunk, model.vox.palatte, model.sizePerVox);
43+
MVImporter.GenerateFaces(chunk, model.vox.alphaMaskChunk);
44+
Mesh[] meshes = MVImporter.CreateMeshesFromChunk(chunk, model.vox.palatte, model.sizePerVox, model.vox.alphaMaskChunk);
4545

4646
if (meshes.Length > 1) {
4747
Debug.LogError ("[MVCombine] Currently does not support combining voxels into multiple meshes, please reduce the number of voxels you are trying to combine");

Assets/MagicaVoxel/Scripts/MVImporter.cs

Lines changed: 85 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public enum MVFaceDir
4444
public class MVMainChunk
4545
{
4646
public MVVoxelChunk voxelChunk;
47+
public MVVoxelChunk alphaMaskChunk;
4748

4849
public Color[] palatte;
4950

@@ -321,7 +322,7 @@ public static Material DefaultMaterial {
321322
}
322323
}
323324

324-
public static MVMainChunk LoadVOXFromData(byte[] data) {
325+
public static MVMainChunk LoadVOXFromData(byte[] data, MVVoxelChunk alphaMask = null, bool generateFaces = true) {
325326
using (MemoryStream ms = new MemoryStream (data)) {
326327
using (BinaryReader br = new BinaryReader (ms)) {
327328
MVMainChunk mainChunk = new MVMainChunk ();
@@ -378,7 +379,11 @@ public static MVMainChunk LoadVOXFromData(byte[] data) {
378379
}
379380
}
380381

381-
GenerateFaces (mainChunk.voxelChunk);
382+
mainChunk.alphaMaskChunk = alphaMask;
383+
384+
if (generateFaces)
385+
GenerateFaces(mainChunk.voxelChunk, alphaMask);
386+
382387
if (mainChunk.palatte == null)
383388
mainChunk.palatte = MVMainChunk.defaultPalatte;
384389

@@ -387,7 +392,7 @@ public static MVMainChunk LoadVOXFromData(byte[] data) {
387392
}
388393
}
389394

390-
public static MVMainChunk LoadVOX(string path)
395+
public static MVMainChunk LoadVOX(string path, MVVoxelChunk alphaMask = null, bool generateFaces = true)
391396
{
392397
byte[] bytes = File.ReadAllBytes (path);
393398
if (bytes [0] != 'V' ||
@@ -398,10 +403,10 @@ public static MVMainChunk LoadVOX(string path)
398403
return null;
399404
}
400405

401-
return LoadVOXFromData (bytes);
406+
return LoadVOXFromData (bytes, alphaMask, generateFaces);
402407
}
403408

404-
public static void GenerateFaces(MVVoxelChunk voxelChunk)
409+
public static void GenerateFaces(MVVoxelChunk voxelChunk, MVVoxelChunk alphaMask)
405410
{
406411
voxelChunk.faces = new MVFaceCollection[6];
407412
for (int i = 0; i < 6; ++i) {
@@ -411,25 +416,28 @@ public static void GenerateFaces(MVVoxelChunk voxelChunk)
411416
for (int x = 0; x < voxelChunk.sizeX; ++x) {
412417
for (int y = 0; y < voxelChunk.sizeY; ++y) {
413418
for (int z = 0; z < voxelChunk.sizeZ; ++z) {
419+
420+
int alpha = alphaMask == null ? (byte)0 : alphaMask.voxels[x, y, z];
421+
414422
// left right
415-
if(x == 0 || voxelChunk.voxels[x-1, y, z] == 0)
416-
voxelChunk.faces [(int)MVFaceDir.XNeg].colorIndices [x, y, z] = voxelChunk.voxels [x, y, z];
423+
if (x == 0 || DetermineEmptyOrOtherAlphaVoxel(voxelChunk, alphaMask, alpha, x - 1, y, z))
424+
voxelChunk.faces [(int)MVFaceDir.XNeg].colorIndices [x, y, z] = voxelChunk.voxels [x, y, z];
417425

418-
if (x == voxelChunk.sizeX - 1 || voxelChunk.voxels [x + 1, y, z] == 0)
426+
if (x == voxelChunk.sizeX - 1 || DetermineEmptyOrOtherAlphaVoxel(voxelChunk, alphaMask, alpha, x + 1, y, z))
419427
voxelChunk.faces [(int)MVFaceDir.XPos].colorIndices [x, y, z] = voxelChunk.voxels [x, y, z];
420428

421429
// up down
422-
if(y == 0 || voxelChunk.voxels[x, y-1, z] == 0)
423-
voxelChunk.faces [(int)MVFaceDir.YNeg].colorIndices [x, y, z] = voxelChunk.voxels [x, y, z];
430+
if (y == 0 || DetermineEmptyOrOtherAlphaVoxel(voxelChunk, alphaMask, alpha, x, y - 1, z))
431+
voxelChunk.faces [(int)MVFaceDir.YNeg].colorIndices [x, y, z] = voxelChunk.voxels [x, y, z];
424432

425-
if (y == voxelChunk.sizeY - 1 || voxelChunk.voxels [x, y+1, z] == 0)
433+
if (y == voxelChunk.sizeY - 1 || DetermineEmptyOrOtherAlphaVoxel(voxelChunk, alphaMask, alpha, x, y + 1, z))
426434
voxelChunk.faces [(int)MVFaceDir.YPos].colorIndices [x, y, z] = voxelChunk.voxels [x, y, z];
427435

428436
// forward backward
429-
if(z == 0 || voxelChunk.voxels[x, y, z-1] == 0)
430-
voxelChunk.faces [(int)MVFaceDir.ZNeg].colorIndices [x, y, z] = voxelChunk.voxels [x, y, z];
437+
if (z == 0 || DetermineEmptyOrOtherAlphaVoxel(voxelChunk, alphaMask, alpha, x, y, z - 1))
438+
voxelChunk.faces [(int)MVFaceDir.ZNeg].colorIndices [x, y, z] = voxelChunk.voxels [x, y, z];
431439

432-
if (z == voxelChunk.sizeZ - 1 || voxelChunk.voxels [x, y, z+1] == 0)
440+
if (z == voxelChunk.sizeZ - 1 || DetermineEmptyOrOtherAlphaVoxel(voxelChunk, alphaMask, alpha, x, y, z + 1))
433441
voxelChunk.faces [(int)MVFaceDir.ZPos].colorIndices [x, y, z] = voxelChunk.voxels [x, y, z];
434442
}
435443
}
@@ -499,27 +507,27 @@ static int ReadPalattee(BinaryReader br, Color[] colors)
499507

500508
public static Mesh[] CreateMeshes(MVMainChunk chunk, float sizePerVox)
501509
{
502-
return CreateMeshesFromChunk (chunk.voxelChunk, chunk.palatte, sizePerVox);
510+
return CreateMeshesFromChunk(chunk.voxelChunk, chunk.palatte, sizePerVox, chunk.alphaMaskChunk);
503511
}
504512

505513
public static GameObject[] CreateVoxelGameObjects(MVMainChunk chunk, Transform parent, Material mat, float sizePerVox)
506514
{
507-
return CreateVoxelGameObjectsForChunk (chunk.voxelChunk, chunk.palatte, parent, mat, sizePerVox);
515+
return CreateVoxelGameObjectsForChunk(chunk.voxelChunk, chunk.palatte, parent, mat, sizePerVox, chunk.alphaMaskChunk);
508516
}
509517

510518
public static GameObject[] CreateVoxelGameObjects(MVMainChunk chunk, Transform parent, Material mat, float sizePerVox, Vector3 origin)
511519
{
512-
return CreateVoxelGameObjectsForChunk(chunk.voxelChunk, chunk.palatte, parent, mat, sizePerVox, origin);
520+
return CreateVoxelGameObjectsForChunk(chunk.voxelChunk, chunk.palatte, parent, mat, sizePerVox, chunk.alphaMaskChunk, origin);
513521
}
514522

515523
public static GameObject[] CreateIndividualVoxelGameObjects(MVMainChunk chunk, Transform parent, Material mat, float sizePerVox)
516524
{
517-
return CreateIndividualVoxelGameObjectsForChunk (chunk.voxelChunk, chunk.palatte, parent, mat, sizePerVox);
525+
return CreateIndividualVoxelGameObjectsForChunk(chunk.voxelChunk, chunk.palatte, parent, mat, sizePerVox, chunk.alphaMaskChunk);
518526
}
519527

520528
public static GameObject[] CreateIndividualVoxelGameObjects(MVMainChunk chunk, Transform parent, Material mat, float sizePerVox, Vector3 origin)
521529
{
522-
return CreateIndividualVoxelGameObjectsForChunk(chunk.voxelChunk, chunk.palatte, parent, mat, sizePerVox, origin);
530+
return CreateIndividualVoxelGameObjectsForChunk(chunk.voxelChunk, chunk.palatte, parent, mat, sizePerVox, chunk.alphaMaskChunk, origin);
523531
}
524532

525533
public static Mesh CubeMeshWithColor(float size, Color c) {
@@ -586,31 +594,41 @@ public static GameObject CreateGameObject(Transform parent, Vector3 pos, string
586594
return go;
587595
}
588596

589-
public static GameObject[] CreateIndividualVoxelGameObjectsForChunk(MVVoxelChunk chunk, Color[] palatte, Transform parent, Material mat, float sizePerVox)
597+
public static GameObject[] CreateIndividualVoxelGameObjectsForChunk(MVVoxelChunk chunk, Color[] palatte, Transform parent, Material mat, float sizePerVox, MVVoxelChunk alphaMask)
590598
{
591599
Vector3 origin = new Vector3(
592600
(float)chunk.sizeX / 2,
593601
(float)chunk.sizeY / 2,
594602
(float)chunk.sizeZ / 2);
595603

596-
return CreateIndividualVoxelGameObjectsForChunk(chunk, palatte, parent, mat, sizePerVox, origin);
604+
return CreateIndividualVoxelGameObjectsForChunk(chunk, palatte, parent, mat, sizePerVox, alphaMask, origin);
597605
}
598606

599-
public static GameObject[] CreateIndividualVoxelGameObjectsForChunk(MVVoxelChunk chunk, Color[] palatte, Transform parent, Material mat, float sizePerVox, Vector3 origin) {
607+
public static GameObject[] CreateIndividualVoxelGameObjectsForChunk(MVVoxelChunk chunk, Color[] palatte, Transform parent, Material mat, float sizePerVox, MVVoxelChunk alphaMask, Vector3 origin) {
600608
List<GameObject> result = new List<GameObject> ();
601609

610+
if (alphaMask != null && (alphaMask.sizeX != chunk.sizeX || alphaMask.sizeY != chunk.sizeY || alphaMask.sizeZ != chunk.sizeZ))
611+
{
612+
Debug.LogErrorFormat("Unable to create meshes from chunk : Chunk's size ({0},{1},{2}) differs from alphaMask chunk's size ({3},{4},{5})", chunk.sizeX, chunk.sizeY, chunk.sizeZ, alphaMask.sizeX, alphaMask.sizeY, alphaMask.sizeZ);
613+
return result.ToArray();
614+
}
615+
602616
for (int x = 0; x < chunk.sizeX; ++x) {
603617
for (int y = 0; y < chunk.sizeY; ++y) {
604618
for (int z = 0; z < chunk.sizeZ; ++z) {
605619

606620
if (chunk.voxels [x, y, z] != 0) {
607621
float px = (x - origin.x + 0.5f) * sizePerVox, py = (y - origin.y + 0.5f) * sizePerVox, pz = (z - origin.z + 0.5f) * sizePerVox;
622+
Color c = palatte [chunk.voxels [x, y, z] - 1];
623+
624+
if (alphaMask != null && alphaMask.voxels[x, y, z] != 0)
625+
c.a = (float)(alphaMask.voxels [x, y, z] - 1) / 255;
608626

609627
GameObject go = CreateGameObject (
610628
parent,
611629
new Vector3 (px, py, pz),
612630
string.Format ("Voxel ({0}, {1}, {2})", x, y, z),
613-
MVImporter.CubeMeshWithColor (sizePerVox, palatte [chunk.voxels [x, y, z] - 1]),
631+
MVImporter.CubeMeshWithColor (sizePerVox, c),
614632
mat);
615633

616634
MVVoxModelVoxel v = go.AddComponent<MVVoxModelVoxel> ();
@@ -625,20 +643,20 @@ public static GameObject[] CreateIndividualVoxelGameObjectsForChunk(MVVoxelChunk
625643
return result.ToArray();
626644
}
627645

628-
public static GameObject[] CreateVoxelGameObjectsForChunk(MVVoxelChunk chunk, Color[] palatte, Transform parent, Material mat, float sizePerVox)
646+
public static GameObject[] CreateVoxelGameObjectsForChunk(MVVoxelChunk chunk, Color[] palatte, Transform parent, Material mat, float sizePerVox, MVVoxelChunk alphaMask)
629647
{
630648
Vector3 origin = new Vector3(
631649
(float)chunk.sizeX / 2,
632650
(float)chunk.sizeY / 2,
633651
(float)chunk.sizeZ / 2);
634652

635-
return CreateVoxelGameObjectsForChunk(chunk, palatte, parent, mat, sizePerVox, origin);
653+
return CreateVoxelGameObjectsForChunk(chunk, palatte, parent, mat, sizePerVox, alphaMask, origin);
636654
}
637655

638-
public static GameObject[] CreateVoxelGameObjectsForChunk(MVVoxelChunk chunk, Color[] palatte, Transform parent, Material mat, float sizePerVox, Vector3 origin) {
656+
public static GameObject[] CreateVoxelGameObjectsForChunk(MVVoxelChunk chunk, Color[] palatte, Transform parent, Material mat, float sizePerVox, MVVoxelChunk alphaMask, Vector3 origin) {
639657
List<GameObject> result = new List<GameObject> ();
640658

641-
Mesh[] meshes = CreateMeshesFromChunk (chunk, palatte, sizePerVox, origin);
659+
Mesh[] meshes = CreateMeshesFromChunk (chunk, palatte, sizePerVox, alphaMask, origin);
642660

643661
int index = 0;
644662
foreach (Mesh mesh in meshes) {
@@ -661,18 +679,24 @@ public static GameObject[] CreateVoxelGameObjectsForChunk(MVVoxelChunk chunk, Co
661679
return result.ToArray ();
662680
}
663681

664-
public static Mesh[] CreateMeshesFromChunk(MVVoxelChunk chunk, Color[] palatte, float sizePerVox)
682+
public static Mesh[] CreateMeshesFromChunk(MVVoxelChunk chunk, Color[] palatte, float sizePerVox, MVVoxelChunk alphaMask)
665683
{
666684
Vector3 origin = new Vector3(
667685
(float)chunk.sizeX / 2,
668686
(float)chunk.sizeY / 2,
669687
(float)chunk.sizeZ / 2);
670688

671-
return CreateMeshesFromChunk(chunk, palatte, sizePerVox, origin);
689+
return CreateMeshesFromChunk(chunk, palatte, sizePerVox, alphaMask, origin);
672690
}
673691

674-
public static Mesh[] CreateMeshesFromChunk(MVVoxelChunk chunk, Color[] palatte, float sizePerVox, Vector3 origin)
692+
public static Mesh[] CreateMeshesFromChunk(MVVoxelChunk chunk, Color[] palatte, float sizePerVox, MVVoxelChunk alphaMask, Vector3 origin)
675693
{
694+
if (alphaMask != null && (alphaMask.sizeX != chunk.sizeX || alphaMask.sizeY != chunk.sizeY || alphaMask.sizeZ != chunk.sizeZ))
695+
{
696+
Debug.LogErrorFormat("Unable to create meshes from chunk : Chunk's size ({0},{1},{2}) differs from alphaMask chunk's size ({3},{4},{5})", chunk.sizeX, chunk.sizeY, chunk.sizeZ, alphaMask.sizeX, alphaMask.sizeY, alphaMask.sizeZ);
697+
return new Mesh[] { };
698+
}
699+
676700
List<Vector3> verts = new List<Vector3> ();
677701
List<Vector3> normals = new List<Vector3> ();
678702
List<Color> colors = new List<Color> ();
@@ -702,6 +726,7 @@ public static Mesh[] CreateMeshesFromChunk(MVVoxelChunk chunk, Color[] palatte,
702726
for (int z = 0; z < chunk.sizeZ; ++z) {
703727

704728
int cidx = chunk.faces [f].colorIndices [x, y, z];
729+
int alpha = alphaMask == null ? (byte)0 : alphaMask.voxels[x, y, z];
705730

706731
if (cidx != 0) {
707732
float px = (x - origin.x + 0.5f) * sizePerVox, py = (y - origin.y + 0.5f) * sizePerVox, pz = (z - origin.z + 0.5f) * sizePerVox;
@@ -712,15 +737,15 @@ public static Mesh[] CreateMeshesFromChunk(MVVoxelChunk chunk, Color[] palatte,
712737
case 0:
713738
{
714739
ry = y + 1;
715-
while (ry < chunk.sizeY && chunk.faces [f].colorIndices [x, ry, z] == cidx)
740+
while (ry < chunk.sizeY && CompareColor(cidx, alpha, chunk, alphaMask, f, x, ry, z))
716741
ry++;
717742
ry--;
718743

719744
rz = z + 1;
720745
while (rz < chunk.sizeZ) {
721746
bool inc = true;
722747
for (int k = y; k <= ry; ++k) {
723-
inc = inc & (chunk.faces [f].colorIndices [x, k, rz] == cidx);
748+
inc = inc & (CompareColor(cidx, alpha, chunk, alphaMask, f, x, k, rz));
724749
}
725750

726751
if (inc)
@@ -736,15 +761,15 @@ public static Mesh[] CreateMeshesFromChunk(MVVoxelChunk chunk, Color[] palatte,
736761
case 2:
737762
{
738763
rx = x + 1;
739-
while (rx < chunk.sizeX && chunk.faces [f].colorIndices [rx, y, z] == cidx)
764+
while (rx < chunk.sizeX && CompareColor(cidx, alpha, chunk, alphaMask, f, rx, y, z))
740765
rx++;
741766
rx--;
742767

743768
rz = z + 1;
744769
while (rz < chunk.sizeZ) {
745770
bool inc = true;
746771
for (int k = x; k <= rx; ++k) {
747-
inc = inc & (chunk.faces [f].colorIndices [k, y, rz] == cidx);
772+
inc = inc & (CompareColor(cidx, alpha, chunk, alphaMask, f, k, y, rz));
748773
}
749774

750775
if (inc)
@@ -760,15 +785,15 @@ public static Mesh[] CreateMeshesFromChunk(MVVoxelChunk chunk, Color[] palatte,
760785
case 4:
761786
{
762787
rx = x + 1;
763-
while (rx < chunk.sizeX && chunk.faces [f].colorIndices [rx, y, z] == cidx)
788+
while (rx < chunk.sizeX && CompareColor(cidx, alpha, chunk, alphaMask, f, rx, y, z))
764789
rx++;
765790
rx--;
766791

767792
ry = y + 1;
768793
while (ry < chunk.sizeY) {
769794
bool inc = true;
770795
for (int k = x; k <= rx; ++k) {
771-
inc = inc & (chunk.faces [f].colorIndices [k, ry, z] == cidx);
796+
inc = inc & (CompareColor(cidx, alpha, chunk, alphaMask, f, k, ry, z));
772797
}
773798

774799
if (inc)
@@ -847,6 +872,9 @@ public static Mesh[] CreateMeshesFromChunk(MVVoxelChunk chunk, Color[] palatte,
847872
// color index starts with 1
848873
Color c = palatte [cidx - 1];
849874

875+
if (alpha != 0)
876+
c.a = (float)(alpha - 1) / 255;
877+
850878
colors.Add (c);
851879
colors.Add (c);
852880
colors.Add (c);
@@ -901,5 +929,25 @@ public static Mesh[] CreateMeshesFromChunk(MVVoxelChunk chunk, Color[] palatte,
901929

902930
return result.ToArray();
903931
}
904-
}
905932

933+
private static bool CompareColor(int cidx, int alpha, MVVoxelChunk chunk, MVVoxelChunk alphaChunk, int f, int x, int y, int z)
934+
{
935+
if (alphaChunk == null)
936+
return chunk.faces[f].colorIndices[x, y, z] == cidx;
937+
else
938+
return chunk.faces[f].colorIndices[x, y, z] == cidx && alphaChunk.voxels[x, y, z] == alpha;
939+
}
940+
941+
private static bool DetermineEmptyOrOtherAlphaVoxel(MVVoxelChunk voxelChunk, MVVoxelChunk alphaMask, int a, int x, int y, int z)
942+
{
943+
bool isEmpty = voxelChunk.voxels[x, y, z] == 0;
944+
945+
if (alphaMask == null)
946+
return isEmpty;
947+
else
948+
{
949+
bool otherAlpha = alphaMask.voxels[x, y, z] != a;
950+
return isEmpty || otherAlpha;
951+
}
952+
}
953+
}

0 commit comments

Comments
 (0)