public static void findShieldedPartsCylinder(Part basePart, Bounds fairingRenderBounds, List <Part> shieldedParts, float topY, float bottomY, float topRadius, float bottomRadius) { float height = topY - bottomY; float largestRadius = topRadius > bottomRadius ? topRadius : bottomRadius; Vector3 lookupCenterLocal = new Vector3(0, bottomY + (height * 0.5f), 0); Vector3 lookupTopLocal = new Vector3(0, topY, 0); Vector3 lookupBottomLocal = new Vector3(0, bottomY, 0); Vector3 lookupCenterGlobal = basePart.transform.TransformPoint(lookupCenterLocal); Ray lookupRay = new Ray(lookupBottomLocal, new Vector3(0, 1, 0)); List <Part> partsFound = new List <Part>(); Collider[] foundColliders = Physics.OverlapSphere(lookupCenterGlobal, height * 1.5f, 1); foreach (Collider col in foundColliders) { Part pt = col.gameObject.GetComponentUpwards <Part>(); if (pt != null && pt != basePart && pt.vessel == basePart.vessel && !partsFound.Contains(pt)) { partsFound.Add(pt); } } Bounds[] otherPartBounds; Vector3 otherPartCenterLocal; float partYPos; float partYPercent; float partYRadius; float radiusOffset = topRadius - bottomRadius; foreach (Part pt in partsFound) { //check basic render bounds for containment //TODO this check misses the case where the fairing is long/tall, containing a wide part; it will report that the wide part can fit inside //of the fairing, due to the relative size of their colliders otherPartBounds = pt.GetRendererBounds(); if (PartGeometryUtil.MergeBounds(otherPartBounds, pt.transform).size.sqrMagnitude > fairingRenderBounds.size.sqrMagnitude) { continue; } Vector3 otherPartCenter = pt.partTransform.TransformPoint(PartGeometryUtil.FindBoundsCentroid(otherPartBounds, pt.transform)); if (!fairingRenderBounds.Contains(otherPartCenter)) { continue; } //check part bounds center point against conic projection of the fairing otherPartCenterLocal = basePart.transform.InverseTransformPoint(otherPartCenter); //check vs top and bottom of the shielded area if (otherPartCenterLocal.y > lookupTopLocal.y || otherPartCenterLocal.y < lookupBottomLocal.y) { continue; } //quick check vs cylinder radius float distFromLine = SSTUUtils.distanceFromLine(lookupRay, otherPartCenterLocal); if (distFromLine > largestRadius) { continue; } //more precise check vs radius of the cone at that Y position partYPos = otherPartCenterLocal.y - lookupBottomLocal.y; partYPercent = partYPos / height; partYRadius = partYPercent * radiusOffset; if (distFromLine > (partYRadius + bottomRadius)) { continue; } shieldedParts.Add(pt); } }
//TODO clean this up to be easier to read/understand now that it is optimized for cylinder check only public static void findShieldedPartsCylinder(Part basePart, List <Part> shieldedParts, float topY, float bottomY, float topRadius, float bottomRadius) { float height = topY - bottomY; float largestRadius = topRadius > bottomRadius ? topRadius : bottomRadius; Vector3 lookupCenterLocal = new Vector3(0, bottomY + (height * 0.5f), 0); Vector3 lookupTopLocal = new Vector3(0, topY, 0); Vector3 lookupBottomLocal = new Vector3(0, bottomY, 0); Vector3 lookupCenterGlobal = basePart.transform.TransformPoint(lookupCenterLocal); Ray lookupRay = new Ray(lookupBottomLocal, new Vector3(0, 1, 0)); List <Part> partsFound = new List <Part>(); //do a basic sphere check vs the maximal size of the cylinder Collider[] foundColliders = Physics.OverlapSphere(lookupCenterGlobal, height * 1.5f, 1); foreach (Collider col in foundColliders) { Part pt = col.gameObject.GetComponentUpwards <Part>(); if (pt != null && pt != basePart && pt.vessel == basePart.vessel && !partsFound.Contains(pt)) { partsFound.Add(pt); } } Vector3 otherPartCenterLocal; float partYPos; float partYPercent; float partYRadius; float radiusOffset = topRadius - bottomRadius; foreach (Part pt in partsFound) { Vector3 otherPartCenter = pt.partTransform.TransformPoint(PartGeometryUtil.FindBoundsCentroid(pt.GetRendererBounds(), pt.transform)); //check part bounds center point against conic projection of the fairing otherPartCenterLocal = basePart.transform.InverseTransformPoint(otherPartCenter); //check vs top and bottom of the shielded area if (otherPartCenterLocal.y > lookupTopLocal.y || otherPartCenterLocal.y < lookupBottomLocal.y) { continue; } //quick check vs cylinder radius float distFromLine = SSTUUtils.distanceFromLine(lookupRay, otherPartCenterLocal); if (distFromLine > largestRadius) { continue; } //more precise check vs radius of the cone at that Y position partYPos = otherPartCenterLocal.y - lookupBottomLocal.y; partYPercent = partYPos / height; partYRadius = partYPercent * radiusOffset; if (distFromLine > (partYRadius + bottomRadius)) { continue; } shieldedParts.Add(pt); //print("Shielding part: " + pt); } }
void enableShielding() { disableShielding(); var attached = getFairingParams(); if (!sideFairing) { return; } // Get all parts in range. var parts = new List <Part>(); var colliders = Physics.OverlapSphere(part.transform.TransformPoint(lookupCenter), lookupRad, 1); for (int i = colliders.Length - 1; i >= 0; --i) { var p = colliders [i].gameObject.GetComponentUpwards <Part>(); if (p != null) { parts.AddUnique(p); } } // Filter parts. float sizeSqr = lookupRad * lookupRad * 4; float boundCylRadSq = boundCylRad * boundCylRad; bool isInline = (sideFairing.inlineHeight > 0); bool topClosed = false; Matrix4x4 w2l = Matrix4x4.identity, w2lb = Matrix4x4.identity; Bounds topBounds = default(Bounds); if (isInline) { w2l = part.transform.worldToLocalMatrix; w2lb = w2l; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { w2lb [i, j] = Mathf.Abs(w2lb [i, j]); } } topBounds = new Bounds(new Vector3(0, boundCylY1, 0), new Vector3(sideFairing.topRad * 2, sideFairing.sideThickness, sideFairing.topRad * 2)); } for (int pi = 0; pi < parts.Count; ++pi) { var pt = parts [pi]; // Check special cases. if (pt == part) { shieldedParts.Add(pt); continue; } bool isSide = false; for (int i = 0; i < attached.Length; ++i) { if (attached [i].attachedPart == pt) { isSide = true; break; } } if (isSide) { continue; } // Check if the top is closed in the inline case. var bounds = pt.GetRendererBounds(); var box = PartGeometryUtil.MergeBounds(bounds, pt.transform); if (isInline && !topClosed && pt.vessel == vessel) { var wb = box; wb.Expand(sideFairing.sideThickness * 4); var b = new Bounds(w2l.MultiplyPoint3x4(wb.center), w2lb.MultiplyVector(wb.size)); if (b.Contains(topBounds.min) && b.Contains(topBounds.max)) { topClosed = true; } } // Check if the centroid is within the fairing bounds. var c = part.transform.InverseTransformPoint(PartGeometryUtil.FindBoundsCentroid(bounds, null)); float y = c.y; if (y < boundCylY0 || y > boundCylY1) { continue; } float xsq = new Vector2(c.x, c.z).sqrMagnitude; if (xsq > boundCylRadSq) { continue; } // Accurate centroid check. float x = Mathf.Sqrt(xsq); bool inside = false; for (int i = 1; i < shape.Length; ++i) { var p0 = shape [i - 1]; var p1 = shape [i]; if (p0.y > p1.y) { var p = p0; p0 = p1; p1 = p; } if (y < p0.y || y > p1.y) { continue; } float dy = p1.y - p0.y, r; if (dy <= 1e-6f) { r = (p0.x + p1.x) * 0.5f; } else { r = (p1.x - p0.x) * (y - p0.y) / dy + p0.x; } if (x > r) { continue; } inside = true; break; } if (!inside) { continue; } shieldedParts.Add(pt); } if (isInline && !topClosed) { disableShielding(); return; } // Add shielding. for (int i = 0; i < shieldedParts.Count; ++i) { shieldedParts [i].AddShield(this); } numShieldedDisplay = shieldedParts.Count; var fbase = part.GetComponent <ProceduralFairingBase>(); if (fbase != null) { fbase.onShieldingEnabled(shieldedParts); } }