Faisons une animation 3D uniquement avec Python sans utiliser Blender! [FBX SDK Python]

20191201_moving_circle_cube.gif

Ceci est l'article du 7ème jour du Calendrier de l'Avent Python 2019. Hier, c'était @ ko-he-8 Introduction d'options pour la bibliothèque de tests unitaires python Nose-19 types-.

Je veux faire une animation 3D avec juste du code Python

Avec un logiciel d'animation 3D comme Blender, vous pouvez créer des fichiers d'animation 3D avec une interface graphique pratique!

alors! Si vous êtes un créateur / animateur, utilisez Blender! !! (Eh bien, Maya va bien? Alors allez voir Maya)

Pourtant! Je pense que la plupart des lecteurs de Qiita sont des ** programmeurs **!

** Si vous êtes programmeur, vous souhaitez créer des animations 3D par programmation! ** **

Non, cela doit être fait par programmation!

Pour vous, il y a ** FBX SDK Python **!

Si vous avez ça! ** Vous pouvez générer un fichier d'animation 3D FBX avec votre Python préféré! ** **

e? Vous n'aimez pas Python? Unity peut-il créer FBX avec C #?

・ ・ ・

** Maintenant, commençons! ** **

L'objectif de cette fois

Le but de cette fois est de créer une animation qui fait bouger le cube en cercle comme indiqué ci-dessous!

20191201_moving_circle_cube.gif

Préparation: installer FBX SDK Python

Tout d'abord, installons le FBX SDK Python!

J'ai écrit quelques articles avant, alors préparez-vous en fonction de votre propre environnement.

Déplaçons l'exemple de code pour le moment

Tout d'abord, déplaçons en fait l'exemple de code!

J'ai mis l'exemple de code ci-dessous, veuillez donc le télécharger.

GitHub / segurvita / fbx_sdk_python_sample

Après le téléchargement, appuyez sur la commande suivante!

python generate_fbx/circle_anim.py resources/cube_ascii.fbx resources/moving_circle_cube_ascii.fbx

Vous devriez avoir créé un fichier appelé moving_circle_cube_ascii.fbx dans le dossier resources.

C'est le livrable de cette fois!

Si vous l'ouvrez avec le logiciel Autodesk FBX Review, vous pouvez voir le cube se déplacer dans un cercle comme la vidéo précédente. Je vais! S'il vous plaît essayez!

Explication de la commande

Eh bien, je n'ai pas encore expliqué Python.

Tout d'abord, je vais expliquer à partir de la commande suivante.

python generate_fbx/circle_anim.py resources/cube_ascii.fbx resources/moving_circle_cube_ascii.fbx

generate_fbx / circle_anim.py est le code source Python utilisé cette fois. Ensuite, il y a deux arguments. C'est chacun

--Fichier d'entrée: resources / cube_ascii.fbx

--Fichier de sortie: resources / moving_circle_cube_ascii.fbx

Il est devenu.

Commentaire Python

Ensuite, jetons un œil à generate_fbx / circle_anim.py!

L'image entière ressemble à ceci!

circle_anim.py


import sys
import math
from fbx import *

fps = 30.0
rps = 0.5

def generate_anim_stack(scene):
    #une fonction
def generate_anim_layer(scene, anim_stack, node):
    #une fonction
def prot_circle(degree, time, curve_t_x, curve_t_y, curve_r_z):
    #une fonction
def move_circle(node, anim_base_layer):
    #une fonction
def get_ascii_format_id(manager):
    #une fonction
def main(obj_path, fbx_path):
    #une fonction

if __name__ == '__main__':
    # get argument
    args = sys.argv

    if len(args) < 2:
        print('Arguments are too short')
    else:
        main(args[1], args[2])

Il y a beaucoup de fonctions, donc cette fois je n'expliquerai que la partie liée à l'animation 3D.

Jetez un œil à la fonction principale

La première est la fonction principale. La partie liée à l'animation est comme ça.

