Esempio n. 1
0
        void OnBuildGameObject(RpcContext <BuildGameObject> ctx)
        {
            var materialCache = new MaterialCache {
                Materials = ctx.Data.Materials
            };
            var meshCache = new MeshCache {
                Meshes = ctx.Data.Meshes
            };
            var configs = new SyncObjectImportConfig
            {
                settings = new SyncObjectImportSettings
                {
                    defaultMaterial = ReflectMaterialManager.defaultMaterial,
                    importLights    = true
                },
                materialCache = materialCache,
                meshCache     = meshCache
            };

            var gameObject = m_Importer.Import(ctx.Data.InstanceData.SourceId, ctx.Data.Object, configs);

            gameObject.name = ctx.Data.Instance.Name;
            gameObject.transform.SetParent(m_Settings.Root);
            ImportersUtils.SetTransform(gameObject.transform, ctx.Data.Instance.Transform);
            ImportersUtils.SetMetadata(gameObject, ctx.Data.Instance.Metadata);

            ctx.SendSuccess(gameObject);
        }
Esempio n. 2
0
    // Use this for initialization
    void Start()
    {
        if (_instance != null)
        {
            Debug.LogWarning("There may be multiple TurboSlice components in scene " + Application.loadedLevelName + ". Please review this!");
        }

        _instance = this;

        if (meshCaching)
        {
            meshCaches = new Dictionary <Mesh, MeshCache>();

            foreach (GameObject go in preload)
            {
                Sliceable s = ensureSliceable(go);

                MeshCache c = cacheFromGameObject(s, false);
                c.wasPreloaded = true;

                MeshFilter filter = getMeshFilter(s);

                if (filter == null)
                {
                    Mesh m = filter.sharedMesh;

                    meshCaches[m] = c;
                }
                else
                {
                    Debug.LogWarning("Turbo Slicer cannot preload object '" + go.name + "'; cannot find mesh filter.");
                }
            }
        }
    }
Esempio n. 3
0
        protected override JobHandle StartGeneration(JobHandle dependOn)
        {
            if (sourceType == SourceType.FromTheme)
            {
                return(deferred ? deferredHandler.ScheduleDeferredJobs(SourceMeshData, Temps, dependOn) :
                       immediateHandler.ScheduleImmediateJobs(SourceMeshData, Temps, dependOn));
            }
            else if (sourceType == SourceType.FromMeshFilters)
            {
                MeshCache meshCache;
                NativeArray <DataInstance> dataInstances;
                MeshCache.CreateCombinationData(meshFilters, out meshCache, Allocator.TempJob, out dataInstances, Allocator.TempJob);

                Temps.Add(meshCache);
                Temps.Add(dataInstances);

                immediateHandler = new Immediate(dataInstances);
                return(immediateHandler.ScheduleImmediateJobs(meshCache.MeshData, Temps, dependOn));
            }
            else if (sourceType == SourceType.FromMeshesAndMatrices)
            {
                MeshCache meshCache;
                NativeArray <DataInstance> dataInstances;
                MeshCache.CreateCombinationData(meshes, matrices, out meshCache, Allocator.TempJob, out dataInstances, Allocator.TempJob);

                Temps.Add(meshCache);
                Temps.Add(dataInstances);

                immediateHandler = new Immediate(dataInstances);
                return(immediateHandler.ScheduleImmediateJobs(meshCache.MeshData, Temps, dependOn));
            }

            return(dependOn);
        }
    // Update mesh filter and mesh data if changed
    bool UpdateMeshFilter()
    {
        // Check change
        if (deformedMeshFilter == MeshFilter)
        {
            return(deformedMesh != null);
        }

        deformedMeshFilter = MeshFilter;

        if (deformedMeshFilter == null)
        {
            meshCache        = null;
            deformedMesh     = null;
            deformedVertices = null;
            deformedNormals  = null;

            return(false);
        }
        else
        {
            // Collect mesh data and cache
            meshCache        = MeshCache.GetMeshCache(deformedMeshFilter.sharedMesh);
            deformedMesh     = deformedMeshFilter.mesh;
            deformedVertices = deformedMesh.vertices;
            deformedNormals  = deformedMesh.normals;

            return(true);
        }
    }
