Example #1
0
        public static int AddMeshObjectToScene(
            [MarshalAs(UnmanagedType.LPStr)] string name,
            InteropMatrix4x4 transform,
            [MarshalAs(UnmanagedType.LPStr)] string material
            )
        {
            InteropLogger.Debug($"Adding mesh <name={name}, material={material}>");

            try
            {
                var obj = new SceneObject(name, SceneObjectType.Mesh);
                obj.Transform = transform;
                obj.Material  = material;

                Bridge.AddObject(obj);
                return(1);
            }
            catch (Exception e)
            {
                SetLastError(e);
                return(-1);
            }
        }
Example #2
0
        void RebuildTriangles()
        {
            triangles.Resize(loopTris.Length * 3);

            InteropLogger.Debug($"RebuildTriangles name={Name}, Length={triangles.Length}");

            for (int t = 0; t < loopTris.Length; t++)
            {
                var loopTri = loopTris[t];

                // Triangles are flipped due to coordinate space conversions
                // that happen from Blender to Unity
                int tri0 = (int)loopTri.tri_2;
                int tri1 = (int)loopTri.tri_1;
                int tri2 = (int)loopTri.tri_0;

                // If the triangle vert has been mapped to a split vertex,
                // use that instead of the original vertex
                triangles[t * 3 + 0] = splitVertices.GetValueOrDefault(tri0, (int)loops[tri0].v);
                triangles[t * 3 + 1] = splitVertices.GetValueOrDefault(tri1, (int)loops[tri1].v);
                triangles[t * 3 + 2] = splitVertices.GetValueOrDefault(tri2, (int)loops[tri2].v);
            }
        }
Example #3
0
        public bool ReplaceOrQueue <T>(RpcRequest type, string target, ref T data) where T : struct
        {
            var header = new InteropMessageHeader {
                type   = type,
                index  = 0,
                length = 0,
                count  = 0
            };

            var payload = FastStructure.ToBytes(ref data);

            // If it's already queued, replace the payload

            /*var queued = FindQueuedMessage(target, ref header);
             * if (queued != null)
             * {
             *  queued.payload = payload;
             *  return true;
             * }*/

            // Remove the old one to then queue up one at the end
            // This ensures messages that are queued up together
            // remain in their queued order.
            RemoveQueuedMessage(target, ref header);


            InteropLogger.Debug($"    ROQ-> {target}:{header.type:F}");
            outboundQueue.Enqueue(new InteropMessage
            {
                target  = target,
                header  = header,
                payload = payload
            });

            return(false);
        }
Example #4
0
        void UpdateMesh()
        {
            // If vertex length changes - Unity will throw a fit since we can't
            // just fill buffers without it trying to second guess us each step.
            //
            // This is especially common with metaballs.
            // So we assume vertex length changes = everything will be dirtied.
            // Not a great assumption though so.. TODO! :)
            if (vertices.Length != mesh.vertices.Length)
            {
                mesh.Clear();
            }

            // Channels that were dirtied from last time get loaded.
            if (vertices.IsDirty)
            {
                InteropLogger.Debug($"Dirty vertices={vertices.Length}");
                mesh.vertices = vertices.Read();
            }

            if (normals.IsDirty)
            {
                InteropLogger.Debug($"Dirty normals={normals.Length}");
                mesh.normals = normals.Read();
            }

            if (triangles.IsDirty)
            {
                InteropLogger.Debug($"Dirty triangles={triangles.Length}");

                // Change index format for large Blender meshes - when needed
                if (triangles.Length > short.MaxValue)
                {
                    mesh.indexFormat = IndexFormat.UInt32;
                }
                else
                {
                    mesh.indexFormat = IndexFormat.UInt16;
                }

                mesh.triangles = triangles.Read();
            }


            if (colors.IsDirty)
            {
                mesh.colors32 = colors.Read();
            }

            if (uv.IsDirty)
            {
                mesh.uv = uv.Read();
            }

            if (uv2.IsDirty)
            {
                mesh.uv2 = uv2.Read();
            }

            if (uv3.IsDirty)
            {
                mesh.uv3 = uv3.Read();
            }

            if (uv4.IsDirty)
            {
                mesh.uv4 = uv4.Read();
            }

            // TODO: Additional channels
            // mesh.boneWeights =
            // mesh.bindposes =
            // mesh.tangents =

            // mesh.MarkModified();

            // In editor mode - if the editor doesn't have focus then the mesh won't be updated.
            // So we force upload to the GPU once we have everything ready. We also maintain
            // local copies of mesh data to be updated by Blender whenever new data comes in.
            // (so we don't free up, say, UVs, and then not have that buffer when re-applying from Blender)
            mesh.UploadMeshData(false);
            // onUpdateVertices.Invoke(mesh);
        }
