int RebuildNormals() { // We assume it always happens AFTER RebuildVertices normals.Resize(vertices.Length); InteropLogger.Debug($"RebuildNormals name={Name}, Length={normals.Length}"); // Normals need to be cast from short -> float from Blender var normalScale = 1f / 32767f; for (int i = 0; i < verts.Length; i++) { var vert = verts[i]; // Like vertex coordinates - we swizzle y/z normals[i] = new InteropVector3( vert.no_x * normalScale, vert.no_z * normalScale, vert.no_y * normalScale ); } // Also update all existing split vertices to match // the original normal that may have been updated foreach (var kv in splitVertices) { var vertIndex = (int)loops[kv.Key].v; normals[kv.Value] = normals[vertIndex]; } // For now we have no splits. Eventually this may // change if we add support for split normals // coming from a CustomData layer return(0); }
int RebuildVertices() { // We resize to the number of vertices from Blender // PLUS the number of split vertices we already calculated // in a prior pass (thus already have the data filled out) vertices.Resize(verts.Length + splitVertices.Count); InteropLogger.Debug($"RebuildVertices name={Name}, Length={vertices.Length}"); for (int i = 0; i < verts.Length; i++) { var vert = verts[i]; // y/z are swizzled here to convert to Unity's coordinate space vertices[i] = new InteropVector3( vert.co_x, vert.co_z, vert.co_y ); } // Also update all existing split vertices to match // the original vertex that may have been updated foreach (var kv in splitVertices) { var vertIndex = (int)loops[kv.Key].v; vertices[kv.Value] = vertices[vertIndex]; } // This will never split return(0); }
internal bool Approx(InteropVector3 v) { var epsilon = 1e-6f; return(Math.Abs(x - v.x) < epsilon && Math.Abs(y - v.y) < epsilon && Math.Abs(z - v.z) < epsilon); }
/// <summary> /// Copy all mesh data - aligned to loops (thus no combination of like-vertices) /// </summary> internal void CopyMeshDataAlignedtoLoops( MVert[] verts, MLoop[] loops, MLoopTri[] loopTris, MLoopCol[] loopCols, List <MLoopUV[]> loopUVs ) { #if LEGACY Reallocate(loops.Length, loopTris.Length, loopUVs.Count, loopCols != null); // Normals need to be cast from short -> float from Blender var normalScale = 1f / 32767f; for (int i = 0; i < loops.Length; i++) { var co = verts[loops[i].v].co; var no = verts[loops[i].v].no; _vertices[i] = new InteropVector3(co[0], co[1], co[2]); _normals[i] = new InteropVector3( no[0] * normalScale, no[1] * normalScale, no[2] * normalScale ); // Copy UV layers - same length as loops for (int layer = 0; layer < loopUVs.Count; layer++) { _uvs[layer][i] = new InteropVector2( loopUVs[layer][i].uv ); } // Copy vertex colors - same length as loops if (loopCols != null) { var col = loopCols[i]; _colors[i] = new InteropColor32( col.r, col.g, col.b, col.a ); } } // Copy triangles for (uint t = 0; t < loopTris.Length; t++) { for (uint i = 0; i < 3; i++) { _triangles[(t * 3) + i] = loopTris[t].tri[i]; } } #endif }
public static Vector3 ToVector3(this InteropVector3 vec) { return(new Vector3(vec.x, vec.y, vec.z)); }
/// <summary> /// Copy all mesh data from Blender in one go and optimize down as much as possible. /// </summary> /// <remarks> /// Reference: LuxCoreRender/LuxCore::Scene_DefineBlenderMesh /// for the logic dealing with split normals / UVs / etc. /// </remarks> /// <param name="verts"></param> /// <param name="loops"></param> /// <param name="loopTris"></param> /// <param name="loopUVs"></param> /// <param name="loopCols"></param> internal void CopyMeshData_V1( MVert[] verts, MLoop[] loops, MLoopTri[] loopTris, MLoopCol[] loopCols, List <MLoopUV[]> loopUVs ) { #if LEGACY // In the case of split vertices - this'll resize DOWN // and then resize UP again for split vertices. Reallocate(verts.Length, loopTris.Length, loopUVs.Count, loopCols != null); var normalScale = 1f / 32767f; // Copy in vertex coordinates and normals for (int i = 0; i < verts.Length; i++) { var co = verts[i].co; var no = verts[i].no; _vertices[i] = new InteropVector3(co[0], co[1], co[2]); // Normals need to be cast from short -> float from Blender _normals[i] = new InteropVector3( no[0] * normalScale, no[1] * normalScale, no[2] * normalScale ); } // Copy UV layers for (int layer = 0; layer < loopUVs.Count; layer++) { var uvLayer = _uvs[layer]; for (uint i = 0; i < loopUVs[layer].Length; i++) { var vertIndex = loops[i].v; // This will overwrite itself for shared vertices - that's fine. // We'll be handling split UVs when reading in triangle data. uvLayer[vertIndex] = new InteropVector2( loopUVs[layer][i].uv ); } } // Copy vertex colors if we got 'em if (loopCols != null) { for (uint i = 0; i < loopCols.Length; i++) { var vertIndex = loops[i].v; var col = loopCols[i]; _colors[vertIndex] = new InteropColor32( col.r, col.g, col.b, col.a ); } } // Track what triangle vertices need to be split. // This maps an index in `triangles` to an index in `loops` var splitTris = new Dictionary <uint, uint>(); // Generate triangle list while identifying any vertices that will need // to be split - due to having split UVs, normals, etc in the loop data. for (uint t = 0; t < loopTris.Length; t++) { for (uint i = 0; i < 3; i++) { var loopIndex = loopTris[t].tri[i]; var vertIndex = loops[loopIndex].v; var split = false; // Assumes .v is already < verts.Length // TODO: Test differing normals - not applicable // here as normals are only read in from MVert // Determine if we should make a new vertex for split UVs for (int layer = 0; layer < loopUVs.Count && !split; layer++) { var loopUV = loopUVs[layer][loopIndex].uv; var vertUV = _uvs[layer][vertIndex]; // TODO: Handle floating point errors? if (loopUV[0] != vertUV.x || loopUV[1] != vertUV.y) { split = true; } } // If we have vertex colors, check for split colors if (loopCols != null) { var col = loopCols[loopIndex]; var vertCol = _colors[vertIndex]; if (col.r != vertCol.r || col.g != vertCol.g || col.b != vertCol.b || col.a != vertCol.a ) { split = true; } } _triangles[(t * 3) + i] = vertIndex; // Track if we need to split the vertex in the triangle // to a new one once we've iterated through everything if (split) { splitTris.Add((t * 3) + i, loopIndex); } } } // 7958 + 32245 = 40203 // LOOPS are 31488 // 7958 * 3 = 23874 // 15744 loop triangles // 31488 vertex color indices // If we have triangle verts to split - apply all at once so there's // only a single re-allocation to our arrays. var totalNewVertices = splitTris.Count; if (totalNewVertices > 0) { InteropLogger.Debug($"Splitting {totalNewVertices} vertices"); var newVertIndex = (uint)verts.Length; // Reallocate everything to fit the new set of vertices Reallocate(verts.Length + totalNewVertices, loopTris.Length, loopUVs.Count, loopCols != null); // Generate new vertices with any split data (normals, UVs, colors, etc) foreach (var tri in splitTris.Keys) { var prevVertIndex = _triangles[tri]; // MVert index var loopIndex = splitTris[tri]; // MLoop index // Same coordinates as the original vertex _vertices[newVertIndex] = _vertices[prevVertIndex]; // TODO: If there were split normals, that'd be handled here. _normals[newVertIndex] = _normals[prevVertIndex]; // Read UVs from loops again to handle any split UVs for (int layer = 0; layer < loopUVs.Count; layer++) { var uv = loopUVs[layer][loopIndex].uv; _uvs[layer][newVertIndex] = new InteropVector2(uv); } // Same deal for vertex colors - copy from the loop if (loopCols != null) { var col = loopCols[loopIndex]; _colors[newVertIndex] = new InteropColor32( col.r, col.g, col.b, col.a ); } // And finally update the triangle to point to the new vertex _triangles[tri] = newVertIndex; newVertIndex++; } } #endif }
/// <summary> /// Copy an <see cref="MVert"/> array into <see cref="Vertices"/> and <see cref="Normals"/>. /// </summary> /// <param name="verts"></param> internal void CopyFromMVerts(MVert[] verts) { var count = verts.Length; if (vertices == null) { vertices = new InteropVector3[count]; } if (normals == null) { normals = new InteropVector3[count]; } Array.Resize(ref vertices, count); Array.Resize(ref normals, count); // We're copying the data instead of sending directly into shared memory // because we cannot guarantee that: // 1. shared memory will be available for write when this is called // 2. the source MVert array hasn't been reallocated once shared // memory *is* available for write // TODO: "Smart" dirtying. // If verts don't change, don't flag for updates. // Or if verts change, only update a region of the verts. // Could do a BeginChangeCheck() ... EndChangeCheck() with // a bool retval if changes happened. // The idea is to do as much work as possible locally within // Blender to transmit the smallest deltas to Unity. // On a resize - this is just everything. int rangeStart = count; int rangeEnd = 0; int changedVertexCount = 0; for (int i = 0; i < count; i++) { var co = verts[i].co; var no = verts[i].no; var newVert = new InteropVector3(co[0], co[1], co[2]); // Normals need to be cast from short -> float var newNorm = new InteropVector3(no[0] / 32767f, no[1] / 32767f, no[2] / 32767f); if (!newVert.Approx(vertices[i]) || !newNorm.Approx(normals[i])) { rangeStart = Math.Min(rangeStart, i); rangeEnd = Math.Max(rangeEnd, i); changedVertexCount++; } vertices[i] = newVert; normals[i] = newNorm; // TECHNICALLY I can collect an index list of what changed and send just // that to Unity - but is it really worth the extra effort? We'll see! // Console.WriteLine($" - v={vertices[i]}, n={normals[i]}"); // Other issue is that changes may affect index 0, 1, and 1000, 1001 for a quad. // Or a change has shifted vertices around in the array due to some sort of // esoteric Blender operator. So to play it safe, we just update the whole array // until it can be guaranteed that we can identify an accurate slice of updates. } InteropLogger.Debug( $"** Changed {changedVertexCount} vertices within index range " + $"[{rangeStart}, {rangeEnd}] covering {rangeEnd - rangeStart} vertices" ); }