Skip to content

Commit

Permalink
Merge pull request #15 from sandrofigo/develop
Browse files Browse the repository at this point in the history
Release v4.0.0
  • Loading branch information
sandrofigo authored Mar 24, 2023
2 parents fa36c96 + a579220 commit 0a938b4
Show file tree
Hide file tree
Showing 24 changed files with 408 additions and 20 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-->

## [4.0.0] - 2023-03-24

### Added

- Added support for parsing palette notes
- Added method `Palette.GetColorsByNote` to get palette colors grouped by palette note texts
- Added method `Palette.GetColorIndicesByNote` to get mapped palette color indices grouped by palette note texts
- Added mapped color index to voxel

### Fixed

- Fixed wrong colors in palette when an IMAP chunk exists

### Changed

- `Palette.Colors` now stores the mapped colors that are visible in the UI of MagicaVoxel instead of the raw colors parsed from the `.vox` file

## [3.1.0] - 2023-01-03

### Added
Expand Down
157 changes: 151 additions & 6 deletions VoxReader.UnitTests/UnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,137 @@ public class UnitTests
private const string TestFile_MultipleModels = "data/multiple_models.zip";
private const string TestFile_3x3x3_at_center_with_corner = "data/3x3x3_at_center_with_corner.zip";
private const string TestFile_groups = "data/groups.zip";
private const string TestFile_notes = "data/color_notes.zip";
private const string TestFile_no_notes = "data/no_notes.zip";
private const string TestFile_color_indices = "data/color_indices.zip";
private const string TestFile_color_indices2 = "data/color_indices_2.zip";

[Fact]
public void VoxReader_GetColorIndicesByNote_ReturnsEmptyArrayWhenNoteTextDoesNotMatch()
{
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_color_indices2).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Palette.GetColorIndicesByNote("no match").Should().BeEmpty();
}

[Fact]
public void VoxReader_GetColorIndicesByNote_ColorIndicesAreCorrect()
{
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_color_indices2).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Palette.GetColorIndicesByNote("red").Should().ContainInOrder(248, 249, 250, 251, 252, 253, 254);
voxFile.Palette.GetColorIndicesByNote("mixed").Should().ContainInOrder(144, 145, 146, 147, 148, 149, 150);
voxFile.Palette.GetColorIndicesByNote("green").Should().ContainInOrder(0, 1, 2, 3, 4, 5, 6);
}

[Theory]
[InlineData(TestFile_color_indices, 0, 2, 0, 152)]
[InlineData(TestFile_color_indices, 1, 1, 0, 99)]
[InlineData(TestFile_color_indices, 2, 0, 0, 16)]
[InlineData(TestFile_color_indices2, 0, 1, 0, 0)]
[InlineData(TestFile_color_indices2, 1, 2, 0, 144)]
[InlineData(TestFile_color_indices2, 2, 0, 0, 254)]
public void VoxReader_Read_ColorIndicesOnVoxelAreCorrect(string testFile, int x, int y, int z, int expectedIndex)
{
string file = Zip.UnzipFilesFromSevenZipArchive(testFile).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Models.First().Voxels.First(voxel => voxel.Position == new Vector3(x, y, z)).ColorIndex.Should().Be(expectedIndex);
}

[Fact]
public void VoxReader_Read_ColorIndicesAreCorrect()
{
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_color_indices).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Palette.Colors[254].Should().Be(Color.Cyan);
voxFile.Palette.Colors[251].Should().Be(Color.Yellow);
voxFile.Palette.Colors[154].Should().Be(Color.Blue);
voxFile.Palette.Colors[152].Should().Be(Color.Red);
voxFile.Palette.Colors[133].Should().Be(Color.Yellow);
voxFile.Palette.Colors[99].Should().Be(Color.Green);
voxFile.Palette.Colors[90].Should().Be(Color.Magenta);
voxFile.Palette.Colors[16].Should().Be(Color.Blue);
}

[Fact]
public void VoxReader_GetColorsByNote_NoteNameMatchesColorsInTheSameRow()
{
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_color_indices).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Palette.GetColorsByNote("note 1").Should().ContainInOrder(Color.Red, Color.Black, Color.Blue, Color.Black, Color.Black, Color.Black, Color.Black, Color.Black);
voxFile.Palette.GetColorsByNote("note 2").Should().ContainInOrder(Color.Black, Color.Black, Color.Black, Color.Black, Color.Black, Color.Yellow, Color.Black, Color.Black);
voxFile.Palette.GetColorsByNote("note 3").Should().ContainInOrder(Color.Black, Color.Black, Color.Black, Color.Green, Color.Black, Color.Black, Color.Black, Color.Black, Color.Black, Color.Black, Color.Magenta, Color.Black, Color.Black, Color.Black, Color.Black, Color.Black);
voxFile.Palette.GetColorsByNote("note 4").Should().ContainInOrder(Color.Blue, Color.Black, Color.Black, Color.Black, Color.Black, Color.Black, Color.Black, Color.Black);
voxFile.Palette.GetColorsByNote("note 5").Should().ContainInOrder(Color.Black, Color.Black, Color.Black, Color.Yellow, Color.Black, Color.Black, Color.Cyan);
}

[Fact]
public void VoxReader_GetColorsByNote_NotMatchingNoteReturnsEmptyCollection()
{
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_color_indices).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Palette.GetColorsByNote("no match").Should().BeEmpty();
}

[Fact]
public void VoxReader_Read_PaletteColorPositionMatchesNoteRow()
{
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_notes).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Palette.Colors[248].Should().Be(Color.Red);
voxFile.Palette.Colors[136].Should().Be(Color.Green);
voxFile.Palette.Colors[0].Should().Be(Color.Blue);
}

[Fact]
public void VoxReader_ReadFileWithNoNotes_NotesAreEmptyStrings()
{
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_no_notes).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Palette.Notes.Should().AllBe("");
}

[Fact]
public void VoxReader_Read_NotesAreParsedCorrectly()
{
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_notes).First();

IVoxFile voxFile = VoxReader.Read(file);

for (int i = 0; i < 32; i++)
{
int iLocal = i;
voxFile.Palette.Notes.Should().ContainSingle(note => note == $"note {iLocal + 1}");
}
}

[Theory]
[InlineData(TestFile_3x3, 32)]
[InlineData(TestFile_notes, 32)]
public void VoxReader_Read_NoteCountIsCorrect(string testFile, int expectedCount)
{
string file = Zip.UnzipFilesFromSevenZipArchive(testFile).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Palette.Notes.Should().HaveCount(expectedCount);
}

[Fact]
public void VoxReader_Read_GlobalVoxelPositionIsCorrect()
Expand All @@ -28,18 +159,18 @@ public void VoxReader_Read_GlobalVoxelPositionIsCorrect()

voxFile.Models.Single(m => m.Name == "obj3").Voxels.Single(v => v.Color == Color.Blue).GlobalPosition.Should().Be(new Vector3(-3, 0, 3));
voxFile.Models.Single(m => m.Name == "obj3").Voxels.Single(v => v.Color == Color.Red).GlobalPosition.Should().Be(new Vector3(-1, 2, 5));

voxFile.Models.Single(m => m.Name == "obj4").Voxels.Single(v => v.Color == Color.Blue).GlobalPosition.Should().Be(new Vector3(-3, 0, 7));
voxFile.Models.Single(m => m.Name == "obj4").Voxels.Single(v => v.Color == Color.Red).GlobalPosition.Should().Be(new Vector3(-1, 2, 9));
}

[Fact]
public void VoxReader_Read_ModelNamesAreParsedCorrectly()
{
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_groups).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Models.Should().ContainSingle(m => m.Name == "obj1");
voxFile.Models.Should().ContainSingle(m => m.Name == "obj2");
voxFile.Models.Should().ContainSingle(m => m.Name == "obj3");
Expand All @@ -53,7 +184,7 @@ public void VoxReader_Read_ModelPositionsAreCorrectInGroups()
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_groups).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Models.Single(m => m.Name == "obj1").Position.Should().Be(new Vector3(0, 0, 0));
voxFile.Models.Single(m => m.Name == "obj2").Position.Should().Be(new Vector3(0, 0, 2));
voxFile.Models.Single(m => m.Name == "obj3").Position.Should().Be(new Vector3(-2, 1, 4));
Expand All @@ -67,7 +198,7 @@ public void VoxReader_Read_ModelPositionsAreCorrect()
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_MultipleModels).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Models.Single(m => m.Name == "black").Position.Should().Be(new Vector3(0, 0, 0));
voxFile.Models.Single(m => m.Name == "red").Position.Should().Be(new Vector3(2, 0, 0));
voxFile.Models.Single(m => m.Name == "green").Position.Should().Be(new Vector3(0, 2, 0));
Expand All @@ -83,7 +214,7 @@ public void VoxReader_Read_ModelPositionIsCorrectFor3x3x3Model()
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_3x3x3_at_center_with_corner).First();

IVoxFile voxFile = VoxReader.Read(file);

