static unsafe void GetVoxelsInternal(ref GetVoxelsContext context, int indexStart) { int i0 = context.indices[indexStart]; int i1 = context.indices[indexStart + 1]; int i2 = context.indices[indexStart + 2]; SimpleMesh.Vertex v0 = context.verts[i0]; SimpleMesh.Vertex v1 = context.verts[i1]; SimpleMesh.Vertex v2 = context.verts[i2]; float3 a = v0.Position; float3 b = v1.Position; float3 c = v2.Position; float3 normalTri; { float3 normalCross = cross(b - a, c - a); float normalCrossLengthSqrd = dot(normalCross, normalCross); if (normalCrossLengthSqrd == 0f) { return; } normalTri = normalCross * rcp(sqrt(normalCrossLengthSqrd)); } // extend every tri by half a voxel by just extending it along the corner-middle dir. // it's a naive attempt to get a conservative rasterizer - works good enough in general float3 middle = (a + b + c) / 3f; a += normalize(a - middle) * 0.5f; b += normalize(b - middle) * 0.5f; c += normalize(c - middle) * 0.5f; // get the AABB float3 minf = min(a, min(b, c)); float3 maxf = max(a, max(b, c)); int3 maxDimensions = context.maxDimensions; int3 mini = clamp(int3(floor(minf)), 0, maxDimensions); int3 maxi = clamp(int3(ceil(maxf)), 0, maxDimensions); int written = 0; VoxelizedPosition *positions = context.positions; int positionsLength = context.positionLength; Color color0 = v0.Color; Color color1 = v1.Color; Color color2 = v2.Color; for (int x = mini.x; x <= maxi.x; x++) { for (int z = mini.z; z <= maxi.z; z++) { for (int y = mini.y; y <= maxi.y; y++) { float3 voxel = float3(x, y, z) + 0.5f; float normalDistToTriangle = dot(voxel - a, normalTri); if (abs(normalDistToTriangle) > 0.5f) { continue; // distance to the triangle plane is over half a voxel -> the other side will have a voxel instead if we're at 0.5-1.0 dist } // set up barycentric coordinates float3 p = voxel - normalTri * normalDistToTriangle; float3 p0 = b - a; float3 p1 = c - a; float3 p2 = p - a; float d00 = dot(p0, p0); float d01 = dot(p0, p1); float d11 = dot(p1, p1); float d20 = dot(p2, p0); float d21 = dot(p2, p1); float denom = 1f / (d00 * d11 - d01 * d01); float3 barry; barry.y = (d11 * d20 - d01 * d21) * denom; // v1 barry.z = (d00 * d21 - d01 * d20) * denom; // v2 barry.x = 1.0f - barry.y - barry.z; // v0 if (any(barry < 0 | barry > 1)) { continue; // we're on the triangle plane, but outside the triangle } // interpolate vertex colors with the barycentric coordinates Color color; color.r = (color0.r * barry.x + color1.r * barry.y + color2.r * barry.z); color.g = (color0.g * barry.x + color1.g * barry.y + color2.g * barry.z); color.b = (color0.b * barry.x + color1.b * barry.y + color2.b * barry.z); color.a = 1f; float2 uv; uv.x = (v0.UV.x * barry.x + v1.UV.x * barry.y + v2.UV.x * barry.z); uv.y = (v0.UV.y * barry.x + v1.UV.y * barry.y + v2.UV.y * barry.z); int idx = x * (maxDimensions.z + 1) + z; positions[written++] = new VoxelizedPosition { XZIndex = idx, Y = (short)y, Color = color, MaterialIndex = (sbyte)v0.MaterialIndex, UV = uv }; if (written == positionsLength) { goto END; // buffer full, our triangle must've been huge } } } } END: context.writtenVoxelCount = written; }
public static SimpleMesh Import(string path, bool swapYZ) { long fileByteSize = new System.IO.FileInfo(path).Length; // would use Allocator.Temp, but that doesn't clean up on re-allocation; so total RAM balloons NativeArrayList <float3> positionsLUT = new NativeArrayList <float3>(1024 * 64, Allocator.Persistent); NativeArrayList <Color32> colorsLUT = new NativeArrayList <Color32>(1024 * 64, Allocator.Persistent); NativeArrayList <float2> uvLookupTable = new NativeArrayList <float2>(1024 * 64, Allocator.Persistent); NativeArrayList <SimpleMesh.Vertex> vertexResult = new NativeArrayList <SimpleMesh.Vertex>(1024 * 64, Allocator.Persistent); SimpleMesh.MaterialLib activeMaterialLib = default; SimpleMesh.Material activeMaterial = default; char[] splits = new char[] { ' ' }; Profiler.BeginSample("Read file"); using (var file = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read)) { using (var text = new System.IO.StreamReader(file)) { while (true) { if (text.EndOfStream) { break; } string line = text.ReadLine(); if (line == null || line.Length == 0) { continue; } // start of a line if (line.StartsWith("v ")) { ParsePositionLine(); } else if (line.StartsWith("f ")) { ParseFaceLine(); } else if (line.StartsWith("vt ")) { ParseUVLine(); } else if (line.StartsWith("vn ")) { // } else if (line.StartsWith("mtllib ")) { activeMaterialLib = SimpleMesh.MaterialLib.ParseFromObj(path, line.Substring("mtllib ".Length)); } else if (line.StartsWith("o ")) { // } else if (line.StartsWith("usemtl ")) { activeMaterial = activeMaterialLib.GetByName(line.Substring("usemtl ".Length)); } continue; void ParseUVLine() { string[] subs = line.Split(splits, System.StringSplitOptions.RemoveEmptyEntries); float2 uv = new float2(ParseFloat(subs[1]), ParseFloat(subs[2])); uvLookupTable.Add(uv); } void ParsePositionLine() { string[] subs = line.Split(splits, System.StringSplitOptions.RemoveEmptyEntries); float3 pos = new float3(ParseFloat(subs[1]), ParseFloat(subs[2]), ParseFloat(subs[3])); if (swapYZ) { float t = pos.z; pos.z = pos.y; pos.y = t; } positionsLUT.Add(pos); Color color; if (subs.Length > 6) { color.r = ParseFloat(subs[4]); color.g = ParseFloat(subs[5]); color.b = ParseFloat(subs[6]); color.a = 1f; } else { color = Color.white; } colorsLUT.Add(color); } float ParseFloat(string str) { return(float.Parse(str, System.Globalization.CultureInfo.InvariantCulture)); } void ParseFaceLine() { int index = 2; int entriesPerIndex = 1; for (int i = index; i < line.Length && line[i] != ' '; i++) { if (line[i] == '/') { entriesPerIndex++; } } switch (entriesPerIndex) { case 1: // f v1 v2 v3 for (int i = 0; i < 3; i++) { vertexResult.Add(GatherVertex(ParseFaceIndex(line, ref index), -1, -1)); index++; } break; case 2: // f v1/vt1 v2/vt2 v3/vt3 for (int i = 0; i < 3; i++) { int v = ParseFaceIndex(line, ref index); index++; // skip / int vt = ParseFaceIndex(line, ref index); index++; // skip space between the 3 indices vertexResult.Add(GatherVertex(v, vt, -1)); } break; case 3: // f v1/vt1/vn1 .. // f v1//vn1 .. for (int i = 0; i < 3; i++) { int v = ParseFaceIndex(line, ref index); index++; // skip / int vt = -1; if (line[index] != '/') { // vt1 vt = ParseFaceIndex(line, ref index); } index++; // skip second / int vn = ParseFaceIndex(line, ref index); index++; // skip space between the 3 indices vertexResult.Add(GatherVertex(v, vt, vn)); } break; } } SimpleMesh.Vertex GatherVertex(int positionIndex, int textureIndex, int normalIndex) { SimpleMesh.Vertex vertex = default; vertex.Color = colorsLUT[positionIndex]; vertex.Position = positionsLUT[positionIndex]; if (textureIndex >= 0) { vertex.UV = uvLookupTable[textureIndex]; } vertex.MaterialIndex = activeMaterial?.MaterialIndex ?? -1; return(vertex); } } } } positionsLUT.Dispose(); uvLookupTable.Dispose(); colorsLUT.Dispose(); Profiler.EndSample(); int vertexCount = vertexResult.Count; unsafe { SimpleMesh.Vertex *vertices = (SimpleMesh.Vertex *)SimpleMesh.MallocHelper <SimpleMesh.Vertex>(vertexCount); UnsafeUtility.MemCpy(vertices, vertexResult.Array.GetUnsafePtr(), UnsafeUtility.SizeOf <SimpleMesh.Vertex>() * vertexCount); vertexResult.Dispose(); int *indices = (int *)SimpleMesh.MallocHelper <int>(vertexCount); for (int i = 0; i < vertexCount; i++) { indices[i] = i; } return(new SimpleMesh(activeMaterialLib, vertices, indices, vertexCount, vertexCount)); } }