Code Vonc

Convertir et exporter

Script Python pour Cinéma 4D R13.

Comment installer ?How to install ?
CommentairesComments

Voici une classe qui converti et regroupe tous les éléments d'un objet à exporter.

Le but est d'avoir aucune contrainte pour exporter, toutes les propriétés nécessaires à l'exportation (normales, uvw, ...) se créeront et se trieront automatiquement, notamment dans les cas complexes où plusieurs matériaux et dépliages UVW sont appliqués à un même objet. Même si les sélections de polygones se superposent, le ménage se fait tout seul et des tableaux tout propres définissent quel matériau et quel dépliage uv est affecté à tel polygone.


Cette classe, nommée Convertisseur, a 3 fonctions :

	execute(objs, triangle) : Fonction principale, converti le document et récupère ses données.
		objs : (booléen) si vrai, exporte les objets sélectionnés, sinon, exporte tous les objets du document.
		triangle : (booléen) converti ou non les quadrangles en triangles.

	normales_obj(index_obj) : Renvoie un tableau contenant les normales de chaque point de l'objet
	(le nombre total de points vaut le nombre de polygones * 4).
		index_obj : (entier) numéro de l'objet dans la liste objs_pol[index_obj]

	uvw_obj_pol(index_obj, index_pol, correction) : Renvoie les coordonnées UVW du polygone,
	avec ou sans les corrections définies dans la propriété Texture (comme l'échelle et le décalage).
		index_obj : (entier) numéro de l'objet dans la liste objs_pol[index_obj]
		index_pol : (entier) numéro du polygone
		correction : (booléen) si vrai, renvoie les coordonnées UVW avec l'échelle et le décalage de la propriété Texture pris en compte.



Après la fonction execute(), la classe contient les variables nécessaires à l'exportation, et peuvent donc être utilisées pour l'écriture du fichier.

Script


Voici le script en question qui créé un fichier contenant les données exportées dans une structure quelconque :


import c4d
import struct
from c4d import plugins, documents, utils, gui

MODULE_ID = 100001

class Convertisseur:
    document = None
    objs_pol =  [] # [] Objets polygonaux
    obj_mat = [] # [[]] Propriétés texture par objet
    obj_mat_pol = [] # [[]] Propriété texture (son index dans obj_mat[[]] ) par polygone par objet
    obj_mat_uvw = [] # [[]] Propriété UVW par propriété texture par objet
    
    objs_cam = [] # Caméras
    
    mats = [] # Matériaux du document
    
    # --- Initialise les variables ---
    def __init__(self):
        self.document = None
        self.objs_pol =  []
        self.obj_mat = []
        self.obj_mat_pol = []
        self.obj_mat_uvw = []
        self.objs_cam = []
        self.mats = []
    
    # --- Récupère les normales d'un objet dans la liste objs_pol ---
    def normales_obj(self, index_obj):
        obj = self.objs_pol[index_obj]
        if (obj is None): return False
        normale = obj.CreatePhongNormals()
        return normale
    
    # --- Récupère les coordonnées UVW d'un polygone d'un objet de objs_pol, avec ou non la correction de l'échelle de la propriété Texture ---
    def uvw_obj_pol(self, index_obj, index_pol, correction):
        index_mat = self.obj_mat_pol[index_obj][index_pol]
        prop_uvw = self.obj_mat_uvw[index_obj][index_mat]
        prop_mat = self.obj_mat[index_obj][index_mat]
        uvw = prop_uvw.GetSlow(index_pol)
        if (correction is True):
            facX = prop_mat[c4d.TEXTURETAG_TILESX]
            facY = prop_mat[c4d.TEXTURETAG_TILESY]
            decX = prop_mat[c4d.TEXTURETAG_OFFSETX]
            decY = prop_mat[c4d.TEXTURETAG_OFFSETY]
            uvw["a"].x *= facX
            uvw["a"].x += decX
            uvw["a"].y *= facY
            uvw["a"].y += decY
            uvw["b"].x *= facX
            uvw["b"].x += decX
            uvw["b"].y *= facY
            uvw["b"].y += decY
            uvw["c"].x *= facX
            uvw["c"].x += decX
            uvw["c"].y *= facY
            uvw["c"].y += decY
            uvw["d"].x *= facX
            uvw["d"].x += decX
            uvw["d"].y *= facY
            uvw["d"].y += decY
        return uvw
        
    # --- Fonction principale (document, objets à convertir(liste, ou nulle pour tout convertir)) ---
    def execute(self, objs, triangle):
        def objets_editables(docu, objs):
            commandeid = c4d.MCOMMAND_MAKEEDITABLE
            marque = c4d.MODELINGCOMMANDFLAGS_CREATEUNDO
            mode = c4d.MODELINGCOMMANDMODE_ALL
            converti = utils.SendModelingCommand(command=commandeid, list=objs, doc=docu, flags=marque, mode=mode)
            c4d.EventAdd()
            return converti
        
        doc =  c4d.documents.GetActiveDocument()
        
        if (objs is True):
            docu = doc.GetClone()
            objets_editables(docu, docu.GetActiveObjects(True))
        else: docu = doc.Polygonize(False)
        
        self.document = docu
        c4d.documents.SetActiveDocument(docu)
        
        retour = self.convertir(docu, objs, triangle)
        
        c4d.documents.SetActiveDocument(doc)
        return retour
    
    # --- Vide la mémoire ---
    def __del__(self):
        c4d.documents.KillDocument(self.document)
        c4d.StatusClear()
    
    def convertir(self, docu, objs, triangle):
        # -- Répertorie tous les objets si aucun n'est sélectionné --
        def repertorie_objs(docu):
            liste = []
            premObj = docu.GetFirstObject()
            if not premObj: return False
            def objetIteration(op):
                if not op: return liste
                liste.append(op)
                objetIteration(op.GetDown())
                objetIteration(op.GetNext())
            objetIteration(premObj)
            return liste
        
        objs_sel = objs # Objets sélectionnés
        if objs_sel is False: objs_sel = repertorie_objs(docu)
        elif objs_sel is True: objs_sel = docu.GetActiveObjects(True)
        if objs_sel is False: return False
        c4d.StatusSetSpin()
        
        # -- Trie les objets --
        def trie_objets(objs):
            polygonaux = []
            cameras = []
            for obj in objs:
                if (obj.GetType() == c4d.Opolygon): # 5100 Objet polygonal
                    polygonaux.append(obj)
                elif (obj.GetType() == c4d.Ocamera): # 5103 Caméra
                    cameras.append(obj)
            return polygonaux, cameras
        
        self.objs_pol, self.objs_cam = trie_objets(objs_sel)
        if (self.objs_pol is None): return False
        c4d.StatusSetSpin()
        
        # -- Triangule les objets --
        def triangule(docu, objs):
            commandeid = c4d.MCOMMAND_TRIANGULATE
            marque = c4d.MODELINGCOMMANDFLAGS_CREATEUNDO
            mode = c4d.MODELINGCOMMANDMODE_ALL
            converti = utils.SendModelingCommand(command=commandeid, list=objs, doc=docu, flags=marque, mode=mode)
            c4d.EventAdd()
            return converti
        
        if triangle is True:
            if triangule(docu, self.objs_pol) is False: return False
        c4d.StatusSetSpin()
        
        # -- Ajoute le matériau par défaut au doc --
        def materiau_def_doc(docu):
            matDef = c4d.BaseMaterial(c4d.Mmaterial)
            matDef.SetName("matpardef")
            docu.InsertMaterial(matDef)
            c4d.EventAdd()
        
        materiau_def_doc(docu)
        c4d.StatusSetSpin()
        
        # -- Répertorie les matériaux --
        self.mats = docu.GetMaterials()
        c4d.StatusSetSpin()
        
        # -- Traitement des propriétés des objets --
        def traite_props(objs, mats, docu):
            # - Ajoute la sélection de polygones par défaut -
            def selection_def(obj):
                selDef = c4d.BaseTag(c4d.Tpolygonselection)
                selDef.SetName(" ")
                obj.InsertTag(selDef)
                nbp = obj.GetPolygonCount()
                nb = [1]*nbp
                selDef.GetBaseSelect().SetAll(nb)
                c4d.EventAdd()
            
            # - Ajoute le matériau par défaut -
            def materiau_def(obj, mats):
                matDef = c4d.BaseTag(c4d.Ttexture)
                matDef[c4d.TEXTURETAG_MATERIAL] = mats[0]
                matDef[c4d.TEXTURETAG_RESTRICTION] = " "
                matDef[c4d.TEXTURETAG_PROJECTION] = 6
                obj.InsertTag(matDef)
                c4d.EventAdd()
            
            # - Ajoute une propriété UVW -
            def uvw_def(obj):
                uvwDef = c4d.BaseTag(c4d.Tuvw)
                obj.InsertTag(uvwDef)
            
            # - Converti en proj UVW -
            def proj_uvw(obj, docu):
                prop = obj.GetFirstTag()
                while prop:
                    propt = prop.GetType()
                    if ((propt != 5616) or (prop[c4d.TEXTURETAG_PROJECTION] == 6)):
                        prop = prop.GetNext()
                        continue
                    docu.SetActiveTag(prop)
                    c4d.CallCommand(12235) # Générer des coordonnées UVW
                    prop = prop.GetNext()
            
            # - Trouve la propriété Sélection de polygones par son nom -
            def trouve_sel(nomsel, obj):
                if (nomsel is None or nomsel == ""): nomsel = " "
                prop = obj.GetFirstTag()
                while prop:
                    if (prop.GetType() == 5673): # Propriété Sélection de polygones
                        if (prop[c4d.ID_BASELIST_NAME] == nomsel):
                            return prop
                    prop = prop.GetNext()
                return None
            
            # - Assigne quel matériau (son index dans obj_mats[[]] ) est associé à quel polygone
            def assigne_mat_par_pol(obj, li_sel):
                nbpol = obj.GetPolygonCount()
                li_matparpol = li_sel[0].GetBaseSelect().GetAll(nbpol)
                li_matparpol = [x * (len(li_sel)-1) for x in li_matparpol]
                for s in range(len(li_sel)):
                    sel = li_sel[s].GetBaseSelect().GetAll(nbpol)
                    for p in range(nbpol):
                        if (li_matparpol[p] == 0): li_matparpol[p] = sel[p] * (len(li_sel) - s-1)
                return li_matparpol
            
            # - Ajoute unr propriété Lissage -
            def phong_def(obj):
                phongDef = c4d.BaseTag(c4d.Tphong)
                phongDef[c4d.PHONGTAG_PHONG_ANGLELIMIT] = True
                phongDef[c4d.PHONGTAG_PHONG_ANGLE] = 0
                obj.InsertTag(phongDef)
            
            # - Trie les propriétés -
            def trie_props(obj):
                li_mat = []
                li_sel = []
                li_uvw = []
                li_mat_uvw = []
                phong = False
                props = obj.GetTags()
                props.reverse()
                for prop in props:
                    propt = prop.GetType()
                    if (propt == 5612): # Propriété Lissage
                        phong = True
                    elif (propt == 5671): # Propriété UVW
                        li_uvw.append(prop)
                    elif (propt == 5616): # Propriété Texture
                        li_mat.append(prop)
                        nomsel = prop[c4d.TEXTURETAG_RESTRICTION]
                        sel = trouve_sel(nomsel, obj)
                        li_sel.append(sel)
                        if (len(li_uvw) == 0): li_mat_uvw.append(None)
                        else: li_mat_uvw.append(li_uvw[len(li_uvw)-1])
                li_mat.reverse()
                self.obj_mat.append(li_mat)
                li_matparpol = assigne_mat_par_pol(obj, li_sel)
                self.obj_mat_pol.append(li_matparpol)
                if (len(li_uvw) != 0):
                    for x in range(len(li_mat_uvw)):
                        if li_mat_uvw[x] is None: li_mat_uvw[x] = li_uvw[0]
                li_mat_uvw.reverse()
                self.obj_mat_uvw.append(li_mat_uvw)
                if (phong is False): phong_def(obj)
            
            for obj in objs:
                selection_def(obj)
                materiau_def(obj, mats)
                proj_uvw(obj, docu)
                trie_props(obj)
                c4d.StatusSetSpin()
            
        traite_props(self.objs_pol, self.mats, docu)
        
        
        return True

class Dialogue(gui.GeDialog):
    annuler = False
    triangle = False
    selection = False
    utf8 = False
    
    def CreateLayout(self):
        self.SetTitle("Paramètres")
        self.AddCheckbox(10, c4d.BFH_SCALEFIT, 0, 0, 'Trianguler les polygones')
        self.AddCheckbox(11, c4d.BFH_SCALEFIT, 0, 0, 'Exporter la sélection')
        self.AddSeparatorV(20)
        self.AddCheckbox(12, c4d.BFH_SCALEFIT, 0, 0, 'Encoder le chemin et le nom du fichier en UTF-8')
        self.AddSeparatorV(21)
        self.AddDlgGroup(c4d.DLG_OK|c4d.DLG_CANCEL)
        
        if (len(doc.GetActiveObjects(True)) > 0): self.SetBool(11, True)
        
        return True
    
    def Command(self, id, msg):
        if (id == 1):
            self.annuler = False
            self.triangle = self.GetBool(10)
            self.selection = self.GetBool(11)
            self.utf8 = self.GetBool(12)
            self.Close()
        if (id == 2):
            self.annuler = True
            self.Close()
        return True

def exporte():
    dial = Dialogue()
    dial.Open(c4d.DLG_TYPE_MODAL, MODULE_ID, -1, -1)
    if (dial.annuler is True): return False
    
    converti = Convertisseur()
    etat_conv = converti.execute(dial.selection, dial.triangle)
    if (etat_conv is not True): return "Erreur de conversion"
    
    def ecriture():
        chemin = c4d.storage.SaveDialog()
        if chemin is None: return False
        
        if dial.utf8 is True:
            chemin = chemin.decode('UTF-8', 'strict')
        
        try:
            f = open(chemin, "wb") # "w" pour texte, "wb" pour binaire
        except IOError:
            return "Erreur d'ouverture : le fichier est peut-être protégé en écriture, ou le dossier de destination est introuvable ; essayez en cochant l'encodage UTF-8."
        
        f.write(struct.pack('BBB', 0xEF, 0xBB, 0xBF)) # Signature UTF-8
        for o in range(len(converti.objs_pol)):
            obj = converti.objs_pol[o]
            nbp = obj.GetPolygonCount()
            f.write(obj.GetName())
            f.write("\n\t" + str(nbp) + " polygones")
            norm = converti.normales_obj(o)
            mat = converti.obj_mat_pol[o]
            tex = converti.obj_mat[o]
            c4d.StatusSetSpin()
            for p in range(nbp):
                pa = obj.GetPolygon(p).a
                pb = obj.GetPolygon(p).b
                pc = obj.GetPolygon(p).c
                pd = obj.GetPolygon(p).d
                puvw = converti.uvw_obj_pol(o, p, True)
                pmatt = mat[p]
                pmat = tex[pmatt]
                pmatn = pmat[c4d.TEXTURETAG_MATERIAL].GetName()
                f.write("\n\t\t" + str(p) + ", " + pmatn)
                f.write("\n\t\t\t" + "a-" + str(pa) + ", XYZ" + str(obj.GetPoint(pa))[6:] + ", UVW" + str(puvw["a"])[6:] + ", Normale" + str(norm[p*4])[6:] )
                f.write("\n\t\t\t" + "b-" + str(pb) + ", XYZ" + str(obj.GetPoint(pb))[6:] + ", UVW" + str(puvw["b"])[6:] + ", Normale" + str(norm[p*4+1])[6:] )
                f.write("\n\t\t\t" + "c-" + str(pc) + ", XYZ" + str(obj.GetPoint(pc))[6:] + ", UVW" + str(puvw["c"])[6:] + ", Normale" + str(norm[p*4+2])[6:] )
                if (dial.triangle is False and pc != pd):
                    f.write("\n\t\t\t" + "d-" + str(pd) + ", XYZ" + str(obj.GetPoint(pd))[6:] + ", UVW" + str(puvw["d"])[6:] + ", Normale" + str(norm[p*4+3])[6:] )
            f.write("\n\n")
        
        try:
            f.close()
        except IOError, e:
            return "Erreur de fermeture : le fichier est peut-être protégé en écriture."
        
        return True
    
    etat = ecriture()
    if (etat is not True and etat is not False): gui.MessageDialog(etat)

def main():
    exporte()

if __name__=='__main__':
    main()



Sortie


Exemple de sortie avec un cube :


Cube
    6 polygones
        0, matpardef
            a-0, XYZ(-100, -100, -100), UVW(0, 1, 0), Normale(0, 0, -1)
            b-1, XYZ(-100, 100, -100), UVW(0, 0, 0), Normale(0, 0, -1)
            c-3, XYZ(100, 100, -100), UVW(1, 0, 0), Normale(0, 0, -1)
            d-2, XYZ(100, -100, -100), UVW(1, 1, 0), Normale(0, 0, -1)
        1, matpardef
            a-2, XYZ(100, -100, -100), UVW(0, 1, 0), Normale(1, 0, 0)
            b-3, XYZ(100, 100, -100), UVW(0, 0, 0), Normale(1, 0, 0)
            c-5, XYZ(100, 100, 100), UVW(1, 0, 0), Normale(1, 0, 0)
            d-4, XYZ(100, -100, 100), UVW(1, 1, 0), Normale(1, 0, 0)
        2, matpardef
            a-4, XYZ(100, -100, 100), UVW(0, 1, 0), Normale(0, 0, 1)
            b-5, XYZ(100, 100, 100), UVW(0, 0, 0), Normale(0, 0, 1)
            c-7, XYZ(-100, 100, 100), UVW(1, 0, 0), Normale(0, 0, 1)
            d-6, XYZ(-100, -100, 100), UVW(1, 1, 0), Normale(0, 0, 1)
        3, matpardef
            a-6, XYZ(-100, -100, 100), UVW(0, 1, 0), Normale(-1, 0, 0)
            b-7, XYZ(-100, 100, 100), UVW(0, 0, 0), Normale(-1, 0, 0)
            c-1, XYZ(-100, 100, -100), UVW(1, 0, 0), Normale(-1, 0, 0)
            d-0, XYZ(-100, -100, -100), UVW(1, 1, 0), Normale(-1, 0, 0)
        4, matpardef
            a-1, XYZ(-100, 100, -100), UVW(0, 1, 0), Normale(0, 1, 0)
            b-7, XYZ(-100, 100, 100), UVW(0, 0, 0), Normale(0, 1, 0)
            c-5, XYZ(100, 100, 100), UVW(1, 0, 0), Normale(0, 1, 0)
            d-3, XYZ(100, 100, -100), UVW(1, 1, 0), Normale(0, 1, 0)
        5, matpardef
            a-6, XYZ(-100, -100, 100), UVW(0, 1, 0), Normale(0, -1, 0)
            b-0, XYZ(-100, -100, -100), UVW(0, 0, 0), Normale(0, -1, 0)
            c-2, XYZ(100, -100, -100), UVW(1, 0, 0), Normale(0, -1, 0)
            d-4, XYZ(100, -100, 100), UVW(1, 1, 0), Normale(0, -1, 0)