Author Topic: unity: editing multiple substances at once  (Read 6848 times)

Hi,

I have 20 different models with 20 substances applied.

each substance has:
two input maps (normal, ao)
exposed diffuse color
exposed paint wear amount
exposed paint dirt amount

now I would like to edit the color and input maps for each substance individually, but edit the paint wear and paint dirt amount for all substances at once (inside unity editor).

I wonder if this is possible and if there is a script or tutorial available?

thanks for your help!
Last Edit: March 31, 2014, 10:34:12 pm

Hi

Multi-editing of ProceduralMaterials is not currently supported in Unity.

However, there are two ways of accomplishing this:
1) Put your 20 substances in a single graph. This will obviously create a lot of outputs, but this way you can have separate inputs and a single tweak for all the materials.
2) You can write a custom editor panel that would dispatch value changes to all Substance assets present in your project for example. I don't think we have tutorials available for that kind of manipulation, but there are numerous presentations and talks about Editor scripting, so the "custom Editor panel" part is probably covered.

Best Regards,
Eric

thanks, eric!

I will try to set up a custom editor panel in unity; basically, it would be the same procedure as described in the pod tutorial scripting series

my scripting skills are very limited, but if I can get something together, I will post it here :)

So here are my first steps:

I got the Custom Editor Panel working, but I don´t know how to access the exposed Properties of my substance Materials (for now, it´s just the exposed Paintwear Property on Substance Material "EB02", which is a graph inside EB13.sbsar)

here is my code:
Code: [Select]
using UnityEngine;
using UnityEditor;
using System.Collections;

public class EB_Editor : EditorWindow
{

bool groupEnabled1;
bool groupEnabled2;
float PaintWear = 0.5F;
float PaintDirt = 0.5F;
Color GlobalColor;
ProceduralMaterial substance;


//Add menu named "EB_Editor" to the Window Menu
[MenuItem ("Window/EB_Editor")]
static void Init ()
{
//Get existing open window or if none, make a new one:
EB_Editor window = (EB_Editor)EditorWindow.GetWindow (typeof (EB_Editor));
}

//EB_Editor Content
void OnGUI ()
{
GUILayout.Label ("EB GLobal Values", EditorStyles.boldLabel);

//start GUI Layout test
groupEnabled1 = EditorGUILayout.BeginToggleGroup ("Layout Test", groupEnabled1);
PaintWear = EditorGUILayout.Slider ("PaintWear", PaintWear, 0, 1);
PaintDirt = EditorGUILayout.Slider ("PaintDirt", PaintDirt, 0, 1);

EditorGUILayout.EndToggleGroup ();
//end GUI Layout test



//here I want to dispatch the Custom PaintWear Value to the substance Materials Paintwear property
groupEnabled2 = EditorGUILayout.BeginToggleGroup ("Substance Properties", groupEnabled2);
GUILayout.TextArea ("trying to access Substance Properties");

ProceduralMaterial substance = Resources.Load("EB02", typeof(ProceduralMaterial)) as ProceduralMaterial;
PaintWear = GUILayout.HorizontalSlider(PaintWear, 0.0F, 1.0F);

//as soon as I comment out the following two lines, at least the editor panel layout works:
substance.SetProceduralFloat("Paintwear", PaintWear);
substance.RebuildTextures ();

EditorGUILayout.EndToggleGroup ();
}


}

any help appreciated!
Last Edit: April 04, 2014, 07:10:19 pm

Hi there once again,

just wanted to bump this thread; I believe, I just need some lines of code to access my substances properties - any help REALLY appreciated!
I also believe that a basic script to access substance properties in the editor might be useful to many substance/unity users.

Just would like to know, if it´s possible in that simple way that I have layed out above.

Would something like this help?
http://docs.unity3d.com/Documentation/ScriptReference/ProceduralMaterial.GetProceduralPropertyDescriptions.html

This can be used to do some ProceduralMaterial introspection and get the list of substance properties at runtime, which sounds like what you are trying to do.

Cheers,
Eric

Hi Eric,

thanks for your reply!

I don´t know, if the GetProceduralPropertyDescription does the job for me. My scripting skills are very limited, but I am working on that. I have to get a better understanding of C# to be able to arrange things properly.

I would like to access the substances properties (like color, paintwear, paintdirt) in the editor, instead of runtime. The goal is to have the option to tweak some exposed properties for all substances at once.
That´s why I have tried to use Resources.Load to directly access the substance - but obviously it doesn´t work - most likely I am using it the wrong way.
(I got the idea from this thread: http://forum.unity3d.com/threads/180862-substance-scripting-in-c)

I am a bit surprised, that managing substances properties in Unity Editor is not a topic anywhere in the substance forum neither in the tutorials - I would have at least expected to find some basic scripts on the web.


Last Edit: April 18, 2014, 01:42:30 pm

Well, the functions you would use to manipulate the Substance properties at Runtime are exactly the same one you would use in the Editor :)

You should not have to use Resources.Load to do this, just referencing the ProceduralMaterials themselves (and potentially reimporting them once the modification is done) should be enough. You can build a list of ProceduralMaterials in your project or scene by using something like:

Code: [Select]
Renderer[] renderers = Object.FindObjectsOfType(typeof(Renderer)) as Renderer[];
foreach (Renderer renderer in renderers)
{
ProceduralMaterial sbs = renderer.sharedMaterial as ProceduralMaterial;
if (sbs)
{
// You now have a valid reference to the Substance...

Furthermore, if you want to reimport the materials in the end, you should probably enclose all your modifications in a AssetDatabase.StartAssetEditing(); / AssetDatabase.StopAssetEditing(); block.

hi Eric,

thanks a lot for your help! Unfortunately I am unable to put your suggestions into context...

I have to continue learning C# to close that gap... unless anybody shares a complete example script   ::)
Last Edit: April 18, 2014, 01:43:01 pm

Hi!

here´s an update about my progress: I have gone through Bob Tabor´s C# Fundamentals lectures  (recommended! http://tinyurl.com/7eqeu9s).

Then I gave my script another try; and even if I haven´t finished it yet, I was able to achieve the desired changes to all of the substances in my scene at once. So that´s a big step for me. Thanks for your help, Eric!

I created an empty game object, attached an empty script to it and referenced that script in an editor script.
I tried to define the variables within the inspector script and access those via editor script (using base.OnInspectorGUI) - but I didn´t get that to work.

Now I have to find a way to store those values in my scene. I don´t understand, how to store those variables within the script attached to the empty game object (inspector script).

So this is the next topic, that I am going to research.

Code: [Select]
using UnityEngine;
using UnityEditor;
using System.Collections;

//used with inspector window "SBS_test_01.cs"

[CustomEditor(typeof(SBS_test_01))]
public class SBS_test_Editor : Editor
{
private Color color=Color.gray;
private float occlusion;
private float paintDirt;
private float paintWear;

public override void OnInspectorGUI()
{
EditorGUILayout.LabelField("Global Substance Settings");

//base.OnInspectorGUI();
//AssetDatabase.StartAssetEditing();

color = EditorGUILayout.ColorField ("Color", color);
occlusion=EditorGUILayout.Slider("Occlusion",occlusion, 0, 1);
paintDirt=EditorGUILayout.Slider("Paint Dirt", paintDirt, 0, 1);
paintWear=EditorGUILayout.Slider("Paint Wear", paintWear, 0, 1);

if(GUILayout.Button ("Apply Global Substance Settings"))
{
Renderer[] renderers = Object.FindObjectsOfType(typeof(Renderer)) as Renderer[];
foreach (Renderer renderer in renderers)
{
ProceduralMaterial sbs = renderer.sharedMaterial as ProceduralMaterial;

sbs.GetProceduralColor("Paint01_Color");
sbs.SetProceduralColor("Paint01_Color", color);

sbs.GetProceduralFloat ("Occlusion_Intensity");
sbs.SetProceduralFloat ("Occlusion_Intensity", occlusion);

sbs.GetProceduralFloat ("Paint01_Dirtiness");
sbs.SetProceduralFloat ("Paint01_Dirtiness", paintDirt);

sbs.GetProceduralFloat ("Paintwear");
sbs.SetProceduralFloat ("Paintwear", paintWear);

sbs.RebuildTextures();
}
SceneView.RepaintAll();
}
//AssetDatabase.StopAssetEditing();
}
}
Last Edit: May 05, 2014, 06:40:59 pm

update:

all substances have their exposed values changed at once; the SBSControllerScript stores the values;
in the scene view, the substances change as they should, each showing the proper maps changes.

but: when I save the scene and reopen the scene, the substances have their previous values.

workaround:
if I select the objects in the scene that have substances applied before saving and exiting Unity, the substances keep the correct (new) values after reopening the scene.
Any idea why that´s the case?

So how can I make the substances not return to their old values upon reopening the scene?

SBSControllerScript (attached to a game object in the scene):
Code: [Select]
using UnityEngine;
using System.Collections;
using System;

[Serializable]
public class SBSControllerScript : MonoBehaviour
{
public Color CColor = Color.cyan;
public float Occlusion=0.3f;
public float PaintDirt=0.1f;
public float PaintWear=0.2f;

public void ApplyToSubstances()
{
Renderer[] renderers = UnityEngine.Object.FindObjectsOfType(typeof(Renderer)) as Renderer[];
foreach (Renderer renderer in renderers)
{
ProceduralMaterial sbs = renderer.sharedMaterial as ProceduralMaterial;
if(sbs)
{
//Debug.Log ("found sbs");
sbs.GetProceduralColor("Paint01_Color");
sbs.SetProceduralColor("Paint01_Color", CColor);
sbs.GetProceduralFloat ("Occlusion_Intensity");
sbs.SetProceduralFloat ("Occlusion_Intensity", Occlusion);

sbs.GetProceduralFloat ("Paint01_Dirtiness");
sbs.SetProceduralFloat ("Paint01_Dirtiness", PaintDirt);
sbs.GetProceduralFloat ("Paintwear");
sbs.SetProceduralFloat ("Paintwear", PaintWear);

sbs.RebuildTextures();
}
}
}

}

and the editor script:

Code: [Select]
using UnityEngine;
using System.Collections;
using UnityEditor;

[CustomEditor(typeof(SBSControllerScript))]
public class SBSControllerScriptEditor : Editor
{
public override void OnInspectorGUI()
{
SBSControllerScript myScript = (SBSControllerScript)target;

EditorGUILayout.LabelField("PaintColor");
myScript.CColor = EditorGUILayout.ColorField(myScript.CColor);

EditorGUILayout.LabelField("Occlusion");
myScript.Occlusion = EditorGUILayout.Slider(myScript.Occlusion, 0, 1);

EditorGUILayout.LabelField("PaintDirt");
myScript.PaintDirt = EditorGUILayout.Slider(myScript.PaintDirt, 0, 1);

EditorGUILayout.LabelField("PaintWear");
myScript.PaintWear = EditorGUILayout.Slider(myScript.PaintWear, 0, 1);

if(GUILayout.Button ("Apply to all Substances"))
{
myScript.ApplyToSubstances();
}
SceneView.RepaintAll();
}
}
Last Edit: May 26, 2014, 03:39:14 pm

Here is a quick test with regular shaders, just changing the color for all materials in the scene, then saving and reopening the scene - everything works fine.

script attached to game object in scene:
Code: [Select]
using UnityEngine;
using System.Collections;

public class CubeColorScript : MonoBehaviour
{
public Color CubeColor = Color.blue;

public void ChangeColor()
{
Renderer[] renderers = UnityEngine.Object.FindObjectsOfType(typeof(Renderer)) as Renderer[];

foreach (Renderer renderer in renderers)
{
//Debug.Log ("Found 1 renderer!");
Material myMaterial = renderer.sharedMaterial as Material;

if(myMaterial)
{
myMaterial.GetColor("_Color");
myMaterial.SetColor("_Color", CubeColor);
}
}
}
}

Editor Script:
Code: [Select]
using UnityEngine;
using System.Collections;
using UnityEditor;

[CustomEditor(typeof(CubeColorScript))]
public class CubeColorScriptEditor : Editor
{
public override void OnInspectorGUI()
{
CubeColorScript myScript = (CubeColorScript)target;

myScript.CubeColor = EditorGUILayout.ColorField(myScript.CubeColor);
SceneView.RepaintAll();

if(GUI.changed)
    {
myScript.ChangeColor();
SceneView.RepaintAll();
}
}
}

I just wanted to check, if my basic funcionality is ok.

When I save the scene, I get the dialogue which asks me if I want to save the changed materials - and I don´t know why I don´t get the same dialogue with my substance-scene ...

Any help appreciated

Hey Michael,

I was wondering why you are using a custom editor. Are you building a custom tool that uses substances?

In this video, I show scripting Substance in Unity. This might be too basic than what you need.

http://youtu.be/Rq_Hl5LZW8U?t=25m23s
Integrations Product Manager / Training
wes.mcdermott@allegorithmic.com
Twitter: The3DNinja