Problem with undoing material changes made via script

Hello,

There seems to be an issue with undo not working properly for material changes made via a script.
For example, when running a script that changes material color of a selected layer, and then Undoing the script, visually (in viewport) all looks OK as if the change was reverted, but the material color in Material Editor stays as if Undo did nothing, and also when file gets saved and reopened, the Undo had no effect and the changed version of the material is saved in the file.

Here is sample file (for the script to work, Custom material needs to be assigned to selected layer, and the layer “selected” in the Layers panel): ScriptedMaterialChangeUndo_Problem.3dm (254.5 KB)

This happens regardless if I use simple RhinoScript method like this:

Option Explicit
Call Main()
Sub Main()
	Dim i : i = Rhino.LayerMaterialIndex(Rhino.SelectedLayers()(0))
	Call Rhino.MaterialColor(i, vbRed)
End Sub

or use RDK method:

Option Explicit
Call Main()
Sub Main()
	Dim i : i = Rhino.LayerMaterialIndex(Rhino.SelectedLayers()(0))
	Dim rdk : Set rdk = Rhino.GetPlugInObject("Renderer Development Kit")
	Dim idRDK : idRDK = rdk.MaterialInstanceId(i)
	Call rdk.ContentParameter(idRDK, "diffuse", vbRed)
End Sub

@Dale, do you have any idea why this may be happening?
@johnc, pinging you as well since in the past you managed to help solve similar issues.

Any help and insight would be greatly appreciated. Thank you-

–jarek

Hi @Jarek,

I have filed a new bug report:

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

Sorry about this. It’s an oversight which I can easily fix. The fix should be available in the next service release of Rhino 7.

John

1 Like

Hi @johnc, that’s great, thank you for working on this fix!

@johnc - there is another thing that may be related to the fix you mentioned.
If I add a new material either via Material Editor UI or via script, before it is assigned to anything in the file, RhinoScript methods like:

MaterialCount
MaterialIds

don’t pick it up - it is visible in the materials panel but invisible to the script engine, unless I add it to an object or layer. This makes it impossible to create a new material via script “standalone” and then assign it to the layer/object.

This is a sample code that adds a Custom material to the file, yet the above script methods don’t see it:

Dim rdk : Set rdk = Rhino.GetPlugInObject("Renderer Development Kit")
Dim F : F = rdk.FactoryList()
Dim n : n = rdk.FactoryNewContent(F(0))

Would this be something related to the original issue here?

Hi @Jarek,

No, that won’t be related. I’ll look into it ASAP.

John

Thanks John, please let me know if any script samples/file samples would be helpful in relation to the 2nd issue I have mentioned.

Seems like there is still a disconnect between RhinoScript methods that were developed way before the current materials system was in place, and how materials behave now.

One thing I scratch my head about is why RhinoScript MaterialID method and RDK MaterialID method for the same material return different material IDs:

if you create a new layer, have it selected, the script below will add a new material to the layer and get its ID via RhinoScript and RDK methods. They are two different things. Do you know why this is happening and should that even be a case? Sorry to bombard you with random stuff (all materials-related) - just trying to make something work with materials via script over here since last week and I got defeated : )

Option Explicit

Call Main()
Sub Main()

	Dim rdk : Set rdk = Rhino.GetPlugInObject("Renderer Development Kit")
	
	Dim idMain,idRDK,m,L
	
	L = Rhino.SelectedLayers()(0)
	
	'get material index from selected layer and assign if none
		m = Rhino.LayerMaterialIndex(L)
	If m = -1 Then m = Rhino.AddMaterialToLayer(L)
	
	'get RhinoScript Mat ID and RDK MatID for the layer's material:
	idMain = Rhino.MaterialId(m)
	idRDK = rdk.MaterialInstanceId(m)
	
	Rhino.Print "MaterialID: " & idMain
	Rhino.Print "RDKMaterialID: " & idRDK
	
End Sub

Hi @Jarek,

