/// Detection Center should be in Global Space. protected void UpdateSolitaryBrushDetection(Vector3 vDetectionCenter_GS) { if (m_CurrentCanvas == null) { m_CurrentCanvas = App.ActiveCanvas; } TrTransform canvasPose = m_CurrentCanvas.Pose; Vector3 vDetectionCenter_CS = canvasPose.inverse * vDetectionCenter_GS; Transform rCanvas = m_CurrentCanvas.transform; int iNumCanvasChildren = rCanvas.childCount; m_TimesUp = false; //reset detection if we've moved or adjusted our size float fDetectionRadius = GetSize(); float fDetectionRadiusSq = fDetectionRadius * fDetectionRadius; m_DetectionStopwatch.Reset(); m_DetectionStopwatch.Start(); //early out if there's nothing to look at if (iNumCanvasChildren > 0 && m_DetectionObjectIndex < iNumCanvasChildren) { Plane rTestPlane = new Plane(); m_ResetDetection = false; //spin until we've taken up too much time while (!m_TimesUp) { //check child bounds Transform rChild = rCanvas.GetChild(m_DetectionObjectIndex); if (rChild.gameObject.activeSelf) { MeshFilter rMeshFilter = rChild.GetComponent <MeshFilter>(); if (rMeshFilter) { Bounds rMeshBounds = rMeshFilter.mesh.bounds; rMeshBounds.Expand(fDetectionRadius); Vector3 vTransformedCenter = rChild.InverseTransformPoint(vDetectionCenter_CS); if (rMeshBounds.Contains(vTransformedCenter)) { //bounds valid, check triangle intersections with sphere int iMeshVertCount = rMeshFilter.mesh.vertexCount; Vector3[] aVerts = rMeshFilter.mesh.vertices; Vector3[] aNorms = rMeshFilter.mesh.normals; while (m_DetectionVertIndex < iMeshVertCount - 2) { //check to see if we're within the sphere radius to the plane of this triangle Vector3 vVert = aVerts[m_DetectionVertIndex]; Vector3 vNorm = aNorms[m_DetectionVertIndex]; rTestPlane.SetNormalAndPosition(vNorm, vVert); float fDistToPlane = rTestPlane.GetDistanceToPoint(vTransformedCenter); if (Mathf.Abs(fDistToPlane) < fDetectionRadius) { //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 = vTransformedCenter - vPlaneOffsetVector; Vector3 vVert2 = aVerts[m_DetectionVertIndex + 1]; Vector3 vVert3 = aVerts[m_DetectionVertIndex + 2]; //walk the projected point toward the triangle center to find the triangle test position Vector3 vTriCenter = (vVert + vVert2 + vVert3) * 0.33333f; bool bIntersecting = false; Vector3 vPointToTriCenter = vTriCenter - vTransformedCenter; if (vPointToTriCenter.sqrMagnitude < fDetectionRadiusSq) { //if the triangle center is within the detection distance, we're definitely intersecting bIntersecting = true; } else { //figure out how far we have left to move toward the tri-center float fNormAngle = Mathf.Acos(Mathf.Abs(fDistToPlane) / fDetectionRadius); float fDistLeft = Mathf.Sin(fNormAngle) * fDetectionRadius; Vector3 vToTriCenter = vTriCenter - vPlaneIntersection; vToTriCenter.Normalize(); vToTriCenter *= fDistLeft; vPlaneIntersection += vToTriCenter; //see if this projected point is in the triangle if (PointInTriangle(ref vPlaneIntersection, ref vVert, ref vVert2, ref vVert3)) { bIntersecting = true; } } if (bIntersecting) { if (HandleIntersectionWithSolitaryObject(rChild.gameObject)) { DoIntersectionResets(); break; } } } //after each triangle, check our time m_DetectionVertIndex += 3; m_TimesUp = m_DetectionStopwatch.ElapsedTicks > m_TimeSliceInTicks; if (m_TimesUp) { break; } } } } } //if we're not flagged as done, we just finished this object, so move on to the next if (!m_TimesUp) { //move to the next object ++m_DetectionObjectIndex; m_DetectionVertIndex = 0; if (m_DetectionObjectIndex >= iNumCanvasChildren) { //if we reached the end of the line, we're done break; } //might as well check our clock per object m_TimesUp = m_DetectionStopwatch.ElapsedTicks > m_TimeSliceInTicks; } } } m_DetectionStopwatch.Stop(); }
/// 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(); }