Utility class for methods that manipulate part attach nodes.
Example #1
0
        private void updateNodePositions(bool userInput)
        {
            float h = currentHeight * 0.5f;

            SSTUAttachNodeUtils.updateAttachNodePosition(part, part.FindAttachNode("top"), new Vector3(0, h, 0), Vector3.up, userInput);
            SSTUAttachNodeUtils.updateAttachNodePosition(part, part.FindAttachNode("bottom"), new Vector3(0, -h, 0), Vector3.down, userInput);
        }
Example #2
0
        private void updateAttachNodes(bool userInput)
        {
            topModule.model.updateAttachNodes(part, topNodeNames, userInput, ModelOrientation.TOP);
            bottomModule.model.updateAttachNodes(part, bottomNodeNames, userInput, ModelOrientation.BOTTOM);

            Vector3 pos = new Vector3(0, getTopFairingBottomY(), 0);

            SSTUSelectableNodes.updateNodePosition(part, noseInterstageNode, pos);
            AttachNode noseInterstage = part.FindAttachNode(noseInterstageNode);

            if (noseInterstage != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, noseInterstage, pos, Vector3.up, userInput);
            }

            float bottomFairingTopY = getBottomFairingTopY();

            pos = new Vector3(0, bottomFairingTopY, 0);
            SSTUSelectableNodes.updateNodePosition(part, mountInterstageNode, pos);
            AttachNode mountInterstage = part.FindAttachNode(mountInterstageNode);

            if (mountInterstage != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, mountInterstage, pos, Vector3.down, userInput);
            }
        }
Example #3
0
        private void updateNodePositions(bool userInput)
        {
            float   scale         = getCurrentScale();
            float   topY          = currentHeight;
            float   innerY        = internalNodePosition * scale;
            float   bottomY       = bottomNodePosition * scale;
            Vector3 bottomNodePOs = new Vector3(0, bottomY, 0);
            Vector3 innerNodePos  = new Vector3(0, innerY, 0);
            Vector3 topNodePos    = new Vector3(0, topY, 0);

            AttachNode node = part.FindAttachNode(bottomNodeName);

            if (node != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, node, bottomNodePOs, node.orientation, userInput);
            }
            node = part.FindAttachNode(internalNodeName);
            if (node != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, node, innerNodePos, node.orientation, userInput);
            }
            node = part.FindAttachNode(topNodeName);
            if (node != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, node, topNodePos, node.orientation, userInput);
            }
        }
Example #4
0
        protected virtual void updateAttachNodes(bool userInput)
        {
            currentNoseModule.updateAttachNodes(part, topNodeNames, userInput, ModelOrientation.TOP);
            currentMountModule.updateAttachNodes(part, bottomNodeNames, userInput, ModelOrientation.BOTTOM);
            AttachNode surface = part.srfAttachNode;

            if (surface != null)
            {
                Vector3 pos = currentMainTankModule.modelDefinition.surfaceNode.position * currentMainTankModule.currentDiameterScale;
                Vector3 rot = currentMainTankModule.modelDefinition.surfaceNode.orientation;
                SSTUAttachNodeUtils.updateAttachNodePosition(part, surface, pos, rot, userInput);
            }

            if (!String.IsNullOrEmpty(interstageNodeName))
            {
                float   y   = currentMountModule.currentVerticalPosition + (currentMountModule.modelDefinition.fairingTopOffset * currentMountModule.currentHeightScale);
                Vector3 pos = new Vector3(0, y, 0);
                SSTUSelectableNodes.updateNodePosition(part, interstageNodeName, pos);
                AttachNode interstage = part.findAttachNode(interstageNodeName);
                if (interstage != null)
                {
                    Vector3 orientation = new Vector3(0, -1, 0);
                    SSTUAttachNodeUtils.updateAttachNodePosition(part, interstage, pos, orientation, userInput);
                }
            }
        }
