Wrong random clone displayed issue



  • Ok, here’s a thing you normally don’t notice when using a cloner with random clones, because random is random, right?.. Under certain circumstances the clones displayed in the viewport don’t correspond with the indices they are supposed to be.

    This is noticeable if you manually turn off & on a/the effector, hence giving only the correct display after the toggle.

    Well, this is not about manual things. For a lot of reasons it’s very important to me to know exactly which clone is which. So, I made a “hack” to get the right results…

    See preview below for a simple example with index 2 selected in the moSelectionTag which is used in the plainEffector to hide the clone. Notice the nrs in the console - which I think are correct & don’t correspond with the viewport in the last 2 images..

    _Left_image: all goes well because the cloners offset is smaller than the “hidden index 2”.
    _Middle_image: here is where it goes wrong. The cloners offset passes index 2 (offset >= 3) and incorrect clones are shown.
    _Right_image: going back to offset 2 also shows the incorrect children.
    Once going to offset 1 everything is fine again - or after dis-/enabling the effector...

    random_clone_effector_moselection_issue_01.jpg

    So, none of this happens if I apply my “hack” in the script below which turns every effector off/on to get the right result.

    Isn’t there a more elegant solution for this?
    Is it a bug, or is it me?..

    import c4d, math
    from c4d.modules import mograph
    from c4d import utils
    
    
    def main():
        moObj = op.GetObject() # cloner > with a pyTag
        
        ##### ----- #####
        useHack = True # use a "hack", turning effector(s) on/off to get the correct random results..
        if useHack == True:
            doc = op.GetDocument()
            inex = moObj[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST]
            for i in range(inex.GetObjectCount()):
                effector = inex.ObjectFromIndex(doc, i)
                effector[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = False
                effector[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = True
                effector.Message(c4d.MSG_UPDATE)
        ##### ----- #####
        
        moData = mograph.GeGetMoData(moObj)
        moCount = moData.GetCount()
        moFlags = moData.GetArray(c4d.MODATA_FLAGS)
        moChil = moData.GetArray(c4d.MODATA_CLONE)
        
        dirChil = len(moObj.GetChildren())
        dirMod = 1/float(dirChil)
    
        for i in range(moCount):
            if moFlags[i] != 1: print ("-fl->", moFlags[i])
            elif moFlags[i] == 1:
                # method 1
                dirInst = int(math.floor(moChil[i]/dirMod))
                if dirInst == dirChil: dirInst -= 1
                # method 2 
                childIdx = int(math.floor(utils.ClampValue(moChil[i] * dirChil, 0, dirChil-1)))
                print ("-fl->", moFlags[i], "-m1->", dirInst, "-m2->", childIdx)
        print ("-----")
    

    Note: there are 2 methods in the script to get the right index for the clone. The 2nd method I found on this page: https://plugincafe.maxon.net/topic/10032/13500_identify-name-of-clone-source-at-index/2 which I used as a sanity check. Both methods display the same indices, so effectively there’s no difference.

    Also, a simple c4d file attached for testing purposes (R23). By the way, the above happens exactly the same in S22.

    _tstCloneIdx_S23_01.c4d



  • Hi @jochemdk, thanks a lot looks like you found a bug. I opened a bug report to our development team :)

    Another workaround is to use the effector not to define the visibility of an object but to define its scale to absolute 0.
    This is more efficient since you don't have to re-enable the effector (which can be an issue and cause other dirty issues) since the effector needs to change the data generated by the cloner.

    Cheers,
    Maxime.



  • @m_adam
    Thx :) I thought it was a bug... I know how to control stuff myself, but this is about catching all kinds of things from "some" user's hierarchy - including cloners in cloners and animated moSelections/Weights.



  • This post is deleted!


  • @m_adam
    Hi, I did some more testing…

    Things only go wrong with a linearCloner’s offset if:
    _ any applied baseEffector has the visibility check turned on
    _ has a mograph selection/weight tag
    _ and if a linearCloner’s offset >= the smallest index of the selection
    _ (so random has nothing to do with it :)



  • Thanks a lot for your additional testing, I will for sure attach it to the bug report :)



  • @m_adam
    Hi Maxime, One additional remark + a question…

    I’m trying to build a minimum set of rules to avoid the dis/enabling of effectors if not really necessary. I can’t use moFlags to check whether a clone is visible or not, and that’s where the issue might be:

    1_ Given the example in the previous post (with offset 2), the moFlags read something like [2,2,2,1,0,1,…..]. Flag 2 being “permanently disabled”. If it means that behind the scenes index 2 is totally ignored, this is where the problem might be…

    2A_ So reading moFlags doesn’t give me the info I need. For the moSelections I was able to find the first index of an invisible clone via “baseSelection.GetAll(count)”. So everything fine here…

    2B_ I’ve spend many hours trying to find a formula to find the (first) invisible clone with moWeights. Given any weight float value between 0 & 1, a clone gets invisible based on the strength & maximum value sliders (which are not limited and can go way past +/- 100%..).

    The question: What is the formula (with strength & maximum) to check whether a weighted clone gets invisible?

    Tia, Jochem
    (If you want I can start a new thread, but this is so closely related…)

    One function added in the script below:

    """ After some code I know which linearCloners have a moSel/Weight tag with the same name as the baseEffector selection string... 
    Per effector - with visibility turned on - I run the func below to check what the minimal offset is (for multiple cloners) & 
    return "update" True if the minOffset is >= the selection index where a clone gets invisible... 
    The passed moSelDict has selection strings as keys & indices of an inexList as values.
    """
    def checkUpd(effector, moSelDict):
        minOff = 999999
        minSelIdx = 999998
        for moInd in moSelDict[effector[c4d.ID_MG_BASEEFFECTOR_SELECTION]]:
            moObj = op[c4d.ID_USERDATA,5].ObjectFromIndex(doc,moInd) # moObj = cloner / index from a linearCloner in-exclusion list..
            if moObj[c4d.MG_LINEAR_OFFSET] < minOff: minOff = moObj[c4d.MG_LINEAR_OFFSET]
            for tag in moObj.GetTags():
                if tag.CheckType(1021338) and tag.GetName() == effector[c4d.ID_MG_BASEEFFECTOR_SELECTION]: # selection
                    bs = mograph.GeGetMoDataSelection(tag)
                    count = moObj[c4d.MG_LINEAR_COUNT] + moObj[c4d.MG_LINEAR_OFFSET]
                    try: minSel = bs.GetAll(count).index(1) # << here is where I can find the correct (in)visible selection values for moSelections
                    except ValueError: break # selectionTag with no selection..
                    if minSel < minSelIdx: minSelIdx = minSel
                    break
                elif tag.CheckType(440000231) and tag.GetName() == effector[c4d.ID_MG_BASEEFFECTOR_SELECTION]: # weight
                    strength = effector[c4d.ID_MG_BASEEFFECTOR_STRENGTH]
                    maximum = effector[c4d.ID_MG_BASEEFFECTOR_MAXSTRENGTH]
                    
                    # WHAT IS THE FORMULA TO GET (IN)VISIBLE STATE OF A WEIGHTED CLONE, BASED ON THE 2 VALUES ABOVE?..
                    
                    bs = mograph.GeGetMoDataWeights(tag)
                    minSel = 999999
                    for w in range(len(bs)):
                        if bs[w] > 0.0: # << this is of course not optimal :)
                            minSel = w
                            break
                    if minSel < minSelIdx: minSelIdx = minSel
                    break
            if minOff >= minSelIdx: return True
        return False
    

    Edit:
    It turns out I need a formula for the strength/maximum values for the moSelection version as well. I plotted some values (in excel:) for various combinations of strength/maximum with moSelections/moWeights. All show an exponential growth(/decay) curve... I know you're not supposed to write scripts for others, but if you've got some code lying around - it would be brilliant.