Your browser does not seem to support JavaScript. As a result, your viewing experience will be diminished, and you have been placed in read-only mode.
Please download a browser that supports JavaScript, or enable it if it's disabled (i.e. NoScript).
Hello, Why I can't make editable the generated object by GetVirtualObjects(). When I select my object in object manager and I press the Make Editable button or I press The C Shortcut no thing happen.
import c4d, os from c4d import documents, plugins, bitmaps PLUGIN_ID = 10000000 class MyObject(c4d.plugins.ObjectData): def Init(self, op): self.SetOptimizeCache(True) return True def GetVirtualObjects(self, op, hierarchyhelp): obj = op.GetDown() if obj is None: doc = documents.GetActiveDocument() # Merge object preset = os.path.join(os.path.dirname(__file__), "res", "my_scene.c4d") load = c4d.documents.MergeDocument(doc, preset, c4d.SCENEFILTER_OBJECTS ) if load is False: return True # Clone preset preset = doc.GetFirstObject() obj = preset.GetClone() obj.InsertUnder(op) obj.ChangeNBit(c4d.NBIT_OHIDE, c4d.NBITCONTROL_SET) preset.Remove() # Unfold in manager for bit in xrange(c4d.NBIT_OM1_FOLD, c4d.NBIT_OM4_FOLD + 1): op.ChangeNBit(bit, c4d.NBITCONTROL_SET) obj.DelBit(c4d.BIT_ACTIVE) op.SetBit(c4d.BIT_ACTIVE) # Update the document c4d.EventAdd() dirty = op.CheckCache(hierarchyhelp) or op.IsDirty(c4d.DIRTY_DATA) if dirty is False: return op.GetCache(hierarchyhelp) if __name__ == "__main__": icon = bitmaps.BaseBitmap() icon.InitWith(os.path.join(os.path.dirname(__file__), 'res', 'icon.tif')) plugins.RegisterObjectPlugin(PLUGIN_ID, 'My Object', MyObject, '', c4d.OBJECT_GENERATOR, icon)
Thank you.
Hi,
your code does look a bit confusing to me.
If you invoke self.SetOptimizeCache(True) in ObjectData.Init() then you do not have to handle your cache manually in ObjectData.GetVirtualObjects() (GVO).
self.SetOptimizeCache(True)
ObjectData.Init()
ObjectData.GetVirtualObjects()
Only letting your GVO logic execute if the BaseObject node attached to your ObjectData has no children and then grabing the first object of the scene as an input is really weird.
GVO
BaseObject
ObjectData
If you do some file I/O, you should not do it in GVO if possible. Also the following snippet is a crash waiting to happen (when your first object in the scene is a MyObject object).
MyObject
preset = doc.GetFirstObject() obj = preset.GetClone()
You do modify the scene graph and invoke events in GVO. You are not allowed to do that.
You also use GVO against its purpose since you never do return any objects constructed in your GVO. You only return the cache when your object node isn't flagged dirty. But that does not mean anything since you never return any objects to populate that cache. In most cases your GVO will return None, which will be read by c4d as that you have indicated an memory error. If you want to return nothing you have to return a null object. So there is nothing to convert in your ObjectData.
None
Think of your ObjectData as a null object. In GVO you have to return the object tree that c4d is supposed to put under that null. These children objects will be hidden from the user. When the user then invokes "current state to object" c4d will remove that null and return the hidden object tree.
Cheers zipit
@zipit Hi,
Here is an example of the exact method that I had used with the GVO and I never get a crash on the application.
import c4d, os from c4d import documents, plugins, bitmaps PLUGIN_ID = 100000000 class MyObject(c4d.plugins.ObjectData): def Init(self, op): self.SetOptimizeCache(True) return True def GetVirtualObjects(self, op, hierarchyhelp): doc = documents.GetActiveDocument() if not op.GetDown(): # Merge object preset = os.path.join(os.path.dirname(__file__), "res", "my_scene.c4d") load = c4d.documents.MergeDocument(doc, preset, c4d.SCENEFILTER_OBJECTS ) if load is False: return True # Search object // I have put the plugin id in the object name to ensure that there is no other object with same. preset = doc.SearchObject("my_object[100000000]") preset.InsertUnder(op) preset.ChangeNBit(c4d.NBIT_OHIDE, c4d.NBITCONTROL_SET) for obj in preset.GetChildren(): try: obj.ChangeNBit(c4d.NBIT_OHIDE, c4d.NBITCONTROL_SET) obj.GetDown().ChangeNBit(c4d.NBIT_OHIDE, c4d.NBITCONTROL_SET) if obj.GetDown().GetDown(): obj.GetDown().GetDown().ChangeNBit(c4d.NBIT_OHIDE, c4d.NBITCONTROL_SET) if obj.GetDown().GetNext(): obj.GetDown().GetNext().ChangeNBit(c4d.NBIT_OHIDE, c4d.NBITCONTROL_SET) except : continue # Unfold in manager for bit in xrange(c4d.NBIT_OM1_FOLD, c4d.NBIT_OM4_FOLD + 1): op.ChangeNBit(bit, c4d.NBITCONTROL_SET) op.SetBit(c4d.BIT_ACTIVE) # Update the document c4d.EventAdd() # Here I add the modifications to the returned object (preset) and also to his children objects. obj = op.GetDown() return op.GetCache(hierarchyhelp) if __name__ == "__main__": icon = bitmaps.BaseBitmap() icon.InitWith(os.path.join(os.path.dirname(__file__), 'res', 'icon.tif')) plugins.RegisterObjectPlugin(PLUGIN_ID, 'My Object', MyObject, '', c4d.OBJECT_GENERATOR, icon)
Note: The parent object ("my_object[100000000]") has a python tag that contain the majority of my python code.
Because my object is too complex (it contain multiples sub-objects) and if I reload it at each change on op parameters, over a period of time when the preset object is reloading the object is disappeared from the scene.
And to make editable my object I just added a button "Make Editable" on my description resource.
The quoted lines in point 3 are a problem because when your first object is a MyObject, you will end up with an infinite recursion, because this object will try to build its caches when it is added to the scene (either in the illegal direct scene graph modification way of yours or by the correct way by returning that MyObject as a result of your GVO). Building cashes will mean that it will try invoke its GVO which then is the start of the infinite recursion. But this is only a somewhat minor issue with your code.
There are two major problems with both your code snippets.
You modify the scene graph and invoke events in a threaded context (see point 4 in my last posting).
You 'misuse' GVO as you basically treat it like a script or CommandData plugin (see points 5 and 6 in my last posting).
CommandData
Disregarding the threaded context of a method and the mentioned limitations can lead to corrupted objects and files or crashes of the main application. I was probably a bit unclear about the importance of the points in my first posting, I just did wrote them down as I red your code. The important points you should focus on are 4, 5, and 6. You will have to change your approach.
If you are unclear on how to do that, check the example plugins on github and then explain here to us what you are trying to do. Because I frankly, have no idea, what your code is supposed to do on a higher level of meaning
Hi, I'm actually developing a new version of HDRI Studio Generator Tool, is a complex Rig using UserData and Xpresso. So for the new version I want to replace all the UserData by the Description Resource and using the GVO to benefit from the c4d.plugins.ObjectData Automated Handle Interface: GetHandle()/SetHandle()/GetHandleCount() and other advantages.
If you don't mind, here is a full plugin that use a similar method: primitive_objects.zip (I don't know if I am authorized to share zip file here)
In comparison with this plugin example my actual rig is very complex it contain hundreds of description resource and twenty Handle Count. the plugin example it's only to see the method that I use.
I hope that is clear. I down know how explain you what I want to do exactly
yes, while I still have no c4d here (so I could not use your zip), this does make things a lot more understandable for me.
Here is a quick sketch on how I would go about implementing, what you want to do.
class LightRig(c4d.plugins.ObjectData): def __init__(self): ''' ''' self._light_rig_cache = {} def Init(self, op): ''' ''' self.SetOptimizeCache(True) return True def _load_first_object_from_file(self, path): ''' Returns the first object from a file path interpreted as a c4d document. Args: path (str): The file path. Raises: IOError: When the file path does not exists or could not be red. IOError: When the document does not contain any objects. Returns: c4d.BaseObject: The first object of the document. ''' doc = c4d.documents.LoadDocument(path, c4d.SCENEFILTER_NONE, None) if doc is None: msg = 'Could not load file.' raise IOError(msg) op = doc.GetFirstObject() if op is None: msg = 'Target object not found.' raise IOError(msg) return op.GetClone() def _get_light_rig(self, lid): ''' Either loads the light rig with the given id from a file or returns a copy of the local cache. Args: lid (int): The integer identifier for the requested light rig. Raises: IOError: When the hardcoded file path does not exists or could not be red. ValueError: If lid is not a legal light rig id. Returns: c4d.BaseObject: The requested light rig. ''' # Return an cached object from our ligth rig hash table if lid in self._light_rig_cache: return self._light_rig_cache[lid].GetClone() # Or load it from a file and add it to the cache if lid == c4d.ID_MY_FANCY_LIGHT_RIG_1: rig = self._load_first_object_from_file(SOME_FRIGGING_FILE_PATH_1) self._light_rig_cache[lid] = rig elif lid == c4d.ID_MY_FANCY_LIGHT_RIG_2: rig = self._load_first_object_from_file(SOME_FRIGGING_FILE_PATH_2) self._light_rig_cache[lid] = rig # ... else: msg = 'Illegal light rig id: {}.' raise ValueError(msg.format(lid)) return rig.GetClone() def GetVirtualObjects(self, op, hierarchyhelp): ''' Returns the preloaded light rig cache. ''' # The more verbose form would be somehing like: # if op[c4d.LIGTH_RIG_TYPE] == c4d.ID_MY_FANCY_LIGHT_RIG_1: # return self.get_light_rig(c4d.ID_MY_FANCY_LIGHT_RIG_1) # elif ... return self._get_light_rig(op[c4d.LIGHT_RIG_TYPE])
A few notes:
I wrote this on an iPad from my head, so, jeah, view it as a 'draft'.
Using predefined data from a document is still a rather exotic concept. You might have to preload all data, when you run into threading issues.
The preloading / caching of your light rigs is in general a problem, as it will increase the memory footprint of your plugin.
Building your rigs procedurally might be a better option performancewise.
@zipit Hi, Here is short video capture to compare between the result of my method and the result of your method, in my method the object stay visible when I moving the Handle point or make change in object data instance but with your method the object is not visible when I moving the Handle point or make change in object data instance. For this reason I'm using this method.
Thank you for your time.
Consider yourself to have been warned
@zipit Hi, Sorry, I said "My method" and "Your method" just to differentiate between the two methods. I don't said that my method is better I'm just searching a legal method that allow me to obtain a similar result as the method that I'm using now. in the method that I'm using the preset is loaded in document only one time. So, for example when I move the object handle points the change is applied into the previous loaded object.
The loaded object has a python tag that contain all object controls. here is an example of python tag content:
import c4d def main(): obj = op.GetObject() parent = obj.GetUp() scale = parent[c4d.PRIMITIVEOBJECTS_GLOBAL_SCALE] # Objects scale obj.SetAbsScale(c4d.Vector(scale))
But with second method at each change the preset object is reloaded from disc or cache with the new changes (the preset object is removed then reloaded with the new changes) and that result that the object is becomes not visible in the View during a few thousandths of a second.
I'm thinking about another alternative, is the following code cause the same problem:
def GetVirtualObjects(self, op, hierarchyhelp): doc = documents.GetActiveDocument() path = os.path.join(os.path.dirname(__file__), "res", "my_scene.c4d") load = c4d.documents.MergeDocument(doc, path, c4d.SCENEFILTER_OBJECTS) if load is False: return True return c4d.BaseObject(c4d.Onull)
no need to be sorry, I am / was not offended. I was just trying to be very clear about the implications of your approach.
draw()
@zipit Hi, Thank you, I'm trying to rebuild all my rig inside the GVO and I think I will using C++. is it better to use c++?
Cheers Mustapha
@mfersaoui said:
is it better to use c++?
I think the answer to that is highly subjective. Both SDKs and languages have their strengths. As your plugin probably is neither going to be computationally expensive, nor will be in need of any of the more advanced features only the C++ SDK does offer, I would say, Python wins this cost/gain comparison by a long shot.
But that is subjective
Hello,
just to make some things clear:
GetVirtualObjects()
GetActiveDocument()
NodeData
EventAdd()
LoadDocument()
best wishes, Sebastian
@s_bach said in Make editable an object generated by GetVirtualObjects():
Hello, Thank you, I decided to use C++ to do that, I just post new topic "MAXPORTS GraphView Flags - Description Resource" is a continuity of this topic.
Hello, I gave up the idea to merge objects from scene file, I had generate my object and all her sub-objects inside the GetVirtualObjects(). But now I'm searching how to assign materials to my sub-objects, I would imagine that is the same as objects, should I use the LoadDocument()? if yes are there examples? and then can I change these materials proprieties from the object description resource or I must to use CommandData dialog? Thank you.
a generator (GetVirtualObjects()) cannot create materials. You must generate materials in reaction to user-interaction (Material Manual). Then you can assign materials to your (virtual) objects using a TextureTag (TextureTag Manual).
for example, you could detect when your generator object is created from the UI by listening to the MSG_MENUPREPARE message in NodeData.Message(). Then you can create all the materials you want to use later.
@s_bach Hello, Thank you, As you explained, I create all materials in NodeData.Message() and then I called them in the GVO. I just want to know if is it allowed to edit the called material parameters inside the GVO or not? see example bellow:
BaseObject* MyObject::GetVirtualObjects(BaseObject* op, HierarchyHelp* hh) { ... BaseObject* container = BaseObject::Alloc(Onull); if (!container) return BaseObject::Alloc(Onull); container->SetName("Container"); BaseMaterial* material = doc->SearchMaterial("my_material"); if (material) { // create the texture tag TextureTag* const textureTag = static_cast<TextureTag*>(container->MakeTag(Ttexture)); if (textureTag == nullptr) return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION); const Float Brightness = data->GetFloat(MYOBJECT_LIGHT_INTENSITY); // Is the following action allowed inside GVO: material->SetParameter(DescID(MATERIAL_LUMINANCE_BRIGHTNESS), Brightness, DESCFLAGS_SET_0); textureTag->SetMaterial(material); } ... }
Best, Mustapha
I think this is not allowed. I've just seen the list of the Forbidden Functions and "Make any changes to materials." is inside.
as said before, it is not the purpose of GetVirtualObject() to edit the scene or to edit elements of the scene; this includes materials.