Example #5
0
        public void updateAttachNodes(Part part, String[] nodeNames, bool userInput, ModelOrientation orientation)
        {
            if (nodeNames.Length == 1 && nodeNames[0] == "NONE")
            {
                return;
            }
            Vector3            basePos = new Vector3(0, currentVerticalPosition, 0);
            AttachNode         node    = null;
            AttachNodeBaseData data;

            int nodeCount = modelDefinition.attachNodeData.Length;
            int len       = nodeNames.Length;

            Vector3 pos    = Vector3.zero;
            Vector3 orient = Vector3.up;
            int     size   = 2;

            bool invert = (orientation == ModelOrientation.BOTTOM && modelDefinition.invertForBottom) || (orientation == ModelOrientation.TOP && modelDefinition.invertForTop);

            for (int i = 0; i < len; i++)
            {
                node = part.findAttachNode(nodeNames[i]);
                if (i < nodeCount)
                {
                    data = modelDefinition.attachNodeData[i];
                    size = data.size;
                    pos  = data.position * currentHeightScale;
                    if (invert)
                    {
                        pos.y = -pos.y;
                        pos.x = -pos.x;
                    }
                    pos.y += currentVerticalPosition;
                    orient = data.orientation;
                    if (invert)
                    {
                        orient = -orient;
                    }
                    if (node == null)//create it
                    {
                        SSTUAttachNodeUtils.createAttachNode(part, nodeNames[i], pos, orient, size);
                    }
                    else//update its position
                    {
                        SSTUAttachNodeUtils.updateAttachNodePosition(part, node, pos, orient, userInput);
                    }
                }
                else//extra node, destroy
                {
                    if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight)
                    {
                        SSTUAttachNodeUtils.destroyAttachNode(part, node);
                    }
                }
            }
        }
Example #6
0
        private void updateNodePositions(bool userInput)
        {
            AttachNode topNode    = part.FindAttachNode("top");
            AttachNode bottomNode = part.FindAttachNode("bottom");
            float      scale      = currentDiameter / modelDiameter;
            float      topY       = topNodePosition * scale;
            float      bottomY    = bottomNodePosition * scale;
            Vector3    pos        = new Vector3(0, topY, 0);

            SSTUAttachNodeUtils.updateAttachNodePosition(part, topNode, pos, topNode.orientation, userInput);
            pos = new Vector3(0, bottomY, 0);
            SSTUAttachNodeUtils.updateAttachNodePosition(part, bottomNode, pos, bottomNode.orientation, userInput);
        }
Example #7
0
        private void updateAttachNodes(bool userInput)
        {
            int              len = modelGroups.Length;
            List <string>    enabledNodeNames = new List <string>();
            AttachNode       attachNode;
            ModelSwitchGroup group;
            ModelSwitchData  model;

            for (int i = 0; i < len; i++)
            {
                group = modelGroups[i];
                model = group.enabledModel;
                if (!controlledNodes.Contains(group.parentNode))
                {
                    continue;
                }                                                             //not a node that we should touch...
                if (model == null || model.suppressNode)
                {
                    continue;
                }
                enabledNodeNames.Add(group.parentNode);
                Vector3 pos = group.getModelRootTransform().position;
                pos = part.transform.InverseTransformPoint(pos);
                Vector3 rotation = group.getModelRootTransform().up;
                attachNode = part.findAttachNode(group.parentNode);
                if (attachNode == null)
                {
                    attachNode = SSTUAttachNodeUtils.createAttachNode(part, group.parentNode, pos, rotation, 2);
                }
                else
                {
                    SSTUAttachNodeUtils.updateAttachNodePosition(part, attachNode, pos, rotation, userInput);
                }
            }
            List <AttachNode> attachNodes = new List <AttachNode>();

            attachNodes.AddRange(part.attachNodes);
            len = attachNodes.Count;
            for (int i = 0; i < len; i++)
            {
                attachNode = attachNodes[i];
                if (!controlledNodes.Contains(attachNode.id))
                {
                    continue;
                }                                                          //not a node that we should touch...
                if (attachNode.attachedPart == null && !enabledNodeNames.Contains(attachNode.id))
                {
                    SSTUAttachNodeUtils.destroyAttachNode(part, attachNode);
                }
            }
        }
        public void toggleNode()
        {
            AttachNode node = part.findAttachNode(nodeName);

            if (node == null)
            {
                currentlyEnabled = true;
                SSTUAttachNodeUtils.createAttachNode(part, nodeName, nodeDefaultPosition, nodeDefaultOrientation, 2);
            }
            else if (node.attachedPart == null)
            {
                currentlyEnabled = false;
                SSTUAttachNodeUtils.destroyAttachNode(part, node);
            }
        }
