Jump to content

MighT

Developer
  • Posts

    400
  • Joined

  • Last visited

  • Days Won

    10

Everything posted by MighT

  1. MighT

    Atom + c4dpy

    I have never used Atom, so I have unfortunately nothing to add. But did you try asking in Plugin Café, where MAXON provides free developer support? Cheers
  2. Not directly, but there's a workaround. Create a User Data parameter on an arbitrary object. This parameter should have the same type as the data you want to display. Then simply use this User Data instead of the Result node ad have it displayed in the HUD. Another option is to convert the value to String and have a String User Data. Then you can get a lot fancier in formatting your data. Or you misuse a MoText or Text spline instead of the HUD to display data. Cheers
  3. MighT

    Xpresso Increment Node

    The pie is a lie. Sorry, couldn't resist.
  4. MighT

    Xpresso Increment Node

    My recommendation would be to make use of a User Data parameter as storage for the distance travelled and simply sum the distance per frame into this. For example it could look like so: The additional Time/Equal/Condition node group is needed to reset the distance on frame 0, otherwise it's pretty straight forward. The "Distance Travelled" port is the User Data parameter of type Real added to my travelling cube, but you could add this parameter to basically any other object or tag you like and see fit. And the scene: test_xpresso_distance_per_frame_3.c4d Cheers
  5. MighT

    Xpresso Increment Node

    It's hard to tell the issue from just this screenshot, without knowing more details, especially about "Travelinger"... Here's how I'd do it: And here's the scene file: test_xpresso_distance_per_frame_2.c4d Cheers
  6. Hi, such questions are probably better asked over at the Plugin Café, where MAXON provides free support for developers. Anyway, looking at the InstanceObject's __init__() function's documentation, it doesn't support a parameter to set the reference object, as you try to do it. Instead you could either set the "Reference Object" parameter like any other parameter or simply call the provided SetReferenceObject() function. Like so (the undo stuff is completely optional): import c4d def main(): if op is None: return inst = c4d.InstanceObject() inst.SetReferenceObject(op) doc.StartUndo() doc.InsertObject(inst) doc.AddUndo(c4d.UNDO_NEW, inst) doc.EndUndo() c4d.EventAdd() if __name__=='__main__': main() Furthermore, as you can see in my example, in Script Manager there are some global variables predefined (actually that's true for all places, where one can use Python in C4D, yet, the available ones and their content differ a bit): 'op' is the currently active object 'doc' is the currently active document ... So there's no need to use GetActiveObject() in this context. For more information I really recommend the docs and above mentioned Plugin Café as it already contains answers to loads of questions. One additional note: As you are using InstanceObject class, which got introduced in R20, you'd probably want to update your profile with the actual version of C4D you are using. This will help people tremendously in helping you. Cheers Edit: Fixed description of 'doc'
  7. Yes, I have been working for MAXON for four and a half years. Not sure about the specialist, though, probably rather something. 😉 And I sure have a good time 🙂 Something like this can not work (not completely true, in Python there are means to pull it off, but that's a completely different ballpark). In short, you can not (as said above, in theory you could in Python, but not as simple as you did) create code from a string. The string itself (not the text represented by it) is an object and therefore, even if not immediately visible rather behaves like a variable. Leaving the quotation aside, you have written something like this: op[a + b] This is a simple function call on op. This is identical: op.__getitem__(a + b) So you have written: a = "c4d." b = bc[c4d.DESC_IDENT] op.__getitem__(a + b) b results from reading the description ID from the BaseContainer, basically an integer, lets say 42. Via type conversion a + b results in a string: "c4d.42" So, already it does not look as what you intended, probably something like c4d.PARAMID. But even if it resulted in a correct string, it's still a string, not code. For example c4d.DESC_NAME is a "constant" (lets keep it simple), in this case representing a reference to a integer value, namely 1. You tried to build the name of such a constant using a string. But instead of trying to interpret your string as the name of something else (I'd actually call it a symbol), it will simply pass the value (rather a reference to the value), which will then be interpreted as an integer (as that's what __getitem__() expects) by __getitem__(). Without going any deeper, please believe me the value of the string "c4d.PARAMID" is in no way 42 (the value assumed in the beginning). And without additional means there's no way for Python to know, that it should actually interpret the content of the string as code (or the reference to an ID) as you intended. Did I sufficiently confuse you? I could try to elaborate a bit more, e.g. via Skype, just contact me via PM. With sufficient interest also in a group. Cheers
  8. This is something, that would probably better be discussed in PluginCafé. Anyway, I'm not sure, I understand your first question. What kind of concatenation? And why? What's your final intent? Inside the loop you already have the DescID you need for parameter access: op[paramid] Your second question boils down to understanding DescIDs and DescLevels. I can't explain the entire concept here. There's some more explanation in the Python docs and I think, it's well explained in the C++ docs: Description manual and DescID manual. In short, with basic (or generic) datatypes (like e.g. integer, float, string,...) it's nice and easy. The parameter has an ID and a datatype, both stored in one single DescLevel. But what, if a datatype is more complex. Like a Vector, it consists of multiple float values. Is the type Vector or is it float? Here multiple DescLevels come into play. On the first level a Vector looks like a vector and then there's the second level describing the next lower level of the data type (every component of the vector is a float (or anything else, like a Vector of strings). For User Data it's basically the same. You have a complex data type (User Data), which needs more detailed explanation on the next level (actual UD ID, data type,...). The Description BaseContainer in the end just contains further details (name, value ranges, widget to use,...) needed to render the parameter in a Description Custom GUI (like e.g. used for the Attribute Manager). Long story short, maybe a few lines of code can shed some more light: import c4d def main(): if op is None: return description = op.GetDescription(c4d.DESCFLAGS_DESC_NONE) for bc, paramid, groupid in description: print paramid # see structure of DescIDs, user data has two levels (except for the root iuser data groups) if paramid[0].id == c4d.ID_USERDATA: # exclude groups (including the root one) from inspection if len(paramid) > 1 and paramid[1].dtype != c4d.DTYPE_GROUP: # the second DescLevel has the user data ID and type print 'User data ID/type:', paramid[1].id, paramid[1].dtype print 'Parameter: %s = %d' % (bc[c4d.DESC_NAME], op[paramid]) if __name__=='__main__': main() I hope this helps. Cheers
  9. Is this more of a practice on how to accomplish this? Otherwise I'd suggest the use of the Convert Selection command.This can also be used via Python (MCOMMAND_CONVERTSELECTION) with SendModelingCommand(). By the way, are you aware of Plugin Café? There MAXON pays a team to answer questions like these and often the answer can already be found there right away. Having said this, we (sorry, I didn't mean to MoClone myself, I was thinking of the community of this forum) could certainly also talk you through this here. The thing is, C4D doesn't really know the concept of an edge, there are only points/vertices and polygons. Edges are implicitly defined due to the order of points which form a polygon. The Neighbor class you are already using provides some helpers to make this a little easier. Yet, it's no one liner. Actually the docs of the Neighbor class already contain roughly the code you need in the description of GetPolygonInfo(). So, if this is more a learning thing, I'd suggest to start there. Cheers
  10. I think, this has safety reasons. After a User Data parameter got defined, it's respective data is stored in a BaseContainer of the entity. And this value may now be used in a gazillion ways all over C4D, Xpresso, Animation,... I wouldn't take for granted, something expecting data of a certain type behaving gracefully when the data type suddenly changes. I'd call this defensive design. What is actually the issue? I do use User Data a lot, e.g. for UI prototyping, and it was never a real issue for me. If I need to change the datatype, I simply create a new parameter as you described and done. Why would I need to worry about the new ID (except changing it in one define in my code). Although I admit (and maybe this is also your main issue), it's a bit sub-optimal, to say the least, one can not see the new ID before closing and re-opening the dialog. In situations with already cluttered IDs (due to a lot of addition and removal of user data) it can get tedious. Which probably also answers my initial question... yet, I don't think, I would need the option to change an existing ID, but rather an option to see the ID of freshly created user data.
  11. Oh, sorry, you misunderstood. Please send them just your own issues and thoughts. I wouldn't want mine to falsely multiply. Seeing this thread we'll overlap anyway. At least for the Script Manager maybe we could get an option to autosave the script on pressing the Execute button. At least users wouldn't loose any scripts by stupid mistakes anymore. If we then could get the option to have external scripts also in Python Generator and Tag (and so on), this autosaving could help there as well. Is probably an easier change than asking MAXON to tackle Pyhon's global lock (which is the real problem behind those lockups), which may not even be technically possible or feasible. Regarding c4dpy: At least if you are Windows, it's well worth taking a look at Visual Studio Code. It's more an editor, than an IDE, yet with nice features. In PluginCafe Donovan also demonstrates the use of PyCharm with c4dpy.
  12. Being a bit old school, I don't actually need an IDE for Python, yet an external editor adds an extra level of security. But please enlighten me, which modern text editor is not able to change and adapt to a text encoding? My personal editor of choice: Certainly Sublime Text 3 (at least on Win and Mac). To which I switched shortly after buying a lifetime license of Ultra Edit. Doh! UE itself is not bad, but nowadays it grew too big and feels sluggish, yet it would be my alternative if you are a more visual, UI focused person. It's certainly one of the most powerful editors, feature-wise. On the other hand Sublime feels so fast (as long as you don't work on multi Megabyte files, in which case Ultra Edit is probably better, and so is its hex mode) and focuses on the important stuff. On Linux I have to admit, I'm still the Xemacs and vim dino. But I tend to use Xemacs really only for text editing, neither web browsing nor playing minesweeper...
  13. Hi, mostly valid points, I agree with. Did you forward these to MAXON? I'm working on my own list and maybe it raises awareness, if enough share the same complaints. The only point I do not completely agree is the Command Line. It's still in R21 and you can add it to your layout as you like. Yet, for me it got redundant, as I have way more power in the Console (e.g. the possibility to quickly test multi line statements in there, I like a lot). In the end I guess, that's a matter of taste and I agree the Command Line should stay for those who prefer it. For the editor... well, lets ignore its flaws for a moment, because you mentioned the external editor option (which currently unfortunately works only for the Script Manager). I wouldn't just see this as a workaround for the internal editor, but also and more importantly as a workaround for another problem. C4D has no watchdog built in, so whenever your Python (your script) runs into an infinite loop (come on, we have all been there...), C4D freezes without any possibility to get it back nor to save your last edits. Not so with an external editor. And since C4D senses changed scripts, the use of an external editor is just one additional click ("file changed, do you want to reload?") as a trade off for lost code. Leaving aside all the other convenience functions you get from a modern editor. I say this over and over and especially for Python beginners (not talking about you Intenditore) this can avoid a lot of frustration. Your post made me think, if I should come up with a plugin, allowing something similar in Python Generators or Tags. Maybe I'll come back to this... By the way, at least for me here on Win10 (no screen scaling, no HiDPI) for long lines even the scrolling and cursor positioning is off. It doesn't follow, if the cursor leaves on the right side. And when you click there, the cursor gets positioned a centimeter off. Sometimes not even with the scrollbar I can reach the end... Yet, for me remain the positive changes. While (or rather because) no longer synchronized (e.g. multithreaded output can get mixed up), the Console Output got way faster, which makes it much more usable. And also the possibility to arbitrarily copy&paste from the console is a big plus for me. So, my feelings are really a bit mixed here.ö Cheers
  14. Oh, sorry, I'm no longer at MAXON. I somehow missed that part.
  15. Here, with this change it will rename all Null objects which name starts with "Null". If there's a numeric suffix and the index can be found in the name list, the respective name is used, in all other cases the first name (index zero) is used. Here, it's only the renaming section. Simply replace the block with this one: # Rename Null objects doc.StartUndo() obj = doc.GetFirstObject() # TODO only searching top-level of object hierarchy for Nulls to rename while obj is not None: if obj.CheckType(c4d.Onull): nameParts = obj.GetName().rsplit(NAME_INDEX_SEPARATOR) if nameParts[0] == 'Null': doc.AddUndo(c4d.UNDOTYPE_CHANGE, obj) if len(nameParts) > 1 and nameParts[1].isdigit() and \ int(nameParts[1]) >= 0 and int(nameParts[1]) < len(names): obj.SetName(names[int(nameParts[1])]) else: obj.SetName(names[0]) obj = obj.GetNext() doc.EndUndo() c4d.EventAdd()
  16. Ah, I missed your question on the first Null without digit. Actually that's why it's always good to post a scene 😉 How are the Nulls to be renamed identified? All Nulls on the topmost hierarchy level? Or just Nulls which begin with Null? I assumed it to be potentially dangerous, if the script simply renamed all Nulls, so I deliberately made it so that it only renames those named like this "Null.n" (with n a number). How is the logic then? If there is only "Null" it will be the name with index zero?
  17. You are welcome, glad it helped. Well, C4D has always been a hobby of mine since its Amiga times. Really just a hobby. I was fascinated by computer generated imagery and simulated physics. Yet, have never been able to produce anything anybody with a clear conscious would call art, because... probably because my brain doesn't work this way. I studied electrical engineering and worked as a system design engineer for almost 15 years (mainly C and VHDL programming). Around that time I decided to shift my focus onto something I like better and to try to hire at MAXON. Therefore I thought, it wouldn't hurt to have developed a plugin before. I took my year's vacation, MAXON's SDK documentation and the SDK examples and worked myself into it. Not that it helped with getting hired by MAXON (it took three more years to get into there), but for a reasonably experienced developer it was certainly possible to learn enough to get three plugins working (of which I released only two to the public and their functionality got redundant with MAXON's next major releases...). Only after I got hired by MAXON and worked inside the SDK Team I then also started familiarizing with the Python side. I doubt, you wanted to hear my life's story, but at least that's how I got into developing plugins. And you asked how I got into plugin development... Please, don't get me wrong, I certainly do not want to say, you need to study programming to get into it. Far from it. Actually I think it's more important to understand certain principles of C4D. Then, if you are able to program in Python, the SDK documentation and examples should get you going quite a bit. And finally, I think MAXON is quite generous in this regard, there's MAXON's SDK Team (and I'm not mentioning this, because I was in there) who provide free support to everybody in the Plugin Cafe forum. These guys usually get your problems solved quite quickly and nicely. Nowadays there are also other nice resources. Did you see @Cairyn's spoonfed series? Recommended. The links somewhere here in the forum. If you are planning to reach an age of 150, you may also wait for me to release my long planned YT tutorials on C4D Python development. Unfortunately there's too much in the pipe to get to these. Last note: I do also teach C++ and/or Python plugin programming for C4D to interested people (and also to not interested people who are forced by their boss to do it nevertheless). Maybe you are interested. While I usually do not do this for free, it is on a pay-what-you-want,-are-able-to-afford-and-deem-worthy basis. No strings attached. PM me, if you are interested. If enough people in here are interested, we can also do a Q&A chat session.
  18. So, here you go as a starter. The CSV file I used for testing: test.csv My test scene: test_rename_object_from_csv.c4d The script for Script Manager: rename_objects_from_csv.py And for discussion, the script itself: # Rename Null objects based on information from a CSV file import c4d SEPARATOR = ',' NAME_INDEX_SEPARATOR = '.' def main(): filename = c4d.storage.LoadDialog(c4d.FILESELECTTYPE_ANYTHING, doc.GetDocumentPath(), c4d.FILESELECT_LOAD) if filename is None: return # Get names as array from CSV file fileCsv = None names = [] try: # Read CSV file fileCsv = open(filename.decode("utf-8"), mode='r') # TODO Currently only reading first line, only (based on forum post) names = fileCsv.readline().split(SEPARATOR) finally: if fileCsv is not None: fileCsv.close() # Rename Null objects doc.StartUndo() obj = doc.GetFirstObject() # TODO only searching top-level of object hierarchy for Nulls to rename while obj is not None: if obj.CheckType(c4d.Onull): nameParts = obj.GetName().rsplit(NAME_INDEX_SEPARATOR) if len(nameParts) > 1 and nameParts[1].isdigit() and nameParts[0] == 'Null' and \ int(nameParts[1]) >= 0 and int(nameParts[1]) < len(names): doc.AddUndo(c4d.UNDOTYPE_CHANGE, obj) obj.SetName(names[int(nameParts[1])]) obj = obj.GetNext() doc.EndUndo() c4d.EventAdd() if __name__=='__main__': main() Cheers
  19. Well, then go ahead and let your ideas run free. The more detailed and accurate you describe your needs, the faster I can try to get you something.
  20. Sure, this is possible and shouldn't be complicated. There's really only this one line in the CSV? And all you want is the Nulls renamed? Wouldn't it be more efficient, to build the entire hierarchy based on the CSV? Also it's always helpful to provide a scene for testing, when asking for scripts or alike. This can remove a lot of ambiguity and also spares the developer the need to setup her/his own scene (which the may already be a wrong basis and misleading). Anyway, I'll be back... Cheers
  21. While this may be an issue of MagicSolo, the workaround could be to simply use the command "Unhide All" (menu Select -> Unhide All).
  22. MighT

    Batch JPEG to materials

    Hi, here's a small script for use in C4D's Script Manager. The script lets you choose a folder and will then create one material per image found in that folder (non-recursive). # For all images in a selected folder create a material # with the image mapped to the color channel import os import c4d # Add missing image suffixes here, if an image file is not found IMAGE_SUFFIXES = ('jpg', 'jpeg', 'png', 'tif', 'gif') def main(): folder = c4d.storage.LoadDialog(c4d.FILESELECTTYPE_ANYTHING, doc.GetDocumentPath(), c4d.FILESELECT_DIRECTORY) if folder is None: return print folder folder = folder.replace('\\', '/') if folder[-1] != '/': folder += '/' doc.StartUndo() for filename in os.listdir(folder.decode("utf-8")): fn = filename.rsplit('.', 1) if len(fn) <= 1 or fn[1].lower() not in IMAGE_SUFFIXES: continue mat = c4d.Material() mat.SetName(fn[0]) shd = c4d.BaseShader(c4d.Xbitmap) shd[c4d.BITMAPSHADER_FILENAME] = folder + filename.encode("utf-8") mat.InsertShader(shd) mat[c4d.MATERIAL_COLOR_SHADER] = shd doc.InsertMaterial(mat) doc.AddUndo(c4d.UNDOTYPE_NEW, mat) doc.EndUndo() c4d.EventAdd() if __name__=='__main__': main() Hope it helps. Cheers Edit 22:09: fixed string handling for os functions
×
×
  • Create New...