public void recalcShape() { var scan = scanPayload(); // Check for reversed bases (inline fairings). float topY = 0; float topRad = 0; AttachNode topSideNode = null; bool isInline = false; var adapter = part.GetComponent <ProceduralFairingAdapter>(); if (adapter) { isInline = true; topY = adapter.height + adapter.extraHeight; if (topY < scan.ofs) { topY = scan.ofs; } topRad = adapter.topRadius; } else if (scan.targets.Count > 0) { isInline = true; var topBase = scan.targets [0].GetComponent <ProceduralFairingBase>(); topY = scan.w2l.MultiplyPoint3x4(topBase.part.transform.position).y; if (topY < scan.ofs) { topY = scan.ofs; } topSideNode = HasNodeComponent <ProceduralFairingSide>(topBase.part.FindAttachNodes("connect")); topRad = topBase.baseSize * 0.5f; } // No payload case. if (scan.profile.Count <= 0) { scan.profile.Add(extraRadius); } // Fill profile outline (for debugging). if (line) { line.positionCount = scan.profile.Count * 2 + 2; float prevRad = 0; int hi = 0; for (int i = 0; i < scan.profile.Count; i++) { var r = scan.profile [i]; line.SetPosition(hi * 2, new Vector3(prevRad, hi * verticalStep + scan.ofs, 0)); line.SetPosition(hi * 2 + 1, new Vector3(r, hi * verticalStep + scan.ofs, 0)); hi++; prevRad = r; } line.SetPosition(hi * 2, new Vector3(prevRad, hi * verticalStep + scan.ofs, 0)); line.SetPosition(hi * 2 + 1, new Vector3(0, hi * verticalStep + scan.ofs, 0)); } // Check for attached side parts. var attached = part.FindAttachNodes("connect"); // Get the number of available fairing attachment nodes from NodeNumberTweaker. var nnt = part.GetComponent <KzNodeNumberTweaker>(); int numSideParts = nnt.numNodes; var sideNode = HasNodeComponent <ProceduralFairingSide>(attached); var baseConeShape = new Vector4(0, 0, 0, 0); var noseConeShape = new Vector4(0, 0, 0, 0); var mappingScale = new Vector2(1024, 1024); var stripMapping = new Vector2(992, 1024); var horMapping = new Vector4(0, 480, 512, 992); var vertMapping = new Vector4(0, 160, 704, 1024); float baseCurveStartX = 0; float baseCurveStartY = 0; float baseCurveEndX = 0; float baseCurveEndY = 0; int baseConeSegments = 1; float noseCurveStartX = 0; float noseCurveStartY = 0; float noseCurveEndX = 0; float noseCurveEndY = 0; int noseConeSegments = 1; float noseHeightRatio = 1; float minBaseConeAngle = 20; float density = 0; if (sideNode != null) { var sf = sideNode.attachedPart.GetComponent <ProceduralFairingSide>(); mappingScale = sf.mappingScale; stripMapping = sf.stripMapping; horMapping = sf.horMapping; vertMapping = sf.vertMapping; noseHeightRatio = sf.noseHeightRatio; minBaseConeAngle = sf.minBaseConeAngle; baseConeShape = sf.baseConeShape; baseCurveStartX = sf.baseCurveStartX; baseCurveStartY = sf.baseCurveStartY; baseCurveEndX = sf.baseCurveEndX; baseCurveEndY = sf.baseCurveEndY; baseConeSegments = (int)sf.baseConeSegments; noseConeShape = sf.noseConeShape; noseCurveStartX = sf.noseCurveStartX; noseCurveStartY = sf.noseCurveStartY; noseCurveEndX = sf.noseCurveEndX; noseCurveEndY = sf.noseCurveEndY; noseConeSegments = (int)sf.noseConeSegments; density = sf.density; } // Compute the fairing shape. float baseRad = baseSize * 0.5f; float minBaseConeTan = Mathf.Tan(minBaseConeAngle * Mathf.Deg2Rad); float cylStart = 0; float maxRad; int profTop = scan.profile.Count; if (isInline) { profTop = Mathf.CeilToInt((topY - scan.ofs) / verticalStep); if (profTop > scan.profile.Count) { profTop = scan.profile.Count; } maxRad = 0; for (int i = 0; i < profTop; ++i) { maxRad = Mathf.Max(maxRad, scan.profile [i]); } maxRad = Mathf.Max(maxRad, topRad); } else { maxRad = PFUtils.GetMaxValueFromList(scan.profile); } if (maxRad > baseRad) { // Try to fit the base cone as high as possible. cylStart = scan.ofs; for (int i = 1; i < scan.profile.Count; ++i) { float y = i * verticalStep + scan.ofs; float r0 = baseRad; float k = (maxRad - r0) / y; if (k < minBaseConeTan) { break; } bool ok = true; float r = r0 + k * scan.ofs; for (int j = 0; j < i; ++j, r += k * verticalStep) { if (scan.profile [j] > r) { ok = false; break; } } if (!ok) { break; } cylStart = y; } } else { // No base cone, just a cylinder and a nose. maxRad = baseRad; } float cylEnd = scan.profile.Count * verticalStep + scan.ofs; if (isInline) { float r0 = topRad; if (profTop > 0 && profTop < scan.profile.Count) { r0 = Mathf.Max(r0, scan.profile [profTop - 1]); if (profTop - 2 >= 0) { r0 = Mathf.Max(r0, scan.profile [profTop - 2]); } } if (maxRad > r0) { if (cylEnd > topY) { cylEnd = topY - verticalStep; } // Try to fit the top cone as low as possible. for (int i = profTop - 1; i >= 0; --i) { float y = i * verticalStep + scan.ofs; float k = (maxRad - r0) / (y - topY); bool ok = true; float r = maxRad + k * verticalStep; for (int j = i; j < profTop; ++j, r += k * verticalStep) { if (r < r0) { r = r0; } if (scan.profile [j] > r) { ok = false; break; } } if (!ok) { break; } cylEnd = y; } } else { cylEnd = topY; } } else { // Try to fit the nose cone as low as possible. for (int i = scan.profile.Count - 1; i >= 0; --i) { float s = verticalStep / noseHeightRatio; bool ok = true; float r = maxRad - s; for (int j = i; j < scan.profile.Count; ++j, r -= s) { if (scan.profile [j] > r) { ok = false; break; } } if (!ok) { break; } float y = i * verticalStep + scan.ofs; cylEnd = y; } } if (autoShape) { manualMaxSize = maxRad * 2; manualCylStart = cylStart; manualCylEnd = cylEnd; } else { maxRad = manualMaxSize * 0.5f; cylStart = manualCylStart; cylEnd = manualCylEnd; } if (cylStart > cylEnd) { cylStart = cylEnd; } // Build the fairing shape line. Vector3 [] shape; if (isInline) { shape = buildInlineFairingShape(baseRad, maxRad, topRad, cylStart, cylEnd, topY, baseConeShape, baseConeSegments, vertMapping, mappingScale.y); } else { shape = buildFairingShape(baseRad, maxRad, cylStart, cylEnd, noseHeightRatio, baseConeShape, noseConeShape, baseConeSegments, noseConeSegments, vertMapping, mappingScale.y); } if (sideNode == null && topSideNode == null) { // No side parts - fill fairing outlines. for (int j = 0; j < outline.Count; j++) { var lr = outline [j]; lr.positionCount = shape.Length; for (int i = 0; i < shape.Length; ++i) { lr.SetPosition(i, new Vector3(shape [i].x, shape [i].y)); } } } else { for (int j = 0; j < outline.Count; j++) { var lr = outline [j]; lr.positionCount = 0; } } // Rebuild the side parts. int numSegs = circleSegments / numSideParts; if (numSegs < 2) { numSegs = 2; } for (int i = 0; i < attached.Length; i++) { var sn = attached [i]; var sp = sn.attachedPart; if (!sp) { continue; } var sf = sp.GetComponent <ProceduralFairingSide>(); if (!sf) { continue; } if (sf.shapeLock) { continue; } var mf = sp.FindModelComponent <MeshFilter>("model"); if (!mf) { Debug.LogError("[PF]: No model in side fairing!", sp); continue; } var nodePos = sn.position; mf.transform.position = part.transform.position; mf.transform.rotation = part.transform.rotation; float ra = Mathf.Atan2(-nodePos.z, nodePos.x) * Mathf.Rad2Deg; mf.transform.Rotate(0, ra, 0); if (sf.meshPos == mf.transform.localPosition && sf.meshRot == mf.transform.localRotation && sf.numSegs == numSegs && sf.numSideParts == numSideParts && sf.baseRad.Equals(baseRad) && sf.maxRad.Equals(maxRad) && sf.cylStart.Equals(cylStart) && sf.cylEnd.Equals(cylEnd) && sf.topRad.Equals(topRad) && sf.inlineHeight.Equals(topY) && sf.sideThickness.Equals(sideThickness) && !sf.baseCurveStartX.Equals(baseCurveStartX) && !sf.baseCurveStartY.Equals(baseCurveStartY) && !sf.baseCurveEndX.Equals(baseCurveEndX) && !sf.baseCurveEndY.Equals(baseCurveEndY) && !sf.baseConeSegments.Equals(baseConeSegments) && !sf.noseCurveStartX.Equals(noseCurveStartX) && !sf.noseCurveStartY.Equals(noseCurveStartY) && !sf.noseCurveEndX.Equals(noseCurveEndX) && !sf.noseCurveEndY.Equals(noseCurveEndY) && !sf.noseConeSegments.Equals(noseConeSegments) && !sf.noseHeightRatio.Equals(noseHeightRatio) && !sf.density.Equals(density)) { continue; } sf.meshPos = mf.transform.localPosition; sf.meshRot = mf.transform.localRotation; sf.numSegs = numSegs; sf.numSideParts = numSideParts; sf.baseRad = baseRad; sf.maxRad = maxRad; sf.cylStart = cylStart; sf.cylEnd = cylEnd; sf.topRad = topRad; sf.inlineHeight = topY; sf.sideThickness = sideThickness; sf.baseCurveStartX = baseCurveStartX; sf.baseCurveStartY = baseCurveStartY; sf.baseCurveEndX = baseCurveEndX; sf.baseCurveEndY = baseCurveEndY; sf.baseConeSegments = baseConeSegments; sf.noseCurveStartX = noseCurveStartX; sf.noseCurveStartY = noseCurveStartY; sf.noseCurveEndX = noseCurveEndX; sf.noseCurveEndY = noseCurveEndY; sf.noseConeSegments = noseConeSegments; sf.noseHeightRatio = noseHeightRatio; sf.density = density; sf.rebuildMesh(); } var shielding = part.GetComponent <KzFairingBaseShielding>(); if (shielding) { shielding.reset(); } }