Example #9
0
        public void updateAttachNodePositions(bool userInput)
        {
            float      h       = height * 0.5f;
            AttachNode topNode = part.FindAttachNode("top");

            if (topNode != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, topNode, new Vector3(topNode.position.x, h, topNode.position.z), topNode.orientation, userInput);
            }
            AttachNode bottomNode = part.FindAttachNode("bottom");

            if (bottomNode != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, bottomNode, new Vector3(bottomNode.position.x, -h, bottomNode.position.z), bottomNode.orientation, userInput);
            }
        }
Example #10
0
        private void updateAttachNodes(bool userInput)
        {
            float      standoffBottomZ = standoffModule.moduleBottom;
            Vector3    pos             = new Vector3(-standoffBottomZ, 0, 0);
            AttachNode srfNode         = part.srfAttachNode;

            if (srfNode != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, srfNode, pos, Vector3.right, userInput, 0);
            }
            AttachNode bottomNode = part.FindAttachNode("bottom");

            if (bottomNode != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, bottomNode, pos, bottomNode.orientation, userInput, 0);
            }
        }
Example #11
0
        private void setTankDiameterFromEditor(float newDiameter, bool updateSymmetry)
        {
            float oldDiameter = prevTankDiameter;

            currentTankDiameter = newDiameter;
            restoreEditorFields();
            updateEditorStats(true);
            SSTUAttachNodeUtils.updateSurfaceAttachedChildren(part, oldDiameter, newDiameter);

            if (updateSymmetry)
            {
                foreach (Part p in part.symmetryCounterparts)
                {
                    p.GetComponent <SSTUModularFuelTank>().setTankDiameterFromEditor(newDiameter, false);
                }
            }
            SSTUStockInterop.fireEditorUpdate();
            SSTUModInterop.onPartGeometryUpdate(part, true);
        }
