Navigation

    • Register
    • Login
        No matches found
    • Search
    1. Home
    2. Cairyn
    3. Posts
    • Profile
    • More
      • Following
      • Followers
      • Topics
      • Posts
      • Best
      • Groups

    Posts made by Cairyn

    RE: BaseObject.SetModelingAxis(self, m) Not Working

    The Tool settings must set the Axis to Free (under Modeling Axis), so the change of the modeling axis has any visible effect.
    If you do that, you will see that the tag affects the modeling axis... go to Polygon Mode, Axis Mode, move the modeling axis, and it snaps back to the origin.

    The more interesting question would be why you'd want to change the tool setting in a tag? What's the purpose?

    (Also, please include the full scene, not just a screenshot, so the Maxon people don't have to re-type everything...)

    posted in Cinema 4D SDK •
    RE: Get parameter value at given time.

    @danniccs said in Get parameter value at given time.:

    I might not have been clear in my previous post. The parameter is a BaseLink to a mesh, and that parameter is not animated. It should always be a link to the same BaseObject. The mesh itself has animated parameters, and I need to access the mesh object at a specified time t. At the very least, I need to know its position, rotation and scale values at t.

    Okay, you are right, that wasn't totally clear 😇
    But anyway, in my understanding now: you have some undefined things that drive a value that does not have a track and therefore neither a curve nor keys, and you want the value itself.
    In my experience C4D will evaluate such a situation dynamically (unless cached) since there may be any kind of stuff used as driver: Python tags, XPresso tags, dynamics, particles, cloners, etc etc. So, unless you replicate the evaluation in your own code, you cannot access the result without actually allowing C4D to execute the evaluation.

    Maybe Ferdinand will have a better idea once you provide the details he asked for, I am not yet seeing quite what you are going for.

    posted in Cinema 4D SDK •
    RE: Get parameter value at given time.

    @danniccs said in Get parameter value at given time.:

    The parameter I am trying to evaluate is actually a BaseLink to a BaseObject representing a mesh. As such, I cannot use the CCurve::GetValue approach directly on the parameter.

    BaseLink is a discrete parameter, as you cannot interpolate between two links. Nevertheless, you should be able to find out what link is represented by a CCurve "curve", as you still have keys.

    1. Use CCurve.FindKey() to determine the key which is holding the data for the current frame. This would be the key to the left, except if there is none, in which case it's the key to the right.
    2. Use CKey.GetGeData() to read the data. This is not a float or long value - in this case, it's a BaseLink.

    Here is some Python script that reads the currently linked camera from a (selected) stage object:

    import c4d
    from c4d import gui
    
    
    def PrintLinkKey (currentTime, obj, parameterDesc):
        track = obj.FindCTrack(parameterDesc)
    
        if track == None: return
        curve = track.GetCurve()
        cat = track.GetTrackCategory()
        if cat != c4d.CTRACK_CATEGORY_DATA: return
    
        key = None
        currentKey = curve.FindKey(currentTime, c4d.FINDANIM_LEFT)
        if currentKey:
            key = currentKey['key']
        else:
            currentKey = curve.FindKey(currentTime, c4d.FINDANIM_RIGHT)
            if currentKey:
                key = currentKey['key']
                
        if key != None:
            data = key.GetGeData()
            print (data)
    
    
    def main():
        if op == None : return
    
        descCamLink = c4d.DescID(c4d.DescLevel(c4d.STAGEOBJECT_CLINK, c4d.DTYPE_BASELISTLINK, 0))
        PrintLinkKey (doc.GetTime(), op, descCamLink)
    
    
    if __name__=='__main__':
        main()
    

    (Yes, I know you tagged C++ but you can use the same calls and logic - not going to create a full plugin for that...)

    posted in Cinema 4D SDK •
    RE: What is my Clipboardowner for a script?

    Hmm, there seem to be recent changes in that functionality. When I try to copy a newly rendered image from the PictureViewer, the owner actually seems to be set fine in the R23.110:

    >>> c4d.GetC4DClipboardOwner()
    200000244
    

    (I assume that Python cannot do anything that C++ would not support...)

    On the other hand, your other code indeed returns 0 so maybe the functionality wasn't consistent in R23 anyway.

    Thanks, I will go with an ownerid of 0 for now... as there are no callbacks targeting my script, it can't hurt anyway.

    posted in Cinema 4D SDK •
    What is my Clipboardowner for a script?

    Hello;

    I was just looking into the clipboard functions and found that the specialized function CopyBitmapToClipboard requires a CLIPBOARDOWNER.

    I assume this has to do with delay-rendering of clipboard content and the WM_RENDERFORMAT message under Windows - no idea how the clipboard works under Mac... Wasn't actually aware that C4D uses that mechanism at all, but that's how it looks.

    So, if I use CopyBitmapToClipboard in a script or plugin of my own, I wouldn't delay-render the bitmap, so I'd use neither the CLIPBOARDOWNER_BODYPAINT nor the CLIPBOARDOWNER_PICTUREVIEWER constants, which (I assume) would cause the clipboard to send WM_RENDERFORMAT to either window instead of my script. Of course, these are the only constants available, and the function description does not offer anything else.

    When getting a bitmap from the system, GetC4DClipboardOwner returns 0, so I assume I can pass 0 safely too in calls to CopyBitmapToClipboard? Or am I overlooking some larger context here? As I am neither BodyPaint nor the PictureViewer, I would never want to use these constants anyway. But there is no speaking constant for 0, and CopyBitmapToClipboard makes it even a mandatory parameter?

    Can I crash C4D by using one of the two CLIPBOARDOWNER constants in a script (while not implementing a delay-render to clipboard at all which C4D doesn't even offer)?

    GetC4DClipboardOwner allows me to retrieve the clipboard owner, but under what circumstances would I want to know that? If this is only for the delayed retrieval of clipboard content, then this should work fully transparently (?)

    (Edit: Does look better with less formatting now?)

    posted in Cinema 4D SDK •
    RE: How can get the visibility of the every point/edge/face?

    Use any of the functions

    PointObject.GetPointH(self)
    PolygonObject.GetPolygonH(self)
    PolygonObject.GetEdgeH(self)
    
    posted in Cinema 4D SDK •
    RE: Modify rotation of clone child objects

    @will_blizzard said in Modify rotation of clone child objects:

    have no issue rendering to picture viewer with python effector, so not sure what you're referring to

    The error is in the function moData.GetFalloffs(), which you are not using, so you are not affected I guess.

    posted in Cinema 4D SDK •
    RE: Base Object - Attributes : Enabled parameter

    I don't know where it's defined, but the console tells me it's c4d.ID_BASEOBJECT_GENERATOR_FLAG.

    (You are aware that you can drag the label "Enabled" et al. to the Python console to get the access constant? And press Return to get the current value, although that is not shown as constant, at least up to R23...)

    posted in Cinema 4D SDK •
    RE: Copy Paste Point Global Position

    @ymoon

    selgmg is a c4d.Vector, while GetStringFromClipboard expects a string, so Python uses the str function to convert, which results in an unwieldy string like
    Vector(50, 0, 0)
    If you want to put that string back into a vector, you need to parse it - I don't think there is an API function that does that.

    However, you can solve the parsing issue pretty quickly by putting a JSON string to the clipboard instead which represents the three components of the vector:

    import c4d
    import json
    
    def main():
    
        sel_pt = op.GetPointS()
        id_sel_pt = 0
    
        for i in range(op.GetPointCount()):
             if sel_pt.IsSelected(i):
                 id_sel_pt = i
    
        objectmg = op.GetMg()
        selgmg = op.GetPoint(id_sel_pt) * objectmg
        selgmgStr = json.dumps([selgmg.x, selgmg.y, selgmg.z])
        print(selgmg, selgmgStr)
        
        c4d.CopyStringToClipboard(selgmgStr) 
    
    if __name__ == '__main__':
        main()
    

    Then the readback would be just as easy:

    import c4d
    import json
    
    def main():
    
        selgmgStr = c4d.GetStringFromClipboard() 
        selgmgList = json.loads(selgmgStr)
        selgmg = c4d.Vector(selgmgList[0], selgmgList[1], selgmgList[2])
        print(selgmg, selgmgList, selgmgStr)
        
        sel_pt = op.GetPointS()
        id_sel_pt = 0
    
        for i in range(op.GetPointCount()):
             if sel_pt.IsSelected(i):
                 id_sel_pt = i
    
        objectmg = op.GetMg()
        op.SetPoint(id_sel_pt, selgmg * ~objectmg)
        
        op.Message (c4d.MSG_UPDATE)
        c4d.EventAdd()
    
    if __name__ == '__main__':
        main()
    

    Python for C4D scripting:
    https://www.patreon.com/cairyn

    posted in Cinema 4D SDK •
    RE: Retrieving camera view volume

    @user168462

    1. Not that I know of

    2. In a parallel camera, the view frustrum is a rectangular box/"cube". The dimensions depend on the render setting dimensions and the camera's zoom value. For some reason the neutral zoom is 1.024, not 1, so: if your render settings define an image of 1000x500 pixels, and the camera zoom is 1.024, then an object with the dimensions 1000x500 units parallel to the camera's view plane will appear as filling the image.
      (Obviously, in parallel projection the distance of the object does not matter.)
      A zoom of 0.512 will show the object as half size (500x250 pixels, plusminus whatever the antialias settings screw up), a zoom of 2.048 will show the object as double size, etc. As the geometric frustrum is simple, the calculations for the volume (up to the focus distance) are trivial.

    0e8b171d-2ed9-4ff3-a14a-c55925fb05fb-image.png

    Note: I did this on R23, so if the later versions changed the neutral zoom, I wouldn't know.

    posted in Cinema 4D SDK •
    RE: MoData's GetFalloffs() returns only 0 while rendering

    Aaaand of course I forgot a crucial point: After modifying the clone positions in marr, you need to write them back to the MoData, but the last parameter apply_strength must be False:

    moData.SetArray(c4d.MODATA_MATRIX, marr, False)
    

    This is at first glance unintuitive since we want the strength to be present but when looking closely, you can fathom that the function will internally multiply with the falloff strength that it calculated itself, and that probably comes from GetFalloffs() again, so it's 0 and nothing is won. We do not want SetArray to do that at all, because we have already applied the falloffs that we calculated ourselves, and SetArray should keep its buggy hands off it!

    posted in Cinema 4D SDK •
    RE: Script Befehl

    @wdp That's not quite correct, the CallCommand IDs and the simple property calls will (almost) never set a key, for example, if you change the subdivision property in a cube, this will be a static change and not set any animation key either. The visibility flags behave exactly the same. Triggering the key-setting is just another functionality, which must be called separately.

    If you're interested in this specific group of functions, I have a course
    https://www.patreon.com/cairyn
    which explains this stuff in section 14.
    (Though I am also sure that the PluginCafé has the answer for free already, I remember the topic being discussed, but you may need to search for it, and perhaps study the API some more. Also, as @m_magalhaes said, there is a Maxon Github example where you can copy and paste code from.)

    Once you understand the way keys are handled, you can create a function that works for any DescID (or copy it...), which is probably what you want.

    As for your other question, you are calling the EditorMode functions on the op variable which is a predefined variable containing a single selection. If there are no objects selected OR there is more than one object selected, then op will be None so your call causes an error.
    To handle more than one selected object at a time, you need to retrieve a list of selections by calling GetActiveObjects ( @m_magalhaes said that as well) and then go through a for loop calling the functions for every object in the list.

    posted in Cinema 4D SDK •
    RE: MoData's GetFalloffs() returns only 0 while rendering

    @m_magalhaes Meanwhile, I found one (more or less): calculating the falloff myself. I just need to access the field list and perform a SampleList on the clones' positions. This actually works within the render process as well. Looks approx. like this:

        md = mo.GeGetMoData(op)
        if not md: return False
        cnt = md.GetCount()
        marr = md.GetArray(c4d.MODATA_MATRIX)
        fieldlist = op[c4d.FIELDS]
    
        matCloner = gen.GetMg()
        inputField = c4d.modules.mograph.FieldInput([marr[i].off * matCloner for i in range(cnt)], cnt)
        info = c4d.modules.mograph.FieldInfo.Create(c4d.FIELDSAMPLE_FLAG_VALUE, None, doc, 0, 1, inputField, gen)
        outputs = c4d.modules.mograph.FieldOutput.Create(cnt, c4d.FIELDSAMPLE_FLAG_VALUE)
        fieldlist.SampleList(info, inputField, outputs)
        # print (outputs.GetValue(0))
        for i in range(0, cnt):
            fieldStrength = outputs.GetValue(i)
            # usw usf
    
    posted in Cinema 4D SDK •
    RE: MoData's GetFalloffs() returns only 0 while rendering

    Okay, Parameter Mode is even worse when it comes to falloffs.
    GetFalloffs() results in an actual error raised.
    GetArray(c4d.MODATA_FALLOFF_WGT) results in None.
    GetArray(c4d.MODATA_WEIGHT) returns a proper array, but it's filled with zeroes no matter where the field is.

    Also, the auto-gen sample code (at least in R23) is faulty:
    it uses an attribute data which is undefined, causing an error (I found on the forum that the mode can be read via GetBlendID, though); and it reads the MoData from the gen variable instead of op, which results in GetCurrentIndex always returning 0.

    It doesn't work in the expected way anyhow. The vector returned by the Python code for ID_MG_BASEEFFECTOR_POSITION is not an absolute, but kind of a weight for the value set in the GUI. I'd need to return the final value though, which cannot be expressed as a product. This works in Full Control mode, but as the falloffs will not work there either with a field while rendering, I guess... I lost the game.

    posted in Cinema 4D SDK •
    RE: MoData's GetFalloffs() returns only 0 while rendering

    Ouch! In R25 still? Thanks for the confirmation; so I may need to rewrite my effector in Parameter mode (since you mentioned the mode, I suppose it'll work this way?)

    posted in Cinema 4D SDK •
    MoData's GetFalloffs() returns only 0 while rendering

    This riddle has stumped me:

    I create a simple Python effector. Here's a trimmed-down version:

    import c4d
    from c4d.modules import mograph as mo
    
    def main() :
        moData = c4d.modules.mograph.GeGetMoData(op)
        if moData is None: return False
    
        hasField = op[c4d.FIELDS].HasContent()
        print ("hasField:", hasField)
        farr = moData.GetFalloffs()
        print ("Falloffs:", farr)
    
        return True
    

    It does nothing to the clones in this case, just shows me the falloffs (the original modifies the clones, but it behaves the same and doesn't add to the question). The scene is also very simple, the effector has a box field that moves over some cube clones.
    6a36c5e4-ce35-48ea-82a3-50b5ae7784f2-image.png

    Now here's the riddle:
    When I animate this scene in the viewport, the console shows me the expected falloffs, changing as the animation runs. All is well with the world.
    When I render the same scene however, the console shows me a constant 0.0 for all falloffs.

    I am not able to find any explanation, any flag to set, any function to call. There are multiple examples for Python effectors, and multiple threads on this forum, none of which mentions any issue with rendering.
    I can put a MoGraph Cache Tag on the cloner, which works fine, and which replays fine (also with the more complicated original scene). But I don't see it mentioned anywhere that a cache is mandatory. The rendering should IMO just work.

    SimplestExample.c4d

    (I tried this on R21 too, with the same results.)

    posted in Cinema 4D SDK •
    RE: Looking for Developer of custom Mograph effector

    Done and delivered.

    posted in General Talk •
    RE: Selected object to selected layer

    But isn't that exactly how the context menu works? Select objects, call up context menu in the OM, go to "Add to layer", select the layer from the dynamic list of layers... okay, it's one submenu more, but I wouldn't bother.

    (Edit: It gets even more interesting if the layers are not just linear, but hierarchical - one layer is child of another layer. Then the existing "Add to layer" menus automatically create submenus for these child layers.)

    posted in Cinema 4D SDK •
    RE: How to select an Edge

    Okay, I cobbled together an example for Neighbor, but I found the class more disappointing that I remembered it.
    Neighbor doesn't seem to have many functions relating to the internal edge indices: notably, there is no method that returns the edge index for its two end points. This would make the algorithm very short (just look up all the neighbor edge indices for the given point pairs and create a BaseSelect from them).

    Instead, you can only use GetEdgePolys to find the (at most) two polygons bordering the edge given by two points. This saves you the explicit loop over all polygons... but from there, you still need to use GetPolygon to retrieve the CPolygon from the indices, then FindEdge to get the poly's edge index, then GetPolyInfo to finally get the edge's neighbor-internal index that you can use in the BaseSelect.

    It works, but unfortunately I am not convinced that it is faster or easier to read than method 2. meh

    import c4d
    from c4d import gui
    
    def IterateSelected(selection, total):
        segments = selection.GetSegments()
        for seg in range(0, segments):
            minS, maxS = selection.GetRange(seg, total)
            for index in range(minS, maxS+1):
                yield index
    
    
    def PrintSelectedEdgeIndex(bs, total):
        print ("Selected Edges:")
        for index in IterateSelected(bs, total):
            print ("Neighbor Edge:", index)
    
    
    def main():
        if op == None: return
        if op.GetType() != c4d.Opolygon: return
        ng = c4d.utils.Neighbor()
        ng.Init(op)
        print("# of edges:", ng.GetEdgeCount())
    
        # show currently selected edges
        bs = op.GetSelectedEdges(ng, c4d.EDGESELECTIONTYPE_SELECTION)
        total = op.GetPolygonCount() * 4
        PrintSelectedEdgeIndex(bs, total)
    
        # edge list as input for the selection
        edge_list = [(3,0),(0,4)]
    
        bsNew = c4d.BaseSelect()
        for edge in edge_list:
            p1,p2 = edge
            
            for poly in ng.GetEdgePolys(p1, p2):
                # print ("Poly:", poly)
                if poly != -1:
                    pinf = ng.GetPolyInfo(poly)
                    edge_index = op.GetPolygon(poly).FindEdge(p1,p2)
                    if edge_index != c4d.NOTOK:
                        ng_edge_index = pinf["edge"][edge_index]
                        print ("Poly:", poly, 
                                    "Edge index:", edge_index, 
                                    "Neighbor edge index:", ng_edge_index)
                        bsNew.Select(ng_edge_index)
    
        op.SetSelectedEdges(ng, bsNew, c4d.EDGESELECTIONTYPE_SELECTION)
        c4d.EventAdd()
    
    
    if __name__=='__main__':
        main()
    

    (Also, I didn't catch any errors, and the code is not optimized in any way.)

    posted in Cinema 4D SDK •
    RE: How to select an Edge

    Hi... not sure whether you see a problem in method 2, it seems fine to me (works, too).
    (Specifically, it works because FindEdge does not care about the direction of the edge, otherwise the middle edge might give you trouble because it runs 4-0 for poly 0, and 0-4 for poly 1. But you do not need to consider the directionality/normal of the polygon here.)
    (Also, border edges are only found once because there is only one polygon they belong to, but that is kinda obvious.)

    Now, the biggest issue seems to me that the algorithm needs to find the polygons the edges belong to. AFAIK the PolygonObject does not carry much permanent relationship data between points, polygons, and edges; only the polygon references its point indices. Everything else needs to be found by an existing method or by walking through the whole structure.

    The Neighbor class however must be initialized before usage, so in terms of speed, I wonder what is faster... Neighbor may internally initialize structures that you never use, but it is written in C++ so perhaps faster. Addressing the polygon object structure directly would be Python code, so slower, but more targeted. In case of large poly counts this may make a difference... I guess a Maxon person would be able to answer that. (I could measure runtimes directly and investigate, but I don't have the leisure to.)

    Let me just look whether I have a fitting example for Neighbor...

    posted in Cinema 4D SDK •