Esempio n. 5
0
        public void Init()
        {
            VerifyBaseVariants();

            if (!configs.IsCreated)
            {
                int count = GetTypeConfigCount(type);
                configs = new NativeArray <ConfigTransformGroup>(count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
                FillConfigurations();
            }

            if (TileThemeCache == null)
            {
                foreach (var configVariants in baseVariants)
                {
                    foreach (var variant in configVariants.Variants)
                    {
                        if (variant == null)
                        {
                            Debug.LogError("theme: " + themeName + " config:" + ConfigToString(configVariants.PieceConfig) + " has null mesh!");
                        }
                    }
                }

                TileThemeCache = new MeshCache(baseVariants, Allocator.Persistent);
            }
        }
Esempio n. 6
0
        public void Dispose()
        {
            SafeDispose(ref configs);
            TileThemeCache?.Dispose();
            TileThemeCache = null;

            refCount = 0;
        }
    private MeshCache cacheFromGameObject(Sliceable sliceable, bool includeRoomForGrowth)
    {
        Renderer renderer = getMeshRenderer(sliceable);

        Mesh m = getMesh(sliceable);

        int initialCapacity = includeRoomForGrowth ? Mathf.RoundToInt((float)m.vertexCount * factorOfSafetyGeometry) : m.vertexCount;

        MeshCache c = new MeshCache();

        c.vertices = new TurboList <Vector3>(initialCapacity);
        if (sliceable.channelNormals)
        {
            c.normals = new TurboList <Vector3>(initialCapacity);
        }
        c.coords = new TurboList <Vector2>(initialCapacity);
        if (sliceable.channelUV2)
        {
            c.coords2 = new TurboList <Vector2>(initialCapacity);
        }

        c.indices = new int[m.subMeshCount][];

        for (int i = 0; i < m.subMeshCount; i++)
        {
            c.indices[i] = m.GetTriangles(i);
        }

        c.vertices.AddArray(m.vertices);
        if (sliceable.channelNormals)
        {
            c.normals.AddArray(m.normals);
        }
        c.coords.AddArray(m.uv);
        if (sliceable.channelUV2)
        {
            c.coords2.AddArray(m.uv2);
        }

        if (renderer != null)
        {
            if (renderer.sharedMaterials == null)
            {
                c.mats    = new Material[1];
                c.mats[0] = renderer.sharedMaterial;
            }
            else
            {
                c.mats = renderer.sharedMaterials;
            }
        }
        else
        {
            Debug.LogError("Object '" + sliceable.name + "' has no renderer");
        }

        return(c);
    }
Esempio n. 8
0
 /// <summary>
 /// Create the shape handler.
 /// </summary>
 /// <param name="categoryCheck"></param>
 /// <param name="meshCache">The mesh cache from which to read resources.</param>
 public PointCloudHandler(Runtime.CategoryCheckDelegate categoryCheck, MeshCache meshCache)
     : base(categoryCheck)
 {
     if (Root != null)
     {
         Root.name = Name;
     }
     MeshCache = meshCache;
 }
Esempio n. 9
0
    public MeshModification(Mesh mesh, string undoMessage)
    {
        _instance           = ScriptableObject.CreateInstance <MeshCache>();
        _instance.hideFlags = HideFlags.HideAndDontSave;
        _instance.mesh      = mesh;

        Undo.RegisterCreatedObjectUndo(_instance, undoMessage);

        Undo.RegisterCompleteObjectUndo(_instance, undoMessage);
    }
Esempio n. 10
0
    public MeshModification(Mesh mesh, string undoMessage)
    {
        _instance = ScriptableObject.CreateInstance<MeshCache>();
        _instance.hideFlags = HideFlags.HideAndDontSave;
        _instance.mesh = mesh;

        Undo.RegisterCreatedObjectUndo(_instance, undoMessage);

        Undo.RegisterCompleteObjectUndo(_instance, undoMessage);
    }
 /// <summary>
 /// Create the shape handler.
 /// </summary>
 /// <param name="meshCache">The mesh cache from which to read resources.</param>
 public PointCloudHandler(MeshCache meshCache)
 {
     // if (Root != null)
     // {
     //   Root.name = Name;
     // }
     _shapeCache.AddShapeDataType <PointsComponent>();
     _transientCache.AddShapeDataType <PointsComponent>();
     MeshCache = meshCache;
 }
Esempio n. 12
0
        public void Dispose()
        {
            for (int i = 0; i < _blockStorages.Length; i++)
            {
                _blockStorages[i]?.Dispose();
            }

            MeshCache?.Dispose();

            //  ArrayPool<byte>.Shared.Return(BlockLight.Data);
            //  ArrayPool<byte>.Shared.Return(SkyLight.Data);
        }
Esempio n. 13
0
            /// <summary>
            /// given a real-tri idx,
            /// return the 3 virtual verts belonging to this tri
            /// [BE WARNED: there're chances that the vverts are not three distinct verts, if the rtri is degraded into edge or point]
            /// </summary>
            public void GetVVertsFromRTri(int rtriIdx, out VVert vv0, out VVert vv1, out VVert vv2)
            {
                MeshCache cache = MeshCache.Instance;

                int[] tris = cache.triangles;

                int rvIdx0 = tris[rtriIdx * 3];
                int rvIdx1 = tris[rtriIdx * 3 + 1];
                int rvIdx2 = tris[rtriIdx * 3 + 2];

                vv0 = GetVV(rvIdx0);
                vv1 = GetVV(rvIdx1);
                vv2 = GetVV(rvIdx2);
            }
    // Get or create mesh cache for given mesh
    static public MeshCache GetMeshCache(Mesh sharedMesh)
    {
        // Linq search
        MeshCache meshCache = Cache.Where(c => c.Mesh == sharedMesh).FirstOrDefault();

        if (meshCache == null)
        {
            // Create a new cache for this mesh and store
            meshCache = new MeshCache(sharedMesh);
            Cache.Add(meshCache);
        }

        return(meshCache);
    }
Esempio n. 15
0
            public void GetVVertsFromRTri(int rtriIdx, List <VVert> vvs)
            {
                MeshCache cache = MeshCache.Instance;

                int[] tris = cache.triangles;

                for (int i = 0; i < 3; ++i)
                {
                    int   rvIdx = tris[rtriIdx * 3 + i];
                    VVert vv    = GetVV(rvIdx);
                    if (!vvs.Contains(vv))
                    {
                        vvs.Add(vv);
                    }
                }
            }
Esempio n. 16
0
        public void Start(IScene scene)
        {
            m_assetClient = scene.Simian.GetAppModule <IAssetClient>();
            m_meshCache   = scene.Simian.GetAppModule <MeshCache>();

            List <string> rendererNames = RenderingLoader.ListRenderers(Util.ExecutingDirectory());

            if (rendererNames.Count == 0)
            {
                m_log.Error("PrimMesher could not find a valid OpenMetaverse.Rendering.*.dll plugin");
                return;
            }

            // TODO: Add a config option to allow a preferred renderer to be selected
            m_renderer = RenderingLoader.LoadRenderer(rendererNames[0]);
            m_log.Debug(scene.Name + " is meshing prims with " + m_renderer);
        }
Esempio n. 17
0
        public static Mesh CreateMesh(IBody2 body, Color color, bool isSolid)
        {
            if (body == null)
            {
                throw new ArgumentNullException(nameof(body));
            }


            var tris = MeshCache.GetValue(body, bdy =>
            {
                var faceList = bdy.GetFaces().CastArray <IFace2>();
                var tess     = GetTess(bdy, faceList);
                return(Tesselate(faceList, tess)
                       .Buffer(3, 3)
                       .Select(b => new TriangleWithNormals(b[0], b[1], b[2])).ToList());
            });

            var edges = new List <Edge3>();

            return(new Mesh(color, isSolid, tris, edges));
        }
Esempio n. 18
0
        public MeshCache clone()
        {
            bool cloneNormals = normals != null;

            MeshCache mc = new MeshCache();

            int cap = Mathf.RoundToInt((float)vertices.Count * factorOfSafetyGeometry);

            ////////////
            mc.vertices = new TurboList <Vector3>(cap);
            if (cloneNormals)
            {
                mc.normals = new TurboList <Vector3>(cap);
            }
            mc.UVs = new TurboList <Vector2>(cap);

            ////////////
            mc.vertices.AddArray(vertices.array);
            if (cloneNormals)
            {
                mc.normals.AddArray(normals.array);
            }
            mc.UVs.AddArray(UVs.array);

            ////////////
            mc.indices = new int[indices.Length][];
            for (int i = 0; i < indices.Length; i++)
            {
                mc.indices[i] = new int[indices[i].Length];
                System.Array.Copy(indices[i], mc.indices[i], indices[i].Length);
            }

            mc.mats = mats;

            return(mc);
        }
Esempio n. 19
0
    private MeshCache cacheFromGameObject(Sliceable sliceable, bool includeRoomForGrowth)
    {
        Renderer renderer = getMeshRenderer(sliceable);

        Mesh m = getMesh(sliceable);

        int initialCapacity = includeRoomForGrowth ? Mathf.RoundToInt((float) m.vertexCount * factorOfSafetyGeometry) : m.vertexCount;

        MeshCache c = new MeshCache();

        c.vertices = new TurboList<Vector3>(initialCapacity);
        if(sliceable.channelNormals) c.normals = new TurboList<Vector3>(initialCapacity);
        c.coords = new TurboList<Vector2>(initialCapacity);
        if(sliceable.channelUV2) c.coords2 = new TurboList<Vector2>(initialCapacity);

        c.indices = new int[m.subMeshCount][];

        for(int i = 0; i < m.subMeshCount; i++)
        {
            c.indices[i] = m.GetTriangles(i);
        }

        c.vertices.AddArray(m.vertices);
        if(sliceable.channelNormals) c.normals.AddArray(m.normals);
        c.coords.AddArray(m.uv);
        if(sliceable.channelUV2) c.coords2.AddArray(m.uv2);

        if(renderer != null)
        {
            if(renderer.sharedMaterials == null)
            {
                c.mats = new Material[1];
                c.mats[0] = renderer.sharedMaterial;
            }
            else
            {
                c.mats = renderer.sharedMaterials;
            }
        }
        else
        {
            Debug.LogError("Object '" + sliceable.name + "' has no renderer");
        }

        return c;
    }
Esempio n. 20
0
    public GameObject[] splitByPlane(GameObject go, Vector4 plane, bool destroyOriginal)
    {
        if(go.GetComponentInChildren<SkinnedMeshRenderer>() != null)
        {
            return splitByPlaneRD(go, plane, destroyOriginal);
        }

        Sliceable sliceable = ensureSliceable(go);

        if(!sliceable.currentlySliceable)
        {
            GameObject[] result = { go };

            return result;
        }

        InfillConfiguration[] ourInfills = sliceable.infillers.Length > 0 ? sliceable.infillers : infills;

        MeshCache c = null;
        do
        {
            MeshFilter filter = getMeshFilter(sliceable);

            Mesh m = filter.sharedMesh;

            if(m == null)
            {
                break;
            }

            if(meshCaches != null && meshCaches.ContainsKey(m))
            {
                c = meshCaches[m];

                //The mesh cache will be directly modified under the assumption that this will be discarded shortly
                //and thus picked up by the GC. It will grow in size; it will not shrink. Thus we do not want to
                //operate on the original, semi-persistent mesh caches that were preloaded on boot. Instead, we want
                //to make a clone.

                if(c.wasPreloaded)
                {
                    c = c.clone();
                }
            }
            else
                c = cacheFromGameObject(sliceable, true);
        }
        while(false);
        if(c == null)
        {
            Debug.LogWarning("Turbo Slicer cannot find mesh filter in object '" + go.name + "' in scene '" + Application.loadedLevelName + "'! Only objects featuring a mesh filter can be sliced.");

            GameObject[] result = { go };

            return result;
        }

        int submeshCount = c.indices.Length;

        //We're going to create two new tentative meshes which contain ALL original vertices in order,
        //plus room for new vertices. Not all of these copied vertices will be addressed, but copying them
        //over eliminates the need to remove doubles and do an On^2 search.

        TurboList<int>[] _frontIndices = new TurboList<int>[ submeshCount ];
        TurboList<int>[] _backIndices = new TurboList<int>[ submeshCount ];

        PlaneTriResult[] sidePlanes = new PlaneTriResult[c.vertices.Count];
        {
            Vector3[] vertices = c.vertices.array;

            for(int i = 0; i < sidePlanes.Length; i++)
            {
                sidePlanes[i] = getSidePlane(ref vertices[i], ref plane);
            }
        }

        for(int j = 0; j < submeshCount; j++)
        {
            int initialCapacityIndices = Mathf.RoundToInt((float) c.indices[j].Length * factorOfSafetyIndices);

            _frontIndices[j] = new TurboList<int>(initialCapacityIndices);
            _backIndices[j] = new TurboList<int>(initialCapacityIndices);

            int[] _indices = c.indices[j];

            TurboList<int> frontIndices = _frontIndices[j];
            TurboList<int> backIndices = _backIndices[j];
            TurboList<int> splitPending = new TurboList<int>(initialCapacityIndices);

            int[] indices = new int[3];

            for(int i = 0; i < _indices.Length; )
            {
                indices[0] = _indices[i++];
                indices[1] = _indices[i++];
                indices[2] = _indices[i++];

                // compute the side of the plane each vertex is on
                PlaneTriResult r1 = sidePlanes[indices[0]];
                PlaneTriResult r2 = sidePlanes[indices[1]];
                PlaneTriResult r3 = sidePlanes[indices[2]];

                if ( r1 == r2 && r1 == r3 ) // if all three vertices are on the same side of the plane.
                {
                    if ( r1 == PlaneTriResult.PTR_FRONT ) // if all three are in front of the plane, then copy to the 'front' output triangle.
                    {
                        frontIndices.AddArray(indices);
                    }
                    else
                    {
                        backIndices.AddArray(indices);
                    }
                }
                else
                {
                    splitPending.AddArray(indices);
                }
            }

            InfillConfiguration ifc = null;

            if(j < c.mats.Length)
            {
                Material mat = c.mats[j];

                foreach(InfillConfiguration _ifc in ourInfills)
                {
                    if(_ifc.material == mat)
                    {
                        ifc = _ifc;
                    }
                }
            }

            splitTriangles(plane, splitPending.ToArray(), c, ifc, frontIndices, backIndices);
        }

        GameObject[] results;

        bool onlyHaveOne = true;

        for(int i = 0; i < c.indices.Length; i++)
        {
            onlyHaveOne &= _frontIndices[i].Count == 0 || _backIndices[i].Count == 0;
        }

        if(onlyHaveOne)
        {
            //Do nothing
            results = new GameObject[1];
            results[0] = go;
        }
        else
        {
            MeshCache frontCache = new MeshCache();
            frontCache.vertices = c.vertices;
            if(sliceable.channelNormals)
                frontCache.normals = c.normals;
            frontCache.UVs = c.UVs;
            frontCache.mats = c.mats;

            MeshCache backCache = new MeshCache();
            backCache.vertices = c.vertices;
            if(sliceable.channelNormals)
                backCache.normals = c.normals;
            backCache.UVs = c.UVs;
            backCache.mats = c.mats;

            frontCache.indices = new int[submeshCount][];
            backCache.indices = new int[submeshCount][];
            for(int i = 0; i < submeshCount; i++)
            {
                frontCache.indices[i] = _frontIndices[i].ToArray();
                backCache.indices[i] = _backIndices[i].ToArray();
            }

            Vector3[] geoSubsetOne, geoSubsetTwo;
            Vector3[] normalsSubsetOne = null, normalsSubsetTwo = null;
            Vector2[] uvSubsetOne, uvSubsetTwo;
            int[][] indexSubsetOne, indexSubsetTwo;

            indexSubsetOne = new int[submeshCount][];
            indexSubsetTwo = new int[submeshCount][];

            //Perfect subset will inflate the array list size if needed to the exact figure. So if we estimate 0,
            //and there is 1 submesh, than we will have 1 allocation, and this is optimal. Estimation can only help
            //if we have THREE or more submeshes, which is a silly scenario for anyone concerned about performance.
            int estimateOne = 0, estimateTwo = 0;

            TurboList<Vector3>
                _geoSubsetOne = null, _geoSubsetTwo = null,
                _normalSubsetOne = null, _normalSubsetTwo = null;

            TurboList<Vector2>
                _uvSubsetOne = null, _uvSubsetTwo = null;

            _geoSubsetOne = new TurboList<Vector3>(estimateOne);
            _geoSubsetTwo = new TurboList<Vector3>(estimateTwo);

            if(sliceable.channelNormals)
            {
                _normalSubsetOne = new TurboList<Vector3>(estimateOne);
                _normalSubsetTwo = new TurboList<Vector3>(estimateTwo);
            }

            _uvSubsetOne = new TurboList<Vector2>(estimateOne);
            _uvSubsetTwo = new TurboList<Vector2>(estimateTwo);

            int transferTableMaximumKey = c.vertices.Count;

            int[] transferTableOne = new int[transferTableMaximumKey];
            int[] transferTableTwo = new int[transferTableMaximumKey];

            for(int i = 0; i < transferTableOne.Length; i++) transferTableOne[i] = -1;
            for(int i = 0; i < transferTableTwo.Length; i++) transferTableTwo[i] = -1;

            for(int i = 0; i < submeshCount; i++)
                perfectSubset(_frontIndices[i], c.vertices, c.normals, c.UVs, out indexSubsetOne[i], _geoSubsetOne, _normalSubsetOne, _uvSubsetOne, ref transferTableOne );

            for(int i = 0; i < submeshCount; i++)
                perfectSubset(_backIndices[i], c.vertices, c.normals, c.UVs, out indexSubsetTwo[i], _geoSubsetTwo, _normalSubsetTwo, _uvSubsetTwo, ref transferTableTwo );

            geoSubsetOne = _geoSubsetOne.ToArray();
            geoSubsetTwo = _geoSubsetTwo.ToArray();
            if(sliceable.channelNormals)
            {
                normalsSubsetOne = _normalSubsetOne.ToArray();
                normalsSubsetTwo = _normalSubsetTwo.ToArray();
            }
            uvSubsetOne = _uvSubsetOne.ToArray();
            uvSubsetTwo = _uvSubsetTwo.ToArray();

            //Note that we do not explicitly call recalculate bounds because (as per the manual) this is implicit in an
            //assignment to vertices whenever the vertex count changes from zero to non-zero.

            Mesh frontMesh = new Mesh();
            Mesh backMesh = new Mesh();

            GameObject frontObject, backObject;

            createResultObjects(go, sliceable, false, plane, out frontObject, out backObject);

            getMeshFilter(frontObject.GetComponent<Sliceable>()).mesh = frontMesh;
            getMeshFilter(backObject.GetComponent<Sliceable>()).mesh = backMesh;

            frontMesh.vertices = geoSubsetOne;
            backMesh.vertices = geoSubsetTwo;

            if(sliceable.channelNormals)
            {
                frontMesh.normals = normalsSubsetOne;
                backMesh.normals = normalsSubsetTwo;
            }
            frontMesh.uv = uvSubsetOne;
            backMesh.uv = uvSubsetTwo;

            frontMesh.subMeshCount = submeshCount;
            backMesh.subMeshCount = submeshCount;

            for(int i = 0 ; i < submeshCount; i++)
            {
                frontMesh.SetTriangles(indexSubsetOne[i], i);
                backMesh.SetTriangles(indexSubsetTwo[i], i);
            }

            if(meshCaches != null)
            {
                if(go.GetComponent<DeletionCallback>() == null)
                {
                    frontObject.AddComponent<DeletionCallback>();
                    backObject.AddComponent<DeletionCallback>();
                }

                DeletionCallback frontCallback = frontObject.GetComponent<DeletionCallback>();
                DeletionCallback backCallback = backObject.GetComponent<DeletionCallback>();

                frontCallback.deletionListener = new DeletionOccurred(this.releaseCacheByMesh);
                backCallback.deletionListener = new DeletionOccurred(this.releaseCacheByMesh);

                frontCallback.mesh = frontMesh;
                backCallback.mesh = backMesh;

                meshCaches[frontMesh] = frontCache;
                meshCaches[backMesh] = backCache;
            }
            else
            {
                DeletionCallback frontCallback = frontObject.GetComponent<DeletionCallback>();
                DeletionCallback backCallback = backObject.GetComponent<DeletionCallback>();

                if(frontCallback != null)
                    GameObject.DestroyImmediate(frontCallback);

                if(backCallback != null)
                    GameObject.DestroyImmediate(backCallback);
            }

            if(destroyOriginal)
                GameObject.Destroy(go);

            results = new GameObject[2];
            results[0] = frontObject;
            results[1] = backObject;

            if(sliceable != null && sliceable.refreshColliders)
            {
                foreach(GameObject r in results)
                {
                    Collider collider = r.GetComponent<Collider>();

                    if(collider != null)
                    {
                        if(collider is BoxCollider)
                        {
                            GameObject.DestroyImmediate(collider);
                            r.AddComponent<BoxCollider>();
                        }
                        else if(collider is SphereCollider)
                        {
                            GameObject.DestroyImmediate(collider);
                            r.AddComponent<SphereCollider>();
                        }
                        else if(collider is MeshCollider)
                        {
                            MeshCollider mc = (MeshCollider) collider;

                            bool isFront = r == frontObject;

                            Mesh mesh = isFront ? frontMesh : backMesh;

                            mc.sharedMesh = mesh;
                        }
                    }
                }
            }

            if(sliceable != null)
                sliceable.handleSlice(results);
        }

        return results;
    }
Esempio n. 21
0
    static void splitTriangles(Vector4 plane, int[] sourceIndices,
		MeshCache meshCache, InfillConfiguration infill, TurboList<int> frontIndices, TurboList<int> backIndices)
    {
        bool doInfill = infill != null;

        bool doNormals = meshCache.normals != null;

        Vector3[] sourceGeometry = meshCache.vertices.array;
        Vector3[] sourceNormals = null;
        if(doNormals)
            sourceNormals = meshCache.normals.array;
        Vector2[] sourceUVs = meshCache.UVs.array;

        float[] pointClassifications = new float[sourceIndices.Length];
        for(int i = 0; i < pointClassifications.Length; i++)
        {
            pointClassifications[i] = classifyPoint(ref plane, ref sourceGeometry[ sourceIndices[i] ]);
        }

        //Now we're going to do the decision making pass. This is where we assess the side figures and produce actions...

        int inputTriangleCount = sourceIndices.Length / 3;

        //A good action count estimate can avoid reallocations.
        //We expect exactly five actions per triangle.
        int actionEstimate = inputTriangleCount * 5;
        List<SplitAction> splitActions = new List<SplitAction>(actionEstimate);

        //We want to count how many vertices are yielded from each triangle split. This will be used later to add the indices.
        short[] frontVertexCount = new short[inputTriangleCount];
        short[] backVertexCount = new short[inputTriangleCount];

        short totalFront = 0, totalBack = 0;

        for(int i = 0; i < sourceIndices.Length; i += 3)
        {
            int[] indices = { sourceIndices[i], sourceIndices[i+1], sourceIndices[i+2] };

            float[] sides = { pointClassifications[i], pointClassifications[i+1], pointClassifications[i+2] };

            short indexA = 2;

            short front = 0, back = 0;

            for(short indexB = 0; indexB < 3; indexB++)
            {
                float sideA = sides[indexA];
                float sideB = sides[indexB];

                if(sideB > 0f)
                {
                    if(sideA < 0f)
                    {
                        //Find intersection between A, B. Add to BOTH
                        splitActions.Add( new SplitAction(indices[indexA], indices[indexB], i) );
                        front++;
                        back++;
                    }
                    //Add B to FRONT.
                    splitActions.Add( new SplitAction(true, false, indices[indexB]));
                    front++;
                }
                else if (sideB < 0f)
                {
                    if (sideA > 0f)
                    {
                        //Find intersection between A, B. Add to BOTH
                        splitActions.Add( new SplitAction(indices[indexA], indices[indexB], i));
                        front++;
                        back++;
                    }
                    //Add B to BACK.
                    splitActions.Add( new SplitAction(false, true, indices[indexB]));
                    back++;
                }
                else
                {
                    //Add B to BOTH.
                    splitActions.Add( new SplitAction(false, true,  indices[indexB]));
                    front++;
                    back++;
                }

                indexA = indexB;
            }

            int j = i / 3; //This is the triangle counter.

            frontVertexCount[j] = front;
            backVertexCount[j] = back;

            totalFront += front;
            totalBack += back;
        }

        // We're going to iterate through the splits only several times, so let's
        //find the subset once now.
        // Since these are STRUCTs, this is going to COPY the array content. The
        //intersectionInverseRelation table made below helps us put it back into the
        //main array before we use it.
        SplitAction[] intersectionActions;
        int[] intersectionInverseRelation;
        {
            int intersectionCount = 0;

            foreach(SplitAction sa in splitActions)
                if((sa.flags & SplitAction.INTERSECT) == SplitAction.INTERSECT)
                    intersectionCount++;

            intersectionActions = new SplitAction[intersectionCount];
            intersectionInverseRelation = new int[intersectionCount];

            int j = 0;
            for(int i = 0; i < splitActions.Count; i++)
            {
                SplitAction sa = splitActions[i];
                if((sa.flags & SplitAction.INTERSECT) == SplitAction.INTERSECT)
                {
                    intersectionActions[j] = sa;
                    intersectionInverseRelation[j] = i;
                    j++;
                }
            }
        }

        // Next, we're going to find out which splitActions replicate the work of other split actions.
        //A given SA replicates another if and only if it _both_ calls for an intersection _and_ has
        //the same two parent indices (index0 and index1). This is because all intersections are called
        //with the same other parameters, so any case with an index0 and index1 matching will yield the
        //same results.
        // Only caveat is that two given splitActions might have the source indices in reverse order, so
        //we'll arbitrarily decide that "greater first" or something is the correct order. Flipping this
        //order has no consequence until after the intersection is found (at which point flipping the order
        //necessitates converting intersection i to 1-i to flip it as well.)
        // We can assume that every SA has at most 1 correlation. For a given SA, we'll search the list
        //UP TO its own index and, if we find one, we'll take the other's index and put it into the CLONE OF
        //slot.
        // So if we had a set like AFDBAK, than when the _latter_ A comes up for assessment, it'll find
        //the _first_ A (with an index of 0) and set the latter A's cloneOf figure to 0. This way we know
        //any latter As are a clone of the first A.

        for(int i = 0; i < intersectionActions.Length; i++)
        {
            SplitAction a = intersectionActions[i];

            //Ensure that the index0, index1 figures are all in the same order.
            //(We'll do this as we walk the list.)
            if(a.index0 > a.index1)
            {
                int j = a.index0;
                a.index0 = a.index1;
                a.index1 = j;
            }

            Vector3 aVector = sourceGeometry[a.index0] + sourceGeometry[a.index1];

            //Only latters clone formers, so we don't need to search up to and past the self.
            for(int j = 0; j < i; j++)
            {
                SplitAction b = intersectionActions[j];

                bool match = a.index0 == b.index0 && a.index1 == b.index1;

                if(match)
                {
                    a.cloneOf = j;
                }

                //TEMPORARY HACK
                //
                // Infill requires that we match doubled vertices based on their physical
                //position and needs a purely-geometrical analysis of this. However as the
                //kit is currently architected, this data will also be used for the slice
                //geometry.
                // This means that UVs will be mangled as they're not taken into account.
                //This stopgap fix makes it so only matches doubles if infill is actually
                //activated. There may be some distorted where UVs are unwelded, but on
                //typical models this will be minor.

                if(doInfill)
                {
                    if(!match)
                    {
                        Vector3 bVector = sourceGeometry[b.index0] + sourceGeometry[b.index1];

                        match = Mathf.Approximately(aVector.x, bVector.x);
                        match &= Mathf.Approximately(aVector.y, bVector.y);
                        match &= Mathf.Approximately(aVector.z, bVector.z);
                    }

                    if(match)
                    {
                        a.cloneOfForInfillPurposes = j;
                    }
                }
            }

            intersectionActions[i] = a;
        }

        //Next, we want to perform all INTERSECTIONS. Any action which has an intersection needs to have that, like, done.

        for(int i = 0; i < intersectionActions.Length; i++)
        {
            SplitAction sa = intersectionActions[i];

            if(sa.cloneOf == SplitAction.nullIndex)
            {
                Vector3 pointA = sourceGeometry[ sa.index0 ];
                Vector3 pointB = sourceGeometry[ sa.index1 ];
                sa.intersectionResult = intersectCommon(ref pointB, ref pointA, ref plane);
                intersectionActions[i] = sa;
            }
        }

        int newIndexStartsAt = meshCache.vertices.Count;

        // Let's create a table that relates an INTERSECTION index to a GEOMETRY index with an offset of 0 (for example
        //to refer to our newVertices or to the transformedVertices or whatever; internal use.)
        // We can also set up our realIndex figures in the same go.
        int uniqueVertexCount = 0;
        int[] localIndexByIntersection = new int[intersectionActions.Length];
        {
            int currentLocalIndex = 0;
            for(int i = 0; i < intersectionActions.Length; i++)
            {
                SplitAction sa = intersectionActions[i];

                int j;

                if(sa.cloneOf == SplitAction.nullIndex)
                {
                    j = currentLocalIndex++;
                }
                else
                {
                    //This assumes that the widget that we are a clone of already has its localIndexByIntersection assigned.
                    //We assume this because above – where we seek for clones – we only look behind for cloned elements.
                    j = localIndexByIntersection[sa.cloneOf];
                }

                sa.realIndex = newIndexStartsAt + j;

                localIndexByIntersection[i] = j;

                intersectionActions[i] = sa;
            }
            uniqueVertexCount = currentLocalIndex;

            //Now we need to have this data for infiller only. Note that localIndexByIntersection is only used
            //for the infiller, so we are going to change its data for our purposes.
            for(int i = 0; i < intersectionActions.Length; i++)
            {
                SplitAction sa = intersectionActions[i];

                if(sa.cloneOfForInfillPurposes == SplitAction.nullIndex)
                {
                    sa.realIndexForInfillPurposes = sa.realIndex;
                }
                else
                {
                    int j = localIndexByIntersection[sa.cloneOfForInfillPurposes];

                    sa.realIndexForInfillPurposes = newIndexStartsAt + j;
                    localIndexByIntersection[i] = j;
                    intersectionActions[i] = sa;
                }
            }
        }

        //Let's figure out how much geometry we might have.
        //The infill geometry is a pair of clones of this geometry, but with different NORMALS and UVs. (Each set has different normals.)

        int newGeometryEstimate = uniqueVertexCount * (doInfill ? 3 : 1);

        //In this ACTION pass we'll act upon intersections by fetching both referred vertices and LERPing as appropriate.
        //The resultant indices will be written out over the index0 figures.

        Vector3[] newVertices = new Vector3[newGeometryEstimate];
        Vector3[] newNormals = null;
        if(doNormals)
            newNormals = new Vector3[newGeometryEstimate];
        Vector2[] newUVs = new Vector2[newGeometryEstimate];

        //LERP to create vertices
        {
            int currentNewIndex = 0;
            foreach(SplitAction sa in intersectionActions)
            {
                if(sa.cloneOf == SplitAction.nullIndex)
                {
                    Vector3 v = sourceGeometry[sa.index0];
                    Vector3 v2 = sourceGeometry[sa.index1];
                    newVertices[currentNewIndex] = Vector3.Lerp(v2, v, sa.intersectionResult);
                    currentNewIndex++;
                }
            }
        }

        //Normals:
        if(doNormals)
        {
            int currentNewIndex = 0;
            foreach(SplitAction sa in intersectionActions)
            {
                if(sa.cloneOf == SplitAction.nullIndex)
                {
                    Vector3 n = sourceNormals[sa.index0];
                    Vector3 n2 = sourceNormals[sa.index1];
                    newNormals[currentNewIndex] = Vector3.Lerp(n2, n, sa.intersectionResult);
                    currentNewIndex++;
                }
            }
        }

        //UVs:
        {
            int currentNewIndex = 0;
            foreach(SplitAction sa in intersectionActions)
            {
                if(sa.cloneOf == SplitAction.nullIndex)
                {
                    Vector2 uv = sourceUVs[sa.index0];
                    Vector2 uv2 = sourceUVs[sa.index1];
                    newUVs[currentNewIndex] = Vector2.Lerp(uv2, uv, sa.intersectionResult);
                    currentNewIndex++;
                }
            }
        }

        //All the polygon triangulation algorithms depend on having a 2D polygon. We also need the slice plane's
        //geometry in two-space to map the UVs.

        //NOTE that as we only need this data to analyze polygon geometry for triangulation, we can TRANSFORM (scale, translate, rotate)
        //these figures any way we like, as long as they retain the same relative geometry. So we're going to perform ops on this
        //data to create the UVs by scaling it around, and we'll feed the same data to the triangulator.

        //Our's exists in three-space, but is essentially flat... So we can transform it onto a flat coordinate system.
        //The first three figures of our plane four-vector describe the normal to the plane, so if we can create
        //a transformation matrix from that normal to the up normal, we can transform the vertices for observation.
        //We don't need to transform them back; we simply refer to the original vertex coordinates by their index,
        //which (as this is an ordered set) will match the indices of coorisponding transformed vertices.

        //This vector-vector transformation comes from Benjamin Zhu at SGI, pulled from a 1992
        //forum posting here: http://steve.hollasch.net/cgindex/math/rotvecs.html

        /*	"A somewhat "nasty" way to solve this problem:

            Let V1 = [ x1, y1, z1 ], V2 = [ x2, y2, z2 ]. Assume V1 and V2 are already normalized.

                V3 = normalize(cross(V1, V2)). (the normalization here is mandatory.)
                V4 = cross(V3, V1).

                     [ V1 ]
                M1 = [ V4 ]
                     [ V3 ]

                cos = dot(V2, V1), sin = dot(V2, V4)

                     [ cos   sin    0 ]
                M2 = [ -sin  cos    0 ]
                     [ 0     0      1 ]

            The sought transformation matrix is just M1^-1 * M2 * M1. This might well be a standard-text solution."

            -Ben Zhu, SGI, 1992
         */

        Vector2[] transformedVertices = new Vector2[0];
        int infillFrontOffset = 0, infillBackOffset = 0;

        if(doInfill)
        {
            transformedVertices = new Vector2[newGeometryEstimate];

            Matrix4x4 flattenTransform;

            //Based on the algorithm described above, this will create a matrix permitting us
            //to multiply a given vertex yielding a vertex transformed to an XY plane (where Z is
            //undefined.)
            {
                Vector3 v1 = Vector3.forward;
                Vector3 v2 = new Vector3( plane.x, plane.y, plane.z ).normalized;
                Vector3 v3 = Vector3.Cross( v1, v2 ).normalized;
                Vector3 v4 = Vector3.Cross( v3, v1 );

                float cos = Vector3.Dot(v2, v1);
                float sin = Vector3.Dot(v2, v4);

                Matrix4x4 m1 = Matrix4x4.identity;
                m1.SetRow(0, (Vector4) v1);
                m1.SetRow(1, (Vector4) v4);
                m1.SetRow(2, (Vector4) v3);

                Matrix4x4 m1i = m1.inverse;

                Matrix4x4 m2 = Matrix4x4.identity;
                m2.SetRow(0, new Vector4(cos, sin, 0, 0) );
                m2.SetRow(1, new Vector4(-sin, cos, 0, 0) );

                flattenTransform = m1i * m2 * m1;
            }

            for(int i = 0; i < newVertices.Length; i++)
            {
                transformedVertices[i] = (Vector2) flattenTransform.MultiplyPoint3x4( newVertices[i] );
            }

            // We want to normalize the entire transformed vertices. To do this, we find the largest
            //floats in either (by abs). Then we scale. Of course, this normalizes us to figures
            //in the range of [-1f,1f] (not necessarily extending all the way on both sides), and
            //what we need are figures between 0f and 1f (not necessarily filling, but necessarily
            //not spilling.) So we'll shift it here.
            {
                float x = 0f, y = 0f;

                for(int i = 0; i < transformedVertices.Length; i++)
                {
                    Vector2 v = transformedVertices[i];

                    v.x = Mathf.Abs(v.x);
                    v.y = Mathf.Abs(v.y);

                    if(v.x > x) x = v.x;
                    if(v.y > y) y = v.y;
                }

                //We would use 1f/x, 1f/y but we also want to scale everything to half (and perform an offset) as
                //described above.
                x = 0.5f / x;
                y = 0.5f / y;

                Rect r = infill.regionForInfill;

                for(int i = 0; i < transformedVertices.Length; i++)
                {
                    Vector2 v = transformedVertices[i];
                    v.x *= x;
                    v.y *= y;
                    v.x += 0.5f;
                    v.y += 0.5f;
                    v.x *= r.width;
                    v.y *= r.height;
                    v.x += r.x;
                    v.y += r.y;
                    transformedVertices[i] = v;
                }
            }

            //Now let's build the geometry for the two slice in-fills.
            //One is for the front side, and the other for the back side. Each has differing normals.

            infillFrontOffset = uniqueVertexCount;
            infillBackOffset = uniqueVertexCount * 2;

            //The geometry is identical...

            System.Array.Copy(newVertices, 0, newVertices, infillFrontOffset, uniqueVertexCount);
            System.Array.Copy(newVertices, 0, newVertices, infillBackOffset, uniqueVertexCount);

            System.Array.Copy(transformedVertices, 0, newUVs, infillFrontOffset, uniqueVertexCount);
            System.Array.Copy(transformedVertices, 0, newUVs, infillBackOffset, uniqueVertexCount);

            if(doNormals)
            {
                Vector3 infillFrontNormal = ((Vector3) plane) * -1f;
                infillFrontNormal.Normalize();

                for(int i = infillFrontOffset; i < infillBackOffset; i++)
                    newNormals[i] = infillFrontNormal;

                Vector3 infillBackNormal = (Vector3) plane;
                infillBackNormal.Normalize();

                for(int i = infillBackOffset; i < newNormals.Length; i++)
                    newNormals[i] = infillBackNormal;
            }
        }

        //Get the exact indices into two tables. Note that these are indices for TRIANGLES and QUADS, which we'll triangulate in the next section.
        int[] newFrontIndex = new int[totalFront];
        int[] newBackIndex = new int[totalBack];

        //Note that here we refer to split actions again, so let's copy back the updated splitActions.
        for(int i = 0; i < intersectionActions.Length; i++)
        {
            int j = intersectionInverseRelation[i];
            splitActions[j] = intersectionActions[i];
        }

        int newFrontIndexCount = 0, newBackIndexCount = 0;
        foreach(SplitAction sa in splitActions)
        {
            if((sa.flags & SplitAction.TO_FRONT) == SplitAction.TO_FRONT)
            {
                newFrontIndex[newFrontIndexCount] = sa.realIndex;
                newFrontIndexCount++;
            }
            if((sa.flags & SplitAction.TO_BACK) == SplitAction.TO_BACK)
            {
                newBackIndex[newBackIndexCount] = sa.realIndex;
                newBackIndexCount++;
            }
        }

        //Now we need to triangulate sets of quads.
        //We recorded earlier whether we're looking at triangles or quads – in order. So we have a pattern like TTQTTQQTTTQ, and
        //we can expect these vertices to match up perfectly to what the above section of code dumped out.

        int startIndex = 0;

        int[] _indices3 = new int[3];
        int[] _indices4 = new int[6];

        foreach(short s in frontVertexCount)
        {
            if(s == 3)
            {
                _indices3[0] = newFrontIndex[startIndex];
                _indices3[1] = newFrontIndex[startIndex + 1];
                _indices3[2] = newFrontIndex[startIndex + 2];
                frontIndices.AddArray(_indices3);
            }
            else if(s == 4)
            {
                _indices4[0] = newFrontIndex[startIndex];
                _indices4[1] = newFrontIndex[startIndex + 1];
                _indices4[2] = newFrontIndex[startIndex + 3];
                _indices4[3] = newFrontIndex[startIndex + 1];
                _indices4[4] = newFrontIndex[startIndex + 2];
                _indices4[5] = newFrontIndex[startIndex + 3];
                frontIndices.AddArray(_indices4);
            }
            startIndex += s;
        }

        startIndex = 0;

        foreach(short s in backVertexCount)
        {
            if(s == 3)
            {
                _indices3[0] = newBackIndex[startIndex];
                _indices3[1] = newBackIndex[startIndex + 1];
                _indices3[2] = newBackIndex[startIndex + 2];
                backIndices.AddArray(_indices3);
            }
            else if(s == 4)
            {
                _indices4[0] = newBackIndex[startIndex];
                _indices4[1] = newBackIndex[startIndex + 1];
                _indices4[2] = newBackIndex[startIndex + 3];
                _indices4[3] = newBackIndex[startIndex + 1];
                _indices4[4] = newBackIndex[startIndex + 2];
                _indices4[5] = newBackIndex[startIndex + 3];
                backIndices.AddArray(_indices4);
            }
            startIndex += s;
        }

        //Let's add this shiznit in!

        meshCache.vertices.AddArray(newVertices);
        if(doNormals)
            meshCache.normals.AddArray(newNormals);
        meshCache.UVs.AddArray(newUVs);

        //Now we need to fill in the slice hole.

        //We need to find the POLYGON[s] representing the slice hole[s]. There may be more than one.
        //Then we need to TRIANGULATE these polygons and write them out.

        //Above we've built the data necessary to pull this off. We have:

        // - Geometry for the polygon around the edges in Vertex3 / Normal / UV format, already added
        //to the geometry setup.
        // - Geometry for the polygon in Vertex2 format in matching order, aligned to the slice plane.
        // - A collection of all data points and 1:1 hashes representing their physical location.

        //In this mess of data here may be 0 or non-zero CLOSED POLYGONS. We need to walk the list and
        //identify each CLOSED POLYGON (there may be none, or multiples). Then, each of these must be
        //triangulated separately.

        //Vertices connected to each other in a closed polygon can be found to associate with each other
        //in two ways. Envision a triangle strip that forms a circular ribbon – and that we slice through
        //the middle of this ribbon. Slice vertices come in two kinds of pairs; there are pairs that COME FROM
        //the SAME triangle, and pairs that come from ADJACENT TRIANGLES. The whole chain is formed from
        //alternating pair-types.

        //So for example vertex A comes from the same triangle as vertex B, which in turn matches the position
        //of the NEXT triangle's vertex A.

        //The data is prepared for us to be able to identify both kinds of associations. First,
        //association by parent triangle is encoded in the ORDERING. Every PAIR from index 0 shares a parent
        //triangle; so indices 0-1, 2-3, 4-5 and so on are each a pair from a common parent triangle.

        //Meanwhile, vertices generated from the common edge of two different triangles will have the SAME
        //POSITION in three-space.

        //We don't have to compare Vector3s, however; this has already been done. Uniques were eliminated above.
        //What we have is a table; localIndexByIntersection. This list describes ALL SLICE VERTICES in terms
        //of which VERTEX (in the array – identified by index) represents that slice vertex. So if we see that
        //localIndexByIntersection[0] == localIndexByIntersection[4], than we know that slice vertices 0 and 4
        //share the same position in three space.

        //With that in mind, we're going to go through the list in circles building chains out of these
        //connections.

        if(doInfill)
        {
            List<int> currentWorkingPoly = new List<int>();
            List<int> currentTargetPoly = new List<int>();
            List<List<int>> allPolys = new List<List<int>>();
            List<int> claimed = new List<int>();

            int lastAdded = -1;

            //ASSUMPTION: Every element will be claimed into some kind of chain by the end whether correlated or not.
            do
            {
                for(int i = 0; i < localIndexByIntersection.Length; i++)
                {
                    bool go = false, fail = false, startNewChain = false;

                    //If we didn't just add one, we're looking to start a chain. That means we have to find one that
                    //isn't already claimed.
                    if(lastAdded < 0)
                    {
                        go = claimed.Contains(i) == false;
                    }
                    else if(lastAdded == i)
                    {
                        //We've gone through twice without finding a match. This means there isn't one, or something.

                        fail = true;
                    }
                    else
                    {
                        //Otherwise, we're trying to find the next-in-chain.
                        //A valid next-in-chain is connected by geometry which, as discussed, means it's connected
                        //by having matching parent indices (index0, index1).

                        bool match = localIndexByIntersection[i] == localIndexByIntersection[lastAdded];

                        //But there's a special case about the match; it's possible that we've closed the loop!
                        //How do we know we've closed the loop? There are multiple ways but the simplest is that
                        //the chain already contains the element in question.

                        bool loopComplete = match && currentWorkingPoly.Contains(i);

                        if(loopComplete)
                        {
                            allPolys.Add(currentTargetPoly);
                            startNewChain = true;
                        }
                        else
                        {
                            go = match;
                        }
                    }

                    if(go)
                    {
                        int partnerByParent = i % 2 == 1 ? i - 1 : i + 1;

                        int[] pair = { i, partnerByParent };

                        currentWorkingPoly.AddRange(pair);
                        claimed.AddRange(pair);

                        currentTargetPoly.Add(partnerByParent);

                        lastAdded = partnerByParent;

                        //Skip ahead and resume the search _from_ here, so that we don't step into it
                        //again from within this loop walk.
                        i = partnerByParent;
                    }
                    else if(fail)
                    {
                        //We want to start a fresh poly without adding this to the valid polys.
                        startNewChain = true;

                        //Debug.Log("[fail]");
                    }

                    if(startNewChain)
                    {
                        currentWorkingPoly.Clear();
                        currentTargetPoly = new List<int>();
                        lastAdded = -1;
                    }
                }
            }
            while(currentWorkingPoly.Count > 0);

            //Now we go through each poly and triangulate it.

            foreach(List<int> _poly in allPolys)
            {
                Vector2[] poly = new Vector2[_poly.Count];

                for(int i = 0; i < poly.Length; i++)
                {
                    int j = localIndexByIntersection[ _poly[i] ];
                    poly[i] = transformedVertices[j];
                }

                int[] result;

                if(triangulate(poly, out result))
                {
                    int[] front = new int[result.Length];
                    int[] back = new int[result.Length];

                    for(int i = 0; i < result.Length; i++)
                    {
                        int p = _poly[ result[i] ];
                        int local = localIndexByIntersection[ p ];
                        front[i] = local + infillFrontOffset + newIndexStartsAt;
                        back[i] = local + infillBackOffset + newIndexStartsAt;
                    }

                    for(int i = 0; i < result.Length; i += 3)
                    {
                        int j = front[i];
                        front[i] = front[i + 2];
                        front[i + 2] = j;
                    }

                    frontIndices.AddArray(front);
                    backIndices.AddArray(back);
                }
                else
                {
                    Debug.Log("TRIANGULATION FAIL");
                }
                //else
                //{
                    //There is some sort of edge case where the code feeding the triangulator will spit out repeating vertices.
                    //It could be anywhere above – or it could even be that the slicer itself is spitting junk data into its
                    //child objects which confuses subsequent processes. It is worth noting that it mainly seems to occur on very
                    //small objects.
                //}
            }
        }
    }
Esempio n. 22
0
    private GameObject[] _splitByPlane(GameObject go, Vector4 planeInLocalSpace, bool destroyOriginal, bool callHandlers = true)
    {
        Sliceable sliceable = ensureSliceable(go);

        if(!sliceable.currentlySliceable)
        {
            GameObject[] result = { go };

            return result;
        }

        InfillConfiguration[] ourInfills = sliceable.infillers.Length > 0 ? sliceable.infillers : new InfillConfiguration[0];

        MeshCache c = null;
        do
        {
            Mesh m = getMesh(sliceable);

            if(m == null)
            {
                break;
            }

            if(meshCaches.ContainsKey(m))
            {
                c = meshCaches[m];
            }
            else
                c = cacheFromGameObject(sliceable, true);
        }
        while(false);
        if(c == null)
        {
            Debug.LogWarning("Turbo Slicer cannot find mesh filter in object '" + go.name + "' in scene '" + Application.loadedLevelName + "'! Only objects featuring a mesh filter can be sliced.");

            GameObject[] result = { go };

            return result;
        }

        int submeshCount = c.indices.Length;

        //We're going to create two new tentative meshes which contain ALL original vertices in order,
        //plus room for new vertices. Not all of these copied vertices will be addressed, but copying them
        //over eliminates the need to remove doubles and do an On^2 search.

        TurboList<int>[] _frontIndices = new TurboList<int>[ submeshCount ];
        TurboList<int>[] _backIndices = new TurboList<int>[ submeshCount ];

        PlaneTriResult[] sidePlanes = new PlaneTriResult[c.vertices.Count];
        {
            Vector3[] vertices = c.vertices.array;

            for(int i = 0; i < sidePlanes.Length; i++)
            {
                sidePlanes[i] = MuffinSliceCommon.getSidePlane(ref vertices[i], ref planeInLocalSpace);
            }
        }

        for(int j = 0; j < submeshCount; j++)
        {
            int initialCapacityIndices = Mathf.RoundToInt((float) c.indices[j].Length * factorOfSafetyIndices);

            _frontIndices[j] = new TurboList<int>(initialCapacityIndices);
            _backIndices[j] = new TurboList<int>(initialCapacityIndices);

            int[] _indices = c.indices[j];

            TurboList<int> frontIndices = _frontIndices[j];
            TurboList<int> backIndices = _backIndices[j];
            TurboList<int> splitPending = new TurboList<int>(initialCapacityIndices);

            int[] indices = new int[3];

            for(int i = 0; i < _indices.Length; )
            {
                indices[0] = _indices[i++];
                indices[1] = _indices[i++];
                indices[2] = _indices[i++];

                // compute the side of the plane each vertex is on
                PlaneTriResult r1 = sidePlanes[indices[0]];
                PlaneTriResult r2 = sidePlanes[indices[1]];
                PlaneTriResult r3 = sidePlanes[indices[2]];

                if ( r1 == r2 && r1 == r3 ) // if all three vertices are on the same side of the plane.
                {
                    if ( r1 == PlaneTriResult.PTR_FRONT ) // if all three are in front of the plane, then copy to the 'front' output triangle.
                    {
                        frontIndices.AddArray(indices);
                    }
                    else
                    {
                        backIndices.AddArray(indices);
                    }
                }
                else
                {
                    splitPending.AddArray(indices);
                }
            }

            InfillConfiguration ifc = null;

            if(j < c.mats.Length)
            {
                Material mat = c.mats[j];

                foreach(InfillConfiguration _ifc in ourInfills)
                {
                    if(_ifc.material == mat)
                    {
                        ifc = _ifc;
                    }
                }
            }

            if(splitPending.Count > 0)
            {
                splitTriangles(planeInLocalSpace, splitPending.ToArray(), c, ifc, frontIndices, backIndices);
            }
        }

        GameObject[] results;

        bool onlyHaveOne = true;

        for(int i = 0; i < c.indices.Length; i++)
        {
            onlyHaveOne &= _frontIndices[i].Count == 0 || _backIndices[i].Count == 0;
        }

        if(onlyHaveOne)
        {
            //Do nothing
            results = new GameObject[1];
            results[0] = go;
        }
        else
        {
            MeshCache frontCache = new MeshCache();
            frontCache.vertices = c.vertices;
            if(sliceable.channelNormals)
                frontCache.normals = c.normals;
            frontCache.coords = c.coords;
            frontCache.coords2 = c.coords2;
            frontCache.mats = c.mats;

            MeshCache backCache = new MeshCache();
            backCache.vertices = c.vertices;
            if(sliceable.channelNormals)
                backCache.normals = c.normals;
            backCache.coords = c.coords;
            backCache.coords2 = c.coords2;
            backCache.mats = c.mats;

            frontCache.indices = new int[submeshCount][];
            backCache.indices = new int[submeshCount][];
            for(int i = 0; i < submeshCount; i++)
            {
                frontCache.indices[i] = _frontIndices[i].ToArray();
                backCache.indices[i] = _backIndices[i].ToArray();
            }

            Vector3[] geoSubsetOne, geoSubsetTwo;
            Vector3[] normalsSubsetOne = null, normalsSubsetTwo = null;
            Vector2[] uvSubsetOne, uvSubsetTwo;
            Vector2[] uv2SubsetOne = null, uv2SubsetTwo = null;
            int[][] indexSubsetOne, indexSubsetTwo;

            indexSubsetOne = new int[submeshCount][];
            indexSubsetTwo = new int[submeshCount][];

            //Perfect subset will inflate the array list size if needed to the exact figure. So if we estimate 0,
            //and there is 1 submesh, than we will have 1 allocation, and this is optimal. Estimation can only help
            //if we have THREE or more submeshes, which is a silly scenario for anyone concerned about performance.
            int estimateOne = 0, estimateTwo = 0;

            TurboList<Vector3>
                _geoSubsetOne = null, _geoSubsetTwo = null,
                _normalSubsetOne = null, _normalSubsetTwo = null;

            TurboList<Vector2>
                _uvSubsetOne = null, _uvSubsetTwo = null;

            TurboList<Vector2>
                _uv2SubsetOne = null, _uv2SubsetTwo = null;

            _geoSubsetOne = new TurboList<Vector3>(estimateOne);
            _geoSubsetTwo = new TurboList<Vector3>(estimateTwo);

            if(sliceable.channelNormals)
            {
                _normalSubsetOne = new TurboList<Vector3>(estimateOne);
                _normalSubsetTwo = new TurboList<Vector3>(estimateTwo);
            }

            _uvSubsetOne = new TurboList<Vector2>(estimateOne);
            _uvSubsetTwo = new TurboList<Vector2>(estimateTwo);

            if(sliceable.channelUV2)
            {
                _uv2SubsetOne = new TurboList<Vector2>(estimateOne);
                _uv2SubsetTwo = new TurboList<Vector2>(estimateTwo);
            }

            int transferTableMaximumKey = c.vertices.Count;

            int[] transferTableOne = new int[transferTableMaximumKey];
            int[] transferTableTwo = new int[transferTableMaximumKey];

            for(int i = 0; i < transferTableOne.Length; i++) transferTableOne[i] = -1;
            for(int i = 0; i < transferTableTwo.Length; i++) transferTableTwo[i] = -1;

            for(int i = 0; i < submeshCount; i++)
                perfectSubset(_frontIndices[i], c.vertices, c.normals, c.coords, c.coords2, out indexSubsetOne[i], _geoSubsetOne, _normalSubsetOne, _uvSubsetOne, _uv2SubsetOne, ref transferTableOne );

            for(int i = 0; i < submeshCount; i++)
                perfectSubset(_backIndices[i], c.vertices, c.normals, c.coords, c.coords2, out indexSubsetTwo[i], _geoSubsetTwo, _normalSubsetTwo, _uvSubsetTwo, _uv2SubsetTwo, ref transferTableTwo );

            geoSubsetOne = _geoSubsetOne.ToArray();
            geoSubsetTwo = _geoSubsetTwo.ToArray();
            if(sliceable.channelNormals)
            {
                normalsSubsetOne = _normalSubsetOne.ToArray();
                normalsSubsetTwo = _normalSubsetTwo.ToArray();
            }
            uvSubsetOne = _uvSubsetOne.ToArray();
            uvSubsetTwo = _uvSubsetTwo.ToArray();

            if(sliceable.channelUV2)
            {
                uv2SubsetOne = _uv2SubsetOne.ToArray();
                uv2SubsetTwo = _uv2SubsetTwo.ToArray();
            }

            //Note that we do not explicitly call recalculate bounds because (as per the manual) this is implicit in an
            //assignment to vertices whenever the vertex count changes from zero to non-zero.

            Mesh frontMesh = new Mesh();
            Mesh backMesh = new Mesh();

            GameObject frontObject, backObject;

            createResultObjects(go, sliceable, false, planeInLocalSpace, out frontObject, out backObject);

            ensureSliceable(frontObject);
            ensureSliceable(backObject);

            setMesh(frontObject.GetComponent<Sliceable>(), frontMesh);
            setMesh(backObject.GetComponent<Sliceable>(), backMesh);

            frontMesh.vertices = geoSubsetOne;
            backMesh.vertices = geoSubsetTwo;

            if(sliceable.channelTangents)
            {
                Vector4[] tangentsOne, tangentsTwo;

                int[] concatenatedIndicesOne = concatenateIndexArrays(indexSubsetOne);
                int[] concatenatedIndicesTwo = concatenateIndexArrays(indexSubsetTwo);

                RealculateTangents(geoSubsetOne, normalsSubsetOne, uvSubsetOne, concatenatedIndicesOne, out tangentsOne);
                RealculateTangents(geoSubsetTwo, normalsSubsetTwo, uvSubsetTwo, concatenatedIndicesTwo, out tangentsTwo);

                frontMesh.tangents = tangentsOne;
                backMesh.tangents = tangentsTwo;
            }

            if(sliceable.channelNormals)
            {
                frontMesh.normals = normalsSubsetOne;
                backMesh.normals = normalsSubsetTwo;
            }
            frontMesh.uv = uvSubsetOne;
            backMesh.uv = uvSubsetTwo;

            if(sliceable.channelUV2)
            {
                frontMesh.uv2 = uv2SubsetOne;
                backMesh.uv2 = uv2SubsetTwo;
            }

            frontMesh.subMeshCount = submeshCount;
            backMesh.subMeshCount = submeshCount;

            for(int i = 0 ; i < submeshCount; i++)
            {
                frontMesh.SetTriangles(indexSubsetOne[i], i);
                backMesh.SetTriangles(indexSubsetTwo[i], i);
            }

            TSCallbackOnDestroy frontCallback = frontObject.GetComponent<TSCallbackOnDestroy>();
            TSCallbackOnDestroy backCallback = backObject.GetComponent<TSCallbackOnDestroy>();

            if(frontCallback == null)
            {
                frontCallback = frontObject.AddComponent<TSCallbackOnDestroy>();
            }

            if(backCallback == null)
            {
                backCallback = backObject.AddComponent<TSCallbackOnDestroy>();
            }

            frontCallback.callWithMeshOnDestroy = releaseMesh;
            frontCallback.mesh = frontMesh;

            backCallback.callWithMeshOnDestroy = releaseMesh;
            backCallback.mesh = backMesh;

            meshCaches[frontMesh] = frontCache;
            meshCaches[backMesh] = backCache;

            results = new GameObject[2];
            results[0] = frontObject;
            results[1] = backObject;

            if(sliceable != null && sliceable.refreshColliders)
            {
                foreach(GameObject r in results)
                {
                    Collider collider = r.collider;

                    if(collider != null)
                    {
                        bool isTrigger = collider.isTrigger;

                        if(collider is BoxCollider)
                        {
                            GameObject.DestroyImmediate(collider);
                            collider = r.AddComponent<BoxCollider>();
                        }
                        else if(collider is SphereCollider)
                        {
                            GameObject.DestroyImmediate(collider);
                            collider = r.AddComponent<SphereCollider>();
                        }
                        else if(collider is MeshCollider)
                        {
                            MeshCollider mc = (MeshCollider) collider;

                            bool isFront = r == frontObject;

                            Mesh mesh = isFront ? frontMesh : backMesh;

                            mc.sharedMesh = mesh;
                        }

                        collider.isTrigger = isTrigger;
                    }
                }
            }

            if(callHandlers && sliceable != null)
                sliceable.handleSlice(results);

            if(destroyOriginal)
                GameObject.Destroy(go);
        }

        return results;
    }
Esempio n. 23
0
        public MeshCache clone()
        {
            bool cloneNormals = normals != null;

            MeshCache mc = new MeshCache();

            int cap = Mathf.RoundToInt((float) vertices.Count * factorOfSafetyGeometry);

            ////////////
            mc.vertices = new TurboList<Vector3>(cap);
            if(cloneNormals)
            {
                mc.normals = new TurboList<Vector3>(cap);
            }
            mc.UVs = new TurboList<Vector2>(cap);

            ////////////
            mc.vertices.AddArray(vertices.array);
            if(cloneNormals)
            {
                mc.normals.AddArray(normals.array);
            }
            mc.UVs.AddArray(UVs.array);

            ////////////
            mc.indices = new int[indices.Length][];
            for(int i = 0 ; i < indices.Length; i++)
            {
                mc.indices[i] = new int[indices[i].Length];
                System.Array.Copy(indices[i], mc.indices[i], indices[i].Length);
            }

            mc.mats = mats;

            return mc;
        }
Esempio n. 24
0
    public GameObject[] splitByPlane(GameObject go, Vector4 plane, bool destroyOriginal)
    {
        if (go.GetComponentInChildren <SkinnedMeshRenderer>() != null)
        {
            return(splitByPlaneRD(go, plane, destroyOriginal));
        }

        Sliceable sliceable = ensureSliceable(go);

        if (!sliceable.currentlySliceable)
        {
            GameObject[] result = { go };

            return(result);
        }

        InfillConfiguration[] ourInfills = sliceable.infillers.Length > 0 ? sliceable.infillers : infills;

        MeshCache c = null;

        do
        {
            MeshFilter filter = getMeshFilter(sliceable);

            Mesh m = filter.sharedMesh;

            if (m == null)
            {
                break;
            }

            if (meshCaches != null && meshCaches.ContainsKey(m))
            {
                c = meshCaches[m];

                //The mesh cache will be directly modified under the assumption that this will be discarded shortly
                //and thus picked up by the GC. It will grow in size; it will not shrink. Thus we do not want to
                //operate on the original, semi-persistent mesh caches that were preloaded on boot. Instead, we want
                //to make a clone.

                if (c.wasPreloaded)
                {
                    c = c.clone();
                }
            }
            else
            {
                c = cacheFromGameObject(sliceable, true);
            }
        }while(false);
        if (c == null)
        {
            Debug.LogWarning("Turbo Slicer cannot find mesh filter in object '" + go.name + "' in scene '" + Application.loadedLevelName + "'! Only objects featuring a mesh filter can be sliced.");

            GameObject[] result = { go };

            return(result);
        }

        int submeshCount = c.indices.Length;

        //We're going to create two new tentative meshes which contain ALL original vertices in order,
        //plus room for new vertices. Not all of these copied vertices will be addressed, but copying them
        //over eliminates the need to remove doubles and do an On^2 search.

        TurboList <int>[] _frontIndices = new TurboList <int> [submeshCount];
        TurboList <int>[] _backIndices  = new TurboList <int> [submeshCount];

        PlaneTriResult[] sidePlanes = new PlaneTriResult[c.vertices.Count];
        {
            Vector3[] vertices = c.vertices.array;

            for (int i = 0; i < sidePlanes.Length; i++)
            {
                sidePlanes[i] = getSidePlane(ref vertices[i], ref plane);
            }
        }

        for (int j = 0; j < submeshCount; j++)
        {
            int initialCapacityIndices = Mathf.RoundToInt((float)c.indices[j].Length * factorOfSafetyIndices);

            _frontIndices[j] = new TurboList <int>(initialCapacityIndices);
            _backIndices[j]  = new TurboList <int>(initialCapacityIndices);

            int[] _indices = c.indices[j];

            TurboList <int> frontIndices = _frontIndices[j];
            TurboList <int> backIndices  = _backIndices[j];
            TurboList <int> splitPending = new TurboList <int>(initialCapacityIndices);

            int[] indices = new int[3];

            for (int i = 0; i < _indices.Length;)
            {
                indices[0] = _indices[i++];
                indices[1] = _indices[i++];
                indices[2] = _indices[i++];

                // compute the side of the plane each vertex is on
                PlaneTriResult r1 = sidePlanes[indices[0]];
                PlaneTriResult r2 = sidePlanes[indices[1]];
                PlaneTriResult r3 = sidePlanes[indices[2]];

                if (r1 == r2 && r1 == r3)                   // if all three vertices are on the same side of the plane.
                {
                    if (r1 == PlaneTriResult.PTR_FRONT)     // if all three are in front of the plane, then copy to the 'front' output triangle.
                    {
                        frontIndices.AddArray(indices);
                    }
                    else
                    {
                        backIndices.AddArray(indices);
                    }
                }
                else
                {
                    splitPending.AddArray(indices);
                }
            }

            InfillConfiguration ifc = null;

            if (j < c.mats.Length)
            {
                Material mat = c.mats[j];

                foreach (InfillConfiguration _ifc in ourInfills)
                {
                    if (_ifc.material == mat)
                    {
                        ifc = _ifc;
                    }
                }
            }

            splitTriangles(plane, splitPending.ToArray(), c, ifc, frontIndices, backIndices);
        }

        GameObject[] results;

        bool onlyHaveOne = true;

        for (int i = 0; i < c.indices.Length; i++)
        {
            onlyHaveOne &= _frontIndices[i].Count == 0 || _backIndices[i].Count == 0;
        }

        if (onlyHaveOne)
        {
            //Do nothing
            results    = new GameObject[1];
            results[0] = go;
        }
        else
        {
            MeshCache frontCache = new MeshCache();
            frontCache.vertices = c.vertices;
            if (sliceable.channelNormals)
            {
                frontCache.normals = c.normals;
            }
            frontCache.UVs  = c.UVs;
            frontCache.mats = c.mats;

            MeshCache backCache = new MeshCache();
            backCache.vertices = c.vertices;
            if (sliceable.channelNormals)
            {
                backCache.normals = c.normals;
            }
            backCache.UVs  = c.UVs;
            backCache.mats = c.mats;

            frontCache.indices = new int[submeshCount][];
            backCache.indices  = new int[submeshCount][];
            for (int i = 0; i < submeshCount; i++)
            {
                frontCache.indices[i] = _frontIndices[i].ToArray();
                backCache.indices[i]  = _backIndices[i].ToArray();
            }

            Vector3[] geoSubsetOne, geoSubsetTwo;
            Vector3[] normalsSubsetOne = null, normalsSubsetTwo = null;
            Vector2[] uvSubsetOne, uvSubsetTwo;
            int[][]   indexSubsetOne, indexSubsetTwo;

            indexSubsetOne = new int[submeshCount][];
            indexSubsetTwo = new int[submeshCount][];

            //Perfect subset will inflate the array list size if needed to the exact figure. So if we estimate 0,
            //and there is 1 submesh, than we will have 1 allocation, and this is optimal. Estimation can only help
            //if we have THREE or more submeshes, which is a silly scenario for anyone concerned about performance.
            int estimateOne = 0, estimateTwo = 0;

            TurboList <Vector3>
            _geoSubsetOne = null, _geoSubsetTwo = null,
                _normalSubsetOne = null, _normalSubsetTwo = null;

            TurboList <Vector2>
            _uvSubsetOne = null, _uvSubsetTwo = null;

            _geoSubsetOne = new TurboList <Vector3>(estimateOne);
            _geoSubsetTwo = new TurboList <Vector3>(estimateTwo);

            if (sliceable.channelNormals)
            {
                _normalSubsetOne = new TurboList <Vector3>(estimateOne);
                _normalSubsetTwo = new TurboList <Vector3>(estimateTwo);
            }

            _uvSubsetOne = new TurboList <Vector2>(estimateOne);
            _uvSubsetTwo = new TurboList <Vector2>(estimateTwo);

            int transferTableMaximumKey = c.vertices.Count;

            int[] transferTableOne = new int[transferTableMaximumKey];
            int[] transferTableTwo = new int[transferTableMaximumKey];

            for (int i = 0; i < transferTableOne.Length; i++)
            {
                transferTableOne[i] = -1;
            }
            for (int i = 0; i < transferTableTwo.Length; i++)
            {
                transferTableTwo[i] = -1;
            }

            for (int i = 0; i < submeshCount; i++)
            {
                perfectSubset(_frontIndices[i], c.vertices, c.normals, c.UVs, out indexSubsetOne[i], _geoSubsetOne, _normalSubsetOne, _uvSubsetOne, ref transferTableOne);
            }

            for (int i = 0; i < submeshCount; i++)
            {
                perfectSubset(_backIndices[i], c.vertices, c.normals, c.UVs, out indexSubsetTwo[i], _geoSubsetTwo, _normalSubsetTwo, _uvSubsetTwo, ref transferTableTwo);
            }

            geoSubsetOne = _geoSubsetOne.ToArray();
            geoSubsetTwo = _geoSubsetTwo.ToArray();
            if (sliceable.channelNormals)
            {
                normalsSubsetOne = _normalSubsetOne.ToArray();
                normalsSubsetTwo = _normalSubsetTwo.ToArray();
            }
            uvSubsetOne = _uvSubsetOne.ToArray();
            uvSubsetTwo = _uvSubsetTwo.ToArray();

            //Note that we do not explicitly call recalculate bounds because (as per the manual) this is implicit in an
            //assignment to vertices whenever the vertex count changes from zero to non-zero.

            Mesh frontMesh = new Mesh();
            Mesh backMesh  = new Mesh();

            GameObject frontObject, backObject;

            createResultObjects(go, sliceable, false, plane, out frontObject, out backObject);

            getMeshFilter(frontObject.GetComponent <Sliceable>()).mesh = frontMesh;
            getMeshFilter(backObject.GetComponent <Sliceable>()).mesh  = backMesh;

            frontMesh.vertices = geoSubsetOne;
            backMesh.vertices  = geoSubsetTwo;

            if (sliceable.channelNormals)
            {
                frontMesh.normals = normalsSubsetOne;
                backMesh.normals  = normalsSubsetTwo;
            }
            frontMesh.uv = uvSubsetOne;
            backMesh.uv  = uvSubsetTwo;

            frontMesh.subMeshCount = submeshCount;
            backMesh.subMeshCount  = submeshCount;

            for (int i = 0; i < submeshCount; i++)
            {
                frontMesh.SetTriangles(indexSubsetOne[i], i);
                backMesh.SetTriangles(indexSubsetTwo[i], i);
            }

            if (meshCaches != null)
            {
                if (go.GetComponent <DeletionCallback>() == null)
                {
                    frontObject.AddComponent <DeletionCallback>();
                    backObject.AddComponent <DeletionCallback>();
                }

                DeletionCallback frontCallback = frontObject.GetComponent <DeletionCallback>();
                DeletionCallback backCallback  = backObject.GetComponent <DeletionCallback>();

                frontCallback.deletionListener = new DeletionOccurred(this.releaseCacheByMesh);
                backCallback.deletionListener  = new DeletionOccurred(this.releaseCacheByMesh);

                frontCallback.mesh = frontMesh;
                backCallback.mesh  = backMesh;

                meshCaches[frontMesh] = frontCache;
                meshCaches[backMesh]  = backCache;
            }
            else
            {
                DeletionCallback frontCallback = frontObject.GetComponent <DeletionCallback>();
                DeletionCallback backCallback  = backObject.GetComponent <DeletionCallback>();

                if (frontCallback != null)
                {
                    GameObject.DestroyImmediate(frontCallback);
                }

                if (backCallback != null)
                {
                    GameObject.DestroyImmediate(backCallback);
                }
            }

            if (destroyOriginal)
            {
                GameObject.Destroy(go);
            }

            results    = new GameObject[2];
            results[0] = frontObject;
            results[1] = backObject;

            if (sliceable != null && sliceable.refreshColliders)
            {
                foreach (GameObject r in results)
                {
                    Collider collider = r.collider;

                    if (collider != null)
                    {
                        if (collider is BoxCollider)
                        {
                            GameObject.DestroyImmediate(collider);
                            r.AddComponent <BoxCollider>();
                        }
                        else if (collider is SphereCollider)
                        {
                            GameObject.DestroyImmediate(collider);
                            r.AddComponent <SphereCollider>();
                        }
                        else if (collider is MeshCollider)
                        {
                            MeshCollider mc = (MeshCollider)collider;

                            bool isFront = r == frontObject;

                            Mesh mesh = isFront ? frontMesh : backMesh;

                            mc.sharedMesh = mesh;
                        }
                    }
                }
            }

            if (sliceable != null)
            {
                sliceable.handleSlice(results);
            }
        }

        return(results);
    }
Esempio n. 25
0
 public static void DestroyInstance()
 {
     sm_Instance = null;
 }
Esempio n. 26
0
 public static void CreateInstance()
 {
     Dbg.Assert(sm_Instance == null, "MeshCache.Init: instance is already created");
     sm_Instance = new MeshCache();
 }
Esempio n. 27
0
    protected override void Start()
    {
        base.Start();

        UpdateMaterials();

        if (Scene != null && Scene.Root != null)
        {
            Scene.Root.transform.parent = this.transform;
        }

        CategoriesHandler categories = new CategoriesHandler();
        MeshCache         meshCache  = new MeshCache();

        Handlers.Register(meshCache);
        Handlers.Register(categories);

        Handlers.Register(new CameraHandler());

        Handlers.Register(new ArrowHandler());
        Handlers.Register(new BoxHandler());
        Handlers.Register(new CapsuleHandler());
        Handlers.Register(new ConeHandler());
        Handlers.Register(new CylinderHandler());
        Handlers.Register(new PlaneHandler());
        Handlers.Register(new SphereHandler());
        Handlers.Register(new StarHandler());
        Handlers.Register(new MeshHandler());
        Handlers.Register(new MeshSetHandler(meshCache));
        Handlers.Register(new PointCloudHandler(meshCache));
        Handlers.Register(new PoseHandler());
        Handlers.Register(new Text2DHandler());
        Text3DHandler text3DHandler = new Text3DHandler();

        text3DHandler.CreateTextMeshHandler = this.GenerateTextMesh;
        Handlers.Register(text3DHandler);

        // Register handlers from plugins.
        string[] loadPaths = new string[]
        {
#if UNITY_EDITOR
            Path.Combine("Assets", "plugins")
#else  // UNITY_EDITOR
            "plugins",
            Path.Combine("3rd-Eye-Scene_Data", "Managed")
#endif // UNITY_EDITOR
        };

        foreach (string loadPath in loadPaths)
        {
            string[] excludeList = new string[] {
                "3esCore.dll",
                "3esRuntime.dll",
                "host*.dll",
                "System.*.dll"
            };
            Handlers.LoadPlugins(loadPath, Plugins, excludeList, new object[] {});
        }

        InitialiseHandlers();

        HandleCommandLineStart();
    }
Esempio n. 28
0
 /// <summary>
 /// Create the shape handler.
 /// </summary>
 /// <param name="meshCache">The mesh cache from which to read resources.</param>
 public MeshSetHandler(MeshCache meshCache)
 {
     MeshCache = meshCache;
     _shapeCache.AddShapeDataType <PartSet>();
     _transientCache.AddShapeDataType <PartSet>();
 }
Esempio n. 29
0
    static void splitTrianglesLH(Vector4 plane, Vector3[] snapshot, PlaneTriResult[] sidePlanes, int[] sourceIndices, MeshCache meshCache,
                                 TurboList <int> frontIndices, TurboList <int> backIndices,
                                 Infill?infillMode, TurboList <int> frontInfill, TurboList <int> backInfill)
    {
        bool doInfill = infillMode.HasValue && frontInfill != null && backInfill != null;

        Vector3[]    sourceGeometry = meshCache.vertices.array;
        Vector3[]    sourceNormals  = meshCache.normals.array;
        Vector2[]    sourceUVs      = meshCache.UVs.array;
        BoneWeight[] sourceWeights  = meshCache.weights.array;

        float[] pointClassifications = new float[sourceIndices.Length];
        for (int i = 0; i < pointClassifications.Length; i++)
        {
            pointClassifications[i] = MuffinSliceCommon.classifyPoint(ref plane, ref snapshot[sourceIndices[i]]);
        }

        //Now we're going to do the decision making pass. This is where we assess the side figures and produce actions...

        int inputTriangleCount = sourceIndices.Length / 3;

        //A good action count estimate can avoid reallocations.
        //We expect exactly five actions per triangle.
        int actionEstimate = inputTriangleCount * 5;
        List <SplitAction> splitActions = new List <SplitAction>(actionEstimate);

        //We want to count how many vertices are yielded from each triangle split. This will be used later to add the indices.
        short[] frontVertexCount = new short[inputTriangleCount];
        short[] backVertexCount  = new short[inputTriangleCount];

        short totalFront = 0, totalBack = 0;

        for (int i = 0; i < sourceIndices.Length; i += 3)
        {
            int[] indices = { sourceIndices[i], sourceIndices[i + 1], sourceIndices[i + 2] };

            float[] sides = { pointClassifications[i], pointClassifications[i + 1], pointClassifications[i + 2] };

            short indexA = 2;

            short front = 0, back = 0;

            for (short indexB = 0; indexB < 3; indexB++)
            {
                float sideA = sides[indexA];
                float sideB = sides[indexB];

                if (sideB > 0f)
                {
                    if (sideA < 0f)
                    {
                        //Find intersection between A, B. Add to BOTH
                        splitActions.Add(new SplitAction(indices[indexA], indices[indexB], i));
                        front++;
                        back++;
                    }
                    //Add B to FRONT.
                    splitActions.Add(new SplitAction(true, false, indices[indexB]));
                    front++;
                }
                else if (sideB < 0f)
                {
                    if (sideA > 0f)
                    {
                        //Find intersection between A, B. Add to BOTH
                        splitActions.Add(new SplitAction(indices[indexA], indices[indexB], i));
                        front++;
                        back++;
                    }
                    //Add B to BACK.
                    splitActions.Add(new SplitAction(false, true, indices[indexB]));
                    back++;
                }
                else
                {
                    //Add B to BOTH.
                    splitActions.Add(new SplitAction(false, true, indices[indexB]));
                    front++;
                    back++;
                }

                indexA = indexB;
            }

            int j = i / 3;             //This is the triangle counter.

            frontVertexCount[j] = front;
            backVertexCount[j]  = back;

            totalFront += front;
            totalBack  += back;
        }

        // We're going to iterate through the splits only several times, so let's
        //find the subset once now.
        // Since these are STRUCTs, this is going to COPY the array content. The
        //intersectionInverseRelation table made below helps us put it back into the
        //main array before we use it.
        SplitAction[] intersectionActions;
        int[]         intersectionInverseRelation;
        {
            int intersectionCount = 0;

            foreach (SplitAction sa in splitActions)
            {
                if ((sa.flags & SplitAction.INTERSECT) == SplitAction.INTERSECT)
                {
                    intersectionCount++;
                }
            }

            intersectionActions         = new SplitAction[intersectionCount];
            intersectionInverseRelation = new int[intersectionCount];

            int j = 0;
            for (int i = 0; i < splitActions.Count; i++)
            {
                SplitAction sa = splitActions[i];
                if ((sa.flags & SplitAction.INTERSECT) == SplitAction.INTERSECT)
                {
                    intersectionActions[j]         = sa;
                    intersectionInverseRelation[j] = i;
                    j++;
                }
            }
        }

        // Next, we're going to find out which splitActions replicate the work of other split actions.
        //A given SA replicates another if and only if it _both_ calls for an intersection _and_ has
        //the same two parent indices (index0 and index1). This is because all intersections are called
        //with the same other parameters, so any case with an index0 and index1 matching will yield the
        //same results.
        // Only caveat is that two given splitActions might as the source indices in reverse order, so
        //we'll arbitrarily decide that "greater first" or something is the correct order. Flipping this
        //order has no consequence until after the intersection is found (at which point flipping the order
        //necessitates converting intersection i to 1-i to flip it as well.)
        // We can assume that every SA has at most 1 correlation. For a given SA, we'll search the list
        //UP TO its own index and, if we find one, we'll take the other's index and put it into the CLONE OF
        //slot.
        // So if we had a set like AFDBAK, than when the _latter_ A comes up for assessment, it'll find
        //the _first_ A (with an index of 0) and set the latter A's cloneOf figure to 0. This way we know
        //any latter As are a clone of the first A.

        for (int i = 0; i < intersectionActions.Length; i++)
        {
            SplitAction a = intersectionActions[i];

            //Ensure that the index0, index1 figures are all in the same order.
            //(We'll do this as we walk the list.)
            if (a.index0 > a.index1)
            {
                int j = a.index0;
                a.index0 = a.index1;
                a.index1 = j;
            }

            Vector3 aVector = sourceGeometry[a.index0] + sourceGeometry[a.index1];

            //Only latters clone formers, so we don't need to search up to and past the self.
            for (int j = 0; j < i; j++)
            {
                SplitAction b = intersectionActions[j];

                bool match = a.index0 == b.index0 && a.index1 == b.index1;

                if (!match)
                {
                    Vector3 bVector = sourceGeometry[b.index0] + sourceGeometry[b.index1];

//					match = Mathf.Approximately(aVector.x, bVector.x);
//					match &= Mathf.Approximately(aVector.y, bVector.y);
//					match &= Mathf.Approximately(aVector.z, bVector.z);

                    // What are the chances, really?

                    match = Mathf.Approximately(aVector.x + aVector.y + aVector.z, bVector.x + bVector.y + bVector.z);
                }

                if (match)
                {
                    a.cloneOf = j;
                }
            }

            intersectionActions[i] = a;
        }

        //Next, we want to perform all INTERSECTIONS. Any action which has an intersection needs to have that, like, done.

        for (int i = 0; i < intersectionActions.Length; i++)
        {
            SplitAction sa = intersectionActions[i];

            if (sa.cloneOf == SplitAction.nullIndex)
            {
                /*float ir = vertexSums[ sa.index0 ] + vertexSums[ sa.index1 ];
                 *
                 * ir += 1f;
                 * ir *= 0.5f;
                 * ir = 1f - ir;
                 *
                 * sa.intersectionResult = ir;*/

                Vector3 pointA = snapshot[sa.index0];
                Vector3 pointB = snapshot[sa.index1];
                sa.intersectionResult  = MuffinSliceCommon.intersectCommon(ref pointB, ref pointA, ref plane);
                intersectionActions[i] = sa;
            }
        }

        // Let's create a table that relates an INTERSECTION index to a GEOMETRY index with an offset of 0 (for example
        //to refer to our newVertices or to the transformedVertices or whatever; internal use.)
        // We can also set up our realIndex figures in the same go.
        int newIndexStartsAt  = meshCache.vertices.Count;
        int uniqueVertexCount = 0;

        int[] localIndexByIntersection = new int[intersectionActions.Length];
        {
            int currentLocalIndex = 0;
            for (int i = 0; i < intersectionActions.Length; i++)
            {
                SplitAction sa = intersectionActions[i];

                int j;

                if (sa.cloneOf == SplitAction.nullIndex)
                {
                    j = currentLocalIndex++;
                }
                else
                {
                    //This assumes that the widget that we are a clone of already has its localIndexByIntersection assigned.
                    //We assume this because above – where we seek for clones – we only look behind for cloned elements.
                    j = localIndexByIntersection[sa.cloneOf];
                }

                sa.realIndex = newIndexStartsAt + j;

                localIndexByIntersection[i] = j;

                intersectionActions[i] = sa;
            }
            uniqueVertexCount = currentLocalIndex;
        }

        //Let's figure out how much geometry we might have.
        //The infill geometry is a pair of clones of this geometry, but with different NORMALS and UVs. (Each set has different normals.)

        int newGeometryEstimate = uniqueVertexCount * (doInfill ? 3 : 1);

        //In this ACTION pass we'll act upon intersections by fetching both referred vertices and LERPing as appropriate.
        //The resultant indices will be written out over the index0 figures.

        Vector3[]    newVertices = new Vector3[newGeometryEstimate];
        Vector3[]    newNormals  = new Vector3[newGeometryEstimate];
        Vector2[]    newUVs      = new Vector2[newGeometryEstimate];
        BoneWeight[] newWeights  = new BoneWeight[newGeometryEstimate];

        //LERP to create vertices
        {
            int currentNewIndex = 0;
            foreach (SplitAction sa in intersectionActions)
            {
                if (sa.cloneOf == SplitAction.nullIndex)
                {
                    Vector3 v  = sourceGeometry[sa.index0];
                    Vector3 v2 = sourceGeometry[sa.index1];
                    newVertices[currentNewIndex] = Vector3.Lerp(v2, v, sa.intersectionResult);
                    currentNewIndex++;
                }
            }
        }

        //Normals:
        {
            int currentNewIndex = 0;
            foreach (SplitAction sa in intersectionActions)
            {
                if (sa.cloneOf == SplitAction.nullIndex)
                {
                    Vector3 n  = sourceNormals[sa.index0];
                    Vector3 n2 = sourceNormals[sa.index1];
                    newNormals[currentNewIndex] = Vector3.Lerp(n2, n, sa.intersectionResult);
                    currentNewIndex++;
                }
            }
        }

        //UVs:
        {
            int currentNewIndex = 0;
            foreach (SplitAction sa in intersectionActions)
            {
                if (sa.cloneOf == SplitAction.nullIndex)
                {
                    Vector2 uv  = sourceUVs[sa.index0];
                    Vector2 uv2 = sourceUVs[sa.index1];
                    newUVs[currentNewIndex] = Vector2.Lerp(uv2, uv, sa.intersectionResult);
                    currentNewIndex++;
                }
            }
        }

        //Bone Weights:
        {
            int currentNewIndex = 0;
            foreach (SplitAction sa in intersectionActions)
            {
                if (sa.cloneOf == SplitAction.nullIndex)
                {
                    BoneWeight bw;

                    if (sidePlanes[sa.index0] == PlaneTriResult.PTR_FRONT)
                    {
                        bw = sourceWeights[sa.index0];
                    }
                    else
                    {
                        bw = sourceWeights[sa.index1];
                    }

                    newWeights[currentNewIndex] = bw;
                    currentNewIndex++;
                }
            }
        }

        //All the polygon triangulation algorithms depend on having a 2D polygon. We also need the slice plane's
        //geometry in two-space to map the UVs.

        //NOTE that as we only need this data to analyze polygon geometry for triangulation, we can TRANSFORM (scale, translate, rotate)
        //these figures any way we like, as long as they retain the same relative geometry. So we're going to perform ops on this
        //data to create the UVs by scaling it around, and we'll feed the same data to the triangulator.

        //Our's exists in three-space, but is essentially flat... So we can transform it onto a flat coordinate system.
        //The first three figures of our plane four-vector describe the normal to the plane, so if we can create
        //a transformation matrix from that normal to the up normal, we can transform the vertices for observation.
        //We don't need to transform them back; we simply refer to the original vertex coordinates by their index,
        //which (as this is an ordered set) will match the indices of coorisponding transformed vertices.

        //This vector-vector transformation comes from Benjamin Zhu at SGI, pulled from a 1992
        //forum posting here: http://steve.hollasch.net/cgindex/math/rotvecs.html

        /*	"A somewhat "nasty" way to solve this problem:
         *
         *      Let V1 = [ x1, y1, z1 ], V2 = [ x2, y2, z2 ]. Assume V1 and V2 are already normalized.
         *
         *          V3 = normalize(cross(V1, V2)). (the normalization here is mandatory.)
         *          V4 = cross(V3, V1).
         *
         *               [ V1 ]
         *          M1 = [ V4 ]
         *               [ V3 ]
         *
         *          cos = dot(V2, V1), sin = dot(V2, V4)
         *
         *               [ cos   sin    0 ]
         *          M2 = [ -sin  cos    0 ]
         *               [ 0     0      1 ]
         *
         *      The sought transformation matrix is just M1^-1 * M2 * M1. This might well be a standard-text solution."
         *
         *      -Ben Zhu, SGI, 1992
         */

        Vector2[] transformedVertices = new Vector2[0];
        int       infillFrontOffset = 0, infillBackOffset = 0;

        if (doInfill)
        {
            transformedVertices = new Vector2[newGeometryEstimate / 3];

            Matrix4x4 flattenTransform;

            //Based on the algorithm described above, this will create a matrix permitting us
            //to multiply a given vertex yielding a vertex transformed to an XY plane (where Z is
            //undefined.)
            {
                Vector3 v1 = Vector3.forward;
                Vector3 v2 = new Vector3(plane.x, plane.y, plane.z).normalized;

                float difference = (v1 - v2).magnitude;

                if (difference > 0.01f)
                {
                    Vector3 v3 = Vector3.Cross(v1, v2).normalized;
                    Vector3 v4 = Vector3.Cross(v3, v1);

                    float cos = Vector3.Dot(v2, v1);
                    float sin = Vector3.Dot(v2, v4);

                    Matrix4x4 m1 = Matrix4x4.identity;
                    m1.SetRow(0, (Vector4)v1);
                    m1.SetRow(1, (Vector4)v4);
                    m1.SetRow(2, (Vector4)v3);

                    Matrix4x4 m1i = m1.inverse;

                    Matrix4x4 m2 = Matrix4x4.identity;
                    m2.SetRow(0, new Vector4(cos, sin, 0, 0));
                    m2.SetRow(1, new Vector4(-sin, cos, 0, 0));

                    flattenTransform = m1i * m2 * m1;
                }
                else
                {
                    flattenTransform = Matrix4x4.identity;
                }
            }
            for (int i = 0; i < transformedVertices.Length; i++)
            {
                transformedVertices[i] = (Vector2)flattenTransform.MultiplyPoint3x4(newVertices[i]);
//				Debug.Log(newVertices[i] + " > " + transformedVertices[i]);
            }

            // We want to normalize the entire transformed vertices. To do this, we find the largest
            //floats in either (by abs). Then we scale. Of course, this normalizes us to figures
            //in the range of [-1f,1f] (not necessarily extending all the way on both sides), and
            //what we need are figures between 0f and 1f (not necessarily filling, but necessarily
            //not spilling.) So we'll shift it here.
            {
                float x = 0f, y = 0f;

                for (int i = 0; i < transformedVertices.Length; i++)
                {
                    Vector2 v = transformedVertices[i];

                    v.x = Mathf.Abs(v.x);
                    v.y = Mathf.Abs(v.y);

                    if (v.x > x)
                    {
                        x = v.x;
                    }
                    if (v.y > y)
                    {
                        y = v.y;
                    }
                }

                //We would use 1f/x, 1f/y but we also want to scale everything to half (and perform an offset) as
                //described above.
                x = 0.5f / x;
                y = 0.5f / y;

                Rect r = new Rect(0, 0, 1f, 1f);

                for (int i = 0; i < transformedVertices.Length; i++)
                {
                    Vector2 v = transformedVertices[i];
                    v.x *= x;
                    v.y *= y;
                    v.x += 0.5f;
                    v.y += 0.5f;
                    v.x *= r.width;
                    v.y *= r.height;
                    v.x += r.x;
                    v.y += r.y;
                    transformedVertices[i] = v;
                }
            }

            //Now let's build the geometry for the two slice in-fills.
            //One is for the front side, and the other for the back side. Each has differing normals.

            infillFrontOffset = uniqueVertexCount;
            infillBackOffset  = uniqueVertexCount * 2;

            //The geometry is identical...

            System.Array.Copy(newVertices, 0, newVertices, infillFrontOffset, uniqueVertexCount);
            System.Array.Copy(newVertices, 0, newVertices, infillBackOffset, uniqueVertexCount);

            System.Array.Copy(newWeights, 0, newWeights, infillFrontOffset, uniqueVertexCount);
            System.Array.Copy(newWeights, 0, newWeights, infillBackOffset, uniqueVertexCount);

            System.Array.Copy(transformedVertices, 0, newUVs, infillFrontOffset, uniqueVertexCount);
            System.Array.Copy(transformedVertices, 0, newUVs, infillBackOffset, uniqueVertexCount);

            Vector3 infillFrontNormal = ((Vector3)plane) * -1f;
            infillFrontNormal.Normalize();

            for (int i = infillFrontOffset; i < infillBackOffset; i++)
            {
                newNormals[i] = infillFrontNormal;
            }

            Vector3 infillBackNormal = (Vector3)plane;
            infillBackNormal.Normalize();

            for (int i = infillBackOffset; i < newNormals.Length; i++)
            {
                newNormals[i] = infillBackNormal;
            }
        }

        //Get the exact indices into two tables. Note that these are indices for TRIANGLES and QUADS, which we'll triangulate in the next section.
        int[] newFrontIndex = new int[totalFront];
        int[] newBackIndex  = new int[totalBack];

        //Note that here we refer to split actions again, so let's copy back the updated splitActions.
        for (int i = 0; i < intersectionActions.Length; i++)
        {
            int j = intersectionInverseRelation[i];
            splitActions[j] = intersectionActions[i];
        }

        int newFrontIndexCount = 0, newBackIndexCount = 0;

        foreach (SplitAction sa in splitActions)
        {
            if ((sa.flags & SplitAction.TO_FRONT) == SplitAction.TO_FRONT)
            {
                newFrontIndex[newFrontIndexCount] = sa.realIndex;
                newFrontIndexCount++;
            }
            if ((sa.flags & SplitAction.TO_BACK) == SplitAction.TO_BACK)
            {
                newBackIndex[newBackIndexCount] = sa.realIndex;
                newBackIndexCount++;
            }
        }

        //Now we need to triangulate sets of quads.
        //We recorded earlier whether we're looking at triangles or quads – in order. So we have a pattern like TTQTTQQTTTQ, and
        //we can expect these vertices to match up perfectly to what the above section of code dumped out.

        int startIndex = 0;

        int[] _indices3 = new int[3];
        int[] _indices4 = new int[6];

        foreach (short s in frontVertexCount)
        {
            if (s == 3)
            {
                _indices3[0] = newFrontIndex[startIndex];
                _indices3[1] = newFrontIndex[startIndex + 1];
                _indices3[2] = newFrontIndex[startIndex + 2];
                frontIndices.AddArray(_indices3);
            }
            else if (s == 4)
            {
                _indices4[0] = newFrontIndex[startIndex];
                _indices4[1] = newFrontIndex[startIndex + 1];
                _indices4[2] = newFrontIndex[startIndex + 3];
                _indices4[3] = newFrontIndex[startIndex + 1];
                _indices4[4] = newFrontIndex[startIndex + 2];
                _indices4[5] = newFrontIndex[startIndex + 3];
                frontIndices.AddArray(_indices4);
            }
            startIndex += s;
        }

        startIndex = 0;

        foreach (short s in backVertexCount)
        {
            if (s == 3)
            {
                _indices3[0] = newBackIndex[startIndex];
                _indices3[1] = newBackIndex[startIndex + 1];
                _indices3[2] = newBackIndex[startIndex + 2];
                backIndices.AddArray(_indices3);
            }
            else if (s == 4)
            {
                _indices4[0] = newBackIndex[startIndex];
                _indices4[1] = newBackIndex[startIndex + 1];
                _indices4[2] = newBackIndex[startIndex + 3];
                _indices4[3] = newBackIndex[startIndex + 1];
                _indices4[4] = newBackIndex[startIndex + 2];
                _indices4[5] = newBackIndex[startIndex + 3];
                backIndices.AddArray(_indices4);
            }
            startIndex += s;
        }

        //Let's add this shiznit in!

        meshCache.vertices.AddArray(newVertices);
        meshCache.normals.AddArray(newNormals);
        meshCache.UVs.AddArray(newUVs);
        meshCache.weights.AddArray(newWeights);

        //Now we need to fill in the slice hole. There are TWO infillers; the Sloppy and Meticulous.

        //The sloppy infiller will find a point in the middle of all slice vertices and produce a triangle fan.
        //It can work fast, but will have issues with non-roundish cross sections or cross sections with multiple holes.

        //The meticulous infill can distinguish between polygons and accurately fill multiple holes, but is more sensitive to
        //geometrical oddities. It may fail when slicing certain joints because of the way that not all geometry is sliced.
        //It is transferred from Turbo Slicer, where it is a key part of the product, but it is not most appropriate here.
        //Nevertheless, it is here in case it is needed.

        if (doInfill && infillMode == Infill.Sloppy)
        {
            VectorAccumulator centerVertex = new VectorAccumulator();
            VectorAccumulator centerUV     = new VectorAccumulator();
            VectorAccumulator centerNormal = new VectorAccumulator();

            Dictionary <int, float> weightsByBone = new Dictionary <int, float>();

            int sliceVertexCount = newGeometryEstimate / 3;

            for (int i = 0; i < sliceVertexCount; i++)
            {
                centerVertex.addFigure(newVertices[i]);
                centerUV.addFigure(newUVs[i]);
                centerNormal.addFigure(newNormals[i]);

                BoneWeight bw = newWeights[i];

                if (weightsByBone.ContainsKey(bw.boneIndex0))
                {
                    weightsByBone[bw.boneIndex0] += bw.weight0;
                }
                else
                {
                    weightsByBone[bw.boneIndex0] = bw.weight0;
                }

                /*if(weightsByBone.ContainsKey(bw.boneIndex1))
                 *      weightsByBone[bw.boneIndex1] += bw.weight1;
                 * else
                 *      weightsByBone[bw.boneIndex1] = bw.weight1;
                 *
                 * if(weightsByBone.ContainsKey(bw.boneIndex2))
                 *      weightsByBone[bw.boneIndex2] += bw.weight2;
                 * else
                 *      weightsByBone[bw.boneIndex2] = bw.weight2;
                 *
                 * if(weightsByBone.ContainsKey(bw.boneIndex3))
                 *      weightsByBone[bw.boneIndex3] += bw.weight3;
                 * else
                 *      weightsByBone[bw.boneIndex3] = bw.weight3;*/
            }

            List <KeyValuePair <int, float> > orderedWeights = new List <KeyValuePair <int, float> >(weightsByBone);

            orderedWeights.Sort((firstPair, nextPair) =>
            {
                return(-firstPair.Value.CompareTo(nextPair.Value));
            }
                                );

            BoneWeight centerWeight     = new BoneWeight();
            Vector4    weightNormalizer = Vector4.zero;

            if (orderedWeights.Count > 0)
            {
                centerWeight.boneIndex0 = orderedWeights[0].Key;
                weightNormalizer.x      = 1f;
            }

            weightNormalizer.Normalize();

            centerWeight.weight0 = weightNormalizer.x;
            centerWeight.weight1 = weightNormalizer.y;
            centerWeight.weight2 = weightNormalizer.z;
            centerWeight.weight3 = weightNormalizer.w;

            int centerIndex = meshCache.vertices.Count;

            meshCache.vertices.Count++;
            meshCache.normals.Count++;
            meshCache.UVs.Count++;
            meshCache.weights.Count++;

            meshCache.vertices.array[centerIndex] = centerVertex.mean;
            meshCache.UVs.array[centerIndex]      = centerUV.mean;
            meshCache.normals.array[centerIndex]  = centerNormal.mean;
            meshCache.weights.array[centerIndex]  = centerWeight;

            Vector2 transformedCenter = Vector2.zero;
            foreach (Vector2 v in transformedVertices)
            {
                transformedCenter += v;
            }
            transformedCenter /= transformedVertices.Length;

            Dictionary <int, float> angleByIndex = new Dictionary <int, float>();
            for (int i = 0; i < transformedVertices.Length; i++)
            {
                Vector2 delta = transformedVertices[i] - transformedCenter;
                angleByIndex[i] = Mathf.Atan2(delta.y, delta.x);
            }

            List <KeyValuePair <int, float> > orderedVertices = new List <KeyValuePair <int, float> >(angleByIndex);

            orderedVertices.Sort((firstPair, nextPair) =>
            {
                return(firstPair.Value.CompareTo(nextPair.Value));
            }
                                 );

            for (int i = 0; i < orderedVertices.Count; i++)
            {
                bool atEnd = i == orderedVertices.Count - 1;
                int  iNext = atEnd ? 0 : i + 1;

                int index0 = orderedVertices[i].Key;
                int index1 = orderedVertices[iNext].Key;

                int[] frontInfillIndices = { centerIndex, index1 + infillFrontOffset + newIndexStartsAt, index0 + infillFrontOffset + newIndexStartsAt };
                frontInfill.AddArray(frontInfillIndices);

                int[] backInfillIndices = { centerIndex, index0 + infillBackOffset + newIndexStartsAt, index1 + infillBackOffset + newIndexStartsAt };
                backInfill.AddArray(backInfillIndices);
            }
        }
        else if (doInfill && infillMode == Infill.Meticulous)
        {
            //If that fails, one can use the more accurate but more delicate "meticulous" infiller.

            //We need to find the POLYGON[s] representing the slice hole[s]. There may be more than one.
            //Then we need to TRIANGULATE these polygons and write them out.

            //Above we've built the data necessary to pull this off. We have:

            // - Geometry for the polygon around the edges in Vertex3 / Normal / UV format, already added
            //to the geometry setup.
            // - Geometry for the polygon in Vertex2 format in matching order, aligned to the slice plane.
            // - A collection of all data points and 1:1 hashes representing their physical location.

            //In this mess of data here may be 0 or non-zero CLOSED POLYGONS. We need to walk the list and
            //identify each CLOSED POLYGON (there may be none, or multiples). Then, each of these must be
            //triangulated separately.

            //Vertices connected to each other in a closed polygon can be found to associate with each other
            //in two ways. Envision a triangle strip that forms a circular ribbon – and that we slice through
            //the middle of this ribbon. Slice vertices come in two kinds of pairs; there are pairs that COME FROM
            //the SAME triangle, and pairs that come from ADJACENT TRIANGLES. The whole chain is formed from
            //alternating pair-types.

            //So for example vertex A comes from the same triangle as vertex B, which in turn matches the position
            //of the NEXT triangle's vertex A.

            //The data is prepared for us to be able to identify both kinds of associations. First,
            //association by parent triangle is encoded in the ORDERING. Every PAIR from index 0 shares a parent
            //triangle; so indices 0-1, 2-3, 4-5 and so on are each a pair from a common parent triangle.

            //Meanwhile, vertices generated from the common edge of two different triangles will have the SAME
            //POSITION in three-space.

            //We don't have to compare Vector3s, however; this has already been done. Uniques were eliminated above.
            //What we have is a table; localIndexByIntersection. This list describes ALL SLICE VERTICES in terms
            //of which VERTEX (in the array – identified by index) represents that slice vertex. So if we see that
            //localIndexByIntersection[0] == localIndexByIntersection[4], than we know that slice vertices 0 and 4
            //share the same position in three space.

            //With that in mind, we're going to go through the list in circles building chains out of these
            //connections.

            List <int>         currentWorkingPoly = new List <int>();
            List <int>         currentTargetPoly  = new List <int>();
            List <List <int> > allPolys           = new List <List <int> >();
            List <int>         claimed            = new List <int>();

            int lastAdded = -1;

            //ASSUMPTION: Every element will be claimed into some kind of chain by the end whether correlated or not.
            do
            {
                for (int i = 0; i < localIndexByIntersection.Length; i++)
                {
                    bool go = false, fail = false, startNewChain = false;

                    //If we didn't just add one, we're looking to start a chain. That means we have to find one that
                    //isn't already claimed.
                    if (lastAdded < 0)
                    {
                        go = claimed.Contains(i) == false;
                    }
                    else if (lastAdded == i)
                    {
                        //We've gone through twice without finding a match. This means there isn't one, or something.

                        fail = true;
                    }
                    else
                    {
                        //Otherwise, we're trying to find the next-in-chain.
                        //A valid next-in-chain is connected by geometry which, as discussed, means it's connected
                        //by having matching parent indices (index0, index1).

                        bool match = localIndexByIntersection[i] == localIndexByIntersection[lastAdded];

                        //But there's a special case about the match; it's possible that we've closed the loop!
                        //How do we know we've closed the loop? There are multiple ways but the simplest is that
                        //the chain already contains the element in question.

                        bool loopComplete = match && currentWorkingPoly.Contains(i);

                        if (loopComplete)
                        {
                            allPolys.Add(currentTargetPoly);
                            startNewChain = true;
                        }
                        else
                        {
                            go = match;
                        }
                    }

                    if (go)
                    {
                        int partnerByParent = i % 2 == 1 ? i - 1 : i + 1;

                        int[] pair = { i, partnerByParent };

                        currentWorkingPoly.AddRange(pair);
                        claimed.AddRange(pair);

                        currentTargetPoly.Add(partnerByParent);

                        lastAdded = partnerByParent;

                        //Skip ahead and resume the search _from_ here, so that we don't step into it
                        //again from within this loop walk.
                        i = partnerByParent;
                    }
                    else if (fail)
                    {
                        //We want to start a fresh poly without adding this to the valid polys.
                        startNewChain = true;

                        //Debug.Log("[fail]");
                    }

                    if (startNewChain)
                    {
                        currentWorkingPoly.Clear();
                        currentTargetPoly = new List <int>();
                        lastAdded         = -1;
                    }
                }
            }while(currentWorkingPoly.Count > 0);

            //Now we go through each poly and triangulate it.

            foreach (List <int> _poly in allPolys)
            {
                Vector2[] poly = new Vector2[_poly.Count];

                for (int i = 0; i < poly.Length; i++)
                {
                    int j = localIndexByIntersection[_poly[i]];
                    poly[i] = transformedVertices[j];
                }

                int[] result;

                if (Triangulation.triangulate(poly, out result))
                {
                    int[] front = new int[result.Length];
                    int[] back  = new int[result.Length];

                    for (int i = 0; i < result.Length; i++)
                    {
                        int p     = _poly[result[i]];
                        int local = localIndexByIntersection[p];
                        front[i] = local + infillFrontOffset + newIndexStartsAt;
                        back[i]  = local + infillBackOffset + newIndexStartsAt;
                    }

                    for (int i = 0; i < result.Length; i += 3)
                    {
                        int j = front[i];
                        front[i]     = front[i + 2];
                        front[i + 2] = j;
                    }

                    frontInfill.AddArray(front);
                    backInfill.AddArray(back);
                }
            }
        }
    }
Esempio n. 30
0
        public void Start(IScene scene)
        {
            m_assetClient = scene.Simian.GetAppModule<IAssetClient>();
            m_meshCache = scene.Simian.GetAppModule<MeshCache>();

            List<string> rendererNames = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
            if (rendererNames.Count == 0)
            {
                m_log.Error("PrimMesher could not find a valid OpenMetaverse.Rendering.*.dll plugin");
                return;
            }

            // TODO: Add a config option to allow a preferred renderer to be selected
            m_renderer = RenderingLoader.LoadRenderer(rendererNames[0]);
            m_log.Debug(scene.Name + " is meshing prims with " + m_renderer);
        }
Esempio n. 31
0
        /// <summary>
        /// Finalises the mesh object an child objects.
        /// </summary>
        /// <param name="obj">Game object to set the mesh on or create children on.</param>
        /// <param name="shape">The <see cref="ShapeComponent"/> belonging to <paramref name="obj"/>.</param>
        /// <param name="meshData">Mesh vertex and index data.</param>
        /// <param name="material">Material to render with. Chosen based on topology.</param>
        /// <param name="colour">The mesh render colour.</param>
        protected void FinaliseMesh(GameObject obj, ShapeComponent shape, MeshDataComponent meshData, Material material, Color32 colour)
        {
            MeshFilter   meshFilter  = obj.GetComponent <MeshFilter>();
            MeshTopology topology    = MeshCache.DrawTypeToTopology(meshData.DrawType);
            bool         haveNormals = meshData.Normals != null && meshData.Vertices != null &&
                                       meshData.Normals.Length == meshData.Vertices.Length;
            bool haveColours = meshData.Colours != null && meshData.Vertices != null &&
                               meshData.Colours.Length == meshData.Vertices.Length;

            if (meshData.Vertices.Length < 65000)
            {
                if (meshFilter == null)
                {
                    meshFilter = obj.AddComponent <MeshFilter>();
                }
                // Can do it with one object.
                Mesh mesh = new Mesh();
                obj.GetComponent <MeshFilter>().mesh = mesh;
                mesh.subMeshCount = 1;
                mesh.vertices     = meshData.Vertices;
                if (haveNormals)
                {
                    mesh.normals = meshData.Normals;
                }
                else
                {
                    mesh.normals = null;
                }

                if (haveColours)
                {
                    mesh.colors32 = meshData.Colours;
                }

                if (meshData.Indices.Length > 0)
                {
                    mesh.SetIndices(meshData.Indices, topology, 0);
                }
                else
                {
                    // No explicit indices. Set sequential indexing.
                    int[] indices = new int[meshData.Vertices.Length];
                    for (int i = 0; i < indices.Length; ++i)
                    {
                        indices[i] = i;
                    }
                    mesh.SetIndices(indices, topology, 0);
                }

                MeshRenderer render = obj.GetComponent <MeshRenderer>();
                render.material       = material;
                render.material.color = colour;
                if (shape.TwoSided && render.material.HasProperty("_BackColour"))
                {
                    render.material.SetColor("_BackColour", colour);
                }
                if (meshData.DrawType == MeshDrawType.Points)
                {
                    int pointSize = 4;
                    if (Materials != null)
                    {
                        pointSize = Materials.DefaultPointSize;
                    }
                    render.material.SetInt("_PointSize", pointSize);
                    render.material.SetInt("_LeftHanded", ServerInfo.IsLeftHanded ? 1 : 0);
                }

                mesh.RecalculateBounds();
                if (meshData.CalculateNormals && !haveNormals)
                {
                    //mesh.RecalculateNormals();
                }
            }
            else
            {
                // Need multiple objects.
                // Destroy the current mesh filter.
                if (meshFilter != null)
                {
                    GameObject.Destroy(meshFilter);
                }

                // Create children.
                int elementIndices = MeshCache.TopologyIndexStep(MeshCache.DrawTypeToTopology(meshData.DrawType));
                // Calculate the number of vertices per mesh by truncation.
                int   itemsPerMesh = (65000 / elementIndices) * elementIndices;
                int[] indices      = meshData.Indices;

                if (indices.Length == 0)
                {
                    // No explicit indices. Set sequential indexing.
                    indices = new int[meshData.Vertices.Length];
                    for (int i = 0; i < indices.Length; ++i)
                    {
                        indices[i] = i;
                    }
                }

                // For now just duplicate vertex data.
                int indexOffset = 0;
                int partCount   = 0;
                int elementCount;
                while (indexOffset < indices.Length)
                {
                    ++partCount;
                    GameObject   part     = new GameObject(string.Format("{0}{1:D2}", topology.ToString(), partCount));
                    Mesh         partMesh = part.AddComponent <MeshFilter>().mesh;
                    MeshRenderer render   = part.AddComponent <MeshRenderer>();
                    render.material       = material;
                    render.material.color = colour;
                    if (shape.TwoSided)
                    {
                        render.material.SetColor("_BackColour", colour);
                    }
                    if (meshData.DrawType == MeshDrawType.Points)
                    {
                        render.material.SetInt("_LeftHanded", ServerInfo.IsLeftHanded ? 1 : 0);
                    }
                    partMesh.subMeshCount = 1;
                    elementCount          = Math.Min(itemsPerMesh, indices.Length - indexOffset);

                    Vector3[] partVerts   = new Vector3[elementCount];
                    Vector3[] partNorms   = (haveNormals) ? new Vector3[elementCount] : null;
                    Color32[] partColours = (haveColours) ? new Color32[elementCount] : null;
                    int[]     partInds    = new int[elementCount];

                    for (int i = 0; i < elementCount; ++i)
                    {
                        partVerts[i] = meshData.Vertices[indices[i + indexOffset]];
                        if (partNorms != null)
                        {
                            partNorms[i] = meshData.Normals[indices[i + indexOffset]];
                        }
                        if (partColours != null)
                        {
                            partColours[i] = meshData.Colours[indices[i + indexOffset]];
                        }
                        partInds[i] = i;
                    }

                    part.transform.SetParent(obj.transform, false);
                    partMesh.vertices = partVerts;
                    if (partNorms != null)
                    {
                        partMesh.normals = partNorms;
                    }
                    if (partColours != null)
                    {
                        partMesh.colors32 = partColours;
                    }
                    partMesh.SetIndices(partInds, topology, 0);
                    partMesh.RecalculateBounds();
                    if (meshData.CalculateNormals && !haveNormals)
                    {
                        //partMesh.RecalculateNormals();
                    }

                    indexOffset += elementCount;
                }
            }
        }
Esempio n. 32
0
    protected override void Start()
    {
        base.Start();

        Materials.Register(MaterialLibrary.VertexColourLit, VertexColourLitMaterial);
        Materials.Register(MaterialLibrary.VertexColourUnlit, VertexColourUnlitMaterial);
        Materials.Register(MaterialLibrary.VertexColourLitTwoSided, VertexColourLitTwoSidedMaterial);
        Materials.Register(MaterialLibrary.VertexColourUnlitTwoSided, VertexColourUnlitTwoSidedMaterial);
        Materials.Register(MaterialLibrary.WireframeTriangles, WireframeTriangles);
        Materials.Register(MaterialLibrary.VertexColourTransparent, VertexColourTransparent);
        Materials.Register(MaterialLibrary.PointsLit, PointsLitMaterial);
        Materials.Register(MaterialLibrary.PointsUnlit, PointsUnlitMaterial);
        Materials.Register(MaterialLibrary.Voxels, VoxelsMaterial);

        if (Scene != null && Scene.Root != null)
        {
            Scene.Root.transform.parent = this.transform;
        }

        CategoriesHandler categories = new CategoriesHandler();
        MeshCache         meshCache  = new MeshCache();

        Handlers.Register(meshCache);
        Handlers.Register(categories);

        Handlers.Register(new CameraHandler(categories.IsActive));

        Handlers.Register(new ArrowHandler(categories.IsActive));
        Handlers.Register(new BoxHandler(categories.IsActive));
        Handlers.Register(new CapsuleHandler(categories.IsActive));
        Handlers.Register(new ConeHandler(categories.IsActive));
        Handlers.Register(new CylinderHandler(categories.IsActive));
        Handlers.Register(new PlaneHandler(categories.IsActive));
        Handlers.Register(new SphereHandler(categories.IsActive));
        Handlers.Register(new StarHandler(categories.IsActive));
        Handlers.Register(new MeshHandler(categories.IsActive));
        Handlers.Register(new MeshSetHandler(categories.IsActive, meshCache));
        Handlers.Register(new PointCloudHandler(categories.IsActive, meshCache));
        Handlers.Register(new Text2DHandler(categories.IsActive));
        Handlers.Register(new Text3DHandler(categories.IsActive));

        // Register handlers from plugins.
        string[] loadPaths = new string[]
        {
#if UNITY_EDITOR
            Path.Combine("Assets", "plugins")
#else  // UNITY_EDITOR
            "plugins",
            Path.Combine("3rd-Eye-Scene_Data", "Managed")
#endif // UNITY_EDITOR
        };

        CategoryCheckDelegate catDelegate = categories.IsActive;
        foreach (string loadPath in loadPaths)
        {
            Handlers.LoadPlugins(loadPath, Plugins, "3esRuntime.dll", new object[] { catDelegate });
        }

        InitialiseHandlers();

        categories.OnActivationChange += (ushort categoryId, bool active) =>
        {
            foreach (MessageHandler handler in Handlers.Handlers)
            {
                handler.OnCategoryChange(categoryId, active);
            }
        };

        LoadCommandLineFile();
    }