Navigation

    • Register
    • Login
    • Search
    1. Home
    2. indexofrefraction
    3. Posts
    • Profile
    • More
      • Following
      • Followers
      • Topics
      • Posts
      • Best
      • Groups

    Posts made by indexofrefraction

    RE: Insert a shader into a shader hierarchy

    Thanks a lot kbar.. i'll watch and learn!
    still... the hurdles to compile a c++ plugin are extremely high nowadays. 😕
    it would be great if the sdk plugins would be downloadable ready compiled as well

    posted in Cinema 4D SDK •
    RE: Insert a shader into a shader hierarchy

    this "Active Object Dialog" plugin looks very interesting
    if it would be understandably described how to compile it / the c++ sdk, we could check it out
    but sadly this got so complicated that you need a degree in IT to do it. .-)

    posted in Cinema 4D SDK •
    developers.maxon.net - offline

    FYI, developers.maxon.net is down since about 24 hours....

    posted in General Talk •
    RE: Gimbal Lock Safe Target Expression

    I have found another way to do this, which is maybe better (?)
    because it seems to work around tackling with parallel vectors....

    LookAtCamera::Execute() translated to python ... 🙂
    https://github.com/PluginCafe/cinema4d_cpp_sdk_extended/blob/master/plugins/cinema4dsdk/source/tag/lookatcamera.cpp

    def lookat(op, target, pitch=True):
      ''' orient op to look at target '''
      local = (~(op.GetUpMg() * op.GetFrozenMln())) * target.GetMg().off - op.GetRelPos()
      hpb = c4d.utils.VectorToHPB(local)
      if not pitch: hpb.y = op.GetRelRot().y
      hpb.z = op.GetRelRot().z
      op.SetRelRot(hpb)
      c4d.EventAdd()
    

    It seems to work fine... but for clarity... after checking the Matrix Manual (see end of the page) ...
    https://developers.maxon.net/docs/Cinema4DPythonSDK/html/manuals/data_algorithms/classic_api/matrix.html?highlight=matrix

    Is this correct / covers all cases to get the targets position in local space?
    (~(op.GetUpMg() * op.GetFrozenMln())) * target.GetMg().off
    specifically.. what is
    m = utils.MatrixMove(op.GetAbsPos()) * utils.HPBToMatrix(op.GetAbsRot()) * utils.MatrixScale(op.GetAbsScale())
    in this context? (ps. GetAbsScale misses the brackets in the docs)

    and... is this the real code of the Target Tag ?

    posted in Cinema 4D SDK •
    RE: Gimbal Lock Safe Target Expression

    Hi Ferdinand, yes all solved
    and sorry if the gimbal lock was a false interpretation of what was happening.
    From reading the old threads I thought the quaternion solution was mandatory to solve this.

    for clarity and as an example I'm including a comparison of
    your solution (working) and riccardos solution (showing the flip)

    spotLight.c4d

    but for me the case is closed...
    Thanks a lot for helping with this !

    posted in Cinema 4D SDK •
    RE: Gimbal Lock Safe Target Expression

    Hi Ferdinand,

    thanks a lot, seems I'm not the only one missing the good weather outside .-)

    The code in the first lookat() function should be abs(ri.x - 1.0),
    this comes from the post in the mentioned link. I corrected that in the original post.
    (it was just missing in the post, my code had the ri.x)

    what I'm on to is a 2 axis ceiling spot.
    its a null with a python tag having a target link userdata, then inside a hierarchy with two axes.
    atm they are driven by 2 target tags (initialized by the pyTag) one needs pitch on, one needs pitch off.
    this works fine, but as i have 900 of these in a file,
    i am trying to get rid of the target tags and do all in the pyTag.
    I can post the file tomorrow.

    what happens with my lookat() is that if you move the target,
    the spot sometimes turns upside down 180 degrees and sticks in the ceiling.

    with lookatQ() the spot simply doesn't point in the correct direction.
    but it would be very interesting to learn how to do it with quaternions.

    edit: your solution seems to work nicely, btw!
    p.s. i guess ~ is normalise and % is the cross product

    posted in Cinema 4D SDK •
    Gimbal Lock Safe Target Expression

    Hi,
    I have seen and as expected this was discussed already in the past, but I could not find any working example in this forum.

    I'd simply like to have a (gimbal lock safe) python replacement for the Target (Expression) Tag.

    I'm attaching what I have come up with until now...
    two versions #1 without , #2 with quaternion --> sadly #1 has gimbal issues and #2 fails completely
    both with a pitch lock like in the Target Tag --> sure this could be done more elegant .-)

    It was stated in one of the (below) mentioned threads, that support can't solve such problems for the users,
    but this would be such a valuable tool in the quiver that it might be an exception (?)

    best, index

    def lookat(op, target, pitchlock=0):
        ''' gimbal lock UNSAFE target expression
            this basically works, but results in axis flips sometimes '''
        r1 = op.GetRelRot() # backup
    
        p1 = op.GetMg().off # from
        p2 = target.GetMg().off # to
        tmp = c4d.Vector(0, 1, 0) # temp-up
        fo = (p2 - p1).GetNormalized() # forward
        ri = tmp.Cross(fo).GetNormalized() # right
        up = fo.Cross(ri).GetNormalized() # up
    
        # https://plugincafe.maxon.net/topic/11152/object-target-expression-in-python/2
        # try to mitigate gimbal-lock --> UNSAFE, RESULTS IN AXIS FLIPS !
        if (fo + tmp).z < 0: up.x = -up.x
        if abs(ri.x - 1.0) < 0.001: ri = -ri
    
        m = c4d.Matrix()
        m.v1 = ri
        m.v2 = up
        m.v3 = fo
        m.off = p1
        op.SetMg(m)
    
        if pitchlock:
            r = op.GetRelRot()
            op.SetRelRot(c4d.Vector(r.x, r1.y, r.z))
    
    def lookatQ(op, target, pitchlock=0):
        ''' quaternion based (gimbal lock safe ???) target expression
            sadly doesn't work and results in a wrong rotation '''
    
        r1 = op.GetRelRot() # backup
    
        p1 = op.GetMg().off # from
        p2 = target.GetMg().off # to
        tmp = c4d.Vector(0, 1, 0) # temp-up
        fo = (p2 - p1).GetNormalized() # forward
        ri = tmp.Cross(fo).GetNormalized() # right
        up = fo.Cross(ri).GetNormalized() # up
    
        # copied from
        # https://www.gamedev.net/forums/topic/613595-quaternion-lookrotationlookat-up/
        w = math.sqrt(1.0 + ri.x + up.y + fo.z) * 0.5
        w4_recip = 1.0 / (4.0 * w)
        x = (up.z - fo.y) * w4_recip
        y = (fo.x - ri.z) * w4_recip
        z = (ri.y - up.x) * w4_recip
    
        # given the above is correct, is this correctly applied here ???
        q = c4d.Quaternion()
        v = c4d.Vector(x, y, z)
        q.SetAxis(v, w)
        m = q.GetMatrix()
        m.off = p1
        op.SetMg(m)
    
        if pitchlock:
            r = op.GetRelRot()
            op.SetRelRot(c4d.Vector(r.x, r1.y, r.z))
    
    posted in Cinema 4D SDK •
    RE: Osubdivisionsurface ?

    ups, sorrryy. my bad .-)

    I know that list ... but LOL,
    now i cant find the object type for the Cloner Object 🦌

    posted in Cinema 4D SDK •
    Osubdivisionsurface ?

    hi.. just to report....

    the object type for the Subdivision Surface object seems to be missing in the python and C++ sdk...

    c4d.Osubdivisionsurface = 1007455

    best, index

    posted in Cinema 4D SDK •
    RE: Insert a shader into a shader hierarchy

    hi Ferdinand,

    i noticed that there is actually an issue with excessive gelist heads....
    if you have this :

    <c4d.BaseShader object called 'Filter/Filter' with ID 1011128 at 0x7fcebe74eeb0>
    + <c4d.GeListHead object at 0x7fcebe787a70> branch 'Shaders'
    + + <c4d.LayerShader object called 'Layer/Layer' with ID 1011123 at 0x7fcebe787ab0>
    + + + <c4d.BaseShader object called 'Noise/Noise' with ID 1011116 at 0x7fcebe787b90>
    

    GetUp() on the LayerShader seems to return None,
    which is a problem if - like in this case - you need to know if a shader is in the middle inside a shader tree,
    or if it is at the top / a direct child of the material

    this makes something like this necessary to properly "insert" a shader ....

    if isinstance(target, c4d.BaseShader): 
        shader.InsertUnder(target)
    else:
        target.InsertShader(shader)
    
    posted in Cinema 4D SDK •
    RE: Copy LayerShader to a new Material

    tx ferdinand,
    and yes intertwined threads .-)

    posted in Cinema 4D SDK •
    RE: Insert a shader into a shader hierarchy

    ok, i think i figured out my issue mentioned above ..
    it would be nice if this could be confirmed:

    materials and shaders can only have shader type nodes,
    there must be a shader gelist head at the beginning and followed by normal nodes.
    i guess this is was InsertShader() actually does.

    now if you InsertShader again in the middle of a tree, you get a second shader gelist head
    and as expected this the point is where my simple shader traversing failed.
    but.. if you insert the filter shader manually in the material manager,
    there is no second shader gelist head, just the one at the beginning.

    so i guess that c4d accepts both ways,
    but the proper way would be already to use InsertUnder if we're already in a shader type node list (?)
    using InsertShader in the middle of a shader list is not necessary and bad practice (?)
    a list with multiple shader gelist heads could (should?) be pruned of excessive heads (?)

    posted in Cinema 4D SDK •
    RE: Copy LayerShader to a new Material

    trying myself...
    a walker as above is probably not possible
    but this might be something handy for others :

    def nodesToList(op, li=None):
      ''' returns a list of a complete shader tree '''
      if not li: li = []
      if isinstance(op, c4d.GeListNode):
        while op:
          if not isinstance(op, c4d.GeListHead):
            li.append(op)
          if op.GetBranchInfo():
            for branch in op.GetBranchInfo():
              li = nodesToList(branch["head"].GetDown(), type, li)
          li = nodesToList(op.GetDown(), type, li)
          op = op.GetNext()
      return li
    
    # example
    for op in nodesToList(Mat):
          print(op)
    
    posted in Cinema 4D SDK •
    RE: Copy LayerShader to a new Material

    I have a follow up question.... and post it here because of the sample code above...
    if that is not good, could you move it to a new thread?

    Isn't there a code example somewhere how to properly walk the shader tree?
    i struggle with extending a "simple" walker function like the one below

    also if there are different node types wouldnt that need/allow an argument on which ones to follow ?
    e.g. would it be possible to walk from doc.GetFirstObject() and just get the tags?
    and finally is the walker below at least ok for objects?
    (its not my code, just copied from some old library)

    def walker(op):
      if not op: return None
      if op.GetDown():
        return op.GetDown()
      while op.GetUp() and not op.GetNext():
        op = op.GetUp()
      return op.GetNext()
    
    posted in Cinema 4D SDK •
    RE: Insert a shader into a shader hierarchy

    hm, actually i think the script does work correctly.

    i was additionally checking if the shader tree was correct
    and i was doing this using my own shader walker
    and it failed, it stopps walking after the Filter Shader

    if i use your PrintRelatedNodes() everything looks ok

    i will post a follow up question in the mentioned thread :
    https://plugincafe.maxon.net/topic/14056/copy-layershader-to-a-new-material/5

    posted in Cinema 4D SDK •
    RE: Insert a shader into a shader hierarchy

    hi ferdinand,

    our posts probably crossed each other ...
    thanks a lot, and yes you understood it right

    could you maybe have a look at the code above ?
    i replaced my findIndex() by a new findDescId() using your example
    but inserting the Layer Shader into the Filter Shader still doesn't work correctly

    best, index

    posted in Cinema 4D SDK •
    RE: Insert a shader into a shader hierarchy

    ok, I've come up with this and it kind of works...
    in the GUI it looks correct, but the shader tree is broken.
    only the filter shader is in the tree, the layer shader is missing
    i'd be thankful for a hint what i'm missing here.

    import c4d
    
    '''
    before running the script, 
    create a material with a layer shader in the color slot
    '''
    
    def main():
    	li = doc.GetActiveMaterials()
    	if not li: li = doc.GetMaterials()
    	mat = li[0]
    
    	layerShaders = []
    	sh = mat.GetFirstShader()
    	while sh is not None:
    		if sh.GetType() == c4d.Xlayer:
    			layerShaders.append(sh)
    		sh = walker(sh)
    	
    	for shLayer in layerShaders:
    		parent = shLayer.GetUp()
    		if parent is None:
    			parent = mat
    		# index = findIndex(parent, shLayer) # old
    		index = findDescId(parent, shLayer) # new :)
    		if index:
    			shLayer = shLayer.GetClone()
    
    			parent[index].Remove() # the original != the clone
    			shFilter = c4d.BaseShader(c4d.Xfilter)
    			shFilter.InsertShader(shLayer)
    			shFilter[c4d.SLA_FILTER_TEXTURE] = shLayer
    
    			parent.InsertShader(shFilter)
    			parent[index] = shFilter
    
    #def findIndex(parent, shader):
    #	description = parent.GetDescription(c4d.DESCFLAGS_DESC_0)
    #	for bc, paramid, groupid in description: 
    #		if paramid:
    #			index = paramid[0].id + 0
    #			try:
    #				if parent[index] == shader:
    #					return index
    #			except AttributeError:
    #				pass
    #	return None
    
    def findDescId(parent, shader):
    	description = parent.GetDescription(c4d.DESCFLAGS_DESC_NONE)
    	for bc, descId, _ in description:
    		if bc[c4d.DESC_SHADERLINKFLAG] == True:
    			if parent[descId] == shader:
    				return descId
    
    def walker(op):
    	if not op: return None
    	if op.GetDown():
    		return op.GetDown()
    	while op.GetUp() and not op.GetNext():
    		op = op.GetUp()
    	return op.GetNext()
    
    if __name__=='__main__':
    	main()
    
    posted in Cinema 4D SDK •
    Insert a shader into a shader hierarchy

    Hi...

    I know how to add shaders to materials or other shaders, set their parameters etc.
    but i can't wrap my head around on how to insert a shader into an unknown tree.

    explanation ..
    lets take Mat[c4d.MATERIAL_COLOR_SHADER] with an unknown content.
    now lets use a walker to process the complete shader hierarchy
    and for any Layer Shader, create a Filter Shader in place, with the Layer Shader in the link.

    creating the Filter Shader and adding the Layer Shader inside is easy,
    but how can you add the Filter Shader to the (former) parent of the Layer Shader ?
    Is there a possibility to know which slot the Layer Shader was in?
    Eg. even if you'd type check and know the parent is a Fusion Shader,
    this shader would have 3 possible slots (Blend, Mask and Channel)
    and you'd not know which slot the Layer Shader has been in.
    Is this possible somehow?

    hope this is explained in an understandable way .-)

    posted in Cinema 4D SDK •
    RE: Node Materials & Python ?

    thanks ferdinand... something to study when this summer heat is over .-)

    posted in Cinema 4D SDK •
    Node Materials & Python ?

    Hi,
    I didn't really find a good post about it...
    Is it possible to modify / create Node Materials with Python ?

    Is there maybe an example somewhere how to

    • add/remove a color shader to/from the diffuse slot
    • select an existing color shader and change its value

    best, index

    posted in Cinema 4D SDK •