On 23/08/2016 at 03:53, xxxxxxxx wrote:

User Information:
Cinema 4D Version:   R14 
Platform:   Windows  ;   
Language(s) :     C++  ;

Hi folks,

I've got GetCursorInfo() in a scene hook plugin, and I'm wanting to find the polygon id and object under the mouse cursor (if any!). I want to be able to do this in standard object mode. The code I have is more for the active scene object, and works only in Mpolygon mode. But I want it to work in object mode. Code as follows:

Bool My_Scene_Hook::GetCursorInfo(BaseSceneHook* node, BaseDocument* doc, BaseDraw* bd, Real x, Real y, BaseContainer& bc)
		LONG left, top, right, bottom;
		bd->GetFrame(&left,  &top, &right, &bottom);
		// here's where I want to get the object the cursor finds, not which object is active
		BaseObject *op = doc->GetActiveObject();  
		if(!op || op->GetType() != Opolygon)
			return FALSE;
		AutoAlloc<ViewportSelect> vs;
		if(vs && vs->Init(...))
			// I test polygon index etc here 
			return TRUE;
		return FALSE;

In short, I want to be able to find the object and polygon index under the mouse in normal object mode. So the two questions are:

1. How do I test which object is under the cursor? (not just the active object)
2. How do I test GetNearestPolygon() in Mobject mode? (doesn't seem to work unless I'm in Mpolygons mode?)


On 23/08/2016 at 07:06, xxxxxxxx wrote:


1. Have you tried ViewportSelect::PickObject()?

Cactus Dan

On 30/08/2016 at 01:53, xxxxxxxx wrote:

Hi Dan - apologies for the delay, thanks.

I did try it initially, but couldn't work it out! Went back in and gave it another crack and got it going. The issue I'm having though is that it doesn't seem to work in model or object mode. GetNearestPolygon() returns a null pointer if the mode is one of the two mentioned.

I tried setting the mode and changing it back after I've finished, but it seems to cause a lot of messaging and redrawing, which seems a bit unnecessary. Is there a way to address this without changing modes?


On 30/08/2016 at 06:01, xxxxxxxx wrote:


Well, I've only ever used GetNearestPoint() on the active object. Is it possible that the object needs to be the active object for GetNearestPoint(), GetNearestEdge() and GetNearestPolygon() to work?

Cactus Dan

On 30/08/2016 at 07:17, xxxxxxxx wrote:

Just a word of caution about what you're doing.

What you are doing is traditionally done in a ToolData plugin because polling for objects might collide with other things running in C4D.
We have to be very careful not to do any tasks in a SH that would interfere with the normal operation of the program. And this sounds like it would.

Using a ToolData plugin allows us to temporarily override these things safely.
If you do decide to use a SH. Make sure that the user has a way to turn it off.


On 14/09/2016 at 02:13, xxxxxxxx wrote:

Hi Dan and Scott, apologies for the delay, I have a lot on at the moment.

Scott, I had originally stayed away from a tool plugin because I didn't want to deal with the AM. It was meant to be something that sits in the background and is only used when another plugin I have is active. But I found an excuse to use the AM to display a few things so I've since changed the above over to a ToolData. Is this the correct one? Or should I be using DescriptionToolData?

However, I still seem to be having the same problem. If the mode is Mobject then it doesn't want to work as GetNearestPolygon() returns null. In polygon mode the same code works fine. I'm wondering if this is not possible in Mobject mode?


On 14/09/2016 at 07:44, xxxxxxxx wrote:

ToolData is the older version.
-ToolData: Uses a hard coded subdialog in the .cpp file for it's GUI options
-ToolDataDescription: Uses a .res file for it's GUI options (also has the 3 reset buttons option)
Both work. But most people will probably use ToolDataDescription version.

You didn't say if you need to detect unselected objects or not?
Most people test for hits like this only while the LMB is being held down. This gives the user more control while the tool is active. And this kind of code is typically done in the MouseInput() function. Not GetCursorInfo().

Here is a small example of getting the object under the cursor

