How to keep Texture mapping result

Hi~
There are cpp, 3dm and jpg in my attached file.
TestSample.zip (140.3 KB)

I want to change texture coordinates and keep the texture result.
Even if it change render mesh density in the Document properties > Mesh > Render mesh quality, I also want to keep my texture mapping result.

So I get the render mesh from Brep

const ON_Mesh* mesh = F->Mesh(ON::render_mesh); 

And I change the texture coordinats from m_T in the mesh.

ON_2fPointArray Texs = mesh->m_T;
for (int i = 0; i<mesh->m_T.Count(); i++)
{
     ON_2fPoint* fpnt = Texs.At(i);
     double x = fpnt->x / ScaleX;
     double y = fpnt->y / ScaleY;
     fpnt->x = float(x);
     fpnt->y = float(y);
 }
 const_cast<ON_Mesh*>(mesh)->m_T = Texs;

After computing, I can’t see the result immediately until I click the object again or change viewport.

When I modify Document properties > Mesh > Render mesh quality and click the object, the texture mapping are changed and the result is Rhino texture mapping way.

And I run my code again to change the texture mapping, but texture mapping failure.
The mapping result always keep the Rhino texture mapping way.

Can somebody help me to solve the problem?

You must set m_S if you want to give surface texture coords.

@andy
Thank you very much for your reply.

Could you show me how to use m_S to give surface texture coordinate?

I try to use in my program, but it nothing happen.

TestSampleNew.zip (140.7 KB)

It works here. I’m running your code, and your changes to m_S are sticking just fine.

Hi @andy and @dale @YokoK

@andy, you said your test by using ON_Mesh::m_S is no problem. But I use ON_Mesh::m_S, still have problem.

So, I record two avi to show my problem.

Set to ON_Mesh::m_S
It set texture coordiates to ON_Mesh::m_S.
Problem: After computing, the texture coordinates don’t change,because the mapping result doesn’t change even if I set different scale.

Set to ON_Mesh::m_T
It set texture coordiates to ON_Mesh::m_T.
Problem: After computing, I can’t see the result immediately until I click the object again or change viewport. And I run my code again which I use same picture and different scale, but texture mapping failure, because the mapping result doesn’t change.

On Rhino4 and Rhino5, we already had UV mapping in shoe filed, it can make shoe have good texture mapping result.
The following picture is UV mapping result by using our plugin.

On Rhino4 and Rhino5, we use the texture coordinates from m_T in the render mesh to modify picture, and it has good result.
On Rhino4 and Rhino5, the coding is
I get the render mesh from Brep

      const ON_Brep* Brep = ON_Brep::Cast(pObject->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;
      }
      const ON_Mesh* Mesh = F->Mesh(ON::render_mesh);

And I change the texture coordinats from m_T in the mesh.

      ON_2fPointArray Texs = Mesh->m_T;
      ON_2dPointArray STexs = Mesh->m_S;
      for (int i = 0; i<Mesh->m_T.Count(); i++)
      {
        const ON_2dPoint* p2d = Mesh->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 /= ScaleX;
        ty /= ScaleY;
        //ON_2dPoint* p2 = STexs.At(i);
        //p2->x = tx;
        //p2->y = ty;
        ON_2fPoint* p2f = Texs.At(i);
        p2f->x = float(tx);
        p2f->y = float(ty);
      }
      const_cast<ON_Mesh*>(Mesh)->m_T = Texs;

Now, on Rhino6 it doesn’t work.
Because this problem we can’t announce our plugin for Rhino6 version.

Could you help me to solve the problem or give us some suggestion?

Thanks~

my Rhino6 SDK is 6.4.18130.19341 and
Rhino6 is 6.4.118130.19341

Hi @angelwang,

Rhino 6 doesn’t always react to modifications in render meshes. Mainly because the display doesn’t work like it did in earlier versions of Rhino - but there are other reasons as well. Anyway, modifying render meshes has in many cases undefined behavior and it should be avoided. Changing m_T and m_S arrays is only possible and acceptable for mesh objects. And when doing so you should modify a copy of the mesh and then call ReplaceObject on the document.

