Build Reliable Export Tools with Blender’s Depsgraph

Build Reliable Export Tools with Blender’s Depsgraph

Si vous écrivez un script Python pour exporter ou analyser une géométrie dans Blender, il est probablement en train de vous raconter des histoires. Vous récupérez obj.data, vous parcourez les sommets, et vous obtenez des nombres qui ne correspondent pas à ce que vous voyez dans la vue, ni à ce que produit le rendu. Le coupable est presque toujours le même : vous lisez les données de maillage d’origine avant que les modificateurs, les drivers et les contraintes aient été appliqués.

Mais Blender possède un système qui produit la version finale de chaque objet dans votre scène. Il s’appelle le graphe de dépendances, ou depsgraph. Cet article vous montre exactement comment y accéder, quoi en faire, et comment construire un outil d’export pratique que votre pipeline peut utiliser.

À la fin, vous disposerez d’un script Python fonctionnel qui parcourt tous les objets de type mesh dans votre scène, lit leur géométrie après modificateurs, et écrit un rapport CSV avec le nombre de sommets, le nombre de polygones et les dimensions dans l’espace monde. Ce rapport peut s’intégrer directement à une vérification de budget de polygones, à une étape de validation avant rendu, ou à un audit de transfert avant que vos assets ne passent à la prochaine équipe.

What's The Depsgraph

Le depsgraph de Blender (graphe de dépendances) est un système interne qui suit toutes les relations et dépendances entre les données d’une scène, comme les objets, modificateurs, contraintes, drivers, shape keys et animations, et les organise en un graphe acyclique dirigé, où chaque nœud représente un élément de données et chaque arête représente une dépendance.

Lorsque quelque chose change, par exemple déplacer un objet ou modifier une valeur, le depsgraph détermine le plus petit ensemble de choses qui doivent être recalculées et les met à jour dans le bon ordre afin d’éviter un travail redondant.

Le depsgraph rend l’évaluation de Blender à la fois correcte (les éléments se mettent toujours à jour dans la bonne séquence, de sorte qu’une contrainte dépendant d’un autre objet voit toujours la transformation à jour) et efficace (seules les parties modifiées du graphe sont réévaluées, ce qui est essentiel pour les performances dans des scènes complexes avec des centaines d’objets, des simulations et des drivers).

Il permet aussi des fonctionnalités comme CoW (Copy-on-Write) afin que la vue et le rendu puissent travailler avec des copies évaluées des données sans corrompre l’original, et il sous-tend l’évaluation multi-threads lorsque des branches indépendantes du graphe peuvent être traitées en parallèle.

Use Cases

Pour les directeurs techniques et les ingénieurs de pipeline dans les studios d’animation, le depsgraph peut être accédé directement via l’API Python de Blender pour débloquer des cas d’usage puissants de scripting.

Les TD peuvent récupérer des versions entièrement évaluées des objets pour exporter des données de maillage finales vers des moteurs de rendu externes ou des moteurs de jeu, sans avoir besoin de faire du baking manuellement.

Les studios peuvent aussi parcourir les instances d’objets évaluées afin de collecter chaque prop instancié par particules ou chaque asset dispersé par géométrie (geometry nodes) dans une scène, pour des pipelines d’export sur mesure ou des outils de reporting d’assets.

Le depsgraph peut détecter ce qui a changé entre les frames en écoutant les tags de mise à jour afin de prendre en charge des systèmes de cache intelligents qui ne réexportent ou ne retraitent que lorsque leurs dépendances ont réellement été modifiées ; un énorme gain de temps dans des pipelines de ferme de rendu longue durée.

1. Get a Reference to the Depsgraph

Le depsgraph est disponible via le contexte actuel. Dans l’espace de travail Scripting, ou à l’intérieur d’un operator, vous l’obtenez via la méthode evaluated_depsgraph_get :

import bpy

depsgraph = bpy.context.evaluated_depsgraph_get()

L’appel retourne un objet Depsgraph qui représente l’état entièrement évalué de votre scène à la frame courante.

Si votre script modifie la scène avant de lire les données évaluées, vous devez dire à Blender de recalculer :

depsgraph.update()

Sans cet appel, le depsgraph reflète l’état avant vos modifications. Et vous n’en avez pas besoin dans un script en lecture seule.

2. Get the Evaluated Version of Each Object

Avoir le depsgraph ne suffit pas à lui seul. Vous devez l’utiliser pour récupérer une copie évaluée de chaque objet. Cela se fait avec evaluated_get() :

obj = bpy.context.active_object
obj_eval = obj.evaluated_get(depsgraph)

L’appel evaluated_get() retourne une copie temporaire, évaluée de l’objet : ce n’est pas l’original ! Toute modification que vous y faites ne persiste pas une fois le script terminé. Vous ne faites que la lire.

Sur cet objet évalué, obj_eval.data est le maillage après modificateurs. Sur l’objet original, obj.data reste le maillage avant modificateurs. Mélanger les deux dans le même script produit des résultats incorrects sans message d’erreur, ce qui rend les bugs difficiles à détecter.

