Le module VideoTexture : bge.texture

Le module bge.texture vous permet de manipuler les textures pendant le jeu. Plusieurs sources de textures sont possibles : fichiers vidéo, fichiers image, capture vidéo, tampon de mémoire, rendu de caméra ou un mélange de tout cela. Les fichiers vidéo et image peuvent être chargés depuis Internet en utilisant un URL à la place d’un nom de fichier. De plus, vous pouvez appliquer des filtres sur les images avant de les envoyer au GPU, produisant ainsi un effet vidéo : écran bleu, gradient, gris, normal map. bge.texture utilise FFmpeg pour charger les images et les vidéo. Tous les formats et codecs pris en charge par FFmpeg le sont par bge.texture, y compris (mais pas limités à ceux-ci) :

  • AVI
  • Ogg
  • Xvid
  • Theora
  • caméra dv1394
  • Carte de capture video4linux (ceci inclut de nombreux webcams)
  • Carte de capture videoForWindows (ceci inclut de nombreux webcams)
  • JPG

Comment ça marche

Le principe est simple : d’abord vous identifiez une texture existante par objet et nom, ensuite vous créez une nouvelle texture avec un contenu dynamique et échangez les deux textures dans le GPU. Le GE n’est pas averti de la substitution et continue à afficher l’objet comme toujours, sauf que vous avez maintenant le contrôle de la texture. À la fin, la nouvelle texture est supprimée et l’ancienne texture restaurée.

La présente page est un guide pour le module bge.texture avec des exemples simples.

Préparation du jeu

Avant que vous puissiez utiliser le module bge.texture, vous devez disposer d’objets avec des textures appliquées de façon appropriée.

Imaginez que vous voulez avoir une télévision affichant des émissions en direct dans le jeu. Vous allez créer un objet télévision et appliquer (UV) une texture différente à la place de l’écran, par exemple tv.png. Ce à quoi ressemble cette texture n’est pas important ; probablement vous aimeriez la rendre en gris foncé pour simuler l’état éteint. Quand la télévision doit être allumée, vous créez une texture dynamique depuis une carte de capture vidéo et l’utilisez à la place de tv.png: l’écran TV va prendre vie.

Vous avez deux façons pour définir les textures que bge.texture peut saisir :

  1. Texture UV simple.
  2. Matériau Blender avec canal de texture d’image.

Du fait que bge.texture fonctionne au niveau texture, c’est compatible avec toutes les fonctions sophistiquées de texturage de BGE : GLSL, multi-texture, custom shaders, etc.

Premier exemple

Supposons que nous avons un objet de jeu avec une ou plusieurs faces assignées à un matériau/image sur lequel nous voulons afficher une vidéo.

La première étape est de créer un objet Texture. Nous allons le faire dans un script qui s’exécute une fois. Il peut être au début du jeu, la vidéo est seulement jouée quand vous actualisez la texture ; nous allons revenir dessus plus tard. Le script est normalement attaché à l’objet sur lequel vous voulons afficher la vidéo de sorte que nous pouvons facilement récupérer la référence de l’objet :

import bge.texture

contr = GameLogic.getCurrentController()
obj = contr.owner

if not hasattr(GameLogic, 'video'):

La coche sur l’attribut video est simplement une astuce pour nous assurer que vous avons créé la texture seulement une fois.

Recherche de matériau

matID = bge.texture.materialID(obj, 'IMvideo.png')

bge.texture.materialID() est une fonction pratique pour récupérer le matériau de l’objet qui utilise video.png comme texture. Cette méthode fonctionnera avec un matériau Blender et une texture UV. Dans le cas d’une texture UV, il prend le matériau interne correspondant aux faces qui sont assignées à cette texture. Dans le cas d’un matériau Blender, il prend le matériau qui a un canal de texture d’image correspondant au nom du premier canal.

Le préfixe IM indique que nous sommes en train de chercher un nom de texture mais nous pouvons aussi chercher un matériau en donnant le préfixe MA . Par exemple, si nous voulons trouver le matériau appelé VideoMat sur cet objet, le code devient :

matID = bge.texture.materialID(obj, 'MAVideoMat')

Création d’une texture

bge.texture.Texture est la classe qui crée l’objet Texture qui charge la texture dynamique sur le GPU. Le constructeur prend un argument obligatoire et trois facultatifs :

gameObj
L’objet game.
materialID
Index du matériau retourné par bge.texture.materialID(), 0 = premier matériau par défaut.
textureID
Index de texture dans le cas de canal multi-texture, 0 = premier canal par défaut. Dans le cas de texture UV, ce paramètre devrait toujours être à 0.
textureObj
Référence à un autre objet Texture dont nous voulons réutiliser la texture. Si nous utilisons cet argument, nous ne devrions pas créer de source sur cette texture et il n’est pas nécessaire de le réactualiser non plus ; l’autre objet Texture fournira la texture à la fois pour les matériaux/textures.
GameLogic.video = bge.texture.Texture(obj, matID)

Rendre une texture persistante

Notez que nous avons assigné l’objet à un GameLogic, attribut video que nous avons créé pour l’occasion. La raison est que l’objet Texture doit être persistant à travers les scripts de jeu. une variable locale serait supprimée à la fin du script et la texture GPU supprimée en même temps. l’objet du module GameLogic est un emplacement pratique pour stocker les objets persistants.

Création d’une source

maintenant nous avons un objet Texture mais il ne peut rien faire car il n’a aucune source. Nous devons créer une source à partir d’une des sources possibles disponibles dans bge.texture :

VideoFFmpeg
images animées. Fichier vidéo, capture vidéo, streaming vidéo.
ImageFFmpeg
Images fixes. Fichier image, image sur le web.
ImageBuff
Image depuis la mémoire de l’application. Pour les images générées par ordinateur, les applications de dessin.
ImageViewport
Tout ou partie de la vue (= rendu de la caméra active affichée à l’écran).
ImageRender
Rendu d’une caméra non active.
ImageMix
Un mélange de deux ou plus des sources ci-dessus.

Dans cet exemple nous utilisons un simple fichier vidéo comme source. Le constructeur VideoFFmpeg prend un nom de fichier comme argument. Pour éviter toute confusion avec l’emplacement du fichier, nous utiliserons GameLogic.expandPath () pour construire un nom de fichier absolu, en supposant que le fichier vidéo est dans le même dossier que le fichier blend:

movie = GameLogic.expandPath('//trailer_400p.ogg')
GameLogic.video.source = bge.texture.VideoFFmpeg(movie)

Nous créons l’objet source vidéo et l’assignons à l’attribut source de l’objet Texture pour définir la source et la rendre persistante : comme l’objet Texture est persistant, l’objet source sera aussi persistant.

Notez que nous pouvons modifier la source Texture à tout moment. Supposez que nous voulons échanger les deux films durant le jeu.

Nous pouvons faire ce qui suit

GameLogic.mySources[0] = bge.texture.VideoFFmpeg('movie1.avi')
GameLogic.mySources[1] = bge.texture.VideoFFmpeg('movie2.avi')

Et ensuite assigner (et réassigner) la source durant le jeu

GameLogic.video.source = GameLogic.mySources[movieSel]

Configuration de la source

La source VideoFFmpeg a plusieurs attributs pour contrôler la lecture vidéo :

range
[start,stop] (flottants). Définissez les temps de début et de fin de la lecture vidéo, exprimés en secondes depuis le début de la vidéo. Par défaut, la vidéo complète.
repeat
(entier). Nombre de lectures vidéo. -1 pour infini.
framerate
(flottant). Fréquence de trames relative, <1.0 pour lent, >1.0 pour rapide.
scale
(booléen). Fixé à True pour activer l’algorithme rapide de mise à l’échelle nearest-neighbor (le plus proche voisin). La largeur et la hauteur de texture doivent être des puissances de 2. Si la taille d’image vidéo n’est pas une puissance de 2, la remise à l’échelle est nécessaire. Par défaut bge.texture utilise la fonction gluScaleImage() précise mais lente. Le mieux est de remettre à l’échelle la vidéo hors ligne afin qu’aucune mise à l’échelle ne soit nécessaire à l’exécution !
flip
(booléen). Fixé à True si l’image doit être retournée verticalement. FFmpeg livre toujours l’image retournée, aussi cet attribut est fixé à True par défaut.
filter
Définir un filtre supplémentaire sur la vidéo avant son envoi vers le GPU. Assignez à un de objets filtres bge.texture. Par défaut l’image est envoyée sans modification au GPU. Si un canal alpha est présent dans la vidéo, il est automatiquement chargé et envoyé de même au GPU.

Nous allons simplement mettre l’attribut scale à True parce que gluScaleImage () est vraiment trop lent pour une vidéo en temps réel. Dans le cas où les dimensions vidéo sont déjà une puissance de 2, il n’a aucun effet.

GameLogic.video.source.scale = True

Lecture de la vidéo

Nous sommes maintenant prêts à lire la vidéo

GameLogic.video.source.play()

La lecture vidéo n’est pas un processus d’arrière-plan : il se passe seulement quand nous actualisons la texture. Aussi nous devons avoir un autre script qui fonctionne sur chaque trame et appelle la méthode refresh() de l’objet Texture

if hasattr(GameLogic, 'video'):
GameLogic.video.refresh(True)

Si la source vidéo est arrêtée, refresh() n’a pas d’effet, L’argument de refresh() est un drapeau qui indique si la texture devrait être recalculée au prochain rafraîchissement. Pour la lecture vidéo, vous voudrez certainement le mettre à True.

Vérification du statut de la vidéo

Les classes de source vidéo (telle que VideoFFMpeg) ont un attribut status. Si la lecture vidéo est en cours, sa valeur est 2, si elle est arrêtée, c’est 3. Aussi dans notre exemple

if GameLogic.video.source.status == 3:
#video has stopped

Flux de travail avancé

L’argument True dans la méthode Texture.refresh() invalide simplement le tampon d’image après son envoi vers le GPU de sorte que sur la trame suivante, une nouvelle image sera chargée depuis la source. Il a l’effet indésirable de rendre l’image indisponible pour Python. Vous pouvez aussi le faire manuellement en appelant directement la méthode refresh() de la source.

Voici certaines possibilités de flux de travail avancés :

  • Utiliser le tampon d’image dans Python (n’affecte pas la Texture)

    GameLogic.video.refresh(False)
    image = GameLogic.video.source.image
    # image is a binary string buffer of row major RGBA pixels
    # ... use image
    # invalidates it for next frame
    GameLogic.video.source.refresh()
    
  • Charger l’image depuis la source pour le traitement avec Python sans chargement vers le GPU :

  • Notez que nous n’appelons même pas d’actualisation de la Texture.

  • Vous pourrions aussi créer un objet source sans objet Texture

    image = GameLogic.video.source.image
    # ... use image
    GameLogic.video.source.refresh()
    
  • Si vous avez plus d’un matériau sur le maillage et que vous voulez modifier une texture d’un matériau particulier, récupérez son ID

    matID = bge.texture.materialID(gameobj, "MAmat.001")
    

Le matériau GLSL peut avoir plus d’un canal de texture, identifiez la texture par le slot de texture où elle est définie, en voici deux

tex=bge.texture.Texture(gameobj, matID, 2)

Démos avancées

Voici une démo qui montre l’utilisation de deux vidéos alternativement sur la même texture. Notez qu’elle nécessite un fichier vidéo supplémentaire qui est la bande-annonce de Elephant Dream. Vous pouvez le remplacer par un autre fichier dont vous voulez lancer la démo.

Voici une démo qui montre l’utilisation de la source ImageMix. ImageMix``est une source qui nécessite des sources, qui peuvent être n'importe quelle autre source ``Texture, comme VideoFFmpeg, ImageFFmpeg ou ImageRender. Vous les définissez avec setSource () et leur poids relatif avec setWeight(). Faites attention au fait que le poids est un nombre entre 0 et 255, et que la somme de tous les poids devrait être 255. ImageMix fait un mélange de tous les sources selon leurs poids. Les sources doivent avoir la même taille d’image (après réduction de la dimension à la puissance de deux la plus proche). Si cette condition n’est pas remplie, vous obtiendrez une erreur Python sur la console.