Ejemplo n.º 1
0
        static public Batch Create(BatchPool parentPool, Transform rParent, Bounds rBounds)
        {
            var        brush  = BrushCatalog.m_Instance.GetBrush(parentPool.m_BrushGuid);
            string     name   = string.Format("Batch_{0}_{1}", parentPool.m_Batches.Count, brush.m_Guid);
            GameObject newObj = new GameObject(name);

            Transform t = newObj.transform;

            t.parent        = rParent;
            t.localPosition = Vector3.zero;
            t.localRotation = Quaternion.identity;
            t.localScale    = Vector3.one;

            newObj.AddComponent <MeshFilter>();

            Renderer renderer = newObj.AddComponent <MeshRenderer>();

            renderer.material = brush.Material;

            var propertyBlock = new MaterialPropertyBlock();

            renderer.GetPropertyBlock(propertyBlock);
            ushort batchId = GpuIntersector.GetNextBatchId();

            propertyBlock.SetFloat("_BatchID", batchId);
            renderer.SetPropertyBlock(propertyBlock);

            Batch batch = newObj.AddComponent <Batch>();

            batch.Init(parentPool, rBounds, batchId);
            // This forces instantiation, but we can detect and clean it up in Destroy()
            batch.m_InstantiatedMaterial = renderer.material;

            return(batch);
        }
Ejemplo n.º 2
0
        /// Destroys the batch and all resources+objects owned by it.
        /// The batch is no longer usable after this.
        public void Destroy()
        {
            m_ParentPool = null;

            // Writing a BatchSubset.Destroy() wouldn't be worth it; there's nothing really to destroy
            foreach (var subset in m_Groups)
            {
                subset.m_ParentBatch = null;
            }
            m_Groups = null;

            // Don't bother with mesh.Clear() since we're about to destroy it.
            // m_MeshFilter.mesh.Clear();

            // Don't bother with m_Geometry.Reset(). Internally it uses List<>.Clear()
            // which wastes time zeroing out the list entries. That's wasted work since
            // we're going to garbage the whole thing.
            // m_Geometry.Reset();

            // I don't think we want to do this until GeometryPool.Free() is smart enough
            // to limit the number of instances on the freelist.
            // GeometryPool.Free(m_Geometry);
            m_Geometry.Destroy();
            m_Geometry = null;

            Destroy(m_InstantiatedMaterial);

            Destroy(m_MeshFilter.mesh);
            m_MeshFilter = null;
            Destroy(gameObject);
        }
Ejemplo n.º 3
0
        /// Creates and returns a new subset containing the passed geometry.
        /// Pass:
        ///   otherSubset -
        ///     Specifies both the material/batch to copy into,
        ///     as well as the geometry to copy.
        ///     May be owned by a different BatchManager.
        ///   leftTransform - optional transform to transform the subset.
        public BatchSubset CreateSubset(BatchSubset otherSubset, TrTransform?leftTransform = null)
        {
            Batch     otherBatch = otherSubset.m_ParentBatch;
            BatchPool otherPool  = otherBatch.ParentPool;

            var pool  = GetPool(otherPool.m_BrushGuid);
            var batch = GetBatch(pool, otherSubset.m_VertLength);

            return(batch.AddSubset(
                       otherBatch.Geometry,
                       otherSubset.m_StartVertIndex, otherSubset.m_VertLength,
                       otherSubset.m_iTriIndex, otherSubset.m_nTriIndex,
                       leftTransform));
        }
Ejemplo n.º 4
0
        void Init(BatchPool parentPool, Bounds bounds, ushort batchId)
        {
            BatchId      = batchId;
            m_ParentPool = parentPool;
            parentPool.m_Batches.Add(this);

            m_Groups     = new List <BatchSubset>();
            m_MeshFilter = GetComponent <MeshFilter>();
            Debug.Assert(m_MeshFilter.sharedMesh == null);

            m_Geometry = new GeometryPool();

            var rNewMesh = new Mesh();

            rNewMesh.MarkDynamic();

            gameObject.layer = ParentPool.Owner.Canvas.gameObject.layer;

            // This is a fix for b/27266757. I don't know precisely why it works.
            //
            // I think the mesh needs to spend "some amount of time" with a non-zero-length
            // vtx buffer. If this line is followed by .vertices = new Vector3[0], the bug
            // appears again. The mysterious thing is that immediately after creation, we
            // start filling up .vertices. Why does the first assignment need to happen here,
            // instead of waiting just a few ms for the mesh to be updated with real data?
            //
            // This seems related to how and when Unity decides to upload mesh data to the GPU.
            rNewMesh.vertices = new Vector3[1];

            // TODO: why set bounds?
            rNewMesh.bounds   = bounds;
            m_MeshFilter.mesh = rNewMesh;

            // Instantiate mesh so we can destroy rNewMesh; destroy rNewMesh to protect
            // against it leaking if/when someone reads m_MeshFilter.mesh.
            bool instantiationSucceeded = (m_MeshFilter.mesh != rNewMesh);

            Debug.Assert(instantiationSucceeded);
            DestroyImmediate(rNewMesh);

            m_bVertexDataDirty = false;
            m_bTopologyDirty   = false;
        }