Example #12
0
        private void updateAttachNodes(bool userInput)
        {
            AttachNode srf = part.srfAttachNode;

            if (srf != null)
            {
                float   standoffHeight = standoffModule.model.currentHeight + currentScale * structureOffset;
                Vector3 pos            = new Vector3(standoffHeight, 0, 0);
                SSTUAttachNodeUtils.updateAttachNodePosition(part, srf, pos, srf.orientation, userInput);
            }
            AttachNode btm = part.FindAttachNode("bottom");

            if (btm != null)
            {
                float   standoffHeight = standoffModule.model.currentHeight + currentScale * structureOffset;
                Vector3 pos            = new Vector3(standoffHeight, 0, 0);
                SSTUAttachNodeUtils.updateAttachNodePosition(part, btm, pos, btm.orientation, userInput);
            }
        }
        /// <summary>
        /// Update the attach node positions based on the current tank parameters.
        /// </summary>
        private void updateNodePositions(bool userInput)
        {
            AttachNode topNode = part.FindAttachNode("top");

            if (topNode != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, topNode, new Vector3(0, partTopY, 0), topNode.orientation, userInput);
            }

            AttachNode bottomNode = part.FindAttachNode("bottom");

            if (bottomNode != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, bottomNode, new Vector3(0, partBottomY, 0), bottomNode.orientation, userInput);
            }

            Vector3 pos = new Vector3(0, topFairingBottomY, 0);

            SSTUSelectableNodes.updateNodePosition(part, noseInterstageNode, pos);
            AttachNode noseInterstage = part.FindAttachNode(noseInterstageNode);

            if (noseInterstage != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, noseInterstage, pos, Vector3.up, userInput);
            }

            pos = new Vector3(0, bottomFairingTopY, 0);
            SSTUSelectableNodes.updateNodePosition(part, mountInterstageNode, pos);
            AttachNode mountInterstage = part.FindAttachNode(mountInterstageNode);

            if (mountInterstage != null)
            {
                SSTUAttachNodeUtils.updateAttachNodePosition(part, mountInterstage, pos, Vector3.down, userInput);
            }

            if (userInput)
            {
                //TODO -- cache prev tank diameter somewhere, use that for child offset functionality
                //SSTUAttachNodeUtils.updateSurfaceAttachedChildren(part, null, currentTankDiameter);
            }
        }
        //[KSPEvent(guiName = "Invert Node", guiActiveEditor = false)]
        //public void invertNodeEvent()
        //{
        //    //TODO
        //}

        public override void OnStart(PartModule.StartState state)
        {
            base.OnStart(state);
            Events["toggleNodeEvent"].guiName = "Toggle " + nodeName + " node";
            if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight)
            {
                if (!initialized)
                {
                    currentlyEnabled = startsEnabled;
                    initialized      = true;
                    AttachNode node = part.findAttachNode(nodeName);
                    if (currentlyEnabled && node == null)
                    {
                        SSTUAttachNodeUtils.createAttachNode(part, nodeName, nodeDefaultPosition, nodeDefaultOrientation, 2);
                    }
                    else if (!currentlyEnabled && node != null && node.attachedPart == null)
                    {
                        SSTUAttachNodeUtils.destroyAttachNode(part, node);
                    }
                    else if (!currentlyEnabled && node != null && node.attachedPart != null)//error, should never occur if things were handled properly
                    {
                        currentlyEnabled = true;
                    }
                }
                else
                {
                    AttachNode node = part.findAttachNode(nodeName);
                    if (currentlyEnabled && node == null)
                    {
                        currentlyEnabled = true;
                        SSTUAttachNodeUtils.createAttachNode(part, nodeName, nodeDefaultPosition, nodeDefaultOrientation, 2);
                    }
                    else if (!currentlyEnabled && node != null && node.attachedPart == null)
                    {
                        currentlyEnabled = false;
                        SSTUAttachNodeUtils.destroyAttachNode(part, node);
                    }
                }
            }
        }
        private void updateAttachNodes(bool userInput)
        {
            if (!standAlonePart)
            {
                return;
            }
            float      height  = model.model.currentHeight;
            AttachNode topNode = part.FindAttachNode("top");

            if (topNode != null)
            {
                Vector3 topNodePos = new Vector3(0, height * 0.5f, 0);
                SSTUAttachNodeUtils.updateAttachNodePosition(part, topNode, topNodePos, topNode.orientation, userInput);
            }
            AttachNode bottomNode = part.FindAttachNode("bottom");

            if (bottomNode != null)
            {
                Vector3 botNodePos = new Vector3(0, -height * 0.5f, 0);
                SSTUAttachNodeUtils.updateAttachNodePosition(part, bottomNode, botNodePos, bottomNode.orientation, userInput);
            }
        }
Example #16
0
        /// <summary>
        /// Updates the attach nodes on the part for the input list of attach nodes and the current specified nodes for this model.
        /// Any 'extra' attach nodes from the part will be disabled.
        /// </summary>
        /// <param name="part"></param>
        /// <param name="nodeNames"></param>
        /// <param name="userInput"></param>
        /// <param name="orientation"></param>
        public void updateAttachNodes(Part part, String[] nodeNames, bool userInput, ModelOrientation orientation)
        {
            if (nodeNames == null || nodeNames.Length < 1)
            {
                return;
            }
            if (nodeNames.Length == 1 && (nodeNames[0] == "NONE" || nodeNames[0] == "none"))
            {
                return;
            }
            float currentVerticalPosition = this.currentVerticalPosition;
            float offset = getVerticalOffset();

            if (orientation == ModelOrientation.BOTTOM)
            {
                offset = -offset;
            }
            currentVerticalPosition -= offset;

            AttachNode         node = null;
            AttachNodeBaseData data;

            int nodeCount = modelDefinition.attachNodeData.Length;
            int len       = nodeNames.Length;

            Vector3 pos    = Vector3.zero;
            Vector3 orient = Vector3.up;
            int     size   = 4;

            bool invert = modelDefinition.shouldInvert(orientation);

            for (int i = 0; i < len; i++)
            {
                node = part.FindAttachNode(nodeNames[i]);
                if (i < nodeCount)
                {
                    data = modelDefinition.attachNodeData[i];
                    size = Mathf.RoundToInt(data.size * currentDiameterScale);
                    pos  = data.position * currentHeightScale;
                    if (invert)
                    {
                        pos.y = -pos.y;
                        pos.x = -pos.x;
                    }
                    pos.y += currentVerticalPosition;
                    orient = data.orientation;
                    if (invert)
                    {
                        orient = -orient; orient.z = -orient.z;
                    }
                    if (node == null)//create it
                    {
                        SSTUAttachNodeUtils.createAttachNode(part, nodeNames[i], pos, orient, size);
                    }
                    else//update its position
                    {
                        SSTUAttachNodeUtils.updateAttachNodePosition(part, node, pos, orient, userInput);
                    }
                }
                else//extra node, destroy
                {
                    if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight)
                    {
                        SSTUAttachNodeUtils.destroyAttachNode(part, node);
                    }
                }
            }
        }
Example #17
0
        public override void OnStart(StartState state)
        {
            base.OnStart(state);
            initialize();

            string[] groupNames = TankSet.getSetNames(tankSets);
            this.updateUIChooseOptionControl(nameof(currentTankSet), groupNames, groupNames, true, currentTankSet);

            string[] names = currentTankSetModule.getModelNames();
            string[] descs = currentTankSetModule.getTankDescriptions();
            this.updateUIChooseOptionControl(nameof(currentTankType), names, descs, true, currentTankType);

            if (maxTankDiameter == minTankDiameter)
            {
                Fields[nameof(currentTankDiameter)].guiActiveEditor = false;
            }
            else
            {
                this.updateUIFloatEditControl(nameof(currentTankDiameter), minTankDiameter, maxTankDiameter, tankDiameterIncrement * 2, tankDiameterIncrement, tankDiameterIncrement * 0.05f, true, currentTankDiameter);
            }
            updateAvailableVariants(false);
            updateUIScaleControls();

            Fields[nameof(currentTankDiameter)].uiControlEditor.onFieldChanged = delegate(BaseField a, object b)
            {
                this.actionWithSymmetry(m =>
                {
                    m.updateEditorStats(true);
                    SSTUAttachNodeUtils.updateSurfaceAttachedChildren(m.part, m.prevTankDiameter, m.currentTankDiameter);
                    SSTUModInterop.onPartGeometryUpdate(m.part, true);
                });
                SSTUStockInterop.fireEditorUpdate();
            };

            Fields[nameof(currentTankVerticalScale)].uiControlEditor.onFieldChanged = delegate(BaseField a, object b)
            {
                this.actionWithSymmetry(m =>
                {
                    m.updateEditorStats(true);
                    SSTUModInterop.onPartGeometryUpdate(m.part, true);
                });
                SSTUStockInterop.fireEditorUpdate();
            };

            Fields[nameof(currentTankSet)].uiControlEditor.onFieldChanged = delegate(BaseField a, object b)
            {
                this.actionWithSymmetry(m =>
                {
                    TankSet newSet         = Array.Find(m.tankSets, s => s.name == m.currentTankSet);
                    m.currentTankSetModule = newSet;
                    string variant         = m.lastSelectedVariant;
                    m.currentTankType      = newSet.getDefaultModel(m.lastSelectedVariant);
                    m.tankModule.updateSelections();
                    m.tankModule.modelSelected(m.currentTankType);
                    m.Fields[nameof(m.currentTankType)].guiActiveEditor = newSet.Length > 1;
                    //re-seat this if it was changed in the 'setMainTankModuleFromEditor' method
                    //will allow for user-initiated main-tank changes to still change the 'last variant' but will
                    //persist the variant if the newly selected set did not contain the selected variant
                    //so that it will persist to the next set selection, OR be reseated on the next user-tank selection within the current set
                    if (!m.currentTankSetModule.hasVariant(variant))
                    {
                        m.lastSelectedVariant = variant;
                    }
                    if (m.variantData != null)
                    {
                        m.updateAvailableVariants(true);
                    }
                    m.updateEditorStats(true);
                    m.updateUIScaleControls();
                    SSTUModInterop.onPartGeometryUpdate(m.part, true);
                });
                SSTUStockInterop.fireEditorUpdate();
            };

            Fields[nameof(currentNoseType)].uiControlEditor.onFieldChanged = delegate(BaseField a, object b)
            {
                noseModule.modelSelected(a, b);
                this.actionWithSymmetry(m =>
                {
                    m.updateEditorStats(true);
                    m.updateAnimationControl(m.noseAnimationID, m.noseModule.model, 1);
                    SSTUModInterop.onPartGeometryUpdate(m.part, true);
                });
                SSTUStockInterop.fireEditorUpdate();
            };

            Fields[nameof(currentTankType)].uiControlEditor.onFieldChanged = delegate(BaseField a, object b)
            {
                tankModule.modelSelected(a, b);
                this.actionWithSymmetry(m =>
                {
                    if (variantData != null)
                    {
                        m.updateAvailableVariants(true);
                    }
                    m.updateEditorStats(true);
                    m.lastSelectedVariant = tankModule.model.variantName;
                    m.updateAnimationControl(m.bodyAnimationID, m.tankModule.model, 3);
                    m.updateUIScaleControls();
                    SSTUModInterop.onPartGeometryUpdate(m.part, true);
                });
                SSTUStockInterop.fireEditorUpdate();
            };

            Fields[nameof(currentMountType)].uiControlEditor.onFieldChanged = delegate(BaseField a, object b)
            {
                mountModule.modelSelected(a, b);
                this.actionWithSymmetry(m =>
                {
                    m.updateEditorStats(true);
                    m.updateAnimationControl(m.mountAnimationID, m.mountModule.model, 5);
                    SSTUModInterop.onPartGeometryUpdate(m.part, true);
                });
                SSTUStockInterop.fireEditorUpdate();
            };

            Fields[nameof(currentNoseTexture)].uiControlEditor.onFieldChanged  = noseModule.textureSetSelected;
            Fields[nameof(currentTankTexture)].uiControlEditor.onFieldChanged  = tankModule.textureSetSelected;
            Fields[nameof(currentMountTexture)].uiControlEditor.onFieldChanged = mountModule.textureSetSelected;

            Fields[nameof(currentTankSet)].guiActiveEditor   = tankSets.Length > 1;
            Fields[nameof(currentTankType)].guiActiveEditor  = currentTankSetModule.Length > 1;
            Fields[nameof(currentNoseType)].guiActiveEditor  = noseModule.models.Count > 1;
            Fields[nameof(currentMountType)].guiActiveEditor = mountModule.models.Count > 1;

            SSTUStockInterop.fireEditorUpdate();
            SSTUModInterop.onPartGeometryUpdate(part, true);
            if (HighLogic.LoadedSceneIsEditor)
            {
                GameEvents.onEditorShipModified.Add(new EventData <ShipConstruct> .OnEvent(onEditorVesselModified));
            }
        }