L’objet évalué vous donne aussi des données de transformation précises. obj_eval.matrix_world reflète les contraintes et les drivers, pas seulement les valeurs de transformation de base.

Lisez aussi la transformation depuis l’objet évalué si vous calculez des boîtes englobantes dans l’espace monde ou si vous construisez un exportateur qui nécessite des positions correctes.

3. Convert to a Mesh and Read the Data

L’objet évalué vous donne accès au maillage via to_mesh(). Il crée un bloc de données de maillage autonome sur lequel vous pouvez itérer :

mesh = obj_eval.to_mesh()

Ce n’est pas gratuit. Cela alloue de la mémoire, et vous êtes responsable de la libérer. Une fois la lecture terminée, appelez :

obj_eval.to_mesh_clear()

Ne pas appeler to_mesh_clear() provoque une fuite de mémoire dans votre session Blender. Dans une grande scène avec des centaines d’objets, cela s’accumule rapidement. Encapsulez toujours la lecture du maillage dans un bloc try/finally afin de garantir le nettoyage même en cas d’erreur :

mesh = obj_eval.to_mesh()
try:
    for poly in mesh.polygons:
        print(poly.area)
finally:
    obj_eval.to_mesh_clear()

Par défaut, to_mesh() peut supprimer les calques UV et les calques de couleurs de sommets pour des raisons de performance. Si votre script d’export en a besoin, passez le paramètre additionnel :

mesh = obj_eval.to_mesh(preserve_all_data_layers=True)

Le maillage résultant vous donne accès à mesh.vertices, mesh.polygons, mesh.loops, mesh.uv_layers et mesh.vertex_colors pour refléter la géométrie finale évaluée.

4. Build the Production Script

Regardons un script d’exemple complet. Il parcourt tous les objets de type mesh dans la scène active, lit leur géométrie évaluée, et écrit un rapport CSV dans votre répertoire personnel :

import bpy
import csv
import os

def get_evaluated_mesh_stats(context, output_path):
    depsgraph = context.evaluated_depsgraph_get()
    rows = []

    for obj in context.scene.objects:
        if obj.type != 'MESH':
            continue

        obj_eval = obj.evaluated_get(depsgraph)
        mesh = obj_eval.to_mesh()

        try:
            vert_count = len(mesh.vertices)
            poly_count = len(mesh.polygons)
            dims = obj_eval.dimensions

            rows.append({
                "name": obj.name,
                "verts": vert_count,
                "polys": poly_count,
                "dim_x": round(dims.x, 4),
                "dim_y": round(dims.y, 4),
                "dim_z": round(dims.z, 4),
            })
        finally:
            obj_eval.to_mesh_clear()

    with open(output_path, "w", newline="") as f:
        writer = csv.DictWriter(
            f,
            fieldnames=["name", "verts", "polys", "dim_x", "dim_y", "dim_z"]
        )
        writer.writeheader()
        writer.writerows(rows)

    print(f"Report written to {output_path}")


get_evaluated_mesh_stats(
    bpy.context,
    os.path.expanduser("~/mesh_report.csv")
)

Vous pouvez l’exécuter depuis l’espace de travail Scripting en le collant dans l’éditeur de texte et en appuyant sur Run Script. Le fichier de sortie est placé à ~/mesh_report.csv. Ouvrez-le dans n’importe quelle application de tableur.

Chaque ligne contient le nom de l’objet, le nombre de sommets et de polygones après modificateurs, ainsi que les dimensions de la boîte englobante dans l’espace monde en X, Y et Z. Les dimensions proviennent de obj_eval.dimensions, qui lit l’objet évalué et reflète donc l’intégralité de la pile de modificateurs.

Pour l’adapter à votre pipeline, vous pouvez filtrer par collection, ajouter un seuil de budget de polygones et signaler les objets qui le dépassent, ou remplacer la sortie CSV par une charge utile JSON que vous pourrez importer facilement dans un outil de gestion de projet ou une base de données d’assets.

Conclusion

Ce script d’exemple est un simple point de départ. Mais lorsque vous comprenez comment lire de manière fiable les données de maillage évaluées, le même schéma s’applique aux exportateurs, aux validateurs automatisés de LOD, aux générateurs de maillages de collision et aux audits de géométrie avant rendu. Les enseignements clés se transmettent à tous ces outils : lisez toujours depuis l’objet évalué, nettoyez toujours avec to_mesh_clear(), et ne mélangez jamais les données originales et évaluées dans la même opération.

La prochaine étape logique consiste à enregistrer cela comme gestionnaire d’enregistrement de fichier en utilisant bpy.app.handlers.save_pre, afin que le rapport se génère automatiquement chaque fois qu’un artiste enregistre la scène, pour l’assurance qualité, et afin de détecter les dépassements de budget de polygones avant qu’ils n’arrivent à l’étape de rendu.