Group Details Private

Global Moderators

Forum wide moderators

  • RE: [FWD] How to retrieve the index of the closest polygon in a mesh to a point?

    Hi Charbel,

    the problem with your example code is that you do use a VoxelizationInterface which is not well suited for this task. The type DistanceQueryInterface(Documentation) is much better suited for the task. You should also be more cautious with statements like the following, taken from your code,

    voxelRef = maxon::PolyVoxelization().Create() iferr_ignore("Could not create VoxelizationRef");
    

    as stepping over a failure of initialization will lead to crashes. At the end of this posting you will find an example for how to use DistanceQueryInterface as well as some light comments on error handling.

    I hope this helps and cheers,
    Ferdinand

    /* This function expects a point object (A) to be the first object in the scene, followed by a 
       polygon object (B) as the second object. It will then return the index for closest polygon
       in B for each vertex in A.
    */
    static maxon::Result<void> Pc13296(BaseDocument* doc)
    {
    	iferr_scope;
    
    	// Get the first and second node in the scene. The first one is expected
    	// to be a point node, serving as a point source, and the second one is
    	// expected to be a polygon node, serving as a polygon source.
    	BaseObject* node = doc->GetFirstObject();
    	if (node == nullptr || !node->IsInstanceOf(Opoint))
    	{
    		ApplicationOutput("Please provide a point object as the first node in the scene."_s);
    		return maxon::OK;
    	}
    	PointObject* pointNode = static_cast<PointObject*>(node);
    
    	node = node->GetNext();
    	if (node == nullptr || !node->IsInstanceOf(Opolygon))
    	{
    		ApplicationOutput("Please provide a polygon object as the second node in the scene."_s);
    		return maxon::OK;
    	}
    	PolygonObject* polygonNode = static_cast<PolygonObject*> (node);
    
    	// Get access to the point and polygon data and the global transforms of both nodes.
    	const Vector* points = pointNode->GetPointR();
    	const CPolygon* polygons = polygonNode->GetPolygonR();
    	const int pointCount = pointNode->GetPointCount();
    	const int polygonCount = polygonNode->GetPolygonCount();
    	Matrix mgPointNode = pointNode->GetMg();
    	// We invert it right away, as we only will need it in this form.
    	Matrix iMgPolygonNode = ~polygonNode->GetMg();
    
    	// When an error is being raised by Cinema we should be cautious with using iferr_ingore, as
    	// then the code will keep running in the current scope. In some cases like manipulating an 
    	// array like structure this can be useful. But when an initialization of an entity fails, we
    	// almost never want to keep going, as trying to use that not initialized object then will be
    	// a sure fire way to crash Cinema 4D. Instead we can either use iferr_return to simply leave 
    	// the current scope, use iferr_throw to throw a specific error or handle it manually with
    	// iferr(). Below you will find a two examples.
    
    	// Create a reference to the distance query interface.
    	maxon::DistanceQueryRef distanceQuery = maxon::DistanceCalculator().Create() iferr_return;
    
    	// Pass true for the second argument to voxelize the input to speed up larger queries. Just as
    	// with any optimization, for smaller data sets this voxelization setup might eat up all the
    	// performance benefits gained latter on.
    	iferr(distanceQuery.Init(polygonNode, false)) {
    		ApplicationOutput("Failed to initialize DistanceQueryInterfacae for @", polygonNode->GetName());
    		return maxon::OK;
    	}
    
    	// Go over all vertices and query them for the closest polygon (id) in the other mesh.
    	float distance = 0;
    	Vector p;
    	for (int poindId = 0; poindId < pointCount; poindId++)
    	{
    		// The point in the point mesh converted first to global coordinates and then to local
    		// coordinates in the polygon node. We have to do that because the points in the point
    		// source live in their own coordinate system as well the ones in the polygon source.
    		p = mgPointNode * iMgPolygonNode * points[poindId];
    		maxon::PrimitiveInformation info;
    		distance = distanceQuery.GetClosestMeshPrimitive(p, info);
    		ApplicationOutput("Closest polygon id for point id @: @", poindId, info.GetRealPolyIndex());
    	}
    
    	return maxon::OK;
    }
    

    The output/setup:
    642545d3-3ffa-467e-9123-cf0a328b18f1-image.png

    posted in Cinema 4D Development
  • RE: Wrong editor preview of material plugins with TextureTag repetitions

    hi,

    This is a bug and will be fix in a futur release.
    Unfortunately, there's no workaround.

    Cheers,
    Manuel

    posted in Cinema 4D Development
  • [FWD] How to retrieve the index of the closest polygon in a mesh to a point?

    Dear community,

    we received a support request via mail which we thought might also be interesting to the community. This thread captures the question asked in the mail.

    The question is "How to retrieve the index of the closest polygon in a mesh to a point?". The question has been asked for the C++ environment, looking for the right optimization data structure provided by the C++ SDK.

    Cheers,
    Ferdinand

    posted in Cinema 4D Development
  • RE: Wrong editor preview of material plugins with TextureTag repetitions

    Hi,

    I did reproduced the issue, with the s22 we changed the viewport and many changes have been introduce.

    I have contacted the devs to have some feedback about that.

    Cheers,
    Manuel

    posted in Cinema 4D Development
  • RE: python plugin in debugging mode

    Hi @Yaroslav this is indeed possible however a bit clunky for the moment, but here a full step by step.

    1. Install pip with c4dpy -m ensurepip
    2. Install Python Visual Studio Debugger with c4dpy -m pip install ptvsd.
    3. Within Visual Studio in your launch.json just set up a basic attach setting, here is mine but for more information see
      Python debug configurations in Visual Studio Code.
    {
        "version": "0.2.0",
        "configurations": [
            {
                "name": "Cinema 4D: Remote Debugger Attacher",
                "type": "python",
                "request": "attach",
                "connect": {
                    "host": "127.0.0.1",
                    "port": 3000
                },
                "pathMappings": [
                    {
                        "localRoot": "${workspaceFolder}",
                        "remoteRoot": "."
                    }
                ]
            },
        ]
    }
    
    1. The *.pyp plugin should be of course in the same workspace as your launch.json.
    2. Edit the *.pyp plugin and write the next code in the first line of your pyp file.
    import ptvsd
    ptvsd.enable_attach(address = ('127.0.0.1', 3000))
    
    1. Start Cinema 4D, wait until its UI is loaded, meaning the pyp plugin is loaded, and the client (your script) is attachable from a debugger.
    2. Run the previously created debug configuration (called Cinema 4D: Remote Debugger Attacher).
    3. Put a breakpoint somewhere (like in the Execute of a CommandData) to trigger the BP.
    4. Enjoy :D

    Note this also works nicely for the script, however for script you should call ptvsd.wait_for_attach() after the ptvsd.enable_attach.

    Cheers,
    Maxime.

    posted in Cinema 4D Development
  • RE: Get the String of the Font Data?

    Hi @bentraje,

    thank you for reaching out to us and thank you @Cairyn for stepping in again.

    The normal behavior of FontData parameters is not to be initialized in Cinema 4D. For a newly allocated node, they then will return None in Python, indicating that they point to the default system font. In Python this default font has to be retrieved a bit awkwardly via c4d.bitmaps.GeClipMap, which has the static method GetDefaultFont(), returning a font container.

    A MoText node is the exception here, as it will then return an empty container. One must still default to the system default font then, but it is just an extra case to handle. Below is an example for that. I have also opened an issue for the inconsistency of MoText in this regard.

    Cheers,
    Ferdinand

    """Example for retrieving a node's PRIM_TEXT_FONT font container.
    
    For newly allocated nodes this will default to the default font internally,
    for nodes where the PRIM_TEXT_FONT parameter has been modified, by setting
    the font to bold for example, the font data will be there.
    
    MoText is somewhat a special case, as it does not align with the normal 
    behavior of FontData not being initialized.
    """
    
    import c4d
    
    def main():
        """
        """
        # Get the selected object.
        if not op:
            raise TypeError("Please select an object.")
    
        # Get the node's data container.
        nodeData = op.GetData()
        if nodeData is None:
            raise RuntimeError("")
    
        # Try to access the PRIM_TEXT_FONT parameter.
        fontData = nodeData[c4d.PRIM_TEXT_FONT]
        # When it is not populated, then fall back to the system font.
        if fontData is None or len(fontData.GetFont()) < 1:
            fontContainer = c4d.bitmaps.GeClipMap.GetDefaultFont(
                c4d.GE_FONT_DEFAULT_SYSTEM)
        # Otherwise get the font container.
        else:
            fontContainer = fontData.GetFont()
    
        # Go over the font container.
        for cid, value in fontContainer:
            print (f"cid: {cid}, value: {value}")
    
    if __name__=='__main__':
        main()
    
    posted in Cinema 4D Development
  • RE: Write a python code into a Python Effector

    Hi @Hugo-BATTISTELLA and @Cairyn,

    thank you for reaching out to us. And thank you @Cairyn for stepping in. We cannot add anything to what @Cairyn already said. We would like however to bring up the fact that topics should be single subject as defined in the Forum Guidelines, as otherwise content will be hard to find for other users. It is fine in this case, but in the future, please open multiple topics for questions that are not direct follow-up questions, but questions with a new scope.

    Thank you for your understanding,
    Ferdinand

    posted in Cinema 4D Development
  • RE: Dialog Plugin with Progress Bar or SetStatusBar

    Hi @HerrMay sorry for the late reply, just re-checking and I can indeed confirm it doesn't work on R18 MAC. However, this is fixed in R19 mac and it's working correctly on windows.

    Unfortunately, we don't support more than 2 versions, so R18 is out of scope, and since the issue comes from Cinema 4D side, I don't see a way for you to fix it.

    Sorry for the long wait for just confirm that it doesn't work...
    Cheers,
    Maxime.

    posted in Cinema 4D Development
  • RE: LINK EFFECTORS TO CLONERS

    Hi @Hugo-BATTISTELLA,

    thank you for reaching out to us and thank you @bentraje for providing help.

    The major problem with your code is that you have the line Cloner.InsertObject(EffectorList) in there. But there is no variable of the name Cloner within the scope of effectorsCreation. Which is why your code will raise a NameError on this line. But even if the variable Cloner from your function cloner_creation would be visible within effectorsCreation, it would still raise an AttributeError, because your Cloner is of type BaseObject which has no attribute (i.e., method) of the name InsertObject.

    I would also recommend not to insatiate an c4d.InExcludeData from scratch, because you will lose any existing links by doing so and then writing back that newly allocated InExcludeData. In this case here it does not really matter, since you also instantiate the MoGraph cloner, i.e., its effectors list will still be empty. Below you can find a commented section of your code and a proposal how I would write something like that (which is effectively also what @bentraje did).

    Cheers,
    Ferdinand

    The code:

    import c4d
    
    # This is a lightly commented version of your code, pointing out the major
    # problem with your version.
    """
    def effectorsCreation():
        Effector = c4d.BaseList2D(1025800)
        # This does not do anything; it just creates an InexCludeData instance.
        EffectorList = c4d.InExcludeData() # Initialize an InExclude data
    	
        # That does not work, Cloner is not visible in this scope, it would also
        # be an BaseObject, which does not have a InsertObject method.
        Cloner.InsertObject(EffectorList) # Add effector to effectors list
        doc.InsertObject(Effector)
    """
    
    def CreateClonerSetup():
        """
        """
        # Instantiate a MoGraph cloner and Python effector. One can also use
        # BaseList2D.__init__(), this version here is just more verbose.
        cloner = c4d.BaseObject(1018544)
        effector = c4d.BaseObject(1025800)
        
        # Get a copy of the InExcludeData Effectors parameter of the cloner.
        inExcludeData = cloner[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST]
        # Add the effector in an activated state (1)
        inExcludeData.InsertObject(effector, 1)
        # Important, we have to write the modified InExcludeData back, since we
        # only retrieved a copy of it.
        cloner[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST] = inExcludeData
        
        # Add both nodes to the currently active document.
        doc.InsertObject(effector)
        doc.InsertObject(cloner)
        
        # Update Cinema 4D.
        c4d.EventAdd()
    
    if __name__=='__main__':
        CreateClonerSetup()
    
    posted in Cinema 4D Development
  • RE: Dynamic elements in a CYCLE, CYCLE empty after loading document?

    Hi sorry for the long waiting.

    As demonstrated in the code snippet from Description - Iterate. I think in your GetDDescription, once the description is saved you should call SetParameter with the correct value.
    This value can only be known by yourself. So you have to store the cycle value within the Hyperfile by overriding Write. Then when Cinema 4D loads the object the Read is called, here you can save in the previous value saved for the cycle. And finally in the GetDDescription, since Read would have been already called, you can call SetParameter with the correct value.

    Hope this clarifies a bit, and good work on Terraform looks solid! :)
    Cheers,
    Maxime.

    posted in Cinema 4D Development