Navigation

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

    Posts made by mikegold10

    RE: A few items that should get fixed on the Python side

    @ferdinand said in A few items that should get fixed on the Python side:

    Hey @mikegold10,

    ..., the word "of" does not in any way indicate ownership, [...]

    It does, of is a possessive preposition, in is a relative preposition, specifically one that denotes a place. See here for an overview of the subject. I would not even challenge the fact that for you and your peers of might sound more natural (or maybe even all native speakers as you claim).

    But language in general, and especially things like prepositions, pronouns, and flexions, is subject to language change which is often highly regional, so what people consider correct can vary regionally. As indicated in my first answer, prepositions are quite interchangeable, and if the text would have said of instead of in, I would not waste any time on changing that. But in the same notion, I cannot change the preposition on what you subjectively consider better. The grammatical situation from a Standard English point of view seems obvious here and personal preference cannot be grounds for a change. I am more than happy to change the preposition when you can demonstrate with a respectable English style guide like Oxford English that it would be more common to use here of. But that seems unlikely to me.

    Cheers,
    Ferdinand

    As additional supportive evidence for my proposed change, I am providing a link to a respected source for the definitions of American English words, including many sample phrases that provide context for the often huge set of alternative definitions that are provided for commonly used words like "of" (and "in"):

    Meriam-Webster Dictionary: of - Definition / preposition

    Specifically, under the definitions of the word "of," when used as a preposition (i.e., Section 1 of 3 on the page, as linked above), please examine the following alternative definitions and examples of proper English usage that I believe are of relevance to my proposed change to the original Python comment:

      1. a, c, and e
      1. a and b
      1. b

    Here is a copy of the original comment to provide context:

    The index of the first and last selected elements in the given segment.

    ..., and the revised version that replaces the preposition in with of::

    The index of the first and last selected elements of the given segment.

    Of course the decision is yours to make. I am simply trying to "open your mind" to, as well as provide objective evidence for, an alternative phrasing which I perceive to be a better fit for the technical subject matter that is being documented, within the framework of modern American English usage patterns. I have to concede on the fact that this is ultimately subjective and may (or may not - not sure) be specific to the en_US locale.

    Michael

    posted in Cinema 4D SDK •
    RE: A few items that should get fixed on the Python side

    @ferdinand said in A few items that should get fixed on the Python side:

    Hey @mikegold10,

    thank you for reaching out to us and for taking the time to point these out; much appreciated. As a FYI, the line number and the __file__ attribute a function/method/class carries are pretty much useless, as internally this data is stored in different files. Just name the class or function, you do not have to go through the effort of getting line numbers and file names, as I will not use them anyways.

    • Matrix.__mul__ and the Matrix Manual: I have fixed that, it will be published with an upcoming release.
    • BaseSelect.GetRange: Hm, I would say this is very much a question of taste, both the preposition in and of are commonly used in such context as this sentence. But the preposition in denotes a location while of denotes ownership. Since the function returns the location of the first and last selected index, I would say in is a more fitting and the grammatically correct choice here. I therefore left this as is.

    Regarding argument names shadowing built-in types and functions: We understand that this can be a bit annoying, but we strive for parity between C++ and Python and will therefore not change the names of arguments in Python. When you invoke functions/methods, this is not an issue as nothing will be shadowed. This is only a problem when you overwrite methods, and then you can rename them locally, as Cinema 4D will not call these methods with keywords, but always positionally.

    import c4d
    
    class MyDialog (c4d.gui.GeDialog):
    
        def Command(self, id: int, msg: c4d.BaseContainer) -> bool:
            """The argument id shadows here the built-in function id(), not so nice.
            """
            return super().Command(id, msg)
    
        def Command(self, cid: int, msg: c4d.BaseContainer) -> bool:
            """You can rename arguments as you please when overwriting methods.
            """
            return super().Command(cid, msg)
    

    For the same reason we will also not rename arguments for readability reasons. And there are also a lot of other PEP8 violations in the c4d and maxon modules due to our C++-first policy. This has been discussed multiple times internally, and we will not adopt PEP8 conformance for our APIs.

    Cheers,
    Ferdinand

    I think you missed one of my corrections for BaseSelect.GetRange() to the second line of the Python comment that is neither grammatically correct nor informative:

    34544: | If the segment has a length of one, the same ->if<- (😕) will be set for the first and last entry in the tuple.

    ..., and my recommended correction that I feel is far more clear:

    34544: | For segments with a length of one, the indices of both elements will be set to the same value.

    With regard to the correction to the previous comment (i.e., the use of "in" vs "of" to start the subsequent preposition):
    34543: | The index of the first and last selected elements in of the given segment.

    ..., the word "of" does not in any way indicate ownership, it is used simply to refer to the specific segment being discussed. For an issue such as this, where the wording is not strictly incorrect in terms of grammar, but the choice of words (IMHO) violates common English language usage patterns for expressing what is stated, I would very strongly urge you to run the two word possibilities by some native (American) English speakers at Maxon. It would be a very good idea to hear the opinions of others, before coming to a firm decision on this matter.

    Believe me, I would not bother pursuing this point any further, if I did not feel that the present wording is very non-conventional, if not outright poor. I will go so far as to say that, in its present form, it sounds like either a non-native English speaker or a mechanical translator translating from a different language (e.g., German) came up with the present wording.

    Michael

    posted in Cinema 4D SDK •
    RE: A few items that should get fixed on the Python side

    Updated post with an additional item. I couldn't update the above post, because of the 3600 s timeout for editing.

    C:\Program Files\Maxon Cinema 4D 2023\resource\modules\python\libs\python39\c4d_init_.py
    Note: Code lines in the file are prefixed with their line numbers, for convenience.

    Instead of: (Note that the second if word in the statement on line 34544 is completely out of place)

    34543:            | The index of the first and last selected elements in the given segment.
    34544:            | If the segment has a length of one, the same if will be set for the first and last entry in the tuple.
    34545:            | **None** if the function failed.
    

    ..., much clearer would be:
    34543: | The index of the first and last selected elements in of the given segment.
    34544: | For segments with a length of one, the indices of both elements will be set to the same value.
    ..., with line 34544 completely rewritten as shown in the above text (or something similar).

    If I find any more I will either update this message or post a new reply.

    posted in Cinema 4D SDK •
    A few items that should get fixed on the Python side

    A few issues I found in:

    C:\Program Files\Maxon Cinema 4D 2023\resource\modules\python\libs\python39\c4d_init_.py
    Line numbers are on the left

    36819:            >>> # Matrix multiplication is not communicative, i.e., executing the transforms in a 
    36820:            >>> # different order will yield another matrix product.
    36821:            >>> Z * Y * X
    36822:            >>> Matrix(v1: (0.5, -0.5, -0.707); v2: (0.146, 0.854, -0.5); v3: (0.854, 0.146, 0.5); off: (0, 0, 0))
    

    36819: >>> # Matrix multiplication is not communicative commutative , i.e., executing the transforms in a
    36820: >>> # different order wil yield another may result in a completely different matrix product.
    ...
    36822: >>> Matrix(v1: (0.5, -0.5, -0.707); v2: (0.146, 0.854, -0.5); v3: (0.854, 0.146, 0.5); off: (0, 0, 0))
    -------^ Remove erroneous >>> prefix from this line

    Also, use of id for a parameter name in this code (and perhaps other places) is highly discouraged, because such use shadows the built-in function id().
    E.g.:

    >>> help(id)
    Help on built-in function id in module builtins:
    
    id(obj, /)
        Return the identity of an object.
    
        This is guaranteed to be unique among simultaneously existing objects.
        (CPython uses the object's memory address.)
    

    A simple suffix in front of id would be a good way to fix the issue and make the code more informative. E.g.:

    ob_id: int
    sub_cont_id: int
    # Etc.
    

    I realize that id is used all over the code and this may not be a simple change, so just a suggested possibility for the next major set of refactoring changes.

    Finally the use of l as a variable name is highly discouraged (see: PEP 8: E741), because it can be easily mistaken for the digit 1, as you can clearly even see within the context of this forum's formatting of the two code excerpts for l and 1 ! I see that the variable name v is used for floating point values and the same name could be used for integral values, as well.

    For example, change:

    37717: def SetInt32(self, id: int, l: int) -> None:
    

    ..., to:

    37717: def SetInt32(self, id: int, v: int) -> None:
    # ---------------------------------^
    
    posted in Cinema 4D SDK •
    RE: A bug that should be fixed in your Python API code generation (Applies to all recent versions)

    Hi, Maxime, great!

    if I come across any additional issues of a similar nature, I'll update this post.

    posted in Cinema 4D SDK •
    A bug that should be fixed in your Python API code generation (Applies to all recent versions)

    The argument self should not appear in any of the following static method declarations that are part of the generated Python C4D API skeletal/placeholder code, since any good Python IDE will complain and/or flag correct user code that use these methods.

    In the future, you guys can do something like the following in ...resource\modules\python\libs:

    # You can also add the -n flag to the first grep in the pipe chain below, to add line number info after the file name
    grep -R -A2 -H "@staticmethod" * 2>&1 | grep -v "MAXON_" | grep -PB2 "def.*?self"
    

    I am not sure if the above grep comprehensively captures every one of these issues, but 99-100% of them should have been caught:

    python39/c4d/bitmaps/__init__.py:    @staticmethod
    python39/c4d/bitmaps/__init__.py-    def AllocWrapper(self, bmp):
    --
    python39/c4d/modules/mograph/__init__.py:    @staticmethod
    python39/c4d/modules/mograph/__init__.py-    def Create(self, flags, thread, doc, currentThreadIndex, threadCount, inputs, callers):
    --
    python39/c4d/utils/__init__.py:    @staticmethod
    python39/c4d/utils/__init__.py-    def PickObject(self, bd, doc, x, y, rad, flags):
    --
    python39/c4d/__init__.py:    @staticmethod
    python39/c4d/__init__.py-    def GetDistance(self, v1, v2):
    --
    python39/maxon/decorators.py:            @staticmethod
    python39/maxon/decorators.py-            @wraps(fn)
    python39/maxon/decorators.py-            def Auto(self, *args):
    --
    python39/maxon/decorators.py:            @staticmethod
    python39/maxon/decorators.py-            @wraps(fn)
    python39/maxon/decorators.py-            def ReferenceConvert(self, *args):
    --
    python39/maxon/decorators.py:            @staticmethod
    python39/maxon/decorators.py-            @wraps(fn)
    python39/maxon/decorators.py-            def Wrap(self, *args):
    --
    python39/maxon/decorators.py:            @staticmethod
    python39/maxon/decorators.py-            @wraps(fn)
    python39/maxon/decorators.py-            def NativeDataOrBuiltin(self, *args):
    --
    python39/maxon/decorators.py:            @staticmethod
    python39/maxon/decorators.py-            @wraps(fn)
    python39/maxon/decorators.py-            def _MaxonConvertOrFail(self, *args):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def Free(self):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def GetLocal(self):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def AllocEmpty(self):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def AllocFromUuid(self, uuid):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def AllocFromString(self, uuidStr):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def DescribeIO(self):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def CreateUuidString(self):
    --
    python39.win64.framework/lib/pathlib.py:        @staticmethod
    python39.win64.framework/lib/pathlib.py-        def link_to(self, target):
    

    Also, for all of the following, the @classmethod attribute should be changed to @staticmethod and the self argument should be removed, just like for the above cases:

    python39.win64.framework/lib/importlib/_bootstrap.py-    @classmethod
    python39.win64.framework/lib/importlib/_bootstrap.py:    def create_module(self, spec):
    --
    python39.win64.framework/lib/importlib/_bootstrap.py-    @classmethod
    python39.win64.framework/lib/importlib/_bootstrap.py:    def exec_module(self, module):
    --
    python39.win64.framework/lib/_collections_abc.py-    @classmethod
    python39.win64.framework/lib/_collections_abc.py:    def _from_iterable(self, it):
    --
    python39.win64.framework/lib/_collections_abc.py-    @classmethod
    python39.win64.framework/lib/_collections_abc.py:    def _from_iterable(self, it):
    
    posted in Cinema 4D SDK •
    Keyboard button down issue in S24

    It seems like S24 is too sensitive when it comes to deciding whether a user intended to hold a key down for a period of time so as to temporarily toggle a state and revert back once the key is released or if the intent was to permanently change to that state.

    For example, the lowercase L key can be used to:

    • Toggle the Enable Axis state on/off when the user momentarily presses the L key
    • Temporarily enter (or exit) the Enable Axis state when the user holds down the L key for a "longer" period of time and then revert back to the original state when the user releases the key

    Unfortunately, the actual length of time used for the decision by C4D as to the user's intent seems to be too short. I don't know where exactly the threshold lies, but it seems to be in the 100 ms ballpark. There should be a way to change the number of milliseconds that a key must be held before C4D assumes that the user's intent was to toggle (and not permanently enter a state/mode). I have not been able to find this setting if it exists it in the preferences. Or, is this value "hard coded" into the app and cannot be modified by the user?

    This issue effects both mice and keyboards that allow for macros, since sometimes they can send the key down even followed by a key up event with some delay in between that cannot be modified via configuration and happens to be long enough to confuse C4D into thinking that the user's intent was to temporarily toggle. I should add that such interpretation can occur intermittently due to key down/key up delay jitter making things even more confusing.

    On the other hand, even using a decent wired keyboard, if I press a key reasonably briefly, but not really fast, I sometimes get a temporary toggle instead of a permanent mode switch. This is true for those commands/modes that are toggleable. One almost has to put in a mental effort when pressing a key on the keyboard so as not to wind up holding it down for what C4D considers too long a time (where "too long a time" could be as little as somewhere in the 100-150 ms ballpark...). Often when I press a key, without concentrating on pressing it really quickly, C4D thinks my intent was to hold the key down so as to temporarily toggle, and reverts the setting back once I release the key.

    There should be some way to control this delay and increase it in the software to make C4D wait longer before assuming that the user intended to temporarily toggle a command (say 250-500 ms would be a reasonable delay as compared to the unnecessarily brief delay it is presently set to by default (50-100 ms??).

    I should note that this issue was present in R23 as well, but the delay was longer and could be lived with (although some users of Bluetooth keyboards and other forms of wireless keyboards as well as macro capable mice experienced the issue). As of S24, the delay has gotten so short, that it even affects users with common run-of-the-mill wired keyboards like myself, unless they can press/release keyboard keys really briefly (as an analogy for piano players out there, think of playing staccato notes vs tenuto notes).

    Test setup:
    OS: Windows 10
    Hardware/PC: Very fast modern desktop
    Keyboards attempted: Velocifire TKL02/wired, an old Dell wired keyboard, an old Logitech wired keyboard

    posted in Maxon Announcements •
    RE: S22: Question about going from a custom shader's ShaderData::Output() function back to the texture tag of an object

    Hi Maxime,

    Thanks for confirming that I was not missing something in the docs and this is just not possible.

    posted in Cineware SDK •
    RE: S22: Question about going from a custom shader's ShaderData::Output() function back to the texture tag of an object

    @zipit I am hopeful that @r_gigante will take a look at my reply to his post and address the question in Step 2 of said post:

    How do you "Hand color information to Cinema 4D inside Draw() (or where else??), so that it can use it to correctly draw the object in a Viewport" ?

    posted in Cineware SDK •
    RE: S22: Question about going from a custom shader's ShaderData::Output() function back to the texture tag of an object

    @zipit I tried this route and it doesn't work, at least not in the way I expected, because for this to work, it is critical that the ordering between Draw() and Output() functions across all object nodes and their respective tags in the node hierarchy, has to be in a certain well-defined and compatible (with this idea) way. Otherwise, the color you cache in Draw() based on the TextureTag received is not necessarily the correct color when Output() gets called (and if Output() gets called, because it almost never called unless you render, but Draw() gets called a lot).

    The reason things don't work as expected using this method of caching color info in Draw(), to be used later in Output (if object info is not available), is because either the object changed or the "current" texture tag is different by the time Output() is called, but often Draw() has not yet been called to update the cached color info. Also, aside from rendering, as I mentioned previously, calls to output are far and few in between. For example, if you use the mouse to move or rotate objects in a view, only Draw() gets called (for each texture tag/object), with no subsequent call to Output() as far as I can tell.

    posted in Cineware SDK •
    RE: S22: Question about going from a custom shader's ShaderData::Output() function back to the texture tag of an object

    @r_gigante

    I understand what you are saying in your bullet point 1:

    • during a rendering process the public VolumeData member of the ChannelData (not of the BaseShader as shown in your code) is valid, you can retrieve the linked BaseTag and, from that, you can access you userdata attached to the TextureTag to find the color of interest

    I made a typo when I used pShaderData several times, both in the code which I wrote from memory and the original post. Obviously, there is no *VolumeData vd member variable in the BaseShader class, so where I originally typed pShaderData, I was referring to pChannelData - my apologies.

    I have edited the original post and corrected this. I just want to add that I have no issue during the rendering process, since I do exactly what you described in your first bullet point (using pChannelData of course). It's "outside a rendering process" where I run into problems and so let's go to your bullet point 2:

    • outside a rendering process, you can use the ShaderData::Draw() to find the BaseTag and, from that, you can access you userdata attached to the TextureTag to find the color of interest.

    The issue is not one of finding the color of interest in Draw(), this part is easy! The issue is giving per object tag based color information back to Cinema 4D at "the right time" for this information to be used to draw in Viewports that show the object (and by the way where does this happen??).

    During renders, within the Output() function, it is clear that you return the color vector from your shader and this is how you tell Cinema 4D what color your shader returns.

    But, during Viewport drawing, it is not clear how to give this color information from a custom shader (after getting color information based on the BaseTag parameter to Draw()) back to Cinema 4D, so that it uses it as part of the Material of an object.

    Unlike the Output() function, Draw() returns a boolean value, not a ColorVector, so how do I give a ColorVector back to Cinema 4D so that it knows how to draw an object, one that has a TextureTag using a material with my shader, correctly in a Viewport?

    That's my dilemma, having solved the render question.

    Instead of seeing this in the viewport (with red indicating that my shader could not get object info for purposes of viewport display and the default error (exception) color is being used, because I do not know how to supply the per object color back to Cinema 4D [presumably in the call to Draw()]):

    Incorrect Viewport Display
    Incorrect Viewport Display

    I want to see this - objects get correctly colored, after you explain to me how to perform Step #2, below:

    Inside the call to Draw():

    Step 1) Fetch the color info from the object's texture tag's user data (how to do this is obvious)
    Step 2) Hand this information to Cinema 4D inside Draw() (or where else??), so that it can use it to correctly draw the object in a Viewport

    Correct Viewport Display
    Correct Viewport Display

    Thanks for your help and advice,

    Michael

    posted in Cineware SDK •
    S22: Question about going from a custom shader's ShaderData::Output() function back to the texture tag of an object

    I have a question about the correct path from the ShaderData::Output() function which gets overriden by a custom shader to the texture tag that links to the material which hosts the shader in one of its slots. Before stating the problem, let me explain the reason why I would like to do this:

    I am trying to reuse the same material across multiple objects. This material hosts a custom shader called Object Controlled Shader in its Color channel's Texture slot, as indicated by the orange arrow in the image below:

    Orange Arrow points to Custom Shader assigned to a material's Color channel's Texture slot

    This custom shader is meant to enable me to drive the color from a custom user data Color property that is associated with a texture tag attached to an object. I can use this user data Color property to set a per object texture tag color for the material and be able to modify it based on various criteria, without having to physically change the material, since it is also shared by other objects. I can do this while reusing all other aspects of the material. So, in effect, this would enable me to parameterize various aspect of a material via custom shaders on a per object per texture tag basis. This is somewhat similar to what the Mograph Color Shader currently does, except that the Color Shader allows for only one point of customization control, since it is hard wired to an Object's Basic Color (ID_BASEOBJECT_COLOR) property.

    It should be noted that the built in Mograph Color Shader works correctly to set the color both in Cinema 4d's Viewports and at render time, which is important as I will explain, below.

    We start with the following overriden function in my custom shader:

    Vector MyShader::Output(BaseShader* pShader, ChannelData* pChanelData)
    {
      ...
    }
    

    Now let's look at what we have available in this function:

    • this - A pointer to an instance of my custom shader class derived from ShaderData
    • pShader - A pointer to the BaseShader that hosts the ShaderData derived object
    • pChannelData - A pointer to an object of type ChannelData

    After much trial and error, I have been able to figure out that the following code (potentially) gets me the texture tag user data based color property I am trying to extract and use that to return a per object texture tag specific color from the Output() function:

    Vector MyShader::Output(BaseShader* pShader, ChannelData* pChanelData)
    {
        int MY_COLOR_ID=1; // Id inside a texture BaseTag's user data
        Vector COLOR_VECTOR_NOT_PRESENT=Vector(1.0, 0.0, 0.0); // Bright red color - to indicate errors
    
        Vector colorVector=COLOR_VECTOR_NOT_PRESENT;
    
        // If the ChannelData has a valid pointer to a VolumeData object (vd), and this object points to a valid
        //   TexData object (tex), which itself points to a valid BaseTag object (link)
        if (pChannelData && pChannelData->vd && pChannelData->vd->tex && pChannelData->vd->tex->link)
        {
            // Then, I can get the texture tag linked to the material hosting my custom shader
            BaseTag *pTag=pChannelData->vd->tex->link;
    
            // ..., and that also means that I can get any UserData associated with this texture tag (if present)
            const BaseContainer *pUserData=pTag->GetDataInstance()->GetContainerInstance(ID_USERDATA);
    
            // If user data is present
            if (pUserData)
            {
                // ..., and it contains a Vector data item at MY_COLOR_ID with a color (i.e., Vector) property type
                colorVector=pUserData->GetVector(MY_COLOR_ID, COLOR_VECTOR_NOT_PRESENT);    
            }
        }
    
        // Return the color vector from the user data to customize the color of the shader on a per texture tag
        // basis (or COLOR_VECTOR_NOT_PRESENT, bright red, if the texture tag does not have this user data item)
        return colorVector;
    }
    

    This seems to work, but it only works during renders. I cannot, for the life of me, figure out how to get the texture tag of an object that is assigned a material using my custom shader for purposes of display in Cinema 4d's Viewports, regardless of the Viewport's Display Shading setting. I have tried, seemingly, every possible path through the pointers available in the objects passed by pointer to the Output() function.

    I have read many reports online that code like this: pChannelData->vd->op->link->GetCacheParent() , assuming of course that all pointers along the way are valid, gets the object that hosts the texture tag linked to the material hosting the custom shader, but this does not work in S22 - the latest C4D release. I only get some internal "virtual" or perhaps "cached" object named "Object" back and every object path through the hierarchy that I have tried using that link pointer seems to get me no closer - it's always BaseObject objects named "Object" which are clearly not my object but some sort of cached copies. I am trying to get my actual object (e.g., an object named "Cube" in the Object Manager's node hierarchy), so that I can access its texture tag and get the per object per texture tag specific user data I am looking for in order to properly set the color that my shader returns from its Output() function when called for that object to display it in a Viewport.

    Is there any possible way to get from a shader to a texture tag (or a BaseObject that hosts that tag) during calls to Output() while an object is being drawn in a Viewport, so that in Output() I can use some user data from this texture tag to set the color vector that my custom shader returns, similar to how I do it in the code above which works for renders. Perhaps my understanding is flawed and the Output() function is not what Viewports use to texture objects, but then where is the correct place to do this in my ShaderData derived custom shader and what code do I need to do this?

    As shown in the shader usage example in this post, my custom shader would enable me to get each object's color in the Viewport to match the color as specified in the user data associated with its assigned Texture tag and make each object get colored differently, assuming my custom shader is used in a material's Color layer's Texture slot.

    Of course, the ultimate goal of this exercise is to be able to use the custom shader in any place of a material where a shader can be placed (i.e., any Texture property in any channel of the material) and be able to customize the material on a per object texture tag user data basis.

    Update with additional info:

    Over the weekend, it has become clear to me that my custom shader's override of the ShaderData::Draw() function, gets called when an object that is assigned a material with my shader in a Texture Tag, gets refreshed and/or drawn in a Cinema 4D Viewport. What is not clear at all is how do I communicate any object texure tag specific customizations from my shader during the call to Draw(), assuming that this is the right place to do that for purposes of Viewport display of an object's material.

    To be clear, unlike the Output() member function which returns a Color Vector containing the color calculated by my custom shader, based on the input parameters supplied to Output(), namely: BaseShader* sh, ChannelData* cd, the Draw() function only returns a Bool success or failure value.

    It is not clear to me, assuming of course that the Draw() function is the right place to make per object per texture tag changes/updates on behalf of an object and based on my shader's custom rules, how I go about communicating this information back to Cinema 4D, so that the object can be properly drawn in a Viewport.

    Perhaps this can be done by making changes to some member variables of the objects, pointers to which are passed to Draw(). That is: BaseShader * sh, BaseObject *op, BaseTag *tag, BaseDraw *bd, BaseDrawHelp *bh .

    If this is the right way to do it. It is not clear to me what changes to make to what members of those objects, or what member functions to call using the pointers to the various objects passed to Draw(), in order to send a Color vector back to Cinema 4D so that a particular object in the scene can be drawn in a custom way using a material that holds my shader in the Texture property of its Color layer.

    posted in Cineware SDK •
    RE: From Line Object to Spline

    Also, wanted to share the result of following the ideas in this thread on a parametric Cissoid object being deformed by a Twist modifier.

    The green stars and blue circles indicate the deformed spline points/knot positions, respectively, and the magenta stars and orange circles indicate the pre-deformation spline points/knot positions, respectively. (Types of) Intermediate Points were set to Natural and Number was set to 4, resulting in about four times as many knots (circles) as spline points (stars), as can be seen from the image. You can see some of the effects of the Natural type on the rightmost vertically oriented curve at the bottom of the deformed Cissoid coming to its lowest point in the image. As a result of being relatively straight, it has a more sparse arrangement of spline points (and knots) than the more curved vertical portion immediately to its left.

    The varying sizes of the circles/star point markers in the image are not due to perspective distortion, at least not in total, but rather due to them being scaled based on the distance of surrounding spline points and knots, respectively, so as to try to reduce collisions among the markers with respect to other markers of the same type (the null shape radii were multiplied by 40% of the smaller of the distances between a point and its surrounding points resulting in a tiny gap between markers of the same type, at least when viewed along the normal of any particular point):

    deformed-cissoid.png

    Michael

    posted in Cinema 4D SDK •
    RE: Should the call to c4d.EventAdd() come before or after the call to doc.EndUndo() ?

    @m_adam Hi Maxime, thanks for your detailed reply. I had a feeling that EventAdd() performed an asynchronous operation (i.e., placed an event on the event queue) and therefore it didn't matter, since control of the main thread would not be relinquished until both EndUndo() were called (regardless of sequence). However, as C4DS and you yourself pointed out, it makes more sense to call EndUndo() prior to EventAdd().

    This is not only true from a safety perspective, but the calls to StartUndo() and EndUndo() can be wrapped in a Python Context Manager, along exception handling and cleanup to be done automatically and correctly when a scope is exited. If one is going to undo whatever operation in case of an error, anyways (e.g., as part of said exception handling), it makes sense to call c4d.EventAdd() after we know that the entire action was successfully performed.

    I am going on the assumption that if my code performs some action inside of a StartUndo() / EndUndo() sequence, possibly consisting of multiple sub-actions and changes resulting in multiple AddUndo() calls, then sees mid-action that there is an and it cannot continue, and assuming the Start/End undo sequence is wrapped in a Python context manager class. Python will call EndUndo() on my behalf, as part of exiting the block governed by the context manager and then in the exception handler, I can perform an undo of the last action, undoing whatever sub-action did perform successfully (and added their AddUndo() pieces). Since this will hopefully leave everything in the same state that it was before the action was started, I am going to assume that there is no point in calling c4d.EventAdd() after the undo of the action is performed in my exception handler.

    To summarize, here is an example scenario:

    try:
        with MyUndoContextManager(doc) as undo_manager: # Calls StartUndo() as part of its Context Manager __enter__() method
            # undo_manager will make the calls to AddUndo() based on our actions
            DoSomethingAndCallAddUndoOnTheDoc(undo_manager);
            DoSomethingElseAndCallAddUndoOnTheDoc(undo_manager);
    
            DoOneLastThingAndCallAddUndoOnTheDoc(undo_manager); # Oh, oh, fails and throws exception
            # Note: EndUndo() will get called as part of the implicit call to MyUndoContextManager's __exit__() method
            #       which will automatically get called when we exit this block due to the exception being thrown
            c4d.EventAdd(); # This code will not get reached due to exception being thrown above
    except:
            # An error occurred, get back to the initial state prior to the action
            # Undo whichever action sub-steps were performed successfully and AddUndo()s got called for, if any
    
            # Since the undo_manager object no longer exists, call DoUndo() on the doc object, directly
            doc.DoUndo()
    
            # Since, we reverted to the initial state before the action, there is no need to call c4d.EventAdd(), right?
    
    posted in Cinema 4D SDK •
    From Line Object to Spline

    (edited by Manuel : I splited this thread from this one)

    @m_magalhaes said in Post Deformer Spline:

    SplineToLineIndex

    Hi Manuel,

    I am not the one who asked the original question, but your find of the SplineToLineIndex() method in SplineHelp is a good one!

    I must have look at SplineHelp's API over a dozen times and never noticed it was there. I thought one would have to traverse all of the LineObject's points and correlate them with points on its parent SplineObject based on position, in order to come up with the mapping from SplineObject point index to LineObject point index. So, this feature of SplineHelp is really valuable, since it offers this very mapping as part of the core Python API.

    I guess this also implies that there is always a 1:1 correspondence between a point of a SplineObject (which represents one or more B-Splines via formulas) and a point of its underlying LineObject which represents a bunch of line segments connected together. I wasn't sure if this was always guaranteed to be the case.

    Now, for a somewhat related question to what has already been discussed, while we are on the topic of SplineHelp, is there some top secret way to do the opposite of what the GetPointIndex() method of SplineHelp does, that is to get the offset (i.e., 0<=offset<=1.0) along a spline's LineObject that a particular LineObject` point idx is located at? This would also be really helpful.

    Let's look at an example:

    Say you create a Rectangle parametric spline object that has a width and a height of 100 cm, so basically a square. We'll set the Intermediate Points setting to None, so that the spline object and its line object have identical point counts (i.e., four, for the four points that make up the corners of the square). Now, let's say you perform a Make Editable operation to convert the (parametric) Rectangle into an editable closed spline object, consisting of one segment with four points. If you go into the Structure Manager, you can see that the points are indexed 0-3 (since, there are four of them) and you see their coordinates, as well.

    These points are located, with regard to offset, at 0%, 25%, 50%, and 75% of the length of the spline (or offsets of 0.000, 0.250, 0.500, and 0.750), respectively, since our original parametric spline shape was a square. Since this is a closed spline, GetPointIndex() will return the (sentinel or logical) idx of 4 (i.e., the count of the number of points in that segment of the LineObject) if asked for the nearest point to 100% of the length of the spline.

    Now, given for example the point at Index #1, so the second point of the spline's LineObject, which is obviously 25% of the distance along the spline as previously mentioned, what method could you use on the SplineObject, it's underlying LineObject, or 'SplineHelp' to get a 0.25 offset value from that LineObject point index of 1.

    The GetPointIndex() member function of SplineHelp takes a (Spline space) offset value and a segment idx, and returns the closest LineObject point, by index, to that spline space offset value.

    As described in the above example, how do you go in the opposite direction, from a LineObject point, by index, to an offset value along the LineObject in spline space?

    posted in Cinema 4D SDK •
    Should the call to c4d.EventAdd() come before or after the call to doc.EndUndo() ?

    I'd like to hear what the canonical way of doing the following sequence, is:

    # Start a chain of undo actions
    doc.StartUndo()
    
    # ... Whatever actions that need undo and their associated calls to doc.AddUndo
    
    # Call EventAdd() before ending the undo chain
    c4d.EventAdd()
    
    # ..., then end the undo chain
    doc.EndUndo()
    

    or:

    # Start a chain of undo actions  
    doc.StartUndo()   
    
    # ... Whatever actions that need undo and their associated calls to doc.AddUndo()
    
    # End the undo chain first
    doc.EndUndo()
    
    # ..., and then call c4d.EventAdd()
    c4d.EventAdd()
    

    In other words, should the call to c4d.EventAdd() come before or after the call to EndUndo() on the document object. I've seen code doing it both ways and am wondering if makes any difference or if there is some advantage to one of the two orderings of calls.

    posted in Cinema 4D SDK •
    There is a bug and some issues in the inner extrude tool res/.h files (S22 and R21)

    Let me be specific:

    Extrude Inner Tool
    In toolextrudeinner.res (resource\modules\model\description subdirectory of the main install location), in the MDATA_MAINGROUP group, the following line:
    REAL MDATA_EXTRUDEINNER_SUBDIVISION { MIN 1; MINSLIDER 1; MAXSLIDER 10; CUSTOMGUI REALSLIDER; STEP 1;}
    should read:
    REAL MDATA_EXTRUDEINNER_SUBDIVISION { MIN 0; MINSLIDER 0; MAXSLIDER 10; CUSTOMGUI REALSLIDER; STEP 1;}

    The correct MIN value for the Subdivision property of the Extrude Inner tool is 0 .

    Also, toolextrudeinner.h documents the property as follows:

    MDATA_EXTRUDEINNER_SUBDIVISION						= 2170, // LONG
    

    Yet, in the actual tool dialog box, it's type is specified as a REAL . One (or both) of these is/are clearly wrong, it doesn't make much sense for the subdivision value to be either a FLOAT or a LONG, it should be an INT!

    I should also add to this that if one was to start using the Extrude Inner tool and to drag the Subdivision property into the Python Console in C4D, they would see:

    >>> ExtrudeInner[c4d.MDATA_EXTRUDEINNER_SUBDIVISION]
    0

    ..., and if one was then to do the following to see what the actual underlying type is:

    >>> type(ExtrudeInner[c4d.MDATA_EXTRUDEINNER_SUBDIVISION])
    <type 'int'>

    They would see that the actual type is the very type (int) that I suggested should appear in both the .res and .h files, above!

    Note: The help documentation is also in error, regarding the Subdivision property of the Extrude Inner tool, listing its range as follows: Subdivision [1..+∞]
    It specifies this and then goes on to present a diagram showing the perfectly valid examples of using values of zero (which is out of the range just documented, but perfectly valid as far as the actual tool is concerned) and two, as the text under the figure indicates:

    003685.jpg

    Subdivision set to 0 and 2.

    See the following section from which the example above was taken: Extrude Inner/Tool

    The actual range in the help should be changed to: [0-+∞]

    Fortunately, when the tool itself is employed, the Subdivision value does correctly default to zero, in contradiction to the Dialog's definition and constraints in the .res file, so I am not sure what is happening behind the scenes in the C4D source code which seems to correct things (at least as far as the tool goes).

    However, the issue becomes apparent when tools try to use Extrude Inner functionality including its dialog groups (e.g., Py-Extrude Inner Modifier that is part of Py-Parametric Tools at Maxon Labs) and cannot change the Subdivision property to a value of zero, even though this is possible using the actual Inner Extrude tool that is part of C4D core functionality.

    Please take a look at this and consider the fixes/changes described above.

    posted in Cinema 4D SDK •
    RE: PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma

    @m_adam said in PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma:
    ...

    Hope it answers your questions,
    Cheers,
    Maxime

    Thank you Maxime, this is a lot of useful info. Let me think through all of this and reply here if I have any further issues.

    posted in Cinema 4D SDK •
    RE: Copying All Compatible Properties from One Object to Another?

    @m_magalhaes said in Copying All Compatible Properties from One Object to Another?:

    Hi,

    as you may know, if the object's type were the same you could use CopyTo
    But in your case, there's no builtin function do to it.

    I've checked how it's done for external compositing programme and there's nothing helpfull.

    Cheers,
    Manuel

    What about copying data from the BaseContainer of the source object to the destination object only if the ids in the source BaseContainer are relevant and/or are already present in the destination object's BaseContainer (i.e., copy only those key/value pairs that are applicable to the destination)?

    posted in Cinema 4D SDK •
    RE: PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma

    @zipit said in PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma:

    Hi,

    edges are globally indexed by polygon_index * 4 + local edge_index (see PolygonObject.GetEdgeS), where the local edge index lies in the interval [0, 3]. So the edge between the points c and d of the polygon with the id 10 would have the index 43. You will have to pass edges in this form to SetSelectEdges, the function will then turn your partial edge selection into a full selection by selecting the corresponding global edge indices of polygons that share the selected edges.

    You can also do the same by using the Neighbor class and do it manually by retrieving the polygons attached to an edge defined by two points.

    Cheers,
    zipit

    The whole point of SetSelectedEdges is not to do the 4*PolygonIdx+PolygonEdgeIdx math and have to supply multiple physical edges where a single logical edge from Neighbor will suffice).

    Please reread the below quoted portion of my post and especially the part I marked in bold:

    SetSelectedEdges(self, e, pSel, ltype)
    

    ..., method takes a pSel argument of type BaseSelect.

    The help for this method further tells us that:

    The edges are indexed uniquely by a Neighbor object, so each
    edge has a single index.

    This tells us that we should not use the 4*PolygonIdx+PolygonEdgeIdx formula to specify the edge indices for the BaseSelect, but instead refer to the logical edge indices as determined and governed by the Neighbor object after it does its calculations. The problem and crux of the question is that we don't know what those edges are, since they don't seem to be accessible to code that is outside of the Neighbor object. This information seems to be encapsulated within the Neighbor's guts and hidden from the users of the object, making it inadequate for its intended role in this case: The selection of a subset of the logical edge indices from the Neighbor as required by the pSel argument of SetSelectedEdges().

    My point with regard to which edge indexes to use is further driven home by the following sample code that appears in the help for BaseSelect.SelectAll() which shows the correct usage of the BaseSelect with the SetSelectedEdges() method and coincides completely with what I am asserting:

    def main():
        nbr = c4d.utils.Neighbor()
        nbr.Init(op) # Initialize neighbor with a polygon object
    
        edges = c4d.BaseSelect()
        
        # Select all edges in the range [0, nbr.GetEdgeCount()-1]   
        edges.SelectAll(nbr.GetEdgeCount()-1) 
    
        # ### Dev Comments
        # ### ============
        # ### The preceding line clearly demonstrates that Neighbor based
        # ### logical indices should be used with the BaseSelect since
        # ### nbr.GetEdgeCount() returns the number of Neighbor deduced
        # ### logical edges and not the total number of (potential)
        # ### physical edges (i.e., which would be 4*num_polys), at least the
        # ### the way I understand it.
    
        # Select all edges from our edges selection
        op.SetSelectedEdges(nbr, edges, c4d.EDGESELECTIONTYPE_SELECTION) 
        c4d.EventAdd() # Update Cinema 4D
    
    if __name__ == '__main__':
        main()
    

    Note: The Dev Comments portion in the above code was added by me to point out what can be deduced from the line of significance in the sample code from the help documentation.

    posted in Cinema 4D SDK •