Ejemplo n.º 5
0
 BatchPool GetPool(Guid brushGuid)
 {
     try {
         return(m_BrushToPool[brushGuid]);
     } catch (KeyNotFoundException) {
         BatchPool rNewPool = new BatchPool(this);
         rNewPool.m_BrushGuid = brushGuid;
         rNewPool.m_Batches   = new List <Batch>();
         Batch b = Batch.Create(rNewPool, m_ParentTransform, m_MeshBounds);
         sm_BatchMap.Add(b.BatchId, b);
         m_Pools.Add(rNewPool);
         m_BrushToPool[rNewPool.m_BrushGuid] = rNewPool;
         foreach (string keyword in m_MaterialKeywords)
         {
             b.InstantiatedMaterial.EnableKeyword(keyword);
         }
         return(rNewPool);
     }
 }
Ejemplo n.º 6
0
        protected void DebugDrawBounds()
        {
            CanvasScript canvas           = App.ActiveCanvas;
            float        fSelectionRadius = GetSize();

            if (App.Config.m_UseBatchedBrushes)
            {
                int iNumBatchPools = canvas.BatchManager.GetNumBatchPools();
                for (int i = 0; i < iNumBatchPools; ++i)
                {
                    BatchPool rPool = canvas.BatchManager.GetBatchPool(i);
                    for (int j = 0; j < rPool.m_Batches.Count; ++j)
                    {
                        for (int k = 0; k < rPool.m_Batches[j].m_Groups.Count; ++k)
                        {
                            Bounds rMeshBounds = rPool.m_Batches[j].m_Groups[k].m_Bounds;
                            rMeshBounds.Expand(fSelectionRadius);
                            Color rDrawColor = rPool.m_Batches[j].m_Groups[k].m_Active ? Color.white : Color.red;
                            DebugDrawBox(rMeshBounds, Vector3.zero, rDrawColor);
                        }
                    }
                }
            }
            else
            {
                Transform rCanvas = canvas.transform;
                for (int i = 0; i < rCanvas.childCount; ++i)
                {
                    Transform rChild = rCanvas.GetChild(i);
                    if (rChild.gameObject.activeSelf)
                    {
                        MeshFilter rMeshFilter = rChild.GetComponent <MeshFilter>();
                        if (rMeshFilter)
                        {
                            Bounds rMeshBounds = rMeshFilter.mesh.bounds;
                            rMeshBounds.Expand(fSelectionRadius);
                            DebugDrawBox(rMeshBounds, rChild.position, Color.white);
                        }
                    }
                }
            }
        }
Ejemplo n.º 7
0
        // Returns a batch such that it has space for at least nVerts
        Batch GetBatch(BatchPool pool, int nVerts)
        {
            if (pool.m_Batches.Count > 0)
            {
                var batch = pool.m_Batches[pool.m_Batches.Count - 1];
                if (batch.HasSpaceFor(nVerts))
                {
                    return(batch);
                }
            }

            {
                Batch b = Batch.Create(pool, m_ParentTransform, m_MeshBounds);
                sm_BatchMap.Add(b.BatchId, b);
                foreach (string keyword in m_MaterialKeywords)
                {
                    b.InstantiatedMaterial.EnableKeyword(keyword);
                }
                Debug.Assert(pool.m_Batches[pool.m_Batches.Count - 1] == b);
                return(b);
            }
        }