I suggest you use mesh primitive texture mapping in your plug-in. I modified your example to demonstrate how to do this:
cmdDES_Test.cpp (7.1 KB)
These are the steps in short:

  1. Make a copy of the render mesh.
  2. Modify the copy as you like.
  3. Set that copy as the custom mapping primitive to a texture mapping.
  4. Apply that texture mapping to your object.

We are planning to add functionality into the SDK to make creating texture mappings easier.

Best regards,
Jussi

Hi @Jussi_Aaltonen

Thank you very much for your reply.

I test your sample and the texture mapping is OK on rendered mode.

But I meet other problem, when I run display mode is Rendered mode and change to Shaded, Raytraced and Rendered, the texture mapping have problem.

I have other question about the m_V on render mesh.
We use m_V on render mesh to do something and there is no problem on Rhino4 and Rhino5.

    const ON_Brep* Brep = ON_Brep::Cast(pObject->Geometry());
    ON_BrepFace* F = Brep->Face(0);
    ON_Mesh* mappingMesh = F->Mesh(ON::render_mesh)->Duplicate();

    ON_3fPointArray Vertex = mappingMesh->m_V;
    for (int i = 0; i < Vertex.Count(); i++)
    {
      ON_3fPoint* p3f = Vertex.At(i);
      ON_3dVector vec = ON_zaxis;
      vec.Unitize(); vec = vec * 1.5;
      p3f->z = p3f->z + float(vec.z);
    }
    mappingMesh->m_V = Vertex;

This is Rhino5 reult and it is OK.
Rhino5_Result

But it doesn’t work on Rhino6 and make texture mapping wrong.
Rhino6_MoveMesh

Could you help me check the two problem and give me some suggestion?

Thanks ~

The upload sample, there are two command,
“DES_TextureTest” is Textrure mapping test.
“DES_MeshMove” is ON_Mesh::m_V test.

cmdDES_Test_Rhino.zip (3.0 KB)

Hi @angelwang,

The first problem you described is a bug in Rhino 6. Thanks for spotting it!

You can’t change the shape of rendered geometry using texture mappings. You need to provide custom render meshes to do this - CRhRdkCustomRenderMeshProvider will let you do this. Example available here: https://github.com/mcneel/rhino-developer-samples/blob/6/cpp/SampleRdkMarmalade/MarmaladePointCloudRMP.cpp

Let me know if you need further help!

Hi @Jussi_Aaltonen
Thank you for your reply so fast.

I’m sorry, I don’t understand the sample “MarmaladePointCloudRMP”.
I try to add the following code into “DES_MeshMove” command.

  CRhinoGetObject get;
  get.SetGeometryFilter(CRhinoObject::surface_object);
  get.AcceptNothing(true);
  get.SetCommandPrompt(L"Select Surface to move mesh");

  const CRhinoObject* pObject = NULL;
  {
    for (;;)
    {
      context.m_doc.UnselectAll();
      context.m_doc.Redraw();

      CRhinoGet::result res = get.GetObjects(1, 1);
      if (res == CRhinoGet::object)
      {
        pObject = const_cast<CRhinoObject*>(get.Object(0).Object());
        break;
      }
      else if (res == CRhinoGet::cancel)
        return CRhinoCommand::cancel;

    }
  }
  if (pObject)
  {
    const auto* pPC = static_cast<const ON_PointCloud*>(pObject->Geometry());
    const int pointCount = pPC->PointCount();
    ON_3dVector vec = ON_zaxis;
    vec.Unitize(); vec = vec * 1.5;
    for (int i = 0; i < pointCount; i++)
    {
      ON_COMPONENT_INDEX ci;
      ci.m_type = ON_COMPONENT_INDEX::pointcloud_point;
      ci.m_index = i;
      ON_3dPoint pt = pPC->Point(ci);
      pt.z = pt.z + (vec.z);
    }
  }

The code can’t work, because “pPC->PointCount()” are no data.

How can I do to change shape of rendered geometry?

