Add Texture /C++

Hi
I am trying to put ON_texture(picture i want ) into ON_Material.
I found an example online that where c++/RDK/ Textures and Mappings.The code used

ON_Texture tex;
tex.m_filename = L"full path to your texture.jpg/bmp/…";

but i didn’t find ‘m_filename’ this code.
so i use

tex.m_image_file_reference.CreateFromFullPath(L"my path", 1, 1);

to repace it.but it don’t work .When i put it into my material use: material.AddTexture(tex);
there were appear a black with nothing picture.

How can I add picture into Rhino?
Is my understanding wrong? texture is under material.
Or there have other way to put my own picture into rhino attribute to become my material or decals or anything.

Hi @tll4568523,

This code sample works here in Rhino 6:

https://github.com/mcneel/rhino-developer-samples/blob/6/cpp/SampleCommands/cmdSampleTexture.cpp

If you really want a Picture, use RhinoCreatePictureFrame. See rhinoSdkObject.h for details.

– Dale

2 Likes

To @dale
Thanks a lot .It’s very useful !!

@dale
How to modify the bitmap texture file ?

I get the object which has bitmap texture and the file name is A11.jpg.
I try to set the bitmap texture which file name is A12.jpg to the object by coding.
I check the object on Rhino material,the name of bitmap texture is still A11.jpg

my code as following

    unsigned int geometry_filter =
      CRhinoGetObject::surface_object |
      CRhinoGetObject::polysrf_object |
      CRhinoGetObject::mesh_object;

    CRhinoGetObject go;
    go.SetCommandPrompt(L"Select surface, polysurface, or mesh to modify texture");
    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;

    int material_index = object->Attributes().m_material_index;
    ON_Material material = object->ObjectMaterial();

    int ti = material.FindTexture(0, ON_Texture::TYPE::bitmap_texture);
    if (ti >= 0) {
      CRhinoGetFileDialog gf1;
      gf1.SetScriptMode(context.IsInteractive() ? FALSE : TRUE);
      BOOL bResult = gf1.DisplayFileDialog(CRhinoGetFileDialog::open_bitmap_dialog);
      if (!bResult)
        return CRhinoCommand::cancel;

      material.DeleteTexture(NULL, ON_Texture::TYPE::bitmap_texture);
      ON_wString found_file_full_path = gf1.FileName();
      ON_Texture tex;
      tex.m_image_file_reference.SetFullPath(found_file_full_path, false);
      tex.m_bOn = true;
      tex.m_type = ON_Texture::TYPE::bitmap_texture;
      tex.m_mode = ON_Texture::MODE::decal_texture;
      int index= material.AddTexture(tex);
      CRhinoObjectAttributes A2(object->Attributes());
      A2.m_material_index = material_index;
      context.m_doc.ModifyObjectAttributes(object_ref, A2);
      context.m_doc.Regen();
    }

Could you help me to check the problem?

@johnc - can you help with this?

Hi @angelwang,

I will try your code, figure out why it doesn’t work, and let you know what the problem is.

I should have an answer for you by tomorrow afternoon at the latest.

Regards,

John

1 Like

Hi @angelwang,

Which version of Rhino are you using?

John

Thank you very much.

The both of Rhino6 SR34 and Rhino7.4 have the same problem.

Hi @angelwang,

I discovered why your code didn’t work. It was because you didn’t have this:

context.m_doc.m_material_table.ModifyMaterial(material, material_index);

This is needed because above, you are copying the original material with this code:

ON_Material material = object->ObjectMaterial();

However, even after making this change, it would not work properly because you are directly manipulating the document material table. Although this used to work back in the days before there was a Material Editor, it won’t fully work now because the Material Editor won’t update. Therefore, I recommend the following approach which operates at a higher level and ensures that everything updates. Behind the scenes this will still do what your code does, but it will also allow the Material Editor to update:

unsigned int geometry_filter =
	CRhinoGetObject::surface_object |
	CRhinoGetObject::polysrf_object |
	CRhinoGetObject::mesh_object;

CRhinoGetObject go;
go.SetCommandPrompt(L"Select surface, polysurface, or mesh to modify texture");
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();