def main(obj_path, fbx_path):
    #Créer diverses instances
    manager = FbxManager.Create()
    scene = FbxScene.Create(manager, "fbxScene")
    importer = FbxImporter.Create(manager, "")
    exporter = FbxExporter.Create(manager, "")

    #Chargez le fichier dans la scène
    importer.Initialize(obj_path, -1)
    importer.Import(scene)

    #Créer une pile d'animation dans la scène
    anim_stack = generate_anim_stack(scene)

    #Obtenez le nœud racine (le nœud le plus élevé de la scène)
    root_node = scene.GetRootNode()
    
    #S'il y a un nœud racine
    if (root_node):
        #Boucle par le nombre de nœuds enfants directement sous la racine
        for i in range(root_node.GetChildCount()):
            #Obtenir un nœud enfant
            node = root_node.GetChild(i)

            #Créer un calque d'animation
            anim_base_layer = generate_anim_layer(scene, anim_stack, node)

            #Créer une courbe d'animation
            move_circle(node, anim_base_layer)

    #Ecrire la scène dans un fichier
    exporter.Initialize(fbx_path, get_ascii_format_id(manager))
    exporter.Export(scene)

    #Détruire l'instance
    exporter.Destroy()
    importer.Destroy()
    scene.Destroy()
    manager.Destroy()

Il existe des termes inconnus.

Scène: Scène

Une scène est comme l'énorme espace dont dispose un fichier FBX.

Diverses données telles que des animations et des maillages sont stockées dans cette scène.

(Blender et Unity ont également le concept de scènes, mais vous pouvez utiliser presque les mêmes images que celles-ci!)

Vous pouvez le générer avec ce code.

#Faire une scène
scene = FbxScene.Create(manager, "fbxScene")

Nœud: Nœud

Il existe différentes données dans la scène. Chacun est un nœud.

Les nœuds ont une relation parent-enfant.

Par exemple, tout le monde, essayez de tourner les épaules.

Lorsque vous tournez les épaules, la position de vos coudes change, non?

En d'autres termes, le ** nœud du coude est un enfant du nœud de l'épaule **. Les nœuds enfants sont affectés par le nœud parent.

Au contraire, tourner uniquement le coude ne change pas la position de l'épaule, non?

Le nœud parent n'est pas affecté par le nœud enfant.

Dans le cas de FBX, les articulations telles que les coudes et les épaules sont également des nœuds, et les animations et les maillages sont tous des nœuds.

Nœud racine: nœud racine

Le nœud racine est la destination finale lorsque vous suivez les parents d'un tel nœud.

C'est le nœud supérieur de la scène!

Vous pouvez l'obtenir avec ce code.

#Obtenez le nœud racine (le nœud le plus élevé de la scène)
root_node = scene.GetRootNode()

Vous pouvez ordonner les nœuds enfants en écrivant comme suit.

#Boucle par le nombre de nœuds enfants directement sous la racine
for i in range(root_node.GetChildCount()):
    #Obtenir un nœud enfant
    node = root_node.GetChild(i)

Créer une pile d'animation

Cela devient de plus en plus difficile ...

Une pile d'animation est un nœud qui organise les données liées à l'animation.

Vous ne pouvez pas animer sans lui!

Pour le moment, faisons-en un.

Dans l'exemple de code, il est créé par une fonction appelée generate_anim_stack.

def generate_anim_stack(scene):
    #Créer une pile d'animation
    anim_stack = FbxAnimStack.Create(scene, "stack")

    return anim_stack

Vous pouvez le faire avec ça!

Faisons un calque d'animation

Un autre terme mystérieux ... Je vais vous expliquer.

Il y a différents mouvements dans l'animation 3D, non?

Courir / marcher / sauter ...

Chacun de ces mouvements est appelé ** couche d'animation ** dans la terminologie FBX. (Je suis désolé si j'ai fait une erreur)

Cette fois, nous allons simplement faire un mouvement circulaire, alors faisons-en un pour le moment!