Example #5
0
        /// <summary>
        /// Generic implementation of mapping an array of Blender structs to interop structs.
        ///
        /// <para>
        ///     This handles target buffer (re)allocation and vertex splitting
        ///     if the values we're reading from <paramref name="source"/> deviates
        ///     from the value already written to the same index.
        /// </para>
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="source">Array of loop structs from Blender. Must be aligned to <see cref="loops"/></param>
        /// <param name="target">Array of interop structs for Unity. Must be aligned to <see cref="vertices"/></param>
        /// <returns></returns>
        int RebuildBuffer <TSource, TTarget>(
            NativeArray <TSource> source,
            ArrayBuffer <TTarget> target
            )
            where TSource : struct, IInteropConvertible <TTarget>
            where TTarget : struct
        {
            // No data in this buffer - clear target.
            if (source.Length < 1)
            {
                target.Clear();
                return(0);
            }

            // Match the target channel to the vertex length
            target.Resize(vertices.Length);

            InteropLogger.Debug(
                $"RebuildBuffer name={Name}, Length={target.Length}, " +
                $"TSource={typeof(TSource)}, TTarget={typeof(TTarget)}"
                );

            int splitCount = 0;
            var written    = new bool[target.Length];

            // Determine which vertices need to split and add entries
            for (int i = 0; i < source.Length; i++)
            {
                var val       = source[i];
                int vertIndex = (int)loops[i].v;

                if (splitVertices.ContainsKey(i))
                {
                    // Already split - copy to the split index
                    vertIndex = splitVertices[i];
                }
                // vertIndex will always be < written.Length
                // because any indices outside of that range will
                // be found in the splitVertices map
                else if (written[vertIndex])
                {
                    // If we already wrote to this vertex once -
                    // determine if we should split from the original.
                    // We exclude any indices outside the initial size
                    // that were added by Split() here.
                    var prevVal = target[vertIndex];

                    if (!val.Equals(prevVal))
                    {
                        vertIndex = Split(i);
                        splitCount++;
                    }
                }
                else
                {
                    written[vertIndex] = true;
                }

                target[vertIndex] = val.ToInterop();
            }

            InteropLogger.Debug($"RebuildBuffer name={Name} - Split {splitCount} vertices");

            return(splitCount);
        }
Example #6
0
        internal void CopyMeshDataNative(
            NativeArray <MVert> verts,
            NativeArray <MLoop> loops,
            NativeArray <MLoopTri> loopTris,
            NativeArray <MLoopCol> loopCols,
            NativeArray <MLoopUV> loopUVs
            )
        {
            // A change of unique vertex count or a change to the primary
            // loop mapping (loop index -> vertex index) requires a full rebuild.
            // This will hit frequently for things like Metaballs that change often
            if (verts.Length != this.verts.Length || !this.loops.Equals(loops))
            {
                InteropLogger.Debug($"CopyMeshData - rebuild all");
                this.loops.CopyFrom(loops);
                this.verts.CopyFrom(verts);
                this.loopCols.CopyFrom(loopCols);
                this.loopUVs.CopyFrom(loopUVs);
                // ... and so on

                this.loopTris.CopyFrom(loopTris);

                RebuildAll();
                return;
            }

            int prevVertexCount = vertices.Length;

            // Trigger rebuild of buffers based on whether the Blender data changed.

            if (!this.verts.Equals(verts))
            {
                InteropLogger.Debug("CopyMeshData - !verts.Equals");
                this.verts.CopyFrom(verts);
                RebuildVertices();
                RebuildNormals();
            }

            if (!this.loopCols.Equals(loopCols))
            {
                InteropLogger.Debug("CopyMeshData - !colors.Equals");
                this.loopCols.CopyFrom(loopCols);
                RebuildBuffer(this.loopCols, colors);
            }

            if (!this.loopUVs.Equals(loopUVs))
            {
                InteropLogger.Debug("CopyMeshData - !uvs.Equals");
                this.loopUVs.CopyFrom(loopUVs);
                RebuildBuffer(this.loopUVs, uvs);
            }

            // ... and so on

            InteropLogger.Debug("CopyMeshData - Check triangles");

            // If any of the channels created new split vertices
            // we need to rebuild the full triangle buffer to re-map
            // old loop triangle indices to new split vertex indices
            if (prevVertexCount != vertices.Length || !this.loopTris.Equals(loopTris))
            {
                InteropLogger.Debug($"CopyMeshData - !loopTris.Equals or prev {prevVertexCount} != {vertices.Length}");

                this.loopTris.CopyFrom(loopTris);
                RebuildTriangles();
            }
        }
Example #7
0
        /// <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
        }
Example #8
0
        /// <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"
                );
        }