int ti = material.FindTexture(0, ON_Texture::TYPE::bitmap_texture);
if (ti >= 0) {
	CRhinoGetFileDialog gf1;
	gf1.SetScriptMode(context.IsInteractive() ? FALSE : TRUE);
	BOOL bResult = gf1.DisplayFileDialog(CRhinoGetFileDialog::open_bitmap_dialog);
	if (!bResult)
		return CRhinoCommand::cancel;

	ON_wString found_file_full_path = gf1.FileName();

	auto& doc = context.m_doc;

	// This is where the procedure is different. We use the RDK SDK instead of the pure Rhino SDK
	// to make sure that the Material Editor also updates. This also ensures that undo will restore
	// the material to its previous state.

	// Find the RDK material.
	auto* pMaterial = doc.Contents().Find(material.RdkMaterialInstanceId());
	if (pMaterial == nullptr)
		return CRhinoCommand::failure;

	// Find the material's child bitmap texture.
	const auto* pChild = pMaterial->FindChild(CS_MAT_BITMAP_TEXTURE);
	if (pChild == nullptr)
		return CRhinoCommand::failure;

	// Make the operation undoable.
	CRhRdkContentUndo cu(doc);
	cu.ModifyContent(*pMaterial);

	// Begin the change operation.
	auto& child = pChild->BeginChange(RhRdkChangeContext::Program);

	// Get the child's file-based interface and set the file name.
	auto* pFileBased = dynamic_cast<IRhRdkFileBasedContent*>(&child);
	if (pFileBased != nullptr)
		pFileBased->SetFilename(found_file_full_path);

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

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

	doc.DeferredRedraw();
}

I hope this does what you need. Please let me know if you have any further questions.

Regards,

John

@johnc

Sorry, I always have compile error on

even I add #include <RhRdkContent.h>.

The both of Rhino6 and Rhino7 have the compile error.

The object on Rhino material, there have textures on Color and Bump,

if I modify bitmap texture by my code and update by

I found the texture of the object changes on the Viewport but I check the object on Rhino material,the name of bitmap texture does not change.

Hi @angelwang,

I’m sorry, I should not have told you to use IRhRdkFileBasedContent because it’s a very recent addition. and only works in the very latest Rhino 7. The best way to do this so it works in 6 and 7 is like this:

const wchar_t* filename = found_file_full_path;
child.SetParameter(FS_TEX_FILENAME, filename);

I found the texture of the object changes on the Viewport but I check the object on Rhino material,
the name of bitmap texture does not change.

That’s because you need to use the new RDK code. The pure Rhino code does not know how to update the Material Editor (which includes any view of the material).

For completeness, the following is the exact code that you should need to get this working the way you want in Rhino 6 and Rhino 7:

	unsigned int geometry_filter =
		CRhinoGetObject::surface_object |
		CRhinoGetObject::polysrf_object |
		CRhinoGetObject::mesh_object;

	CRhinoGetObject go;
	go.SetCommandPrompt(L"Select surface, polysurface, or mesh to modify texture");
	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();

	int ti = material.FindTexture(0, ON_Texture::TYPE::bitmap_texture);
	if (ti >= 0) {
		CRhinoGetFileDialog gf1;
		gf1.SetScriptMode(context.IsInteractive() ? FALSE : TRUE);
		BOOL bResult = gf1.DisplayFileDialog(CRhinoGetFileDialog::open_bitmap_dialog);
		if (!bResult)
			return CRhinoCommand::cancel;

		ON_wString found_file_full_path = gf1.FileName();

		auto& doc = context.m_doc;

		// This is where the procedure is different. We use the RDK SDK instead of the pure Rhino SDK
		// to make sure that the Material Editor also updates. This also ensures that undo will restore
		// the material to its previous state.

		// Find the RDK material.
		auto* pMaterial = doc.Contents().Find(material.RdkMaterialInstanceId());
		if (pMaterial == nullptr)
			return CRhinoCommand::failure;

		// Find the material's child bitmap texture.
		const auto* pChild = pMaterial->FindChild(CS_MAT_BITMAP_TEXTURE);
		if (pChild == nullptr)
			return CRhinoCommand::failure;

		// Make the operation undoable.
		CRhRdkContentUndo cu(doc);
		cu.ModifyContent(*pMaterial);

		// Begin the change operation.
		auto& child = pChild->BeginChange(RhRdkChangeContext::Program);

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

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

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

		doc.DeferredRedraw();
	}