HI @angelwang,
You need to inherit a new custom render mesh provider class from CRhRdkCustomRenderMeshProvider. Then you need to create an instance of that class to handle your render geometry manipulation.

Here is a minimal example of a custom render mesh provider that will offset render geometry of all objects:

class CSampleRMP : public CRhRdkCustomRenderMeshProvider
{
public:
  CSampleRMP() {}
  virtual ~CSampleRMP() {}
  virtual UUID PlugInId(void) const override
  {
    // return your plug-in id here
  }
  virtual UUID ProviderId(void) const override
  {
    // create new id for your custom render mesh provider class and return it
  }
  virtual ON_wString Name(void) const override
  {
    const static ON_wString wszName = L"SampleCustomRenderMeshProvider";
    return wszName;
  }
  bool WillBuildCustomMesh(const ON_Viewport& vp, const CRhinoObject* pObject, const CRhinoDoc& doc, const UUID& uuidRequestingPlugIn, const CDisplayPipelineAttributes* pAttributes) const override
  {
    if (nullptr == pObject)
      return false;

    return true;
  }
  ON_BoundingBox BoundingBox(const ON_Viewport& vp, const CRhinoObject* pObject, const CRhinoDoc& doc, const UUID& uuidRequestingPlugIn, const CDisplayPipelineAttributes* pAttributes) const override
  {
    if (nullptr == pObject)
      return ON_BoundingBox::EmptyBoundingBox;

    ON_BoundingBox bb = pObject->BoundingBox();
    bb.m_max = bb.m_max - 0.5 * ON_3dVector::ZAxis;
    bb.m_min = bb.m_min - 0.5 * ON_3dVector::ZAxis;
    return bb;
  }

  bool BuildCustomMeshes(const ON_Viewport& vp, const UUID& uuidRequestingPlugIn, const CRhinoDoc& doc, IRhRdkCustomRenderMeshes& crmInOut, const CDisplayPipelineAttributes* pAttributes, bool bWillBuildCustomMeshCheck) const override
  {
    const CRhinoObject* pObject = crmInOut.Object();
    if (nullptr == pObject)
      return false;

    if (bWillBuildCustomMeshCheck)
    {
      if (!WillBuildCustomMesh(vp, pObject, doc, uuidRequestingPlugIn, pAttributes))
      {
        return false;
      }
    }

    ON_SimpleArray<const ON_Mesh*> renderMeshes;
    if (pObject->GetMeshes(ON::render_mesh, renderMeshes) > 0)
    {
      for (int mi = 0; mi < renderMeshes.Count(); mi++)
      {
        crmInOut.SetUseObjectsMappingChannels(false);
        crmInOut.SetInstanceTransform(ON_Xform::IdentityTransformation);
        crmInOut.SetAutoDeleteMeshesOn();
        crmInOut.SetAutoDeleteMaterialsOn();
        ON_Mesh* pMesh = new ON_Mesh(*renderMeshes[mi]);
        if (nullptr != pMesh)
        {
          pMesh->Transform(ON_Xform::TranslationTransformation(0.5 * ON_3dVector::ZAxis));
        }
        crmInOut.Add(pMesh, nullptr);
      }
    }
    return true;
  }

  bool IsViewDependent(void) const override
  {
    return false;
  }

  bool IsRequestingPlugInDependent(void) const override
  {
    return false;
  }

  bool IsPreviewAndStandardSameMesh(void) const override
  {
    return true;
  }

  CRhRdkVariant GetParameter(const CRhinoObject& object, const wchar_t* wszParamName) const override
  {
    return CRhRdkVariant::Null();
  }

  void SetParameter(const CRhinoObject& object, const wchar_t* wszParamName, const CRhRdkVariant& value) override
  {
  }
};

You can use for example object user data to mark objects that your RMP needs to manipulate.

You also need to connect your RMP to the custom render mesh manager once. Here’s an example how to do that:

RhRdkCustomRenderMeshManager().Add(new CSampleRMP());

Hi @Jussi_Aaltonen

DESSample is my plugin which has “CSampleRMP”, the plugin test texture mapping, change the shape of rendered geometry and extract rendered mesh.

