Load material from file to layer in rhinoscript

How can I load a material from file in Rhino 6+ in Rhinoscript? This is what was working in Rhino4:

Function LoadMaterialToLayer(MatFilename, LayerName)
	Set RDK = Rhino.GetPlugInObject("Renderer Development Kit")
	MatID = RDK.ContentLoadFromFile(MatFilename)
	MatIndex = Rhino.LayerMaterialIndex(LayerName)
	If MatIndex < 0 Then MatIndex = Rhino.AddMaterialToLayer(LayerName)
	LoadMaterialToLayer = RDK.SetMaterialInstanceId(MatID, MatIndex)
End Function

It works so far as loading the material in Rhino 6, but it won’t get assigned to the layer. Instead. an “unnamed” generic material gets created and assigned to the layer. I can manually assign the newly imported material to the layer, so what am I missing? Thanks.

Hi @C.S,

I’ve logged the issue so a developer can look into this.

https://mcneel.myjetbrains.com/youtrack/issue/RH-62860

In the mean time, you might consider looking at Python scripting which is a bit more mainstream these days.

– Dale

Thank you Dale. I ended up switching to calling the command “-_RenderAssignMaterialToLayer” which luckily accepts Material instance ID.

I would love to switch to Python, but with 50 thousand lines of interconnected vbs automation code spanning 16-17 years, sadly, it’s not happening. I am hoping vbs support is not dropped or gets the ugly duckling treatment in the near future for the sake of long time customers.

Hi @C.S,

RhinoScript won’t be going away anytime soon. Because it is limited to Windows-only, along with other reasons, it isn’t being actively worked on. And while we’re happy to fix issues as they arise, you probably won’t see much, if any, new functionality added.

– Dale

I am in the same boat, with complex RS tools developed over a decade; there is no way they will get recreated in Python. So I was happy to hear that at least we’ll have issues fixed. RS already got some ugly duckling treatment with the Script Editor getting worse and having a few new bugs and features gone in V6. For me the sad part is dropping the development of it while it was actually getting quite powerful with new methods added in V6, but without even small additions or modifications to RS it will be hard to keep up with Rhino changing over time.

@C.S - with the above in mind, if you ever run into a need to add some functionality to your old RS code that is not possible with RS but doable in Python, it is actually possible to mix them, or rather run Python code inside RS or even exchange data between. I had to come up with this method to be able to keep the tools up to speed.

You can take a look at this sample : changing an advanced setting from RS that is normally not possible unless using Python/RhinoCommon:

Call Main()
Sub Main()

	Call Rhino.Print("Current ZoomScaleFactor: " & PythonZoomWheelScaleGet)
	Dim dbScale : dbScale = Rhino.GetReal("New ZoomScaleFactor?", 0.9, 0, 1)
	If Not isnull(dbScale) Then 
		Call PythonZoomWheelScaleSet(dbScale)
		Call Rhino.Print("Current ZoomScaleFactor: " & PythonZoomWheelScaleGet)	
	End If
	
End Sub


Function PythonZoomWheelScaleGet()
	Dim s : s = ""
	'python code goes here in this format:
	s = s & "import scriptcontext as sc" & vbCrLf
	s = s & "import Rhino.ApplicationSettings as appsettings" & vbCrLf
	s = s & "sc.doc.Strings.SetString(""SAMPLE"", ""ZoomScale"", appsettings.ViewSettings.ZoomScale.ToString())" & vbCrLf
	'end of Python code
	
	'run Python code
	Call Rhino.Command("_-RunPythonScript (" & s & ")", False)
	
	'retrieve the data from Python to RhinoScript via DocumentUserText
	PythonZoomWheelScaleGet = Cdbl(Rhino.GetDocumentUserText("SAMPLE\ZoomScale"))
	'clear DocUserText
	Call Rhino.SetDocumentUserText("SAMPLE\ZoomScale")
End Function


Sub PythonZoomWheelScaleSet(dbScale)
	Dim s : s = ""
	'python code goes here in ths format:
	s = s & "import scriptcontext as sc" & vbCrLf
	s = s & "import Rhino.ApplicationSettings as appsettings" & vbCrLf
	'adding RS variable value to Python code as String
	s = s & "appsettings.ViewSettings.ZoomScale = " & Cstr(dbScale) & vbCrLf
	'end of Python code
	
	'run Python code
	Call Rhino.Command("_-RunPythonScript (" & s & ")", False)
End Sub

The above method works even with complex Python code, including conduits etc. Obviously it’s using DocumentUserText to exchange data between languages, so depending on data type the conversion would be needed to/from string. So for tasks where speed is critical this may not be the best, but we managed to expand the old RS tools using this method quite well.

Here is a code to convert any Python script into a set of RS multi-line string to be executed by RunPythonScript command inside RS:

Option Explicit
Call ConvertPythonCodeToStrings()

Sub ConvertPythonCodeToStrings()
	
	'get text file 
	Dim i,arrText : arrText = Rhino.ReadTextFile(Rhino.OpenFileName(), False, False)
	'replace quotes with double-quotes and add NextLine character
	For i=0 To Ubound(arrText)
		arrText(i) = "s = s & " & chr(34) & Replace(arrText(i), chr(34), chr(34) & chr(34)) & chr(34) & " & vbCrLf"
	Next
	'save text file
	Call Rhino.WriteTextFile(Rhino.SaveFileName(), arrText, False, 0)
	
End Sub

hth,

–jarek

1 Like

Hi @C.S,

I have fixed the issue that stopped your function LoadMaterialToLayer from working. This fix will be available in version 7.5.

Note that the script is relying on old Rhino 4 behavior which has now changed. You will see a ‘spare’ material appear in the Material Editor. This is because of the call to Rhino.AddMaterialToLayer() which now adds a new material for the layer to the Material Editor. Then the script’s call to RDK.SetMaterialInstanceId() redirects the layer material to the loaded one, leaving the other one unused.

If this is a problem, you can use RenderRemoveUnusedMaterials to get rid of the unused ones.

Regards,

John