System.AccessViolationException when reading doc.RenderMaterials[]

Hi,

i get a ‘System.AccessViolationException’ when i run the plugin, accessing doc.RenderMaterials[0] for the SECOND time. First time works.
Also when running the plugin once with multibe objects selected, no problem.
There is not much documentation on RenderMaterials …
Hope someone can help. Thanks.

       if (objRm == null)
                {
                    
                    obj.RenderMaterial = doc.RenderMaterials[0];
                    obj.CommitChanges();
                    obj.Attributes.MaterialIndex = findsRM;
                    obj.CommitChanges();
                }

full code:

    protected override Result RunCommand(RhinoDoc doc, RunMode mode)
    {
        Rhino.Input.Custom.GetObject go = new Rhino.Input.Custom.GetObject();
        go.SetCommandPrompt("Select objects to apply 18ct red gold");
        go.GroupSelect = true;
        go.SubObjectSelect = false;
        go.EnableClearObjectsOnEntry(false);
        go.EnableUnselectObjectsOnExit(false);
        go.DeselectAllBeforePostSelect = false;
        go.GetMultiple(1, 0);

        int findsRM = doc.Materials.Find("18ct red gold", true);

        for (int i = 0; i < go.ObjectCount; i++)
        {
            
            Rhino.DocObjects.ObjRef objref = go.Object(i);
            Rhino.DocObjects.RhinoObject obj = objref.Object();

            if (findsRM == -1)
            {
                Rhino.DocObjects.Material rg18ct = new Rhino.DocObjects.Material();
                rg18ct.Reflectivity = 1;
                rg18ct.ReflectionColor = System.Drawing.Color.FromArgb(255, 187, 132);
                string rg18ctName = "18ct red gold";
                rg18ct.Name = rg18ctName;
                rg18ct.CommitChanges();
                var idrg18 = doc.Materials.Add(rg18ct);

                var rmrg18 = Rhino.Render.RenderMaterial.CreateBasicMaterial(doc.Materials[idrg18]);
                doc.RenderMaterials.Add(rmrg18);
                obj.RenderMaterial = rmrg18;
                obj.CommitChanges();
            }
            else
            {
                var objRm = obj.RenderMaterial;

                if (objRm == null)
                {
                    
                    obj.RenderMaterial = doc.RenderMaterials[0];
                    obj.CommitChanges();
                    obj.Attributes.MaterialIndex = findsRM;
                    obj.CommitChanges();
                }
                else
                {
                    obj.Attributes.MaterialIndex = findsRM;
                    obj.CommitChanges();
                }
            }
        }
        doc.Views.Redraw();
        return Result.Success;
    }
}

Hi @seppeldue,

Can you include a sample model that this command repeats the issue on?

– Dale

Hi @dale,
Thanks for looking at my problem.
A new rhino scene with 3 boxes (or spheres) with no material is sufficient.
Run the command on each of these separatly.
On the first run a new material is created. On the 2nd the (now) existing material is used. When you run it the 3rd time on the last object the exception happens.
Strangely, when running the command on multiple objects, it works fine…

-Seb

Hi @seppeldue,

Thanks you. I am able to repeat this and I have logged the issue.

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

– Dale

Hi

I couldn’t figure out exactly what you were trying to do from the code, but here’s a much simplified and better way of doing what you want:

protected override Result RunCommand(RhinoDoc doc, RunMode mode)
    {
      Rhino.Input.Custom.GetObject go = new Rhino.Input.Custom.GetObject();
      go.SetCommandPrompt("Select objects to apply 18ct red gold");
      go.GroupSelect = true;
      go.SubObjectSelect = false;
      go.EnableClearObjectsOnEntry(false);
      go.EnableUnselectObjectsOnExit(false);
      go.DeselectAllBeforePostSelect = false;
      go.GetMultiple(1, 0);

      //Nothing to do - get out quick.
      if (go.ObjectCount == 0)
        return Result.Success;

      const string rg18ctName = "18ct red gold";

      //int findsRM = doc.Materials.Find("18ct red gold", true);
      var rm = FindMaterial(doc, rg18ctName);
      if (null == rm)
      {
        //Didn't find the material - create one and carry on.

        //Create a basic material
        var custom = new Rhino.DocObjects.Material();
        custom.Reflectivity = 1;
        custom.ReflectionColor = System.Drawing.Color.FromArgb(255, 187, 132);
        custom.Name = rg18ctName;
        custom.CommitChanges();

        rm = RenderMaterial.CreateBasicMaterial(custom);

        var docMats = doc.RenderMaterials;

        docMats.BeginChange(RenderContent.ChangeContexts.Program);
        docMats.Add(rm);
        docMats.EndChange();
      }

      //Now we always have a material to assign, this part is easy
      for (int i = 0; i < go.ObjectCount; i++)
      {
        var obj = go.Object(i).Object();

        obj.RenderMaterial = rm;
        obj.CommitChanges();
      }

      doc.Views.Redraw();
      return Result.Success;
    }
  }

Hi @andy,
thank you very much for looking at my (bad) code. This looks much better.
I did add using Rhino.Render but FindMaterials, BeginChange and EndChange is still not recognized…
Am i missing a using directive or a reference?
Sorry for the noob questions :confused:

The Begin/End changes stuff is new to V7 - so you can (I think) just miss that out for V6.

The FindMaterials function is just a little helper I implemented:

RenderMaterial FindMaterial(RhinoDoc doc, string name)
    {
      foreach (var material in doc.RenderMaterials)
      {
        if (material.Name == name)
        {
          return material;
        }
      }

      return null;
    }
1 Like

@andy and @dale,
thank you both very much!