1 module voxd.vox;
2 
3 import std.range,
4        std.file,
5        std.array,
6        std.string;
7 
8 import voxd.utils;
9 
10 /// Supports the VOX format used by MagicaVoxel
11 
12 /// Decodes a VOX file.
13 /// Throws: VoxdException on error.
14 VOX decodeMagicaVOXFromFile(string filepath)
15 {
16     auto bytes = cast(ubyte[]) std.file.read(filepath);
17     return decodeMagicaVOXFromMemory(bytes);
18 }
19 
20 /// Decodes a VOX.
21 /// Throws: VoxdException on error.
22 VOX decodeMagicaVOXFromMemory(ubyte[] input)
23 {
24     // check header
25     {
26         uint magic = popBE!uint(input);
27         if (magic != RIFFChunkId!"VOX ")
28             throw new VoxdException("Expected 'VOX ' chunk.");
29 
30         immutable uint maxSupportedVersion = 150;
31         uint fileVersion = popLE!uint(input);
32         if (fileVersion > maxSupportedVersion)
33             throw new VoxdException(format("Unsupported version %s, maximum supported is %s", fileVersion, maxSupportedVersion));
34     }
35 
36     uint mainId;
37     uint mainSize;
38     getRIFFChunkHeader(input, mainId, mainSize);
39     if (mainId != RIFFChunkId!"MAIN")
40         throw new VoxdException("Expected MAIN chunk");
41 
42     long mainChildrenSize = popLE!uint(input);
43 
44     // skip main
45     skipBytes(input, cast(int)mainSize);
46 
47     // initialize palette
48     alias palette_t = VoxColor[256];
49     palette_t palette;
50     for (int i = 0; i < 256; ++i)
51         palette[i] = VoxColor(defaultPalette[i]);
52 
53 
54     // default palette
55     int[] indices;
56     bool foundSizeChunk = false;
57     int width;
58     int height;
59     int depth;
60 
61     while (mainChildrenSize > 0)
62     {
63         uint chunkId, chunkSize, childrenSize;
64         getRIFFChunkHeader(input, chunkId, chunkSize);
65         childrenSize = popLE!uint(input);
66 
67         if (chunkId == RIFFChunkId!"SIZE")
68         {
69             width = popLE!int(input);
70             height = popLE!int(input);
71             depth = popLE!int(input);
72 
73             indices.length = (width * height * depth);
74             indices[] = -1;
75             foundSizeChunk = true;
76         }
77         else if (chunkId == RIFFChunkId!"XYZI")
78         {
79             if (!foundSizeChunk)
80                 throw new VoxdException("Expected SIZE chunk before XYZI chunk");
81 
82             uint numVoxels = popLE!uint(input);
83 
84             if (chunkSize != 4 + numVoxels * 4)
85                 throw new VoxdException("Incorrect XYZI chunk size");
86 
87             for (uint i = 0; i < numVoxels; ++i)
88             {
89                 int x = popUbyte(input);
90                 int y = popUbyte(input);
91                 int z = popUbyte(input);
92                 ubyte c = popUbyte(input);
93                 if (c == 0)
94                     throw new VoxdException("Color 0 encountered");
95 
96                 indices[x + y * width + z *width * height] = c - 1;
97             }
98         }
99         else if (chunkId == RIFFChunkId!"RGBA")
100         {
101             if (chunkSize != 256 * 4)
102                 throw new VoxdException("Incorrect RGBA chunk size");
103 
104             for (int i = 0; i < 256; ++i)
105                 palette[i] = VoxColor(popLE!uint(input));
106         }
107 
108         // skip children size
109         skipBytes(input, childrenSize);
110 
111         mainChildrenSize -= (chunkSize + childrenSize + 12);
112     }
113 
114     if (!foundSizeChunk)
115         throw new VoxdException("Expected SIZE chunk");
116 
117     VOX result;
118 
119     result.width = width;
120     result.height = height;
121     result.depth = depth;
122     result.voxels.length = width * height * depth;
123 
124     for (int i = 0; i < width * height * depth; ++i)
125     {
126         if (indices[i] == -1)
127             result.voxels[i] = VoxColor(0);
128         else
129             result.voxels[i] = palette[indices[i]];
130     }
131 
132     return result;
133 }
134 
135 private
136 {
137 
138 
139 
140     struct MV_Voxel
141     {
142         ubyte x, y, z, colorIndex;
143     }
144 
145     static immutable uint[256] defaultPalette =
146     [
147         0x00000000, 0xffffffff, 0xffccffff, 0xff99ffff, 0xff66ffff, 0xff33ffff, 0xff00ffff, 0xffffccff, 0xffccccff, 0xff99ccff, 0xff66ccff, 0xff33ccff, 0xff00ccff, 0xffff99ff, 0xffcc99ff, 0xff9999ff,
148         0xff6699ff, 0xff3399ff, 0xff0099ff, 0xffff66ff, 0xffcc66ff, 0xff9966ff, 0xff6666ff, 0xff3366ff, 0xff0066ff, 0xffff33ff, 0xffcc33ff, 0xff9933ff, 0xff6633ff, 0xff3333ff, 0xff0033ff, 0xffff00ff,
149         0xffcc00ff, 0xff9900ff, 0xff6600ff, 0xff3300ff, 0xff0000ff, 0xffffffcc, 0xffccffcc, 0xff99ffcc, 0xff66ffcc, 0xff33ffcc, 0xff00ffcc, 0xffffcccc, 0xffcccccc, 0xff99cccc, 0xff66cccc, 0xff33cccc,
150         0xff00cccc, 0xffff99cc, 0xffcc99cc, 0xff9999cc, 0xff6699cc, 0xff3399cc, 0xff0099cc, 0xffff66cc, 0xffcc66cc, 0xff9966cc, 0xff6666cc, 0xff3366cc, 0xff0066cc, 0xffff33cc, 0xffcc33cc, 0xff9933cc,
151         0xff6633cc, 0xff3333cc, 0xff0033cc, 0xffff00cc, 0xffcc00cc, 0xff9900cc, 0xff6600cc, 0xff3300cc, 0xff0000cc, 0xffffff99, 0xffccff99, 0xff99ff99, 0xff66ff99, 0xff33ff99, 0xff00ff99, 0xffffcc99,
152         0xffcccc99, 0xff99cc99, 0xff66cc99, 0xff33cc99, 0xff00cc99, 0xffff9999, 0xffcc9999, 0xff999999, 0xff669999, 0xff339999, 0xff009999, 0xffff6699, 0xffcc6699, 0xff996699, 0xff666699, 0xff336699,
153         0xff006699, 0xffff3399, 0xffcc3399, 0xff993399, 0xff663399, 0xff333399, 0xff003399, 0xffff0099, 0xffcc0099, 0xff990099, 0xff660099, 0xff330099, 0xff000099, 0xffffff66, 0xffccff66, 0xff99ff66,
154         0xff66ff66, 0xff33ff66, 0xff00ff66, 0xffffcc66, 0xffcccc66, 0xff99cc66, 0xff66cc66, 0xff33cc66, 0xff00cc66, 0xffff9966, 0xffcc9966, 0xff999966, 0xff669966, 0xff339966, 0xff009966, 0xffff6666,
155         0xffcc6666, 0xff996666, 0xff666666, 0xff336666, 0xff006666, 0xffff3366, 0xffcc3366, 0xff993366, 0xff663366, 0xff333366, 0xff003366, 0xffff0066, 0xffcc0066, 0xff990066, 0xff660066, 0xff330066,
156         0xff000066, 0xffffff33, 0xffccff33, 0xff99ff33, 0xff66ff33, 0xff33ff33, 0xff00ff33, 0xffffcc33, 0xffcccc33, 0xff99cc33, 0xff66cc33, 0xff33cc33, 0xff00cc33, 0xffff9933, 0xffcc9933, 0xff999933,
157         0xff669933, 0xff339933, 0xff009933, 0xffff6633, 0xffcc6633, 0xff996633, 0xff666633, 0xff336633, 0xff006633, 0xffff3333, 0xffcc3333, 0xff993333, 0xff663333, 0xff333333, 0xff003333, 0xffff0033,
158         0xffcc0033, 0xff990033, 0xff660033, 0xff330033, 0xff000033, 0xffffff00, 0xffccff00, 0xff99ff00, 0xff66ff00, 0xff33ff00, 0xff00ff00, 0xffffcc00, 0xffcccc00, 0xff99cc00, 0xff66cc00, 0xff33cc00,
159         0xff00cc00, 0xffff9900, 0xffcc9900, 0xff999900, 0xff669900, 0xff339900, 0xff009900, 0xffff6600, 0xffcc6600, 0xff996600, 0xff666600, 0xff336600, 0xff006600, 0xffff3300, 0xffcc3300, 0xff993300,
160         0xff663300, 0xff333300, 0xff003300, 0xffff0000, 0xffcc0000, 0xff990000, 0xff660000, 0xff330000, 0xff0000ee, 0xff0000dd, 0xff0000bb, 0xff0000aa, 0xff000088, 0xff000077, 0xff000055, 0xff000044,
161         0xff000022, 0xff000011, 0xff00ee00, 0xff00dd00, 0xff00bb00, 0xff00aa00, 0xff008800, 0xff007700, 0xff005500, 0xff004400, 0xff002200, 0xff001100, 0xffee0000, 0xffdd0000, 0xffbb0000, 0xffaa0000,
162         0xff880000, 0xff770000, 0xff550000, 0xff440000, 0xff220000, 0xff110000, 0xffeeeeee, 0xffdddddd, 0xffbbbbbb, 0xffaaaaaa, 0xff888888, 0xff777777, 0xff555555, 0xff444444, 0xff222222, 0xff111111
163     ];
164 }