No need to apologize; this stuff can be hard to understand. Usually, users don’t need to know the details of how Rhino works under the hood, but in the case of scripting, I think you need to know a bit more. I always find diagrams easier to understand than text, and in this case I think a diagram explains it more clearly, so I made a simple diagram showing the relationship between Rhino objects, Rhino materials and RDK materials:


Before the RDK existed, Rhino just had a simple material table with materials that were closely related to how OpenGL works. These materials have their own ids within the table. The RDK extended this so that you can have very sophisticated material/texture hierarchies which can be implemented in 3rd-party plug-ins. But when we added this, we had to do it without breaking existing plug-ins and scripts, so the original Rhino material table still exists alongside the RDK extension. The coupling between the two is quite loose, being a UUID linking the Rhino material to the RDK material. This UUID is the RDK Material Instance Id.

So,

  • Rhino.MaterialId gets you the id of the Rhino material in the Rhino material table.
  • rdk.MaterialInstanceId gets you the id of the RDK material in the RDK material list.

The one you need depends on which table/list you will later be using with the id.

Note the Rhino material at index 2 with zero for the RDK ID. This is a material with no RDK material association and is something we are trying to get rid of. These materials appear in the material editor as ‘legacy V4 materials’, AKA ‘proxies’. They can be created by legacy commands and scripts. We are gradually updating all our commands to use the RDK material list instead of the Rhino material table.

I hope this sheds a bit more light on things. I will now take a look at your second question about MaterialCount and MaterialIds…

John

@Jarek FWIW check this and this. It may be hard to grasp at first glance BUT at some point, you will connect more dots then you can imagine :wink:

Just fiddle with it a bit more.

It may be a bit overdoing it but you can always make dup, change it, apply dup, do whatever you’re after, reassign original one and delete dup. (ofc I’m talking about dup at rdk level)

@johnc your diagram is very good to understand this relation. Btw. maybe while logging bugs we will log lack of RDK access in RhCommon FileIO ? I can imagne this topic is very deep as those are probably saved in PluginDataTable along with plugin data so we would need to have option to enter the RDK plugin data table.

Hi @Jarek,

Regarding MaterialCount and MaterialIds, the problem is that those two functions are operating on the ‘legacy’ Rhino material table which only contain materials that have been assigned to objects or layers. They don’t know anything about the RDK material list.

When you create an RDK material, you’re adding an RDK material to the document’s RDK material list. This does not create an associated material in the Rhino material table. When you assign the RDK material to an object, we create a Rhino material in the material table and associate it with the RDK material via the instance id. At this point, MaterialCount and MaterialIds will see that material.

I understand that this is confusing, but it’s been caused by the way Rhino has evolved since those functions were conceived.

Unfortunately, I’m not very knowledgeable about scripting. In fact I’m learning how to do it by trying to help you. I found that you can add a new material to an object or layer by using these functions:

Rhino.AddMaterialToObject()
Rhino.AddMaterialToLayer()

passing the id of the object. These create the material and assign it. I see you already know about the 2nd one. This is probably not helpful.

John

Hi @D-W,

If I understand correctly, I think we are already looking into the FileIO problems. Here is a bug that was filed recently.

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

Sorting this out will be a big job but rest assured that we are looking into it.

John

Hi @johnc,

thanks for passing the link to a pile. Any move in this direction will be really appreciated :slight_smile:

I had a really hard time with FileIO and I had to make a nasty workaround to automate the second instance of Rhino and copy/paste models to achieve desired results. You can read through it - in short, I wanted to export the model in a block with related materials - each time I ended with an incomplete 3dm or huge mess in the material table. Collecting bits and pieces and patching the whole thing with scripting was the only way out to do this.

Ok. I will put a period here to not mix these topics :wink:

@johnc ,

First of all, thank you very much for taking time to not only look into this but also explain the inner workings of this. The diagram is super helpful, along with other explanations. All of this is kind of what I had a sense of, based on the failures I was getting and our conversations from the past in the legacy materials area, so now at least I know where the problem lies, not a solution yet and I am not sure if it is possible since it would involve some updates to some of the scripting methods that deal with materials and the new vs old system.

@D-W - thanks for your input, I have been working with RDK SDK methods for a while and quite familiar with what it can and cannot do. It was hard to grasp but I think I grasped it enough to know there is a problem.