Regards,

John

@johnc
Thank you for your reply.

I found the code

it does not work, when I modify bitmap texture, the name of material does not change.

@johnc

if I modify my code

// Find the RDK material.
auto* pMaterial = ::RhinoApp().ActiveDoc()->Contents().Find(M.RdkMaterialInstanceId());
if (pMaterial == nullptr)
{
  pMaterial = ::RhRdkNewBasicMaterial(M, ::RhinoApp().ActiveDoc());
  // Attach the material to the document's render content collection.
  auto& contents = ::RhinoApp().ActiveDoc()->Contents().BeginChange(RhRdkChangeContext::Program);
  contents.Attach(*pMaterial);
  contents.EndChange();
}

in my dialog.cpp and not cmdXXX.cpp
it compile error, could you help me to solve the problem?

Hi @angelwang,

The name of the material will not change because we are only setting the name of the texture. I see I have written the comment wrong. It should say:

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

Sorry about that.

If you want the material name to change as well, you can add this line:

  pMaterial->SetInstanceName(sName);

I can help you with the compiler error – please copy and paste the error message here.

Thanks.

John

Hi @angelwang,

I suspect the compiler error is about ::RhinoApp().ActiveDoc as that is a very old way of getting the document. If so, please try this:

  const auto doc_serial = CRhinoDoc::ModelessUserInterfaceDocSerialNumber();
  auto* pDoc = CRhinoDoc::FromRuntimeSerialNumber(doc_serial);
  if (nullptr == pDoc)
    return;

and change all ::RhinoApp().ActiveDoc() to pDoc, for example:

  pDoc->Contents().Find(M.RdkMaterialInstanceId());

If that doesn’t help, please send me the compiler errors.

John

@johnc
Thank you very much.

I upload my project to you
DESNewSample.zip (89.1 KB)

After compile the project, you can
Step1. Load DESSample plugin
Step2. On Rendered mode, drawing rectangular plane surface.
Step3. Run SampleTexture on Rhino command prompt
Step4. Click the rectangular plane surface
Step5. Click the option “material” on Rhino command prompt
Step6. it issue dialog box

You can set attribute or material to the object.
In the CDlgSampleTexture::UpdateObj(), I have several conditions,

  1. // There are no attribute and material on the object
    // So it want to add attribute and material which no bitmap texture to the object

  2. // it want to modify the color and name of the attribute and material to the object

  3. // the object has material which no bitmap texture, so it want to add bitmap texture the object
    // or modify bitmap texture on the material
    // or delete bitmap texture from the material, but keep the color and name of attribute and material on the object.
    PS: If turn off the check box of Texture, the path of the Bitmap will be cleared.
    If turn on the check box of Texture, you can select picture by click “…” button 2021-05-04_20-29-32

In the condition 1 and 2, I write several code, but I’m not sure if is correct and I still use old way.
In the condition 3, I need to you help me , because I don’t know to write the program.

Until now, we always use old way to set attribute and material, now we have big problem to add texture and modify texture. It is very unstable.
For new way, I can not use clearly and I feel confuse.

The project, I compile on Rhino6.

Hi @angelwang,

Thanks for the project. I’m currently on vacation until Monday. I will try to look into this early next week.

John

@johnc
Thank you very much! :grinning:

Hi @angelwang,

I have updated your code and attached the changed file (DlgSampleTexture.cpp) as a zip. I had to reorganize the UpdateObj() function a bit to make it easier to add the new code. I also added some notes about what the code is doing. I hope this does what you need. If you have any further questions, I’d be happy to try and help.

Regards,

John

DlgSampleTexture.zip (4.1 KB)

@johnc
Thank you very much. :smiley: