VariableTag, Resize, and CopyTo
-
Hi Ilia, baca
Thanks for the reply.Thanks for the example code! Is there a manual for the Modeling kernel? I took a look in the SDK but didn't see one. Would it always be better to use that approach for creating geometry?
Here's my current setup.
For more context on what I'm trying to achieve, this is my hierarchy:
"My Point Generator" is returning a series of points, a polygon object with only points, no polygons. I'm adding a Vertex Color Tag to the plugin, and copying it to my cache. Right now with the colors being random.
Then "My Point Generator" is being given to the Cloner, for the point locations. The Vertex Color Tag is put in the Field list for the Plain effector and then the Plain Effector is given to the Cloner to pass the color data on.
Right now that is working and resulting in this:Dan
-
Does this provide the needed context or would more details be helpful?
Dan
-
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).
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.
-
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 -
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 -
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
-
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
-
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,
IliaHere 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; }
-
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
-
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 -
Hi Ilia,
I'm on Mac 13.3 and C4D 2024.0.0 at the moment.
Dan