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

    VariableTag, Resize, and CopyTo

    Cinema 4D SDK
    2024 c++
    3
    18
    2.8k
    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.
    • D
      d_schmidt
      last edited by

      Does this provide the needed context or would more details be helpful?

      Dan

      1 Reply Last reply Reply Quote 0
      • D
        d_schmidt @i_mazlov
        last edited by

        I'm also confused by what you meant here.

        @i_mazlov said in VariableTag, Resize, and CopyTo:

        How are you going to use this color data? VertexColorTag works with polygon objects, so having it on the op (the actual object that user sees in c4d) doesn't make too much sense, as it can neither be modified with the painting tool (because it sits on your plugin object, not on polygon object) nor can it be displayed (the same reason).

        Screenshot 2024-02-05 at 12.09.50 PM.png
        Screenshot 2024-02-05 at 12.09.55 PM.png

        The VertexColorTag data can be displayed in this case. If I have the tag selected the black to white gradient is being displayed in the viewport.

        1 Reply Last reply Reply Quote 0
        • i_mazlovI
          i_mazlov
          last edited by

          Hi @d_schmidt ,

          Please excuse the delay, there's currently a deadline I need to meet. Your thread is not forgotten, I'll reply asap.

          Cheers,
          Ilia

          MAXON SDK Specialist
          developers.maxon.net

          i_mazlovI 1 Reply Last reply Reply Quote 0
          • i_mazlovI
            i_mazlov @i_mazlov
            last edited by

            Hi @d_schmidt ,

            Regarding the Modeling kernel, it is up to you what approach better suits your needs. I personally find it easier to work with ngons or when you need to modify your geometry. If you work plainly with tris/quads and do not make topological operations on your geometry, it might be just easier to use the other approach with the GetPolygonR/W() / GetPointR/W().

            Regarding documentation, you can find the reference page on our documentation Modeling. You can also check the Edge Cut Tool example in our C++ github repo.

            I'm also confused by what you meant here.

            I mean there's not much sense of having the vertex color tag only on your geometry cache, because you cannot actually use it. (The only use case I see is to expose it on make editable command). Hence, you need to have vertex color tag on both your cache object as well as on the connected BaseObject object. If your geometry has changed inside the GVO function, this means your vc tags are not in sync anymore (namely, using CopyTagsTo() function wouldn't be enough, because of the point mapping mismatch). This means that you need to rebuild your vertex color tag data.

            I see your intention to reuse the already existing data instead of rebuilding everything from scratch. However, you cannot just copy the data from previous vc tag to the new one, as you need to take into account the mapping between point data in the old vc tag and new one. For example, your points changed from [0, 1, 2] to [0, 3, 4, 1, 2]. If you copy over the old vc tag data to a new one, you'd end up in wrong results for all the points except the first one.

            In general, you should just create new vertex color tag and populate it the way you need. Plus, you need to have this tag on both cache object (what's returned by GVO) and the connected BaseObject (what's appears in the cinema). I'd suggest doing similar thing to what you showed in the very first posting:

            VertexColorTag* opVertexColorTag = static_cast<VertexColorTag*>(op->GetTag(Tvertexcolor));
            
            Int oldCount = -1;
            if (opVertexColorTag)
               oldCount = opVertexColorTag->GetDataCount();
            
            if (!opVertexColorTag || newCount != oldCount)
            {
               op->KillTag(Tvertexcolor); // how do you handle user-created VC tags?
               opVertexColorTag = VertexColorTag::Alloc(newCount);
               op->InsertTag(opVertexColorTag);
            }
            
            // fill in your data to opVertexColorTag
            
            op->CopyTagsTo(myGeometry, true, true, false, nullptr);
            

            Cheers,
            Ilia

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • D
              d_schmidt
              last edited by

              Hi @i_mazlov
              I'm currently using CopyTagsTo(), to copy the tag from the object to the cache. The problem with the methodology is that if I'm linking to the VertexColorTag externally, that deleting it and creating a new tag would break that link.

              That's where I was left at this point:

              PolygonObject* myGeometry = PolygonObject::Alloc(polygonCount,pointCount);
              
              //create geometry 
              
              VertexColorTag* existingVertexColor = static_cast<VertexColorTag*>(op->GetTag(Tvertexcolor));
              if(vertexColorTag == nullptr)
              {
                existingVertexColor = VertexColorTag::Alloc(pointCount);
                op->InsertTag(existingVertexColor);
              }
              if(pointCount != oldPointCount)
              {
                 VertexColorTag* largerVertexColor  = VertexColorTag::Alloc(pointCount);
              
                 //fill in color data for the new tag, then copy it to the existing tag
              
                largerVertexColor->CopyTo(existingVertexColor, COPYFLAGS::NONE, nullptr);
              
              
              }
              else
              {
                   //fill in color data for existingVertexColor
              }
              op->CopyTagsTo(myGeometry, true, true, false, nullptr);
              

              The problem I was encountering here is that CopyTo() doesn't seem to be copying the data from the new correct larger tag to the smaller existing one.

              Should CopyTo() work here?

              Dan

              1 Reply Last reply Reply Quote 0
              • D
                d_schmidt
                last edited by

                @i_mazlov

                From my current testing it seems like CopyTo() 'work', in so far as the data is copied, but it seems to break all existing Links to the existingVertexColor VertexColorTag. Would there be a way to preserve the links?

                Dan

                i_mazlovI 1 Reply Last reply Reply Quote 0
                • i_mazlovI
                  i_mazlov @d_schmidt
                  last edited by

                  Hi Dan,

                  Please excuse the delayed answer.

                  If the tag is deleted, the corresponding links are updated, there's nothing you can do inside the objectdata plugin to prevent this, this is simply not the plugin's responsibility to manage these links.

                  The more I look to your use case the more it seems to be something wrong, so I suggest we make a step back and see what's the stumbling block here. Your plugin wants to control the data of the vertex color tag, but not only that, you also need to expose this tag to the c4d, otherwise you won't be able to use it in the field list. Moreover, eventually your plugin changes the underlying geometry and would like to adjust the vertex color data accordingly without breaking the links that were made by the user outside of your cache geometry.

                  There's no (easy) way to keep the links of the tag to other parts of the scene if you create new tag (unless you want to fall in the rabbit hole of tracking these links manualy using FiledLayer methods), so I checked the ways to adjust the existing data of the variable tag. There's VariableChanged struct that surves for adjusting variable tag data to the new geometry (this is what's happening internally, when the geometry is changed and these changes are propagated to the tags by the modeling kernel).

                  I assume in your scenario (when you need to keep vc tags in sync: the cache one and the exposed one), you can do the mapping manually. Please check the code snippet below.

                  Cheers,
                  Ilia

                  Here is the entire GVO function based on RoundedTube example plugin:

                  
                  BaseObject* RoundedTube::GetVirtualObjects(BaseObject* op, const HierarchyHelp* hh)
                  {
                  	BaseObject* opResult = nullptr;
                  	iferr_scope_handler
                  	{
                  		BaseObject::Free(opResult);
                  		return BaseObject::Alloc(Onull);
                  	};
                  
                  	Bool isDirty = op->CheckCache(hh) || op->IsDirty(DIRTYFLAGS::DATA);
                  	if (!isDirty)
                  	{
                  		op->TouchDependenceList();
                  		DiagnosticOutput("Returning CACHE");
                  		return op->GetCache();
                  	}
                  
                  	DiagnosticOutput("Calculating NEW_OBJECT");
                  
                  	BaseContainer* data = op->GetDataInstance();
                  	Float					 dataRad = ClampValue(data->GetFloat(TUBEOBJECT_RAD, 50.0), 1.0, LIMIT<Float>::MAX);
                  	Int32					 dataPtsCount = ClampValue(data->GetInt32(TUBEOBJECT_SEG, 3), 3, LIMIT<Int32>::MAX);
                  
                  	opResult = BaseObject::Alloc(Opolygon);
                  	CheckState(opResult);
                  
                  	MAXON_SCOPE // Create geometry
                  	{
                  		AutoAlloc<Modeling> pKernel;
                  		CheckState(pKernel);
                  		CheckState(pKernel->InitObject(opResult));
                  
                  		maxon::BaseArray<Int32> pointIndices;
                  
                  		// Create points
                  		Float radStep = PI2 / dataPtsCount;
                  		for (Int idx = 0; idx < dataPtsCount; ++idx)
                  		{
                  			Vector point(Cos(radStep * idx) * dataRad, Sin(radStep * idx) * dataRad, 0);
                  
                  			Int32 pIdx = pKernel->AddPoint(opResult, point);
                  			CheckState(pIdx);
                  			pointIndices.Append(pIdx) iferr_return;
                  		}
                  
                  		// Create a single Ngon consisting of all points we've added
                  		Int32 ngonIdx = pKernel->CreateNgon(opResult, pointIndices.GetFirst(), (Int32)pointIndices.GetCount());
                  		CheckState(ngonIdx);
                  
                  		CheckState(pKernel->Commit());
                  	};
                  
                  		
                  	VertexColorTag* existingVCTag = static_cast<VertexColorTag*>(op->GetTag(Tvertexcolor));
                  
                  	// Create VC tag
                  	if (!existingVCTag)
                  	{
                  		existingVCTag = VertexColorTag::Alloc(dataPtsCount);
                  		CheckState(existingVCTag);
                  		op->InsertTag(existingVCTag);
                  		existingVCTag->SetPerPointMode(true);
                  
                  		const Float clrStep = 1.0 / dataPtsCount;
                  		const VertexColorHandle handle = existingVCTag->GetDataAddressW();
                  		for (Int32 idx = 0; idx < dataPtsCount; ++idx)
                  		{
                  			const Vector clr = HSVToRGB(Vector(clrStep * idx, 1.0, 1.0));
                  			VertexColorTag::Set(handle, nullptr, nullptr, idx, maxon::ColorA32(clr.GetColor(), 1.0));
                  		}
                  	}
                  
                  	// VC tag needs adjustments
                  	if (existingVCTag->GetDataCount() != dataPtsCount)
                  	{
                  		DiagnosticOutput("Points count mismatch");
                  
                  		Random rnd;
                  		rnd.Init(123);
                  
                  		const Int32 oldDataPtsCount = existingVCTag->GetDataCount();
                  
                  		maxon::BaseArray<Int32> map;
                  		map.Resize(oldDataPtsCount) iferr_return;
                  		for (Int32 idx = 0; idx < oldDataPtsCount; ++idx)
                  			map[idx] = idx; // We just keep all the previous points stays as is
                  
                  		VariableChanged vc;
                  		vc.old_cnt = oldDataPtsCount;
                  		vc.new_cnt = dataPtsCount;
                  		vc.map = map.GetFirst();
                  
                  		// Notify object tags about the geometry change
                  		if (op->Message(MSG_POINTS_CHANGED, &vc))
                  		{
                  			// Tags adjusted its data structures, so now we fill the new data in
                  			const VertexColorHandle handle = existingVCTag->GetDataAddressW();
                  			for (Int32 idx = vc.old_cnt; idx < vc.new_cnt; ++idx)
                  			{
                  				const Vector clr = HSVToRGB(Vector(rnd.Get01(), 1.0, 1.0));
                  				VertexColorTag::Set(handle, nullptr, nullptr, idx, maxon::ColorA32(clr.GetColor(), 1.0));
                  			}
                  		}
                  	}
                  
                  	CheckState(op->CopyTagsTo(opResult, true, true, false, nullptr));
                  
                  	return opResult;
                  }
                  

                  MAXON SDK Specialist
                  developers.maxon.net

                  1 Reply Last reply Reply Quote 0
                  • D
                    d_schmidt
                    last edited by d_schmidt

                    Hello, @i_mazlov

                    You're exactly right about what I'm trying to achieve. Thanks again for the answers.

                    With your code, placed in RoundedTube, seems to work in that the VertrexColorTag is preserved between updates and I can see the tag changes as it's updated.

                    I am encountering an issue where if I increasing and decreasing Rotation Segments I'll occasionally get a crash. Sometimes the crash is pointing to this line: "CheckState(pKernel->Commit());" but other times I'm not getting a particular crash location. Do you encounter this issue?

                    Then, when I implement your fix into my code, everything seems to be working well, until I shrink down the number of points I have. When increasing the number of points it seems like the VertexColorTag is increasing up correctly but when I scale decrease the number of points it will crash occasionally. This isn't tied to the above crash, since I'm not using Modeling in my code. Would any tweaks need to be done to the code to handle shrinking point counts?

                    Dan

                    1 Reply Last reply Reply Quote 0
                    • i_mazlovI
                      i_mazlov
                      last edited by

                      Hi Dan,

                      which C4D version are you using and which platform? I haven't encountered any crashes, but I only checked the functionality itself (without hard testing)

                      Cheers,
                      Ilia

                      MAXON SDK Specialist
                      developers.maxon.net

                      1 Reply Last reply Reply Quote 0
                      • D
                        d_schmidt
                        last edited by

                        Hi Ilia,

                        I'm on Mac 13.3 and C4D 2024.0.0 at the moment.

                        Dan

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