Global Moderators

Forum wide moderators

Private

Posts

  • RE: How to find the closest primitive in a mesh

    First of all, you should open new topics for new questions, as one otherwise tends to derail old topics, which makes it hard to for others to follow things later. I forked your topic.

    And I also do not quite understand what your question is. GetClosestMeshPrimitive returns you the primitive type via primInfo.

    When you only want to get the closest point on a mesh, I would build a KD-Tree for its vertices. You then get the closest vertex and its index for your point p. Then you orthogonally project p onto the polygons attached to that vertex (via cinema:: Neighbor). Then you chose the closest point. We have this Python example for KDTree and I also used Neighbor there. Building a KD-tree or just brute forcing it depends a bit on how often you want to do it. For just a handful of points, brute forcing it is usually faster.

    I am sure you can also do this with DistanceQueryInterface and the uvw coordinate returned in the PrimitiveInformation, but all that extra work where you have to deal with all the primitive types seems like more work than it is worth. And when you want to do this in a generator (by the looks of your screens), using a manual KDTree approach will make it easier to parallelize it effectively and make it go zoom zoom instead of computing all that extra data.

    Cheers,
    Ferdinand

  • RE: How to find the closest primitive in a mesh

    Hey @dex,

    distancequery.h is part of the mesh_misc.framework which you alreeady include. Sounds a bit like you have a corrupted build system on your hands or are not using the frameworks you think you are using. Where does the CMake fragment come from you show us? I assume you are using the old projectdefintion.txt sourced workflow (in which case you should us show that file). Or are you a beta member and have access to the 2027 SDK (which is natively pure CMake), in which case this would be invalid CMake markup, as some stuff has changed.

    Have you checked in the IDE of your choice that the mesh_misc.framework shows up as a dependency of the project in which you want to #include "maxon/distancequery.h"? And just for the recoord, this is not a std lib, so you cannot use #include <> but must use #include "". And since this is part of the Maxon API, you also need to specify the maxon folder.

    Cheers,
    Ferdinand

  • RE: File import and doc merge -> InsertAfter or c4d.PREF_INTERFACE_I_BOTTOM

    Hey @mogh,

    let me clarify things first a bit.

    Reproduce:

    1. You have a scene A in an alien scene format such as FBX or OBJ.
    2. You have an existing native Cinema 4D scene B.
    3. You are trying to merge A into B.

    Result:
    The objects of A appear before the objects of B in the scene hierarchy of the merged scene. Even when you change the insertion order in Preferences > Interface.

    First of all, it is not very surprising that "insert at" does not work (and "paste at" does neither), because these are for individual objects within a scene, not for merging entire scenes. What you are effectively missing is a scene merge order option and this does not really have something to do with importers. This would be addressed best by a feature request in our Support Center.

    With a pinch of Python, you can solve this yourself as demonstrated by @Anlv (thanks!). Using BaseList2D.GetGUID as @Anlv did is okay, but the more native solution would be hash(obj), i.e., C4DAtom.__hash__. Scene elements are for quite a while now natively hashable, so you can for example do this:

    roots: set[int] = { hash(obj) for obj in doc.GetObjects() }
    new_roots: set[int] = roots.intersection({ hash(obj) for obj in new_doc.GetObjects() })
    

    And it will give you the set of all root level hashes (GeMarker IDs). I think also the transform copying @Anlv does at the end of the script should not be necessary, because these are root level objects, and therefore removing and re-adding them will not affect their transforms (as they have no parents to which their transform would be relative to). But I have not run @Anlv's full script myself, so it could be that I am overlooking something.

    Cheers,
    Ferdinand

  • RE: TreeView DropDown Menu

    Hey,

    yes it is. The trick is c4d.LV_CHECKBOX_HIDE, the relevant bits happen in GetDropDownMenu.

    Cheers,
    Ferdinand

    I removed the command, you can directly run this in the script manager:

    import c4d 
    
    
    PLUGIN_ID = 12345678
    PLUGIN_NAME = "ListView Dropdown Prototype"
    
    ID_TREE_VIEW = 1000
    ID_FILE_NAME = 1001
    ID_IMPORT_OPTION = 1002
    
    ID_IMPORT_OPTION_DEFAULT = 1100
    ID_IMPORT_OPTION_HIGH = 1101
    ID_IMPORT_OPTION_MEDIUM = 1102
    ID_IMPORT_OPTION_LOW = 1103
    
    dummyfile_list = ["hud.stp", "material_reference.wrl", "front_axle.stp", "body.wrl", "wheel_variant.stp"]
    
    
    class DummyFile:
        def __init__(self, file_name, import_option="Default"):
            self.file_name = file_name
            self.import_option = import_option
    
    
    class DummyFileListView(c4d.gui.TreeViewFunctions):
        def __init__(self, dialog):
            self.dialog = dialog
            self.files = [DummyFile(file_name) for file_name in dummyfile_list]
    
        def GetFirst(self, root, userdata):
            return self.files[0] if self.files else None
    
        def GetNext(self, root, userdata, dummy_file):
            current_index = self.files.index(dummy_file)
            next_index = current_index + 1
            return self.files[next_index] if next_index < len(self.files) else None
    
        def GetDown(self, root, userdata, dummy_file):
            return None
    
        def GetId(self, root, userdata, dummy_file):
            return id(dummy_file)
    
        def GetName(self, root, userdata, dummy_file):
            return dummy_file.file_name
    
        def IsSelected(self, root, userdata, dummy_file):
            return False
    
        def GetColumnWidth(self, root, userdata, dummy_file, column_id, area):
            if column_id == ID_FILE_NAME:
                return area.DrawGetTextWidth(dummy_file.file_name) + 24
            if column_id == ID_IMPORT_OPTION:
                return 120
            return 96
    
        def GetDropDownMenu(self, root, userdata, dummy_file, column_id, menu_info):
            if column_id != ID_IMPORT_OPTION:
                menu_info["state"] = c4d.LV_CHECKBOX_HIDE
                return
    
            if not dummy_file.file_name.lower().endswith(".stp"):
                menu_info["state"] = c4d.LV_CHECKBOX_HIDE
                return
    
            options = {
                ID_IMPORT_OPTION_DEFAULT: "Default",
                ID_IMPORT_OPTION_HIGH: "High",
                ID_IMPORT_OPTION_MEDIUM: "Medium",
                ID_IMPORT_OPTION_LOW: "Low",
            }
            for option_id, label in options.items():
                menu_info["menu"].SetString(option_id, label)
            menu_info["entry"] = next(
                option_id for option_id, label in options.items()
                if label == dummy_file.import_option
            )
            menu_info["state"] = c4d.LV_CHECKBOX_ENABLED
    
        def SetDropDownMenu(self, root, userdata, dummy_file, column_id, entry):
            if column_id != ID_IMPORT_OPTION:
                return
    
        #this is logical but does not do the trick ....
    
            if not dummy_file.file_name.lower().endswith(".stp"):
                return
    
            options = {
                ID_IMPORT_OPTION_DEFAULT: "Default",
                ID_IMPORT_OPTION_HIGH: "High",
                ID_IMPORT_OPTION_MEDIUM: "Medium",
                ID_IMPORT_OPTION_LOW: "Low",
            }
            dummy_file.import_option = options.get(entry, dummy_file.import_option)
            self.dialog.tree_view.Refresh()
    
    
    class ListViewDropdownDialog(c4d.gui.GeDialog):
        def __init__(self):
            self.tree_view = None
            self.file_list_view = DummyFileListView(self)
    
        def CreateLayout(self):
            self.SetTitle(PLUGIN_NAME)
    
            tree_view_settings = c4d.BaseContainer()
            tree_view_settings.SetBool(c4d.TREEVIEW_BORDER, c4d.BORDER_THIN_IN)
            tree_view_settings.SetBool(c4d.TREEVIEW_HAS_HEADER, True)
            tree_view_settings.SetBool(c4d.TREEVIEW_HIDE_LINES, True)
            tree_view_settings.SetBool(c4d.TREEVIEW_RESIZE_HEADER, True)
            tree_view_settings.SetBool(c4d.TREEVIEW_FIXED_LAYOUT, True)
            tree_view_settings.SetBool(c4d.TREEVIEW_ALTERNATE_BG, True)
            if c4d.GetC4DVersion() >= 24000:
                tree_view_settings.SetInt32(c4d.TREEVIEW_VERTICAL_SPACE, 4)
    
            self.tree_view = self.AddCustomGui(ID_TREE_VIEW, c4d.CUSTOMGUI_TREEVIEW, "", c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 500, 220, tree_view_settings, )
            if not self.tree_view:
                return False
    
            layout = c4d.BaseContainer()
            layout.SetLong(ID_FILE_NAME, c4d.LV_TREE)
            layout.SetLong(ID_IMPORT_OPTION, c4d.LV_DROPDOWN)
            self.tree_view.SetLayout(2, layout)
            self.tree_view.SetHeaderText(ID_FILE_NAME, "Dummy file")
            self.tree_view.SetHeaderText(ID_IMPORT_OPTION, "STP import option")
            self.tree_view.SetRoot(self.tree_view, self.file_list_view, None)
            return True
    
        def InitValues(self):
            self.tree_view.Refresh()
            return True
    
    if __name__ == "__main__":
        dialog = ListViewDropdownDialog()
        dialog.Open(c4d.DLG_TYPE_MODAL, 0, -1, -1, 500, 220)
    
  • RE: c4d.RSCAMERAOBJECT_COLOR_CONTROLS_CURVE_RGB

    Hey @lasselauch,

    no, that is unfortunately not possible. Unlike for the other big custom data type of Redshift RSFILE, the curve data type RSPFXCURVE does not implement Get/SetParameter to access internals of the data type, as you can for the file type with REDSHIFT_FILE_PATH.

    It also stands a bit to question how such lower desc level access should work here? Maybe as a lower level which can read/write the curve as SplineData? But I am not sure if this type would be faithfully convertable to SplineData`.

    I cannot fix this as Redshift is not part of the Cinema code base where the Python API lives. I gave one of the RS developers a bump so that they maybe can implement Get/SetParameter for that custom data type.

    Cheers,
    Ferdinand

  • RE: Change render Space (Color Profile) - how?

    You likely have to call UpdateOcioColorSpaces, just follow the example. There is btw no guarantee at all that DOCUMENT_COLOR_MANAGEMENT entails a render space of ACEScg. That is just the default, the user could have changed that.

    I am also not sure what all that RDATA_IMAGECOLORPROFILE code is meant to do. It depends a bit on what your plugin does and what self.IsForceLinear is meant to express. Only so much: An OCIO document does not necessarily entail a bitmap with a non-linear color profile, and OCIO also does not mean that the bitmap has to be 32bit. You are however missing UNDOTYPE_PRIVATE_DOCUMENTDATA undo management for setting the color management.

  • RE: Change render Space (Color Profile) - how?

    I do not quite understand your question. A node (e.g., an object) will always hold its color parameters in render space. The only exception is NodeData.Init and it is explained in the example.

  • RE: Change render Space (Color Profile) - how?

    Yes, OCIO having become the standard color management entailed some changes. In case your plugin is some kind of NodeData, you should also look at ocio_node_2025.

  • RE: Change render Space (Color Profile) - how?

    Hey @mogh,

    you cannot do that, because we do not provide access to GUIs as always, only to the data structures that stand behind them. When you see this dropdown in a document, it means it is in OCIO mode. Which in turns means all colors are in render space; which is the main idea of OCIO that all computations happen in render space. All color read and write operations happen in render space, i.e., ACEScg by default. You can use a color converter to convert a color from for example sRGB to render space. But there is no color space setting for a color, that is just UI fluff. See GetSetColorValuesInSceneElements.

    I would recommend to read:

    Cheers,
    Ferdinand

  • RE: Change render Space (Color Profile) - how?

    Hey @mogh,

    the answer can be found in the OCIO examples, specifically here. In the example I do the exact opposite from what you want to do, I convert a scene from legacy/basic mode to OCIO. Its inverse would look somewhat like what I show below. I hope this helps.

    Cheers,
    Ferdinand

    """Demonstrates how to convert a document from OCIO color management to basic color management.
    """
    import c4d
    
    
    op: c4d.BaseObject | None # The primary selected object in the scene, can be None.
    doc: c4d.documents.BaseDocument # The currently active document.
    
    def main() -> None:
        """Runs the example.
        """
        # The document is already in basic mode, so no conversion is needed.
        if doc[c4d.DOCUMENT_COLOR_MANAGEMENT] == c4d.DOCUMENT_COLOR_MANAGEMENT_BASIC:
            return c4d.gui.MessageDialog("The document is already in basic mode. No conversion is needed.")
    
        # Get the converter, and the active render space, which by default will be ACEScg.
        converter: c4d.modules.render.SceneColorConverter = c4d.modules.render.SceneColorConverter()
        renderSpace: str = doc.GetOcioRenderingColorSpaceNames()[0]
    
        # Initialize the converter with:
        #              doc, from-low   , from-high   , to
        converter.Init(doc, renderSpace, renderSpace, "scene-linear Rec.709-sRGB")
    
        # Convert everything in the document (we pass the document itself as the second argument). The
        # undo management is only needed so that our DOCUMENT_COLOR_MANAGEMENT below is reversible. The
        # ConvertObject call creates undo steps on its own with the default flag we pass. 
        doc.StartUndo()
        doc.AddUndo(c4d.UNDOTYPE_PRIVATE_DOCUMENTDATA, doc)
        if not converter.ConvertObject(doc, doc):
            print(f"Failed to color convert document '{doc}'.")
    
        # Finally, set the color management mode to basic/legacy. It is absolutely important that we do
        # things in this order, otherwise SceneColorConverter will not work correctly.
        doc[c4d.DOCUMENT_COLOR_MANAGEMENT] = c4d.DOCUMENT_COLOR_MANAGEMENT_BASIC
        doc.EndUndo()
        c4d.EventAdd()
        
    if __name__ == "__main__":
        main()