Jump to content

A crash-course in model nodes [tutorial].


ThePsuedoMonkey

Recommended Posts

When 0.20 was released we were given a

few tools for addon development, one of which was called "MODEL Nodes." This allows multiple parts models to be loaded as a single part, gives options to reposition, rescale, and rotate the models, and even the ability to overload their textures. Since this thing is seen as a single part there will be no physics calculations between the models in the node, which has the obvious application of improving performance of large vessels. This tool is called from a part config file, in place of the standard model definitions.

Premade model nodes

thrust plates.

has a bunch of assemblies and has been developing a plugin to do the work for you.

A few of
old ones.

Building from scratch:

For a relatively simple demonstration, lets combine a structural quad adapter with some aerospikes.

5wZsoySl.png

The first thing we need to know is that the position values of a model node are relative to its center of mass, and each model is centered on its own center of mass. To determine where to position the models in the node, we can open the config of the Toroidal Aerospike and check its node definitions

node_stack_top = 0.0, 0.0, 0.0, 0.0, 1.0, 0.0

This tells us that the aerospikes only attachment node is at its center of mass (first 3 numbers), and faces straight up (4th-6th numbers). The position values are in meters in the X,Y,Z coordinates, and the direction is a vector with the X,Y,Z components. Now we need to know how to place the aerospikes relative to the structural piece, so we look at its node definitions

node_stack_top = 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2

node_stack_bottom01 = 0.625, -0.75, 0.625, 0.0, 1.0, 0.0, 1

node_stack_bottom02 = 0.625, -0.75, -0.625, 0.0, 1.0, 0.0, 1

node_stack_bottom03 = -0.625, -0.75, 0.625, 0.0, 1.0, 0.0, 1

node_stack_bottom04 = -0.625, -0.75, -0.625, 0.0, 1.0, 0.0, 1

If you looked closely, you saw a seventh number, which just refers to the visual size of the attachment node in the VAB/SPH. Once again, the top node is at the models center of mass, but the bottom nodes are 0.75m below it. The Y-values of the aerospikes and the adapter must be separated by the total distance between their centers of mass (i.e. 0.0 + [0.0 - -0.75] = 0.75), while the X and Y values should correspond to the offsets in the adapters attachment nodes. The last piece of information we need is where to define the center of mass; the aerospikes are 6T and the adaptor is 0.2T, so the CoM is [0.75 * 0.2 / 6.2 = 0.02419] above the Aerospikes. Lets round this to 0.025 for simplicity and we've determined that the positions of the models are

adaptor = 0.0, 0.725, 0.0

aerospike1 = 0.625, -0.025, 0.625

aerospike2 = 0.625, -0.025, -0.625

aerospike3 = -0.625, -0.025, -0.625

aerospike4 = -0.625, -0.025, 0.625

Now we just convert this data into the model node format, and include the rotation of the aerospikes that symmetry would create. It will be important later on that rotation is applied in the order Z,X,Y.

MODEL
{
model = Squad/Parts/Structural/adapterLargeSmallQuad/model
position = 0,0.725,0
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Engine/toroidalAerospike/model
position = 0.625, -0.025, 0.625
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Engine/toroidalAerospike/model
position = 0.625, -0.025, -0.625
rotation = 0, 90, 0
}
MODEL
{
model = Squad/Parts/Engine/toroidalAerospike/model
position = -0.625, -0.025, 0.625
rotation = 0, 180, 0
}
MODEL
{
model = Squad/Parts/Engine/toroidalAerospike/model
position = -0.625, -0.025, -0.625
rotation = 0, 270, 0
}

If you were to load this into a part config, you'd find that the aerospikes are a little smaller than they should be: this is because their model was made before the switch to 1.25m parts so they must be rescaled. You will also find that this engine doesn't overheat as much as its counterpart. The position of the top attachment node should reflect its original position. The FX definitions will be mimicked for each engine, so we do not need to change them from the default settings in the Aerospike config. The cost, mass, and engine properties will need to be updated as well. When we fix these issues, the part config should look similar to this

-------------------------------------------------------------

PART
{
name = ZWeldedEngineA
module = Part
author = ThePsuedoMonkey

MODEL
{
model = Squad/Parts/Engine/toroidalAerospike/model
position = 0.625, -0.025, 0.625
scale = 1.25, 1.25, 1.25
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Engine/toroidalAerospike/model
position = -0.625, -0.025, 0.625
scale = 1.25, 1.25, 1.25
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Engine/toroidalAerospike/model
position = 0.625, -0.025, -0.625
scale = 1.25, 1.25, 1.25
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Engine/toroidalAerospike/model
position = -0.625, -0.025, -0.625
scale = 1.25, 1.25, 1.25
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Structural/adapterLargeSmallQuad/model
position = 0.0, 0.725, 0.0
scale = 1, 1, 1
rotation = 0, 0, 0
}

rescaleFactor = 1

// --- node definitions ---
node_stack_top = 0.0, 0.725, 0.0, 0.0, 1.0, 0.0, 2
// --- FX definitions ---
fx_exhaustFlame_blue = 0.0, -0.0, 0.0, 0.0, 1.0, 0.0, running
fx_exhaustLight_blue = 0.0, -0.0, 0.0, 0.0, 0.0, 1.0, running
fx_smokeTrail_light = 0.0, -0.0, 0.0, 0.0, 1.0, 0.0, running
fx_exhaustSparks_flameout = 0.0, -0.0, 0.0, 0.0, 1.0, 0.0, flameout
// --- Sound FX definition ---
sound_vent_medium = engage
sound_rocket_hard = running
sound_vent_soft = disengage
sound_explosion_low = flameout

cost = 4200
category = Propulsion
subcategory = 0
title = Quadraspike
manufacturer = KittyHawk United
description = Just a bunch of Aerospikes welded together, move along people.
// attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision
attachRules = 1,0,1,0,0

mass = 6.2
dragModelType = default
crashTolerance = 12
maxTemp = 3400
breakingForce = 50
breakingTorque = 50

MODULE
{
name = ModuleEngines
thrustVectorTransformName = thrustTransform
exhaustDamage = True
ignitionThreshold = 0.1
minThrust = 0
maxThrust = 700
heatProduction = 600
fxOffset = 0, 0, 0.25
PROPELLANT
{
name = LiquidFuel
ratio = 0.9
DrawGauge = True
}
PROPELLANT
{
name = Oxidizer
ratio = 1.1
}
atmosphereCurve
{
key = 0 390
key = 1 388
}
}
MODULE
{
name = ModuleAnimateHeat
}
}

In essence, you can think of it as modifying a part config file and telling it where to place the models.

Building from a *.Craft file

Rocket engines are cool and all, but you know what's awesome? Motorbikes.

pBjCsIzl.png

If you open the craft file, you get a lot of data that you don't really need and some that needs interpretation. For instance, here is the data on the probe core.

