Hey @zauhar,
The main problem in your example is that your code does not enable the camera dependent option of the expression.

To do that, you must deal with the c4d.PriorityData
which wraps that value. Find a simple example at the end of the posting.
I understand and prefer the 'programmatic approach' you also shared, but don't understand the 'Optional' stuff at this point, which I assume involves interactive object selection??)
I am not quite sure what you are talking about, you mean the typing.Optional
? typing
is just a module for type assertions in modern Python and in the process of becoming the standard, with a more strictly typed Python being the goal. We are already orienting our code examples towards that future, and because this also makes code more readable. typing.Optional
just means that something is optional, i.e., can also be None
. E.g., def foo(a: int, b: typing.Optional[int]) -> typing.Optional[bool]:
means that foo
can either take an int
as b
or None
and returns either a bool
or None
.
But in the relevant code example the import was not required, and it was only there because it is part of the default script and I forgot to remove it. I have removed it now to avoid further confusion.
Cheers,
Ferdinand
The code:
"""Adds a camera dependent constraint tag to a text object.
The reason why your script did not work was because you did not enable the camera dependence of
your expression.
"""
import c4d
import typing
doc: c4d.documents.BaseDocument # The active document
op: typing.Optional[c4d.BaseObject] # The active object, None if unselected
TEXT_HEIGHT: int = 10 # The height of text in world units.
def AssertType(item: any, t: typing.Union[typing.Type, tuple[typing.Type]], label: str) -> any:
"""Asserts #item to be of type #t.
When the assertion fails, a TypeError is risen. Otherwise, #item is being passed through.
"""
if not isinstance(item, t):
raise TypeError(f"Expected {t} for '{label}'. Received: {type(item)}")
return item
def main() -> None:
"""
"""
# It is really important to check things for not being the nullptr, or the Python equivalent,
# None, in the classic API.
myCube: c4d.BaseObject = c4d.BaseObject(c4d.Ocube)
if myCube is None:
raise MemoryError(f"Could not allocate cube.")
# Which can get a bit tedious to write, so I often use something like the function AssertType,
# which has been provided above. You could also use typing_extensions.assert_type() instead.
extrude: c4d.BaseObject = AssertType(c4d.BaseObject(c4d.Oextrude), c4d.BaseObject, "extrude")
extrude[c4d.EXTRUDEOBJECT_EXTRUSIONOFFSET] = TEXT_HEIGHT * 0.5
extrude.SetBit(c4d.BIT_ACTIVE)
text: c4d.BaseObject = AssertType(c4d.BaseObject(c4d.Osplinetext), c4d.BaseObject, "text")
text[c4d.PRIM_TEXT_TEXT] = "Hello"
text[c4d.PRIM_TEXT_ALIGN] = c4d.PRIM_TEXT_ALIGN_MIDDLE
text[c4d.PRIM_TEXT_HEIGHT] = TEXT_HEIGHT
text.InsertUnder(extrude)
doc.InsertObject(extrude)
# This is especially true in these calls, the active render BaseDraw can be None.
doc.ForceCreateBaseDraw()
bd: c4d.BaseDraw = AssertType(doc.GetRenderBaseDraw(), c4d.BaseDraw, "bd")
camera: c4d.BaseObject = AssertType(bd.GetSceneCamera(doc), c4d.BaseObject, "camera")
# Instead of instantiating and then inserting the tag, you can also just use MakeTag(). The
# character animation stuff is unfortunately lacking symbols left and right, so we have to
# use the raw integer here.
tag: c4d.BaseObject = AssertType(text.MakeTag(1019364), c4d.BaseTag, "tag")
tag[c4d.ID_CA_CONSTRAINT_TAG_UP] = True
tag[c4d.ID_CA_CONSTRAINT_TAG_AIM] = True
# These are the only two parameters we must write, the target object and the axis. Here the
# symbols are also not available, but for a different reason. This part of the description is
# dynamic (the user can add or remove targets). This is usually solved with stride symbols,
# e.g., tag[SOME_STRIDE * tag.GetItemCount() + OFFSET_PARAMETER_1], but the CA Team did not
# even expose the strides. I could tell them you, but this won't help much in this case, so
# we are just going to hard-code the values.
tag[20001] = camera
tag[20004] = 5
# The important bit, you must enable the camera dependence of the tag.
priority: c4d.PriorityData = AssertType(
tag[c4d.EXPRESSION_PRIORITY], c4d.PriorityData, "priority")
priority.SetPriorityValue(c4d.PRIORITYVALUE_MODE, c4d.CYCLE_EXPRESSION)
priority.SetPriorityValue(c4d.PRIORITYVALUE_CAMERADEPENDENT, True)
tag[c4d.EXPRESSION_PRIORITY] = priority
# This is important and was missing in your script, as otherwise the GUI won't update until
# the user interacts with it in some form.
c4d.EventAdd()
if __name__ == '__main__':
main()