When I run command “DES_MeshMove”, the result is


it is OK.

But when I run Rhino command “ExtractRenderMesh” and only show mesh on Rendered display mode.


There is no material on the rendered mesh, how to make the rendered mesh that I extract has material which same as original object.

I try to use the following code in the command “DES_ExtractRenderMesh” in the DESSamplePlugin to extract rendered mesh from the shape of the rendered geometry.

  if (pObject)
  {
    ON_3dmObjectAttributes  Att = CRhinoObjectAttributes(pObject->Attributes());
    const ON_Brep* brep = ON_Brep::Cast(pObject->Geometry());
    ON_SimpleArray< const ON_Mesh* > MeshAry;
    int n = brep->GetMesh(ON::mesh_type::render_mesh, MeshAry);
    for (int k = 0; k<MeshAry.Count(); k++)
      context.m_doc.AddMeshObject(**MeshAry.At(k),&Att);

  }

There are two problems,
One is the mesh still doesn’t have material.
Two is the rendered mesh that I extract by this command, the rendered mesh is original object position.

Could you tell me, how to extract the rendered mesh form the shape of rendered geometry and the mesh still has material?

Hi @angelwang,

The reason why you don’t see the mesh after running ExtractRenderMesh is this:
CSampleRMP::WillBuildCustomMesh returns true for all objects including the extracted mesh object. But CSampleRMP::BuildCustomMeshes only provides meshes for objects that have brep geometry. So what you need to do is to make WillBuildCustomMeshreturn false for objects that BuildCustomMeshes should not be called for.

Hi @Jussi_Aaltonen

I add my Code to CSampleRMP::WillBuildCustomMesh like as following,

bool WillBuildCustomMesh(const ON_Viewport& vp, const CRhinoObject* pObject, const CRhinoDoc& doc, const UUID& uuidRequestingPlugIn, const CDisplayPipelineAttributes* pAttributes) const override
 {
   if (nullptr == pObject)
     return false;

   const ON_Brep* Brep = ON_Brep::Cast(pObject->Geometry());
   if (Brep) return true;

   return false;
 }

and run Rhino command “ExtractRenderdMesh”, it is OK.

But when I save the object like as following picture which doesn’t run Rhino command “ExtractRenderMesh”,

and open the file again, the render mesh new position disappear.

How to keep the new render mesh position, when I open file?

Everytime Rhino is closed all custom render mesh providers are removed. So your plugin needs make sure the render mesh provider is attached to custom render mesh manager. Good practice is to only attach RMP’s when they are needed.

Hi @Jussi_Aaltonen

Sorry, I don’t know how to make sure the render mesh provider is attached to custom render mesh manager.
Could you show me how to do?

Thanks~

You can start by adding your RMP everytime your plug-in is loaded.

Hi @Jussi_Aaltonen

Thank you very much :smiley:
Now it is work ~

1 Like

Hi @Jussi_Aaltonen
About “Using mesh primitive texture mapping”, I feel it is not stable.

I run the sample cmdDES_Test_Rhino_New.cpp (14.6 KB).

The first time, I set Scalex=3, ScaleY=3 and select Picture1, result is ok.
The second time, I set Scalex=1, ScaleY=1 and select Picture1, result is ok.
The third time, I set Scalex=4, ScaleY=4 and select Picture2, result is ok.
The fourth time, I set Scalex=1, ScaleY=1 and select Picture2, result is NG, and it shows to Pictur1.

Could you help me to check what’s happen?

Thanks~

Hi @angelwang, you’re right. There’s something odd here. This issue is already visible if you run your command twice with same scale values but different texture. Let us look into this.

Looks like material changes are not always visible in Rendered view when custom primitive mapping is used. Raytraced display mode doesn’t have this problem (need to comment out the display mode set in the command in order to test).

@DavidEranen Do you have any thoughts? Looks like object material is always the one that was first used with the exact same custom mesh mapping. Could this be a caching problem? Materials used here are pure ON_Materials.

@Jussi_Aaltonen Can you make a YouTrack item and attach the needed plug-in/source code and assign it to me please?

-David