Bool DrawTool::GetCursorInfo(BaseDocument *doc, BaseContainer &data, BaseDraw *bd, Real x, Real y, BaseContainer &bc)  
  AutoAlloc<C4DObjectList> list;  
  if (!list) return FALSE;  
  LONG radius = 50;  
  if (ViewportSelect::PickObject(bd, doc, x, y, radius, VIEWPORT_PICK_FLAGS_0, NULL, list))  
      if (list->GetCount() > 0)  
          GePrint(list->GetObject(0)->GetName());  //<--Print the name of the first object in the list  
  return TRUE;  


On 15/09/2016 at 04:50, xxxxxxxx wrote:

I usually do things the hard-coded way :slightly_smiling_face:. I didn't see this before but the description tool looks derived from tool data, I was thinking the two were entirely separate. The ToolData should be fine for me at this stage though. The object doesn't need to be selected.

Your code seems to work, and I'm curious to know why mine won't in Mobject mode. Am going to fiddle with mine to see if I can spot why. It never ends though, the mouse wheel doesn't work inside a tool plugin it seems, unless you start with something else first (e.g. left mouse button). The viewport zoom seems to take precedence. How irritating, there's always something. Will take a further look at this tomorrow.

Thanks again!


On 15/09/2016 at 05:29, xxxxxxxx wrote:

Hey guys,

terribly sorry for leaving you alone so long.
WP. can you please post us some code, so we can get a better idea of what's going on.

On 16/09/2016 at 05:15, xxxxxxxx wrote:

Hi Andreas,

it's no worries!

I've realised I've jumped the gun with my last response to Scott above. What I had was working, up until needing to test the polygon. That's where the issue is. If it's in polygon mode, it works fine. If I try it in Mobject mode, I get a null ViewportPixel reference. I.e the line in the middle here:

// doc->SetMode(Mpolygon); <-- hack
ViewportPixel *ViewPixel = ViewSel->GetNearestPolygon(objL,mx,my,MAXLONGl,FALSE,nullptr,0);
// doc->SetMode(Mobject);   <-- hack

I have a hack I could use, which is to change the mode, run the above line, then change the mode back to what it was again, but that seems to cause a lot of unnecessary messaging. Is there a way to get the polygon hit in Mobject mode?


On 26/09/2016 at 09:37, xxxxxxxx wrote:


sorry, this is taking so long.

I have done some tests here and for me GetNearestPolygon() seems to work quite well, even in Object mode.

This is what I did in a ToolData:

	Bool MyToolData::GetCursorInfo(BaseDocument* doc, BaseContainer& data, BaseDraw* bd, Float x, Float y, BaseContainer& bc)
		AutoAlloc<AtomArray> ops;
		AutoAlloc<ViewportSelect> vpsel;
		if (!doc || !bd || !ops || !vpsel)
			return false;
		// Get viewport dimensions
		Int32 cl, ct, cr, cb;
		bd->GetFrame(&cl, &ct, &cr, &cb);
		// Get some objects from the scene
		BaseObject* op = doc->GetFirstObject();
		while (op)
			if (op->GetType() == Opolygon)
				// GetNearestPolygon() only works with polygonal objects, so get the cache instead
				BaseObject* opCache = op->GetCache(); // Note: Depending on the scene, this probably is not sufficient, please see GetCache() and GetDeformCache() docs
				if (opCache)
			op = op->GetNext();
		if (!vpsel->Init(cr - cl + 1, cb - ct + 1, bd, ops, Mpolygons, true, VIEWPORTSELECTFLAGS_0))
			return false;
		for (Int32 idxObj = 0; idxObj < ops->GetCount(); ++idxObj)
			Int32 ix = SAFEINT32(x);
			Int32 iy = SAFEINT32(y);
			BaseObject* opCurrent = static_cast<BaseObject*>(ops->GetIndex(idxObj));
			if (!opCurrent)
			ViewportPixel* vpp = vpsel->GetNearestPolygon(opCurrent, ix, iy, 5, false);
			if (vpp && vpp->op)
				GePrint("Found: " + vpp->op->GetName() + " - Poly Idx: " + String::IntToString(vpp->i));
		return true;

I hope this helps.

On 30/09/2016 at 01:30, xxxxxxxx wrote:

Hi Andreas,

thanks. I've spent a bit of time trying to diagnose why my code didn't work and I'm thinking it has something to do with the caches. When I pass an object to the vpsel->Init() instead of an object array, then the ViewportPixel reference was returning null. After a bit of fiddeling, I've managed to pass an object and successfully test it. Looks like I need to learn a little more on caches!