Navigation

    • Register
    • Login
    • Search
    1. Home
    2. s_bach
    S

    s_bach

    @s_bach

    218
    Reputation
    257
    Posts
    438
    Profile views
    0
    Followers
    0
    Following
    Joined Last Online

    • Profile
    • More
      • Following
      • Followers
      • Topics
      • Posts
      • Best
      • Groups
    s_bach Follow

    Best posts made by s_bach

    RE: Want to show text in dialog box similar to "Help" menu of Cinema 4D(only text)

    Hello,

    the "Help" does not use a GeUserArea.

    The "Help" is displayed using the HtmlViewerCustomGui GUI element. This GUI element encapsulates the local browser to display a HTML document.

    If you want to create the same user experience, you can simply use that GUI element to load a custom HTML document that presents your information.

    • HtmlViewerCustomGui (C++)
    • HtmlViewerCustomGui (Python)
    • Custom GUI Elements

    As always you can use the Q&A Functionality to mark a thread as a question.

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    RE: NewMem question

    Hello,

    Cinema 40 R20 introduces a new, elaborate error handling system. A function can return now a dedicated result object that stores additional information if the operation was successful or not.

    NewMem does return such a result value. You find information on how to handle errors in the documentation: Error Handling. See also Error System in the API Transition.

    That being said, in most cases there should be no reason to allocate memory with NewMem. It is recommended to use arrays like BaseArray instead. Such an array provides save access to and management of the allocated data.

    See

    • Memory Allocation
    • BaseArray Manual

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    RE: Miscellaneous questions about "BaseContainer","DescID" etc

    Hello,

    An object like BaseObject can store data. This data is typically stored in the object's BaseContainer. But there are cases where the data is not stored in the BaseContainer so in general it is saver to use GetParameter().

    A BaseContainer can store any kind of data. This includes other BaseContainers. The BaseContainer of the object stores a sub-BaseContainer that stores all the values of the user data. The ID of the User-Data-BaseContainer is c4d.ID_USERDATA. Using this ID you can obtain the BaseContainer:

    bc = op.GetData()
    userDataValues = bc[c4d.ID_USERDATA]
    
    # or
    
    userDataValues = op.GetParameter(c4d.ID_USERDATA, c4d.DESCFLAGS_GET_0)
    

    This BaseContainer stores the parameter values of user data parameters.

    A parameter is identified using a DescID. This DescID has nothing to do with UUID.

    A DescID is composed of one or several DescLevel components. In most cases the DescID has only one DescLevel. The DescLevel defines these properties:

    • id: id of the level. Typically the parameter ID.
    • dtype: type of the parameter.
    • creator: ID of the "creator" of the parameter. E.g. on a cube you have some parameters that were created by the "Cube" (Size, Segments, ...). But there are also other parameters that are shared by all BaseObject's (Position, Scale, ...). These shared parameters are inherited from a base class, in this case Obase (5155). The "creator" value is not always relevant so it might not always be set.

    A DescID can have multiple DescLevels since a parameter may be stored inside another parameter. As described above, user data values are stored in a BaseContainer that is stored in the default BaseContainer. The full DescID of a user-data parameter has to reflect that. The DescID of a user-data parameter must first identify the user-data container and then the actual parameter in that container. This is done using two DescLevels:

    id = c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA, c4d.DTYPE_SUBCONTAINER, 0), c4d.DescLevel(1))
    
    value = op.GetParameter(id, c4d.DESCFLAGS_GET_0)
    

    But the Python API also allows you to do the same with less code:

    value = op[c4d.ID_USERDATA, 1]
    

    Or you access the user-data BaseContainer:

    bc = op.GetData()
    userDataValues = bc[c4d.ID_USERDATA]
    value = userData[1]
    

    The DescIDs are not necessarily stored in a file. The number of parameters of an object may change depending on how the object is used. The list of all displayed parameters is stored in the parameter Description. This Description contains the IDs as well as information on how to display the parameters in the Attribute Manager. The information how the parameter should be displayed is also stored as a BaseContainer (but this is NOT the object's BaseContainer).

    The parameter description of user data parameters is stored in the object. You can access this data with GetUserDataContainer():

    for id, descriptionBC in op.GetUserDataContainer():
    	print("User Data ID: " + str(id))
    	print("User Data Value: " + str(op[id]))
    	print("User Data Name: " + descriptionBC[c4d.DESC_NAME])
    

    To make that clear: the VALUES of user data parameters are stored in the object's BaseContainer. The DESCRIPTION of the user data parameters is stored in the BaseContainers accessed with GetUserDataContainer().

    You find an overview over the most fundamental concepts and classes currently in the C++ documentation under Foundations, especially Classic API Base Classes.

    You can also find further information about these classes:

    • BaseContainer, BaseContainer Manual
    • DescID, DescID Manual
    • DynamicDescription Manual
    • Description Manual
    • Description Settings Manual

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    RE: Dynamically changing icons

    Hello,

    you can dynamically change the icon of ObjectData and TagData based plugins by implementing NodeData.Message() and reacting to the message MSG_GETCUSTOMICON. There you can define the BaseBitmap used as the icon. Something like this:

    if type == c4d.MSG_GETCUSTOMICON:
    
        # load a standard Cinema icon
        icon = c4d.bitmaps.InitResourceBitmap(c4d.RESOURCEIMAGE_OK)
        data["bmp"] = icon
        data["w"] = icon.GetBw()
        data["h"] = icon.GetBh()
        # set as filled
        data["filled"] = True
    
        return True
    

    You find related information in the C++ documentation (NodeData::Message() Manual).

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    RE: Universal "Undo" to Rule them All?

    Hello,

    as Cairyn pointed out, the undo system does not support nesting.

    So calling CallCommand() multiple times is the same as pressing multiple buttons in the GUI - it will create multiple undo steps. The only alternative is to not use CallCommand() but to do the specific operations (using the API) yourself.

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    Goodbye

    Hello,

    at the end of this week, I will leave Maxon for a new direction in my life.

    In the last five years, I tried to give you the best answers to your questions and to provide you with documentation and examples that would make your daily life easier.

    During my time here, I met remarkable developers and learned about their projects. I learned a lot by working with you here on the forum, via mail or in person. Especially, I want to thank Riccardo, Maxime and Manuel for being great colleagues and an amazing team. Be assured that this team will continue to work with great commitment to make sure you get the support you need.

    I wish you all the best.
    Sebastian

    posted in Maxon Announcements •
    RE: Modular plugin programming

    Hello,

    you might have to add the pyp file path to the system path using:

    sys.path.append(os.path.dirname(__file__))
    

    so

    import sys
    import os
    sys.path.append(os.path.dirname(__file__))
    
    
    import hello_function
    

    best wishes,
    Sebastian

    posted in General Talk •
    RE: styleguide warnings

    Hello,

    you can disable style checks in the projectdefinition.txt file of your project. See Stylecheck.

    Also just a reminder that only Visual Studio 2015 is officially supported to develop Cinema 4D R20 plugins.

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    RE: NewMem question

    Hello,

    the best solution is probably to create the BaseArray in the caller and hand over a reference to that BaseArray. Your function can then safely fill the BaseArray:

    maxon::Result<void> GetVertexNormals(PolygonObject * const pObj, maxon::BaseArray<Vector>& normals)
    

    If you encounter unexpected break points, please open a new thread to discuss this issue.

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    RE: getting 3D noise values

    Hello,

    as @eZioPan has shown you can use the C4DNoise class to sample a noise. Additionally, you find further noises in the c4d.utils.noise namespace.

    A noise function typically just returns a scalar value. So if you want to create a color you have to combine different noises (with different seeds) or you sample the same noise with different offsets.

    Please also make sure to mark your post as a question using our Q&A system.

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •

    Latest posts made by s_bach

    RE: Example of ObjectData in Maxon API only?

    Hello,

    this is not possible. ObjectData is a class of the "classic" API. The Maxon API has no equivalent counterpart.

    You get an overview over plugin types here: Plugin Types.

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    Goodbye

    Hello,

    at the end of this week, I will leave Maxon for a new direction in my life.

    In the last five years, I tried to give you the best answers to your questions and to provide you with documentation and examples that would make your daily life easier.

    During my time here, I met remarkable developers and learned about their projects. I learned a lot by working with you here on the forum, via mail or in person. Especially, I want to thank Riccardo, Maxime and Manuel for being great colleagues and an amazing team. Be assured that this team will continue to work with great commitment to make sure you get the support you need.

    I wish you all the best.
    Sebastian

    posted in Maxon Announcements •
    RE: A strange error

    Hello,

    please use tags and the Q&A system when posting questions.

    What version of Cinema 4D and the SDK are you using?

    You can enable utility functions like NewObjClear() by enabling this setting in your projectdefinition.txt file

    C4D=true
    

    and re-creating the project files. See Project Tool.

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    RE: Copy shader from one material to another

    InsertShader() and the parameter do NOT need the shader type. They desire an actual shader instance. You can create a clone of the original shader with GetClone().

    See also BaseShader Manual.

    posted in Cinema 4D SDK •
    RE: Best Practices for Reporting Errors to Users?

    Hello,

    there is no "official" way of handling or reporting errors in Python scripts. You can do whatever fits your needs or the needs of your user best. Why do you think the console is not useful to users?

    The only thing to consider are general best practices regarding software design. E.g. to only show a (modal) dialog when you know that a user is interacting with the software, not in some sub-function.

    Another philosophical discussion is, what actually is an error? An error should be returned by a function that failed; but what does that mean? What should happen if an error is detected? (Here is some video about this topic I just recently watched: https://www.youtube.com/watch?v=GC4cp4U2f2E)

    For argument's sake, lets say an error is when something in your code, you are 100% sure should and must work, didn't work. So your program shuts down, if such such an "error" was detected.

    In your example, LoadDialog() is a function that - in my opinion - cannot fail. Either the user selects a folder or he presses "Cancel". Both are fine results. And if the user tells you to abort, you can simply exit your program.

    Similarly, when the user selects the wrong file type, that's not a program error. Compare that with a user entering a wrong password somewhere - that is not an error, but something to expect. Providing feedback to the user and handling errors are two different things.

    def handleError(error):
        print(error)
        c4d.gui.MessageDialog(error)
    
    def getImagePathFromUserInteraction():
        res = c4d.storage.LoadDialog(
            type=c4d.FILESELECTTYPE_IMAGES,
            title="Select an image in the folder you want to bulk rename",
            flags=c4d.FILESELECT_LOAD
        )
        
        if res == None:
            return False
        
        return res
    
        
    def main():
    
        # UI interaction
        res = getImagePathFromUserInteraction()
        
        if res == False:
            # nothing to do, lets go home
            print("script aborted by user")
            return
        
        # do actual work 
    
        try:
            doSomething(res)
            doSomethingElse(res)
            
        except ValueError as error:
            handleError(error)
            return
        
        # show result in the UI
        c4d.gui.MessageDialog(res)
    

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    RE: Changing the syntax from "SetString" to "Brackets" type

    @bentraje You find also some information in this discussion: Miscellaneous questions about "BaseContainer","DescID" etc

    posted in Cinema 4D SDK •
    RE: Make Button Invisible And Still Occupies Space?

    I don't know why you think you need two groups.

    You can simply have one group that you flush and re-fill on demand. In that re-fill, you can replace anything with that static text placeholder as shown above:

    self.LayoutFlushGroup(1000)
    
    flags = c4d.BFH_LEFT | c4d.BFV_TOP
    width = c4d.gui.SizePix(100)
    height = c4d.gui.SizePix(100)
    
    if self.mode == True:
        # add bitmap button
        settings = c4d.BaseContainer()
        settings.SetBool(c4d.BITMAPBUTTON_BUTTON, False)
    
        bitmapButtonGUI = self.AddCustomGui(1001, c4d.CUSTOMGUI_BITMAPBUTTON, "", flags, width, height, settings)
    
        if bitmapButtonGUI is not None:
            icon = c4d.gui.GetIcon(c4d.Ocube)
            bmp = icon["bmp"]
            subBitmap = c4d.bitmaps.BaseBitmap()
            bmp.CopyPartTo(subBitmap, icon["x"], icon["y"], icon["w"], icon["h"] )
            bitmapButtonGUI.SetImage(subBitmap, True, False)
    
    else:
        # add placeholder with the size of the bitmap button
        self.AddStaticText(999, flags, width, height)
    
    self.LayoutChanged(1000)
    

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    RE: Make Button Invisible And Still Occupies Space?

    Hello,

    what exactly do you mean with "button"?

    A standard button (AddButton()), a bitmap button or a custom GeUserArea based gadget?

    @lasselauch 's idea to use Enable() is, I guess, the easiest way to achieve what you want. But what is the overall behaviour of your dialog? Is the user able to resize it; can the width or height or your GUI elements changes based on resizing the dialog? Do your UI elements have a minimum or fixed size?

    If the element has fixed size, you can insert an empty static text element with the desired dimensions in place of the original element when rebuilding your layout.

    self.AddStaticText(999,c4d.BFH_LEFT | c4d.BFV_TOP, c4d.gui.SizePix(100), c4d.gui.SizePix(100))
    

    best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    RE: UA is update too often

    Hello,

    as always, efficiency is the result of good software design. One way of speeding things up is to use caches.

    DrawMsg() is called by Cinema whenever Cinema things something might have changed. I don't think you can do anything about that.

    You have to make sure that within DrawMsg() you only draw and do nothing else. Why are you loading the bitmap in the context of DrawMsg()? Why are you scaling the images in the context of DrawMsg()? Why not earlier?

    I guess at some point, your program knowns what images to display. At that point you could load all these (scaled) images into a cache. Then in DrawMsg(), you can simply access the data in that cache.

    Compare e.g. BaseShader.InitRender() which is used to do all the heavy lifting, so that BaseShader.Sample() can be fast.

    Depending on the reason for the redraw, you could optimize further. E.g. you could use a GeClipMap to draw whatever you want into a BaseBitmap and simply draw that BaseBitmap in the context of DrawMsg().

    Speed is the result of software design. Caches can help, but of course they increase code complexity and memory footprint.

    Best wishes,
    Sebastian

    posted in Cinema 4D SDK •
    RE: Plugin not found after switching layout

    The ID used with GeDialog::Open() and RestoreLayout() is typically the plugin ID of the CommandData plugin.

    See e.g. activeobject.cpp

    posted in Cinema 4D SDK •