Instance object and ObjectData,GetVirtualObjects



  • On 25/04/2016 at 13:10, xxxxxxxx wrote:

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

    ---------
    Because it is going to take a lot of code rewriting to implement/test this, I thought that it might be best to ask about this before embarking on such an attempt.

    My plugin is an ObjectData using GetVirtualObjects() but it doesn't use any input objects - everything is created procedurally.  It creates a lot of (similar) objects.  To add even more speed to the process, I am thinking about making a singular instance of the 'real' object and filling the result of GVO() with Oinstance objects (set up as Render Instances) which point to the 'real' object.

    The question is this: Does the 'real' object need to be in the document or would similar results be possible if it were simply included in the GVO() result?

    For example (the basic hierarchy returned by GVO()) :

    mainop_null
    ->'real'op
    ->a ton of instance ops with INSTANCEOBJECT_LINK set to 'real'op



  • On 25/04/2016 at 14:58, xxxxxxxx wrote:

    Update: It appears that this works great... until rendering.  Probably loses the link during document cloning because the linked object isn't in the document.  Worth a trial and now I know the error. ;)



  • On 25/04/2016 at 22:03, xxxxxxxx wrote:

    Hello Robert,

    no, it is not a good idea to have a linked object outside of a scene (the singular instance part of your question), as you may have found out yourself by now.

    Not only in rendering context, but on many other occasions, C4D needs to clone objects and documents. And usually links are evaluated only within a single document.

    On the second part your question, it should be perfectly fine to have the object in your GVO result, as it "will" be in the document, when other instances of C4D evaluate the links. And you may also hide the real_op from the user. Let me know, when your second post already relates to this solution.

    In C++ one way to go would be a SceneHook, which can hold the object (without having it inserted into the Object Manager). But still, you'd need to make sure, that the links would be reworked after your generator is being copied into a new scene to avoid cross-scene links.



  • On 26/04/2016 at 09:47, xxxxxxxx wrote:

    Another way would be to insert all the clone masters under a main null in GVO (and not directly into the document). Once they are inserted, you can use them as instance masters, and it would work in both viewport and render.
    The user would not see anything different as everything is parented under the main null.



  • On 26/04/2016 at 10:29, xxxxxxxx wrote:

    If you don't want the link to break, you can implement NodeData::GetBranchInfo() with your own
    branch that contains the object(s) that you need, but don't want to appear anywhere in the scene.
    (it's still linked into the document though, but no manager/plugin implements displaying your branch :)

    Cheers,
    Niklas



  • On 26/04/2016 at 10:36, xxxxxxxx wrote:

    That is exactly what I was doing.

    The master object is created uniquely per Object plugin instance in the document (not one for all instances of the Object plugin in all documents).  If I put this master object under the main null in GVO and link all of the instances (under the same main null in GVO) to it, it works until I render.  Then the instances do not show up.



  • On 26/04/2016 at 11:07, xxxxxxxx wrote:

    I can confirm that adding the master object under the GVO main null does not work.  When I render, none of the instances linking to the master are rendered - but they show in the viewport.

    How would one go about implementing the GetBranchInfo() in this case?  In my one plugin, Greebler, the 'nurnies' (objects) exist or are added to the document for Instance references (the simple way).  For GetBranchInfo(), should I store the master object in a BaseLink (in the description resource) or would that be redundant with GetBranchInfo()?  Not many examples except an old one from 2006 (presented again by Scott in 2011).  This master object will need to be persistent (between saves and loads of the document).



  • On 26/04/2016 at 11:40, xxxxxxxx wrote:

    Not sure about the saving part. I do not think that it will be saved automatically, so I do think that
    you might need to save it yourself from NodeData::Write() somehow. Hopefully the SDK Support Team
    can shed some light on this.

    Your implementation could look something like what you see below. After you did this, the Instance
    Object should keep the link to the object even when you render.

    	GeListHead* myListHead = nullptr;
    	String* myListHeadName = nullptr;
    	virtual Int32 GetBranchInfo(GeListNode* node, BranchInfo* info, Int32 max, GETBRANCHINFO flags) override {
    		// Allocate the head and name for the branch. You probably want to do this
    		// from something like Init() instead. And free it in Free()!
    		if (!myListHead) {
    			myListHead = GeListHead::Alloc();
    			if (myListHead) {
    				myListHead->SetParent(node);
    				BaseObject* test = BaseObject::Alloc(Ocube);
    				if (test) myListHead->InsertFirst(test);
    			}
    		}
    		if (!myListHeadName) {
    			myListHeadName = NewObjClear(String);
    			if (myListHeadName) *myListHeadName = "myListHead";
    		}
      
    		Int32 count = 0;
    		if (myListHead && myListHeadName && count < max) {
    			BranchInfo& branch = info[count++];
    			branch.flags = BRANCHINFOFLAGS_0;
    			branch.head = myListHead;
    			branch.name = myListHeadName;
    		}
    		return count;		
    	}
    

    This is how it would look like in the C++ SDK ActiveObjectManager dialog.

    Cheers,
    Niklas



  • On 26/04/2016 at 11:45, xxxxxxxx wrote:

    Still not seeing the objects in render:

    Header:

    // CLASS: V4DObject
    class V4DObject : public ObjectData
    {
    	INSTANCEOF(V4DObject,ObjectData)
      
    	private:
    		// Data
    		// --- This is the list head were the plugin node will be inserted to
    		AutoAlloc<GeListHead>		m_pBranchHead;
    		String						m_String_BranchName;
    		//pointer to an instance of the plugin node
    		BaseList2D*					m_pBranchNode;
                    ...
    

    CPP File

    // NodeData.Init
    //*---------------------------------------------------------------------------*
    Bool V4DObject::Init(GeListNode* node)
    //*---------------------------------------------------------------------------*
    {
    	if (node == nullptr)
    		return MessageSystem::Throw(GeLoadString(KDZS_ERR_MEMORY), "V4DObject.Init.node");
      
    	// Allocate V4DPlant
    	m_pPlant =	NewObjClear(V4DPlant, static_cast<BaseObject*>(node));
    	if (m_pPlant == nullptr)
    		return ErrPrt("V4DPlant allocation failed!");
    	if (!m_pPlant->Init())
    		return ErrPrt("V4DPlant initialization failed!");
      
    	// Establish GeListHead and Node to store Leaf Object (??)
    	if (m_pBranchHead == nullptr)
    		return ErrPrt("V4DObject.Init.m_pBranchHead");
    	if (m_pBranchNode == nullptr)
    	{
    		// - Create Master Leaf Object for Render Instances
    		m_pBranchNode =	m_pPlant->CreateLeafObject();
    		if (m_pBranchNode == nullptr)
    			return ErrPrt("V4DObject.Init.m_pPlant.CreateLeafObject");
    	}
    	// Needed to save the node list
    	m_pBranchHead->SetParent(node);
    	// Insert the plugin node into the list
    	m_pBranchHead->Insert(m_pBranchNode, nullptr, nullptr);
      
    	// Allocate dialog to impart and stop growth
    	m_pDialog =	NewObjClear(V4DGrowthDialog);
    	if (m_pDialog == nullptr)
    		return ErrPrt("V4DGrowthDialog allocation failed!");
      
    	// Set default values
    	SetDefaults(static_cast<BaseObject*>(node));
    	m_Bool_RebuildCache =	TRUE;
    	return TRUE;
    }
    // NodeData.GetBranchInfo
    //*---------------------------------------------------------------------------*
    Int32 V4DObject::GetBranchInfo(GeListNode* node, BranchInfo* info, Int32 max, GETBRANCHINFO flags)
    //*---------------------------------------------------------------------------*
    {
    	// Fill the info structure array; only one element is saved in this example
    	info[0].head =	m_pBranchHead;
    	info[0].name =	&m_String_BranchName;
    	info[0].id =	ID_VINEMA4D_OBJECT;
    	info[0].flags =	BRANCHINFOFLAGS_0;
      
    	// Return the number of filled BranchInfo elements
    	return 1;
    }
    // NodeData.Copy - Copy data to copied BaseObject
    //*---------------------------------------------------------------------------*
    Bool V4DObject::CopyTo(NodeData* dest, GeListNode* snode, GeListNode* dnode, COPYFLAGS flags, AliasTrans* trn)
    //*---------------------------------------------------------------------------*
    {
    	if (snode == nullptr)
    		return MessageSystem::Throw(GeLoadString(KDZS_ERR_MEMORY), "V4DObject.CopyTo.snode");
    	if (m_pBranchHead == nullptr)
    		return MessageSystem::Throw(GeLoadString(KDZS_ERR_MEMORY), "V4DObject.CopyTo.m_pBranchHead");
    	if (m_pPlant == nullptr)
    		return MessageSystem::Throw(GeLoadString(KDZS_ERR_MEMORY), "V4DObject.CopyTo.m_pPlant");
    	// Get Destination V4DObject and its V4DPlant
    	V4DObject*		dobj =	(V4DObject* )dest;
    	if (dobj == nullptr)
    		return MessageSystem::Throw(GeLoadString(KDZS_ERR_MEMORY), "V4DObject.CopyTo.dobj");
    	if (dobj->m_pPlant == nullptr)
    		return MessageSystem::Throw(GeLoadString(KDZS_ERR_MEMORY), "V4DObject.CopyTo.dobj.m_pPlant");
    	if (!m_pBranchHead->CopyTo(dobj->m_pBranchHead, COPYFLAGS_0, trn))
    		return MessageSystem::Throw(GeLoadString(KDZS_ERR_MEMORY), "V4DObject.CopyTo.m_pBranchHead.CopyTo");
    	return m_pPlant->CopyTo(dobj->m_pPlant);
    }
    

    m_pBranchNode is being passed to be set as the link in the Render Instance objects.



  • On 26/04/2016 at 12:11, xxxxxxxx wrote:

    Yes, it does exist.  And just for completeness, my first attempt was with an object underneath my Object plugin object used to link to the GVO Render Instance objects and that worked perfectly.  So, something about having it stored 'internally' isn't working for me.  A pointer to the object (m_pBranchNode) is being passed to be set in the Render Instances.  (???)



  • On 26/04/2016 at 12:24, xxxxxxxx wrote:

    It just appeared to me that the object won't get all the object routines if it is in a separate branch.
    So it's GetVirtualObjects() method will not be called, thus the cache will not be filled etc. And that will make
    the Instance Objects actually instance nothing..

    Sorry :)



  • On 26/04/2016 at 12:36, xxxxxxxx wrote:

    What is weird is that if I look at the CACHE, the instances are there and they are pointing to that Leaf object.  When I render *OR* when I MakeEditable, the link to the Leaf object is lost.

    Unless anyone has any code tricks that work, the master object is going into the document I suppose.



  • On 26/04/2016 at 14:03, xxxxxxxx wrote:

    This code, added in GVO,  works for me in both viewport and render

    	BaseObject* cube = BaseObject::Alloc(Ocube);
    	LONG count = 10;
    	BaseObject* main = BaseObject::Alloc(Onull);
    	cube->InsertUnder(main);
    	for (LONG i=0; i<count; i++)
    	{
    		BaseObject*	iop = BaseObject::Alloc(Oinstance); 
    		BaseContainer* ibc = iop->GetDataInstance();
    		ibc->SetLink(INSTANCEOBJECT_LINK, cube);
    		ibc->SetBool(INSTANCEOBJECT_RENDERINSTANCE, TRUE);
    		iop->SetAbsPos(Vector(300*i, 0,0));
    		iop->InsertUnderLast(main);
    	}
      
    	return main;
    

    If it works for you, you can then substitute the cube for whatever objects you have in your plugin.



  • On 26/04/2016 at 14:10, xxxxxxxx wrote:

    Now, even with the master object in the document, I still don't get anything even with MakeEditable (???). So, the master object is in the document.  The Instances point to it.  And they still won't render.  That is strange.  It is almost as if something is broken somewhere (but I can't find it).  Maybe it is my master object.  I will try with another object to see the results.

    Note: Since my first similar test, I have implemented multi-processing threads to break up the work for GVO.  That works when I create the objects as objects (instead of instances).  It is very fast - but since it is creating thousands of objects, it gets slow (say, in render or running animation in the viewport).  Thus, the reason to use instances instead of geometry.

    UPDATE NOTE!!!!!
    Yeah, it helps when none of the scale values are 0.0.  Do not ask how that got passed into the viewport and displayed at all.  Once scales were properly set on the instances, they are working with the master under the GVO null.  Oye vay!

    Thanks for your help and patience, everyone!!!!!
    Have a beer on me.  I am going to have a few on you guys! 👏


Log in to reply