Phong Tag with ObjectData



  • Hello,
    I'm trying to get a Phong tag to work with my ObjectData plugin similar to the Phong tags assigned to primitive Polygon Objects such as Sphere & Cube.

    Using the py-rounded_tube_r13.pyp as reference, I'm currently calling a function from ObjectData.GetVirtualObjects that sets the Phong with BaseObject.SetPhong. In my ObjectData I'm creating a Phong tag in my overridden NodeData.Init function. The Phong Tag is not having any effect unless I make the Object Editable. I've tried removing the SetPhong call, but it doesn't make a difference.

    How can I get the Phong tag to have an effect on my ObjectData object?

    Thank you!



  • Hi @blastframe I think you get the concept wrong, Generator doesn't have a "proper" Phong tag, or at least the Phong tag you apply to it has no real meaning since Phong is not a hierarchical tag(like Texture is), but it affects only the current object and a generator don't have any Polygon Information.
    With that's said this Phong tag on the generator is very useful because it let you expose parameter to the user in a meaningful manner, so the Generator can know for each Polygon Object it will generate, it can basically Retrieve a clone of this Phong and apply the same value. But the generator still has the liberty to not do it.

    So the proper workflow is

    • Create a Tag in the MSG_MENUPREPARE so you expose to the user a UI to control the Phong shading.
    • Attach a Clone of this Tag to all PolygonObject you generate within your Generator.

    So this gives us code like that:

    import os
    import math
    import sys
    import c4d
    from c4d import utils
    
    PLUGIN_ID = 1234569
    
    
    class PhongDemoObject(c4d.plugins.ObjectData):
        size = 100
    
        def __init__(self, *args):
            super(PhongDemoObject, self).__init__(*args)
            self.SetOptimizeCache(True)
    
        def GetVirtualObjects(self, op, hierarchyhelp):
            ret = self.CreateMyObject(op)
            if ret is None:
                return None
    
            ret.SetName(op.GetName())
            return ret
    
        def CreateMyObject(self, node):
            newObj = c4d.PolygonObject(8, 6)
    
            newObj.SetPoint(0, c4d.Vector(-self.size,-self.size,-self.size))
            newObj.SetPoint(1, c4d.Vector(-self.size,self.size,-self.size))
            newObj.SetPoint(2, c4d.Vector(self.size,-self.size,-self.size))
            newObj.SetPoint(3, c4d.Vector(self.size,self.size,-self.size))
            newObj.SetPoint(4, c4d.Vector(self.size,-self.size,self.size))
            newObj.SetPoint(5, c4d.Vector(self.size,self.size,self.size))
            newObj.SetPoint(6, c4d.Vector(-self.size,-self.size,self.size))
            newObj.SetPoint(7, c4d.Vector(-self.size,self.size,self.size))
    
            newObj.SetPolygon(0, c4d.CPolygon(3, 1, 7, 5))
            newObj.SetPolygon(1, c4d.CPolygon(3, 5, 4, 2))
            newObj.SetPolygon(2, c4d.CPolygon(4, 6, 0, 2))
            newObj.SetPolygon(3, c4d.CPolygon(6, 7, 1, 0))
            newObj.SetPolygon(4, c4d.CPolygon(0, 1, 3, 2))
            newObj.SetPolygon(5, c4d.CPolygon(4, 5, 7, 6))
    
            # If the generator already have a PhongTag, jsut clone it otherwise define it a 92° phong shading
            phongTag = node.GetTag(c4d.Tphong)
            if phongTag is not None:
                newTag = phongTag.GetClone(c4d.COPYFLAGS_NONE)
                newObj.InsertTag(newTag)  # Add the cloned tag to the new Polygon Object present only in the cache
            else:
                newObj.SetPhong(True, True, c4d.utils.DegToRad(92.0))
    
            newObj.Message(c4d.MSG_UPDATE)
            return newObj
    
        def Message(self, node, type, data):
            # On the creation of the Object, via the Menu create a Phong tag on the generator
            # c4d.BaseList2D(1234569) will not trigger it
            if type == c4d.MSG_MENUPREPARE:
                # Create a phong tag and set the default value to 92° (so we see the phong shading)
                node.SetPhong(True, True, c4d.utils.DegToRad(92.0))
            return True
    
    
    if __name__ == "__main__":
        c4d.plugins.RegisterObjectPlugin(id=PLUGIN_ID,
                                         str="phongdemoobject",
                                         g=PhongDemoObject,
                                         description="phongdemoobject",
                                         icon=None,
                                         info=c4d.OBJECT_GENERATOR )
    

    Regarding your issue about AttributeError: 'function' object has no attribute 'im_func': As written in the doc of NodeData.Message you are supposed to return a bool. (I agree the code sample is a bit weird because it doesn't show the return statement, I will fix it)

    I've opened a BugReport since the error could be more explicit I will look at it but I guess this is a leftover of Python2.7 since im_func is removed in Python3.

    If you have any questions, please let me know,
    Cheers,
    Maxime.



  • Hi,

    could you show us an example for your code not working? On R23 SetPhong seems to work properly, when tested from a Python generator object.

    import c4d
    
    def main():
        """
        """
        node = c4d.BaseList2D(c4d.Osphere)
        if op[c4d.ID_USERDATA, 1]:
            node.SetPhong(True, True, 3.14159 * .125)
        return node
    

    smooth_sphere.c4d

    Cheers,
    zipit



  • hi @blastframe, usually the BaseObject::SetPhong() is not called in ObjectData::GetVirtualObjects() but rather in NodeData::Message() when the message intercepted is MSG_MENUPREPARE.

    Something like

    if type == c4d.MSG_MENUPREPARE:
        node.SetPhong(True, True, c4d.utils.DegToRad(40.0))
    

    Cheers, R



  • Hi @r_gigante & @zipit,
    Thank you both for your replies. I am still experiencing the issue, unfortunately. I can set the Phong angle manually, but I want to set (or override) my object's Phong angle with a tag. As can be seen in this image, the Phong tag is at 91⁰.
    bc6daa42-e84a-4c99-a033-7414e135dec7-image.png

    It should look like this. (side note: when the object is made editable, it creates a Phong tag with the angle set with SetPhong and deletes the previous one):
    057fc233-14ec-4270-a677-5596801181af-image.png

    @zipit
    My issue is not with setting the Phong. My issue is that I cannot surface the Phong tag to the user as is done with the Cinema 4D Primitives. Here is a code example that shows how the Phong tag I'm creating does not associate with the object's Phong tag until it is made editable.

    import os
    import math
    import sys
    import c4d
    from c4d import utils
    
    PLUGIN_ID = 1234569
    
    class PhongDemoObject(c4d.plugins.ObjectData):
        size = 100
    
        def __init__(self, *args):
            super(PhongDemoObject, self).__init__(*args)
            self.SetOptimizeCache(True)
    
        def Init(self, op):
            tag = op.MakeTag(c4d.Tphong)
            tag[c4d.PHONGTAG_PHONG_ANGLELIMIT] = True
            tag[c4d.PHONGTAG_PHONG_ANGLE] = c4d.utils.DegToRad(91.0)
            tag[c4d.PHONGTAG_PHONG_USEEDGES] = False
            c4d.EventAdd()
    
            return True
    
        def GetVirtualObjects(self, op, hierarchyhelp):
            ret = self.CreateMyObject()
            if ret is None:
                return None
            ret.SetName(op.GetName())
            return ret
    
        def CreateMyObject(self):
            node = c4d.PolygonObject(8, 6)
    
            node.SetPoint(0, c4d.Vector(-self.size,-self.size,-self.size))
            node.SetPoint(1, c4d.Vector(-self.size,self.size,-self.size))
            node.SetPoint(2, c4d.Vector(self.size,-self.size,-self.size))
            node.SetPoint(3, c4d.Vector(self.size,self.size,-self.size))
            node.SetPoint(4, c4d.Vector(self.size,-self.size,self.size))
            node.SetPoint(5, c4d.Vector(self.size,self.size,self.size))
            node.SetPoint(6, c4d.Vector(-self.size,-self.size,self.size))
            node.SetPoint(7, c4d.Vector(-self.size,self.size,self.size))
    
            node.SetPolygon(0, c4d.CPolygon(3,1,7,5))
            node.SetPolygon(1, c4d.CPolygon(3,5,4,2))
            node.SetPolygon(2, c4d.CPolygon(4,6,0,2))
            node.SetPolygon(3, c4d.CPolygon(6,7,1,0))
            node.SetPolygon(4, c4d.CPolygon(0,1,3,2))
            node.SetPolygon(5, c4d.CPolygon(4,5,7,6))
    
            node.SetPhong(True, True, c4d.utils.DegToRad(40.0))
    
            node.Message(c4d.MSG_UPDATE)
            return node
    
    """
        #this throws an error:
        #AttributeError: 'function' object has no attribute 'im_func'
        def Message(self, node, type, data):
            if type == c4d.MSG_MENUPREPARE:
                node.SetPhong(True, True, c4d.utils.DegToRad(40.0))
                return True
    """
    
    if __name__ == "__main__":
        c4d.plugins.RegisterObjectPlugin(id=PLUGIN_ID,
                                         str="PhongDemoObject",
                                         g=PhongDemoObject,
                                         description="phongdemoobject",
                                         icon=None,
                                         info=c4d.OBJECT_GENERATOR )
    
    

    I got some errors without including the res folder, so I've uploaded it if you wish to demo it on your machine:
    Phong Demo Object.zip

    @r_gigante
    I am calling BaseObject::SetPhong() as it is done in the example: py-rounded_tube_r13.pyp. In that file, the method that sets the Node's points & then the Phong (GenerateLathe) is called from ObjectData::GetVirtualObjects().

    Using the code below threw this error AttributeError: 'function' object has no attribute 'im_func':

        def Message(self, node, type, data):
            if type == c4d.MSG_MENUPREPARE:
                node.SetPhong(True, True, c4d.utils.DegToRad(40.0))
    

    Am I doing this incorrectly?

    Thanks again.



  • Hi @blastframe I think you get the concept wrong, Generator doesn't have a "proper" Phong tag, or at least the Phong tag you apply to it has no real meaning since Phong is not a hierarchical tag(like Texture is), but it affects only the current object and a generator don't have any Polygon Information.
    With that's said this Phong tag on the generator is very useful because it let you expose parameter to the user in a meaningful manner, so the Generator can know for each Polygon Object it will generate, it can basically Retrieve a clone of this Phong and apply the same value. But the generator still has the liberty to not do it.

    So the proper workflow is

    • Create a Tag in the MSG_MENUPREPARE so you expose to the user a UI to control the Phong shading.
    • Attach a Clone of this Tag to all PolygonObject you generate within your Generator.

    So this gives us code like that:

    import os
    import math
    import sys
    import c4d
    from c4d import utils
    
    PLUGIN_ID = 1234569
    
    
    class PhongDemoObject(c4d.plugins.ObjectData):
        size = 100
    
        def __init__(self, *args):
            super(PhongDemoObject, self).__init__(*args)
            self.SetOptimizeCache(True)
    
        def GetVirtualObjects(self, op, hierarchyhelp):
            ret = self.CreateMyObject(op)
            if ret is None:
                return None
    
            ret.SetName(op.GetName())
            return ret
    
        def CreateMyObject(self, node):
            newObj = c4d.PolygonObject(8, 6)
    
            newObj.SetPoint(0, c4d.Vector(-self.size,-self.size,-self.size))
            newObj.SetPoint(1, c4d.Vector(-self.size,self.size,-self.size))
            newObj.SetPoint(2, c4d.Vector(self.size,-self.size,-self.size))
            newObj.SetPoint(3, c4d.Vector(self.size,self.size,-self.size))
            newObj.SetPoint(4, c4d.Vector(self.size,-self.size,self.size))
            newObj.SetPoint(5, c4d.Vector(self.size,self.size,self.size))
            newObj.SetPoint(6, c4d.Vector(-self.size,-self.size,self.size))
            newObj.SetPoint(7, c4d.Vector(-self.size,self.size,self.size))
    
            newObj.SetPolygon(0, c4d.CPolygon(3, 1, 7, 5))
            newObj.SetPolygon(1, c4d.CPolygon(3, 5, 4, 2))
            newObj.SetPolygon(2, c4d.CPolygon(4, 6, 0, 2))
            newObj.SetPolygon(3, c4d.CPolygon(6, 7, 1, 0))
            newObj.SetPolygon(4, c4d.CPolygon(0, 1, 3, 2))
            newObj.SetPolygon(5, c4d.CPolygon(4, 5, 7, 6))
    
            # If the generator already have a PhongTag, jsut clone it otherwise define it a 92° phong shading
            phongTag = node.GetTag(c4d.Tphong)
            if phongTag is not None:
                newTag = phongTag.GetClone(c4d.COPYFLAGS_NONE)
                newObj.InsertTag(newTag)  # Add the cloned tag to the new Polygon Object present only in the cache
            else:
                newObj.SetPhong(True, True, c4d.utils.DegToRad(92.0))
    
            newObj.Message(c4d.MSG_UPDATE)
            return newObj
    
        def Message(self, node, type, data):
            # On the creation of the Object, via the Menu create a Phong tag on the generator
            # c4d.BaseList2D(1234569) will not trigger it
            if type == c4d.MSG_MENUPREPARE:
                # Create a phong tag and set the default value to 92° (so we see the phong shading)
                node.SetPhong(True, True, c4d.utils.DegToRad(92.0))
            return True
    
    
    if __name__ == "__main__":
        c4d.plugins.RegisterObjectPlugin(id=PLUGIN_ID,
                                         str="phongdemoobject",
                                         g=PhongDemoObject,
                                         description="phongdemoobject",
                                         icon=None,
                                         info=c4d.OBJECT_GENERATOR )
    

    Regarding your issue about AttributeError: 'function' object has no attribute 'im_func': As written in the doc of NodeData.Message you are supposed to return a bool. (I agree the code sample is a bit weird because it doesn't show the return statement, I will fix it)

    I've opened a BugReport since the error could be more explicit I will look at it but I guess this is a leftover of Python2.7 since im_func is removed in Python3.

    If you have any questions, please let me know,
    Cheers,
    Maxime.



  • It's working! 😄 Thank you @zipit , @r_gigante , & @m_adam ! Have an excellent weekend.

    A note for future readers
    One issue with my demo code is I wasn't passing the op from GetVirtualObjects to the CreateMyObject method. @m_adam created a new object (newObj) and only calls GetTag on the op (variable name: node).

    phongTag = node.GetTag(c4d.Tphong)
    

Log in to reply