Solved Python Generator: Getting polygon data from new generators

Hi all,
I'm trying to see if I can access the polygon data from generators that I create within the Python Generator.
E.g. the default python generator creates a cube, is it possible to print out the polygon count of that cube?

GetCache() returns None as there doesn't seem to be any cache yet.
What is the designated way to do this? Is CurrentStateToObject an optimal way?

The default python generator returns a parametric object (c4d.Ocube). So you have to convert it to a editable poly in the generator itself. So you don't need a cso in your script but in the generator when using parametric objects.

Python Generator

import c4d
#Welcome to the world of Python


def main():
    c = c4d.BaseObject(c4d.Ocube)
    return c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_CURRENTSTATETOOBJECT,list    = [c],
                                                 bc      = c4d.BaseContainer(),
                                                 mode    = c4d.MODELINGCOMMANDMODE_ALL,
                                                 doc     = doc,
                                                 flags   = 0)[0]

script

import c4d

doc = c4d.documents.GetActiveDocument()
gen =doc.GetActiveObject()
print gen.GetCache()



Hi,

there are two different things here at play.

  1. Caches of objects are hierarchical (see docs). So when you want to evaluate the polygon count of your Python Generator Object (PGO) you haven to get the cache of the cube object returned by the cache of the PGO. Here is a little script that will spit out the cumulative polygon count of basically anything you throw at it.
import c4d

def get_cumlative_polygon_count(op):
    """ Returns the cumlative polygon count of the passed node and its 
     descendants. Will also inspcect caches.

    Args:
        op (c4d.BaseObject): The node to evaluate the polygon count for.

    Returns:
        int: The cumlative polygon count of the passed node and its 
         descendants.
    """
    if not isinstance(op, c4d.BaseObject):
        return 0

    # Op is not a polygon object, walk down the cache chain.
    if not isinstance(op, c4d.PolygonObject):
        cache = op.GetCache()
        res = get_cumlative_polygon_count(cache) if cache else 0
    # else get the polygon count
    else:
        res = op.GetPolygonCount()

    # Evaluate the children
    for child in op.GetChildren():
        res += get_cumlative_polygon_count(child)
    return res


def main():
    """ 
    """
    print get_cumlative_polygon_count(op)

if __name__=='__main__':
    main()
  1. Generator objects (e.g. a cube object) are recipes for generating objects, instantiating them does not produce any geometry. If you want to evaluate the output of a generator you have to add it to a document and execute its passes (or specifically the cache pass). As an example for your scenario:
import c4d
# Welcome to the world of Python


def get_cumlative_polygon_count(op):
    """ Returns the cumlative polygon count of the passed node and its 
     descendants. Will also inspcect caches.

    Args:
        op (c4d.BaseObject): The node to evaluate the polygon count for.

    Returns:
        int: The cumlative polygon count of the passed node and its 
         descendants.
    """
    if not isinstance(op, c4d.BaseObject):
        return 0

    # Op is not a polygon object, walk down the cache chain.
    if not isinstance(op, c4d.PolygonObject):
        cache = op.GetCache()
        res = get_cumlative_polygon_count(cache) if cache else 0
    # else get the polygon count
    else:
        res = op.GetPolygonCount()

    # Evaluate the children
    for child in op.GetChildren():
        res += get_cumlative_polygon_count(child)
    return res


def main():
    """
    """
    cube = c4d.BaseObject(c4d.Ocube)
    msg = "Cube polygon count before document execution:"
    print msg, get_cumlative_polygon_count(cube)
    temp_doc = c4d.documents.BaseDocument()
    temp_doc.InsertObject(cube)
    temp_doc.ExecutePasses(bt=None, animation=False,
                           expressions=False, caches=True, flags=0)
    msg = "Cube polygon count after document execution:"
    print msg, get_cumlative_polygon_count(cube)
    cube.Remove()
    return cube

Cheers
zipit

MAXON SDK Specialist
developers.maxon.net

Hi @orestiskon

So if you want to have a new generator within your own generator you need to evaluate this generator.

def main():
    # Create a Cube Generator in memory
    cube = c4d.BaseObject(c4d.Ocube)
    if cube is None:
        raise RuntimeError("Failed to create a cube")

    # Creates a temporary document that will be used to evaluate the cache of the object
    tempDoc = c4d.documents.BaseDocument()
    if tempDoc is None:
        raise RuntimeError("Failed to create a temporary doc")

    # Inserts the cube that exists only in memory into our tempo doc
    tempDoc.InsertObject(cube)

    # Computes cache from the document (aka compute the cache for our cube generator)
    tempDoc.ExecutePasses(bt=c4d.threading.GeGetCurrentThread(), animation=False, expressions=False, caches=True, flags=c4d.BUILDFLAGS_EXPORT)

    # Retrieve a copy of the cache (don't return directly the cache, since this cache belongs to the other document)
    clonedCube = cube.GetCache().GetClone(c4d.COPYFLAGS_NONE)
    return clonedCube

Cheers,
Maxime.

Wow much obliged for all the replies everyone, it is really helpful!
I think evaluating in a tempdocument is what I was looking for. I have another question about best practices:

In zipit's example the original cube is removed from the temp document after use. Is there a reason to do that? Does it help with performance and/or memory management?

I did that for the same reason @m_adam created a copy in his version: A node can only be member of one document at a time and also only be inserted exactly one time into the same document (long way of saying that Cinema is monohierachical).

Cheers
zipit

MAXON SDK Specialist
developers.maxon.net