Add Texture /C++

@johnc

I have new problem
In the DlgSampleTexutre.cpp
I add code to modify the scale of the bitmap texture.

   //Modify Scale
    const ON_Brep* Brep = ON_Brep::Cast(myObj->Geometry());
    ON_BrepFace* F = Brep->Face(0);
    ON_Interval u_dom, v_dom;
    const ON_Surface* Sur = *Brep->m_S.At(0);
    {
      u_dom = Sur->Domain(0);
      v_dom = Sur->Domain(1);

      ON_Curve* uCrv = Sur->IsoCurve(0, v_dom.Mid());
      ON_Curve* vCrv = Sur->IsoCurve(1, u_dom.Mid());

      double len0, len1;
      uCrv->GetLength(&len0);
      vCrv->GetLength(&len1);
      delete uCrv;	delete vCrv;
    }

    ON_Mesh* mappingMesh = F->Mesh(ON::render_mesh)->Duplicate();
    ON_2fPointArray Texs = mappingMesh->m_T;
    for (int i = 0; i < mappingMesh->m_T.Count(); i++)
    {
      const ON_2dPoint* p2d = mappingMesh->m_S.At(i);
      ON_2dPoint Pnt; Pnt.x = p2d->x; Pnt.y = p2d->y;

      double x = Pnt.x;
      double y = Pnt.y;

      double tx = (x - u_dom[0]) / u_dom.Length();
      double ty = (y - v_dom[0]) / v_dom.Length();

      tx /= m_ScaleX;
      ty /= m_ScaleY;
      ON_2fPoint* p2f = Texs.At(i);
      p2f->x = float(tx);
      p2f->y = float(ty);
    }
    mappingMesh->m_T = Texs;
    CDES_TextureMapping DES_TextureMap;
    DES_TextureMap.SetCustomMeshMapping(*myObj, *pDoc, mappingMesh);
 

But I found, the scale of the bitmap texture be changed on the firs time value setting, if I change the value of the scale again, the scale of the bitmap texture doesn’t change.

Could you help me to check the program?

PS:
We need to use above code to change the scale of the bitmap texture.

The project, I compile on Rhino6.
DESNewSample.zip (110.3 KB)
In DESNewSample.zip there are
checkmat small.jpg and my project

Hi @angelwang,

I’m afraid this kind of texture mapping code is outside my zone of knowledge. I’ll ask one of our resident mapping experts for advice. @Jussi_Aaltonen Could you take a look at this code and see if you can figure out what’s wrong? Thanks.

John

@Jussi_Aaltonen

Because of the inform and I meet problem about manage material in the new version for Rhino6 , so I ask @johnc about the new manage material way.

In the new way, I have problem about scale.
But the code about scale work fine in the old way.

Hi @johnc

Can I purge the unused materials by coding?
In the old way to add material on Rhino6, it will purge the unused materials automatically.

Hi @angelwang,

The easiest way to clear out unused materials is to run a special command. You need to know the document you are working on (I’ll assume it’s called doc). Try this:

  const auto doc_sn = doc.RuntimeSerialNumber();
  RhinoApp().RunScript(doc_sn, L"RenderRemoveUnusedMaterials", 0);

Please note that if you are doing this from within a command, it will only work if your command is derived from CRhinoScriptCommand.

Please let me know if it does what you need.

Regards,

John

Hi @johnc

It work fine, thank you :slight_smile:

I have another questions
RhinoMaterials

  1. How to find the Panel_1_Back of ON_Material in the Rhino Materials by coding?
  2. How to check Panel_1_Back is unused by coding?
  3. How to only delete Panel_1_Back in the Rhino Materials by coding?

Hi @angelwang,

I will show you how to do these steps using the new system. This uses CRhRdkContent instead of ON_Material.

	const wchar_t* name = L"Panel_1_Back";

	// Find the content by name.
	CRhRdkContentArray aContent;
	doc.Contents().Find(CRhRdkContent::Kinds::Material, name,
	                    false, false, aContent);
	if (aContent.Count() == 0)
	{
		RhinoApp().Print(L"'%s' was not found\n", name);
		return failure;
	}

	// Check if the content is in use (assume only 1 match).
	const auto& c = *aContent[0];
	if (c.UseCount() > 0)
	{
		RhinoApp().Print(L"'%s' is in use\n", name);
		return failure;
	}

	// Detach the content from the document.
	auto& contents = doc.Contents().BeginChange(RhRdkChangeContext::Program);
	auto* pDetach = contents.Detach(c);
	contents.EndChange();

	if (nullptr != pDetach)
	{
		// Delete the content.
		pDetach->Uninitialize();
		delete pDetach;
	}