Believe me, I fiddled with it a lot, before I post here I usually spend days investigating and trying things out before bothering the Devs. here. I think it is acknowledged there is a problem with some of the legacy methods.

Let me explain below why that will not work - RDK-only level does not connect to the rest of the methods available.

So based on John’s explanations and investigations, here is what is happening now:
The “modern” materials operate in its own space, that is not visible to the “basic” or old RS methods that deal with materials, or at least some of them.
I can do anything with RDK materials, create, change any single setting and parameter, copy, delete etc. but there is no “bridge” from them to the RS methods. For example a newly created RDK material is not visible to RS methods unless it is assigned to a Layer or Object, but it is impossible to assign it to them via RS RDK (AFAIK) and also not possible to assign them via RhinoScript since they are not visible to its methods, until assigned … ugh.

So the question is if there is anything that can be done about it for RS. I know it’s been decided there is no more new developments to RS but I look at it as maintenance of the old stuff that used to work and does not anymore with the new materials.

Here is what at this point would be needed:

  • Ability to rerefence RDK MaterialID and assign it to layers/objects via RS
  • Ability to add RDK material to Rhino material table (so it has its index)
  • Ability to find Material’s index and/or ID(s) by Material Name (I assume names must be unique, right?)
  • Ability to remove Material from Layer or Object (currently Rhino.LayerMaterialIndex strLayer, -1 does not work as expected
  • Ability to make Layer/Object material Unique (scripted equivalent of clicking the little yellow warning box that shows in layer/object material dialog when using a shared/instanced material).

@Dale, in case you are seeing this, would it be at all possible to bring the RS Materials methods up to date to play well with the RDK, based on the list above?
We can use the exisiting RDK methods to do all the nuanced material parameter and creation work that RS does not provide access to, but a bridge between the two is needed so they can be assigned to objects/layers and properly evaluated.

Thank you,

–jarek

@Jarek,

I asked Andy about this and he said that the preferred method of scripting Rhino these days is by using Python. I believe we are concentrating our efforts on keeping the Python system as up-to-date as possible. I know this is inconvenient when you have invested so much effort with Rhino Script, but this is the way it’s going.

I don’t actually think the Python syntax is terribly different and it might not be that hard to start migrating to it. But having said that, you might well uncover a bunch of similar bugs. If that happens though, we would be much more likely to fix them in the Python system than in the Rhino Script system.

This might not be useful, but I thought I should mention it.

John

1 Like

hi @johnc,

Thanks for the update. This is not news to me, I mentioned before here that I know and been told in the past that on different occasions that RhinoScript will be no longer developed, but at the same time it was pledged it will be maintained to stay functional so the tools developed before still work, and perhaps (maybe I am reading too much into it) the methods will at least try to keep up with changes that come with Rhino changing.
So I don’t expect a new set of tools to handle SubD or many other new areas that were developed. But I am hoping that a) RS tools that no longer work can be fixed to work if at all possible b) RS tools that work but became limited or return invalid information due to change in Rhino (like new Transparency parameter in Color added in V7) can fall under this “maintenance plan”.

In this case here RhinoScript and RDK Scripting methods take care of 99% of what may be needed for dealing with Materials, but they operate in 2 different spaces, so it may not even be a case of a need to update any RS methods or RDK methods, but add a couple of RS methods that could provide a bridge to RDK-created materials could be used by existing RS methods (per your diagram - so they can become visible in the Rhino Material Table).

There are two problems I have with the Python advice (and having said that I realize much bigger potential for coding in Rhino using Python than it used to be via RhinoScript)

Problem # 1 - on the scripting level, RhinoScript is actually still much more mature and developed vs Python, when we consider what was ported to RhinoScriptSyntax, which was advertised as the “easy transition” from RS to Python. Indeed, on the basic scripting level, not much syntax or complexity difference between the two, but in Python many methods available in RhinoScript have never been ported. Take a look just at the material methods, the two I mentioned earlier here are not even there, so it’s not like they have bugs - they don’t because they don’t exist ( MaterialCount,MaterialIds):

