Jump to content


  • Content Count

  • Joined

  • Last visited

Community Reputation

10 Noble Beginner

Profile Information

  • First Name
  • Last Name

Cinema 4D Information

  • C4D Version
    20.057 Studio

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. What you're looking for is a specialized algorithm. However if you don't want to get your hands dirty with Python building a sudoku algorithm then I would still take inspiration from Sudoku and build a simpler solution that depending on how you approach it may or may not result in additional manual work. The idea is simple. Build some blocks first that you know won't have neighbors of the same type. Then clone those blocks! At first you might be thinking "But that's going to be even more manual labour than just selecting!". Not necessarily, hear me out, apart form throwing objects into a couple of cloners it could be all procedural provided you have enough "posters" to clone. I would recommend at least 3x3 as it ensures you have no same neighbors to all sides, but you could even do 2x2 grids if you're really short on objects to clone. With a 3x3 you can in theory do this down to just 3 different "posters" but you could have to arrange them in the same pattern as a Sudoku for that manually! i.e., 1 2 3 2 3 1 3 1 2 Then the four rotations + order change to give you quite a few variations. The patterning would still be pretty obvious but better than just straight random. 2x2 Would make for even more obvious patterning with less than 4 things to clone. But I think this approach can scale pretty well even down to few objects to clone. Ideally I'd try to have at least 9 different posters though. Then you take your grid of 9 posters and clone the grids, using a random value to select the grid cloned using the "Modify Clone" parameter in an Effector. You could make this a little simpler and a little more procedural by using a "Sort" randomness on the smaller grid component - Sort uses a card deck shuffle approach to randomness, so unique clones should not end up next to each other. Remember to set the Min/Max values to 0-100% in the Effector for this to work properly! You could then use two of those small grids to generate all your sub grids by making use of the Blend mode of a parent Cloner, if you include the Effector modifying the small grid in the hierarchy being cloned and duplicate it with a different seed the Blend mode will handle the generation of all your variations. Drive the parent Cloner's "Modify Clone" value with it's own Random Effector or Field and you're good to go. I've attached a scene showing how to do this. This should result in you having a much reduced chance of groups of three (and no chance of 3 in a row). Groups of 2 will still show up though, but hopefully less often, bear in mind that with this algorithm the corners of the sub-blocks are the weak point here as you could end up in theory with 4 that all happen to have the same object in that one corner. Hopefully that won't happen that often (it's a pretty low probability) and you could either just try changing the seeds or random effector settings, or manually selecting and using another effector to modify the clone value a bit further to fix that. Best of all this approach keeps things algorithmic and at the same time relatively simple. wallofposters.c4d
  2. PerAnders

    C4D Cafe 2.0!

    Awesome job guys, a lot of work obviously went into this and it shows! Can't wait to see what people make for the challenges too.
  3. Which is the part your'e having trouble with? Just being able to go through all the materials in the document rather than a selection? mat = doc.GetFirstMaterial() while (mat != None): do something to mat mat = mat.GetNext() Or being able to automatically determine which materials are the same? In which case you need to determine what you mean by the same, if it's everything i.e. color, settings etc, then you should be able to collect all of them and compare the contents of their BaseContainers. If it's just something like the name must be the same then you can compare the individual parameters. In each case push the matches into an array/list then apply the function you've already written to eliminate the duplicates.
  4. Hi, in this case you don't need to call GetObject() as the Python code is actually on the object itself. You use that in a tag because "op" is the tag there (quirk of the Python tag in Cinema), so when you call op.GetObject() you're actually getting the object that the tag is on by calling the tag's "GetObject" function. This means all you should need is op[c4d.ID_USERDATA,8] =50 etc.
  5. Well you know you can start out with what you have here, just swap out the null for a python generator, copy and paste your interface over and add the message receiving function to accept your button presses then go from there. You can use it to set the default parameters however you want. Useful things to help you as you go - you can drag & drop in GUI elements to get their ID's directly from the Attributes Manager, however these just show you the main ID's to check then you should then use c4d.DescID and c4d.DescLevel. I've done basically what i've described with the file attached, this allows you to see how it can work. Then you just need to set your defaults the if statements. I haven't checked that the userdata linked correctly in expresso but you should be ale to quickly check and update those ports if they were incorrectly copied across. In general starting scripting isn't that hard, it's just learning the basics of python. there are lots of courses out there for this, but also a decent documentation on plugincafe.com for the cinema API. It helps having tasks that you have in mind you want to do, then you can figure out the right questions to ask and get answers both on plugincafe.com and here. CameraRig_002_pythonized.c4d
  6. Hi. Ok so I think I got the wrong end of the stick there. The def message(id, data): function belongs on a tag or in a python object. It can only receive messages from its own GUI i.e. user data on that actual tag or object. The buttons you have on the null object will instead send the message to the null object so your script will never get it. If you were to swap the null for a python object then you could handle the GUI changes through that. Effectively it would still work as a null object, you'd just return an "Onull" rather than the default "Ocube" that's in there, but also have the message function. Now if that sounds like too much the hack that we used to do for this when scripting was to use a boolean checkbox instead and as soon as it was checked make the change then disable the checkbox. It's not as clean for the UI but it works. It really depends on how important spit and polish is for you with your object.
  7. Hi. To deal with button presses you need to receive the message MSG_DESCRIPTION_COMMAND, this involves creating the Message function which from the top of my head should go something like : def Message(id, msgData): if id == c4d.MSG_DESCRIPTION_COMMAND: # a button was clicked in the GUI so do something here, check the passed id in the msgData if you have more than one command to figure out which was pressed return True Hope that helps.
  8. Your matrix vectors must be orthogonal, they describe the projections in each axis for the points. You can ensure this by applying cross products of the other two vectors e.g. x = z.Cross(y) and so on. In addition the length of each vector should be unit unless you're intending to scale the object, this is achieved through normalization. Bear in mind that Cinema uses the left hand rule for it's matrices, which is to say stick out your left hand and point the thumb up, point the index out and the middle finger across and that's the "positive" directions (as opposed to the right hand rule where the x+ is inverted). Bringing matrices across from elsewhere isn't always easy, they may assume Z is up (3D Max) or use the right handed rule (inverted x vector) etc. Rotation from Euler requires using the same rotation order as the source. If you go this route then you must make sure that you match rotation orders between applications.
  9. Firstly thing - There is no way to directly access and change the value of the dropdown for the render setting within the Batch Renderer with Python AFAIK. Which means that while it's not very efficient your approach is not terrible when dealing with a body of files, don't feel bad about it, if it works then that's more important than if it works efficiently. Now onto ways you could improve your code : For a start I don't see anything in your script accessing the batch renderer to get the file. So that's done first by using the "GetBatchRender" function, e.g. batchRender = c4d.documents.GetBatchRender() count = batchRender.GetElementCount() for i in range(count): fileName = batchRender.GetElement(i) This goes through the whole list, but perhaps you only want to deal with the first (top most, just about to render) document, in which case you wouldn't do the for loop but would just do fileName = batchRender.GetElement(0). Next, you can just load in and save the file more directly than using CallCommand using the c4d.document.LoadDocument(file, flags). myDoc = c4d.documents.LoadDocument(fileName, c4d.SCENEFILTER_PROGRESSALLOWED) if myDoc is None: return At this point you now have an opened document and can see the progress as the file is loaded in. Your next step is to switch out the render data in that document. Just use the code you already have for that, it's fine. You shouldn't need the EventAdd() function calls in there though. The final step is to re-save your document, this time rather than using CallCommand again you can do it directly. With c4d.document.SaveDocument(doc, file, flags, format) result = c4d.documents.SaveDocument(myDoc, fileName, c4d.SAVEDOCUMENTFLAGS_DONTADDTORECENTLIST, c4d.FORMAT_C4DEXPORT) if result is False: return At this point you should be able to start the batch render and be good to go. If you want you can do this programmatically again without having to use CallCommand by accessing the BatchRender object's SetRendering() function i.e. batchRender.SetRendering(c4d.BR_START) should do the job.
  • Create New...