Need help with Bend-Deformer in Object-Plugin
-
Hi,
I created a function in Object_Plugin which creates a leaf.
The leaf is created with a plane. In the SplineData in the Attribute-Manager you can shape the leaf shape, like in the hair-Material and depending on how much points the user adds, the plane subdivides. Also I move the points of the plane so that the axis is exact on the tip/bottom of the plane.Then I added a bend deformer and wanted to fit it to the parent but I had to do it twice, so at the end i also had to move the deformer , so it behaves a little weird. It doesn`t really fit.
At the the end the deformer is in the right place but it just refresh if the user moves a point in the splinedata.
So the code is just a test and far from finished.
At the end I convert all into a polygon object.- why does I have to call fit to parent twice
- why does it not refresh
So what is the problem here, can you help me.
So I added the code and for the res-file, I think the header and str file is in this case not necessary.
I also attached the plugin file and a small video do demonstate, what I mean.Cheers
Thomas -
Hello @ThomasB,
Thank you for reaching out to us. As announced here, Maxon is currently conducting a company meeting. Please understand that our capability to answer questions is therefore limited at the moment.
Without debugging your plugin, I cannot give you here an exact answer, and I currently do not have the time to do that (extensively). Here are some pointers/ideas:
- The problem is located in
GetVirtualObjects
(GVO), i.e.,create_leaf
, with on certainty bordering probability. - You use
SendModelingCommand
(SMC) in yourcreate_leaf
and with thatGVO
. UsingSMC
inGVO
is possible, but you must adhere to threading restrictions. - You violate threading restrictions when executing the SMC
MCOMMAND_CURRENTSTATETOOBJECT
indoc=op.GetDocument()
, i.e., modify the document the node is part of while the caches of that document are being built (that is why GVO is being called). - I personally would just write a function which generates the uvw mapped plane, and one which deforms it, but a middle ground could be to execute passes as shown below.
- There were also some other problems that I spotted, see code listing.
I could only partially reproduce your problem (in 2023.2) but the code might help you anyways. I have marked my changes with
[FH]
.Cheers,
FerdinandResult:
Code:
import c4d from c4d import plugins, bitmaps import os import math PLUGIN_ID = 999999999 doc = c4d.documents.GetActiveDocument() # No, just, no. Never do this in a plugin. class Ivy_Maker(plugins.ObjectData): def __init__(self): self.SetOptimizeCache(True) def Init(self, op): """[FH] Added spline parameter and value init. """ self.InitAttr(op, bool, c4d.PY_CHECK_EXCLUDE) self.InitAttr(op, c4d.SplineData, c4d.PY_SPLINE) op[c4d.PY_CHECK_EXCLUDE] = True spline: c4d.SplineData = c4d.SplineData() spline.MakeLinearSplineLinear(lPoints=6) spline.SetKnot(0, c4d.Vector(0, 10, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear) spline.SetKnot(1, c4d.Vector(10, 40, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear) spline.SetKnot(2, c4d.Vector(30, 75, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear) spline.SetKnot(3, c4d.Vector(52, 73, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear) spline.SetKnot(4, c4d.Vector(75, 50, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear) spline.SetKnot(5, c4d.Vector(105, 0, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear) op[c4d.PY_SPLINE] = spline return True def GetVirtualObjects(self, op, hh): """[FH] """ def GetLeaf() -> c4d.PolygonObject: """[FH] This is your #create_leaf. """ # Using SMC to create a plane is overkill, I personally would just write a function that # does create that PolygonObject manually. A middle ground could be executing the cache # pass on #plane so that we can hold of its cache. plane = c4d.BaseObject(c4d.Oplane) if not plane: raise MemoryError(f"{plane = }") spline: c4d.SplineData = op[c4d.PY_SPLINE] plane[c4d.PRIM_PLANE_WIDTH] = 12 plane[c4d.PRIM_PLANE_HEIGHT] = 12 plane[c4d.PRIM_AXIS] = 2 plane[c4d.PRIM_PLANE_SUBH] = max(1, (spline.GetKnotCount() - 1)) plane[c4d.PRIM_PLANE_SUBW] = 2 temp: c4d.documents.BaseDocument = c4d.documents.BaseDocument() temp.InsertObject(plane) temp.ExecutePasses(c4d.threading.GeGetCurrentThread(), False, False, True, c4d.BUILDFLAGS_NONE) cache: c4d.PolygonObject = plane.GetCache().GetClone() if plane.GetCache() else None if cache is None: raise RuntimeError("Could not build plane generator cache.") # Do your SplineData based profile deformation. knots = spline.GetKnots() counter = 0 for index in range(0, (cache.GetPointCount() - 1) - 1, 3): knot_pos = knots[counter]["vPos"] x = -knot_pos.y / 8 y = knot_pos.z z = knot_pos.x / 2 counter += 1 cache.SetPoint(index, c4d.Vector(x, y, z)) if index + 1 == 1: z_var = 0 elif index + 1 == cache.GetPointCount() - 2: z_var = 0 else: z_var = 0 #x / 3 cache.SetPoint(index + 1, c4d.Vector(0, z_var, z)) cache.SetPoint(index + 2, c4d.Vector(-x, y, z)) cache.Message(c4d.MSG_UPDATE) # Get the bend deformation, removed never to be executed CallButton code since we are # not on the MT here. Instead, we use the bounding box of #cache to fit the bend object. boundingBox: c4d.Vector = cache.GetRad() * 2 bend: c4d.BaseObject = c4d.BaseObject(c4d.Obend) if not bend: raise MemoryError(f"{bend = }") bend.InsertUnder(cache) bend[c4d.DEFORMOBJECT_STRENGTH] = c4d.utils.DegToRad(30) bend[c4d.DEFORMOBJECT_SIZE] = c4d.Vector(0, boundingBox.z, boundingBox.x) # Rotate and move the bend deformer into place. bend.SetMg(c4d.Matrix(off=cache.GetMp()) * c4d.utils.MatrixRotY(c4d.utils.DegToRad(90)) * c4d.utils.MatrixRotZ(c4d.utils.DegToRad(90))) # Get the deform cache for that bend deformation. temp.InsertObject(cache) temp.ExecutePasses(c4d.threading.GeGetCurrentThread(), False, False, True, c4d.BUILDFLAGS_NONE) if not cache.GetDeformCache(): raise RuntimeError("Could not build plane generator deform cache.") result: c4d.PolygonObject = cache.GetDeformCache().GetClone() temp.Flush() return result return GetLeaf() def GetDDescription(self, op, description, flags): if not description.LoadDescription(op.GetType()): return False spline_data = c4d.DescID(c4d.PY_SPLINE) in_exclude = c4d.DescID(c4d.PY_INEXCLUDE) db = description.GetParameterI(spline_data) db2 = description.GetParameterI(in_exclude) if op[c4d.PY_CHECK_EXCLUDE] == 0: db.SetBool(c4d.DESC_HIDE, True) else: if db2: db2.SetBool(c4d.DESC_HIDE, True) return (True, flags | c4d.DESCFLAGS_DESC_LOADED) def GetDEnabling(self, op, did, t_data, flags, itemdesc): # if did[0].id == c4d.PY_SPLINE and op[c4d.PY_CHECK_EXCLUDE] == 1: # return False return True def Message(self, op, type, data): """[FH] """ # [FH] The message MSG_MENUPREPARE is not the place to init a parameter, unless you # intentionally want to overwrite user settings. return True if __name__ == "__main__": path, file = os.path.split(__file__) file = "icon.tif" new_path = os.path.join(path, "res", file) bitmap = bitmaps.BaseBitmap() bitmap.InitWith(new_path) plugins.RegisterObjectPlugin( id=PLUGIN_ID, str="ivy_maker", g=Ivy_Maker, description="ivy_maker", icon=bitmap, info=c4d.OBJECT_GENERATOR)
- The problem is located in
-
-
I could only partially reproduce your problem (in 2023.2) but the code might help you anyways. I have marked my changes with [FH].
And how that helped me. Thank you very much that was very clear for me to understand.
Thanks alotTom
-
@ferdinand
Hi Ferdinand, I reproduced your code and changed the things with this cache and ExecutePasses etc. in my code.
But there is still a problem with it, when I call Reload Python Plugins or when I call the plugin from Menue. The leaf is not bended. Only when I click on the splinedata field to move a point does the leaf jump into the curved shape.When I click make editable directly after I called it from the menue, the leaf is bended as polygon object, but not in the view after calling it from menue.
-
Hello @ThomasB,
I cannot reproduce this here. On which version and OS are you? Judging by the code you have sent earlier, I would say you have simply reintroduced a bug.
My code should not be able to return an unbent leaf, it either returns a leaf or throws and error. One of the problems of your code was that you had this call-button code in there which more or less led to a race condition between fit-command and the cache building of your leaf. I also removed quite a bit of questionable branching in your code (
if elif else
). I would suggest that you debug your plugin to see its branching when the bug occurs. We cannot debug your plugin for you.Cheers,
Ferdinand -
Hello @ThomasB,
without further questions or postings, we will consider this topic as solved by Monday 05/06/2023 and flag it accordingly.
Thank you for your understanding,
Maxime.