/// <summary> /// These are just a few different attempts to figure drag for various blunt bodies /// </summary> private void DragModel(Vector3d local_velocity, double M, double rho) { // Has the same x/y/z as the vertices in PartMaxBoundaries etc Vector3d model_velocity = to_model_rotation * local_velocity; double viscousLift, potentialLift, newtonianLift; viscousLift = potentialLift = newtonianLift = 0; double CdAdd = 0; //float AxialProportion = Vector3.Dot(localUpVector, local_velocity); double AxialProportion = model_velocity.y; float AxialProportion_flt = (float)model_velocity.y; double AxialProportion_2 = AxialProportion * AxialProportion; double OneMinusAxial_2 = Math.Abs(1 - AxialProportion_2); double M_2 = M * M; double M_2_recip = 1 / M_2; double maxPressureCoeff; if (FARDebugValues.useSplinesForSupersonicMath) { maxPressureCoeff = FARAeroUtil.MaxPressureCoefficient.Evaluate((float)M); } else { maxPressureCoeff = FARAeroUtil.MaxPressureCoefficientCalc(M); } double sepFlowCd = SeparatedFlowDrag(M, M_2, M_2_recip); //This handles elliptical and other non-circular cross sections //float crossflowParameter = Vector3.Dot(localForwardVector, Vector3.Exclude(localUpVector, local_velocity).normalized); //crossflowParameter *= crossflowParameter; double crossflowParameter = model_velocity.z * model_velocity.z; double crossflow = model_velocity.x * model_velocity.x + crossflowParameter; if (crossflow != 0) { crossflowParameter /= crossflow; } crossflowParameter = crossflowParameter * majorMinorAxisRatio + (1 - crossflowParameter) / majorMinorAxisRatio; if (AxialProportion_2 > 0.98) { crossflowParameter *= 50 * OneMinusAxial_2; } Cd += CdCurve.Evaluate(AxialProportion_flt); viscousLift = ClViscousCurve.Evaluate(AxialProportion_flt); double axialDirectionFactor = cosAngleCutoff * AxialProportion; if (axialDirectionFactor > 0) { Cd = Math.Min(Cd, sepFlowCd * taperCrossSectionAreaRatio) * AxialProportion_2 + Cd * OneMinusAxial_2; } else { Cd = Math.Min(Cd, maxPressureCoeff * taperCrossSectionAreaRatio) * AxialProportion_2 + Cd * OneMinusAxial_2; } if (M_2 > 1) { potentialLift = ClPotentialCurve.Evaluate(AxialProportion_flt) * M_2_recip; } else { potentialLift = ClPotentialCurve.Evaluate(AxialProportion_flt); } Cm = CmCurve.Evaluate(AxialProportion_flt) * 0.1; CoDshift = CenterOfDrag; double MachMultiplier = MachDragEffect(M); Cd *= MachMultiplier; if (HighLogic.LoadedSceneIsFlight) { Cd += FARAeroUtil.SkinFrictionDrag(rho, lengthScale, local_velocity.magnitude, M, FlightGlobals.getExternalTemperature(part.transform.position) + FARAeroUtil.currentBodyTemp); //Skin friction drag } else { Cd += 0.005; } for (int i = 0; i < attachNodeDragList.Count; i++) { attachNodeData node = attachNodeDragList[i]; double dotProd = Vector3d.Dot(node.location.normalized, local_velocity); double tmp = 0; double Cltmp = 0; if (dotProd < 0) { dotProd *= dotProd; tmp = sepFlowCd; // Cltmp = tmp * (dotProd - 1); // Cltmp *= pair.Value; tmp *= node.areaValue * dotProd; // Vector3 CoDshiftOffset = -Vector3.Exclude(pair.Key, part.transform.worldToLocalMatrix.MultiplyVector(velocity.normalized)).normalized; // CoDshiftOffset *= Mathf.Sqrt(Mathf.Clamp01(1 - dotProd)); // CoDshiftOffset *= Mathf.Sqrt(1.5f * pair.Value); CoDshift += node.location * (tmp / (tmp + Cd)); } else { Vector3d worldPairVec = part_transform.TransformDirection(node.location.normalized); double dotProd_2 = dotProd * dotProd; double liftProd_2 = 1 - dotProd_2; double liftProd = Vector3d.Dot(worldPairVec, liftDir); double forceCoefficient = dotProd_2 * bluntBodyCosForceParameter; forceCoefficient += liftProd_2 * bluntBodySinForceParameter; forceCoefficient *= maxPressureCoeff * node.areaValue; //force applied perependicular to the flat end of the node tmp = forceCoefficient * dotProd; Cltmp = -forceCoefficient * liftProd; //negative because lift is in opposite direction of projection of velocity vector onto node direction double Cmtmp = dotProd * liftProd * bluntBodyMomentParameter; Cmtmp *= node.areaValue * node.recipDiameter; if (!node.pitchesAwayFromUpVec) { Cmtmp *= -1; } Cm += Cmtmp; double tmpCdCl = Math.Sqrt(tmp * tmp + Cltmp * Cltmp); CoDshift += node.location * (tmpCdCl / (tmpCdCl + Math.Sqrt(Cd * Cd + Cl * Cl))); /*double liftProd = Vector3d.Dot(worldPairVec, liftDir); * * tmp = maxPressureCoeff * dotProd_2 * dotProd; * tmp *= node.areaValue; * * Cltmp = maxPressureCoeff * dotProd_2 * liftProd; * Cltmp *= -node.areaValue; * * double radius = Math.Sqrt(node.areaValue / Math.PI); * Vector3 CoDshiftOffset = Vector3.Exclude(node.location, local_velocity).normalized; * CoDshiftOffset *= (float)(Math.Abs(liftProd) * radius * 0.4); * * double Cmtmp; * if (node.pitchesAwayFromUpVec) * Cmtmp = -0.325 * radius * node.areaValue / S * Math.Abs(liftProd); * else * Cmtmp = 0.325 * radius * node.areaValue / S * Math.Abs(liftProd); * * double tmpCdCl = Math.Sqrt(tmp * tmp + Cltmp * Cltmp); * * CoDshift += node.location * (tmpCdCl / (tmpCdCl + Math.Sqrt(Cd * Cd + Cl * Cl))) + CoDshiftOffset; * * Cm += Cmtmp;*/ } CdAdd += tmp; newtonianLift += Cltmp; } viscousLift *= MachMultiplier; Cd += CdAdd; Cl = viscousLift + potentialLift; Cl *= crossflowParameter; Cm *= crossflowParameter; Cl += newtonianLift; // Debug.Log("Cd = " + Cd + " Cl = " + Cl + " Cm = " + Cm + "\nPot Lift = " + potentialLift + " Visc Lift = " + viscousLift + " Newt Lift = " + newtonianLift + "\nCdAdd = " + CdAdd + " sepFlowCd = " + sepFlowCd + " maxPressureCoeff = " + maxPressureCoeff + "\ntaperCrossSectionAreaRatio = " + taperCrossSectionAreaRatio + " crossflowParameter = " + crossflowParameter); }
private void AttachNodeCdAdjust() { if (part.Modules.Contains("FARPayloadFairingModule")) //This doesn't apply blunt drag drag to fairing parts if one of their "exempt" attach nodes is used, indicating attached fairings { return; } if (VesselPartList == null) { UpdateShipPartsList(); } if (attachNodeDragList == null) { attachNodeDragList = new List <attachNodeData>(); } attachNodeDragList.Clear(); Transform transform = part.partTransform; if (transform == null) { transform = part.transform; } if (transform == null) { Debug.LogError("Part " + part.partInfo.title + " has null transform; drag interactions cannot be applied."); return; } SPlusAttachArea = S; Vector3d partUpVector = transform.TransformDirection(localUpVector); Bounds[] rendererBounds = part.GetRendererBounds(); //print("Updating drag for " + part.partInfo.title); foreach (AttachNode Attach in part.attachNodes) { if (Attach.nodeType == AttachNode.NodeType.Stack) { if (Attach.id.ToLowerInvariant() == "strut") { continue; } Vector3d relPos = Attach.position + Attach.offset; if (part.Modules.Contains("FARCargoBayModule")) { FARCargoBayModule bay = (FARCargoBayModule)part.Modules["FARCargoBayModule"]; Vector3d maxBounds = bay.maxBounds; Vector3d minBounds = bay.minBounds; if (relPos.x < maxBounds.x && relPos.y < maxBounds.y && relPos.z < maxBounds.z && relPos.x > minBounds.x && relPos.y > minBounds.y && relPos.z > minBounds.z) { continue; } } if (Attach.attachedPart != null) { if (AttachedPartIsNotClipping(Attach.attachedPart, rendererBounds)) { continue; } } Vector3d origToNode = transform.localToWorldMatrix.MultiplyVector(relPos); double attachSize = FARMathUtil.Clamp(Attach.size, 0.5, double.PositiveInfinity); if (UnattachedPartRightAgainstNode(origToNode, attachSize, relPos, Attach.attachedPart)) { continue; } attachNodeData newAttachNodeData = new attachNodeData(); double exposedAttachArea = attachSize * FARAeroUtil.attachNodeRadiusFactor; newAttachNodeData.recipDiameter = 1 / (2 * exposedAttachArea); exposedAttachArea *= exposedAttachArea; exposedAttachArea *= Math.PI * FARAeroUtil.areaFactor; SPlusAttachArea += exposedAttachArea; exposedAttachArea /= FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); newAttachNodeData.areaValue = exposedAttachArea; if (Vector3d.Dot(origToNode, partUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } newAttachNodeData.location = transform.worldToLocalMatrix.MultiplyVector(origToNode); attachNodeDragList.Add(newAttachNodeData); } } }
private void AttachNodeCdAdjust() { if (part.Modules.Contains("FARPayloadFairingModule")) //This doesn't apply blunt drag drag to fairing parts if one of their "exempt" attach nodes is used, indicating attached fairings { return; } if (VesselPartList == null) { UpdateShipPartsList(); } if (attachNodeDragList == null) { attachNodeDragList = new List <attachNodeData>(); } attachNodeDragList.Clear(); Transform transform = part.transform; if (transform == null) { transform = part.transform; } if (transform == null) { Debug.LogError("Part " + part.partInfo.title + " has null transform; drag interactions cannot be applied."); return; } SPlusAttachArea = S; Vector3d partUpVector = transform.TransformDirection(localUpVector); //print("Updating drag for " + part.partInfo.title); foreach (AttachNode attach in part.attachNodes) { if (attach.nodeType == AttachNode.NodeType.Stack) { if (attach.id.ToLowerInvariant() == "strut") { continue; } Vector3d relPos = attach.position;// +Attach.offset; if (part.Modules.Contains("FARCargoBayModule")) { FARCargoBayModule bay = (FARCargoBayModule)part.Modules["FARCargoBayModule"]; if (bay.bayBounds.Contains(relPos)) { continue; } } Vector3d origToNode = transform.localToWorldMatrix.MultiplyVector(relPos); double attachSize = FARMathUtil.Clamp(attach.size, 0.5, double.PositiveInfinity); if (attach.attachedPart != null) { Vector3 location = attach.attachedPart.transform.position; FARBasicDragModel d = attach.attachedPart.GetComponent <FARBasicDragModel>(); if (d != null) { location += attach.attachedPart.transform.localToWorldMatrix.MultiplyVector(d.CenterOfDrag); } //Debug.Log(Attach.attachedPart.partInfo.title + " " + location + " " + Attach.attachedPart.transform.position + " " + (origToNode + part.transform.position)); if (AttachedPartCoDIsFurtherThanAttachLocation(location, origToNode)) { continue; } if (AttachedPartIsNotClipping(location, PartBounds)) { if (d != null) { location = this.part.transform.localToWorldMatrix.MultiplyVector(this.CenterOfDrag); location += this.part.transform.position; if (d.AttachedPartIsNotClipping(location, d.PartBounds)) { continue; } } else { continue; } } //this is a bit of a hack to make intakes function slightly better, since very thin ones seem to have issues. ModuleResourceIntake intake = attach.attachedPart.GetComponent <ModuleResourceIntake>(); if (intake != null) { Transform intakeTrans = attach.attachedPart.FindModelTransform(intake.intakeTransformName); if ((object)intakeTrans != null) { Vector3 intakeForwardVec = (intakeTrans.forward); //Vector3 attachOrientation = this.part.transform.localToWorldMatrix.MultiplyVector(attach.orientation); int intakeOrientSign = Math.Sign(Vector3.Dot((Vector3)origToNode, intakeForwardVec)); //int attachLocSign = Math.Sign(Vector3.Dot(attachOrientation, (Vector3)origToNode)); //int intakeOrientationSign = Math.Sign(Vector3.Dot(intakeForwardVec, (Vector3)origToNode)); if (intakeOrientSign > 0) { continue; } } } } if (UnattachedPartRightAgainstNode(origToNode, attachSize, attach.attachedPart)) { continue; } attachNodeData newAttachNodeData = new attachNodeData(); double exposedAttachArea = attachSize * FARAeroUtil.attachNodeRadiusFactor; newAttachNodeData.recipDiameter = 1 / (2 * exposedAttachArea); exposedAttachArea *= exposedAttachArea; exposedAttachArea *= Math.PI * FARAeroUtil.areaFactor; SPlusAttachArea += exposedAttachArea; exposedAttachArea /= FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); newAttachNodeData.areaValue = exposedAttachArea; if (Vector3d.Dot(origToNode, partUpVector) > 0) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } newAttachNodeData.location = relPos; //Debug.Log(part.partInfo.title + " found open node"); attachNodeDragList.Add(newAttachNodeData); } } }
private void UpdateNonAttachBluntEnds(Dictionary <string, List <AttachNode> > attachNodeGroups, Dictionary <string, AttachGroupType> attachNodeType, Matrix4x4 partUpMatrix, Transform[] ModelTransforms, Transform[] VesselModelTransforms) { bool upperEndHandled = false; bool lowerEndHandled = false; Vector2 verticalPartMeshBounds = FARGeoUtil.PartLengthBounds(part, Vector3.zero, partUpMatrix, ModelTransforms); foreach (KeyValuePair <string, List <AttachNode> > pair in attachNodeGroups) { Vector3 avgPos = Vector3.zero; Vector3 avgOrient = Vector3.zero; foreach (AttachNode node in pair.Value) { avgPos += node.position; avgOrient += node.orientation; } avgPos /= pair.Value.Count; avgOrient.Normalize(); if (Mathf.Abs(avgPos.y - verticalPartMeshBounds.x) <= 0.3f) { upperEndHandled = true; } if (Mathf.Abs(avgPos.y - verticalPartMeshBounds.y) <= 0.3f) { lowerEndHandled = true; } if (upperEndHandled && lowerEndHandled) { break; } } if (!upperEndHandled) { Vector3 position = Vector3.up * verticalPartMeshBounds.x; Vector2 bounds = FARGeoUtil.NodeBoundaries(part, position, position, Vector3.zero, 0.05f, ModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = position; if (Vector3d.Dot(orientation, localUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else { attachNodeDragDict.Add(orientation, newAttachNodeData); } } if (!lowerEndHandled) { Vector3 position = Vector3.up * verticalPartMeshBounds.y; Vector2 bounds = FARGeoUtil.NodeBoundaries(part, position, position, Vector3.zero, 0.05f, ModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = position; if (Vector3d.Dot(orientation, localUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else { attachNodeDragDict.Add(orientation, newAttachNodeData); } } }
private void AttachNodeCdAdjust() { //BaseCd = 0; // if (!part.Modules.Contains("FARPayloadFairingModule")) // { if (part.Modules.Contains("FARPayloadFairingModule")) //This doesn't apply blunt drag drag to fairing parts if one of their "exempt" attach nodes is used, indicating attached fairings { return; } if (VesselPartList == null) { UpdateShipPartsList(); } if (attachNodeDragDict == null) { attachNodeDragDict = new Dictionary <Vector3d, attachNodeData>(); } attachNodeDragDict.Clear(); Transform transform = part.partTransform; Vector3d partUpVector = transform.TransformDirection(localUpVector); //print("Updating drag for " + part.partInfo.title); foreach (AttachNode Attach in part.attachNodes) { if (Attach.nodeType == AttachNode.NodeType.Stack) { if (Attach.attachedPart != null) { continue; } if (Attach.id.ToLowerInvariant() == "strut") { continue; } /* string attachId = Attach.id.ToLowerInvariant(); * bool leaveAttachLoop = false; * foreach (string s in FARMiscData.exemptAttachNodes) * if (attachId.Contains(s)) * { * leaveAttachLoop = true; * break; * } * if (leaveAttachLoop) * continue;*/ Ray ray = new Ray(); Vector3d relPos = Attach.position + Attach.offset; if (part.Modules.Contains("FARCargoBayModule")) { FARCargoBayModule bay = (FARCargoBayModule)part.Modules["FARCargoBayModule"]; Vector3d maxBounds = bay.maxBounds; Vector3d minBounds = bay.minBounds; if (relPos.x < maxBounds.x && relPos.y < maxBounds.y && relPos.z < maxBounds.z && relPos.x > minBounds.x && relPos.y > minBounds.y && relPos.z > minBounds.z) { return; } } Vector3d origToNode = transform.localToWorldMatrix.MultiplyVector(relPos); double mag = (origToNode).magnitude; //print(part.partInfo.title + " Part Loc: " + part.transform.position + " Attach Loc: " + (origToNode + part.transform.position) + " Dist: " + mag); ray.direction = origToNode; ray.origin = transform.position; double attachSize = FARMathUtil.Clamp(Attach.size, 0.5, double.PositiveInfinity); bool gotIt = false; RaycastHit[] hits = Physics.RaycastAll(ray, (float)(mag + attachSize), FARAeroUtil.RaycastMask); foreach (RaycastHit h in hits) { if (h.collider == part.collider) { continue; } if (h.distance < (mag + attachSize) && h.distance > (mag - attachSize)) { foreach (Part p in VesselPartList) { if (p.collider == h.collider) { gotIt = true; break; } } } if (gotIt) { break; } } if (!gotIt) { // float exposedAttachArea = (Mathf.PI * Mathf.Pow(attachSize * FARAeroUtil.attachNodeRadiusFactor, 2) / Mathf.Clamp(S, 0.01f, Mathf.Infinity)); double exposedAttachArea = attachSize * FARAeroUtil.attachNodeRadiusFactor; exposedAttachArea *= exposedAttachArea; exposedAttachArea *= Math.PI * FARAeroUtil.areaFactor; exposedAttachArea /= FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = exposedAttachArea; if (Vector3d.Dot(origToNode, partUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(transform.worldToLocalMatrix.MultiplyVector(origToNode))) { attachNodeData tmp = attachNodeDragDict[transform.worldToLocalMatrix.MultiplyVector(origToNode)]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[transform.worldToLocalMatrix.MultiplyVector(origToNode)] = tmp; } else { attachNodeDragDict.Add(part.transform.worldToLocalMatrix.MultiplyVector(origToNode), newAttachNodeData); } } } } // } //print(part.partInfo.title + " Num unused Attach Nodes: " + attachNodeDragDict.Count); }
private void UpdateAttachGroupDrag(List <AttachNode> attachNodeGroup, AttachGroupType attachGroupType, Transform[] ThisPartModelTransforms, Transform[] AllVesselModelTransforms) { //This is standard single node; the blunt area is to be determined by everything near it if (attachGroupType == AttachGroupType.INDEPENDENT_NODE) { //Get area represented by current node Vector2 bounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, ThisPartModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5f; opposedArea *= opposedArea; opposedArea *= Math.PI; //Calculate area based on circular cross-section //Debug.Log(part.partInfo.title + " Area: " + area + " Opposed area: " + opposedArea + " OtherTransforms: " + OtherTransforms.Length); area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = attachNodeGroup[0].position; if (Vector3d.Dot(orientation, localUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else { attachNodeDragDict.Add(orientation, newAttachNodeData); } return; } //This is a group of nodes that can be used for clustered tanks/engines; the area calculated must be divided over them else if (attachGroupType == AttachGroupType.PARALLEL_NODES) { //Get area represented by current nodes Vector2 bounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, ThisPartModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5; opposedArea *= opposedArea; opposedArea *= Mathf.PI; //Calculate area based on circular cross-section //Debug.Log(part.partInfo.title + " Area: " + area + " Opposed area: " + opposedArea + " OtherTransforms: " + OtherTransforms.Length); area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = Vector3.zero; foreach (AttachNode attach in attachNodeGroup) { orientation += attach.position; } orientation /= attachNodeGroup.Count; if (Vector3d.Dot(orientation, localUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else { attachNodeDragDict.Add(orientation, newAttachNodeData); } return; } //This represents a vertical stack of nodes, for different payload heights, multiple payload fairings, etc. One node being used means that they are all used else if (attachGroupType == AttachGroupType.VERTICAL_NODES) { double area = 0; AttachNode usedNode; Vector2 bounds = Vector2.zero; Vector3 orientation = Vector3.zero; foreach (AttachNode attach in attachNodeGroup) { //Get area represented by current node bounds = FARGeoUtil.NodeBoundaries(part, attach, Vector3.zero, 0.05f, ThisPartModelTransforms); double tmpArea = (bounds.x + bounds.y); tmpArea *= 0.5; tmpArea *= tmpArea; tmpArea *= Math.PI; //Calculate area based on circular cross-section if (tmpArea > area) { area = tmpArea; usedNode = attach; } orientation += attach.position; } //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5; opposedArea *= opposedArea; opposedArea *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); orientation /= attachNodeGroup.Count; if (Vector3d.Dot(orientation, localUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else { attachNodeDragDict.Add(orientation, newAttachNodeData); } return; } }
/* private Dictionary<string, List<AttachNode>> GetAttachNodeGroups() { Dictionary<string, List<AttachNode>> attachNodeGroups = new Dictionary<string, List<AttachNode>>(); List<AttachNode> rawNodeList = part.attachNodes; foreach (AttachNode attach in rawNodeList) { if (attach.nodeType == AttachNode.NodeType.Stack) { string attachName = attach.id; attachName = Regex.Match(attachName, @"^[^\d]+").Value; List<AttachNode> tmpAttachNodeList; bool groupExists = attachNodeGroups.TryGetValue(attachName, out tmpAttachNodeList); if (groupExists) { tmpAttachNodeList.Add(attach); attachNodeGroups[attachName] = tmpAttachNodeList; } else { tmpAttachNodeList = new List<AttachNode>(); tmpAttachNodeList.Add(attach); attachNodeGroups[attachName] = tmpAttachNodeList; } } } return attachNodeGroups; } private Dictionary<string, AttachGroupType> GetAttachGroupTypes(Dictionary<string, List<AttachNode>> attachNodeGroups) { Dictionary<string, AttachGroupType> attachGroupTypes = new Dictionary<string, AttachGroupType>(); foreach (KeyValuePair<string, List<AttachNode>> nodeGroup in attachNodeGroups) { //A single node in the group means that it is an independent node if (nodeGroup.Value.Count <= 1) { attachGroupTypes.Add(nodeGroup.Key, AttachGroupType.INDEPENDENT_NODE); continue; } Vector3 avgPos = Vector3.zero; foreach (AttachNode attach in nodeGroup.Value) { avgPos += attach.position; } avgPos /= nodeGroup.Value.Count; float groupingFactor = 0; foreach (AttachNode attach in nodeGroup.Value) { groupingFactor += Mathf.Abs(Vector3.Dot(attach.position - avgPos, attach.orientation)); } groupingFactor /= nodeGroup.Value.Count; if (groupingFactor > 0.8f) //Node group is mainly aligned; vertical node group { attachGroupTypes.Add(nodeGroup.Key, AttachGroupType.VERTICAL_NODES); continue; } else { attachGroupTypes.Add(nodeGroup.Key, AttachGroupType.PARALLEL_NODES); continue; } } return attachGroupTypes; } private void UpdateAttachGroupDrag(List<AttachNode> attachNodeGroup, AttachGroupType attachGroupType, Transform[] ThisPartModelTransforms, Transform[] AllVesselModelTransforms) { //This is standard single node; the blunt area is to be determined by everything near it if (attachGroupType == AttachGroupType.INDEPENDENT_NODE) { //Get area represented by current node Vector2 bounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, ThisPartModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5f; opposedArea *= opposedArea; opposedArea *= Math.PI; //Calculate area based on circular cross-section //Debug.Log(part.partInfo.title + " Area: " + area + " Opposed area: " + opposedArea + " OtherTransforms: " + OtherTransforms.Length); area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = attachNodeGroup[0].position; if (Vector3d.Dot(orientation, localUpVector) > 1) newAttachNodeData.pitchesAwayFromUpVec = true; else newAttachNodeData.pitchesAwayFromUpVec = false; if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else attachNodeDragDict.Add(orientation, newAttachNodeData); return; } //This is a group of nodes that can be used for clustered tanks/engines; the area calculated must be divided over them else if (attachGroupType == AttachGroupType.PARALLEL_NODES) { //Get area represented by current nodes Vector2 bounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, ThisPartModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5; opposedArea *= opposedArea; opposedArea *= Mathf.PI; //Calculate area based on circular cross-section //Debug.Log(part.partInfo.title + " Area: " + area + " Opposed area: " + opposedArea + " OtherTransforms: " + OtherTransforms.Length); area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = Vector3.zero; foreach (AttachNode attach in attachNodeGroup) { orientation += attach.position; } orientation /= attachNodeGroup.Count; if (Vector3d.Dot(orientation, localUpVector) > 1) newAttachNodeData.pitchesAwayFromUpVec = true; else newAttachNodeData.pitchesAwayFromUpVec = false; if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else attachNodeDragDict.Add(orientation, newAttachNodeData); return; } //This represents a vertical stack of nodes, for different payload heights, multiple payload fairings, etc. One node being used means that they are all used else if (attachGroupType == AttachGroupType.VERTICAL_NODES) { double area = 0; AttachNode usedNode; Vector2 bounds = Vector2.zero; Vector3 orientation = Vector3.zero; foreach (AttachNode attach in attachNodeGroup) { //Get area represented by current node bounds = FARGeoUtil.NodeBoundaries(part, attach, Vector3.zero, 0.05f, ThisPartModelTransforms); double tmpArea = (bounds.x + bounds.y); tmpArea *= 0.5; tmpArea *= tmpArea; tmpArea *= Math.PI; //Calculate area based on circular cross-section if (tmpArea > area) { area = tmpArea; usedNode = attach; } orientation += attach.position; } //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5; opposedArea *= opposedArea; opposedArea *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); orientation /= attachNodeGroup.Count; if (Vector3d.Dot(orientation, localUpVector) > 1) newAttachNodeData.pitchesAwayFromUpVec = true; else newAttachNodeData.pitchesAwayFromUpVec = false; if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else attachNodeDragDict.Add(orientation, newAttachNodeData); return; } } private void HandleAttachNodeCd() { if(VesselPartList == null) UpdateShipPartsList(); if (attachNodeDragDict == null) attachNodeDragDict = new Dictionary<Vector3d, attachNodeData>(); attachNodeDragDict.Clear(); Dictionary<string, List<AttachNode>> attachNodeGroups = GetAttachNodeGroups(); Dictionary<string, AttachGroupType> attachNodeType = GetAttachGroupTypes(attachNodeGroups); if (VesselModelTransforms == null) { if (HighLogic.LoadedScene == GameScenes.EDITOR) VesselModelTransforms = FARGeoUtil.VesselModelTransforms(EditorLogic.SortedShipList); else VesselModelTransforms = FARGeoUtil.VesselModelTransforms(vessel); foreach (Part p in VesselPartList) foreach(PartModule m in p.Modules) if (m is FARBasicDragModel) { (m as FARBasicDragModel).VesselModelTransforms = this.VesselModelTransforms; continue; } } foreach (KeyValuePair<string, List<AttachNode>> attachGroup in attachNodeGroups) { UpdateAttachGroupDrag(attachGroup.Value, attachNodeType[attachGroup.Key], PartModelTransforms, VesselModelTransforms); } //UpdateNonAttachBluntEnds(attachNodeGroups, attachNodeType, FARGeoUtil.WorldToPartUpMatrix(part), PartModelTransforms, VesselModelTransforms); VesselModelTransforms = null; } private void UpdateNonAttachBluntEnds(Dictionary<string, List<AttachNode>> attachNodeGroups, Dictionary<string, AttachGroupType> attachNodeType, Matrix4x4 partUpMatrix, Transform[] ModelTransforms, Transform[] VesselModelTransforms) { bool upperEndHandled = false; bool lowerEndHandled = false; Vector2 verticalPartMeshBounds = FARGeoUtil.PartLengthBounds(part, Vector3.zero, partUpMatrix, ModelTransforms); foreach (KeyValuePair<string, List<AttachNode>> pair in attachNodeGroups) { Vector3 avgPos = Vector3.zero; Vector3 avgOrient = Vector3.zero; foreach (AttachNode node in pair.Value) { avgPos += node.position; avgOrient += node.orientation; } avgPos /= pair.Value.Count; avgOrient.Normalize(); if (Mathf.Abs(avgPos.y - verticalPartMeshBounds.x) <= 0.3f) upperEndHandled = true; if (Mathf.Abs(avgPos.y - verticalPartMeshBounds.y) <= 0.3f) lowerEndHandled = true; if (upperEndHandled && lowerEndHandled) break; } if (!upperEndHandled) { Vector3 position = Vector3.up * verticalPartMeshBounds.x; Vector2 bounds = FARGeoUtil.NodeBoundaries(part, position, position, Vector3.zero, 0.05f, ModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = position; if (Vector3d.Dot(orientation, localUpVector) > 1) newAttachNodeData.pitchesAwayFromUpVec = true; else newAttachNodeData.pitchesAwayFromUpVec = false; if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else attachNodeDragDict.Add(orientation, newAttachNodeData); } if (!lowerEndHandled) { Vector3 position = Vector3.up * verticalPartMeshBounds.y; Vector2 bounds = FARGeoUtil.NodeBoundaries(part, position, position, Vector3.zero, 0.05f, ModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = position; if (Vector3d.Dot(orientation, localUpVector) > 1) newAttachNodeData.pitchesAwayFromUpVec = true; else newAttachNodeData.pitchesAwayFromUpVec = false; if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else attachNodeDragDict.Add(orientation, newAttachNodeData); } } */ private void AttachNodeCdAdjust() { //BaseCd = 0; // if (!part.Modules.Contains("FARPayloadFairingModule")) // { if (part.Modules.Contains("FARPayloadFairingModule")) //This doesn't apply blunt drag drag to fairing parts if one of their "exempt" attach nodes is used, indicating attached fairings { return; } if(VesselPartList == null) UpdateShipPartsList(); if (attachNodeDragDict == null) attachNodeDragDict = new Dictionary<Vector3d, attachNodeData>(); attachNodeDragDict.Clear(); Transform transform = part.partTransform; if (transform == null) transform = part.transform; if(transform == null) { Debug.LogError("Part " + part.partInfo.title + " has null transform; drag interactions cannot be applied."); return; } SPlusAttachArea = S; Vector3d partUpVector = transform.TransformDirection(localUpVector); //print("Updating drag for " + part.partInfo.title); foreach (AttachNode Attach in part.attachNodes) { if (Attach.nodeType == AttachNode.NodeType.Stack) { if (Attach.attachedPart != null) { continue; } if (Attach.id.ToLowerInvariant() == "strut") continue; Ray ray = new Ray(); Vector3d relPos = Attach.position + Attach.offset; if(part.Modules.Contains("FARCargoBayModule")) { FARCargoBayModule bay = (FARCargoBayModule)part.Modules["FARCargoBayModule"]; Vector3d maxBounds = bay.maxBounds; Vector3d minBounds = bay.minBounds; if (relPos.x < maxBounds.x && relPos.y < maxBounds.y && relPos.z < maxBounds.z && relPos.x > minBounds.x && relPos.y > minBounds.y && relPos.z > minBounds.z) { return; } } Vector3d origToNode = transform.localToWorldMatrix.MultiplyVector(relPos); double mag = (origToNode).magnitude; //print(part.partInfo.title + " Part Loc: " + part.transform.position + " Attach Loc: " + (origToNode + part.transform.position) + " Dist: " + mag); ray.direction = origToNode; ray.origin = transform.position; double attachSize = FARMathUtil.Clamp(Attach.size, 0.5, double.PositiveInfinity); bool gotIt = false; RaycastHit[] hits = Physics.RaycastAll(ray, (float)(mag + attachSize), FARAeroUtil.RaycastMask); foreach (RaycastHit h in hits) { if (h.collider == part.collider) continue; if (h.distance < (mag + attachSize) && h.distance > (mag - attachSize)) foreach (Part p in VesselPartList) if (p.collider == h.collider) { gotIt = true; break; } if (gotIt) { break; } } if (!gotIt) { double exposedAttachArea = attachSize * FARAeroUtil.attachNodeRadiusFactor; exposedAttachArea *= exposedAttachArea; exposedAttachArea *= Math.PI * FARAeroUtil.areaFactor; SPlusAttachArea += exposedAttachArea; exposedAttachArea /= FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = exposedAttachArea; if (Vector3d.Dot(origToNode, partUpVector) > 1) newAttachNodeData.pitchesAwayFromUpVec = true; else newAttachNodeData.pitchesAwayFromUpVec = false; if (attachNodeDragDict.ContainsKey(transform.worldToLocalMatrix.MultiplyVector(origToNode))) { attachNodeData tmp = attachNodeDragDict[transform.worldToLocalMatrix.MultiplyVector(origToNode)]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[transform.worldToLocalMatrix.MultiplyVector(origToNode)] = tmp; } else attachNodeDragDict.Add(part.transform.worldToLocalMatrix.MultiplyVector(origToNode), newAttachNodeData); } } } }
private void UpdateNonAttachBluntEnds(Dictionary<string, List<AttachNode>> attachNodeGroups, Dictionary<string, AttachGroupType> attachNodeType, Matrix4x4 partUpMatrix, Transform[] ModelTransforms, Transform[] VesselModelTransforms) { bool upperEndHandled = false; bool lowerEndHandled = false; Vector2 verticalPartMeshBounds = FARGeoUtil.PartLengthBounds(part, Vector3.zero, partUpMatrix, ModelTransforms); foreach (KeyValuePair<string, List<AttachNode>> pair in attachNodeGroups) { Vector3 avgPos = Vector3.zero; Vector3 avgOrient = Vector3.zero; foreach (AttachNode node in pair.Value) { avgPos += node.position; avgOrient += node.orientation; } avgPos /= pair.Value.Count; avgOrient.Normalize(); if (Mathf.Abs(avgPos.y - verticalPartMeshBounds.x) <= 0.3f) upperEndHandled = true; if (Mathf.Abs(avgPos.y - verticalPartMeshBounds.y) <= 0.3f) lowerEndHandled = true; if (upperEndHandled && lowerEndHandled) break; } if (!upperEndHandled) { Vector3 position = Vector3.up * verticalPartMeshBounds.x; Vector2 bounds = FARGeoUtil.NodeBoundaries(part, position, position, Vector3.zero, 0.05f, ModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = position; if (Vector3d.Dot(orientation, localUpVector) > 1) newAttachNodeData.pitchesAwayFromUpVec = true; else newAttachNodeData.pitchesAwayFromUpVec = false; if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else attachNodeDragDict.Add(orientation, newAttachNodeData); } if (!lowerEndHandled) { Vector3 position = Vector3.up * verticalPartMeshBounds.y; Vector2 bounds = FARGeoUtil.NodeBoundaries(part, position, position, Vector3.zero, 0.05f, ModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = position; if (Vector3d.Dot(orientation, localUpVector) > 1) newAttachNodeData.pitchesAwayFromUpVec = true; else newAttachNodeData.pitchesAwayFromUpVec = false; if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else attachNodeDragDict.Add(orientation, newAttachNodeData); } }
private void UpdateAttachGroupDrag(List<AttachNode> attachNodeGroup, AttachGroupType attachGroupType, Transform[] ThisPartModelTransforms, Transform[] AllVesselModelTransforms) { //This is standard single node; the blunt area is to be determined by everything near it if (attachGroupType == AttachGroupType.INDEPENDENT_NODE) { //Get area represented by current node Vector2 bounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, ThisPartModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5f; opposedArea *= opposedArea; opposedArea *= Math.PI; //Calculate area based on circular cross-section //Debug.Log(part.partInfo.title + " Area: " + area + " Opposed area: " + opposedArea + " OtherTransforms: " + OtherTransforms.Length); area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = attachNodeGroup[0].position; if (Vector3d.Dot(orientation, localUpVector) > 1) newAttachNodeData.pitchesAwayFromUpVec = true; else newAttachNodeData.pitchesAwayFromUpVec = false; if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else attachNodeDragDict.Add(orientation, newAttachNodeData); return; } //This is a group of nodes that can be used for clustered tanks/engines; the area calculated must be divided over them else if (attachGroupType == AttachGroupType.PARALLEL_NODES) { //Get area represented by current nodes Vector2 bounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, ThisPartModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5; opposedArea *= opposedArea; opposedArea *= Mathf.PI; //Calculate area based on circular cross-section //Debug.Log(part.partInfo.title + " Area: " + area + " Opposed area: " + opposedArea + " OtherTransforms: " + OtherTransforms.Length); area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = Vector3.zero; foreach (AttachNode attach in attachNodeGroup) { orientation += attach.position; } orientation /= attachNodeGroup.Count; if (Vector3d.Dot(orientation, localUpVector) > 1) newAttachNodeData.pitchesAwayFromUpVec = true; else newAttachNodeData.pitchesAwayFromUpVec = false; if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else attachNodeDragDict.Add(orientation, newAttachNodeData); return; } //This represents a vertical stack of nodes, for different payload heights, multiple payload fairings, etc. One node being used means that they are all used else if (attachGroupType == AttachGroupType.VERTICAL_NODES) { double area = 0; AttachNode usedNode; Vector2 bounds = Vector2.zero; Vector3 orientation = Vector3.zero; foreach (AttachNode attach in attachNodeGroup) { //Get area represented by current node bounds = FARGeoUtil.NodeBoundaries(part, attach, Vector3.zero, 0.05f, ThisPartModelTransforms); double tmpArea = (bounds.x + bounds.y); tmpArea *= 0.5; tmpArea *= tmpArea; tmpArea *= Math.PI; //Calculate area based on circular cross-section if (tmpArea > area) { area = tmpArea; usedNode = attach; } orientation += attach.position; } //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5; opposedArea *= opposedArea; opposedArea *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); orientation /= attachNodeGroup.Count; if (Vector3d.Dot(orientation, localUpVector) > 1) newAttachNodeData.pitchesAwayFromUpVec = true; else newAttachNodeData.pitchesAwayFromUpVec = false; if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else attachNodeDragDict.Add(orientation, newAttachNodeData); return; } }
private void AttachNodeCdAdjust() { if (part.Modules.Contains("FARPayloadFairingModule")) //This doesn't apply blunt drag drag to fairing parts if one of their "exempt" attach nodes is used, indicating attached fairings { return; } if (VesselPartList == null) { UpdateShipPartsList(); } if (attachNodeDragList == null) { attachNodeDragList = new List <attachNodeData>(); } attachNodeDragList.Clear(); Transform transform = part.partTransform; if (transform == null) { transform = part.transform; } if (transform == null) { Debug.LogError("Part " + part.partInfo.title + " has null transform; drag interactions cannot be applied."); return; } SPlusAttachArea = S; Vector3d partUpVector = transform.TransformDirection(localUpVector); //print("Updating drag for " + part.partInfo.title); foreach (AttachNode Attach in part.attachNodes) { if (Attach.nodeType == AttachNode.NodeType.Stack) { if (Attach.attachedPart != null) { continue; } if (Attach.id.ToLowerInvariant() == "strut") { continue; } Ray ray = new Ray(); Vector3d relPos = Attach.position + Attach.offset; if (part.Modules.Contains("FARCargoBayModule")) { FARCargoBayModule bay = (FARCargoBayModule)part.Modules["FARCargoBayModule"]; Vector3d maxBounds = bay.maxBounds; Vector3d minBounds = bay.minBounds; if (relPos.x < maxBounds.x && relPos.y < maxBounds.y && relPos.z < maxBounds.z && relPos.x > minBounds.x && relPos.y > minBounds.y && relPos.z > minBounds.z) { return; } } Vector3d origToNode = transform.localToWorldMatrix.MultiplyVector(relPos); double mag = (origToNode).magnitude; //print(part.partInfo.title + " Part Loc: " + part.transform.position + " Attach Loc: " + (origToNode + part.transform.position) + " Dist: " + mag); ray.direction = origToNode; ray.origin = transform.position; double attachSize = FARMathUtil.Clamp(Attach.size, 0.5, double.PositiveInfinity); bool gotIt = false; RaycastHit[] hits = Physics.RaycastAll(ray, (float)(mag + attachSize), FARAeroUtil.RaycastMask); foreach (RaycastHit h in hits) { if (h.collider == part.collider) { continue; } if (h.distance < (mag + attachSize) && h.distance > (mag - attachSize)) { foreach (Part p in VesselPartList) { if (p.collider == h.collider) { gotIt = true; break; } } } if (gotIt) { break; } } if (!gotIt) { double exposedAttachArea = attachSize * FARAeroUtil.attachNodeRadiusFactor; exposedAttachArea *= exposedAttachArea; exposedAttachArea *= Math.PI * FARAeroUtil.areaFactor; SPlusAttachArea += exposedAttachArea; exposedAttachArea /= FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = exposedAttachArea; if (Vector3d.Dot(origToNode, partUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } newAttachNodeData.location = transform.worldToLocalMatrix.MultiplyVector(origToNode); attachNodeDragList.Add(newAttachNodeData); } } } }