def generate_anim_layer(scene, anim_stack, node):
    #Obtenez le nom du nœud (cube)
    node_name = node.GetName()

    #Définissez la position et la rotation du nœud (cube) sur 0
    node.LclTranslation.Set(FbxDouble3(0.0, 0.0, 0.0))
    node.LclRotation.Set(FbxDouble3(0.0, 0.0, 0.0))

    #Créez un calque d'animation (nommez les nœuds afin qu'ils puissent être distingués)
    anim_base_layer = FbxAnimLayer.Create(scene, "layer_" + node_name)
    
    #Ajouter une couche d'animation sous la pile d'animations
    anim_stack.AddMember(anim_base_layer)

    #Créer un nœud de courbe d'animation
    anim_curve_node = node.LclTranslation.GetCurveNode(
        anim_base_layer, True
    )
    
    #Renvoyer le calque d'animation
    return anim_base_layer

Vous pouvez maintenant créer un calque d'animation!

・ ・ ・

D'une manière ou d'une autre, le mystérieux terme de nœud de courbe d'animation est apparu sur le chemin ...

Il s'agit des données nécessaires pour animer les informations sur la position et la rotation d'un nœud (dans ce cas, un cube). J'en ai besoin pour le moment, alors faisons-le. (Je ne comprends pas vraiment non plus. Je suis désolé.)

Faisons une courbe d'animation

Ahh! Un autre terme mystérieux!

Pour dire la vérité, si vous animez un nœud (cube),

Il existe diverses animations, non?

Par exemple, si vous souhaitez effectuer un mouvement circulaire comme cette fois,

--Déplacez la coordonnée x de la position du nœud --Déplacez la coordonnée y de la position du nœud --Déplacez la coordonnée z dans le sens de rotation du nœud

Vous déplacerez trois coordonnées comme ceci.

Chacun de ces éléments est une ** courbe d'animation **!

