-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathQbFile.cs
More file actions
155 lines (132 loc) · 5.92 KB
/
QbFile.cs
File metadata and controls
155 lines (132 loc) · 5.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using static Voxels.MagicaVoxel;
namespace Voxels {
/// <summary>
/// Support for reading .QB files generated by Minidesk's Qubicle Tool.
/// http://minddesk.com/learn/article.php?id=22
/// </summary>
public class QbFile {
public uint Version { get; private set; } = 200;
VoxelDataColors voxelData;
const uint CODEFLAG = 2;
const uint NEXTSLICEFLAG = 6;
public QbFile() { }
public VoxelData Flatten() { return voxelData; }
bool Read(BinaryReader reader) {
// Version bytes are major, minor, release, build
Version = reader.ReadUInt32();
var colorFormat = reader.ReadInt32(); // 0=RGBA, 1=BGRA
var zAxisOrientation = reader.ReadInt32(); // 0=LeftHanded, 1=RightHanded
var compression = reader.ReadInt32(); // 1=compressed, 0=uncompressed
var visibilityMaskEncoded = reader.ReadInt32(); // 0=A->0(invisible),255(visible), 1=A->side visibility
var matrixCount = reader.ReadInt32();
var matrices = new List<VoxelData>();
var positions = new List<XYZ>();
for (var i = 0; i < matrixCount; ++i) {
var nameLength = reader.ReadByte();
var name = Encoding.UTF8.GetString(reader.ReadBytes(nameLength));
var sizeX = reader.ReadInt32();
var sizeY = reader.ReadInt32();
var sizeZ = reader.ReadInt32();
var posX = reader.ReadInt32();
var posY = reader.ReadInt32();
var posZ = reader.ReadInt32();
Func<uint, Voxel> colorFn = (v) => {
var c = colorFormat == 0 ? Color.FromRGBA(v) : Color.FromBGRA(v);
return new Voxel(new Color(c, c.A == 0 ? byte.MinValue : byte.MaxValue));
};
positions.Add(new XYZ(posX, posY, posZ));
var matrix = new VoxelDataColors(new XYZ(sizeX, sizeY, sizeZ));
if (compression == 0) {
for (var z = 0; z < sizeZ; z++) {
for (var y = 0; y < sizeY; y++) {
for (var x = 0; x < sizeX; x++) {
matrix[new XYZ(x, y, z)] = colorFn(reader.ReadUInt32());
}
}
}
}
else {
var z = 0;
while (z < sizeZ) {
var index = 0;
while (true) {
var data = reader.ReadUInt32();
if (data == NEXTSLICEFLAG) {
break;
}
if (data == CODEFLAG) {
var count = reader.ReadUInt32();
data = reader.ReadUInt32();
for (var j = 0; j < count; j++) {
var x = index % sizeX; // mod = modulo e.g. 12 mod 8 = 4
var y = index / sizeX; // div = integer division e.g. 12 div 8 = 1
index++;
matrix[new XYZ(x,y,z)] = colorFn(data);
}
}
else {
var x = index % sizeX;
var y = index / sizeX;
index++;
matrix[new XYZ(x,y,z)] = colorFn(data);
}
}
z++;
}
}
matrices.Add(matrix);
}
// Combine matrices into overall matrix
var minExtents = Enumerable.Range(0, matrixCount)
.Aggregate(XYZ.Max, (a, i) => {
var v = positions[i];
return new XYZ(Math.Min(a.X, v.X), Math.Min(a.Y, v.Y), Math.Min(a.Z, v.Z));
});
var maxExtents = Enumerable.Range(0, matrixCount)
.Aggregate(XYZ.Min, (a, i) => {
var v = positions[i] + matrices[i].Size;
return new XYZ(Math.Max(a.X, v.X), Math.Max(a.Y, v.Y), Math.Max(a.Z, v.Z));
});
var extents = maxExtents - minExtents;
var voxelData = new VoxelDataColors(extents);
for (var i = 0; i < matrixCount; ++i) {
var matrix = matrices[i];
var offset = positions[i] - minExtents; // Start at 0,0,0
for (var x = 0; x < matrix.Size.X; ++x) {
for (var y = 0; y < matrix.Size.Y; ++y) {
for (var z = 0; z < matrix.Size.Z; ++z) {
var pos = new XYZ(x, y, z);
voxelData[pos+offset] = matrix[pos];
}
}
}
}
// Orient .qb so thumbnail matches Qubicle default view
var s = voxelData.Size;
var d = new VoxelDataColors(new XYZ(s.X, s.Z, s.Y));
foreach (var i in voxelData) {
if (zAxisOrientation == 0) { // LEFT
d[new XYZ(i.X, s.Z - 1 - i.Z, i.Y)] = voxelData[i];
}
else { // RIGHT
d[new XYZ(i.X, i.Z, i.Y)] = voxelData[i];
}
}
this.voxelData = d;
return true;
}
public bool Read(Stream stream) {
return Read(new BinaryReader(stream));
}
public static VoxelData ReadAndFlatten(Stream stream) {
var quibicle = new QbFile();
quibicle.Read(stream);
return quibicle.Flatten();
}
}
}