voxFile.Models.Single(m => m.Name == "obj1").Position.Should().Be(new Vector3(1, 1, 1));
}

Expand Down Expand Up @@ -120,6 +251,20 @@ public void VoxReader_Read_VoxelCountIsCorrect(string file, params int[] expecte
}
}

[Fact]
public void VoxReader_ReadFileFromVersion0_99_6_4_VoxelColorIsCorrect()
{
string file = Zip.UnzipFilesFromSevenZipArchive(TestFile_color_indices).First();

IVoxFile voxFile = VoxReader.Read(file);

IModel model = voxFile.Models.First();

model.Voxels.First(voxel => voxel.Position == new Vector3(0, 2, 0)).Color.Should().Be(Color.Red);
model.Voxels.First(voxel => voxel.Position == new Vector3(1, 1, 0)).Color.Should().Be(Color.Green);
model.Voxels.First(voxel => voxel.Position == new Vector3(2, 0, 0)).Color.Should().Be(Color.Blue);
}

[Fact]
public void VoxReader_Read_VoxelColorIsCorrect()
{
Expand Down
3 changes: 3 additions & 0 deletions VoxReader.UnitTests/data/color_indices.zip
Git LFS file not shown
3 changes: 3 additions & 0 deletions VoxReader.UnitTests/data/color_indices_2.zip
Git LFS file not shown
3 changes: 3 additions & 0 deletions VoxReader.UnitTests/data/color_notes.zip
Git LFS file not shown
3 changes: 3 additions & 0 deletions VoxReader.UnitTests/data/no_notes.zip
Git LFS file not shown
6 changes: 4 additions & 2 deletions VoxReader/ChunkFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ public static IChunk Parse(byte[] data)
return new GroupNodeChunk(data);
case ChunkType.ShapeNode:
return new ShapeNodeChunk(data);
case ChunkType.Note:
return new NoteChunk(data);
case ChunkType.IndexMap:
return new IndexMapChunk(data);
case ChunkType.MaterialOld:
case ChunkType.MaterialNew:
case ChunkType.Layer:
case ChunkType.Object:
case ChunkType.Camera:
case ChunkType.Note:
case ChunkType.IndexMap:
return new Chunk(data);
default:
throw new ArgumentOutOfRangeException();
Expand Down
21 changes: 21 additions & 0 deletions VoxReader/Chunks/IndexMapChunk.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using VoxReader.Interfaces;

namespace VoxReader.Chunks
{
internal class IndexMapChunk : Chunk, IIndexMapChunk
{
public int[] ColorIndices { get; }

public IndexMapChunk(byte[] data) : base(data)
{
var formatParser = new FormatParser(Content);

ColorIndices = new int[256];

for (int i = 0; i < ColorIndices.Length; i++)
{
ColorIndices[i] = formatParser.ParseInt8();
}
}
}
}
11 changes: 11 additions & 0 deletions VoxReader/Chunks/IndexMapChunk.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions VoxReader/Chunks/NoteChunk.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using VoxReader.Interfaces;

namespace VoxReader.Chunks
{
internal class NoteChunk : Chunk, INoteChunk
{
public string[] Notes { get; }

public NoteChunk(byte[] data) : base(data)
{
var formatParser = new FormatParser(Content);

int noteCount = formatParser.ParseInt32();

Notes = new string[noteCount];

for (int i = 0; i < noteCount; i++)
{
Notes[i] = formatParser.ParseStringAuto();
}
}
}
}
11 changes: 11 additions & 0 deletions VoxReader/Chunks/NoteChunk.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions VoxReader/Chunks/PaletteChunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ internal class PaletteChunk : Chunk, IPaletteChunk

public PaletteChunk(byte[] data) : base(data)
{
Colors = new Color[256];

var formatParser = new FormatParser(Content);

Colors = formatParser.ParseColors(Colors.Length);
Colors = formatParser.ParseColors(256);
}
}
}
3 changes: 3 additions & 0 deletions VoxReader/Color.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ public override string ToString()
internal static readonly Color Red = new(255, 0, 0, 255);
internal static readonly Color Green = new(0, 255, 0, 255);
internal static readonly Color Blue = new(0, 0, 255, 255);
internal static readonly Color Yellow = new(255, 255, 0, 255);
internal static readonly Color Magenta = new(255, 0, 255, 255);
internal static readonly Color Cyan = new(0, 255, 255, 255);
internal static readonly Color White = new(255, 255, 255, 255);
internal static readonly Color Black = new(0, 0, 0, 255);
}
Expand Down
Loading

0 comments on commit 0a938b4

Please sign in to comment.