I hope this helps.

John

Hi @johnc
Thank you very much for your reply.


Panel.3dm (193.3 KB)

Can I delete the object and the object material in the Rhino Material at the same time?

Because when I delete object, I check CRhRdkContent::UseCount() of the material of the object, it still in use.

  context.m_doc.UnselectAll();
  context.m_doc.Regen();
  unsigned int geometry_filter =
    CRhinoGetObject::surface_object |
    CRhinoGetObject::polysrf_object |
    CRhinoGetObject::mesh_object;

  CRhinoGetObject go;
  go.SetCommandPrompt(L"Select surface, polysurface, or mesh to delete and del Material");
  go.SetGeometryFilter(geometry_filter);
  go.EnablePreSelect(TRUE);
  go.EnableSubObjectSelect(FALSE);
  go.GetObjects(1, 1);
  if (go.CommandResult() != CRhinoCommand::success)
    return go.CommandResult();

  const CRhinoObjRef& object_ref = go.Object(0);
  const CRhinoObject* object = object_ref.Object();
  if (0 == object)
    return CRhinoCommand::failure;

  ON_Material material = object->ObjectMaterial();
  ON_Material M = object->ObjectMaterial();
  CRhinoObjRef refObj(object);
  const CRhRdkContent* pMaterial = context.m_doc.Contents().Find(object->ObjectMaterial().RdkMaterialInstanceId());
  ON_wString N = pMaterial->InstanceName();
  context.m_doc.DeleteObject(refObj, true, true);
  object = NULL;

  // Find the content by name.
  CRhRdkContentArray aContent;
  context.m_doc.Contents().Find(CRhRdkContent::Kinds::Material, N, false, false, aContent);
  if (aContent.Count() > 0)
  {
    const CRhRdkContent* c = *aContent.First();
    if (c->UseCount() > 0)  
    {
      // the material of the object  is not unused material
      int n = c->UseCount();
    }
  }
  context.m_doc.Regen();
  return CRhinoCommand::success;

Hi @angelwang,

When an object is deleted, Rhino does not update the content usage straight away because it can be slow when there are a lot of items. Instead, it does it later when it has time. This means that your code won’t work because Rhino hasn’t had time to update yet. But you can force it to update immediately by adding this (just after object = NULL):

    RhinoApp().RunScript(context.m_rhino_doc_sn, L"ResetContentUsage", 0);

Again, note that if you are doing this from within a command, it will only work if your command is derived from CRhinoScriptCommand .

Yes, but it’s usually not safe because there might be something else using the material. This is why you need to check the use count.

John

Hi @johnc
Thank very much~

It works fine.

About Add Texture /C++ - #21 by angelwang
@Jussi_Aaltonen & @johnc
I load the plugin on Rhino7 and set scale on the dialog, it work OK.
If I load the plugin on Rhino6 and set scale on the dialog, it doesn’t work.
I want to know why it work on Rhino6 has problem.

Hi @johnc
When I add or modify the bitmap texture on the material, if the type of the Texture Mapping is not Surface, can I change the type of Texture Mapping to be Surface by coding?

    // The object has a material so it wants to add or modify the bitmap texture on the material.
    // Find the RDK material.
    ON_Material Material = myObj->ObjectMaterial();
    auto* pMaterial = pDoc->Contents().Find(Material.RdkMaterialInstanceId());
    if (pMaterial != nullptr)
    {
      // Find the material's child bitmap texture.
      const auto* pTexture = pMaterial->FindChild(CS_MAT_BITMAP_TEXTURE);
      if (pTexture == nullptr)
      {
        // Texture not found; create it and set it as a child of the material.
        CRhRdkSimulatedTexture tex;
        pTexture = ::RhRdkNewBitmapTexture(tex, pDoc);
        auto& m = pMaterial->BeginChange(RhRdkChangeContext::UI);
        m.SetChild(pTexture, CS_MAT_BITMAP_TEXTURE);
        m.EndChange();
      }

      if (pTexture != nullptr)
      {
        auto currentFileName = pTexture->GetParameter(FS_TEX_FILENAME).AsString();
        if (currentFileName != m_BmpFile)
        {
          // Make the operation undoable.
          CRhRdkContentUndo cu(*pDoc);
          cu.ModifyContent(*pMaterial);

          // Begin the change operation.
          auto& texture = pTexture->BeginChange(RhRdkChangeContext::UI);

          // Set the child texture's file name.
          const wchar_t* filename = m_BmpFile;
          texture.SetParameter(FS_TEX_FILENAME, filename);

          // Set the name of the material from the file name.
          ON_wString sName;
          ON_FileSystemPath::SplitPath(m_BmpFile, nullptr, nullptr, &sName, nullptr);
          texture.SetInstanceName(sName);

          // End the change operation.
          texture.EndChange();

          if (!pMaterial->ChildSlotOn(CS_MAT_BITMAP_TEXTURE))
          {
            // Turn the bitmap child slot on.
            auto& m = pMaterial->BeginChange(RhRdkChangeContext::UI);
            m.SetChildSlotOn(CS_MAT_BITMAP_TEXTURE, true);
            m.EndChange();
          }
        }
      }
    }

