Created
April 6, 2020 13:41
-
-
Save HurricanKai/d63a36016167204bcf613d08d07d7e70 to your computer and use it in GitHub Desktop.
.vox unity importer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections; | |
| using System.Collections.Generic; | |
| using System.IO; | |
| using System.Linq; | |
| using System.Runtime.InteropServices; | |
| using System.Text; | |
| using NUnit.Framework.Internal; | |
| using Unity.Collections.LowLevel.Unsafe; | |
| using Unity.Mathematics; | |
| using UnityEditor.Experimental.AssetImporters; | |
| using UnityEngine; | |
| [ScriptedImporter(1, "vox", AllowCaching = false, AutoSelect = true)] | |
| public class VoxImporter : ScriptedImporter | |
| { | |
| private List<int3> _modelSizes = new List<int3>(); | |
| private List<OctreeNode> _models = new List<OctreeNode>(); | |
| private VoxMaterial[] _mats = new VoxMaterial[256]; | |
| private VoxLayer[] _layers = new VoxLayer[8]; | |
| private int[] _palette; | |
| private Dictionary<int, INode> _nodes = new Dictionary<int, INode>(); | |
| private Material[] _finalMats; | |
| private Mesh _cubeMesh; | |
| private sealed class OctreeNode | |
| { | |
| public bool IsEmpty { get; } | |
| public int3 Extends { get; } | |
| public bool IsLeaf { get; } | |
| public byte[,,] Content { get; } | |
| public OctreeNode[] Children { get; } | |
| public int3 Offset { get; } | |
| public OctreeNode(int3 extends, byte[,,] content, int3 offset = default) | |
| { | |
| if (extends.x == 1 && extends.y == 1 && extends.z == 1) | |
| { | |
| Offset = offset; | |
| Extends = extends; | |
| IsEmpty = content[0, 0, 0] == 0; | |
| IsLeaf = true; | |
| Content = content; | |
| return; | |
| } | |
| // Pad to power of two, else an octree cannot work | |
| { | |
| var extends2 = math.ceilpow2(extends); | |
| content = Pad(content, extends2 - extends); | |
| extends = extends2; | |
| } | |
| Extends = extends; | |
| Offset = offset; | |
| IsEmpty = true; | |
| bool single = true; | |
| byte s = content[0, 0, 0]; | |
| for (int x = 0; x < Extends.x; x++) | |
| for (int z = 0; z < Extends.z; z++) | |
| for (int y = 0; y < Extends.y; y++) | |
| { | |
| var c = content[x, y, z]; | |
| if (c != s) single = false; | |
| if (c != 0) IsEmpty = false; | |
| } | |
| IsLeaf = single; | |
| if (!single) | |
| { | |
| Children = new OctreeNode[8]; | |
| var newExtends = extends / 2; | |
| var o = int3.zero; | |
| Children[0] = new OctreeNode(newExtends, Slice(content, newExtends, o), Offset + o); | |
| o = new int3(newExtends.x, 0, 0); | |
| Children[1] = new OctreeNode(newExtends, Slice(content, newExtends, o), Offset + o); | |
| o = new int3(0, newExtends.y, 0); | |
| Children[2] = new OctreeNode(newExtends, Slice(content, newExtends, o), Offset + o); | |
| o = new int3(0, 0, newExtends.z); | |
| Children[3] = new OctreeNode(newExtends, Slice(content, newExtends, o), Offset + o); | |
| o = new int3(newExtends.x, newExtends.y, 0); | |
| Children[4] = new OctreeNode(newExtends, Slice(content, newExtends, o), Offset + o); | |
| o = new int3(newExtends.x, 0, newExtends.z); | |
| Children[5] = new OctreeNode(newExtends, Slice(content, newExtends, o), Offset + o); | |
| o = new int3(0, newExtends.y, newExtends.z); | |
| Children[6] = new OctreeNode(newExtends, Slice(content, newExtends, o), Offset + o); | |
| o = newExtends; | |
| Children[7] = new OctreeNode(newExtends, Slice(content, newExtends, o), Offset + o); | |
| } | |
| else if (!IsEmpty) | |
| { | |
| Content = content; | |
| } | |
| } | |
| private static byte[,,] Slice(byte[,,] arr, int3 newExtends, int3 offset) | |
| { | |
| var n = new byte[newExtends.x, newExtends.y, newExtends.z]; | |
| for (int x = 0; x < newExtends.x; x++) | |
| for (int z = 0; z < newExtends.z; z++) | |
| for (int y = 0; y < newExtends.y; y++) | |
| { | |
| n[x, y, z] = arr[x + offset.x, y + offset.y, z + offset.z]; | |
| } | |
| return n; | |
| } | |
| private static byte[,,] Pad(byte[,,] arr, int3 amount) | |
| { | |
| if (amount.x == 0 && amount.y == 0 && amount.z == 0) | |
| return arr; | |
| var xL = arr.GetLength(0); | |
| var yL = arr.GetLength(1); | |
| var zL = arr.GetLength(2); | |
| var n = new byte[xL + amount.x, yL + amount.y, zL + amount.z]; | |
| for (int x = 0; x < xL; x++) | |
| for (int z = 0; z < zL; z++) | |
| for (int y = 0; y < yL; y++) | |
| { | |
| n[x, y, z] = arr[x, y, z]; | |
| } | |
| return n; | |
| } | |
| } | |
| private interface INode | |
| { | |
| int OwnId { get; set; } | |
| Dictionary<string, string> Attributes { set; } | |
| IEnumerable<int> Walk(AssetImportContext ctx, GameObject parent, out GameObject gameObject); | |
| } | |
| private sealed class TransformNode : INode | |
| { | |
| private readonly int _child; | |
| private readonly int _layer; | |
| private readonly Frame[] _frames; | |
| public readonly struct Frame | |
| { | |
| public readonly float3x3 Rotation; | |
| public readonly int3 Translation; | |
| public Frame(float3x3 rotation, int3 translation) | |
| { | |
| Rotation = rotation; | |
| Translation = translation; | |
| } | |
| } | |
| public int OwnId { get; set; } | |
| public Dictionary<string, string> Attributes { get; set; } | |
| public TransformNode(int child, int layer, Frame[] frames) | |
| { | |
| _child = child; | |
| _layer = layer; | |
| _frames = frames; | |
| } | |
| public IEnumerable<int> Walk(AssetImportContext ctx, GameObject parent, out GameObject gameObject) | |
| { | |
| string name; | |
| if (!Attributes.TryGetValue("_name", out name)) | |
| name = $"Transform {OwnId}"; | |
| gameObject = new GameObject(parent.name + " - " + name); | |
| gameObject.transform.SetParent(parent.transform, false); | |
| foreach (var frame in _frames) | |
| { | |
| gameObject.transform.Translate((float3)frame.Translation); | |
| // TODO: rotation | |
| } | |
| ctx.AddObjectToAsset(gameObject.name, gameObject); | |
| return new[] {_child}; | |
| } | |
| } | |
| private sealed class GroupNode : INode | |
| { | |
| private readonly int[] _children; | |
| public int OwnId { get; set; } | |
| public Dictionary<string, string> Attributes { get; set; } | |
| public GroupNode(int[] children) | |
| { | |
| _children = children; | |
| } | |
| public IEnumerable<int> Walk(AssetImportContext ctx, GameObject parent, out GameObject gameObject) | |
| { | |
| gameObject = new GameObject($"{parent.name} - Group {OwnId}"); | |
| gameObject.transform.SetParent(parent.transform, false); | |
| ctx.AddObjectToAsset(gameObject.name, gameObject); | |
| return _children; | |
| } | |
| } | |
| private sealed class ShapeNode : INode | |
| { | |
| public readonly struct ModelInfo | |
| { | |
| public readonly int Id; | |
| public readonly IReadOnlyDictionary<string, string> Attributes; | |
| public ModelInfo(int id, IReadOnlyDictionary<string, string> attributes) | |
| { | |
| Id = id; | |
| Attributes = attributes; | |
| } | |
| } | |
| private readonly ModelInfo[] _modelInfos; | |
| private readonly VoxImporter _parent; | |
| public int OwnId { get; set; } | |
| public Dictionary<string, string> Attributes { get; set; } | |
| public ShapeNode(ModelInfo[] modelInfos, VoxImporter parent) | |
| { | |
| _modelInfos = modelInfos; | |
| _parent = parent; | |
| } | |
| public IEnumerable<int> Walk(AssetImportContext ctx, GameObject parent, out GameObject gameObject) | |
| { | |
| _parent.BuildPalette(); | |
| gameObject = new GameObject($"{parent.name} - Shape {OwnId}"); | |
| gameObject.transform.SetParent(parent.transform, false); | |
| ctx.AddObjectToAsset(gameObject.name, gameObject); | |
| var resolved = _modelInfos | |
| .Select(x => _parent._models[x.Id]); | |
| int i = 1; | |
| foreach (var colors in resolved) | |
| { | |
| int j = 0; | |
| var subshape = new GameObject($"{gameObject.name} - Subshape {i}"); | |
| subshape.transform.SetParent(gameObject.transform, false); | |
| ctx.AddObjectToAsset(subshape.name, subshape); | |
| var stack = new Stack<OctreeNode>(); | |
| stack.Push(colors); | |
| while (stack.Count > 0) | |
| { | |
| var node = stack.Pop(); | |
| if (node.IsEmpty) | |
| continue; | |
| if (node.IsLeaf) | |
| { | |
| var mat = _parent._finalMats[node.Content[0, 0, 0]]; | |
| var cube = new GameObject($"{subshape.name} - {j}"); | |
| cube.transform.SetParent(subshape.transform, false); | |
| cube.transform.localPosition = (float3)node.Offset; | |
| cube.transform.localScale = (float3)node.Extends; | |
| var mr = cube.AddComponent<MeshRenderer>(); | |
| mr.material = mat; | |
| var mf = cube.AddComponent<MeshFilter>(); | |
| mf.mesh = _parent._cubeMesh; | |
| ctx.AddObjectToAsset(cube.name, cube); | |
| j++; | |
| } | |
| else | |
| { | |
| foreach (var child in node.Children) stack.Push(child); | |
| } | |
| } | |
| i++; | |
| } | |
| return Enumerable.Empty<int>(); | |
| } | |
| } | |
| private readonly struct ModelPositionAndColor | |
| { | |
| public readonly byte X; | |
| public readonly byte Y; | |
| public readonly byte Z; | |
| public readonly byte ColorIndex; | |
| public readonly int VoxelCount; | |
| public ModelPositionAndColor(byte x, byte y, byte z, byte colorIndex, int voxelCount) | |
| { | |
| X = x; | |
| Y = y; | |
| Z = z; | |
| ColorIndex = colorIndex; | |
| VoxelCount = voxelCount; | |
| } | |
| } | |
| private readonly struct VoxMaterial | |
| { | |
| public readonly VoxMaterialType Type; | |
| public readonly float? Weight; | |
| public readonly float? Rough; | |
| public readonly float? Spec; | |
| public readonly float? Ior; | |
| public readonly float? Att; | |
| public readonly float? Flux; | |
| public readonly float? Plastic; | |
| public VoxMaterial(VoxMaterialType type, float? weight, float? rough, float? spec, float? ior, float? att, float? flux, float? plastic) | |
| { | |
| Type = type; | |
| Weight = weight; | |
| Rough = rough; | |
| Spec = spec; | |
| Ior = ior; | |
| Att = att; | |
| Flux = flux; | |
| Plastic = plastic; | |
| } | |
| } | |
| private enum VoxMaterialType | |
| { | |
| Unknown = 0, | |
| Diffuse, | |
| Metal, | |
| Glass, | |
| Emit | |
| } | |
| private readonly struct VoxLayer | |
| { | |
| public readonly string Name; | |
| public readonly bool Hidden; | |
| public VoxLayer(string name, bool hidden) | |
| { | |
| Name = name; | |
| Hidden = hidden; | |
| } | |
| } | |
| private void BuildPalette() | |
| { | |
| if (_finalMats != null) | |
| return; | |
| _finalMats = new Material[256]; | |
| for (int i = 0; i < 256; i++) | |
| { | |
| var c = _palette[i]; | |
| var alpha = (byte)((c & 0xFF000000) >> 24); | |
| var blue = (byte)((c & 0xFF0000) >> 16); | |
| var green = (byte)((c & 0xFF00) >> 8); | |
| var red = (byte)(c & 0xFF); | |
| var color = new Color32(red, green, blue, alpha); | |
| string shader; | |
| var voxMaterial = _mats[i]; | |
| switch (voxMaterial.Type) | |
| { | |
| case VoxMaterialType.Metal: | |
| shader = "vox/metal"; | |
| break; | |
| case VoxMaterialType.Glass: | |
| shader = "vox/glass"; | |
| break; | |
| case VoxMaterialType.Emit: | |
| shader = "vox/emit"; | |
| break; | |
| case VoxMaterialType.Unknown: | |
| case VoxMaterialType.Diffuse: | |
| default: | |
| shader = "vox/diffuse"; | |
| break; | |
| } | |
| var mat = new Material(Shader.Find(shader)); | |
| mat.color = color; | |
| mat.name = $"Palette Material {i}"; | |
| if (voxMaterial.Weight.HasValue) | |
| mat.SetFloat("_weight", voxMaterial.Weight.Value); | |
| if (voxMaterial.Rough.HasValue) | |
| mat.SetFloat("_rough", voxMaterial.Rough.Value); | |
| if (voxMaterial.Spec.HasValue) | |
| mat.SetFloat("_spec", voxMaterial.Spec.Value); | |
| if (voxMaterial.Ior.HasValue) | |
| mat.SetFloat("_ior", voxMaterial.Ior.Value); | |
| if (voxMaterial.Att.HasValue) | |
| mat.SetFloat("_att", voxMaterial.Att.Value); | |
| if (voxMaterial.Flux.HasValue) | |
| mat.SetFloat("_flux", voxMaterial.Flux.Value); | |
| if (voxMaterial.Plastic.HasValue) | |
| mat.SetFloat("_plastic", voxMaterial.Plastic.Value); | |
| mat.enableInstancing = true; | |
| _finalMats[i] = mat; | |
| } | |
| } | |
| public override void OnImportAsset(AssetImportContext ctx) | |
| { | |
| var path = ctx.assetPath; | |
| using (var fileStream = File.OpenRead(path)) | |
| using (var binaryStream = new BinaryReader(fileStream)) | |
| { | |
| var str = Encoding.ASCII.GetString(binaryStream.ReadBytes(4)); | |
| if (str != "VOX ") | |
| { | |
| ctx.LogImportError("Invalid Vox File"); | |
| return; | |
| } | |
| var version = binaryStream.ReadUInt32(); | |
| if (version != 150) | |
| { | |
| ctx.LogImportError($"Unknown Vox File Version {version}"); | |
| return; | |
| } | |
| ProcessChunks(binaryStream, ctx, int.MaxValue); | |
| } | |
| _cubeMesh = CreateCubeMesh(); | |
| ctx.AddObjectToAsset("Cube Mesh", _cubeMesh); | |
| var currentNode = _nodes[0]; | |
| var currentObj = new GameObject("Root"); | |
| ctx.AddObjectToAsset(currentObj.name, currentObj); | |
| Walk(ctx, currentNode, currentObj); | |
| if (_finalMats != null) | |
| for (var index = 0; index < _finalMats.Length; index++) | |
| { | |
| var mat = _finalMats[index]; | |
| ctx.AddObjectToAsset(mat.name, mat); | |
| } | |
| ctx.SetMainObject(currentObj); | |
| } | |
| private Mesh CreateCubeMesh() | |
| { | |
| Vector3[] c = new Vector3[8]; | |
| c[0] = new Vector3(0, 0, 1); | |
| c[1] = new Vector3(1, 0, 1); | |
| c[2] = new Vector3(1, 0, 0); | |
| c[3] = new Vector3(0, 0, 0); | |
| c[4] = new Vector3(0, 1, 1); | |
| c[5] = new Vector3(1, 1, 1); | |
| c[6] = new Vector3(1, 1, 0); | |
| c[7] = new Vector3(0, 1, 0); | |
| Vector3[] vertices = new Vector3[] | |
| { | |
| c[0], c[1], c[2], c[3], // Bottom | |
| c[7], c[4], c[0], c[3], // Left | |
| c[4], c[5], c[1], c[0], // Front | |
| c[6], c[7], c[3], c[2], // Back | |
| c[5], c[6], c[2], c[1], // Right | |
| c[7], c[6], c[5], c[4] // Top | |
| }; | |
| //5) Define each vertex's Normal | |
| Vector3 up = Vector3.up; | |
| Vector3 down = Vector3.down; | |
| Vector3 forward = Vector3.forward; | |
| Vector3 back = Vector3.back; | |
| Vector3 left = Vector3.left; | |
| Vector3 right = Vector3.right; | |
| Vector3[] normals = new Vector3[] | |
| { | |
| down, down, down, down, // Bottom | |
| left, left, left, left, // Left | |
| forward, forward, forward, forward, // Front | |
| back, back, back, back, // Back | |
| right, right, right, right, // Right | |
| up, up, up, up // Top | |
| }; | |
| Vector2 uv00 = new Vector2(0f, 0f); | |
| Vector2 uv10 = new Vector2(1f, 0f); | |
| Vector2 uv01 = new Vector2(0f, 1f); | |
| Vector2 uv11 = new Vector2(1f, 1f); | |
| Vector2[] uvs = new Vector2[] | |
| { | |
| uv11, uv01, uv00, uv10, // Bottom | |
| uv11, uv01, uv00, uv10, // Left | |
| uv11, uv01, uv00, uv10, // Front | |
| uv11, uv01, uv00, uv10, // Back | |
| uv11, uv01, uv00, uv10, // Right | |
| uv11, uv01, uv00, uv10 // Top | |
| }; | |
| int[] triangles = new int[] | |
| { | |
| 3, 1, 0, 3, 2, 1, // Bottom | |
| 7, 5, 4, 7, 6, 5, // Left | |
| 11, 9, 8, 11, 10, 9, // Front | |
| 15, 13, 12, 15, 14, 13, // Back | |
| 19, 17, 16, 19, 18, 17, // Right | |
| 23, 21, 20, 23, 22, 21, // Top | |
| }; | |
| Mesh mesh = new Mesh(); | |
| mesh.Clear(); | |
| mesh.vertices = vertices; | |
| mesh.triangles = triangles; | |
| mesh.normals = normals; | |
| mesh.uv = uvs; | |
| mesh.Optimize(); | |
| return mesh; | |
| } | |
| private void Walk(AssetImportContext ctx, INode currentNode, GameObject currentObj) | |
| { | |
| var enumerable = currentNode.Walk(ctx, currentObj, out var newObj); | |
| foreach (var child in enumerable) | |
| { | |
| if (!_nodes.TryGetValue(child, out var node)) | |
| { | |
| ctx.LogImportWarning($"Could not find Node {child}"); | |
| continue; | |
| } | |
| Walk(ctx, node, newObj); | |
| } | |
| } | |
| private void ProcessChunks(BinaryReader binaryStream, AssetImportContext ctx, int count) | |
| { | |
| for (int i = 0; i < count && binaryStream.BaseStream.Position != binaryStream.BaseStream.Length; i++) | |
| { | |
| var str = Encoding.ASCII.GetString(binaryStream.ReadBytes(4)); | |
| var dataLength = binaryStream.ReadInt32(); | |
| var children = binaryStream.ReadInt32(); | |
| var data = binaryStream.ReadBytes(dataLength); | |
| using (var b = new BinaryReader(new MemoryStream(data))) | |
| { | |
| switch (str) | |
| { | |
| case "MAIN": | |
| break; | |
| case "PACK": | |
| ReadPack(b); | |
| break; | |
| case "XYZI": | |
| ReadXYZI(b); | |
| break; | |
| case "SIZE": | |
| ReadSize(b); | |
| break; | |
| case "RGBA": | |
| ReadPalette(b); | |
| break; | |
| case "MATL": | |
| ReadMaterial(ctx, b); | |
| break; | |
| case "LAYR": | |
| ReadLayer(ctx, b); | |
| break; | |
| case "nTRN": | |
| ReadTransformNode(ctx, b); | |
| break; | |
| case "nGRP": | |
| ReadGroupNode(b); | |
| break; | |
| case "nSHP": | |
| ReadShapeNode(b); | |
| break; | |
| case "rOBJ": | |
| // TODO: detect env objects? | |
| break; | |
| default: | |
| ctx.LogImportWarning($"Cannot understand Chunk \"{str}\" Size {dataLength}"); | |
| break; | |
| } | |
| } | |
| if (children > 0) | |
| ProcessChunks(binaryStream, ctx, children); | |
| } | |
| } | |
| private void ReadShapeNode(BinaryReader b) | |
| { | |
| var nodeId = b.ReadInt32(); | |
| var attributeCount = b.ReadInt32(); | |
| var attributes = new Dictionary<string, string>(attributeCount); | |
| for (int j = 0; j < attributeCount; j++) | |
| attributes.Add(ReadString(b), ReadString(b)); | |
| var modelCount = b.ReadInt32(); | |
| var modelInfos = new ShapeNode.ModelInfo[modelCount]; | |
| for (int j = 0; j < modelCount; j++) | |
| { | |
| var modelId = b.ReadInt32(); | |
| var modelAttributeCount = b.ReadInt32(); | |
| var modelAttributes = new Dictionary<string, string>(modelAttributeCount); | |
| for (int k = 0; k < attributeCount; k++) modelAttributes.Add(ReadString(b), ReadString(b)); | |
| modelInfos[j] = new ShapeNode.ModelInfo(modelId, modelAttributes); | |
| } | |
| var node = new ShapeNode(modelInfos, this); | |
| node.OwnId = nodeId; | |
| node.Attributes = attributes; | |
| _nodes[nodeId] = node; | |
| } | |
| private void ReadGroupNode(BinaryReader b) | |
| { | |
| var nodeId = b.ReadInt32(); | |
| var attributeCount = b.ReadInt32(); | |
| var attributes = new Dictionary<string, string>(); | |
| for (int j = 0; j < attributeCount; j++) | |
| attributes.Add(ReadString(b), ReadString(b)); | |
| var childCount = b.ReadInt32(); | |
| var children = new int[childCount]; | |
| for (int i = 0; i < childCount; i++) | |
| children[i] = b.ReadInt32(); | |
| var node = new GroupNode(children); | |
| node.OwnId = nodeId; | |
| node.Attributes = attributes; | |
| _nodes[nodeId] = node; | |
| } | |
| private void ReadTransformNode(AssetImportContext ctx, BinaryReader b) | |
| { | |
| var nodeId = b.ReadInt32(); | |
| var attributeCount = b.ReadInt32(); | |
| var attributes = new Dictionary<string, string>(); | |
| for (int j = 0; j < attributeCount; j++) | |
| attributes.Add(ReadString(b), ReadString(b)); | |
| var child = b.ReadInt32(); | |
| if (b.ReadInt32() != -1) | |
| ctx.LogImportWarning("Reserved was not -1"); | |
| var layer = b.ReadInt32(); | |
| var frameCount = b.ReadInt32(); | |
| if (frameCount != 1) | |
| { | |
| ctx.LogImportError("More then one Transform frame"); | |
| throw new Exception(); | |
| } | |
| float3x3 rotation = float3x3.identity; | |
| int3 translation = int3.zero; | |
| var dictFrameSize = b.ReadInt32(); | |
| for (int j = 0; j < dictFrameSize; j++) | |
| { | |
| var key = ReadString(b); | |
| var value = ReadString(b); | |
| switch (key) | |
| { | |
| case "_r": | |
| rotation = ParseRotation(byte.Parse(value)); | |
| break; | |
| case "_t": | |
| var parts = value.Split(' '); | |
| var x = int.Parse(parts[0]); | |
| var z = int.Parse(parts[1]); | |
| var y = int.Parse(parts[2]); | |
| translation = new int3(x, y, z); | |
| break; | |
| default: | |
| ctx.LogImportWarning($"Unknown Frame Attribute {key}"); | |
| break; | |
| } | |
| } | |
| var node = new TransformNode(child, layer, new[] {new TransformNode.Frame(rotation, translation)}); | |
| node.Attributes = attributes; | |
| node.OwnId = nodeId; | |
| _nodes[nodeId] = node; | |
| } | |
| private void ReadLayer(AssetImportContext ctx, BinaryReader b) | |
| { | |
| var layerId = b.ReadInt32(); | |
| var layerPropertyCount = b.ReadInt32(); | |
| string name = null; | |
| bool? hidden = null; | |
| for (int j = 0; j < layerPropertyCount; j++) | |
| { | |
| var layerPropertyKey = ReadString(b); | |
| var layerPropertyValue = ReadString(b); | |
| switch (layerPropertyKey) | |
| { | |
| case "_name": | |
| name = layerPropertyValue; | |
| break; | |
| case "_hidden": | |
| if (layerPropertyValue == "0") | |
| hidden = false; | |
| else if (layerPropertyValue == "1") | |
| hidden = true; | |
| else | |
| ctx.LogImportWarning($"Could not Parse Layer Hidden value {layerPropertyValue} at {layerId}"); | |
| break; | |
| default: | |
| ctx.LogImportWarning($"Unknown Layer Property {layerPropertyKey}"); | |
| break; | |
| } | |
| } | |
| _layers[layerId] = new VoxLayer(name, hidden ?? false); | |
| if (b.ReadInt32() != -1) ctx.LogImportWarning("Reserved Layer ID was not -1"); | |
| } | |
| private void ReadMaterial(AssetImportContext ctx, BinaryReader b) | |
| { | |
| var matId = b.ReadInt32(); | |
| var matDictCount = b.ReadInt32(); | |
| VoxMaterialType matType = VoxMaterialType.Unknown; | |
| float? weight = null; | |
| float? rough = null; | |
| float? spec = null; | |
| float? ior = null; | |
| float? att = null; | |
| float? flux = null; | |
| float? plastic = null; | |
| for (int j = 0; j < matDictCount; j++) | |
| { | |
| var matDictKey = ReadString(b); | |
| var matDictValue = ReadString(b); | |
| float v; | |
| switch (matDictKey) | |
| { | |
| case "_type": | |
| switch (matDictValue) | |
| { | |
| case "_diffuse": | |
| matType = VoxMaterialType.Diffuse; | |
| break; | |
| case "_metal": | |
| matType = VoxMaterialType.Metal; | |
| break; | |
| case "_glass": | |
| matType = VoxMaterialType.Glass; | |
| break; | |
| case "_emit": | |
| matType = VoxMaterialType.Emit; | |
| break; | |
| default: | |
| ctx.LogImportError($"Unrecognized Material type {matDictValue} at {matId}"); | |
| break; | |
| } | |
| break; | |
| case "_weight": | |
| if (!float.TryParse(matDictValue, out v)) | |
| ctx.LogImportError($"Could not parse Weight value {matDictValue} at {matId}"); | |
| else | |
| weight = v; | |
| break; | |
| case "_rough": | |
| if (!float.TryParse(matDictValue, out v)) | |
| ctx.LogImportError($"Could not parse Rough value {matDictValue} at {matId}"); | |
| else | |
| rough = v; | |
| break; | |
| case "_spec": | |
| if (!float.TryParse(matDictValue, out v)) | |
| ctx.LogImportError($"Could not parse Spec value {matDictValue} at {matId}"); | |
| else | |
| spec = v; | |
| break; | |
| case "_ior": | |
| if (!float.TryParse(matDictValue, out v)) | |
| ctx.LogImportError($"Could not parse ior value {matDictValue} at {matId}"); | |
| else | |
| ior = v; | |
| break; | |
| case "_att": | |
| if (!float.TryParse(matDictValue, out v)) | |
| ctx.LogImportError($"Could not parse att value {matDictValue} at {matId}"); | |
| else | |
| att = v; | |
| break; | |
| case "_flux": | |
| if (!float.TryParse(matDictValue, out v)) | |
| ctx.LogImportError($"Could not parse flux value {matDictValue} at {matId}"); | |
| else | |
| flux = v; | |
| break; | |
| case "_plastic": | |
| if (!float.TryParse(matDictValue, out v)) | |
| ctx.LogImportError($"Could not parse plastic value {matDictValue} at {matId}"); | |
| else | |
| plastic = v; | |
| break; | |
| default: | |
| ctx.LogImportWarning($"Unknown Material Property {matDictKey}"); | |
| break; | |
| } | |
| } | |
| _mats[matId - 1] = new VoxMaterial(matType, weight, rough, spec, ior, att, flux, plastic); | |
| } | |
| private unsafe void ReadPalette(BinaryReader b) | |
| { | |
| _palette = new int[256]; | |
| var paletteBytes = b.ReadBytes(4 * 256); | |
| fixed (void* palettePtr1 = paletteBytes) | |
| fixed (void* palettePtr2 = _palette) | |
| { | |
| UnsafeUtility.MemCpy(palettePtr2, palettePtr1, 4 * 256); | |
| } | |
| } | |
| private void ReadSize(BinaryReader b) | |
| { | |
| _modelSizes.Add(new int3(b.ReadInt32(), b.ReadInt32(), | |
| b.ReadInt32()).xzy); | |
| } | |
| private void ReadXYZI(BinaryReader b) | |
| { | |
| var voxelCount = b.ReadInt32(); | |
| var size = _modelSizes[_models.Count]; | |
| var arr = new byte[size.x, size.y, size.z]; | |
| for (int i = 0; i < voxelCount; i++) | |
| { | |
| var x = b.ReadByte(); | |
| var z = b.ReadByte(); | |
| var y = b.ReadByte(); | |
| var colorIndex = b.ReadByte(); | |
| arr[x, y, z] = colorIndex; | |
| } | |
| _models.Add(new OctreeNode(size, arr)); | |
| } | |
| private void ReadPack(BinaryReader b) | |
| { | |
| var size = b.ReadInt32(); | |
| var old1 = _modelSizes; | |
| var old2 = _models; | |
| _modelSizes = new List<int3>(size); | |
| _models = new List<OctreeNode>(size); | |
| _modelSizes.AddRange(old1); | |
| _models.AddRange(old2); | |
| } | |
| private static string ReadString(BinaryReader reader) | |
| { | |
| var length = reader.ReadInt32(); | |
| var bytes = reader.ReadBytes(length); | |
| return Encoding.ASCII.GetString(bytes); | |
| } | |
| private static float3x3 ParseRotation(byte b) | |
| { | |
| return float3x3.identity; | |
| var row1Index = b & 0b000000011; | |
| var row2Index = (b & 0b000001100) >> 2; | |
| row1Index--; | |
| row2Index--; | |
| float3 row1; | |
| float3 row2; | |
| float3 row3; | |
| if (row1Index == 0) | |
| row1 = new float3(1, 0, 0); | |
| else if (row1Index == 1) | |
| row1 = new float3(0, 1, 0); | |
| else if (row1Index == 2) | |
| row1 = new float3(0, 0, 1); | |
| else | |
| throw new IOException("Invalid Rotation Read 1"); | |
| if (row2Index == 0) | |
| row2 = new float3(1, 0, 0); | |
| else if (row2Index == 1) | |
| row2 = new float3(0, 1, 0); | |
| else if (row2Index == 2) | |
| row2 = new float3(0, 0, 1); | |
| else | |
| throw new IOException("Invalid Rotation Read 2"); | |
| if (row1Index == 0 && row2Index == 1 || row1Index == 1 && row2Index == 0) | |
| row3 = new float3(0, 0, 1); | |
| else if (row1Index == 0 && row2Index == 2 || row1Index == 2 && row2Index == 0) | |
| row3 = new float3(0, 1, 0); | |
| else if (row1Index == 1 && row2Index == 2 || row1Index == 2 && row2Index == 1) | |
| row3 = new float3(1, 0, 0); | |
| else | |
| throw new IOException("Invalid Rotation Read 3"); | |
| if (((b >> 4) & 0b1) != 0) | |
| row1 *= -1; | |
| if (((b >> 5) & 0b1) != 0) | |
| row2 *= -1; | |
| if (((b >> 6) & 0b1) != 0) | |
| row3 *= -1; | |
| return new float3x3(row1, row2, row3); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment