UniqueObjectId,GetMarkerStampEx,GetMarker [SOLVED]



  • On 12/10/2014 at 01:17, xxxxxxxx wrote:

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

    ---------
    Hi PluginCafe,

    I've read a lot about uniquely identifying objects in C4D and came up with this one:

    std::string createUniqueObjectId(GeListNode* pObject)
    {
        if (pObject == nullptr)
            return "";
      
        BaseList2D* pBaseList = static_cast<BaseList2D*>(pObject);
      
        UInt32 id1;
        UInt32 id2;
        pBaseList->GetMarkerStampEx(&id1, &id2);
        return std::to_string(id1) + std::to_string(id2);
    }
    

    The advantages of a simple string are:
    * I can add a human readable postfix to it, e.g. "_" + pObject->Name() or something like that
    * I can export this id to any textfile, send it via network and therefore use it in another application as well

    The problem is, that the docu says that GetMarkerStampEx is deprecated and GetMarker should be used instead. But I want an id-system that has the same advantages listed above. I'm not interested in an object-oriented marker, that can be compared, because I cannot do this in a text-file or an external application.

    Should I ignore the "deprecated" note and go with my code? Do you know a better way of retrieving a simply string that identifies an object uniquely in C4D?

    Thanks!

    [EDIT]: I want to add that I also search for a way to identify a RayObject uniquely. Right now I'm just using the "link" member of a RayObject to get the original BaseObject. I'm doing this in VIDEOPOST_INNER in a VideoPostData plugin. C4D seems to copy all BaseObject first, so I actually can get my unique ids for them, which is great. But there is a problem with "Instances" because they get a new id every time I render an image. "Arrays" on the other hand work great. That's a bit strange because it seems to me that an Array does exactly the same as an "Instance", it simply duplicates an object and just uses different matrices etc.
    So how could I identify a RayObject uniquely? Is it even possible or is this a problem, because they only live for the short time of rendering? But why do most of the objects in the scene keep their id? Is it because C4D has its own dirty/cache-system for the VolumeData-structre and RayObjects in the background?

    [EDIT 2]: GetUniqueIP and GetGUID do not fulfill my requirements since they change when rendering to Picture viewer and afterwards to editor view. I have not experimented with them. I take this information from this thread:
    https://plugincafe.maxon.net/topic/7582/9505_getguid-fails-when-rendering&KW=GetUniqueIP&PID=37514#37514



  • On 14/10/2014 at 13:59, xxxxxxxx wrote:

    Hi FrozenTarzan,

    I'd like to note that I'm new at Maxon, but will help you as much as possible with your set of related questions:

    1. Ignoring 'deprecated': You should not ignore the 'deprecated' note, as that means the functionality was marked for removal in a future release, but retained to help SDK users port old code in advance.  As for why it's deprecated, I agree it should have been indicated, but it's clear that the replacement functionality exists.  Therefore, the new functionality should be used, even if it creates inconveniences.  An important rule of thumb is any new code should not include deprecated functionality.

    2. A better way to id an object uniquely: I looked into a few sources of info about the same issue.  I think you should use GeMarker, and use its GetMemory() or Read()/Write() functionality.  GetMemory() is probably the right choice, even if it's a bit harder to use than GetMarkerStampEx().  It's certainly easier to use than any custom solution, such as adding ID data entries to all objects and managing the generation of unique values.

    3. Identifying RayObject uniquely: There are certainly good reasons for some objects to only live a short time, even if it means their unique ID does not last long. They probably contain metadata that is generated at high cost whenever you edit your data, and yet useless unless you actually render.

    4. GetUniqueIP and GetGUID do not fulfill the requirements: I can confirm the documentation indicates it's not adequate for your needs.  A checksum certainly isn't good enough to substitute for a unique ID; with a lot of unique objects, you're bound to get two or more with the same checksum.

    I hope the above helps.

    Joey Gaspe
    SDK Support



  • On 14/10/2014 at 15:35, xxxxxxxx wrote:

    Hey Joey,

    thanks for your answers, but I'm afraid that they don't really help because I need a fixed, official solution that I can rely on (now and in the future).

    I would like to see an example of how to use GeMarker and the related functions. Can you post code snippets or, even better, a simple test-plugin that uses the Marker-System? What identification system do the developers use in the backend and is it accessible from outside (plugin developers)?



  • On 15/10/2014 at 00:16, xxxxxxxx wrote:

    Hi FrozenTarzan,

    I'm not sure why Joey's answer was not helpful for your. As he suggested, you can use
    GeMarker::GetMemory(). I would suggest you to use BaseList2D::FindUniqueID() with the
    MAXON_CREATOR_ID which basically just uses GeMarker::GetMemory(). So, the two functions
    bellow are semantically equal.

    std::string GetUniqueIDMarker(GeListNode* node)
    {
      if (node->IsInstanceOf(Tbaselist2d))
      {
        void* data = nullptr;
        Int32 size = 0;
        const GeMarker& marker = static_cast<BaseList2D*>(node)->GetMarker();
        marker.GetMemory(data, size);
        return std::string(static_cast<char*>(data), size);
      }
      else
      {
        return "";
      }
    }
      
    std::string GetUniqueID(GeListNode* node)
    {
      if (node->IsInstanceOf(Tbaselist2d))
      {
        const Char* data = nullptr;
        Int size = 0;
        if (static_cast<BaseList2D*>(node)->FindUniqueID(MAXON_CREATOR_ID, data, size))
        {
          return std::string(data, size);
        }
      }
      else
      {
        return "";
      }
    }
    

    Best,
    -Niklas



  • On 15/10/2014 at 05:47, xxxxxxxx wrote:

    Hello,

    you get a RayObject from the current VolumeData by it's index in the list with GetObj(). For a given index you can use TranslateObjIndex() function to get the old index. So you can find out if a given RayObject is new or existed in the previous frame.

    Best whishes,
    Sebastian



  • On 15/10/2014 at 07:47, xxxxxxxx wrote:

    Hi FrozenTarzan,

    I believe all your questions have been answered.  However, please still let us know either way.

    I'd like to thank NiklasR and Sebastian for the answers to FrozenTarzan's questions.

    Thanks,

    Joey Gaspe
    SDK Support



  • On 15/10/2014 at 08:30, xxxxxxxx wrote:

    Niklas.
    Your code always returns the same ID# for me.
    I had to convert it to R13 code. Am I'm converting it wrong?

    R13 versions

    String GetUniqueIDMarker(GeListNode *node)  
    {  
      if(node->IsInstanceOf(Tbaselist2d))  
      {  
      void *data = nullptr;  
      LONG size = 0;  
      const GeMarker &marker = static_cast<BaseList2D*>(node)->GetMarker();  
      marker.GetMemory(data, size);  
      CHAR *data2 = static_cast<CHAR*>(data);  
      
      return LongToString(*data2) + LongToString(size);  
      }  
      else  return "";  
    }  
      
    String GetUniqueID(GeListNode* node)  
    {  
      if (node->IsInstanceOf(Tbaselist2d))  
      {  
      const CHAR *data = nullptr;  
      VLONG size = 0;  
      if(static_cast<BaseList2D*>(node)->FindUniqueID(MAXON_CREATOR_ID, data, size))  
      {  
        return LongToString(*data) + LongToString(size);  
      }  
      }  
      
      else  return "";  
    }
    

    Using the methods to get an object's ID#

    Bool SimplePlugin::Execute(BaseDocument *doc)  
    {  
      PolygonObject *obj = (PolygonObject* ) doc->GetActiveObject();  
      if(!(obj && (obj->GetType() == Opolygon))) return FALSE;   
      
      
      GePrint( GetUniqueIDMarker(obj) );  //<--- always returns 016  
      GePrint( GetUniqueID(obj) );          //<--- always returns 016  
      
      
      EventAdd();  
      return TRUE;  
    }
    

    -ScottA



  • On 15/10/2014 at 08:47, xxxxxxxx wrote:

    Hey Joey, Niklas and Sebastian,

    thanks a lot for your responses. Since you three agree that this solution should be taken, I will rely on it :-)

    I added [SOLVED] to the title to mark it as closed/solved, I hope this is ok for you.

    [EDIT]: my title is too long^^ How do you handle closed topics here? Should I leave it as is?



  • On 15/10/2014 at 08:54, xxxxxxxx wrote:

    Hey Scott,

    your code:

    LongToString(*data)
    

    tries to convert only the first bytes to a string (depending on the size of LONG).

    Niklas uses the constructor of std::string to convert it to a string:

    std::string(data, size);
    

    In my case I want the id to be a string, but represented as a number, so I don't get strange characters when printing it to the console. So I do this:

    std::string createUniqueId(GeListNode* pObject)
    {
        if (pObject == nullptr)
            return "";
      
    	if (!pObject->IsInstanceOf(Tbaselist2d))
    		return "";
      
    	void* pData = nullptr;
    	Int32 size = 0;
    	const GeMarker& marker = static_cast<BaseList2D*>(pObject)->GetMarker();
    	marker.GetMemory(pData, size);
      
    	std::string id;
    	unsigned char* pC = static_cast<unsigned char*>(pData);
    	for (Int32 i = 0; i < size; i++)
    		id += std::to_string(static_cast<int>(pC[i]));
      
    	return id;
    }
    


  • On 15/10/2014 at 09:03, xxxxxxxx wrote:

    OK.
    Thanks FT.

    -ScottA



  • On 15/10/2014 at 10:17, xxxxxxxx wrote:

    Hi FrozenTarzan,

    I see the topic has been fully explored/verified/debugged, so I compressed the title to make the [SOLVED] fit.

    Joey


Log in to reply