(Le concept est presque le même que la courbe d'animation de Blender et Unity)

Cette fois, il y a trois courbes d'animation!

Voici donc l'exemple de code.

def move_circle(node, anim_base_layer):
    #Créer une courbe d'animation de la coordonnée x de la position du nœud
    curve_t_x = node.LclTranslation.GetCurve(
        anim_base_layer, "X", True
    )
    
    #Créer une courbe d'animation de la coordonnée y de la position du nœud
    curve_t_y = node.LclTranslation.GetCurve(
        anim_base_layer, "Y", True
    )
    
    #Créer une courbe d'animation de la coordonnée z dans le sens de rotation du nœud
    curve_r_z = node.LclRotation.GetCurve(
        anim_base_layer, "Z", True
    )

    #Préparer les variables pour l'enregistrement du temps
    time = FbxTime()

    #Démarrer l'enregistrement de la courbe d'animation
    curve_t_x.KeyModifyBegin()
    curve_t_y.KeyModifyBegin()
    curve_r_z.KeyModifyBegin()

    # fps =30 (30 images en 1 seconde)
    # rps = 0.5 (demi-cercle en 1 seconde)
    # fps/rps =60 (1 tour de mouvement circulaire en 60 images)
    #Traitez 60 images image par image.
    for frame in range(int(fps / rps)):
        #Calculez le temps écoulé. Nombre de cadres/fps
        sec = float(frame) / fps
        
        #Enregistrer le temps écoulé
        time.SetSecondDouble(sec)
        
        #Calculez l'angle de rotation. rps x temps écoulé x 360 °
        degree = rps * sec * 360.0
        
        #Tracez la valeur en fonction de l'angle de rotation sur la courbe d'animation
        prot_circle(degree, time, curve_t_x, curve_t_y, curve_r_z)

    #Terminer l'enregistrement de la courbe d'animation
    curve_t_x.KeyModifyEnd()
    curve_t_y.KeyModifyEnd()
    curve_r_z.KeyModifyEnd()

Il existe différentes formules de calcul détaillées, mais vous pouvez voir que le temps écoulé est enregistré pour chaque image et que l'angle de rotation du mouvement circulaire est calculé.

L'endroit où enregistrer réellement la valeur dans la courbe d'animation est décrit dans la fonction prot_circle, voyons-le donc.

Ajouter une clé à la courbe d'animation

Enregistrer des valeurs sur une courbe d'animation revient à ** ajouter une clé **.

Vous trouverez ci-dessous le code du processus d'ajout de clé.

def prot_circle(degree, time, curve_t_x, curve_t_y, curve_r_z):
    #Convertir l'angle de rotation en radian
    radian = math.radians(degree)

    #La coordonnée x de la position est cos(radian)Calculer avec et ajouter la clé
    key_index, key_last = curve_t_x.KeyAdd(time)
    curve_t_x.KeySet(
        key_index, time, math.cos(radian), FbxAnimCurveDef.eInterpolationLinear
    )

    #La coordonnée y de la position est sin(radian)Calculer avec et ajouter la clé
    key_index, key_last = curve_t_y.KeyAdd(time)
    curve_t_y.KeySet(
        key_index, time, math.sin(radian), FbxAnimCurveDef.eInterpolationLinear
    )

    #Ajouter une clé tout en conservant la coordonnée z dans le sens de rotation comme une fréquence au lieu d'un radian
    key_index, key_last = curve_r_z.KeyAdd(time)
    curve_r_z.KeySet(
        key_index, time, degree, FbxAnimCurveDef.eInterpolationLinear
    )

En appelant cette fonction image par image, des clés seront ajoutées image par image aux trois courbes d'animation.

C'est là que la clé est ajoutée.

key_index, key_last = curve_t_x.KeyAdd(time)

Cela ajoutera la clé.

Pour ceux qui ne connaissent pas Python, juste au cas où, il y a deux valeurs de retour pour la fonction KeyAdd, qui est le code attribué respectivement à key_index et key_last.

Ecrire deux variables côte à côte de cette manière est parfois lu comme un taple.

Cette fois, le numéro de clé est attribué à «key_index». Je n'utilise pas key_last, alors ignorez-le.

Le code suivant définit la valeur réelle de cet «index_clé».

curve_t_x.KeySet(
    key_index, time, math.cos(radian), FbxAnimCurveDef.eInterpolationLinear
)

Cela conclut l'explication de Python! Merci pour votre soutien!

à la fin

Merci pour la lecture.

Vous pouvez maintenant créer des animations 3D avec seulement Python, sans utiliser Blender! !!

En créant cet article, je me suis référé aux pages suivantes.

Recommended Posts

Faisons une animation 3D uniquement avec Python sans utiliser Blender! [FBX SDK Python]
Faisons une interface graphique avec python.
Faisons une discussion WEB en utilisant WebSocket avec AWS sans serveur (Python)!
Faisons un jeu de shiritori avec Python
Travailler avec OpenStack à l'aide du SDK Python
Faisons la voix lentement avec Python
Créez un framework Web avec Python! (1)
Faisons un bot Twitter avec Python!
Créez un framework Web avec Python! (2)
[Blender x Python] Commençons avec Blender Python !!
Faisons un module pour Python en utilisant SWIG
[Jouons avec Python] Créer un livre de comptes de ménage
Essayez de créer un jeu simple avec Python 3 et iPhone
Laissez Python segfo sur une ligne sans utiliser de ctypes
[Super facile] Faisons un LINE BOT avec Python.
Exécutez Blender avec python
Créons un client de socket Web avec Python. (Authentification par jeton d'accès)
[Blender Python] Afficher les images sur la vue 3D en utilisant OpenGL (bgl)
Comment transloquer un tableau à deux dimensions en utilisant uniquement python [Note]
Jouons avec la 4e dimension 4e
[S3] CRUD avec S3 utilisant Python [Python]
Utilisation de Quaternion avec Python ~ numpy-quaternion ~
Créer un gif 3D avec python3
[Python] Utilisation d'OpenCV avec Python (basique)
Faisons Othello avec wxPython
Faites une loterie avec Python
Écrivons python avec cinema4d.
Utiliser OpenCV avec Python @Mac
Envoyer en utilisant Python avec Gmail
J'ai essayé de créer une application todo en utilisant une bouteille avec python