Jump to content

[ANSWERED] CFG to AnimationCurve / KeyFrame


Recommended Posts

I did some searching around regarding AnimationCurve's, and found some good things. However, what I did not find, was anywhere that explains the easiest way to come from the keys in the cfg file

someModule
{
  someAnimationCurve
  {
    key = 0 320
    key = 1 280
    key = 6 0.001
  }
}

reading that in C# (plugin) into an AnimationCurve object

FloatCurve curve = new FloatCurve();
curve.add(0,320);
curve.add(1,280)
curve.add(6,0.001);

So what is the most efficient way to populate AnimationCurve from the cfg file format we know in KSP?

other related question reference:

for answer see

and

 

Edited by Warezcrawler
added reference to other related question
Link to comment
Share on other sites

23 minutes ago, sarbian said:

In the thread you liked I talk about FloatCurve.  FloatCurve are a wrapper for AnimationCurve and has a Load call (here it would be curve.Load(someAnimationCurveNode)

Thanks for the reply. But how do I get the "someAnimationCurveNode" loaded into the code in the first place. Is that some specific KSPField type? Or am I forced into using confignode directly.

If the last thing is the case, then how do I get that specific node from my module "someModule"?

Link to comment
Share on other sites

So after a lot of trials and errors with config node, I think the question is (if there are no standard  KSPField type)...

How do you load configurations which are in the CFG files for e.g. an engine (GameData\MyMod\myCFGfile.cfg) that are not persisted in the save file.

As far as I can discern, the node in the "OnLoad" is only the node of the module which is persisted in the save file.

 

Spoiler

public override void OnLoad(ConfigNode node)
{
  string[] NodeValues;

  Debug.Log("ConfigNode Debug");

  Debug.Log("node.name  " + node.name);
  Debug.Log("node.id  " + node.id);
  Debug.Log("node.CountNodes  " + node.CountNodes);
  Debug.Log("node.CountValues  " + node.CountValues);
  Debug.Log("node.ToString  " + node.ToString());

  NodeValues = node.GetValues();

  Debug.Log("foreach (string n in NodeValues)");
  foreach (string n in NodeValues)
  {
    Debug.Log("n  " + n);
  }

  Debug.Log("foreach (ConfigNode n in node.nodes)");
  foreach (ConfigNode n in node.nodes)
  {
    Debug.Log("n.name  " + n.name);
    Debug.Log("n.id  " + n.id);
    Debug.Log("n.CountNodes  " + n.CountNodes);
    Debug.Log("n.CountValues  " + n.CountValues);
  }
}

 

Link to comment
Share on other sites

To persist data in the save file OnSave and OnLoad are the methods you need.

The node OnSave passes is the "same" confignode that gets passed to the OnLoad method. I say "same" because the confignode values will be identical strings, but they technically aren't the same C# object so you can't reference them that way.

So you'd have to save your FloatCurve to the node in OnSave and re-create it in OnLoad. It would be a bit of code because ConfigNodes only accept string values.

Now I'm not sure if Squads done the heavy lifting, if there is a FloatCurve save method that returns a ConfigNode it is just two lines of code:

in OnSave:
node.AddNode("NodeNameString",MyFloatCurve.Save());

in OnLoad:
MyFloatCurve.Load(node.GetNode("NodeNameString"));

If you want to load from a file on disk, format the file as a ConfigNode and pull it into KSP with the ConfigNode.Load() command.

D.

Link to comment
Share on other sites

7 hours ago, Diazo said:

If you want to load from a file on disk, format the file as a ConfigNode and pull it into KSP with the ConfigNode.Load() command.

Thanks for the answer Diazo. I want to load the configuration of the part which is not in the persistent file, find my module and load the exact part I was missing, in this instance it is the floatcurve. If that means loading directly from the file, then how do I do that? How do I find the file? And how do I load the relevant part?

Are we absolutely sure squad does not have a standard way, like KSPField to load such things?

 

7 hours ago, Diazo said:

To persist data in the save file OnSave and OnLoad are the methods you need.

While I'm still not 100% sure how to use confignode to read and write to the persistent file, I have found lot's of examples and think I can figure that out. However, I'm not actually trying to read and write to that in this instance, I just pointed out, that the "OnLoad" that passes the node, does not hold the parts configuration, but only the persisted part, and that I not usable in this instance.

Just to try and be clearer. I am creating a module which has a floatcurve. The floatcurve is to be configured in the module (cfg) of the part. I have no way of knowing beforehand which parts have my module or which floatcurve is configured. (Just as is the case for ModuleEnginesFX). So basic question is still. How do I get the floatcurve into the code.

somePart
{
  someModule
  {
    someAnimationCurve
    {
      key = 0 320
      key = 1 280
      key = 6 0.001
    }
  }
}

Pieces of the puzzle from above answers (hopefully ,ore to come)

  • When the floatcurve node is aquired it is loaded into the floatcurve by a simple Load statement.
    • curve.Load(someAnimationCurveNode)
Link to comment
Share on other sites

Okay, I wasn't quite answering the question you'd asked.

Now, I can't actually track 100% how things work, but let's start with a ModuleEngines as it does exactly this for an atmosphereCurve.

Using the "liquidEngine24-77", in the part.cfg you have:

MODULE
	{
		name = ModuleEnginesFX
		thrustVectorTransformName = thrustTransform
		exhaustDamage = True
		ignitionThreshold = 0.1
		minThrust = 0
		maxThrust = 16
		heatProduction = 150
		fxOffset = 0, 0, 0.01
		EngineType = LiquidFuel
		exhaustDamageDistanceOffset = 0.12
		PROPELLANT
		{
			name = LiquidFuel
			ratio = 0.9
			DrawGauge = True
		}
		PROPELLANT
		{
			name = Oxidizer
			ratio = 1.1
		}
		atmosphereCurve
		{
			key = 0 290
			key = 1 250
			key = 7 0.001
		}
	}

Note the "atmosphereCurve" sub-node.

Note this engine uses ModuleEnginesFX, but that inherits from ModuleEngines so looking at the ModuleEngines partModule we have:

[KSPField]
public FloatCurve atmosphereCurve;

Note the exact same spelling, what I think should happen here is that when KSP loads and process all it's KSPFields, it will take the atmosphereCurve sub-node in the part.cfg and load it into the atmosphereCurve FloatCurve in code for you.

The issue is that I can't track how this happens so I'm not sure it will work without testing and I have nothing remotely similar setup on my IDE to actually test this with.

Note that isPersistent is false so any changes to this floatCurve won't save, but it looks like it will work to load.

 

If that doesn't work, the best case I can think of is to do it manually:

ConfigNode EntirePartCFG = GameDatabase.Instance.GetConfigNode("MyModFolder/part"; 
//note that file extensions don't come in, this would find the file at GameData\MyModFolder\part.cfg as a confignode

ConfigNode MyPartModule = EntirePartCFG.GetNode("MyPartModule");
//find our partmodule in the part.cfg file

FloatCurve MyFloatCurve = new FloatCurve.Load(MyPartModule.GetNode("FloatCurveSubNode");
//load the floatcurve from file, use an existing engine's part.cfg for the formatting

One very handy trick when working with confignodes is the ToString() method:

Debug.Log(MyConfigNode);
//prints a single line to the log with the name of the confignode

Debug.Log(MyConfigNode.ToString());
//prints the entire contents of the confignode to the log so you can see exactly what is going on inside one.

I think that gets you going now but let me know how it goes.

D.

Edited by Diazo
Link to comment
Share on other sites

20 hours ago, Diazo said:

[KSPField]
public FloatCurve atmosphereCurve;

Well as far as my tests show, doing this yields an empty floatcurve :( I was so confident this was the hint, but I don't think so.

I did this

//C# plugin

[KSPField]
public FloatCurve ThrottleISPCurve;

public override void OnLoad(ConfigNode node)
{
  Debug.Log("ThrottleISPCurve.maxTime\n" + ThrottleISPCurve.maxTime);
  Debug.Log("ThrottleISPCurve.minTime\n" + ThrottleISPCurve.minTime);
  Debug.Log("ThrottleISPCurve.Curve.keys.ToString()\n" + ThrottleISPCurve.Curve.keys.ToString());
  Debug.Log("Print Keys from KeyFrame");
  foreach (Keyframe k in ThrottleISPCurve.Curve.keys)
  {
    Debug.Log("k.time: " + k.time);
    Debug.Log("k.value: " + k.value);
    Debug.Log("k.inTangent: " + k.inTangent);
    Debug.Log("k.outTangent: " + k.outTangent);
    Debug.Log("k.tangentMode: " + k.tangentMode);
  }
}

LOG Result
[LOG 14:36:48.949] ThrottleISPCurve.maxTime
-3.402823E+38
[LOG 14:36:48.949] ThrottleISPCurve.minTime
3.402823E+38
[LOG 14:36:48.949] ThrottleISPCurve.Curve.keys.ToString()
UnityEngine.Keyframe[]
[LOG 14:36:48.949] Print Keys from KeyFrame

I think I have to look further into using confignode for this. But I do not like having to reference a specific cfg file. Is there really no way of accessing the "prefab" (isn't that what they call it) where KSP stores the settings it is using, incl. all non persisted informations.

 

20 hours ago, Diazo said:

Debug.Log(MyConfigNode.ToString()); //prints the entire contents of the confignode to the log so you can see exactly what is going on inside one.

This trick is great, thank you very much for that one. I must say it was like searching for a needle in a haystack without knowing which haystack I was looking in.... This helps a lot!

Link to comment
Share on other sites

So to link to the ThrottleISPCurve, you had that sub-node in your part.cfg right?

//Rest of Part.cfg file snipped
MODULE
{
	name=MyTestModule
	ThrottleISPCurve
	{
		key = 0 1
		key = 1 100
	}
}

As for a link to the configNode for a part, I've never found a good way to link to it so I just ended up doing my own lookup of it with the GameDatabase command linked in my last post.

Now you would have to lookup the part.cfg location as you can't hardcode it if you are going to use it on multiple modules. I'm not sure where to get that but I'd start by looking at the model information to see if it's saved in there.

Failing that, I would try to see if the ProtoPart has the information needed, you can look that up by partName.

D.

Edited by Diazo
Link to comment
Share on other sites

2 minutes ago, Diazo said:

So to link to the ThrottleISPCurve, you had that sub-node in your part.cfg right?

Yes... Sorry, I forgot to include the when I put in my code snip

	MODULE
	{
		name = GTI_MultiModeEngineFX
		engineID = Ion_Normal;Ion_HV;Ion_OD
		GUIengineID = Normal;High Velocity;Overdrive
		
		ThrottleISPCurve		//Throttle %	Multiplier
		{	
			key = 0 2
			key = 1 1
		}
		
		debugMode = true
		
		availableInFlight = true
		availableInEditor = true
	}
Spoiler

PART        //:NEEDS[SmokeScreen]
{
    name = GTI_ionEngine
    module = Part
    author = Warezcrawler            //NovaSilisko
    MODEL
    {
        model = Squad/Parts/Engine/ionEngine/model
    }
    //mesh = model.mu
    scale = 1
    rescaleFactor = 1
    node_stack_top = 0.0, 0.2135562, 0.0, 0.0, 1.0, 0.0, 1
    node_stack_bottom = 0.0, -0.1872844, 0.0, 0.0, -1.0, 0.0, 1
    TechRequired = ionPropulsion
    entryCost = 16800
    cost = 12000
    category = Engine
    subcategory = 0
    title = GTI-2020 "Icicle" Multi Electric Propulsion System
    manufacturer = Guybrush Threepwood Industries
    description = By emitting ionized xenon gas through a small thruster port, Icicle can produce incredibly efficient propulsion, but with a downside of very low thrust and high energy usage. According to ISP Electronics sales reps, the rumours of this engine being powered by "dark magic" are largely exaggerated. GTIndustries have upgraded to former "Dawn" version, with the capability of higher efficiency or higher thrust while in space - the extra versatility can be the difference between failure or success.
    attachRules = 1,0,1,1,0
    mass = 0.25
    dragModelType = default
    maximum_drag = 0.2
    minimum_drag = 0.2
    angularDrag = 2
    crashTolerance = 7
    maxTemp = 2000 // = 3600
    bulkheadProfiles = size0
    tags = (icicle deep drive efficient engine (ion probe thruster vacuum xenon

    MODULE
    {
        name = GTI_MultiModeEngineFX
        engineID = Ion_Normal;Ion_HV;Ion_OD
        GUIengineID = Normal;High Velocity;Overdrive
        
        ThrottleISPCurve        //Throttle %    Multiplier
        {    
            key = 0 2
            key = 1 1
        }

        
        debugMode = true
        
        availableInFlight = true
        availableInEditor = true
    }

    MODULE
    {
        name = ModuleEnginesFX
        engineID = Ion_Normal
        thrustVectorTransformName = thrustTransform
        exhaustDamage = False
        ignitionThreshold = 0.1
        minThrust = 0
        maxThrust = 2
        heatProduction = 0
        powerEffectName = IonPlume
        EngineType = Electric
        PROPELLANT
        {
            name = ElectricCharge
            ratio = 1.8
            DrawGauge = True
            minResToLeave = 1.0
        }
        PROPELLANT
        {
            name = XenonGas
            ratio = 0.1
            DrawGauge = True
        }
        atmosphereCurve
        {
            key = 0 5200
            key = 1 100
            key = 1.2 0.001
        }
    }
    MODULE
    {...  }

    MODULE
    {
        name = ModuleEnginesFX
        engineID = Ion_HV
        thrustVectorTransformName = thrustTransform
        exhaustDamage = False
        ignitionThreshold = 0.1
        minThrust = 0
        maxThrust = 1
        heatProduction = 0
        powerEffectName = IonPlume_HV
        EngineType = Electric
        PROPELLANT
        {
            name = ElectricCharge
            ratio = 1.8
            DrawGauge = True
            minResToLeave = 1.0
        }
        PROPELLANT
        {
            name = XenonGas
            ratio = 0.1
            DrawGauge = True
        }
        atmosphereCurve
        {
            key = 0 6200
            key = 1 10
            key = 1.2 0.0001
        }
    }
    MODULE
    {... }

    MODULE
    {
        name = ModuleEnginesFX
        engineID = Ion_OD
        thrustVectorTransformName = thrustTransform
        exhaustDamage = False
        ignitionThreshold = 0.1
        minThrust = 0
        maxThrust = 10
        heatProduction = 350
        powerEffectName = powerflame_OD
        EngineType = Electric
        PROPELLANT
        {
            name = ElectricCharge
            ratio = 0.8
            DrawGauge = True
            minResToLeave = 1.0
        }
        PROPELLANT
        {
            name = LiquidFuel
            ratio = 0.2
            DrawGauge = True
        }
        PROPELLANT
        {
            name = XenonGas
            ratio = 0.1
            DrawGauge = True
        }
        atmosphereCurve
        {
            key = 0 4000
            key = 1 300
            key = 1.2 0.01
        }
    }
    MODULE
    {... }
    
    
    //GTIndustries/FX/HotRockets/
    EFFECTS
    { ...}
}

I'm currently experimenting with the gamedatabase as you suggested. It does seem to have a life of it own, but I'll keep at it.

And I will see if there is something interesting in  ProtoPart, thanks for that suggestion.

Link to comment
Share on other sites

@Diazo I have found out some more because of your hints :)

see this post for the stadard way of getting a FloatCurve from cfg configuration

Alternate method

I found that in the object "PartLoader" we have access for all parts and their confignodes. So if we have a custom module, then we can do as follows

ConfigNode myConfigNode = new ConfigNode();
for (int i = 0; i < PartLoader.Instance.loadedParts.Count; i++)
{
	if (this.part.name == PartLoader.Instance.loadedParts[i].name)
	{
		myConfigNode = PartLoader.Instance.loadedParts[i].partConfig;
		break;
	}
}

Now "myConfigNode" have the PART confignode incl. all details, which we can then work on.

In "PartLoader.Instance.loadedParts.partUrl" we have access to the current path, which can be used with 

ConfigNode EntirePartCFG = GameDatabase.Instance.GetConfigNode("MyModFolder/part"); 
//note that file extensions don't come in, this would find the file at GameData\MyModFolder\part.cfg as a confignode

as you pointed me to.

I don't know if any of this was new to you, but I wanted to share in case it was, because it might help.

I'm not done however, because I have not worked much with ConfigNodes, I still need to figure out how to take the part and find the relevant module and in turn the relevant subNode/floatcurve. For this I will dig into google and other mods that uses confignode an see if I can piece it together.

But when I have figured that out I guess it is as simple as combining this with @sarbian's hint

curve.Load(someAnimationCurveNode)

I think this means me question is now answered. We did not find an simple stock way, but a way we have. Thanks for all your help.

Edited by Warezcrawler
updated answer
Link to comment
Share on other sites

Did you ever try:

[KSPField]
public FloatCurve ThrottleISPCurve = new FloatCurve();

I just looked at Near Future and that mod loads FloatCurves without any of the extra stuff you're having to do, just with the KSPField tag and nothing else. I can't see anything else it does different then the original test we tried.

https://github.com/ChrisAdderley/NearFuturePropulsion/blob/21cbf5fd6018e042b1a372bf10d62f4fc47a860d/Source/VariablePowerEngine.cs

D.

Edited by Diazo
Link to comment
Share on other sites

2 minutes ago, Diazo said:

Did you ever try:


[KSPField]
public FloatCurve ThrottleISPCurve = new FloatCurve();

I just looked at Near Future and that mod loads FloatCurves without any of the extra stuff you're having to do, just with the KSPField tag and nothing else. I can't see anything else it does different then the original test we tried.

https://github.com/ChrisAdderley/NearFuturePropulsion/blob/21cbf5fd6018e042b1a372bf10d62f4fc47a860d/Source/VariablePowerEngine.cs

D.

I did try it (though I did not have the "= new FloatCurve()" part in the declaration), but I agree with you when reading the Near Future reference. It seems he does indeed loads them that simply. I will try that again, as that is very much preferred.

But nothing is so bad that it is not good for something. I did find out how I could read the original configuration. :) And every day you learn something new is a good day.

Thanks.

Link to comment
Share on other sites

21 minutes ago, Diazo said:

I just looked at Near Future and that mod loads FloatCurves without any of the extra stuff you're having to do

I've looked through what he does, and I do actually not think he actually get them loaded that way....

Here he declares the curves, and we can easily think they are indeed loaded there.
https://github.com/ChrisAdderley/NearFuturePropulsion/blob/21cbf5fd6018e042b1a372bf10d62f4fc47a860d/Source/VariableIspEngine.cs#L25-L29

Here he sets the curves based on the engines he is manipulating
https://github.com/ChrisAdderley/NearFuturePropulsion/blob/21cbf5fd6018e042b1a372bf10d62f4fc47a860d/Source/VariableIspEngine.cs#L310-L311

Here he sets some curves that look a lot like the same ones
https://github.com/ChrisAdderley/NearFuturePropulsion/blob/21cbf5fd6018e042b1a372bf10d62f4fc47a860d/Source/VariableIspEngine.cs#L167-L173

 

I did retest just now, and got the same result as before. The curves does not seem to be loaded by

[KSPField(isPersistant = false)]
public FloatCurve ThrottleISPCurve = new FloatCurve();

I can have misinterpreted the near future code, but to me it does not seem to work there either. His does just seem to populate them later in the code, and then his mod probably runs as it should.

Link to comment
Share on other sites

Okay, setting up a test on my end, it works.

Part.cfg (I used the solarpanel as my test part)

PART
{
	name = solarPanels4
	module = Part
	author = NovaSilisko
	mesh = model.mu
	rescaleFactor = 1
	node_attach = 0.0, 0.0, 0.0, 1.0, 0.0, 0.0
	TechRequired = advElectrics
	entryCost = 3800
	cost = 380
	category = Electrical
	subcategory = 0
	title = OX-4L 1x6 Photovoltaic Panels
	manufacturer = Probodobodyne Inc
	description = The OX-4 is similar in design to the SP series solar panels, but without the heavy casing.  Includes passive radiators on the reverse side for better heat dissipation. WARNING: Not retractable once deployed!
	attachRules = 0,1,0,0,1
	mass = 0.0175
	dragModelType = default
	maximum_drag = 0.2
	minimum_drag = 0.2
	angularDrag = 1
	crashTolerance = 8
	maxTemp = 1200 // = 3200
	bulkheadProfiles = srf
	thermalMassModifier = 2.0
	emissiveConstant = 0.95
	heatConductivity = 0.04 // 1/3 the default
	tags = array charge deploy e/c elect energ extend generat (light photo power solar sun track unfold volt watt	
		
	MODULE
	{
		name = ModuleDeployableSolarPanel
		resourceName = ElectricCharge
		chargeRate = 1.64
		retractable = false
		animationName = solarpanels4
		secondaryTransformName = panel3
	}
	MODULE
	{
		name = ICFNTest
		testCurve
		{
			key = 3 15
			key = 4 20
			key = 5 25
		}
	}
}

ICFNTest is the partModule I'm using to test, it contains just the following:

public class ICFNTest : PartModule
    {
        [KSPField(isPersistant = false)]
        public FloatCurve testCurve = new FloatCurve();

        public void Start()
        {
            Debug.Log("CFNTest " + testCurve.Curve.length);
            foreach(Keyframe ky in testCurve.Curve.keys)
            {
                Debug.Log("CFNTest2 " + ky.time + "|" + ky.value);
            }
        }
    }

This prints the 3 key values to the log file:

[LOG 20:29:12.921] CFNTest 3
[LOG 20:29:12.921] CFNTest2 3|15
[LOG 20:29:12.922] CFNTest2 4|20
[LOG 20:29:12.922] CFNTest2 5|25

So I'm not sure what is going on at your end at this point.

D.

edit: Looking at the code in your previous posts, can you get rid of the "//" note after ThrottleISPCurve? I think it's reading the trailing spaces as part of the name and so when it tries to load, the FloatCurve ThrottleISPCurve in code does not match "ThrottleISPCurve                  " in the part.cfg file.

Edited by Diazo
Link to comment
Share on other sites

7 hours ago, Diazo said:

So I'm not sure what is going on at your end at this point.

I have only one thing to say....... I must have done something wrong :sealed:

I will recreate your example on my end, and then when it works I will try and incorporate it into what I have done so far. Your comment of trailing space will definitely be taken into consideration.

Thanks for all your time mr. D. I will post my findings, but I don't know if I have the time today....

Link to comment
Share on other sites

8 hours ago, Diazo said:

So I'm not sure what is going on at your end at this point.

Well now I'm not sure either. I must have had some kind of typo which I have removed at some point, because it is working now. I will update in the answer. Thanks very much for your help :cool:

NOTE: I tested specifically on the trailing spaces and that was not the issue.

Link to comment
Share on other sites

Glad to be of assistance.

One thing that really jumps out at me in all this is just how powerful IConfigNode is. I don't know that I'm going to go back and rewrite anything, but had I know about it at the time it would have made things a lot simpler in my own mods in a lot of ways.

D.

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...