bc lost stored mesh
-
my generator plugin takes the first mesh child object and modify it. than it stores the current values into a bc and return the mesh to the scene unfortunally i can't get acces to my cache mesh via bc via
clone = op[res.REDUCER_CACHE].GetData(1005)
full gvo :
def GetVirtualObjects(self, op, hh): """ :param op: :param hh: :return:mesh """ """ setup """ if not op[c4d.ID_BASEOBJECT_GENERATOR_FLAG]: return None doc = op.GetDocument() srcObj = op.GetDown() if not srcObj: return None while srcObj: mesh = self.ModelCurrentStateToOBject(doc, [srcObj])[0] if isinstance(mesh,c4d.PolygonObject): op.GetAndCheckHierarchyClone(hh, srcObj, c4d.HIERARCHYCLONEFLAGS_0, True) break srcObj = srcObj.GetNext() mesh = self.HelperFieldToVertexmap(op,mesh) firstFieldCache = sum(self.HelperGetVertexMapData(mesh)) clone = op[res.REDUCER_CACHE].GetData(1005) if clone and self.CacheCheck(op,firstFieldCache): return clone mesh,avgLvlDistList = self.Reducer(doc, op, mesh) """ cache """ bc = c4d.BaseContainer() bc.SetData(1001, self.HelperGetOptions(op)) bc.SetData(1002, firstFieldCache) if doc != None: bc.SetData(1004, doc.GetTime().GetFrame(doc.GetFps())) cMesh = mesh.GetClone() bc.SetData(1005, cMesh) op[res.REDUCER_CACHE] = bc return mesh
-
Hi,
your data is not lost. At least not in the way you think it is. The statement
bc.SetData(1005, cMesh)
in your code will store aBaseLink
tocMesh
in the container at the given ID, not the node itself. When you later on poll that ID, without having ensured thatcMesh
is not being freed/garbage collected, thatBaseLink
will returnNone
, due to the fact that the object it is pointing to does not exist anymore.While
BaseContainer
is a versatile data type that can store basically anything, it is severely crippled by the fact that we do not have access toGeData
in Python and we are therefor at the mercy of the Python bindings and how they interpret data passed toSetData
. Because of that there is unfortunately no way to store aBaseObject
in aBaseContainer
in Python. At least I am not aware of a way.If it is just some vertex and polygon data you want to store, you could serialise them manually into a
BaseContainer
. The other option would be to attach the data to yourNodeData
instance and overwrite the IO-methods to serialise your data into aHyperFile
when the documented is being loaded, closed, etc.Cheers,
zipit -
This is the GVO from another plugin which works flawless. The only difference is that i didn't use an input object.
def GetVirtualObjects(self, op, hh): if not op[c4d.ID_BASEOBJECT_GENERATOR_FLAG]: return None if op[res.GRIDDER_OUTPUT] == 0: if self.CacheCheck(op, op.GetDocument()) is True: preReturn = op[res.GRIDDER_CACHE].GetData(1005) return preReturn return self.Gridder(op) return None
meshCache = op.GetClone() bc = c4d.BaseContainer() bc.SetData(1001, self.HelperGetOptions(op)) bc.SetData(1002, self.CacheShader(op, doc)[0]) bc.SetData(1003, self.CacheField(op, doc)) if doc != None: bc.SetData(1004, doc.GetTime().GetFrame(doc.GetFps())) bc.SetData(1005, meshCache) op[res.GRIDDER_CACHE] = bc c4d.StatusClear()
On the other hand: I have no idea how to serialize a polygon object with uvs, vertex maps and phongtag. Also seems HyperFile a little bit over the top to store something temporary. MemoryFileStruct() could work but I need to store the byteseq as well...
GetAndCheckHierarchyClone works but it has the disadvantage of refreshing during viewport changes, so I need to use my own dirtycheck.
I think I am doing something fundamental wrong there
-
Hi,
full disclaimer: I did not read your code very thoroughly, because it is often pointless with these snippets, as most of it makes little sense without the whole context. I am arguing from a purely formal stand point and have little to no clue what you are trying to do on a bigger semantic scope. So my advice should be taken with a grain of salt, there might be easier solutions.
Initially your statement that this "works flawless" in another plugin had me a bit worried that I told you nonsense, but after a quick test I highly doubt that that other code "works flawless". I expanded my quick test into some narrative code which should illustrate the problem. You will find it at the end of the post.
For the serialising part: If you want to do it manually, you just have to decompose you object (in a pythonics sense) into data types that can be stored in a
BaseContainer
. There are no inherently wrong ways to do this. But I only mentioned these things because I did not know what you were trying to do. When you are not interested in making whatever you are trying to cache to be persistent with a document state (i.e. load/save/copy persistent), you should just attach whatever data you want to store to some Python object of your choice. As already mentioned, theNodeData
instance of your plugin would be a good candidate. Here is some mock/pseudo code to illustrate what I mean:def GVO(self, node, **kwargs): """ """ nid = node.FindUniqueID(c4d.MAXON_CREATOR_ID) if nid in self.some_cache: cache = self.some_cache[nid] # do something else: cache = node.GetClone(0) self.some_cache[nid] = cache # do something else
Cheers,
zipitTest code:
import c4d import gc def test_container_entry(bc, cid): """Pretty prints the data stored at and if it is a BaseLink element for a given element id of a BaseContainer. Args: bc (c4d.BaseContainer): The container to test. cid (int): The element id. """ is_baselink = bc.GetType(cid) == c4d.DA_ALIASLINK print "Data for id {}: {}".format(cid, bc[cid]) print "Element at id {} is a BaseLink: {}".format(cid, is_baselink) print "-" * 100 def some_scope_context(node): """Demonstrates your problem. This will create a only locally bound clone of a node and try to "insert" it into a BaseContainer (it will actually only create a link). Args: node (c4d.C4DAtom): Something to cache/clone. Returns: c4d.BaseContainer: The "cache" container, which is not really a cache at all. """ # The container. bc = c4d.BaseContainer() # A node that is only bound to the local scope, this would be the # cMesh symbol in your code. If we would pass in node directly, this # all would not happen. clone = node.GetClone(0) # These are IMHO equivalent in Python due to the fact that we have # no control over how SetData interprets the passed data, i.e. just # like with __setitem__, we are at the mercy of the Python bindings. bc[1000] = clone bc.SetData(1001, clone) # This will print out that there are some BaseObjects at the given IDs # and that these elements are BaseLinks. I.e. it behaves like you expect # the container to behave (minus the element type part). print "In scope context:" print "=" * 100 test_container_entry(bc, 1000) test_container_entry(bc, 1001) # We push the container out of the scope. The node 'clone' will be # freed after the interpreter steps to the next instruction. return bc def main(): """Entry point. """ # Some dummy node we want to cache. node = c4d.BaseList2D(c4d.Onull) # Now we pass our node into another context, this would be GVO in your # code. It returns a BaseContainer which is meant to hold our "cache", # which is not really a cache, because it only links to a cache. bc = some_scope_context(node) # This line should not be necessary due to the fact that we just left # a scope when exiting 'some_scope_context', but Python's garbage # collector is a nasty piece of work, so this is only here to make sure # to get the point of this example across and the node "clone" freed. gc.collect() # When we now poll our returned container it is "empty" (returns None) # due to the fact that the cloned node is not alive anymore. # It died with the scope context of the function 'some_scope_context'. print("After scope context:") print "=" * 100 test_container_entry(bc, 1000) test_container_entry(bc, 1001) if __name__ == "__main__": main()
Test output:
In scope context: ==================================================================================================== Data for id 1000: <c4d.BaseObject object called 'Null/Null' with ID 5140 at 0x0000021CADCAFA70> Element at id 1000 is a BaseLink: True ---------------------------------------------------------------------------------------------------- Data for id 1001: <c4d.BaseObject object called 'Null/Null' with ID 5140 at 0x0000021CADCAFA70> Element at id 1001 is a BaseLink: True ---------------------------------------------------------------------------------------------------- After scope context: ==================================================================================================== Data for id 1000: None Element at id 1000 is a BaseLink: True ---------------------------------------------------------------------------------------------------- Data for id 1001: None Element at id 1001 is a BaseLink: True ---------------------------------------------------------------------------------------------------- [Finished in 7.2s]
-
hi,
as @zipit said, you are storing a baselink in the basecontainer, not the object itself.
Your GVO should return None only in case of Memory Error as it's noted on the documentation
That said, i don't understand what you are trying to do and why.
it stores the current values into a bc
witch values do you want to store ? Or is it the current state of the object you want to store ?Cheers,
Manuel -
After taking a deep dive into the documentation again i figure out that im thinking way to complicated. I use the Cache provided by cinema and write a custom check around it. This avoid using a cached mesh in a baseContainer and also removing a cinema4d breaking memoryleak in my plugin i just found.
So thank you.