Author Topic: Rebuilding a Substance in the Editor from Script  (Read 2266 times)

Calls to RenderSubstanceSync() while in the editor do not appear to do anything in edit mode.  After this call, I access the textures from the substance's associated material and they haven't changed at all, but the variables my script input to the substance are correctly set on the substance graph in the inspector.

+1 here.

Setting 'shouldGenerateAllOutputs' on a graph, setting new resolution etc don't really do anything when called from script in the editor. Values change in the user interface, but they don't seem to trigger regeneration of textures and/or creating new ones. Is there any way to force the same thing as pressing "Apply" button from the script? I don't mind if it's synchronous or not, I just want to batch process a number of graphs in the editor.

Are you queuing the target substance before rendering?

Code: [Select]
graph.QueueForRender();
Substance.Game.Substance.RenderSubstancesAsync();

Yes, though I'm calling sync, not async.  I need them rerendered immediately, and besides, as I understand it, there isn't any way to know when the async function is done yet.  I should also mention that the target settings don't appear to apply, either.  I cannot change the output size, which is another function I need for my project.
Last Edit: September 25, 2018, 11:09:20 am

Yes, though I'm calling sync, not async.  I need them rerendered immediately, and besides, as I understand it, there isn't any way to know when the async function is done yet.  I should also mention that the target settings don't appear to apply, either.  I cannot change the output size, which is another function I need for my project.

You can set the output size of a graph with the following:

Code: [Select]
graph.SetInputVector2Int("$outputsize", 10, 10); //Set output size to (1024, 1024)

The values passed to set output size are not the most intuitive. 10 outputs to 1024, 9 gives you 512, 11 gives you 2048, etc. I have written some extension methods to make this task easier. The function lets you pass in slightly more understandable values:

Code: [Select]
graph.SetOutputSize(SubstanceOutputSize._1024);

You can view the forum thread for the extensions here.

When you say, "the target settings don't appear to apply" what exactly do you mean? The textures aren't using values you have set?

There's a class called TargetSettingItem in the SubstanceGraph class, and the graph has these vars:

A property signed List<TargetSettingItem> tsList {get; set;}
A method signed void SetInitialTargetSetting(List<TargetSettingItem> pList)
A property signed TargetSettingItem activeSettingItem {get;}

The api doc isn't exactly verbose in describing what the parts of the api do, it's mostly just a list of signatures, but the vars in the TargetSettingItem class definitely look like what I need on the face of it. It includes other settings I need to adjust besides output size, such as setting the graph to output raw if it's set to compressed when my script runs.  The graph object in the unity inspector completely ignores any changes made to these.

$outputsize is a Vector2Int now?  That'll probably explain why trying to set it as a Vector2, like it was in back in 2017, didn't work (not that Vector2Int existed back then).  It would be nice if we could call an array of input vars, like ProceduralPropertiesDescriptions was with ProceduralMaterial.  Not only would that avoid type issues like this, but I've had problems in the past with artists being imprecise when naming and documenting variables on their substances; I'd rather just be able to query the substance directly.  Has the type for $randomseed been changed as well?

The two numbers for the output size are the powers of 2 (e.g. 2^10 is 1024) for width and height, just as an aside for anyone reading who didn't know since Synthoid didn't explain that part.

Regarding adjusting the randomseed value:

A quick and dirty way to do this is to call:

Code: [Select]
graph.SetInputInt("$randomseed", newSeed); //Set the random seed value.
graph.QueueForRender(); //Queue the graph to refresh its textures.

I've created some extensions for the Substance in Unity plugin (thread link) that lets you randomize the random seed of a graph by just calling:

Code: [Select]
graph.RandomizeSeed();
Or if you want to manually set the seed:

Code: [Select]
graph.SetRandomSeed(256);

If I remember correctly, the tsList and pList values contain data associated with parameters, but are not the parameter values themselves. That part of the system could definitely use some more documentation. There is a way to retrieve input parameters, but it's not very straightforward:

Code: [Select]
int inputCount = NativeFunctions.cppGetNumInputs(graph.nativeHandle);
NativeTypes.Input[] inputs = NativeTypes.GetNativeInputs(graph, inputCount);

You can then iterate through the inputs array to get info about the SubstanceGraph's parameters.

Thanks a lot for that NativeFunctions method for getting the inputs; that's very useful. 

It got me thinking about other native functions, so I started throwing data at some of them that looked promising, trying to figure out what they do and if they would help.  My code works now, but it's a weird fix.

First, I found that adding a call to NativeFunctions.cppProcessOutputQueue() would finally make the substance change to my input vars, but it would consistently output the result of the previous set of input vars, not the current set.  It didn't matter when I called this function (before I set my vars, between setting and calling the rendering functions, or after the rendering functions); just adding this call got it to start responding.  I got it working fully by calling the following after setting my input vars (generator is the substance graph):

Code: [Select]
NativeFunctions.cppProcessOutputQueue();
generator.QueueForRender();
Substance.Game.Substance.RenderSubstancesSync();
NativeFunctions.cppProcessOutputQueue();
generator.QueueForRender();
Substance.Game.Substance.RenderSubstancesSync();

Yes, it needs to be repeated, and all of it needs to be there (I tried removing some of the calls, and it would revert to giving me the output from the previous values).  Very weird.