Skip to content

Instantly share code, notes, and snippets.

@HurricanKai
Created April 6, 2020 13:41
Show Gist options
  • Select an option

  • Save HurricanKai/d63a36016167204bcf613d08d07d7e70 to your computer and use it in GitHub Desktop.

Select an option

Save HurricanKai/d63a36016167204bcf613d08d07d7e70 to your computer and use it in GitHub Desktop.
.vox unity importer
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