Hello @konradsawicki,
yes, you are right BIT_CONTROLOBJECT
is a poor indicator. There are more flags which are used for driving the visibility of objects, but they won't help you either [1].
Internally, this has been implemented quite unevenly, which in the end makes it impossible for users of the public API to properly react to this.
As said before, BIT_CONTROLOBJECT
is only the front facing part of some being BaseObject::Touch
'ed. The result of this is then that the object has this bit flag set and its caches being flushed. So, when you take your array/cube example, you can see that the Cube object will return nullptr
when its BaseObject::GetCache()
is being called, while the same cube will return its polygonal cache when outisde of the array object (check this in case you are a bit lost, as I talked there by chance exactly about this array/cube case). And when the cache of a generator object is empty, then there is also nothing to draw.
The problem is, that this all does not hold true for splines as input objects, when you for example put a circle spline below an Extrude object, it will still return something other than nullptr
for ::GetCache()
. This thread (which I had totally forgotten) sheds some light indirectly on the subject. For splines to be hidden in this manner, one must call GetHierarchyClone
or GetAndCheckHierarchyClone
on these input objects (other than for a polygon object generator which can just touch its inputs). As I wrote there, this method also calls ::Touch
in the end, but for splines this is nor permanent and there is also non-public data involved. Which is then used by the super custom drawing routines (not) drawing such splines.
What you could do is, check with GeListNode::GetInfo()
if an object has the flag OBJECT_ISSPLINE
and then check if its parent has the flag OBJECT_INPUT
, i.e., something like this:
const BaseObject* const op;
if (!op)
return NOTOK;
const BaseObject* const parent = op->GetUp();
if (!parent)
return NOTOK;
if ((op->GetInfo() & OBJECT_ISSPLINE) & (op->GetInfo() & OBJECT_INPUT))
// Do something under the assumption that #op is a spline input for a generator that is hidden.
But as explained above, this is not the actual mechanism which hides things, and you will run into many false positives and false negatives with this, because how custom all the spline input generators are with their inputs.
Long story short, checking if an object O is a currently hidden input object for a generator object is unfortunately not possible to do in the public API. For an upcoming API we have added a data dependency system which will make it easier to determine this, but for now you are out of luck.
Maybe you and I can find an alternative approach? What you want to do is a bit unusual and to me it is not clear why you are trying to do this. Could you elaborate why this is useful and how you want to use it?
Cheers,
Ferdinand
[1] I used this to explore how the flags behave on generator object input objects and their caches. Simply select an object an run this script, it will dump a little tree to the console.
"""Prints the hierarchy and cache hierachy of the currently selected object and dumps visibility
information for each node in that hierarchy.
"""
import c4d
# The primary selected object in the scene, can be `None`.
op: c4d.BaseObject | None
def PrintSceneTree(node: c4d.BaseObject, indent: int = 0) -> None:
"""Prints the hierarchy and cache hierachy of #node.
"""
if not isinstance(node, c4d.BaseObject):
return
nodeName: str = f"{node.GetName()} ({node.GetRealType()})"
tab: str = " " * indent
tabtab: str = " " * (indent + 1)
nodeString: str = f"{tab}{nodeName} ("
space: str = " " * len(nodeString)
bits: list[tuple(str, int)] = [
("BIT_CONTROLOBJECT", c4d.BIT_CONTROLOBJECT),
("BIT_IGNOREDRAW", c4d.BIT_IGNOREDRAW),
("BIT_TEMPDRAW_VISIBLE_CACHECHILD", c4d.BIT_TEMPDRAW_VISIBLE_CACHECHILD),
("BIT_TEMPDRAW_VISIBLE_DEFCACHECHILD", c4d.BIT_TEMPDRAW_VISIBLE_DEFCACHECHILD),
("BIT_TEMPDRAW_VISIBLE_CHILD", c4d.BIT_TEMPDRAW_VISIBLE_CHILD)
]
nbits: list[tuple(str, int)] = [
("NBIT_EHIDE", c4d.NBIT_EHIDE),
("NBIT_HIDEEXCEPTVIEWSELECT", c4d.NBIT_HIDEEXCEPTVIEWSELECT),
]
for i, (symbol, bitValue) in enumerate(bits):
nodeString += f"{space if i != 0 else ''}{symbol}: {node.GetBit(bitValue)}\n"
for symbol, bitValue in nbits:
nodeString += f"{space}{symbol}: {node.GetNBit(bitValue)}\n"
nodeString = nodeString[:-1] + ")"
print(nodeString)
for funcName, cache in ((f.__name__, f())
for f in [node.GetCache, node.GetDeformCache] if f()):
print(f"{tabtab}{nodeName} returns for {funcName}():")
PrintSceneTree(cache, indent + 2)
if not node.GetChildren():
return
print(f"{tabtab}{nodeName} returns for GetChildren():")
for child in node.GetChildren():
PrintSceneTree(child, indent + 2)
if __name__ == "__main__":
PrintSceneTree(op)
print ("-" * 100)