Hey @ThomasB,
so I did a clean writing of your plugin demo code in preparation to debug the output and I cannot reproduce the issue you report. For me the light works. There were some issues with your code, but none of them should cause the light to malfunction as your reported on LoadDocument. What you did there with self.container was a bit dicey and could have caused the issue but it seems somewhat unlikely.
I am today on my Mac, it seems quite unlikely that this is an OS related issue.
The object after pressing the button in your plugin:
[image: 1784573182532-f6aea3b3-a566-4779-99f4-e71a61cea4f0-image.png]
And the object after collapsing it:
[image: 1784573259288-56138dee-a9f5-415d-bc69-19e0a7695df1-image.png]
Cheers,
Ferdinand
from c4d import plugins, bitmaps, BaseObject, GeListNode
import c4d
import mxutils
import os
PLUGIN_ID: int = 1000200
PY_ADD_LIGHTS: int = 10000
class RedshiftLightsTest(plugins.ObjectData):
"""
"""
HOUSE_PATH: str = os.path.join(os.path.dirname(__file__), "res", "models", "build_1.c4d")
def __init__(self) -> None:
"""
"""
self.SetOptimizeCache(True)
self._asset_doc: c4d.documents.BaseDocument | None = None
# No, never store a reference to what you return as your cache in GVO!
# self.container = None
def Init(self, op, isCloneInit: bool = False) -> bool:
"""
"""
if not os.path.exists(self.HOUSE_PATH):
raise FileNotFoundError(f"House model not found at {self.HOUSE_PATH}")
# For cloning it would be better to copy over the document to avoid reloading it, but since
# we do not implement NodeData.CopyTo() we will just reload it for now.
self._asset_doc = mxutils.CheckType(
c4d.documents.LoadDocument(self.HOUSE_PATH, c4d.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS))
return True
def GetAssetPayload(self) -> c4d.BaseObject | None:
"""Returns a copy of the asset payload for the plugin.
"""
if not isinstance(self._asset_doc, c4d.documents.BaseDocument):
raise RuntimeError("Asset not loaded correctly.") # Should not happen, but just in case.
root: c4d.BaseObject = self._asset_doc.GetFirstObject()
if not root:
raise RuntimeError("Asset document has no root object.")
payload: c4d.BaseObject = root.GetDown()
if not payload:
raise RuntimeError("Asset document has no payload object.")
return payload.GetClone()
def GetVirtualObjects(self, op: BaseObject, hh: object) -> BaseObject | None:
"""
"""
# Return the cached object if it is not dirty, otherwise rebuild it.
dirty: bool = op.CheckCache(hh) or op.IsDirty(c4d.DIRTYFLAGS_DATA | c4d.DIRTYFLAGS_ALL)
if not dirty:
return op.GetCache(hh)
# Returning None in an ObjectData.GetVirtualObjects() call will tell Cinema that a memory error
# occurred, and with that cause Cinema to stop calling/building the object. This is almost never
# what we want, return a Null object instead.
null: c4d.BaseObject | None = c4d.BaseObject(c4d.Onull)
if not null:
return c4d.BaseObject(c4d.Onull)
payload: c4d.BaseObject | None = self.GetAssetPayload()
if payload:
payload.InsertUnder(null)
return null
def Message(self, node: GeListNode, type: int, data: object) -> bool:
"""
"""
if type == c4d.MSG_DESCRIPTION_COMMAND:
if data["id"][0].id == PY_ADD_LIGHTS:
self.ImportLights(node)
return True
return True
def ImportLights(self, node: GeListNode) -> bool:
"""
"""
if not isinstance(node, c4d.BaseObject) or not c4d.threading.GeIsMainThreadAndNoDrawThread():
return False
# This is a bit tedious but each of these calls can fail, and you cannot, or better, should
# not just do chain calls such as GetDown().GetDown().GetNext().GetNext(). One could abstract
# this away with a function such as Traverse(node, "DDNN") where D = Down, N = Next, but for
# this example I just did it manually.
doc: c4d.documents.BaseDocument = node.GetDocument()
if not doc:
return False
payload: c4d.BaseObject | None = self.GetAssetPayload()
if not payload:
return False
down: c4d.BaseObject | None = payload.GetDown()
if not down:
return False
item: c4d.BaseObject | None = down.GetNext()
if not item:
return False
light: c4d.BaseObject | None = item.GetNext()
if not light:
return False
clone: c4d.BaseObject | None = light.GetClone()
if not clone:
return False
doc.InsertObject(clone)
c4d.EventAdd()
return True
if __name__ == "__main__":
path, file = os.path.split(__file__)
file = "icon.tif"
new_path = os.path.join(path, "res", file)
bitmap = bitmaps.BaseBitmap()
bitmap.InitWith(new_path)
plugins.RegisterObjectPlugin(id=PLUGIN_ID, str="redshift_lights_test", g=RedshiftLightsTest, description="redshift_lights_test", icon=bitmap,
info=c4d.OBJECT_GENERATOR)