private void initialize() { //MonoBehaviour.print("NodeFairingInit: "+fairingCreated+ " :: " +fairingForceDisabled+ " :: "+fairingJettisoned + " :: " +fairingEnabled); if (rendersToRemove != null && rendersToRemove.Length > 0) { ROTUtils.removeTransforms(part, ROTUtils.parseCSV(rendersToRemove)); } loadFairingData(ROTConfigNodeUtils.parseConfigNode(configNodeData)); if (externalUpdateData != null) { updateFromExternalData(externalUpdateData); } if (fairingCreated || (fairingEnabled && !fairingJettisoned && !fairingForceDisabled && string.IsNullOrEmpty(nodeName)))//previously existed, recreate it, or should exist by default values in the config { buildFairing(); if (!string.IsNullOrEmpty(nodeName)) { AttachNode n = part.FindAttachNode(nodeName); if (n != null && n.attachedPart != null) { prevAttachedPart = n.attachedPart; //MonoBehaviour.print("Setting initial attached part to: " + prevAttachedPart); } } } else if (!fairingJettisoned && !fairingForceDisabled && !string.IsNullOrEmpty(nodeName))//else could potentially be activated by a node...check for activation { needsStatusUpdate = true; } updateTextureSet(false); needsGuiUpdate = true; //MonoBehaviour.print("NodeFairingInit End: " + fairingCreated + " :: " + fairingForceDisabled + " :: " + fairingJettisoned + " :: " + fairingEnabled); }
public AttachNodeBaseData(String nodeData) { String[] dataVals = nodeData.Split(new String[] { "," }, StringSplitOptions.None); position = new Vector3(ROTUtils.safeParseFloat(dataVals[0].Trim()), ROTUtils.safeParseFloat(dataVals[1].Trim()), ROTUtils.safeParseFloat(dataVals[2].Trim())); orientation = new Vector3(ROTUtils.safeParseFloat(dataVals[3].Trim()), ROTUtils.safeParseFloat(dataVals[4].Trim()), ROTUtils.safeParseFloat(dataVals[5].Trim())); size = dataVals.Length > 6 ? ROTUtils.safeParseInt(dataVals[6]) : 4; }
public void loadPersistence(String data) { String[] csv = ROTUtils.parseCSV(data); topY = ROTUtils.safeParseFloat(csv[0]); bottomY = ROTUtils.safeParseFloat(csv[1]); topRadius = ROTUtils.safeParseFloat(csv[2]); bottomRadius = ROTUtils.safeParseFloat(csv[3]); }
public void setOpacity(float value) { opacity = value; if (rootObject != null) { ROTUtils.setOpacityRecursive(rootObject.transform, value); } }
public static bool[] GetBoolValues(this ConfigNode node, String name) { String[] values = node.GetValues(name); int len = values.Length; bool[] vals = new bool[len]; for (int i = 0; i < len; i++) { vals[i] = ROTUtils.safeParseBool(values[i]); } return(vals); }
private void initialize() { if (initialized) { return; } initialized = true; prevDiameter = currentDiameter; coreNodeNames = ROTUtils.parseCSV(coreManagedNodes); //model-module setup/initialization ConfigNode node = ROTConfigNodeUtils.parseConfigNode(configNodeData); //list of CORE model nodes from config //each one may contain multiple 'model=modelDefinitionName' entries //but must contain no more than a single 'variant' entry. //if no variant is specified, they are added to the 'Default' variant. ConfigNode[] coreDefNodes = node.GetNodes("CORE"); ModelDefinitionLayoutOptions[] coreDefs; List <ModelDefinitionLayoutOptions> coreDefList = new List <ModelDefinitionLayoutOptions>(); int coreDefLen = coreDefNodes.Length; for (int i = 0; i < coreDefLen; i++) { string variantName = coreDefNodes[i].GetStringValue("variant", "Default"); coreDefs = ROTModelData.getModelDefinitionLayouts(coreDefNodes[i].GetStringValues("model")); coreDefList.AddUniqueRange(coreDefs); ModelDefinitionVariantSet mdvs = getVariantSet(variantName); mdvs.addModels(coreDefs); } coreDefs = coreDefList.ToArray(); coreModule = new ROTModelModule <ModuleROTProbe>(part, this, getRootTransform("ModularProbe-CORE"), ModelOrientation.CENTRAL, nameof(currentCore), null, nameof(currentCoreTexture), nameof(coreModulePersistentData)); coreModule.name = "ModularProbe-Core"; coreModule.getSymmetryModule = m => m.coreModule; coreModule.getValidOptions = () => getVariantSet(currentVariant).definitions; coreModule.massScalar = massScalingPower; coreModule.volumeScalar = volumeScalingPower; //set up the model lists and load the currently selected model coreModule.setupModelList(coreDefs); coreModule.setupModel(); updateModulePositions(); updateMassAndDimensions(); updateAttachNodes(false); updateAvailableVariants(); ROTStockInterop.updatePartHighlighting(part); }
public static float[] GetFloatValues(this ConfigNode node, String name, float[] defaults) { String baseVal = node.GetStringValue(name); if (!String.IsNullOrEmpty(baseVal)) { String[] split = baseVal.Split(new char[] { ',' }); float[] vals = new float[split.Length]; for (int i = 0; i < split.Length; i++) { vals[i] = ROTUtils.safeParseFloat(split[i]); } return(vals); } return(defaults); }
public static Vector3 GetVector3(this ConfigNode node, String name, Vector3 defaultValue) { String value = node.GetValue(name); if (value == null) { return(defaultValue); } String[] vals = value.Split(','); if (vals.Length < 3) { MonoBehaviour.print("ERROR parsing values for Vector3 from input: " + value + ". found less than 3 values, cannot create Vector3"); return(defaultValue); } return(new Vector3((float)ROTUtils.safeParseDouble(vals[0]), (float)ROTUtils.safeParseDouble(vals[1]), (float)ROTUtils.safeParseDouble(vals[2]))); }
public static int[] GetIntValues(this ConfigNode node, string name, int[] defaultValues = null) { int[] values = defaultValues; string[] stringValues = node.GetValues(name); if (stringValues == null || stringValues.Length == 0) { return(values); } int len = stringValues.Length; values = new int[len]; for (int i = 0; i < len; i++) { values[i] = ROTUtils.safeParseInt(stringValues[i]); } return(values); }
public GameObject[] generatePanels(Transform parent) { //each panelEdgeGroup should now contain the vertical edge loops for each vertical panel grouping //from here, need to generate meshes for panels depending upon splitPanels setting int len = panelGroups.Length; GameObject[] gos = new GameObject[panels]; for (int i = 0; i < len; i++) { gos[i] = parent.FindOrCreate("FairingPanel-" + i).gameObject; ROTUtils.destroyChildren(gos[i].transform);//remove any existing colliders MeshFilter mf = gos[i].GetComponent <MeshFilter>(); if (mf == null) { mf = gos[i].AddComponent <MeshFilter>(); } mf.mesh = panelGroups[i].generatePanels(offset, outsideUV, insideUV, edgesUV); if (colliders) { GameObject[] cols = panelGroups[i].generateColliders(offset, facesPerCollider); for (int k = 0; k < cols.Length; k++) { cols[k].transform.NestToParent(gos[i].transform); } } MeshRenderer mr = gos[i].GetComponent <MeshRenderer>(); if (mr == null) { mr = gos[i].AddComponent <MeshRenderer>(); } gos[i].transform.parent = parent; gos[i].transform.localPosition = Vector3.zero; gos[i].transform.rotation = parent.rotation; } Transform[] trs; for (int i = len; i < 8; i++)//destroy extra unused panels { trs = parent.transform.FindChildren("FairingPanel-" + i); for (int k = 0; k < trs.Length; k++) { GameObject.DestroyImmediate(trs[k].gameObject); } } return(gos); }
public static FloatCurve GetFloatCurve(this ConfigNode node, String name, FloatCurve defaultValue = null) { FloatCurve curve = new FloatCurve(); if (node.HasNode(name)) { ConfigNode curveNode = node.GetNode(name); String[] values = curveNode.GetValues("key"); int len = values.Length; String[] splitValue; float a, b, c, d; for (int i = 0; i < len; i++) { splitValue = Regex.Replace(values[i], @"\s+", " ").Split(' '); if (splitValue.Length > 2) { a = ROTUtils.safeParseFloat(splitValue[0]); b = ROTUtils.safeParseFloat(splitValue[1]); c = ROTUtils.safeParseFloat(splitValue[2]); d = ROTUtils.safeParseFloat(splitValue[3]); curve.Add(a, b, c, d); } else { a = ROTUtils.safeParseFloat(splitValue[0]); b = ROTUtils.safeParseFloat(splitValue[1]); curve.Add(a, b); } } } else if (defaultValue != null) { foreach (Keyframe f in defaultValue.Curve.keys) { curve.Add(f.time, f.value, f.inTangent, f.outTangent); } } else { curve.Add(0, 0); curve.Add(1, 1); } return(curve); }
public static Material loadMaterial(String diffuse, String normal, String emissive, String shader) { Material material; Texture diffuseTexture = ROTUtils.findTexture(diffuse, false); Texture normalTexture = String.IsNullOrEmpty(normal) ? null : ROTUtils.findTexture(normal, true); Texture emissiveTexture = String.IsNullOrEmpty(emissive) ? null : ROTUtils.findTexture(emissive, false); material = new Material(Shader.Find(shader)); material.SetTexture("_MainTex", diffuseTexture); if (normalTexture != null) { material.SetTexture("_BumpMap", normalTexture); } if (emissiveTexture != null) { material.SetTexture("_Emissive", emissiveTexture); } return(material); }
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 = ROTUtils.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); } }
public void enableColliders(bool val) { ROTUtils.enableColliderRecursive(rootObject.transform, val); }
/// <summary> /// Initialize the UI controls, including default values, and specifying delegates for their 'onClick' methods.<para/> /// All UI based interaction code will be defined/run through these delegates. /// </summary> private void initializeUI() { Action <ModuleROTank> modelChangedAction = (m) => { m.updateModulePositions(); m.updateDimensions(); m.updateAttachNodes(true); m.updateFairing(true); m.updateAvailableVariants(); m.updateDragCubes(); ROTModInterop.updateResourceVolume(m.part); }; //set up the core variant UI control string[] variantNames = ROTUtils.getNames(variantSets.Values, m => m.variantName); this.updateUIChooseOptionControl(nameof(currentVariant), variantNames, variantNames, true, currentVariant); Fields[nameof(currentVariant)].guiActiveEditor = variantSets.Count > 1; Fields[nameof(currentVariant)].uiControlEditor.onFieldChanged = (a, b) => { //TODO find variant set for the currently enabled core model //query the index from that variant set ModelDefinitionVariantSet prevMdvs = getVariantSet(coreModule.definition.name); //this is the index of the currently selected model within its variant set int previousIndex = prevMdvs.indexOf(coreModule.layoutOptions); //grab ref to the current/new variant set ModelDefinitionVariantSet mdvs = getVariantSet(currentVariant); //and a reference to the model from same index out of the new set ([] call does validation internally for IAOOBE) ModelDefinitionLayoutOptions newCoreDef = mdvs[previousIndex]; //now, call model-selected on the core model to update for the changes, including symmetry counterpart updating. this.actionWithSymmetry(m => { m.currentVariant = currentVariant; m.coreModule.modelSelected(newCoreDef.definition.name); modelChangedAction(m); }); }; Fields[nameof(currentDiameter)].uiControlEditor.onFieldChanged = (a, b) => { this.actionWithSymmetry(m => { if (m != this) { m.currentDiameter = this.currentDiameter; } modelChangedAction(m); m.prevDiameter = m.currentDiameter; }); ROTStockInterop.fireEditorUpdate(); }; Fields[nameof(currentVScale)].uiControlEditor.onFieldChanged = (a, b) => { this.actionWithSymmetry(m => { if (m != this) { m.currentVScale = this.currentVScale; } modelChangedAction(m); }); ROTStockInterop.fireEditorUpdate(); }; Fields[nameof(currentNose)].uiControlEditor.onFieldChanged = (a, b) => { noseModule.modelSelected(a, b); this.actionWithSymmetry(modelChangedAction); ROTStockInterop.fireEditorUpdate(); }; Fields[nameof(currentCore)].uiControlEditor.onFieldChanged = (a, b) => { coreModule.modelSelected(a, b); this.actionWithSymmetry(modelChangedAction); ROTStockInterop.fireEditorUpdate(); }; Fields[nameof(currentMount)].uiControlEditor.onFieldChanged = (a, b) => { mountModule.modelSelected(a, b); this.actionWithSymmetry(modelChangedAction); ROTStockInterop.fireEditorUpdate(); }; //------------------MODEL DIAMETER SWITCH UI INIT---------------------// if (maxDiameter == minDiameter) { Fields[nameof(currentDiameter)].guiActiveEditor = false; } else { this.updateUIFloatEditControl(nameof(currentDiameter), minDiameter, maxDiameter, diameterLargeStep, diameterSmallStep, diameterSlideStep, true, currentDiameter); } Fields[nameof(currentVScale)].guiActiveEditor = enableVScale; //------------------MODULE TEXTURE SWITCH UI INIT---------------------// Fields[nameof(currentNoseTexture)].uiControlEditor.onFieldChanged = noseModule.textureSetSelected; Fields[nameof(currentCoreTexture)].uiControlEditor.onFieldChanged = coreModule.textureSetSelected; Fields[nameof(currentMountTexture)].uiControlEditor.onFieldChanged = mountModule.textureSetSelected; if (HighLogic.LoadedSceneIsEditor) { GameEvents.onEditorShipModified.Add(new EventData <ShipConstruct> .OnEvent(onEditorVesselModified)); } }
//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 = ROTUtils.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); } }
//creates/recreates FairingData instances from data from config node and any persistent node (if applicable) private void loadFairingData(ConfigNode node) { recolorHandler = new RecoloringHandler(Fields[nameof(customColorData)]); ConfigNode[] fairingNodes = node.GetNodes("FAIRING"); fairingParts = new ROTNodeFairingData[fairingNodes.Length]; Transform modelBase = part.transform.FindRecursive("model"); Transform parent; ModuleROTNodeFairing[] cs = part.GetComponents <ModuleROTNodeFairing>(); int l = Array.IndexOf(cs, this); int moduleIndex = l; for (int i = 0; i < fairingNodes.Length; i++) { parent = modelBase.FindOrCreate(fairingName + "-" + moduleIndex + "-" + i); fairingParts[i] = new ROTNodeFairingData(); fairingParts[i].load(fairingNodes[i], parent.gameObject); if (fairingParts[i].canAdjustTop) { enableTopDiameterControls = true; if (guiTopDiameter < 0) { guiTopDiameter = fairingParts[i].topRadius * 2f; } else { fairingParts[i].topRadius = guiTopDiameter * 0.5f; } } if (fairingParts[i].canAdjustBottom) { enableBottomDiameterControls = true; if (guiBottomDiameter < 0) { guiBottomDiameter = fairingParts[i].bottomRadius * 2f; } else { fairingParts[i].bottomRadius = guiBottomDiameter * 0.5f; } } } //reload fairing data from persistence; //it -should- already match the guiTopDiameter/guiBottomDiameter (or else was already corrupted/invalid when saved out). if (!String.IsNullOrEmpty(persistentDataString)) { String[] datas = ROTUtils.parseCSV(persistentDataString, ":"); int length = datas.Length; for (int i = 0; i < length; i++) { fairingParts[i].loadPersistence(datas[i]); } } string[] names = node.GetStringValues("textureSet"); string[] titles = ROTUtils.getNames(TexturesUnlimitedLoader.getTextureSets(names), m => m.title); TextureSet t = TexturesUnlimitedLoader.getTextureSet(currentTextureSet); if (t == null) { currentTextureSet = names[0]; t = TexturesUnlimitedLoader.getTextureSet(currentTextureSet); initializedColors = false; } if (!initializedColors) { initializedColors = true; recolorHandler.setColorData(t.maskColors); } this.updateUIChooseOptionControl(nameof(currentTextureSet), names, titles, true, currentTextureSet); }
public string[] getLayoutTitles() { return(ROTUtils.getNames(layouts, m => m.title)); }