To do more things with Python, you actually need to dive into RhinoCommon, which is awesome, but calling RhinoScript and Python RhinoCommon scripting for me is not a fair comparison. It’s a different ball game, especially for a casual coder like myself.

So what is being said by RMA is that the days of “easy” scripting are over, if you are really interested you have to get into advanced RhinoCommon stuff, otherwise RhinoScriptSyntax in Python will not get you far. This is all fair and understandable, but please understand that this is not just a scripting language preference but the matter of much higher complexity when dealing with RhinoCommon Python.

We were able to create and use amazing scripted tools with RhinoScript using just this basic level coding, kudos to RMA for making it possible before but too bad this “easy” level is being abandoned, which brings me to:

Problem # 2, and the reason I still keep using RS, finding problems and trying to ask for some maintenance. I started scripting in Rhino 15 years ago, well before Python scripting was on the horizon. So since then we developed a big and complex toolset based on this. tens of thousands of lines of code. It is just not possible at all to go now and rewrite all that, it would need to be the next life or a two-year long sabbatical to just do that. These tools still work great and I maintain it all with a great success, but every now and then I either find a RhinoScript bug that was there for a while, or some glitch caused by some changes in Rhino itself. Then I come here and ask for help and solutions. Sometimes I am lucky, sometimes I am not, but the answer “just transition to Python” considering the above is not helpful.
For these legacy tools I actually found a way to use Python code to implement some functionality in them not accessible by RhinoScript, but there are limits to it. That’s why I am here : )

Sorry It was not meant as a rant and not even directed at you since you’ve been super helpful and thorough with your support, I just wanted to give some more context of why I am asking for help with these things.

many thanks,

–jarek

For now I think I figured out a workaround to access the RDK materials regardless of RhinoScript access, I just need to use good old Rhino commands to assign them to objects/layers by knowing their name which I can get from the RDK SDK:

For example here is how to make a selected layer’s material unique by making a duplicate via RDK and assigning it to selected layer via RhinoScript using Rhino’s “_-Layer” command. From then on it becomes visible to RhinoScript.

Call Main()
Sub Main()

	'make selected layer material unique
	Dim rdk : Set rdk = Rhino.GetPlugInObject("Renderer Development Kit")
	Dim L : L = Rhino.SelectedLayers()(0)
	Dim ind : ind = Rhino.LayerMaterialIndex(L)
	Dim idRDK : idRDK = rdk.MaterialInstanceId(ind)
	Dim idCopy : idCopy = rdk.DuplicateContent(idRDK)
	Dim strName : strName = rdk.ContentInstanceName(idCopy)
	Call Rhino.Command("-Layer _Material " & chr(34) & Rhino.LayerName(L) & chr(34) & " " & chr(34) & strName & chr(34) & " _EnterEnd")
		
End Sub

So what I wrote above still stands, but for the immediate needs I will make-do with this method to do the work needed. @johnc thank you very much again for explaining the inner workings / wirings of the material systems.

best,

–jarek

Hi @Jarek,

I completely understand your position and appreciate the fact that you have a huge amount of legacy scripting code. I’d like to try and help but my problem is that I have very limited knowledge of how the scripting systems work. I will talk to Andy about this again and see if we can come up with something that helps you. After all, it was easy to fix the undo bug, so maybe there are a few other ‘easy’ fixes that would be worth a lot to you. I can at least look into that.

John

1 Like

Thanks John, I appreciate it!

For the problem I was trying to solve the command line solution worked. If there was a chance to do anything more for scripting, it would be this:

  • Ability to rerefence RDK MaterialID and assign it to layers/objects via RS
  • Ability to remove Material from Layer or Object (currently Rhino.LayerMaterialIndex strLayer, -1 does not work as expected

These are probably more in @Dale’s realm as they would need to happen on the RS end, not RDK.
Actually only the first one would be the “bridge” method I talked about earlier, the 2nd one is just what I consider a bug in the old RS method that just came up while working on some material tools.

Best,

–jarek

RH-65476 is fixed in Rhino 7 Service Release 11 Release Candidate