• 0 Votes
    6 Posts
    1k Views
    ferdinandF
    Hello @JH23, without any further questions and other postings, we will consider this topic as solved and flag it as such by Friday, 17/06/2022. Thank you for your understanding, Ferdinand
  • 0 Votes
    3 Posts
    822 Views
    M
    Thank You @ferdinand Access an Enclosing Entity from a Subordinate Entity, was the solution I was searching for hence I needed to alter the GeDialog from inside the TreeViewFunctions. This seems to be the only solution hence these checkboxes belong to the treeview ?!? Anyway its working and I am very happy User sets checkbox -> Sum is calculated -> button is activated. I updated the example code (and removed the now unnecessary button to prevent further confusion) with the desired behavior for future readers. kind regards mogh """ Adapted from: https://developers.maxon.net/forum/topic/10654/14102_using-customgui-listview/2 """ import c4d import random # Be sure to use a unique ID obtained from [URL-REMOVED] PLUGIN_ID = 1000010 # TEST ID ONLY # TreeView Column IDs. ID_CHECKBOX = 1 ID_NAME = 2 ID_OTHER = 3 ID_LONGFILENAME = 4 MY_BUTON = 5 HOW_MANY = 6 ID_FILESIZE = 7 HOW_MANY_MB = 8 MY_CALC = 9 TREEVIEW = 99 # A tuple of characters to select from (a-z). CHARACTERS = tuple( chr ( n) for n in range(97, 122)) class TextureObject(object): """ Class which represent a texture, aka an Item in our list """ def __init__(self): self.texturePath = TextureObject.RandomString(5, 10) self.otherData = TextureObject.RandomString(5, 20) self.longfilename = "-" self._selected = False self.filesize = TextureObject.RandomNumber() @staticmethod def RandomString(minLength, maxLength): """Returns a string of random characters with a length between #minLength and #maxLength. The characters are taken from the 97 (a) to 122 (z) ASCII range. """ return "".join( (random.choice(CHARACTERS) for _ in range(minLength, maxLength))) @staticmethod def RandomNumber(): return random.randrange(1, 99) @property def IsSelected(self): return self._selected def Select(self): self._selected = True def Deselect(self): self._selected = False def __repr__(self): return str(self) def __str__(self): return self.texturePath class ListView(c4d.gui.TreeViewFunctions): def __init__(self, host): """Initializes a ListView with a host. Args: host (c4d.gui.GeDialog): The hosting dialog. """ # Add ten mock data texture objects. self.listOfTexture = [TextureObject() for _ in range(10)] if not isinstance(host, c4d.gui.GeDialog): raise TypeError("Expected {} for argument 'host'. Received: {}".format(c4d.gui.GeDialog, host)) else: self._host = host def DoSomethingWithDialog(self): """Calls the dialog over the stored reference. """ self._host.calc_selected() def IsResizeColAllowed(self, root, userdata, lColID): return True def IsTristate(self, root, userdata): return False def GetColumnWidth(self, root, userdata, obj, col, area): """Measures the width of cells. Although this function is called #GetColumnWidth and has a #col, it is not only executed by column but by cell. So, when there is a column with items requiring the width 5, 10, and 15, then there is no need for evaluating all items. Each item can return its ideal width and Cinema 4D will then pick the largest value. Args: root (any): The root node of the tree view. userdata (any): The user data of the tree view. obj (any): The item for the current cell. col (int): The index of the column #obj is contained in. area (GeUserArea): An already initialized GeUserArea to measure the width of strings. Returns: TYPE: Description """ # The default width of a column is 80 units. width = 80 # Replace the width with the text width. area is a prepopulated # user area which has already setup all the font stuff, we can # measure right away. if col == ID_NAME: return area.DrawGetTextWidth(obj.texturePath) + 5 if col == ID_OTHER: return area.DrawGetTextWidth(obj.otherData) + 5 if col == ID_LONGFILENAME: return area.DrawGetTextWidth(obj.longfilename) + 5 return width def IsMoveColAllowed(self, root, userdata, lColID): # The user is allowed to move all columns. # TREEVIEW_MOVE_COLUMN must be set in the container of AddCustomGui. return True def GetFirst(self, root, userdata): """ Return the first element in the hierarchy, or None if there is no element. """ rValue = None if not self.listOfTexture else self.listOfTexture[0] return rValue def GetDown(self, root, userdata, obj): """ Return a child of a node, since we only want a list, we return None everytime """ return None def GetNext(self, root, userdata, obj): """ Returns the next Object to display after arg:'obj' """ rValue = None currentObjIndex = self.listOfTexture.index(obj) nextIndex = currentObjIndex + 1 if nextIndex < len(self.listOfTexture): rValue = self.listOfTexture[nextIndex] return rValue def GetPred(self, root, userdata, obj): """ Returns the previous Object to display before arg:'obj' """ rValue = None currentObjIndex = self.listOfTexture.index(obj) predIndex = currentObjIndex - 1 if 0 <= predIndex < len(self.listOfTexture): rValue = self.listOfTexture[predIndex] return rValue def GetId(self, root, userdata, obj): """ Return a unique ID for the element in the TreeView. """ return hash(obj) def Select(self, root, userdata, obj, mode): """ Called when the user selects an element. """ # I only use the checkbox to select list elemenmts """ if mode == c4d.SELECTION_NEW: for tex in self.listOfTexture: tex.Deselect() obj.Select() elif mode == c4d.SELECTION_ADD: obj.Select() elif mode == c4d.SELECTION_SUB: obj.Deselect() """ def IsSelected(self, root, userdata, obj): """ Returns: True if *obj* is selected, False if not. """ return obj.IsSelected def SetCheck(self, root, userdata, obj, column, checked, msg): """ Called when the user clicks on a checkbox for an object in a `c4d.LV_CHECKBOX` column. """ if checked: obj.Select() else: obj.Deselect() self.DoSomethingWithDialog() def IsChecked(self, root, userdata, obj, column): """ Returns: (int): Status of the checkbox in the specified *column* for *obj*. """ if obj.IsSelected: return c4d.LV_CHECKBOX_CHECKED | c4d.LV_CHECKBOX_ENABLED else: return c4d.LV_CHECKBOX_ENABLED def GetName(self, root, userdata, obj): """ Returns the name to display for arg:'obj', only called for column of type LV_TREE """ return str(obj) # Or obj.texturePath def DrawCell(self, root, userdata, obj, col, drawinfo, bgColor): """ Draw into a Cell, only called for column of type LV_USER """ if col in (ID_OTHER, ID_LONGFILENAME): text = obj.otherData if col == ID_OTHER else obj.longfilename canvas = drawinfo["frame"] textWidth = canvas.DrawGetTextWidth(text) textHeight = canvas.DrawGetFontHeight() xpos = drawinfo["xpos"] ypos = drawinfo["ypos"] + drawinfo["height"] if (drawinfo["width"] < textWidth): while (drawinfo["width"] < textWidth): if len(text) <= 4: text = "..." break text = text[:-4] + "..." textWidth = canvas.DrawGetTextWidth(text) textWidth = canvas.DrawGetTextWidth(text) drawinfo["frame"].DrawText(text, xpos, ypos - int(textHeight * 1.1)) if col == ID_FILESIZE: text = obj.filesize canvas = drawinfo["frame"] # w = geUserArea.DrawGetTextWidth(name) h = canvas.DrawGetFontHeight() # xpos = drawinfo["xpos"] + 10 xpos = drawinfo["xpos"] + drawinfo["width"] - canvas.DrawGetTextWidth(text) ypos = drawinfo["ypos"] + drawinfo["height"] drawinfo["frame"].DrawText(text, xpos, ypos - int(h * 1.1)) def DoubleClick(self, root, userdata, obj, col, mouseinfo): """ Called when the user double-clicks on an entry in the TreeView. Returns: (bool): True if the double-click was handled, False if the default action should kick in. The default action will invoke the rename procedure for the object, causing `SetName()` to be called. """ c4d.gui.MessageDialog("You clicked on " + str(obj)) return True def DeletePressed(self, root, userdata): "Called when a delete event is received." for tex in reversed(self.listOfTexture): if tex.IsSelected: self.listOfTexture.remove(tex) class TestDialog(c4d.gui.GeDialog): _treegui = None # Our CustomGui TreeView # _listView = ListView() # Our Instance of c4d.gui.TreeViewFunctions def __init__(self): """ """ # Create an instance of ListView and give it access to the dialog # which carries it. self._listView = ListView(host=self) self.overall = len(self._listView.listOfTexture) def CreateLayout(self): # Create the TreeView GUI. customgui = c4d.BaseContainer() customgui.SetBool(c4d.TREEVIEW_BORDER, c4d.BORDER_THIN_IN) customgui.SetBool(c4d.TREEVIEW_HAS_HEADER, True) # True if the tree view may have a header line. customgui.SetBool(c4d.TREEVIEW_HIDE_LINES, True) # True if no lines should be drawn. customgui.SetBool(c4d.TREEVIEW_MOVE_COLUMN, True) # True if the user can move the columns. customgui.SetBool(c4d.TREEVIEW_RESIZE_HEADER, True) # True if the column width can be changed by the user. customgui.SetBool(c4d.TREEVIEW_FIXED_LAYOUT, True) # True if all lines have the same height. customgui.SetBool(c4d.TREEVIEW_ALTERNATE_BG, True) # Alternate background per line. customgui.SetBool(c4d.TREEVIEW_CURSORKEYS, True) # True if cursor keys should be processed. customgui.SetBool(c4d.TREEVIEW_NOENTERRENAME, False) # Suppresses the rename popup when the user presses enter. if self.GroupBegin(id=1000, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, rows=2, cols=1, groupflags=c4d.BORDER_OUT): self.GroupBorderSpace(left=5, top=5, right=5, bottom=5) self.GroupBorderNoTitle(borderstyle=c4d.BORDER_NONE) if self.GroupBegin(id=1001, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, rows=2, cols=3, groupflags=c4d.BORDER_OUT): self.GroupBorderNoTitle(borderstyle=c4d.BORDER_NONE) self._treegui = self.AddCustomGui(TREEVIEW, c4d.CUSTOMGUI_TREEVIEW, "", c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, minw=430, minh=160, customdata=customgui) if not self._treegui: print("[ERROR]: Could not create TreeView") return False self.GroupEnd() if self.GroupBegin(id=1002, flags=c4d.BFH_FIT | c4d.BFV_FIT, rows=2, cols=3, groupflags=c4d.BORDER_OUT): self.GroupBorderNoTitle(borderstyle=c4d.BORDER_NONE) self.AddStaticText(HOW_MANY, flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_LEFT | c4d.BFH_SCALE, name="Selected: 0 / " + str(self.overall)) self.AddStaticText(HOW_MANY_MB, flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_LEFT | c4d.BFH_SCALE, name="Filesize Sum: __________") self.AddButton(MY_BUTON, c4d.BFH_CENTER, name="Enable me by check-boxing") self.Enable(MY_BUTON, False) self.GroupEnd() self.GroupEnd() return True def calc_selected(self): """this is a helper to calculate the selected elements and to enable the button :return: # slected and sum of the selecteed filsize """ selected = 0 filsizesum = 0 self.overall = len(self._listView.listOfTexture) # update our count for fileitem in self._listView.listOfTexture: if fileitem.IsSelected is True: filsizesum += fileitem.filesize selected += 1 if selected > 0: self.Enable(MY_BUTON, True) else: self.Enable(MY_BUTON, False) sel_string = "Selected: " + str(selected) + " / " + str(self.overall) self.SetString(HOW_MANY, sel_string) mb_string = "Filesize Sum: " + str(filsizesum) self.SetString(HOW_MANY_MB, mb_string) return selected, filsizesum def InitValues(self): # Initialize the column layout for the TreeView. layout = c4d.BaseContainer() layout.SetLong(ID_CHECKBOX, c4d.LV_CHECKBOX) layout.SetLong(ID_NAME, c4d.LV_TREE) layout.SetLong(ID_LONGFILENAME, c4d.LV_USER) layout.SetLong(ID_OTHER, c4d.LV_USER) layout.SetLong(ID_FILESIZE, c4d.LV_USER) self._layout = layout self._treegui.SetLayout(5, layout) # Set the header titles. self._treegui.SetHeaderText(ID_CHECKBOX, "Check") self._treegui.SetHeaderText(ID_NAME, "Name") self._treegui.SetHeaderText(ID_LONGFILENAME, "Long Filename") self._treegui.SetHeaderText(ID_OTHER, "Other") self._treegui.SetHeaderText(ID_FILESIZE, "Filesize") self._treegui.Refresh() # Set TreeViewFunctions instance used by our CUSTOMGUI_TREEVIEW self._treegui.SetRoot(self._treegui, self._listView, None) return True def Command(self, id, msg): # Click on button if id == MY_BUTON: newID = int(len(self._listView.listOfTexture) + 1) tex = TextureObject() tex.texturePath = "Some new data " + str(newID) tex.longfilename = TextureObject.RandomString(20, 40) self._listView.listOfTexture.append(tex) self.calc_selected() self._treegui.Refresh() return True class MenuCommand(c4d.plugins.CommandData): dialog = None def Execute(self, doc): if self.dialog is None: self.dialog = TestDialog() return self.dialog.Open(c4d.DLG_TYPE_ASYNC, PLUGIN_ID, defaulth=300, defaultw=430) def RestoreLayout(self, sec_ref): if self.dialog is None: self.dialog = TestDialog() return self.dialog.Restore(PLUGIN_ID, secret=sec_ref) def main(): c4d.plugins.RegisterCommandPlugin( PLUGIN_ID, "Python TreeView Example", 0, None, "Python TreeView Example", MenuCommand()) if __name__ == "__main__": main() [URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
  • Thinking Particles in ParallelFor

    Cinema 4D SDK r20 c++ windows sdk
    5
    0 Votes
    5 Posts
    941 Views
    N
    Hello @ferdinand, thank you again for this very detailed and helpful reply! I tried both methods, allocating beforehand and like the previous setup, just have it after the ParallelFor altogether. Both worked, although allocating beforehand introduced some weird flickering in the particles and was (at least it feels that way) more unstable. Which is why I opted to go for the second solution as with some exstensive testing it didn't seem to cause any issues. For anyone maybe reading this in the future and having the same issues: Do not allocate the particle group inside the plugin but use a link field and have the user handle the particle group creation inside c4d, that solved a lot of weird behaviours and seemed to work better/more stable. (at least in my not so intended usecase) I will mark this thread as solved, as it works now and I can fix the main-thread issue as well without too much trouble. So thank you very much for your quick and very detailed replies! Best Regards, Florian
  • Treeview Column adjust

    Cinema 4D SDK python r20 r25
    8
    2
    0 Votes
    8 Posts
    2k Views
    ferdinandF
    Hey @mogh, Thank you for the kind words and I am happy that this solved your problem. While I sometimes channel here my inner librarian and press for a formal order for new topics, so that the SDK Team can answer them effectively, I want to make clear that all Cinema 4D programming questions are welcome on Plugin Café. Just remember to open a new topic for new questions and to place them in the General Talk forum when they are general programming questions not directly related to our APIs. We will treat them then with a lower priority and rigor, but we usually also answer there. Cheers, Ferdinand
  • Detect tool button pressed - outside tool plugin

    Cinema 4D SDK r20 python c++
    5
    0 Votes
    5 Posts
    804 Views
    C4DSC
    As I won't be spending more time on this project there is no need for delving further into the subject. As such I am setting the status of the topic to "solved".
  • How to pass a value from GeDialog to another class

    Cinema 4D SDK
    5
    0 Votes
    5 Posts
    916 Views
    D
    @ferdinand Thank you for your awesome explanation! I will keep this post as a reference for my studies as your approach seems a bit complicated for my understanding at the moment. I'm learning a lot with your contribution in this forum, and in this case here it's not gonna be different. Cheers
  • Multi-line Tabs

    Cinema 4D SDK r20 r21 r23 r25 s22 s24 c++
    3
    1
    0 Votes
    3 Posts
    844 Views
    ManuelM
    Hi, Without more question we will consider this thread as solved and close it. Cheers, Manuel
  • Unique Material per Object Plugin instance?

    Cinema 4D SDK r20 python macos
    4
    0 Votes
    4 Posts
    925 Views
    H
    Hi Guys, as I'm pretty sure I found a way to achieve what I'm after I thought I update this thread. Maybe this will help others as well. After taking some time to make NodeData.CopyTo() work, I ended up not getting it to work at all. So I thought about how I could achieve what I'm after a different way. Long story short, I ended up implementing a MessageData plugin as some kind of watchdog for a document. Since its CoreMessage runs on the main thread I can happily insert and delete materials as much as I wish to. (At least I'm hoping so ) Tl; dr The idea behind this goes as follows. I have a timer running and in addition to that I listen for c4d.EVMSG_CHANGE and do some checking to see if the scene needs to update. In my case it's comparing the amount of a specific object against the amount of "specific" materials. If there's a difference I use that difference to delete or insert materials until there's no difference. Once there's no difference I can assign the materials to the objects and let each object control its own material. To distinguish between materials responsible for my plugin and the ones that aren't I make sure to put a unique plugin id inside the base container of the material I can then check for. Here's a code snippet of that MessageData: class Watchdog(c4d.plugins.MessageData): PLUGIN_ID = "Use your own unique one" PLUGIN_NAME = "A MessageData plugin." PLUGIN_INFO = 0 def __init__(self): self._time = 1000 def GetTimer(self): return self._time def SetTimer(self, time): self._time = time @property def should_execute(self): is_mainthread = c4d.threading.GeIsMainThread() check_running = ( bool(c4d.CheckIsRunning(c4d.CHECKISRUNNING_EDITORRENDERING)), bool(c4d.CheckIsRunning(c4d.CHECKISRUNNING_EXTERNALRENDERING)), bool(c4d.CheckIsRunning(c4d.CHECKISRUNNING_INTERACTIVERENDERING)), bool(c4d.CheckIsRunning(c4d.CHECKISRUNNING_ANIMATIONRUNNING)), bool(c4d.CheckIsRunning(c4d.CHECKISRUNNING_VIEWDRAWING)) ) is_running = any(item is True for item in check_running) return is_mainthread and not is_running def CoreMessage(self, mid, mdata): if not self.should_execute: return False doc = c4d.documents.GetActiveDocument() # SceneHandler is a custom class I delegate the whole creation and comparing stuff to. objs, mats = ..., ... scene = SceneHandler(objs, mats) # Check for a change and start the timer again. But only if the scene should update. Otherwise the timer would run all the time. if mid == c4d.EVMSG_CHANGE: if scene.should_update: self.SetTimer(1000) # If we get a timer event we update the scene as long as it shouldn't update anymore. We can then stop the timer. if mid == c4d.MSG_TIMER: if not scene.should_update: self.SetTimer(0) scene.update(doc) return True Maybe this will help others. Since I found a solution for my problem this thread can be marked solved. Cheers, Sebastian
  • The oldest SDK for Cinema 4D 19-25

    Cinema 4D SDK sdk r19 r20 r21 r25 r23 s22 s24
    7
    0 Votes
    7 Posts
    2k Views
    ferdinandF
    Hello @jeremyliu1989, without any further questions or postings, we will consider this thread as solved by Friday the 4th, February 2022. Thank you for your understanding, Ferdinand
  • import presets and be reversible

    Moved Cinema 4D SDK r20 python
    6
    0 Votes
    6 Posts
    896 Views
    ferdinandF
    Hello @JH23, without any further questions or postings, we will consider this thread as solved by Friday the 4th, February 2022. Thank you for your understanding, Ferdinand
  • 0 Votes
    11 Posts
    2k Views
    ferdinandF
    Hey @HerzogVonWiesel, There is nothing that is strictly illegal to do with Script Manager scripts, but threading is not supported. Scripts are intended to be little blocks of code that run and while they run block the execution of other things. They are not intended to spawn dialogs which then dangle forever without an owner. Because when the user closes a dialog, it is not deallocated (because dialogs can be re-opened). This contrasts for example with a node, tool, or command plugin, which always sit there and do stuff even when they are not the focus of things. When they own a dialog, it is bound to the plugin instance and when properly implemented, the plugin also uses one dialog which is simply opened and closed instead of allocating a new dialog for each execution. In the end you can do pretty much anything with scripts, but you should avoid the c4d.threading module and async dialogs. There might be a minor feature I am overlooking here, but the yardstick is async execution. When something is async in nature it might not work properly in Script Manager scripts. edit: You should also avoid (attempting) to draw into a viewport from a script, or more precisely, manipulate the draw buffer of a BaseDraw. Cinema 4D won't let you do this anyways but there might be cases where you can still mess up things when you add drawing instructions. Getting information, e.g., calling myBaseDraw.GetDisplayFilter() or doing other things which are not drawing instructions is fine. You can also manipulate the parameters of a BaseDraw or switch the camera. Cheers, Ferdinand
  • Ensuring only 1 specific pyTag in a document?

    Cinema 4D SDK python r20 s24
    9
    0 Votes
    9 Posts
    1k Views
    ferdinandF
    Okay, thanks for the heads up
  • Mograph Effector List

    Cinema 4D SDK r20 sdk c++
    3
    0 Votes
    3 Posts
    712 Views
    J
    Thanks for the response. I figured it would need to be the SceneHook but always best to double check. John Terenece
  • BaseDraw::DrawTexture alpha issue

    Cinema 4D SDK r20 r21 c++
    5
    1
    0 Votes
    5 Posts
    820 Views
    C4DSC
    @m_magalhaes [image: 1631562541042-gradients.png] Did some more testing, using the original code and the conversion code, with linear workflow on and off. When I look at the original code with linear workflow OFF, this most resembles the gradient from Photoshop. Except for the gap at the top .. obviously. The result from conversion code with linear workflow ON is what I am using now, as this provides the smallest gap. And to my eyes that result looks the most linear.
  • Linkbox Dropdown Arrow

    Cinema 4D SDK c++ r20 r23
    9
    1
    0 Votes
    9 Posts
    1k Views
    LorenzJaegerL
    Hi, sorry for the late reply. Just got to test out your fix and everything works now. Thank you very much for your persistence! cheers, Lorenz
  • Drag and Drop Gradient Data onto a Gradient

    Cinema 4D SDK c++ r20 r21 r23 s22 s24 sdk
    5
    0 Votes
    5 Posts
    787 Views
    ferdinandF
    Hello @kbar, without any further questions, we will consider this topic as solved by Monday, the 25th and flag it accordingly. Thank you for your understanding, Ferdinand
  • Filename::SetMemoryReadMode Docs don't seem right.

    Moved Bugs c++ r20 r21 r23 s22 s24 sdk
    4
    1
    0 Votes
    4 Posts
    977 Views
    ferdinandF
    Hello @kbar, I should have replied here since there is at least an implied request in your last answer. I have added a task for this in our documentation task pool and added the tracking tag to this topic. Without any further questions or replies, we will consider this thread as solved by Monday the 20th and flag it accordingly. But that does not mean that the topic is forgotten, due to its to fix tag. Thank you for your understanding, Ferdinand
  • MoData Effector Rotation

    Cinema 4D SDK r20 c++ sdk
    8
    1
    0 Votes
    8 Posts
    1k Views
    ferdinandF
    Hello @johnterenece, so, to clarify this for myself. You have an ObjectData implementation which probably overwrites GetContour, i.e., builds a SplineObject , or draws spline-like information into the viewport. This plugin then has an InExcludeData parameter which is populated with Mograph effectors which are then meant to influence the vertices of your plugin. Consequently you want to do the deformation of your input geometry yourself and not like I assumed before not just clone stuff onto a PolygonObject that has been deformed by a PolyFx. First of all, I do not see really the point in reinventing the wheel in the first place, why not use PolyFX if it does what you need anyways? I would also stress that this cobling up of functionalties, deformation and generation in your case, in a single plugin and even single function in your case, is often not a very good idea, bot for technical and debugging reasons. As I said before, we cannot debug your code for you, but here are some points: The way you use a MoData tag is not its intended usage, the tag is reserved for MoGraph generators only and you hijacking it here is something you have to do on your own responsiblity. You also do not take the transforms of the effectors into account which might be a cause for your problem. There are other problems like using BaseObject::MakeTag and a possibly thread environment and the lack of error handling with maxon::BaseArray::Append which can lead to crashes, as you just store posible erros in resultVector and resultVector2D in your code which are undefined, but I assume to be of type maxon::Result<Vector> and maxon::Result<maxon:BaseArray<Vector>. But they are not the source of your problem, which likely lies in the fact that you do hijack MoData in the way you do and then do not respect the transforms of the effectors. You still have not told us the plugin type you are implementing, but if it is a ObjectData which generates a spline, I would simply build the spline on the orginal polygon geometry and then retun the spline with a PolyFX attached to it in GetContour. Much easier than reinventing the wheel here. You would have to pass through the content of the InExcludeData of your plugin to the PolyFX. Cheers, Ferdinand
  • DESC_PARENT_COLLAPSE Display

    Cinema 4D SDK r20 c++ sdk
    5
    0 Votes
    5 Posts
    943 Views
    J
    Thanks for the response. I was assuming that would be the case. John Terenece
  • Checking if Cache/Deform Cache Is Dirty?

    Cinema 4D SDK c++ r20
    4
    0 Votes
    4 Posts
    769 Views
    D
    Thanks, that was exactly what I needed!