Create PBR material in c++

I am writing an exporter and importer for a non-standard file format.

For exporting pbr materials, I am able to get texture paths and other values this way (shortened example!):

	{
		matInfo.isPhysicallyBased = true;
		//cycle throught textures and get each type.
		const ON_PhysicallyBasedMaterial pbrMat = rhinoMat;
		//get other attributes similar to Rh6 materials
		int totalTextures = pbrMat.Material().m_textures.Count();
		for (int x = 0; x < totalTextures; x++)
		{
			if (pbrMat.Material().m_textures[x].m_type == ON_Texture::TYPE::opacity_texture) //BITMAP_TEXTURE DEPRECATED
			{
				if (pbrMat.Material().m_textures[x].IsValid()) // Sometimes support files are missing
					{
					try
					{
						const ON_wString textPathW = pbrMat.Material().m_textures[x].m_image_file_reference.FullPath();
						//save this value for use with my custom export

					}
					catch (...)
					{
					}
				}
			}
		}

		ON_4fColor emissionColorToSave(pbrMat.Emission());
	}

For importing, I have been able to create “Custom” material as described here:
rhino-developer-samples/SampleRdkAddRdkMaterialsCommand.cpp at 7 · mcneel/rhino-developer-samples · GitHub

However, I want to recreate a fully defined pbr material, not a “custom” material.

I have tried this:

  bool isPPBR = false;
  //Set up both types
  ON_Material rMat;
  ON_Color col(255, 0, 0); // Initial color.
  rMat.SetDiffuse(col);
  rMat.SetName(L"myMaterialName");

  ON_PhysicallyBasedMaterial pbrMat = rMat;

  if (myImporterFinds_PBR_tag)
  {
      isPBR = true;
  }

  //Loop to get each line from my custom file format, some examples of data collected:
  while (getline(myMaterialFile, line))
  {
    if (line.find("opacity_texture: ") != std::string::npos)
    {
       // call my helper function to get the path value 
       pbrMat.AddTexture(getPathFromline(line), ON_Texture::TYPE::pbr_opacity_ior_texture);
    }

    else if (line.find("emission_color: ") != std::string::npos)
    {
      if (isPPBR) 
      {
        ON_4fColor emColor;
        // call my helper function to get the color value
        getColor4(line, &emColor);
        pbrMat.SetEmission(emColor);
      }
      else
      {
        ON_Color emColor;
        // call my helper function to get the color value
        getColor(line, &emColor);
        rMat.SetEmission(emColor);
      }
    }

    else
    {
      // more code to handle importing data about the material
    }
  } //end of while loop

  //after collecting all the material data, create the material
  if (isPPBR) 
  {
    // What do I do here??????
  }
  else
  {
    const int matIndex = RhinoApp().ActiveDoc()->m_material_table.AddMaterial(rMat);
  }

Am I close, or did I miss it completely?

Hi

Firstly, you need to be using the modern “RDK” methods to work with these richer material definitions. There are plenty of samples - but this should be a good start:

The function CRhRdkMaterial::FromOnMaterial will create a Physically Based material from an ON_Material that is actually a PBR.

  • Andy

Hi Andy,

I pulled the rhino-developer-samples yesterday. Building samples.sln in VS2019 resulted in a lot of errors. Building just SampleRdkAddRdkMaterials had two errors:

|Error|C2664|'CRhRdkSimulatedTexture::CRhRdkSimulatedTexture(const CRhRdkSimulatedTexture &)': cannot convert argument 1 from 'CRhinoDoc *' to 'const CRhRdkSimulatedTexture &'|SampleRdkAddRdkMaterials|E:\GitE\rhino-developer-samples\cpp\SampleRdkAddRdkMaterials\SampleRdkAddRdkMaterialsCommand.cpp|34||
|Error|C2039|'SetDiffuse': is not a member of 'CRhRdkMaterial'|SampleRdkAddRdkMaterials|E:\GitE\rhino-developer-samples\cpp\SampleRdkAddRdkMaterials\SampleRdkAddRdkMaterialsCommand.cpp|31||

I modified those two lines of code, and set line 35 to point to a .jpg on my system:

  // Optionally set the diffuse color again.
  //pMaterial->SetDiffuse(RGB(0, 255, 0));

  // Add a bitmap texture to the bitmap slot of the Basic Material.
  CRhRdkSimulatedTexture tex; // (pDoc);

It will now build, but will crash when run, until I commented out the “dib” section, lines 52 to 72.

With those changes, it will run and create a material, but the material type is “Custom”

I want to create a “Physically Based” type as shown here, with all the texture slots.

Are there additional steps needed to create the type of material I want?

Make sure you have an up to date SDK.

Andy

Hi @mgraham,

