コード例 #1
0
        private void setupEngineModels(Transform modelBase, ConfigNode baseNode)
        {
            ConfigNode[] modelNodes = baseNode.GetNodes("MODEL");
            Transform    engineRoot = modelBase.FindOrCreate("SSTU-ISDC-EngineRoot");

            engineModels = new ModelModule <ISDCModelData, SSTUInterstageDecoupler>(part, this, engineRoot, ModelOrientation.TOP, nameof(customEngineColorData), nameof(currentEngineModel), nameof(currentEngineTextureSet));
            engineModels.getSymmetryModule = m => m.engineModels;
            engineModels.setupModelList(ModelData.parseModels <ISDCModelData>(modelNodes, m => new ISDCModelData(m)));
            engineModels.setupModel();
            updateEnginePositionAndScale();
        }
コード例 #2
0
ファイル: SSTUModularRCS.cs プロジェクト: pap1723/SSTULabs
        private void init()
        {
            if (initialized)
            {
                return;
            }
            initialized         = true;
            standoffRotatedRoot = part.transform.FindRecursive("SSTUModularRCSStructureRoot");
            if (standoffRotatedRoot == null)
            {
                standoffRotatedRoot = new GameObject("SSTUModularRCSStructureRoot").transform;
                standoffRotatedRoot.NestToParent(part.transform.FindRecursive("model"));
                standoffRotatedRoot.Rotate(90, -90, 0);
            }
            modelRotatedRoot = part.transform.FindRecursive("SSTUModularRCSBlockRoot");
            if (modelRotatedRoot == null)
            {
                modelRotatedRoot = new GameObject("SSTUModularRCSBlockRoot").transform;
                modelRotatedRoot.NestToParent(part.transform.FindRecursive("model"));
                modelRotatedRoot.Rotate(0, 0, 0);
            }
            ConfigNode node = SSTUConfigNodeUtils.parseConfigNode(configNodeData);

            ModelDefinitionLayoutOptions[] blocks  = SSTUModelData.getModelDefinitions(node.GetNodes("MODEL"));
            ModelDefinitionLayoutOptions[] structs = SSTUModelData.getModelDefinitions(node.GetNodes("STRUCTURE"));

            modelTransform      = modelRotatedRoot.FindOrCreate("ModularRCSModel");
            rcsBlockModule      = new ModelModule <SSTUModularRCS>(part, this, modelTransform, ModelOrientation.CENTRAL, nameof(currentModel), nameof(currentLayout), nameof(currentTexture), nameof(modelPersistentData), null, null, null, null);
            rcsBlockModule.name = "RCSBlock";
            rcsBlockModule.getSymmetryModule = m => m.rcsBlockModule;
            rcsBlockModule.setupModelList(blocks);
            rcsBlockModule.getValidOptions = () => blocks;
            rcsBlockModule.setupModel();
            rcsBlockModule.updateSelections();

            standoffTransform   = standoffRotatedRoot.FindOrCreate("ModularRCSStandoff");
            standoffModule      = new ModelModule <SSTUModularRCS>(part, this, standoffTransform, ModelOrientation.TOP, nameof(currentStructure), nameof(currentLayout), nameof(currentStructureTexture), nameof(structurePersistentData), null, null, null, null);
            standoffModule.name = "Standoff";
            standoffModule.getSymmetryModule = m => m.standoffModule;
            standoffModule.setupModelList(structs);
            standoffModule.getValidOptions = () => structs;
            standoffModule.setupModel();
            standoffModule.updateSelections();

            updateModelScale();
            updateMassAndCost();
            rcsBlockModule.renameRCSThrustTransforms(rcsThrustTransformName);

            updateAttachNodes(false);
        }
