How to load a `RenderTexture` in Rhino?

Hello,

Following Nathan’s response, I’m trying to test a custom RenderTexture.

It took me about an hour to realize that the texture loads when the plugin is loaded.
However, I’m creating a Grasshopper component with a ScriptComponent, so no plugin, no loading! :upside_down_face:
And I’ve been creating empty materials for an hour before finally deciding to open Visual Studio…

So, I was wondering how to load the texture into Rhino’s texture table.
I looked through the documentation and tried RenderTextureTable.Add, but nothing seems to work…?

(I use Rhino 7)

Thanks,
Jmv

Whenever you want to create a custom render content you need to use RenderContent.RegisterContent Method . For RhinoCycles I use the overload RenderContent.RegisterContent Method (Assembly, Guid), the use of which you can see here RhinoCycles/Plugin.cs at 52a64a2c1be0a3a4f8171926e97f84a5b94868f8 · mcneel/RhinoCycles · GitHub .

For this to work you will have to have a plug-in, since RenderContent implementations (RenderEnvironment, RenderMaterial and RenderTexture) is tied to the specific plug-in that provides those.

Creating an instance of a specific RenderContent and have it be in the document you can do with RenderContentType.NewContentFromTypeId Method (Guid, RhinoDoc)

For more info how to create different RenderContent, how to access, etc you can check out my literate examples here https://jesterking.github.io/rhipy/ .

I imagine it’s not exactly Tolkien, but I’ll have something to read before falling asleep tonight… :slight_smile:
Thanks, Nathan!

Hmm, that’s an interesting idea. Have Hobbits and Elves discussing code as an adventure…

1 Like

Hi @kitjmv
This is a Python code to create xml material with texture, is that what you asking for?

Oh yes!
Using Grasshopper materials is a good idea; it could make things easier for me… Thank you @seg !
However, I’m not sure if these materials interact with Rhino’s different rendering modes (Raytracing, shadows, lighting),
but it’s a good idea!

Note that this GH script suggestion uses on-disk image files.

Happy Birthday, @nathanletwory !! (That was indeed the red cake next to your name on Sunday, right?)

I managed to get your example working, but I’ve completely given up on trying to run my own tests with a Rhino plugin.

As a user who writes commands or Grasshopper components to assist in my work (often temporary and for specific projects), it’s been years since I stopped writing plugins for my tools.

Rhino still uses the system registry as a personal database. There’s no way to uninstall a plugin without manually tinkering with the Windows registry. I create dozens of commands or components each year, and I don’t want all of that recorded by default.

For the topic at hand, creating a plugin for three lines of code for a test is truly inconvenient. The Yak package system is great, but again, it’s not designed for writing small tools or running quick tests.

Nowadays, with tools like Visual Studio, Chrome, Blender, etc., launching specific instances is as easy as specifying a configuration directory on the command line. With Rhino, this is still not possible.
We’re either developers, jewelers, architects, marine designers, or concept artists—meaning we’re always stuck with (1 system registry == 1 Rhino == 1 Activity).

But when I’m developing, I don’t want VisualArq loaded; when I’m doing architecture, I don’t need XNurbs. Each time I change how I use the software, I either have to go into the registry or block the plugins from loading in the options and then restart Rhino.

In any case, it’s not practical for me as a user. So, I resort to workarounds, like creating fake plugins that I then use in my scripts or resorting to System.Reflection to use internal functions and load Grasshopper components.
And all this is no more complicated than creating a temporary plugin to allow scripts to register temporary commands, components, or textures, or simply changing the internal keyword to public. (And from experience, this hasn’t caused any issues for quite a while…)

Of course, this isn’t about you personally, Nathan. The Rhino loading system has been around for a long time. I’m mainly writing this to raise awareness of this inconsistency: on one hand, plugins require assemblies to load on startup, while shiny new tools like ScriptEditor can’t load commands, components, or textures.

As with commands and components, I was ready to use workaround methods to load RenderContent. When I looked at the RegisterContent function, I saw there were quite a few steps and several registry updates required. It seems too risky to try hacking it.

But I realized that if you exposed the following function, it would solve the problem:

public static Type[] RegisterContent(IEnumerable<Type> renderContentTypes, Guid pluginId)

This would simply enable the loading of RenderContent associated with a plugin, rather than only from DLLs.

I hope all this feedback will be useful. My current project will be modeled in Rhino 7, and I’m not expecting any updates, so for now, I’ve given up on using RenderTexture

Best regards,
jmv

That is essentially RenderContent.RegisterContent Method (PlugIn) .

Thank you, yes, I levelled up indeed (:

Just be mindful that workarounds aren’t supported. If something internal gets changed then your code doing workarounds may cause crashes.

Personally I think the complaint about the registry isn’t very valid. If you already resort to System.Reflection then it is easy enough to have a .reg file that just deletes the key you want to delete. And is a whole lot more safe to do than mucking around in internals you’re not supposed to poke around in anyway. The public API is what we can support, beyond that bets are off.

Although I am not aware of strong enforcement policy, technically reverse engineering and decompiling is against the EULA. Don’t know if it matters, but wanted to throw it out there.

I would argue it is still best and in the long run easiest to work on a plug-in doing what you want to do. And instead of creating many plug-ins you can always have one plug-in into which you add all the things you want to test out etc.

There will be no changes to loading stuff here in Rhino 8 I don’t think, and I don’t know if it would happen in Rhino 9 either.

The plug-in ID is necessary at least since that is part of the ‘fingerprint’ of a custom RenderContent creation.

Indeed, I’ve just recently started doing that, as I didn’t know how to do it until now. In my case, I’m using a C# script.

delete_plugin.csx
using System;
using Microsoft.Win32;

var todo = new Dictionary <string, string> ();
RegistryKey HKEY_USERS = Registry.Users;

foreach (string profil in HKEY_USERS.GetSubKeyNames())
{
    var pluginsKey = HKEY_USERS.OpenSubKey (profil + "\\SOFTWARE\\McNeel\\Rhinoceros\\7.0\\Plug-Ins");
    if (pluginsKey == null) continue;

    foreach (string id in pluginsKey.GetSubKeyNames())
    {
        if (pluginsKey.OpenSubKey (id) is not RegistryKey idKey ||
            idKey.GetValue ("EnglishName") is not string name ||
            name != NAME_OF_THE_PLUGIN
        )   continue;

        todo.Add (name, profil + "\\SOFTWARE\\McNeel\\Rhinoceros\\7.0\\Plug-Ins\\" + id);
        idKey.Close ();
    }

    pluginsKey.Close ();
}

if (todo.Count == 0)
{
    Console.WriteLine ("No plugins found.");
    return;
}

foreach (var item in todo)
    Console.WriteLine (item.Key);

Console.WriteLine ("Do you delete Keys [y|n]?");
var consoleKeyInfo = Console.ReadKey ();
Console.WriteLine ();
if (consoleKeyInfo.KeyChar == 'y' || consoleKeyInfo.KeyChar == 'Y')
{
    foreach (var item in todo)
    {
        HKEY_USERS.DeleteSubKeyTree (item.Value);
        Console.WriteLine ("DELETE " +  item.Value);
    }
}

I don’t know if there’s a better way, but it’s the most practical option I have at the moment.
However, it’s strange that this is something we have to do ourselves. I wonder why the decision was made not to include a delete button…

Yes, you’re right—I probably shouldn’t say out loud that I hook into high-level private functions like LoadGHA or Canvas_DragDrop to make Grasshopper respond differently when a JSON file is dropped onto the canvas.

OK, I’ll keep using tools that are practical for me! :slight_smile: