You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			1042 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			1042 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C#
		
	
| namespace MagicaVoxelToolbox {
 | |
| 	using System.Collections;
 | |
| 	using System.Collections.Generic;
 | |
| 	using UnityEngine;
 | |
| 	using System.IO;
 | |
| 	using System.Text;
 | |
| 
 | |
| 
 | |
| 	public static class VoxelFile {
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- SUB ---
 | |
| 
 | |
| 
 | |
| 		public delegate void OnProgressHandler (float progress);
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- API ---
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static VoxelData GetVoxelData (byte[] voxelBytes, bool isVox, OnProgressHandler onProgress = null) {
 | |
| 			return isVox ? GetVoxelDataFromVox(voxelBytes, onProgress) : GetVoxelDataFromQb(voxelBytes);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		public static byte[] GetVoxelByte (VoxelData data, bool isVox, OnProgressHandler onProgress = null) {
 | |
| 			return isVox ? GetVoxFromVoxelData(data, onProgress) : GetQbFromVoxelData(data);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- VOX ---
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- Read ---
 | |
| 
 | |
| 
 | |
| 		private static VoxelData GetVoxelDataFromVox (byte[] voxBytes, OnProgressHandler onProgress) {
 | |
| 
 | |
| 			VoxelData data = new VoxelData();
 | |
| 
 | |
| 			if (!CheckID(voxBytes, "VOX ")) {
 | |
| 				Debug.LogError("Error with Magic Number. The file is not a vox file.");
 | |
| 				return null;
 | |
| 			}
 | |
| 
 | |
| 			using (MemoryStream ms = new MemoryStream(voxBytes)) {
 | |
| 				using (BinaryReader br = new BinaryReader(ms)) {
 | |
| 
 | |
| 					// VOX_
 | |
| 					br.ReadInt32();
 | |
| 
 | |
| 					// VERSION
 | |
| 					data.Version = System.BitConverter.ToInt32(br.ReadBytes(4), 0);
 | |
| 
 | |
| 					// MAIN
 | |
| 					byte[] chunkId = br.ReadBytes(4);
 | |
| 					int mainChunkSize = br.ReadInt32();
 | |
| 					int mainChildrenSize = br.ReadInt32();
 | |
| 					br.ReadBytes(mainChunkSize);
 | |
| 					if (!CheckID(chunkId, "MAIN")) {
 | |
| 						Debug.LogError("Error with Main Chunk ID");
 | |
| 						return null;
 | |
| 					}
 | |
| 
 | |
| 					// Containt
 | |
| 					int readSize = 0;
 | |
| 					Vector3 tempSize = new Vector3();
 | |
| 
 | |
| 					while (readSize < mainChildrenSize) {
 | |
| 
 | |
| 						string id = GetID(br.ReadBytes(4));
 | |
| 						readSize += 4;
 | |
| 
 | |
| 						switch (id) {
 | |
| 							case "PACK":
 | |
| 								readSize += ReadPackChunk(br);
 | |
| 								break;
 | |
| 							case "SIZE":
 | |
| 								readSize += ReadSizeChunk(br, out tempSize);
 | |
| 								break;
 | |
| 							case "XYZI":
 | |
| 								int[,,] tempVoxels = new int[(int)tempSize.x, (int)tempSize.y, (int)tempSize.z];
 | |
| 								readSize += ReadVoxelChunk(br, ref tempVoxels);
 | |
| 								data.Voxels.Add(tempVoxels);
 | |
| 								break;
 | |
| 							case "RGBA":
 | |
| 								readSize += ReadPalattee(br, ref data.Palette);
 | |
| 								break;
 | |
| 							case "nTRN":
 | |
| 								readSize += ReadTransform(br, ref data);
 | |
| 								break;
 | |
| 							case "nGRP":
 | |
| 								readSize += ReadGroup(br, ref data);
 | |
| 								break;
 | |
| 							case "nSHP":
 | |
| 								readSize += ReadShape(br, ref data);
 | |
| 								break;
 | |
| 							case "MATL":
 | |
| 								readSize += ReadMaterial(br, ref data);
 | |
| 								break;
 | |
| 							case "RIGG":
 | |
| 								readSize += ReadRig(br, ref data, 0);
 | |
| 								break;
 | |
| 							case "_RIG":
 | |
| 								readSize += ReadRig(br, ref data, VoxelData.RigData.CURRENT_VERSION);
 | |
| 								break;
 | |
| 							default:
 | |
| 								int chunkSize = br.ReadInt32();
 | |
| 								int childrenSize = br.ReadInt32();
 | |
| 								br.ReadBytes(chunkSize + childrenSize);
 | |
| 								readSize += chunkSize + childrenSize + 4 + 4;
 | |
| 								break;
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					if (onProgress != null) { onProgress.Invoke((float)readSize / mainChildrenSize); }
 | |
| 
 | |
| 					// Add Default Node if No Node
 | |
| 					if (data.Transforms.Count == 0) {
 | |
| 						data.ResetToDefaultNode();
 | |
| 					}
 | |
| 
 | |
| 					// Mat Fix
 | |
| 					if (data.Materials != null) {
 | |
| 						data.Materials.Insert(0, new VoxelData.MaterialData());
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 			}
 | |
| 			return data;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		// Chunk Reader
 | |
| 		private static int ReadPackChunk (BinaryReader _br) {
 | |
| 			int chunkSize = _br.ReadInt32();
 | |
| 			int childrenSize = _br.ReadInt32();
 | |
| 
 | |
| 			_br.ReadInt32();
 | |
| 
 | |
| 			if (childrenSize > 0) {
 | |
| 				_br.ReadBytes(childrenSize);
 | |
| 			}
 | |
| 			return chunkSize + childrenSize + 4 + 4;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static int ReadSizeChunk (BinaryReader _br, out Vector3 size) {
 | |
| 			int chunkSize = _br.ReadInt32();
 | |
| 			int childrenSize = _br.ReadInt32();
 | |
| 			int x = _br.ReadInt32();
 | |
| 			int z = _br.ReadInt32();
 | |
| 			int y = _br.ReadInt32();
 | |
| 			size = new Vector3(x, y, z);
 | |
| 			if (childrenSize > 0) {
 | |
| 				_br.ReadBytes(childrenSize);
 | |
| 			}
 | |
| 			return chunkSize + childrenSize + 4 + 4;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static int ReadVoxelChunk (BinaryReader _br, ref int[,,] tempVoxels) {
 | |
| 			int chunkSize = _br.ReadInt32();
 | |
| 			int childrenSize = _br.ReadInt32();
 | |
| 			int voxelNum = _br.ReadInt32();
 | |
| 			for (int i = 0; i < voxelNum; ++i) {
 | |
| 				int x = _br.ReadByte();
 | |
| 				int z = _br.ReadByte();
 | |
| 				int y = _br.ReadByte();
 | |
| 				tempVoxels[x, y, z] = _br.ReadByte();
 | |
| 			}
 | |
| 			if (childrenSize > 0) {
 | |
| 				_br.ReadBytes(childrenSize);
 | |
| 			}
 | |
| 			return chunkSize + childrenSize + 4 + 4;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static int ReadPalattee (BinaryReader _br, ref List<Color> colors) {
 | |
| 			colors = new List<Color>();
 | |
| 			int chunkSize = _br.ReadInt32();
 | |
| 			int childrenSize = _br.ReadInt32();
 | |
| 			for (int i = 0; i < 256; i++) {
 | |
| 				colors.Add(new Color(
 | |
| 					_br.ReadByte() / 255f,
 | |
| 					_br.ReadByte() / 255f,
 | |
| 					_br.ReadByte() / 255f,
 | |
| 					_br.ReadByte() / 255f)
 | |
| 				);
 | |
| 			}
 | |
| 			if (childrenSize > 0) {
 | |
| 				_br.ReadBytes(childrenSize);
 | |
| 			}
 | |
| 			return chunkSize + childrenSize + 4 + 4;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static int ReadMaterial (BinaryReader _br, ref VoxelData data) {
 | |
| 			int chunkSize = _br.ReadInt32();
 | |
| 			int childrenSize = _br.ReadInt32();
 | |
| 
 | |
| 			int id = _br.ReadInt32();
 | |
| 			var dic = ReadDictionary(_br);
 | |
| 
 | |
| 			data.Materials.Add(new VoxelData.MaterialData() {
 | |
| 				Index = id,
 | |
| 				Type = VoxelData.MaterialData.GetTypeFromString(TryGetString(dic, "_type", "_diffuse")),
 | |
| 				Weight = TryGetFloat(dic, "_weight", 0f),
 | |
| 				Rough = TryGetFloat(dic, "_rough", 0f),
 | |
| 				Spec = TryGetFloat(dic, "_spec", 0f),
 | |
| 				Ior = TryGetFloat(dic, "_ior", 0f),
 | |
| 				Att = TryGetFloat(dic, "_att", 0f),
 | |
| 				Flux = TryGetFloat(dic, "_flux", 0f),
 | |
| 				LDR = TryGetFloat(dic, "_ldr", 0f),
 | |
| 				Plastic = TryGetInt(dic, "_plastic", 0),
 | |
| 			});
 | |
| 
 | |
| 			if (childrenSize > 0) {
 | |
| 				_br.ReadBytes(childrenSize);
 | |
| 			}
 | |
| 			return chunkSize + childrenSize + 4 + 4;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static int ReadTransform (BinaryReader _br, ref VoxelData data) {
 | |
| 
 | |
| 			int chunkSize = _br.ReadInt32();
 | |
| 			int childrenSize = _br.ReadInt32();
 | |
| 
 | |
| 			int id = _br.ReadInt32();
 | |
| 			var dic = ReadDictionary(_br);
 | |
| 			int childID = _br.ReadInt32();
 | |
| 			int reservedID = _br.ReadInt32();
 | |
| 			int layerID = _br.ReadInt32();
 | |
| 			int frameNum = _br.ReadInt32();
 | |
| 			var frameData = new VoxelData.TransformData.FrameData[frameNum];
 | |
| 
 | |
| 			for (int i = 0; i < frameNum; i++) {
 | |
| 				var frameDic = ReadDictionary(_br);
 | |
| 				Vector3 rot;
 | |
| 				Vector3 scale;
 | |
| 				VoxMatrixByteToTransform(TryGetByte(frameDic, "_r", 4), out rot, out scale);
 | |
| 				frameData[i] = new VoxelData.TransformData.FrameData() {
 | |
| 					Position = TryGetVector3(frameDic, "_t", Vector3.zero),
 | |
| 					Rotation = rot,
 | |
| 					Scale = scale,
 | |
| 				};
 | |
| 				// Fix Y-Z
 | |
| 				frameData[i].Position = frameData[i].Position;
 | |
| 				frameData[i].Scale = frameData[i].Scale;
 | |
| 			}
 | |
| 
 | |
| 			if (!data.Transforms.ContainsKey(id)) {
 | |
| 				data.Transforms.Add(id, new VoxelData.TransformData() {
 | |
| 					Name = TryGetString(dic, "_name", ""),
 | |
| 					Hidden = TryGetString(dic, "_hidden", "0") == "1",
 | |
| 					ChildID = childID,
 | |
| 					LayerID = layerID,
 | |
| 					Reserved = reservedID,
 | |
| 					Frames = frameData,
 | |
| 				});
 | |
| 			}
 | |
| 
 | |
| 			if (childrenSize > 0) {
 | |
| 				_br.ReadBytes(childrenSize);
 | |
| 			}
 | |
| 			return chunkSize + childrenSize + 4 + 4;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static int ReadGroup (BinaryReader _br, ref VoxelData data) {
 | |
| 			int chunkSize = _br.ReadInt32();
 | |
| 			int childrenSize = _br.ReadInt32();
 | |
| 
 | |
| 			int id = _br.ReadInt32();
 | |
| 			var nodeAttr = ReadDictionary(_br);
 | |
| 			int childNum = _br.ReadInt32();
 | |
| 			var childData = new int[childNum];
 | |
| 
 | |
| 			for (int i = 0; i < childNum; i++) {
 | |
| 				childData[i] = _br.ReadInt32();
 | |
| 			}
 | |
| 
 | |
| 			if (!data.Groups.ContainsKey(id)) {
 | |
| 				data.Groups.Add(id, new VoxelData.GroupData() {
 | |
| 					Attributes = nodeAttr,
 | |
| 					ChildNodeId = childData,
 | |
| 				});
 | |
| 			}
 | |
| 
 | |
| 			if (childrenSize > 0) {
 | |
| 				_br.ReadBytes(childrenSize);
 | |
| 			}
 | |
| 			return chunkSize + childrenSize + 4 + 4;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static int ReadShape (BinaryReader _br, ref VoxelData data) {
 | |
| 			int chunkSize = _br.ReadInt32();
 | |
| 			int childrenSize = _br.ReadInt32();
 | |
| 
 | |
| 			int id = _br.ReadInt32();
 | |
| 			var nodeAttr = ReadDictionary(_br);
 | |
| 			int modelNum = _br.ReadInt32();
 | |
| 			var modelData = new KeyValuePair<int, Dictionary<string, string>>[modelNum];
 | |
| 
 | |
| 			for (int i = 0; i < modelNum; i++) {
 | |
| 				int modelId = _br.ReadInt32();
 | |
| 				var modelAttr = ReadDictionary(_br);
 | |
| 				modelData[i] = new KeyValuePair<int, Dictionary<string, string>>(modelId, modelAttr);
 | |
| 			}
 | |
| 
 | |
| 			if (!data.Shapes.ContainsKey(id)) {
 | |
| 				data.Shapes.Add(id, new VoxelData.ShapeData() {
 | |
| 					Attributes = nodeAttr,
 | |
| 					ModelData = modelData,
 | |
| 				});
 | |
| 			}
 | |
| 
 | |
| 			if (childrenSize > 0) {
 | |
| 				_br.ReadBytes(childrenSize);
 | |
| 			}
 | |
| 			return chunkSize + childrenSize + 4 + 4;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static int ReadRig (BinaryReader _br, ref VoxelData data, int version) {
 | |
| 			int chunkSize = _br.ReadInt32();
 | |
| 			int childrenSize = _br.ReadInt32();
 | |
| 
 | |
| 			int id = _br.ReadInt32();
 | |
| 
 | |
| 			var rigData = new VoxelData.RigData() {
 | |
| 				Bones = new List<VoxelData.RigData.Bone>(),
 | |
| 				Weights = new List<VoxelData.RigData.Weight>(),
 | |
| 				Version = version,
 | |
| 			};
 | |
| 
 | |
| 			// Bone
 | |
| 			int boneCount = _br.ReadInt32();
 | |
| 			for (int i = 0; i < boneCount; i++) {
 | |
| 				var bone = new VoxelData.RigData.Bone {
 | |
| 					Name = ReadString(_br),
 | |
| 					ParentIndex = _br.ReadInt32(),
 | |
| 					PositionX = _br.ReadInt32(),
 | |
| 					PositionY = _br.ReadInt32(),
 | |
| 					PositionZ = _br.ReadInt32(),
 | |
| 				};
 | |
| 				rigData.Bones.Add(bone);
 | |
| 			}
 | |
| 
 | |
| 			for (int i = 0; i < rigData.Bones.Count; i++) {
 | |
| 				int pIndex = rigData.Bones[i].ParentIndex;
 | |
| 				if (pIndex >= 0 && pIndex < rigData.Bones.Count) {
 | |
| 					rigData.Bones[i].Parent = rigData.Bones[pIndex];
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// Weight
 | |
| 			int WeightCount = _br.ReadInt32();
 | |
| 			for (int i = 0; i < WeightCount; i++) {
 | |
| 				var weight = new VoxelData.RigData.Weight {
 | |
| 					X = _br.ReadInt32(),
 | |
| 					Y = _br.ReadInt32(),
 | |
| 					Z = _br.ReadInt32(),
 | |
| 					BoneIndexA = _br.ReadInt32(),
 | |
| 					BoneIndexB = _br.ReadInt32(),
 | |
| 				};
 | |
| 				rigData.Weights.Add(weight);
 | |
| 			}
 | |
| 
 | |
| 			// Version
 | |
| 			rigData.FixVersion();
 | |
| 
 | |
| 			// End
 | |
| 			if (!data.Rigs.ContainsKey(id)) {
 | |
| 				data.Rigs.Add(id, rigData);
 | |
| 			}
 | |
| 
 | |
| 			if (childrenSize > 0) {
 | |
| 				_br.ReadBytes(childrenSize);
 | |
| 			}
 | |
| 
 | |
| 			return chunkSize + childrenSize + 4 + 4;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- Write ---
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] GetVoxFromVoxelData (VoxelData data, OnProgressHandler onProgress) {
 | |
| 			if (!data) { return null; }
 | |
| 			List<byte> voxByte = new List<byte>();
 | |
| 			byte[] mainChrunk = WriteMain(data, onProgress);
 | |
| 			voxByte.AddRange(Encoding.Default.GetBytes("VOX "));
 | |
| 			voxByte.AddRange(GetBytes(data.Version));
 | |
| 			voxByte.AddRange(Encoding.Default.GetBytes("MAIN"));
 | |
| 			voxByte.AddRange(GetBytes(0));
 | |
| 			voxByte.AddRange(GetBytes(mainChrunk.Length));
 | |
| 			voxByte.AddRange(mainChrunk);
 | |
| 			return voxByte.ToArray();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] WriteMain (VoxelData data, OnProgressHandler onProgress) {
 | |
| 
 | |
| 			const float STEP_COUNT = 5f;
 | |
| 
 | |
| 			List<byte> bytes = new List<byte>();
 | |
| 
 | |
| 			if (data.Voxels.Count > 1) {
 | |
| 				// PACK
 | |
| 				//bytes.AddRange(WritePack(data));
 | |
| 			}
 | |
| 
 | |
| 			for (int i = 0; i < data.Voxels.Count; i++) {
 | |
| 				if (onProgress != null) { onProgress.Invoke((i + 1) / STEP_COUNT / data.Voxels.Count); }
 | |
| 
 | |
| 				// SIZE
 | |
| 				bytes.AddRange(WriteSize(data.Voxels[i]));
 | |
| 				// XYZI
 | |
| 				bytes.AddRange(WriteVoxels(data.Voxels[i]));
 | |
| 			}
 | |
| 
 | |
| 			// RGBA
 | |
| 			if (onProgress != null) { onProgress.Invoke((2 / STEP_COUNT)); }
 | |
| 
 | |
| 			bytes.AddRange(WritePalette(data.Palette));
 | |
| 
 | |
| 
 | |
| 			// TGS
 | |
| 			var tgsList = new SortedList<int, byte[]>();
 | |
| 
 | |
| 			if (onProgress != null) { onProgress.Invoke(3 / STEP_COUNT); }
 | |
| 
 | |
| 			// nTRN
 | |
| 			foreach (var t in data.Transforms) {
 | |
| 				tgsList.Add(t.Key, WriteTransform(t.Key, t.Value));
 | |
| 			}
 | |
| 
 | |
| 			// nGRP
 | |
| 			foreach (var g in data.Groups) {
 | |
| 				tgsList.Add(g.Key, WriteGroup(g.Key, g.Value));
 | |
| 			}
 | |
| 
 | |
| 			// nSHP
 | |
| 			foreach (var s in data.Shapes) {
 | |
| 				tgsList.Add(s.Key, WriteShape(s.Key, s.Value));
 | |
| 			}
 | |
| 
 | |
| 			for (int i = 0; i < tgsList.Keys.Count; i++) {
 | |
| 				bytes.AddRange(tgsList[tgsList.Keys[i]]);
 | |
| 			}
 | |
| 
 | |
| 			if (onProgress != null) { onProgress.Invoke(4 / STEP_COUNT); }
 | |
| 			// MATL
 | |
| 			for (int i = 0; i < data.Materials.Count; i++) {
 | |
| 				bytes.AddRange(WriteMaterial(data.Materials[i]));
 | |
| 			}
 | |
| 
 | |
| 			if (onProgress != null) { onProgress.Invoke(5 / STEP_COUNT); }
 | |
| 			// RIGG
 | |
| 			foreach (var r in data.Rigs) {
 | |
| 				bytes.AddRange(WriteRig(r.Key, r.Value));
 | |
| 			}
 | |
| 
 | |
| 			return bytes.ToArray();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		// Chrunk Writer
 | |
| 		private static byte[] WritePack (VoxelData data) {
 | |
| 			List<byte> bytes = new List<byte>();
 | |
| 			bytes.AddRange(GetBytes(data.Voxels.Count));
 | |
| 			return ToChrunkByte(bytes, "PACK");
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] WriteSize (int[,,] voxels) {
 | |
| 			List<byte> bytes = new List<byte>();
 | |
| 			int size0 = Mathf.Clamp(voxels.GetLength(0), byte.MinValue, byte.MaxValue);
 | |
| 			int size1 = Mathf.Clamp(voxels.GetLength(1), byte.MinValue, byte.MaxValue);
 | |
| 			int size2 = Mathf.Clamp(voxels.GetLength(2), byte.MinValue, byte.MaxValue);
 | |
| 			bytes.AddRange(GetBytes(size0));
 | |
| 			bytes.AddRange(GetBytes(size2));
 | |
| 			bytes.AddRange(GetBytes(size1));
 | |
| 			return ToChrunkByte(bytes, "SIZE");
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] WriteVoxels (int[,,] voxels) {
 | |
| 			List<byte> bytes = new List<byte>();
 | |
| 			int lenX = Mathf.Clamp(voxels.GetLength(0), byte.MinValue, byte.MaxValue);
 | |
| 			int lenY = Mathf.Clamp(voxels.GetLength(1), byte.MinValue, byte.MaxValue);
 | |
| 			int lenZ = Mathf.Clamp(voxels.GetLength(2), byte.MinValue, byte.MaxValue);
 | |
| 			int voxelNum = 0;
 | |
| 			for (byte x = 0; x < lenX; x++) {
 | |
| 				for (byte y = 0; y < lenY; y++) {
 | |
| 					for (byte z = 0; z < lenZ; z++) {
 | |
| 						if (voxels[x, y, z] != 0) {
 | |
| 							bytes.Add(x);
 | |
| 							bytes.Add(z);
 | |
| 							bytes.Add(y);
 | |
| 							bytes.Add((byte)voxels[x, y, z]);
 | |
| 							voxelNum++;
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			bytes.InsertRange(0, GetBytes(voxelNum));
 | |
| 			return ToChrunkByte(bytes, "XYZI");
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] WritePalette (List<Color> palette) {
 | |
| 			List<byte> bytes = new List<byte>();
 | |
| 			for (int i = 0; i < 256; i++) {
 | |
| 				Color color = i < palette.Count ? palette[i] : Color.black;
 | |
| 				bytes.Add((byte)(color.r * 255f));
 | |
| 				bytes.Add((byte)(color.g * 255f));
 | |
| 				bytes.Add((byte)(color.b * 255f));
 | |
| 				bytes.Add((byte)(color.a * 255f));
 | |
| 			}
 | |
| 			return ToChrunkByte(bytes, "RGBA");
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] WriteTransform (int id, VoxelData.TransformData transform) {
 | |
| 
 | |
| 			List<byte> bytes = new List<byte>();
 | |
| 
 | |
| 			bytes.AddRange(GetBytes(id));
 | |
| 			// Dic Name Hidden
 | |
| 			bytes.AddRange(GetBytes(2));
 | |
| 			AddVoxStringBytes(ref bytes, "_name");
 | |
| 			AddVoxStringBytes(ref bytes, transform.Name);
 | |
| 			AddVoxStringBytes(ref bytes, "_hidden");
 | |
| 			AddVoxStringBytes(ref bytes, transform.Hidden ? "1" : "0");
 | |
| 			// child node id
 | |
| 			bytes.AddRange(GetBytes(transform.ChildID));
 | |
| 			bytes.AddRange(GetBytes(transform.Reserved));
 | |
| 			bytes.AddRange(GetBytes(transform.LayerID));
 | |
| 			bytes.AddRange(GetBytes(transform.Frames.Length));
 | |
| 			// frame dic
 | |
| 			for (int i = 0; i < transform.Frames.Length; i++) {
 | |
| 				var frame = transform.Frames[i];
 | |
| 				bytes.AddRange(GetBytes(2));
 | |
| 				AddVoxStringBytes(ref bytes, "_r");
 | |
| 				AddVoxStringBytes(ref bytes, TransformToVoxMatrixByteString(frame.Rotation, frame.Scale));
 | |
| 				AddVoxStringBytes(ref bytes, "_t");
 | |
| 				AddVoxStringBytes(ref bytes, string.Format(
 | |
| 					"{0} {1} {2}",
 | |
| 					((int)frame.Position.x).ToString(),
 | |
| 					((int)frame.Position.z).ToString(),
 | |
| 					((int)frame.Position.y).ToString()
 | |
| 				));
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 			return ToChrunkByte(bytes, "nTRN");
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] WriteGroup (int id, VoxelData.GroupData group) {
 | |
| 			List<byte> bytes = new List<byte>();
 | |
| 			bytes.AddRange(GetBytes(id));
 | |
| 			bytes.AddRange(GetBytes(group.Attributes.Count));
 | |
| 			foreach (var att in group.Attributes) {
 | |
| 				AddVoxStringBytes(ref bytes, att.Key);
 | |
| 				AddVoxStringBytes(ref bytes, att.Value);
 | |
| 			}
 | |
| 			bytes.AddRange(GetBytes(group.ChildNodeId.Length));
 | |
| 			for (int i = 0; i < group.ChildNodeId.Length; i++) {
 | |
| 				bytes.AddRange(GetBytes(group.ChildNodeId[i]));
 | |
| 			}
 | |
| 			return ToChrunkByte(bytes, "nGRP");
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] WriteShape (int id, VoxelData.ShapeData shape) {
 | |
| 			List<byte> bytes = new List<byte>();
 | |
| 			bytes.AddRange(GetBytes(id));
 | |
| 			bytes.AddRange(GetBytes(shape.Attributes.Count));
 | |
| 			foreach (var att in shape.Attributes) {
 | |
| 				AddVoxStringBytes(ref bytes, att.Key);
 | |
| 				AddVoxStringBytes(ref bytes, att.Value);
 | |
| 			}
 | |
| 			bytes.AddRange(GetBytes(shape.ModelData.Length));
 | |
| 			for (int i = 0; i < shape.ModelData.Length; i++) {
 | |
| 				var pair = shape.ModelData[i];
 | |
| 				bytes.AddRange(GetBytes(pair.Key));
 | |
| 				bytes.AddRange(GetBytes(pair.Value.Count));
 | |
| 				foreach (var att in pair.Value) {
 | |
| 					AddVoxStringBytes(ref bytes, att.Key);
 | |
| 					AddVoxStringBytes(ref bytes, att.Value);
 | |
| 				}
 | |
| 			}
 | |
| 			return ToChrunkByte(bytes, "nSHP");
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] WriteMaterial (VoxelData.MaterialData material) {
 | |
| 			List<byte> bytes = new List<byte>();
 | |
| 
 | |
| 			bytes.AddRange(GetBytes(material.Index));
 | |
| 			bytes.AddRange(GetBytes(9));
 | |
| 			AddVoxStringBytes(ref bytes, "_type");
 | |
| 			AddVoxStringBytes(ref bytes, VoxelData.MaterialData.GetStringFromType(material.Type));
 | |
| 			AddVoxStringBytes(ref bytes, "_weight");
 | |
| 			AddVoxStringBytes(ref bytes, material.Weight.ToString());
 | |
| 			AddVoxStringBytes(ref bytes, "_rough");
 | |
| 			AddVoxStringBytes(ref bytes, material.Rough.ToString());
 | |
| 			AddVoxStringBytes(ref bytes, "_spec");
 | |
| 			AddVoxStringBytes(ref bytes, material.Spec.ToString());
 | |
| 			AddVoxStringBytes(ref bytes, "_ior");
 | |
| 			AddVoxStringBytes(ref bytes, material.Ior.ToString());
 | |
| 			AddVoxStringBytes(ref bytes, "_att");
 | |
| 			AddVoxStringBytes(ref bytes, material.Att.ToString());
 | |
| 			AddVoxStringBytes(ref bytes, "_flux");
 | |
| 			AddVoxStringBytes(ref bytes, material.Flux.ToString());
 | |
| 			AddVoxStringBytes(ref bytes, "_ldr");
 | |
| 			AddVoxStringBytes(ref bytes, material.LDR.ToString());
 | |
| 			AddVoxStringBytes(ref bytes, "_plastic");
 | |
| 			AddVoxStringBytes(ref bytes, material.Plastic.ToString());
 | |
| 			return ToChrunkByte(bytes, "MATL");
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] WriteRig (int id, VoxelData.RigData rig) {
 | |
| 
 | |
| 			List<byte> bytes = new List<byte>();
 | |
| 
 | |
| 			bytes.AddRange(GetBytes(id));
 | |
| 
 | |
| 			// Bones
 | |
| 			bytes.AddRange(GetBytes(rig.Bones.Count));
 | |
| 			for (int i = 0; i < rig.Bones.Count; i++) {
 | |
| 				var bone = rig.Bones[i];
 | |
| 				AddVoxStringBytes(ref bytes, bone.Name);
 | |
| 				bytes.AddRange(GetBytes(bone.ParentIndex));
 | |
| 				bytes.AddRange(GetBytes(bone.PositionX));
 | |
| 				bytes.AddRange(GetBytes(bone.PositionY));
 | |
| 				bytes.AddRange(GetBytes(bone.PositionZ));
 | |
| 			}
 | |
| 
 | |
| 			// Weights
 | |
| 			bytes.AddRange(GetBytes(rig.Weights.Count));
 | |
| 			for (int i = 0; i < rig.Weights.Count; i++) {
 | |
| 				var weight = rig.Weights[i];
 | |
| 				bytes.AddRange(GetBytes(weight.X));
 | |
| 				bytes.AddRange(GetBytes(weight.Y));
 | |
| 				bytes.AddRange(GetBytes(weight.Z));
 | |
| 				bytes.AddRange(GetBytes(weight.BoneIndexA));
 | |
| 				bytes.AddRange(GetBytes(weight.BoneIndexB));
 | |
| 			}
 | |
| 
 | |
| 			return ToChrunkByte(bytes, "_RIG");
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- QB ---
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static VoxelData GetVoxelDataFromQb (byte[] qbBytes) {
 | |
| 			QbData qData = new QbData();
 | |
| 			using (MemoryStream ms = new MemoryStream(qbBytes)) {
 | |
| 				using (BinaryReader br = new BinaryReader(ms)) {
 | |
| 
 | |
| 
 | |
| 					int index;
 | |
| 					int data;
 | |
| 					uint count;
 | |
| 					const uint CODEFLAG = 2;
 | |
| 					const uint NEXTSLICEFLAG = 6;
 | |
| 
 | |
| 					qData.Version = br.ReadUInt32();
 | |
| 					qData.ColorFormat = br.ReadUInt32();
 | |
| 					qData.ZAxisOrientation = br.ReadUInt32();
 | |
| 					qData.Compressed = br.ReadUInt32();
 | |
| 					qData.VisibleMask = br.ReadUInt32();
 | |
| 					qData.NumMatrixes = br.ReadUInt32();
 | |
| 
 | |
| 					qData.MatrixList = new List<QbData.QbMatrix>();
 | |
| 
 | |
| 					for (int i = 0; i < qData.NumMatrixes; i++) {
 | |
| 
 | |
| 						QbData.QbMatrix qm = new QbData.QbMatrix();
 | |
| 
 | |
| 						// read matrix name
 | |
| 						int nameLength = br.ReadByte();
 | |
| 						qm.Name = br.ReadChars(nameLength).ToString(); // Name
 | |
| 
 | |
| 						// read matrix size
 | |
| 						qm.SizeX = br.ReadInt32();
 | |
| 						qm.SizeY = br.ReadInt32();
 | |
| 						qm.SizeZ = br.ReadInt32();
 | |
| 
 | |
| 						// read matrix position
 | |
| 						qm.PosX = br.ReadInt32();
 | |
| 						qm.PosY = br.ReadInt32();
 | |
| 						qm.PosZ = br.ReadInt32();
 | |
| 
 | |
| 						// create matrix and add to matrix list
 | |
| 						qm.Voxels = new int[qm.SizeX, qm.SizeY, qm.SizeZ];
 | |
| 
 | |
| 						int x, y, z;
 | |
| 						if (qData.Compressed == 0) {
 | |
| 							for (z = 0; z < qm.SizeZ; z++) {
 | |
| 								for (y = 0; y < qm.SizeY; y++) {
 | |
| 									for (x = 0; x < qm.SizeX; x++) {
 | |
| 										qm.Voxels[x, y, z] = (int)br.ReadUInt32();
 | |
| 									}
 | |
| 								}
 | |
| 							}
 | |
| 						} else {
 | |
| 							z = 0;
 | |
| 							while (z < qm.SizeZ) {
 | |
| 								index = 0;
 | |
| 								while (true) {
 | |
| 									data = (int)br.ReadUInt32();
 | |
| 									if (data == NEXTSLICEFLAG)
 | |
| 										break;
 | |
| 									else if (data == CODEFLAG) {
 | |
| 										count = br.ReadUInt32();
 | |
| 										data = (int)br.ReadUInt32();
 | |
| 										for (int j = 0; j < count; j++) {
 | |
| 											x = index % qm.SizeX;
 | |
| 											y = index / qm.SizeX;
 | |
| 											index++;
 | |
| 											qm.Voxels[x, y, z] = data;
 | |
| 										}
 | |
| 									} else {
 | |
| 										x = index % qm.SizeX;
 | |
| 										y = index / qm.SizeX;
 | |
| 										index++;
 | |
| 										qm.Voxels[x, y, z] = data;
 | |
| 									}
 | |
| 								}
 | |
| 								z++;
 | |
| 							}
 | |
| 						}
 | |
| 						qData.MatrixList.Add(qm);
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 			}
 | |
| 			return qData.GetVoxelData();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] GetQbFromVoxelData (VoxelData data) {
 | |
| 
 | |
| 
 | |
| 			List<byte> bytes = new List<byte>();
 | |
| 
 | |
| 			bytes.AddRange(System.BitConverter.GetBytes((uint)257));
 | |
| 			bytes.AddRange(System.BitConverter.GetBytes((uint)0)); // Always RGBA
 | |
| 			bytes.AddRange(System.BitConverter.GetBytes((uint)1)); // Always Right Handed
 | |
| 			bytes.AddRange(System.BitConverter.GetBytes(0)); // Always Not Compressed
 | |
| 			bytes.AddRange(System.BitConverter.GetBytes((uint)1));
 | |
| 			bytes.AddRange(System.BitConverter.GetBytes((uint)data.Voxels.Count));
 | |
| 
 | |
| 			for (int index = 0; index < data.Voxels.Count; index++) {
 | |
| 
 | |
| 				var voxels = data.Voxels[index];
 | |
| 
 | |
| 				// Get Size
 | |
| 				int sizeX = voxels.GetLength(0);
 | |
| 				int sizeY = voxels.GetLength(1);
 | |
| 				int sizeZ = voxels.GetLength(2);
 | |
| 
 | |
| 				// Get Position
 | |
| 				Vector3 size = data.GetModelSize(index);
 | |
| 				Vector3 pos, rot, scl;
 | |
| 				data.GetModelTransform(index, out pos, out rot, out scl);
 | |
| 				int posX = (int)pos.x - (int)size.x / 2;
 | |
| 				int posY = (int)pos.y - (int)size.y / 2;
 | |
| 				int posZ = (int)pos.z - (int)size.z / 2;
 | |
| 
 | |
| 				// name
 | |
| 				bytes.Add(0);
 | |
| 
 | |
| 				// size
 | |
| 				bytes.AddRange(System.BitConverter.GetBytes(sizeX));
 | |
| 				bytes.AddRange(System.BitConverter.GetBytes(sizeY));
 | |
| 				bytes.AddRange(System.BitConverter.GetBytes(sizeZ));
 | |
| 
 | |
| 				// pos
 | |
| 				bytes.AddRange(System.BitConverter.GetBytes(posX));
 | |
| 				bytes.AddRange(System.BitConverter.GetBytes(posY));
 | |
| 				bytes.AddRange(System.BitConverter.GetBytes(posZ));
 | |
| 
 | |
| 				// voxels
 | |
| 				for (int z = 0; z < sizeZ; z++) {
 | |
| 					for (int y = 0; y < sizeY; y++) {
 | |
| 						for (int x = 0; x < sizeX; x++) {
 | |
| 							int v = voxels[x, y, z];
 | |
| 							bytes.AddRange(System.BitConverter.GetBytes(
 | |
| 								v == 0 ? 0 : ColorToInt(data.GetColorFromPalette(v))
 | |
| 							));
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 
 | |
| 			return bytes.ToArray();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#region --- UTL ---
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte[] GetBytes (int i) {
 | |
| 			return System.BitConverter.GetBytes(i);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static void AddVoxStringBytes (ref List<byte> bytes, string str) {
 | |
| 			var strBytes = Encoding.Default.GetBytes(str);
 | |
| 			bytes.AddRange(GetBytes(strBytes.Length));
 | |
| 			bytes.AddRange(strBytes);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static bool CheckID (byte[] bytes, string id) {
 | |
| 			for (int i = 0; i < bytes.Length && i < id.Length; i++) {
 | |
| 				if (id[i] != bytes[i]) {
 | |
| 					return false;
 | |
| 				}
 | |
| 			}
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static string GetID (byte[] bytes) {
 | |
| 			string id = "";
 | |
| 			for (int i = 0; i < bytes.Length; i++) {
 | |
| 				id += (char)bytes[i];
 | |
| 			}
 | |
| 			return id;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static string ReadString (BinaryReader br) {
 | |
| 			int len = br.ReadInt32();
 | |
| 			byte[] bytes = br.ReadBytes(len);
 | |
| 			string str = "";
 | |
| 			for (int i = 0; i < bytes.Length; i++) {
 | |
| 				str += (char)bytes[i];
 | |
| 			}
 | |
| 			return str;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		// Dic
 | |
| 		private static Dictionary<string, string> ReadDictionary (BinaryReader br) {
 | |
| 			var dic = new Dictionary<string, string>();
 | |
| 			int len = br.ReadInt32();
 | |
| 			for (int i = 0; i < len; i++) {
 | |
| 				string key = ReadString(br);
 | |
| 				string value = ReadString(br);
 | |
| 				dic.Add(key, value);
 | |
| 			}
 | |
| 			return dic;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static float TryGetFloat (Dictionary<string, string> dic, string key, float defaultValue) {
 | |
| 			float res;
 | |
| 			if (dic.ContainsKey(key) && float.TryParse(dic[key], out res)) {
 | |
| 				return res;
 | |
| 			}
 | |
| 			return defaultValue;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static int TryGetInt (Dictionary<string, string> dic, string key, int defaultValue) {
 | |
| 			int res;
 | |
| 			if (dic.ContainsKey(key) && int.TryParse(dic[key], out res)) {
 | |
| 				return res;
 | |
| 			}
 | |
| 			return defaultValue;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static string TryGetString (Dictionary<string, string> dic, string key, string defaultValue) {
 | |
| 			return dic.ContainsKey(key) ? dic[key] : defaultValue;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static byte TryGetByte (Dictionary<string, string> dic, string key, byte defaultValue) {
 | |
| 			byte res;
 | |
| 			if (dic.ContainsKey(key) && byte.TryParse(dic[key], out res)) {
 | |
| 				return res;
 | |
| 			}
 | |
| 			return defaultValue;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static Vector3 TryGetVector3 (Dictionary<string, string> dic, string key, Vector3 defaultValue) {
 | |
| 			if (dic.ContainsKey(key)) {
 | |
| 				string[] valueStr = dic[key].Split(' ');
 | |
| 				Vector3 vector = Vector3.zero;
 | |
| 				if (valueStr.Length == 3) {
 | |
| 					for (int i = 0; i < 3; i++) {
 | |
| 						int value;
 | |
| 						if (int.TryParse(valueStr[i], out value)) {
 | |
| 							vector[i] = value;
 | |
| 						} else {
 | |
| 							return defaultValue;
 | |
| 						}
 | |
| 					}
 | |
| 					return Util.SwipYZ(vector);
 | |
| 				}
 | |
| 			}
 | |
| 			return defaultValue;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		// Chrunk
 | |
| 		private static byte[] ToChrunkByte (List<byte> source, string id) {
 | |
| 			int len = source.Count;
 | |
| 			source.InsertRange(0, GetBytes(0));
 | |
| 			source.InsertRange(0, GetBytes(len));
 | |
| 			source.InsertRange(0, Encoding.Default.GetBytes(id));
 | |
| 			return source.ToArray();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		// Matrix
 | |
| 		public static void VoxMatrixByteToTransform (byte mByte, out Vector3 rotation, out Vector3 scale) {
 | |
| 			Util.VoxMatrixByteToTransform(mByte, out rotation, out scale);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		private static string TransformToVoxMatrixByteString (Vector3 rot, Vector3 scale) {
 | |
| 			return Util.TransformToVoxMatrixByte(rot, scale).ToString();
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 		// Qb
 | |
| 		private static int ColorToInt (Color color) {
 | |
| 			return (
 | |
| 				((int)(color.a * 255) << 24) |
 | |
| 				((int)(color.b * 255) << 16) |
 | |
| 				((int)(color.g * 255) << 8) |
 | |
| 				((int)(color.r * 255) << 0)
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		#endregion
 | |
| 
 | |
| 
 | |
| 
 | |
| 	}
 | |
| } |