Ejemplo n.º 8
0
        /// Detection Center should be in Global Space.
        protected void UpdateBatchedBrushDetection(Vector3 vDetectionCenter_GS)
        {
            // The CPU intersection code still needs to be updated to iterate over multiple canvases.
            //
            // TODO: Update CPU intersection checking to work on multiple canvases,
            //       then get rid of automatic defaulting to ActiveCanvas.
            //       Possibly let m_CurrentCanvas be null to represent a desire to intersect
            //       with all canvases.
            if (m_CurrentCanvas == null)
            {
                m_CurrentCanvas = App.ActiveCanvas;
            }

            // If we changed canvases, abandon any progress we made on checking for
            // intersections in the previous canvas.
            if (m_CurrentCanvas != m_PreviousCanvas)
            {
                ResetDetection();
                m_PreviousCanvas = m_CurrentCanvas;
            }

            TrTransform canvasPose          = m_CurrentCanvas.Pose;
            Vector3     vDetectionCenter_CS = canvasPose.inverse * vDetectionCenter_GS;

            m_TimesUp = false;

            // Reset detection if we've moved or adjusted our size
            float fDetectionRadius_CS   = GetSize() / canvasPose.scale;
            float fDetectionRadiusSq_CS = fDetectionRadius_CS * fDetectionRadius_CS;

            // Start the timer!
            m_DetectionStopwatch.Reset();
            m_DetectionStopwatch.Start();

            int  iSanityCheck    = 10000;
            bool bNothingChecked = true;

            if (App.Config.m_GpuIntersectionEnabled)
            {
                // Run GPU intersection if enabled; will update m_TimesUp.
                if (UpdateGpuIntersection(vDetectionCenter_GS, GetSize()))
                {
                    IntersectionHappenedThisFrame();
                    m_DetectionStopwatch.Stop();
                    DoIntersectionResets();
                    return;
                }
            }

            m_TimesUp = m_DetectionStopwatch.ElapsedTicks > m_TimeSliceInTicks;

            //check batch pools first
            int iNumBatchPools = m_CurrentCanvas.BatchManager.GetNumBatchPools();

            if (!App.Config.m_GpuIntersectionEnabled &&
                iNumBatchPools > 0 &&
                m_BatchPoolIndex < iNumBatchPools)
            {
                bNothingChecked  = false;
                m_ResetDetection = false;
                Plane     rTestPlane = new Plane();
                BatchPool rPool      = m_CurrentCanvas.BatchManager.GetBatchPool(m_BatchPoolIndex);

                //spin until we've taken up too much time
                while (!m_TimesUp)
                {
                    --iSanityCheck;
                    if (iSanityCheck == 0)
                    {
                        Batch tmpBatch = rPool.m_Batches[m_BatchObjectIndex];
                        Debug.LogErrorFormat("Stroke while loop error.  NumPools({0}) BatchPoolIndex({1}) NumBatchStrokes({2}) BatchStrokeIndex({3}) NumStrokeGroups({4})",
                                             iNumBatchPools, m_BatchPoolIndex, rPool.m_Batches.Count, m_BatchObjectIndex, tmpBatch.m_Groups.Count);
                    }

                    Batch batch = rPool.m_Batches[m_BatchObjectIndex];
                    if (m_BatchVertGroupIndex < batch.m_Groups.Count)
                    {
                        var    subset      = batch.m_Groups[m_BatchVertGroupIndex];
                        Bounds rMeshBounds = subset.m_Bounds;
                        rMeshBounds.Expand(2.0f * fDetectionRadius_CS);

                        if (subset.m_Active && rMeshBounds.Contains(vDetectionCenter_CS))
                        {
                            //bounds valid, check triangle intersections with sphere
                            int       nTriIndices = subset.m_nTriIndex;
                            Vector3[] aVerts;
                            int       nVerts;
                            int[]     aTris;
                            int       nTris;
                            batch.GetTriangles(out aVerts, out nVerts, out aTris, out nTris);
                            while (m_BatchTriIndexIndex < nTriIndices - 2)
                            {
                                //check to see if we're within the brush size (plus some) radius to this triangle
                                int     iTriIndex            = subset.m_iTriIndex + m_BatchTriIndexIndex;
                                Vector3 v0                   = aVerts[aTris[iTriIndex]];
                                Vector3 v1                   = aVerts[aTris[iTriIndex + 1]];
                                Vector3 v2                   = aVerts[aTris[iTriIndex + 2]];
                                Vector3 vTriCenter           = (v0 + v1 + v2) * 0.33333f;
                                Vector3 vToTestCenter        = vDetectionCenter_CS - vTriCenter;
                                float   fTestSphereRadius_CS = Vector3.Distance(v1, v2) + fDetectionRadius_CS;
                                if (vToTestCenter.sqrMagnitude < fTestSphereRadius_CS * fTestSphereRadius_CS)
                                {
                                    //check to see if we're within the sphere radius to the plane of this triangle
                                    Vector3 vNorm = Vector3.Cross(v1 - v0, v2 - v0).normalized;
                                    rTestPlane.SetNormalAndPosition(vNorm, v0);
                                    float fDistToPlane = rTestPlane.GetDistanceToPoint(vDetectionCenter_CS);
                                    if (Mathf.Abs(fDistToPlane) < fDetectionRadius_CS)
                                    {
                                        //we're within the radius to this triangle's plane, find the point projected on to the plane
                                        fDistToPlane *= -1.0f;
                                        Vector3 vPlaneOffsetVector = vNorm * fDistToPlane;
                                        Vector3 vPlaneIntersection = vDetectionCenter_CS - vPlaneOffsetVector;

                                        //walk the projected point toward the triangle center to find the triangle test position
                                        bool    bIntersecting     = false;
                                        Vector3 vPointToTriCenter = vTriCenter - vDetectionCenter_CS;
                                        if (vPointToTriCenter.sqrMagnitude < fDetectionRadiusSq_CS)
                                        {
                                            //if the triangle center is within the detection distance, we're definitely intersecting
                                            bIntersecting = true;
                                        } //check against triangle segments
                                        else if (SegmentSphereIntersection(v0, v1, vDetectionCenter_CS, fDetectionRadiusSq_CS))
                                        {
                                            bIntersecting = true;
                                        }
                                        else if (SegmentSphereIntersection(v1, v2, vDetectionCenter_CS, fDetectionRadiusSq_CS))
                                        {
                                            bIntersecting = true;
                                        }
                                        else if (SegmentSphereIntersection(v2, v0, vDetectionCenter_CS, fDetectionRadiusSq_CS))
                                        {
                                            bIntersecting = true;
                                        }
                                        else
                                        {
                                            //figure out how far we have left to move toward the tri-center
                                            float fNormAngle = Mathf.Acos(Mathf.Abs(fDistToPlane) / fDetectionRadius_CS);
                                            float fDistLeft  = Mathf.Sin(fNormAngle) * fDetectionRadius_CS;

                                            Vector3 vToTriCenter = vTriCenter - vPlaneIntersection;
                                            vToTriCenter.Normalize();
                                            vToTriCenter       *= fDistLeft;
                                            vPlaneIntersection += vToTriCenter;

                                            //see if this projected point is in the triangle
                                            if (PointInTriangle(ref vPlaneIntersection, ref v0, ref v1, ref v2))
                                            {
                                                bIntersecting = true;
                                            }
                                        }

                                        if (bIntersecting)
                                        {
                                            if (HandleIntersectionWithBatchedStroke(subset))
                                            {
                                                DoIntersectionResets();
                                                break;
                                            }
                                        }
                                    }
                                }

                                //after each triangle, check our time
                                m_BatchTriIndexIndex += 3;
                                m_TimesUp             = m_DetectionStopwatch.ElapsedTicks > m_TimeSliceInTicks;
                                if (m_TimesUp)
                                {
                                    break;
                                }
                            }
                        }
                    }

                    //if we're not flagged as done, we just finished this group, so move on to the next group
                    if (!m_TimesUp)
                    {
                        m_BatchTriIndexIndex = 0;
                        ++m_BatchVertGroupIndex;

                        //if we're done with groups, go to the next object
                        if (m_BatchVertGroupIndex >= batch.m_Groups.Count)
                        {
                            m_BatchVertGroupIndex = 0;
                            ++m_BatchObjectIndex;

                            //aaaand if we're done with objects, go on to the next pool
                            if (m_BatchObjectIndex >= rPool.m_Batches.Count)
                            {
                                m_BatchObjectIndex = 0;
                                ++m_BatchPoolIndex;
                                if (m_BatchPoolIndex >= iNumBatchPools)
                                {
                                    //get out if we've traversed the last pool
                                    break;
                                }
                                rPool = m_CurrentCanvas.BatchManager.GetBatchPool(m_BatchPoolIndex);
                            }
                        }

                        //we check again here in case the early checks fail
                        m_TimesUp = m_DetectionStopwatch.ElapsedTicks > m_TimeSliceInTicks;
                    }
                }
            }

            if (App.Config.m_GpuIntersectionEnabled && m_GpuFutureResult != null)
            {
                // We have an intersection test in flight, make sure we don't reset detection.
                // This ensures consistency between collision tests and avoids strobing of the result (e.g.
                // without this, one test will return "no result" and the next may return some result and this
                // oscillation will continue).
                bNothingChecked  = false;
                m_ResetDetection = false;
                return;
            }

            // If our scene doesn't have anything in it, reset our detection.
            if (bNothingChecked)
            {
                m_ResetDetection = true;
            }

            m_DetectionStopwatch.Stop();
        }