The first error suggests that you are using an old version of the Rhino SDK. Could you tell me what version of Rhino you’re working with?

Thanks,

John

The error about “Diffuse” is a big I introduced today by changing ::RhRdkNewBasicMaterial to CRhRdkMaterial::FromOnMaterial

All, Updating the SDK helped some. Everything compiles with only the need to comment out

//pMaterial->SetDiffuse(RGB(0, 255, 0));

when run, it still crashes unless I remove the dib texture code, lines 52 to 72.

And it is still creating a “Custom” layer, not a “Physically Based” layer.

I need to assign multiple textures, and although my code does so without throwing any errors, In Rhino the material appears as a custom type with only one texture.

For example, if I add this around line 43:

  CRhRdkSimulatedTexture tex2(pDoc);
  tex2.SetFilename(L"C:\\my_local.bmp", pDoc, false); 
  auto* pBitmap2 = ::RhRdkNewBitmapTexture(tex, pDoc);
  if (nullptr != pBitmap2)
  {
      pMaterial->SetChild(pBitmap, FS_MAT_PBR_METALLIC);
      pMaterial->SetChildSlotOn(FS_MAT_PBR_METALLIC, true);
  }

The resulting material does not show that texture:

If as a Rhino user, I change it to a Physically Based material type, there is no texture entry for Metallic.

OK - sorry it took a while. The sample is now updated to support PBR materials and fix a couple of bugs. Just pull a new version.

And yes - you will need to use CRhRdkMaterial::FromOnMaterial instead of RhRdkNewBitmapTexture if you want the ON_Material in PBR mode to create a PBR material. You’ll see it in the new sample code.

1 Like

Thank you, I downloaded the sample changes, and this is much closer. It does create a Physically Based material with the two textures shown in the sample. However, when I try to add a texture for metallic, it does not appear (shown here between the original lines of code):

  // Add a bitmap texture to the bitmap slot of the Basic Material.
  CRhRdkSimulatedTexture tex(pDoc);
  tex.SetFilename(L"C:\\myLocalBmpBase.bmp", pDoc, false); 
  auto* pBitmap = ::RhRdkNewBitmapTexture(tex, pDoc);
  if (nullptr != pBitmap)
  {
      pPBR->SetChild(pBitmap, CS_MAT_PBR_BASE_COLOR);
      pPBR->SetChildSlotOn(CS_MAT_PBR_BASE_COLOR, true);
  }

  //Attempt to add a metallic texture here
  CRhRdkSimulatedTexture tex2(pDoc);
  tex2.SetFilename(L"C:\\myLocalBmpMet.bmp", pDoc, false);
  auto* pBitmap2 = ::RhRdkNewBitmapTexture(tex, pDoc);
  if (nullptr != pBitmap2)
  {
      pPBR->SetChild(pBitmap, CS_MAT_PBR_METALLIC);
      pPBR->SetChildSlotOn(CS_MAT_PBR_METALLIC, true);
  }

  // Add a wood texture to the transparency slot of the Basic Material.
  auto* pWood = ::RhRdkContentFactoriesEx().NewContentFromTypeEx(uuidWoodTextureType, pDoc);
  if (nullptr != pWood)
  {
      pPBR->SetChild(pWood, CS_MAT_PBR_ROUGHNESS);
      pPBR->SetChildSlotOn(CS_MAT_PBR_ROUGHNESS, true);
  }

Will I be able to assign textures to each of the texture slots?

Call SetChild with pBitmap2

Awesome! It works! Thank you for catching my typo/error!

1 Like

Found another issue. Even though the SampleRdkAddRdkMaterialsCommand.cpp code is trying to create a PBR with the name “Sample PBR”, as shown here:

  //Now do the same for a PBR material.
  // Create an ON_Material as a PBR.
  ON_Material mat;
  mat.SetName(L"Sample PBR");

  mat.ToPhysicallyBased();
  ASSERT(mat.IsPhysicallyBased());
  auto pbr = mat.PhysicallyBased();

  ON_4fColor col;
  col.SetRGBA(1.f, 0.f, 0.f, 1.f);
  pbr->SetBaseColor(col);

  pPBR = CRhRdkMaterial::FromOnMaterial(mat, pDoc);

The material is being created with the name “Physically Based”, which can be seen in the last image included here. Should it inherit the name from the “mat” variable, or is there another way to set the name on the “pPBR” object?

I believe you want to use the base class method CRhRdkContent::SetInstanceName:

pPBR->SetInstanceName(L"Sample PBR");

I’ve now further updated the sample to include Nathan’s fix and add a third method of creating materials.

1 Like

Actually, the fact that the ON_Material’s name is being ignored is a bug. I have filed it:

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

The fix will be in the next service release.

John

1 Like