Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Applying a Target Expression tag to a child object of GVO

    Cinema 4D SDK
    c++ sdk
    3
    10
    1.4k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • mfersaouiM
      mfersaoui
      last edited by mfersaoui

      Hi,
      I want to apply a Target Expression tag to one of a child objects of GetVirtualObjects. is it possible?
      I tried with following code but nothing happens (The Target Expression tag is was added to the "Orbit" Null object, together with the target link but the Null object is not looking to the target).

      BaseContainer* data = op->GetDataInstance();
      
      ...
      
      AutoAlloc<BaseObject>	ParentPtr(Onull);
      AutoAlloc<BaseObject>	OrbitPtr(Onull);
      
      if (!ParentPtr || !OrbitPtr)
      	return false;
      
      BaseDocument* doc = op->GetDocument();
      
      ParentPtr->SetName("Parent");
      OrbitPtr->SetName("Orbit");
      
      BaseDocument* doc = op->GetDocument();
      
      BaseTag* tagTarget = OrbitPtr->MakeTag(Ttargetexpression);
      
      BaseList2D*	oTarget = data->GetLink(MYOBJECT_TARGET, doc);
      
      tagTarget->GetDataInstance()->SetLink(TARGETEXPRESSIONTAG_LINK, oTarget);
      
      OrbitPtr->InsertUnder(ParentPtr);
      
      ...
      
      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        Hi,

        the reason is probably that Cinema does not not execute tags on caches (otherwise it would not really be a cache) and what you are effectively doing in GVO is building the cache of your object. Solutions would be either creating a temporary document, adding your objects, execute the expressions pass on that document and then remove and return the objects from that temporary document. Or as a solution which much less overhead not using a target expression at all and simply construct the frame of your object yourself.

        When p is the world space position of your object, q is the world space position of your target and r is the up-vector (either user defined or derived from the object), then your frame for a z-axis look-at will be (in pseudo code):

        tangent = (q - p).normalized
        # x denotes the cross product
        normal = (tangent x r).normalized
        binormal = (tangent x normal).normalized
        # where a frame is defined as [x, y, z]
        frame = [normal, binormal, tangent]
        

        Cheers,
        zipit

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 1
        • ManuelM
          Manuel
          last edited by Manuel

          Hello,

          as @zipit said, you should create this hierarchy in a temporary document and ExecutesPasses there.

          The more straight forward is to calculate yourself the matrix or you can use VectorToHPB

          in python we could do something like this. (the method are the same in c++)

              # where a need to point to b
              apos = a.GetRelPos()
              bpos = b.GetRelPos()
              
              # be careful at the order of the operation we want to point toward b so bpos-apos
              direction = bpos-apos 
              hpb = c4d.utils.VectorToHPB(direction)
              a.SetRelRot(hpb)
          

          Some comment on your code :

          • as you are in GVO this line is not good :
          if (!ParentPtr || !OrbitPtr)
          	return false; 
          

          as said here : return "The newly allocated object chain, or nullptr if a memory error occurred."

          • we don't know what you are doing after that snippet but using AutoAlloc is good but you have to release is if you want to return the result. Otherwise, AutoAlloc will delete the object. Have a look at this manual

          Cheers,
          Manuel

          MAXON SDK Specialist

          MAXON Registered Developer

          mfersaouiM 2 Replies Last reply Reply Quote 1
          • mfersaouiM
            mfersaoui @Manuel
            last edited by mfersaoui

            @m_magalhaes
            Hi,
            Thank you so much @m_magalhaes and @zipit

            I used the following code:

            Vector direction = (target - eye).GetNormalized();
            Vector hpb = VectorToHPB(direction);
            
            eyePtr->SetRelRot(hpb);
            
            

            This line is good:

            if (!ParentPtr || !OrbitPtr)
            	return false;
            

            This line is inside a global function and not directly inside the GVO function. like the function CreateTempleBase() from the SDK Greek Temple example.

            static Bool CreateTempleRoof(BaseObject* &parentObj, const Float& baseUnit, const Vector& objSize, const Int32* objSegsPtr = nullptr);
            static Bool CreateTempleRoof(BaseObject* &parentObj, const Float& baseUnit, const Vector& objSize, const Int32* objSegsPtr /*= nullptr*/)
            {
            	...
            
            	return true;
            }
            
            1 Reply Last reply Reply Quote 0
            • mfersaouiM
              mfersaoui @Manuel
              last edited by

              @m_magalhaes
              Hi,
              I have an additional question concerning this subject.
              When I link object via the link box (Description Resource), The targetObj is not taken into account by the eyeObj immediately, but only after I change any parameter of my plugin object. The rotation change is not updated when I moving the target object.

              ...
              
              BaseList2D*	targetLink = data->GetLink(MYOBJECT_TARGET_OBJECT, doc);
              BaseObject* targetObj = static_cast<BaseObject*> (targetLink);
              
              Vector = eye = eyeObj->GetAbsPos();
              Vector = target = targetObj->GetAbsPos();
              
              Vector direction = (target - eye).GetNormalized();
              Vector hpb = VectorToHPB(direction);
              
              eyePtr->SetRelRot(hpb);
              

              Thanks.

              1 Reply Last reply Reply Quote 0
              • ManuelM
                Manuel
                last edited by Manuel

                hi,

                what come to mind is that GetVirtualObject is probably returning the cache of the object and doesn't redo the calculation.

                You need to check the dirtiness of the target and compare with what you stored in your generator.
                You have to override the function GetDirty in your generator. Check there the dirtiness of the target and change a bool. In GVO, check that bool also.

                Cheers,
                Manuel.

                MAXON SDK Specialist

                MAXON Registered Developer

                mfersaouiM 2 Replies Last reply Reply Quote 0
                • mfersaouiM
                  mfersaoui @Manuel
                  last edited by

                  @m_magalhaes

                  Hi,
                  I tried with the following code, it's work only when I drag my target object into the link box, but when I move the target object nothing happens.

                  Bool bIsDirty = op->CheckCache(hh) || op->IsDirty(DIRTYFLAGS_DATA | DIRTYFLAGS_MATRIX);
                  
                  if (!bIsDirty)
                  	return op->GetCache(hh);
                  
                  1 Reply Last reply Reply Quote 0
                  • mfersaouiM
                    mfersaoui @Manuel
                    last edited by mfersaoui

                    @m_magalhaes
                    Hi,

                    The following code works, but the op dirty is updated every time an object in my scene is moved. Not only when the target object is changing position. I don't know if this is Ok or not.

                    Float checkDirty;
                    
                    BaseObject* MagicStudio::GetVirtualObjects(BaseObject* op, HierarchyHelp* hh)
                    {
                    	// Check the passed pointer.
                    	if (!op)
                    		return BaseObject::Alloc(Onull);
                    
                    	BaseDocument* doc = op->GetDocument();
                    	BaseObject* activeObj = doc->GetActiveObject();
                    
                    	if (activeObj) {
                    		Float activeObjDirty = activeObj->GetDirty(DIRTYFLAGS_MATRIX);
                    		if (activeObjDirty) {
                    			if (checkDirty != activeObjDirty) {
                    				checkDirty = activeObjDirty;
                    				op->SetDirty(DIRTYFLAGS_DATA | DIRTYFLAGS_MATRIX);
                    			}
                    		}
                    	}
                    
                    	Bool bIsDirty = op->CheckCache(hh) || op->IsDirty(DIRTYFLAGS_DATA | DIRTYFLAGS_MATRIX);
                    
                    	if (!bIsDirty)
                    		return op->GetCache(hh);
                    
                    	...
                    
                    }
                    
                    1 Reply Last reply Reply Quote 0
                    • ManuelM
                      Manuel
                      last edited by Manuel

                      hello,

                      sorry for what we can call the latest reply ever.

                      you can store the dirtyCheck of your target in a local private variable. Just check its state and update the cache if it has changed. You also have to check for the dirtiness of the generator's matrix.

                      virtual BaseObject* GetVirtualObjects(BaseObject *op, HierarchyHelp *hh)
                      	{
                      
                      		// Basic check for error
                      		if (op == nullptr)
                      			return BaseObject::Alloc(Onull);
                      
                      		BaseDocument* doc = op->GetDocument();
                      		if (doc == nullptr)
                      			return BaseObject::Alloc(Onull);
                      
                      
                      		// Verify if object cache already exist and check its status.
                      		Bool bIsDirty = op->CheckCache(hh) || op->IsDirty(DIRTYFLAGS::DATA | DIRTYFLAGS::MATRIX);
                      
                      
                      		// retrieves the target if exist
                      		GeData data;
                      		op->GetParameter(ID_LINK, data, DESCFLAGS_GET::NONE);
                      		BaseLink *link = data.GetBaseLink();
                      
                      		BaseObject* target = nullptr;
                      		Bool bTargetDirty = false;
                      		if (link != nullptr)
                      		{
                      			target = static_cast<BaseObject*>(link->GetLink(doc, Onull));
                      			if (target != nullptr)
                      				// check the target's dirty value with the one store in our generator.
                      				// targetDirty_ is a local private variable to store the state of the target. It can be initialize in the Init function to -1 for example.
                      				if (targetDirty_ != target->GetDirty(DIRTYFLAGS::MATRIX))
                      				{
                      				
                      					targetDirty_ = target->GetDirty(DIRTYFLAGS::MATRIX);
                      					bTargetDirty = true;
                      				}
                      				
                      		}
                      
                      		bIsDirty |= bTargetDirty;
                      
                      		
                      
                      		// In case it's not dirty return the cache data without doing any calculation.
                      		if (!bIsDirty)
                      			return op->GetCache(hh);
                      
                      		ApplicationOutput("rebuild the cache");
                      
                      		BaseObject* pyr = BaseObject::Alloc(Opyramid);
                      		pyr->SetParameter(PRIM_AXIS, GeData(4), DESCFLAGS_SET::NONE);
                      
                      		if (target != nullptr)
                      		{
                      			Vector targetPos = target->GetMg().off;
                      			// don't forget to get the world coordinate of the object.
                      			Vector eyePos = op->GetMg() * pyr->GetMg().off ;
                      
                      			Vector direction = (targetPos - eyePos).GetNormalized();
                      			Vector hpb = VectorToHPB(direction);
                      
                      			pyr->SetRelRot(hpb);
                      		}
                      
                      
                      		return pyr;
                      
                      	}
                      

                      Cheers,
                      Manuel

                      MAXON SDK Specialist

                      MAXON Registered Developer

                      mfersaouiM 1 Reply Last reply Reply Quote 1
                      • mfersaouiM
                        mfersaoui @Manuel
                        last edited by

                        @m_magalhaes

                        Hello, Thank you so much.

                        1 Reply Last reply Reply Quote 0
                        • First post
                          Last post