コード例 #3
0
        private void initialize()
        {
            if (initialized)
            {
                return;
            }
            initialized = true;
            double hsp  = 1;
            double dens = 1;

            PartResourceDefinition resource = PartResourceLibrary.Instance.GetDefinition(resourceName);

            hsp  = resource.specificHeatCapacity;
            dens = resource.density;

            fluxPerResourceUnit = hsp * ablationEfficiency * dens;
            baseSkinIntMult     = part.skinInternalConductionMult;
            baseCondMult        = part.heatConductivity;

            ConfigNode node = SSTUConfigNodeUtils.parseConfigNode(configNodeData);

            Transform mhsRoot = part.transform.FindRecursive("model").FindOrCreate("SSTU-MHS-Root");

            ConfigNode[] modelNodes = node.GetNodes("MODELS");
            ModelDefinitionLayoutOptions[] models = SSTUModelData.getModelDefinitions(modelNodes);
            model = new ModelModule <SSTUModularHeatShield>(part, this, mhsRoot, ModelOrientation.CENTRAL, nameof(currentShieldModel), null, nameof(currentShieldTexture), nameof(modelPersistentData), null, null, null, null);
            model.getSymmetryModule = (m) => m.model;
            model.setupModelList(models);
            model.setupModel();
            model.setScaleForDiameter(currentDiameter);
            model.setPosition(0);
            model.updateModelMeshes();
            model.updateSelections();
            model.volumeScalar = resourceScalePower;
            model.massScalar   = resourceScalePower;
            if (standAlonePart)
            {
                updateDragCube();
                updateAttachNodes(false);
            }
            SSTUModInterop.updateResourceVolume(part);
            ConfigNode[] typeNodes = node.GetNodes("SHIELDTYPE");
            shieldTypeData        = HeatShieldTypeData.load(typeNodes);
            currentShieldTypeData = Array.Find(shieldTypeData, m => m.baseType.name == currentShieldType);
            updateModuleStats();
            updatePartCost();
            SSTUModInterop.onPartGeometryUpdate(part, false);
            SSTUModInterop.updateResourceVolume(part);
            SSTUStockInterop.fireEditorUpdate();//update for mass/cost/etc.
        }
コード例 #4
0
        private void init()
        {
            if (initialized)
            {
                return;
            }
            initialized = true;
            ConfigNode node = SSTUConfigNodeUtils.parseConfigNode(configNodeData);

            standoffTransform = part.transform.FindRecursive("model").FindOrCreate("ModularRCSStandoff");
            standoffTransform.localRotation = Quaternion.Euler(0, 0, 90);//rotate 90' on z-axis, to face along x+/-; this should put the 'top' of the model at 0,0,0
            standoffModule = new ModelModule <SingleModelData, SSTUModularRCS>(part, this, standoffTransform, ModelOrientation.TOP, nameof(structurePersistentData), nameof(currentStructure), nameof(currentStructureTexture));
            standoffModule.getSymmetryModule = m => m.standoffModule;
            standoffModule.setupModelList(ModelData.parseModels <SingleModelData>(node.GetNodes("STRUCTURE"), m => new SingleModelData(m)));
            standoffModule.setupModel();

            if (string.IsNullOrEmpty(modelName))
            {
                MonoBehaviour.print("ERROR: SSTUModularRCS - null/empty modelName in module config.");
            }
            modelTransform = part.transform.FindModel(modelName);

            updateModelScale();
            updateMassAndCost();

            updateAttachNodes(false);

            ConfigNode[] fuelTypeNodes = node.GetNodes("FUELTYPE");
            int          len           = fuelTypeNodes.Length;

            fuelTypes = new ContainerFuelPreset[len];
            for (int i = 0; i < len; i++)
            {
                fuelTypes[i] = VolumeContainerLoader.getPreset(fuelTypeNodes[i].GetValue("name"));
            }
            fuelType = Array.Find(fuelTypes, m => m.name == currentFuelType);
            if (fuelType == null && (fuelTypes != null && fuelTypes.Length > 0))
            {
                MonoBehaviour.print("ERROR: SSTUModularRCS - currentFuelType was null for value: " + currentFuelType);
                fuelType        = fuelTypes[0];
                currentFuelType = fuelType.name;
                MonoBehaviour.print("Assigned default fuel type of: " + currentFuelType + ".  This is likely a config error that needs to be corrected.");
            }
            else if (fuelTypes == null || fuelTypes.Length < 1)
            {
                //TODO -- handle cases of disabled fuel switching
                MonoBehaviour.print("ERROR: SSTUModularRCS - No fuel type definitions found.");
            }
        }
コード例 #5
0
        private void initialize()
        {
            if (initialized)
            {
                return;
            }
            initialized = true;
            ConfigNode node = SSTUConfigNodeUtils.parseConfigNode(configNodeData);
            Transform  root = part.transform.FindRecursive("model").FindOrCreate(rootTransformName);

            models = new ModelModule <PositionedModelData, SSTUModelSwitch2>(part, this, root, ModelOrientation.TOP, nameof(modelPersistentData), nameof(currentModel), nameof(currentTexture));
            models.getSymmetryModule = m => m.models;
            models.setupModelList(ModelData.parseModels <PositionedModelData>(node.GetNodes("MODEL"), m => new PositionedModelData(m)));
            models.setupModel();
            models.model.updateScale(currentScale);
            models.updateModel();
            updateMassAndCost();
            updateAttachNodes(false);
        }
コード例 #6
0
        /// <summary>
        /// Loads all of the part definitions and values from the stashed config node data
        /// </summary>
        private void loadConfigData()
        {
            ConfigNode node = SSTUConfigNodeUtils.parseConfigNode(configNodeData);

            noseModule = new ModelModule <SingleModelData, SSTUModularUpperStage>(part, this, getRootTransform("MUSNose"), ModelOrientation.TOP, nameof(nosePersistentData), nameof(currentNose), nameof(currentNoseTexture));
            noseModule.getSymmetryModule = m => m.noseModule;
            noseModule.setupModelList(SingleModelData.parseModels(node.GetNodes("NOSE")));
            noseModule.setupModel();

            upperModule = new ModelModule <SingleModelData, SSTUModularUpperStage>(part, this, getRootTransform("MUSUpper"), ModelOrientation.TOP, nameof(upperPersistentData), nameof(currentUpper), nameof(currentUpperTexture));
            upperModule.getSymmetryModule = m => m.upperModule;
            upperModule.setupModelList(SingleModelData.parseModels(node.GetNodes("UPPER")));
            upperModule.setupModel();

            intertankModule = new ModelModule <SingleModelData, SSTUModularUpperStage>(part, this, getRootTransform("MUSIntertank"), ModelOrientation.TOP, nameof(intertankPersistentData), nameof(currentIntertank), nameof(currentIntertankTexture));
            intertankModule.getSymmetryModule = m => m.intertankModule;
            intertankModule.setupModelList(SingleModelData.parseModels(node.GetNodes("INTERTANK")));
            intertankModule.setupModel();

            lowerModule = new ModelModule <SingleModelData, SSTUModularUpperStage>(part, this, getRootTransform("MUSLower"), ModelOrientation.TOP, nameof(lowerPersistentData), nameof(currentLower), nameof(currentLowerTexture));
            lowerModule.getSymmetryModule = m => m.lowerModule;
            lowerModule.setupModelList(SingleModelData.parseModels(node.GetNodes("LOWER")));
            lowerModule.setupModel();

            mountModule = new ModelModule <SingleModelData, SSTUModularUpperStage>(part, this, getRootTransform("MUSMount"), ModelOrientation.BOTTOM, nameof(mountPersistentData), nameof(currentMount), nameof(currentMountTexture));
            mountModule.getSymmetryModule = m => m.mountModule;
            mountModule.setupModelList(SingleModelData.parseModels(node.GetNodes("MOUNT")));
            mountModule.setupModel();

            rcsModule = new ModelModule <SSTUModularUpperStageRCS, SSTUModularUpperStage>(part, this, getRootTransform("MUSRCS"), ModelOrientation.CENTRAL, nameof(rcsPersistentData), nameof(currentRCS), nameof(currentRCSTexture));
            rcsModule.getSymmetryModule = m => m.rcsModule;
            rcsModule.setupModelList(SingleModelData.parseModels(node.GetNodes("RCS"), m => new SSTUModularUpperStageRCS(m)));
            rcsModule.setupModel();
            updateRCSModule();

            if (!splitTank)
            {
                intertankModule.model.destroyCurrentModel();
                lowerModule.model.destroyCurrentModel();
            }
        }
コード例 #7
0
ファイル: SSTUModelSwitch.cs プロジェクト: zxzxn3/SSTULabs
        private void initialize()
        {
            if (initialized)
            {
                return;
            }
            initialized = true;
            ConfigNode node = SSTUConfigNodeUtils.parseConfigNode(configNodeData);

            ModelDefinitionLayoutOptions[] defs = SSTUModelData.getModelDefinitions(node.GetNodes("MODEL"));
            Transform root = part.transform.FindRecursive("model").FindOrCreate(rootTransformName);

            models      = new ModelModule <SSTUModelSwitch>(part, this, root, ModelOrientation.TOP, nameof(currentModel), null, nameof(currentTexture), nameof(modelPersistentData), nameof(animationPersistentData), nameof(animationMaxDeploy), nameof(enableAnimationEvent), nameof(disableAnimationEvent));
            models.name = "ModelSwitch";
            models.getSymmetryModule = m => m.models;
            models.getValidOptions   = () => defs;
            models.setupModelList(defs);
            models.setupModel();
            models.setScale(currentScale);
            models.updateModelMeshes();
            models.updateSelections();
            updateMassAndCost();
            updateAttachNodes(false);
        }
コード例 #8
0
        private void initialize()
        {
            if (initialized)
            {
                return;
            }
            initialized = true;
            double hsp  = 1;
            double dens = 1;

            if (heatSoak)
            {
                PartResourceDefinition resource = PartResourceLibrary.Instance.GetDefinition(resourceName);
                hsp  = resource.specificHeatCapacity;
                dens = resource.density;
            }
            else
            {
                resource = part.Resources[resourceName];
                if (resource != null)
                {
                    hsp  = resource.info.specificHeatCapacity;
                    dens = resource.info.density;
                }
                else
                {
                    hsp  = PhysicsGlobals.StandardSpecificHeatCapacity;
                    dens = 0.005f;
                }
            }
            fluxPerResourceUnit = hsp * ablationEfficiency * dens;
            baseSkinIntMult     = part.skinInternalConductionMult;
            baseCondMult        = part.heatConductivity;

            ConfigNode node = SSTUConfigNodeUtils.parseConfigNode(configNodeData);

            //stand-alone modular heat-shield setup
            if (standAlonePart)
            {
                ConfigNode[] modelNodes = node.GetNodes("MODEL");
                model = new ModelModule <SingleModelData, SSTUModularHeatShield>(part, this, part.transform.FindRecursive("model"), ModelOrientation.CENTRAL, nameof(modelPersistentData), nameof(currentShieldModel), nameof(currentShieldTexture));
                model.setupModelList(SingleModelData.parseModels(modelNodes));
                model.setupModel();
                model.model.updateScaleForDiameter(currentDiameter);
                model.setPosition(0, ModelOrientation.CENTRAL);
                model.model.updateModel();
                updateAttachNodes(false);
                updateDragCube();
            }

            ConfigNode[] typeNodes = node.GetNodes("SHIELDTYPE");
            shieldTypeData        = HeatShieldTypeData.load(typeNodes);
            currentShieldTypeData = Array.Find(shieldTypeData, m => m.baseType.name == currentShieldType);
            updateModuleStats();

            updatePartCost();
            if (!initializedResources && (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight))
            {
                updatePartResources();
                initializedResources = true;
            }
        }
コード例 #9
0
        private void initialize(bool start)
        {
            if (initialized)
            {
                return;
            }
            initialized = true;

            topNodeNames    = SSTUUtils.parseCSV(topManagedNodes);
            bottomNodeNames = SSTUUtils.parseCSV(bottomManagedNodes);

            ConfigNode node = SSTUConfigNodeUtils.parseConfigNode(configNodeData);

            coreModule = new ModelModule <ServiceModuleCoreModel, SSTUModularServiceModule>(part, this, getRootTransform("MSC-CORE", true), ModelOrientation.TOP, nameof(coreModulePersistentData), nameof(currentCore), nameof(currentCoreTexture));
            coreModule.getSymmetryModule = m => m.coreModule;
            coreModule.setupModelList(ModelData.parseModels(node.GetNodes("CORE"), m => new ServiceModuleCoreModel(m)));

            topModule = new ModelModule <SingleModelData, SSTUModularServiceModule>(part, this, getRootTransform("MSC-TOP", true), ModelOrientation.TOP, nameof(topModulePersistentData), nameof(currentTop), nameof(currentTopTexture));
            topModule.getSymmetryModule  = m => m.topModule;
            topModule.getValidSelections = m => topModule.models.FindAll(s => s.canSwitchTo(part, topNodeNames));

            bottomModule = new ModelModule <SingleModelData, SSTUModularServiceModule>(part, this, getRootTransform("MSC-BOTTOM", true), ModelOrientation.BOTTOM, nameof(bottomModulePersistentData), nameof(currentBottom), nameof(currentBottomTexture));
            bottomModule.getSymmetryModule  = m => m.bottomModule;
            bottomModule.getValidSelections = m => bottomModule.models.FindAll(s => s.canSwitchTo(part, bottomNodeNames));

            solarModule = new ModelModule <SolarData, SSTUModularServiceModule>(part, this, getRootTransform("MSC-Solar", true), ModelOrientation.CENTRAL, null, nameof(currentSolar), null);
            solarModule.getSymmetryModule = m => m.solarModule;
            solarModule.setupModelList(ModelData.parseModels(node.GetNodes("SOLAR"), m => new SolarData(m)));
            solarModule.getValidSelections = delegate(IEnumerable <SolarData> all)
            {
                //System.Linq.Enumerable.Where(all, s => s.isAvailable(upgradesApplied));
                float scale = coreModule.model.currentDiameterScale;
                //find all solar panels that are unlocked via upgrades/tech-tree
                List <SolarData> unlocked = solarModule.models.FindAll(s => s.isAvailable(upgradesApplied));
                //filter those to find only the ones available for the current
                List <SolarData> availableByScale = unlocked.FindAll(s => coreModule.model.isValidSolarOption(s.name, scale));
                return(availableByScale);
            };
            solarModule.preModelSetup = delegate(SolarData d)
            {
                d.positions = coreModule.model.getPanelConfiguration(d.name).getScaledPositions(coreModule.model.currentDiameterScale);
            };

            rcsModule = new ModelModule <ServiceModuleRCSModelData, SSTUModularServiceModule>(part, this, getRootTransform("MSC-Rcs", true), ModelOrientation.CENTRAL, null, nameof(currentRCS), null);
            rcsModule.getSymmetryModule = m => m.rcsModule;
            rcsModule.setupModelList(ModelData.parseModels(node.GetNodes("RCS"), m => new ServiceModuleRCSModelData(m)));
            rcsModule.getValidSelections = m => rcsModule.models.FindAll(s => s.isAvailable(upgradesApplied));

            List <ConfigNode> tops    = new List <ConfigNode>();
            List <ConfigNode> bottoms = new List <ConfigNode>();

            ConfigNode[] mNodes = node.GetNodes("CAP");
            ConfigNode   mNode;
            int          len = mNodes.Length;

            for (int i = 0; i < len; i++)
            {
                mNode = mNodes[i];
                if (mNode.GetBoolValue("useForTop", true))
                {
                    tops.Add(mNode);
                }
                if (mNode.GetBoolValue("useForBottom", true))
                {
                    bottoms.Add(mNode);
                }
            }
            topModule.setupModelList(SingleModelData.parseModels(tops.ToArray()));
            bottomModule.setupModelList(SingleModelData.parseModels(bottoms.ToArray()));

            tops.Clear();
            bottoms.Clear();
            topModule.setupModel();
            coreModule.setupModel();
            coreModule.model.updateScaleForDiameter(currentDiameter);
            bottomModule.setupModel();
            solarModule.setupModel();
            rcsModule.setupModel();
            rcsModule.model.renameThrustTransforms(rcsThrustTransformName);

            updateModulePositions();
            updateMassAndCost();
            updateAttachNodes(false);
            SSTUStockInterop.updatePartHighlighting(part);
        }
コード例 #10
0
        /// <summary>
        /// Restores ModelData instances from config node data, and populates the 'currentModule' instances with the currently enabled modules.
        /// </summary>
        private void loadConfigData()
        {
            ConfigNode node = SSTUConfigNodeUtils.parseConfigNode(configNodeData);

            ConfigNode[] tankSetsNodes = node.GetNodes("TANKSET");
            ConfigNode[] tankNodes     = node.GetNodes("TANK");
            ConfigNode[] mountNodes    = node.GetNodes("CAP");

            variantData = TankVariant.parseVariants(node.GetNodes("VARIANT"));

            tankSets = TankSet.parseSets(tankSetsNodes);
            //if no sets exist, initialize a default set to add all models to
            if (tankSets.Length == 0)
            {
                tankSets = new TankSet[1];
                ConfigNode defaultSetNode = new ConfigNode("TANKSET");
                defaultSetNode.AddValue("name", "default");
                tankSets[0] = new TankSet(defaultSetNode);
            }
            TankModelData[] mainTankModules = ModelData.parseModels <TankModelData>(tankNodes, m => new TankModelData(m));

            int     len = mainTankModules.Length;
            TankSet set;

            for (int i = 0; i < len; i++)
            {
                set = Array.Find(tankSets, m => m.name == mainTankModules[i].setName);
                //if set is not found by name, add it to the first set which is guaranteed to exist due to the default-set-adding code above.
                if (set == null)
                {
                    set = tankSets[0];
                }
                set.addModel(mainTankModules[i]);
            }

            topNodeNames    = SSTUUtils.parseCSV(topManagedNodeNames);
            bottomNodeNames = SSTUUtils.parseCSV(bottomManagedNodeNames);

            tankModule = new ModelModule <TankModelData, SSTUModularFuelTank>(part, this, getRootTransform(rootTransformName, true), ModelOrientation.CENTRAL, nameof(bodyModuleData), nameof(currentTankType), nameof(currentTankTexture));
            tankModule.getSymmetryModule = m => m.tankModule;
            tankModule.getDisplayNames   = m => SSTUUtils.getNames(m, s => s.variantName);
            tankModule.setupModelList(mainTankModules);

            currentTankSetModule = Array.Find(tankSets, m => m.name == tankModule.model.setName);
            currentTankSet       = currentTankSetModule.name;
            lastSelectedVariant  = tankModule.model.variantName;

            tankModule.getValidSelections = delegate(IEnumerable <TankModelData> data) { return(System.Linq.Enumerable.Where(data, s => s.setName == currentTankSet)); };
            tankModule.updateSelections();
            tankModule.setupModel();

            len = mountNodes.Length;
            ConfigNode             mountNode;
            List <SingleModelData> noses  = new List <SingleModelData>();
            List <SingleModelData> mounts = new List <SingleModelData>();

            for (int i = 0; i < len; i++)
            {
                mountNode = mountNodes[i];
                if (mountNode.GetBoolValue("useForNose", true))
                {
                    mountNode.SetValue("nose", "true");
                    noses.Add(new SingleModelData(mountNode));
                }
                if (mountNode.GetBoolValue("useForMount", true))
                {
                    mountNode.SetValue("nose", "false");
                    mounts.Add(new SingleModelData(mountNode));
                }
            }

            noseModule = new ModelModule <SingleModelData, SSTUModularFuelTank>(part, this, getRootTransform(rootNoseTransformName, true), ModelOrientation.TOP, nameof(noseModuleData), nameof(currentNoseType), nameof(currentNoseTexture));
            noseModule.getSymmetryModule  = m => m.noseModule;
            noseModule.getValidSelections = delegate(IEnumerable <SingleModelData> data)
            {
                return(System.Linq.Enumerable.Where(data, m => m.canSwitchTo(part, topNodeNames) && TankVariant.isValidNose(m.name, tankModule.model.variantName, variantData)));
            };
            noseModule.setupModelList(noses);
            noseModule.setupModel();

            mountModule = new ModelModule <SingleModelData, SSTUModularFuelTank>(part, this, getRootTransform(rootMountTransformName, true), ModelOrientation.BOTTOM, nameof(mountModuleData), nameof(currentMountType), nameof(currentMountTexture));
            mountModule.getSymmetryModule  = m => m.mountModule;
            mountModule.getValidSelections = delegate(IEnumerable <SingleModelData> data)
            {
                return(System.Linq.Enumerable.Where(data, m => m.canSwitchTo(part, bottomNodeNames) && TankVariant.isValidMount(m.name, tankModule.model.variantName, variantData)));
            };
            mountModule.setupModelList(mounts);
            mountModule.setupModel();
        }
コード例 #11
0
        private void initialize(bool start)
        {
            if (initialized)
            {
                return;
            }
            initialized = true;

            topNodeNames    = SSTUUtils.parseCSV(topManagedNodes);
            bottomNodeNames = SSTUUtils.parseCSV(bottomManagedNodes);

            ConfigNode node = SSTUConfigNodeUtils.parseConfigNode(configNodeData);

            coreModule = new ModelModule <SingleModelData, SSTUModularStationCore>(part, this, getRootTransform("MSC-CORE", true), ModelOrientation.TOP, nameof(coreModulePersistentData), nameof(currentCore), nameof(currentCoreTexture));
            coreModule.getSymmetryModule = m => m.coreModule;
            coreModule.setupModelList(SingleModelData.parseModels(node.GetNodes("CORE")));

            topModule = new ModelModule <SingleModelData, SSTUModularStationCore>(part, this, getRootTransform("MSC-TOP", true), ModelOrientation.TOP, nameof(topModulePersistentData), nameof(currentTop), nameof(currentTopTexture));
            topModule.getSymmetryModule  = m => m.topModule;
            topModule.getValidSelections = m => topModule.models.FindAll(s => s.canSwitchTo(part, topNodeNames));

            bottomModule = new ModelModule <SingleModelData, SSTUModularStationCore>(part, this, getRootTransform("MSC-BOTTOM", true), ModelOrientation.BOTTOM, nameof(bottomModulePersistentData), nameof(currentBottom), nameof(currentBottomTexture));
            bottomModule.getSymmetryModule  = m => m.bottomModule;
            bottomModule.getValidSelections = m => bottomModule.models.FindAll(s => s.canSwitchTo(part, bottomNodeNames));

            solarModule = new ModelModule <SolarData, SSTUModularStationCore>(part, this, getRootTransform("MSC-Solar", true), ModelOrientation.CENTRAL, null, nameof(currentSolar), null);
            solarModule.getSymmetryModule = m => m.solarModule;
            solarModule.setupModelList(SingleModelData.parseModels(node.GetNodes("SOLAR"), m => new SolarData(m)));
            solarModule.getValidSelections = m => solarModule.models.FindAll(s => s.isAvailable(upgradesApplied));

            List <ConfigNode> tops    = new List <ConfigNode>();
            List <ConfigNode> bottoms = new List <ConfigNode>();

            ConfigNode[] mNodes = node.GetNodes("CAP");
            ConfigNode   mNode;
            int          len = mNodes.Length;

            for (int i = 0; i < len; i++)
            {
                mNode = mNodes[i];
                if (mNode.GetBoolValue("useForTop", true))
                {
                    tops.Add(mNode);
                }
                if (mNode.GetBoolValue("useForBottom", true))
                {
                    bottoms.Add(mNode);
                }
            }
            topModule.setupModelList(SingleModelData.parseModels(tops.ToArray()));
            bottomModule.setupModelList(SingleModelData.parseModels(bottoms.ToArray()));

            tops.Clear();
            bottoms.Clear();
            topModule.setupModel();
            coreModule.setupModel();//TODO -- only setup core module if not the prefab part -- else need to add transform updating/fx-updating for RCS and engine modules, as they lack proper handling for transform swapping at runtime
            bottomModule.setupModel();
            solarModule.setupModel();

            updateModulePositions();
            updateMassAndCost();
            updateAttachNodes(false);
            SSTUStockInterop.updatePartHighlighting(part);
        }