Example #18
0
        private void updateAttachNodes(bool userInput)
        {
            int              len = modelGroups.Length;
            List <string>    enabledNodeNames = new List <string>();
            AttachNode       attachNode;
            ModelSwitchGroup group;
            ModelSwitchData  model;

            for (int i = 0; i < len; i++)
            {
                //updated node handling routing
                group = modelGroups[i];
                if (!group.groupEnabled)//group disabled
                {
                    continue;
                }
                model = group.enabledModel;
                if (model == null)//ERROR - no model on enabled group
                {
                    continue;
                }
                //if (model.nodes == null) { continue; }//ERROR - nodes should not be null; let it crash
                int len2 = model.nodes.Length;
                if (len2 > 0)
                {
                    for (int k = 0; k < len2; k++)
                    {
                        if (!controlledNodes.Contains(model.nodes[k].name))
                        {
                            continue;
                        }                                                                //not a node under this modules control
                        if (group.isChildAtNodeEnabled(model.nodes[k].name))
                        {
                            continue;
                        }                                                                 //child enabled, let it handle the node setup
                        if (!model.nodes[k].createAttachNode)
                        {
                            continue;
                        }                                                  //node is disabled
                        if (enabledNodeNames.Contains(model.nodes[k].name))
                        {
                            continue;
                        }                                                                //node already enabled from other model -- user config/setup ERROR
                        // if it passed all those checks it is a valid model node for attach-node creation; setup the position and rotation relative to this groups base transform
                        Vector3    pos = model.nodes[k].position;
                        Quaternion nr  = Quaternion.Euler(model.nodes[k].rotation);
                        Vector3    rot = Vector3.zero;
                        //position will be a local position transformed by group base transform into world space, and then by part transform into local space
                        //rotation will be the base-transforms rotation quaternion multiplied (or inverse) by the local-rotation quaternion from euler-angle of the rotation for the node
                        //and then how to get it as a vector-axis?  mult the 'fwd' vector by the quat?

                        attachNode = part.FindAttachNode(model.nodes[k].name);
                        if (attachNode == null)
                        {
                            attachNode = SSTUAttachNodeUtils.createAttachNode(part, group.parentNode, pos, rot, 2);
                        }
                        else
                        {
                            SSTUAttachNodeUtils.updateAttachNodePosition(part, attachNode, pos, rot, userInput);
                        }
                    }
                    //check each node for 'enabled' flag
                    //if enabled check for children
                    //if child is present, let child handle the node
                    //else enable it
                }
                else//no model nodes, so no chance for children groups; only nodes defined in the MODEL and flagged for enabled will be enabled; no nodes == no nodes!
                {
                    //NOOP
                }

                // original code block
                // does not use the 'enableAttachNode' data from the node specifications in the MODELs
                //group = modelGroups[i];
                //model = group.enabledModel;
                //if (!controlledNodes.Contains(group.parentNode)) { continue; }//not a node that we should touch...
                //if (model == null || model.suppressNode) { continue; }
                //enabledNodeNames.Add(group.parentNode);
                //Vector3 pos = group.getModelRootTransform().position;
                //pos = part.transform.InverseTransformPoint(pos);
                //Vector3 rotation = group.getModelRootTransform().up;
                //attachNode = part.findAttachNode(group.parentNode);
                //if (attachNode == null)
                //{
                //    attachNode = SSTUAttachNodeUtils.createAttachNode(part, group.parentNode, pos, rotation, 2);
                //}
                //else
                //{
                //    SSTUAttachNodeUtils.updateAttachNodePosition(part, attachNode, pos, rotation, userInput);
                //}
            }
            List <AttachNode> attachNodes = new List <AttachNode>();

            attachNodes.AddRange(part.attachNodes);
            len = attachNodes.Count;
            for (int i = 0; i < len; i++)
            {
                attachNode = attachNodes[i];
                if (!controlledNodes.Contains(attachNode.id))
                {
                    continue;
                }                                                          //not a node that we should touch...
                if (attachNode.attachedPart == null && !enabledNodeNames.Contains(attachNode.id))
                {
                    SSTUAttachNodeUtils.destroyAttachNode(part, attachNode);
                }
            }
        }