Hi @angelwang,

You can change a mapping on an object back to surface by deleting the mapping. Surface mapping is the default when there is no actual mapping:

	auto attr = myObj->Attributes();

	const int channel = 1; // Usually mappings are on channel 1.
	const auto plugin_id = RhinoApp().GetDefaultRenderApp();
	if (!attr.m_rendering_attributes.DeleteMappingChannel(plugin_id, channel))
		return failure;

	if (!pDoc->ModifyObjectAttributes(CRhinoObjRef(myObj), attr, true))
		return failure;

	return success;

I hope this helps,

John

Hi @johnc
Thank you very much.
It work fine.

Hi @angelwang, you reported some time ago that the view does not react when you change the Scale value on the dialog. I looked into this issue and it appears to be the CDES_RenderMeshRMP render mesh provider that is causing this issue. So to solve it you need make sure that CDES_RenderMeshRMP::BuildCustomMeshes gets called for the object whose texture mapping you modify. You can do this by calling ::RhRdkCustomRenderMeshManager().OnRhinoObjectChanged and these are the options where you can call it:

  1. in the end of the CDlgSampleTexture::UpdateObj before pDoc->Regen();
  2. in the CSampleRhinoEventWatcher::TextureMappingTableEvent

Option 1) is easier since there you know which object that texture mapping affects.

Yes, we have fixed a bunch of texture mapping related bugs in Rhino 7.

Hi @Jussi_Aaltonen
Thank you very much~

It work fine on Rhino6! :slight_smile:

Hi @johnc
When I select an object, how to know the material of the object is plastic and not custom by coding?
And how to change the material of the object to be custom by coding?

Because if the material of the object is not custom, I can’t set bitmap texture to the object.

Hi @angelwang,

I tried this by writing the following test code. If an object is not selected it will prompt for one, then get the assigned material. If it is not a Custom (Basic) Material, it will replace it with a new Custom Material.

	CRhinoGetObject go;
	if (CRhinoGet::object != go.GetObjects(1, 1))
		return failure;

	const auto* pObject = go.Object(0).Object();
	if (nullptr == pObject)
		return failure;

	ON_COMPONENT_INDEX ci;
	const auto* pMaterial = pObject->ObjectRdkMaterial(ci);
	if (nullptr == pMaterial)
		return failure;

	if (pMaterial->TypeId() == uuidBasicMaterialType)
	{
		// This is a Custom Material.
	}
	else
	{
		// Change the material to a Custom Material.
		auto* pNewMaterial = ::RhRdkNewBasicMaterial(ON_Material(), pDoc);
		if (nullptr == pNewMaterial)
			return failure;

		auto& c = pDoc->Contents().BeginChange(RhRdkChangeContext::Program);
		c.Replace(*pMaterial, *pNewMaterial); // Deletes pMaterial.
		c.EndChange();

		pMaterial = pNewMaterial;
	}

If you’re not doing this in a command, you might want to react to the CRhinoEventWatcher::OnSelectObjects() event. Be aware that you are not allowed to change the document inside that event handler. Please see the comments in rhinoSdkEventWatcher.h.

Regards,

John

@johnc

Thank you very much~ :slightly_smiling_face:
I run my code in a command.
I have compile error for “c.Replace(*pMaterial, *pNewMaterial);”.

my Rhino6 SDK is rh634sdk_6.34.21034.07001

Hi @angelwang,

Sorry, Replace() is new in V7. In V6 you have to use Change().

John