PART
{
part = probeCoreCube_4294680264
partName = Part
pos = -0.1005701,5.050443,0.2725831
rot = 0.7071068,1.256074E-15,1.256074E-15,0.7071068
attRot = -3.247939E-15,-4.385655E-23,1.776357E-15,0.9999999
mir = 1,1,1
istg = 0
dstg = 0
sidx = -1
sqor = -1
attm = 0
link = SmallGearBay_4294680234
EVENTS
{
}
ACTIONS
{
}
MODULE
{
name = ModuleCommand
isEnabled = True
controlSrcStatusText =
EVENTS
{
MakeReference
{
active = True
guiActive = True
guiIcon = Control From Here
guiName = Control From Here
category = Control From Here
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
RenameVessel
{
active = True
guiActive = True
guiIcon = Rename Vessel
guiName = Rename Vessel
category = Rename Vessel
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
}
}
MODULE
{
name = ModuleReactionWheel
isEnabled = True
stateString = Active
WheelState = Active
EVENTS
{
OnToggle
{
active = True
guiActive = True
guiIcon = Toggle Torque
guiName = Toggle Torque
category = Toggle Torque
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
Activate
{
actionGroup = None
}
Deactivate
{
actionGroup = None
}
Toggle
{
actionGroup = None
}
}
}
MODULE
{
name = ModuleSAS
isEnabled = True
EVENTS
{
}
ACTIONS
{
}
}
MODULE
{
name = ModuleTripLogger
isEnabled = False
EVENTS
{
}
ACTIONS
{
}
Surfaced
{
}
Flew
{
}
FlewBy
{
}
Orbited
{
}
SubOrbited
{
}
}
}

The pertinent data approximates to

part = probeCoreCube_4294680264

pos = -0.1005701,5.050443,0.2725831

rot = sqrt(2)/2, ~0, ~0, sqrt(2)/2

attRot = ~0, ~0, ~0, ~1

It's listed position is relative to the center of the floor in the SPH, and the rotation values are split into two fields and appear to be given as either the sine or cosine of an angle. I'm not exactly sure how to interpret it, so I'll just ignore it for now and just use the position information and knowledge of the directory structure to get a starting point.

MODEL
{
model = Squad/Parts/Command/probeCoreCube/model
position = -0.1005701, 5.050443, 0.2725831
}
MODEL
{
model = Squad/Parts/Wheel/SmallGearBay/model
position = -0.10057,4.642117,0.4109806
}
MODEL
{
model = Squad/Parts/Utility/spotLight1/model
position = -0.1005701,5.039511,0.7625586
}
MODEL
{
model = Squad/Parts/Structural/structuralPanel1/model
position = -0.10057,4.713505,-0.807771
}
MODEL
{
model = Squad/Parts/Command/sasModule/model
position = -0.10057,4.923228,-0.807772
}
MODEL
{
model = Squad/Parts/Command/seatExternalCmd/model
position = -0.10057,5.274733,-0.6029134
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.6008557,4.713508,-1.33992
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.7094498,4.713508,-1.578224
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.8164898,4.713508,-1.813576
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.923505,4.713508,-2.050247
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.923505,4.713508,-2.307719
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.9235049,4.713508,-2.565187
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.9235051,4.476118,-2.586868
}
MODEL
{
model = Squad/Parts/Wheel/wheelMed/model
position = -0.8157229,4.476118,-2.54761
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.8229024,4.923228,-0.8520064
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.8846188,4.692904,-0.8666103
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.9512572,4.444208,-0.8666108
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -1.017895,4.195512,-0.8666106
}
MODEL
{
model = Squad/Parts/Electrical/RTG/model
position = -0.5352382,4.713508,-0.8125257
}
MODEL
{
model = Squad/Parts/Electrical/RTG/model
position = 0.3340983,4.713508,-0.8125257
}

The first task is to calculate the CoM and then convert these values using that information. The CoM for those values would be at (0.10057,4.863508, -0.76329), so we subtract that from each coordinate and check the result in the SPH. It turns out that the Y and Z coordinates were flipped, and when we fix that we see that the Z has been mirrored, so multiply those by -1 and all of the parts should be in their proper position. Now we need to deal with rotation. The axes in the SPH are {X = parallel to floor and entrance, Y = parallel to floor and walls, Z = parallel to walls and entrance} and rotations are in degrees and seem to resolve in the order Z, X, Y. Put it together and what have you got?

PART
{
name = ZWeldedCommandB
module = Part
author = ThePsuedoMonkey

MODEL
{
model = Squad/Parts/Command/probeCoreCube/model
position = 0.0, 1.03590, -0.1870
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Wheel/SmallGearBay/model
position = 0.0, 1.17225, 0.2214
rotation = 90, 0, 180
}
MODEL
{
model = Squad/Parts/Utility/spotLight1/model
position = 0.0, 1.52585, -0.1760
rotation = 15, 0, 180
}
MODEL
{
model = Squad/Parts/Structural/structuralPanel1/model
position = 0.0, -0.0445, 0.15
rotation = 90, 0, 0
}
MODEL
{
model = Squad/Parts/Command/sasModule/model
position = 0.0, -0.0445, -0.0597
rotation = 90, 0, 0
}
MODEL
{
model = Squad/Parts/Command/seatExternalCmd/model
position = 0.0, 0.1604, -0.4112
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.50025, -0.5767, 0.15
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.60885, -0.815, 0.15
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.71590, -1.0503, 0.15
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.82290, -1.287, 0.15
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.82290, -1.5445, 0.15
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.82290, -1.8025, 0.15
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.82290, -1.8236, 0.3874
rotation = 90, 0, 0
}
MODEL
{
model = Squad/Parts/Wheel/wheelMed/model
position = -0.7151, -1.7843, 0.3874
rotation = 90, 0, 180
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.72230, -0.0888, -0.0597
rotation = 0, 0, 90
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.78700, -0.1035, 0.1706
rotation = 0, 80, 90
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.85065, -0.1035, 0.4193
rotation = 0, 80, 90
}
MODEL
{
model = Squad/Parts/Structural/strutCube/model
position = -0.91730, -0.1035, 0.668
rotation = 0, 80, 90
}
MODEL
{
model = Squad/Parts/Electrical/RTG/model
position = -0.435, -0.05, 0.15
rotation = 0, 0, 0
}
MODEL
{
model = Squad/Parts/Electrical/RTG/model
position = 0.435, -0.05, 0.15
rotation = 0, 0, 0
}
rescaleFactor = 1

// --- node definitions ---
node_stack_top = 0.0, -0.072, 0.15, 0.0, 0.0, 1.0, 0

cost = 5110
category = Pods
subcategory = 0
title = KU Motorbike
manufacturer = KittyHawk United
description = Not sure if functional...
// attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision
attachRules = 1,0,1,1,0

mass = 0.73
dragModelType = default
maximum_drag = 0.2
minimum_drag = 0.2
angularDrag = 2
crashTolerance = 20
breakingForce = 200
breakingTorque = 200
maxTemp = 3500
vesselType = Rover

MODULE
{
name = ModuleLight
lightName = spotlight
useAnimationDim = true
lightBrightenSpeed = 2.5
lightDimSpeed = 2.5
resourceAmount = 0.04
animationName = LightAnimation
useResources = true
}
MODULE
{
name = ModuleLight
lightName = LandingLight
useAutoDim = true
}
MODULE
{
name = ModuleLandingGear
}
MODULE
{
name = ModuleGenerator
isAlwaysActive = true
OUTPUT_RESOURCE
{
name = ElectricCharge
rate = 1.5
}
}
MODULE
{
name = ModuleWheel
hasMotor = true
resourceName = ElectricCharge
resourceConsumptionRate = 0.7
canSteer = true
controlAxisType = Forward
steeringModeType = AutomaticSteer
brakeTorque = 500
brakeSpeed = 2.5
impactTolerance = 300
overSpeedDamage = 60

WHEEL
{
wheelName = wheel
wheelColliderName = wheelCollider
suspensionTransformName = suspensionTraverse
suspensionNeutralPointName = suspensionNeutralPoint
damagedObjectName = bustedwheel
rotateX = 1
rotateY = 0
rotateZ = 0
}

steeringCurve
{
key = 0 10
key = 10 6
key = 30 2
}

torqueCurve
{
key = 0 100 0 0
key = 2.5 70 0 0
key = 30 0 0 0
}

}

MODULE
{
name = FXModuleConstrainPosition
matchRotation = true
matchPosition = false
CONSTRAINFX
{
targetName = steering
moversName = trackSteering
}

}
MODULE
{
name = KerbalSeat
seatPivotName = seatPivot
ejectDirection = 0, 1, 0.2
}
MODULE
{
name = ModuleCommand
minimumCrew = 0
RESOURCE
{
name = ElectricCharge
rate = 0.02777778
}
}
MODULE
{
name = ModuleReactionWheel
PitchTorque = 20.5
YawTorque = 20.5
RollTorque = 20.5
RESOURCE
{
name = ElectricCharge
rate = 0.325
}
}
MODULE
{
name = ModuleSAS
}
RESOURCE
{
name = ElectricCharge
amount = 410
maxAmount = 410
}
}

Something like this:

5TbIH2Rl.png

Caveats

If you played with the motorbike, you will notice a few things are very different from the craft we based this on. You are only able to toggle the light on the landing gear, and the driving power is coming from the landing gear instead of the electric wheel. These are both a result of being unable to resolve multiple instances of MODULE functions: since the landing gear was the first applicable model node for each of those in the definitions list, they are only applied to the landing gear. This is also why we didn't define four engine modules in the quadraspike. There may be ways around this, but I've not learned them yet. Another problem is that trying to turn with the wheels doesn't work, since it is seen as neither behind nor ahead of the CoM, which also prevents some uses of RCS thrusters. Remember that this system isn't meant to replace the way KSP works, but it may help us take on more grandiose missions with better performance.
:cool:

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...