Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Problem with Xref script

    Scheduled Pinned Locked Moved PYTHON Development
    8 Posts 0 Posters 978 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H Offline
      Helper
      last edited by

      On 22/11/2017 at 05:51, xxxxxxxx wrote:

      Hi,
      i currently try to write a script that takes all top-level hierarchy elements and converts them into Xrefs, just like the 'Convert the selection to Xref', when right-clicking an object.
      I found out that the 'Convert selection to Xref' option is a plugin that if used in code, also opens up the save dialog.

      Is there a way to achieve the same functionality without opening the user dialog? Or is it possible to interact with the save dialog so that the user does not need to click anything during the process ?

      In my current script, i do not use the Pluging. My current script copies all objects into seperate c4d scenes and saves them, and creates Xrefs for all of them. Unfortunetaly, i did not consider the materials since i wrote the script using simple cubes.. So this script does not work as intended.

      Here it is:
      import os
      import c4d
      from c4d import gui
      from c4d import documents
      from c4d import storage
      from c4d import plugins

      #An iterator, taking the first top level element of a hierarchy and iterating as long as elements are left
      class ItemIterator:
          def __init__(self, firstObject) :
              self.firstObject = firstObject
              self.currentObject = firstObject
          
          def __iter__(self) :
              return self
          
          def next(self) :
              if self.currentObject == None:
                  raise StopIteration
              else:
                  toReturn = self.currentObject
                  self.currentObject = self.currentObject.GetNext()
                  return toReturn

      #Identifiers for the layout
      BUTTON_CONVERT = 1
      BUTTON_CANCEL = 2
      BUTTON_GROUP = 3
      TEXT_FIRST_LINE = 4
      TEXT_SECOND_LINE = 5

      #A class containing the GUI of the save dialog
      class SaveDialog(gui.GeDialog) :
          #Creates the Layout of the GUI when invoked
          def CreateLayout(self) :
              self.SetTitle("Conversion to XRef")
              self.AddStaticText(TEXT_FIRST_LINE, c4d.BFH_CENTER, name = "All top level objects will be saved into separate scenes.\n")
              self.AddStaticText(TEXT_SECOND_LINE, c4d.BFH_CENTER, name = "A new scene containing all these objects as XRefs will be created\n")
              self.GroupBegin(BUTTON_GROUP, c4d.BFH_CENTER, 2, 1)
              self.AddButton(BUTTON_CONVERT, c4d.BFH_SCALE, name = "Convert")
              self.AddButton(BUTTON_CANCEL, c4d.BFH_SCALE, name = "Cancel")
              self.GroupEnd()
              self.ok = False
              return True
          
          #Invokes the suitable function for the cancel and the convert button of the layout.
          def Command(self, id, msg) :
              if id == BUTTON_CANCEL:
                  self.Close()
              if id == BUTTON_CONVERT:
                  convert()
                  self.Close()
              return True

      def convert() :
          activeDocument = documents.GetActiveDocument()
          documentPath = activeDocument[c4d.DOCUMENT_PATH]
          #Check whether the current scene is saved, so that a relative path can be used for the xrefs and the new scene.
          if len(documentPath) == 0:
              gui.MessageDialog("Please save the actual project, so that the xrefs are saved to the appropriate position")
              return
          
          #Generate the path for the xrefs, and check whether a folder already exists. If so tell the user to move/delete.
          #This way no merging of data has to be performed.
          saveDirectory = os.path.join(documentPath, "xrefs")
          if not os.path.exists(saveDirectory) :
              os.makedirs(saveDirectory)
          else:
              gui.MessageDialog("There is already an 'xref' folder in existence. Please delete or move it.")
              return
          firstObject = activeDocument.GetFirstObject()
          itemIterator = ItemIterator(firstObject)
          finalXrefDocument = c4d.documents.BaseDocument()

      counter = 0
          generatedDocuments = []
          for item in itemIterator:
              #Create new scene and insert a copy of the item into it.
              generatedDocuments.append(c4d.documents.BaseDocument())
              print counter
              for material in activeDocument.GetMaterials() :
                  generatedDocuments[counter].InsertMaterial(material)
              generatedDocuments[counter].SetDocumentName(item.GetName())
              documentName = generatedDocuments[counter].GetDocumentName() + ".c4d"
              itemClone = item.GetClone()
              generatedDocuments[counter].InsertObject(itemClone)
              #Generate the complete part to where to save the scene.
              completePath = os.path.join(saveDirectory, documentName)
              c4d.documents.SaveDocument(generatedDocuments[counter], completePath, c4d.SAVEDOCUMENTFLAGS_DONTADDTORECENTLIST, c4d.FORMAT_C4DEXPORT)
              #Construct the xref in the final scene.
              op = c4d.BaseObject(c4d.Oxref)
              finalXrefDocument.InsertObject(op)
              #Set the name and the reference file of the xref.
              op.SetParameter(c4d.ID_CA_XREF_FILE, completePath, c4d.DESCFLAGS_SET_USERINTERACTION)
              op.SetName(item.GetName())
              counter = counter + 1
              
          #Save the scene containing the xrefs.
          convertedScenePath =  os.path.join(documentPath, "ConvertedScene.c4d")
          documents.SaveDocument(finalXrefDocument, convertedScenePath, c4d.SAVEDOCUMENTFLAGS_0,  c4d.FORMAT_C4DEXPORT) 
          documents.InsertBaseDocument(finalXrefDocument)
          
          #Working, but stupid, fix.
          #Iterate through the new scene.
          newActiveDocument = documents.GetActiveDocument()
          newFirstObject = newActiveDocument.GetFirstObject()
          newItemIterator = ItemIterator(newFirstObject)
          
          #Rename the elements and Undo the Change afterwards, so they become editable.
          newActiveDocument.StartUndo()
          
          for newItem in newItemIterator:
              newActiveDocument.AddUndo(c4d.UNDOTYPE_CHANGE, newItem)
              newItem.SetName("BLAA")
              
          newActiveDocument.EndUndo()
          newActiveDocument.DoUndo()
          
          c4d.EventAdd()
          gui.MessageDialog("Conversion successful.")
                
      if __name__=='__main__':
          #Open up the save dialog.
          saveDialog = SaveDialog()
          saveDialog.Open(c4d.DLG_TYPE_MODAL, defaultw = 300)

      Any help would be appreciated!

      1 Reply Last reply Reply Quote 0
      • H Offline
        Helper
        last edited by

        On 23/11/2017 at 02:35, xxxxxxxx wrote:

        I solved the problem with the textures by now.
        There are two problems remaining:
        1. In the complete scene with all the xrefs, all materials from all external scenes are gathered, resulting in some materials that are the same, but only differently named. Is there an easy way to merge them together while apprporiately renaming all objects referring to them?
        2. All elements in the new scene are not editable, everything is greyed out. How do i change this ?

        1 Reply Last reply Reply Quote 0
        • H Offline
          Helper
          last edited by

          On 23/11/2017 at 07:28, xxxxxxxx wrote:

          Hello and welcome here ! 
          About your first ask you can compare two materials together using  BaseMaterial.Compare, so you can iterate all yours materials, then check if one is similar to another one. If yes replace/merge those assigment Example for get since assigment is jsut an c4d.InExcludeData

          About 2, I Don't know I guess it's a special bit, to remove but don't know which one.

          1 Reply Last reply Reply Quote 0
          • H Offline
            Helper
            last edited by

            On 23/11/2017 at 08:17, xxxxxxxx wrote:

            Thanks for the link, this way i think i am able to solve the material problem.

            To my second problem, i can update this. I am able to enable the reload option of the xref by renaming the xref, followed by an undo of the rename action. Kind of a dirty fix, but it does the job.

            The problem now is, that if i edit the external xref and hit reload in the master scene, nothing happens. I first have to check the generator option on the xref, and then do the reload to get the changes into the master scene. Afterwards, the generator option can be turned off, and everything works fine for further reloads.

            Any idea ?

            And thanks for the welcome 🙂

            1 Reply Last reply Reply Quote 0
            • H Offline
              Helper
              last edited by

              On 23/11/2017 at 11:21, xxxxxxxx wrote:

              Hi,

              welcome to the Plugin Café forums 🙂

              Regarding use of 'Convert selection to Xref' commands: Using commands via CallCommand() is basically identical to the user calling the command from GUI. There's unfortunately no way to avoid requesters popping up in this way.

              So your approach to re-implement the behavior is correct.

              On question 1:
              There is a command "Remove Duplicate Materials" which would normally achieve this for you. But with Xref'ed materials this unfortunately does not work. So once more you'd have to come up with a custom option.

              On question 2:
              This is a matter of the options of the Xref (as user click "Options..." on Xref's Object Properties tab, there under "Modify"). I'm still looking into it, if it is possible to set these options via code. I will get back to you on this, no promises made, though.

              1 Reply Last reply Reply Quote 0
              • H Offline
                Helper
                last edited by

                On 24/11/2017 at 01:22, xxxxxxxx wrote:

                Regarding removing the materials: What does the Compare function on Materials actually compare ? I have two materials which only differ in their name, but the compare functions outputs false. I am i doing something wrong? :

                (Update, i now do the comparison based on the suffix of the Material, since same materials will have the same suffix: objectA::Material, objectB::Material). Nonetheless i hope that i can find a clean solution for it.

                removeList = []
                    materialList = finalXrefDocument.GetMaterials()
                    for i in range(len(materialList)) :
                        for j in range(i + 1, len(materialList)) :
                            if materialList .Compare(materialList[j]) :
                _                ObjLink = materialList[j][c4d.ID_MATERIALASSIGNMENTS]_
                _                print "ObjLink"_
                _                objectCount = ObjLink.GetObjectCount()_
                _                for k in xrange(objectCount) :_
                _                     tag = ObjLink.ObjectFromIndex(finalXrefDocument,k)_
                _                     tag[c4d.TEXTURETAG_MATERIAL] = materialList ___
                _ _                removeList.append(materialList[j])__
                _ _    __
                _ _    for itemToRemove in removeList:__
                _ _        itemToRemove.Remove()__
                _ _        c4d.EventAdd()__
                __
                _ Another issue: To actually use the Xref properly, i found out that i have to check and uncheck the generator option one time on the xref, and hit the modify parameters in the xrefs option. Afterwards i can edit the external scene, and the "reload" button is working properly, and i can also edit the actual objects in the master scene. It would therefore be useful if could perform this programatically._
                _ Also, is there are some things i have to do during the process so a correct new document is generated. I would be nice to know if they are actually needed, or if i am doing something wrong/too complex:_
                _ 1. after copying the object into a new document, i have to get the materials needed in the object, clone them and link them again correctly_
                _ 2. if an instance object is used, i have to set the reference object of the instance object again in the document_
                __
                _ Thanks for any assistance!_


                1 Reply Last reply Reply Quote 0
                • H Offline
                  Helper
                  last edited by

                  On 24/11/2017 at 08:35, xxxxxxxx wrote:

                  So all questions are sorted, except for the "Modify Parameters" Option of the Xrefs, which i still want to set programatically.

                  1 Reply Last reply Reply Quote 0
                  • H Offline
                    Helper
                    last edited by

                    On 28/11/2017 at 09:10, xxxxxxxx wrote:

                    Hi,

                    sorry, it took it a bit longer.
                    The material comparison is actually comparing the materials in depth, among other things the BaseContainer content (aka parameters). Unfortunately these "other things" cause it to fail for Xref'ed materials.

                    For the last question I have no good news. There's no way to change the options of an Xref via the public APIs.

                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post