Blender (jusqu'à 2.49)
Python : animation
LOD-matic
Level of details automatique en python
( 8/5/2005 )
(english version)
    Début   Index
précédentScript Contrainte
Importer framstick Suivant
Attention :  désolé mais ce script ne fonctionne plus corretcment avec  Blender 2.40 .puisque l'API ne permet plus d'accéder aux paramètres de réglage de division de subsurfaces .

En animation 3D, les temps de calcul peuvent être très longs à moins de réduire la définition des objets en fonction de leur éloignement de la caméra.  Dans Blender, le scripting python permet d'automatiser cette opération. 
 

Subdivision de surface
Automatisation pour l'animation
Lier un script à la scene
L'état de la caméra
Accélérer un peu en utilisant la Bounding Box
Le script 
Variable EDITMODE : limiter l'effet au rendu de l'animation8/5/2005

Subdivision de surface

 Blender offre  une option de subdivision de surface de type Catmull-Clarck avec 7 niveaux de finesse (0->6) applicable aux objets de type meshes. Certains artistes se servent de cet outil pour réduire le poids de l'objet en fonction de l'éloignement. Une valeur 4 ou 5 pour les objets les plus proches de la  caméra (au-delà le nombre de faces serait prohibitif) et 0 pour les plus éloignés . 

Automatisation pour l'animation

Un seul ennui : cette opération doit être réalisée à la main et n'est pas très pratique pendant une animation car il faut réétalonner la division entre chaque image.  C'est le genre de travail qu'un script peut faire en se connectant à l'animation courante pour effectuer le calcul en tache de fond, à chaque changement de frame

Lier un script à la scene

L'ensemble des objets utilisés dans l'animation courante est regroupé dans une "scène" .

 En python, ce qui touche à une scene (les informations de  rendu, la listes de objets liés à cette scène, par exemple) est géré au travers du module du même nom . La première chose à faire consiste donc à obtenir une variable représentant l'objet Scene grâce à la méthode : 
 

SCENE=Scene.getCurrent()

Pour faciliter la compréhension, on écrit toutes les variables en lettre majuscules .
Les objets lampes et caméras ainsi que les scènes possèdent une méthode qui permet de leur associer automatiquement un script qui sera éxécuté à chaque changement de frame : 
 

SCENE.addScriptLink(NAME,EVENT) 

Le lien ne doit être établi qu'une seule fois, ce qui oblige à récupérer et à tester la liste des scripts déjà liés 
 

SCRIPTLINK=SCENE.getScriptLinks(EVENT) 


[Figure 3] 

SCENE.addScriptLink(NAME,EVENT) 

L'état de la caméra.

La scène permet d'accéder aux données de la caméra
 

CAMERA=Scene.getCurrent().getCurrentCamera() 

qui possède deux limites, avant et arrière , ClipStart et ClipEnd :


[Figure 4]


CAMclipEN=CAMERA.getData().getClipEnd()

Tout ce qui se trouve en dehors ne sera pas affiché. On trie donc systématiquement tous les objets pour ne traiter que ceux qui se trouvent à l'intérieur et dont l'option SubSurf est activée .

Accélérer un peu en utilisant la Bounding Box

Chacun des objets de Blender contient les données de la boite englobante dans laquelle il est enfermé (Figure 4, tracé rose ) . Quel que soit le nombre de sommets que contient le mesh, en passant par cette boite on connait la position du plus extrème d'entre eux . Il n'y a donc plus que huit tests à effectuer pour savoir si une partie de cet objet se trouve dans les limites de vision de la caméra. Il ne reste plus qu'à effectuer une simple division sur la distance qui sépare l'objet de la caméra et à ranger le résultat dans une liste pour nourrir la fonction M.setSubDivLevels([LOD,LOD]) qui fixera la valeur de subdivision de surface :


 

[Figures 5 et 6].

Des améliorations sont possibles en ne prennant plus en compte les objets qui seraient entièrement en dessous de la limite ClipStart de la caméra, ou encore en dehors de la pyramide visuelle. 

Le Script
Télécharger le script 
Télécharger un fichier d'exemple zippé
 

#!BPY

"""
Name: 'A Cool Faked LOD'
Blender: 234
Group: 'Wizards'
Tooltip: 'Ce script se copie dans la fenetre de texte et gere les effets de Level of Detail en se liant a la scene courant en mode FrameChanged .'
"""
__author__ = "Jean-Michel Soler (jms)"
__url__ = ("Script's homepage, ","""Communicate problems and errors, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender""")
__version__ = "11/2004"

__bpydoc__ = """\
Ce script se copie dans la fenetre de texte et gere les effets de Level of Detail en se liant a la scene courant en mode FrameGanged .

Usage:

   Soit lancer le script  partir du menu Wizard soit le charger dans la fenetre de texte et faire alt-p. La mise  jour de l'affichage se fait au changement de d'image pendant l'animation.
""" 
# ====================================
# (c) jmsoler 01/11/2004, 
# ==================================== 

#========================================
# Chargement du module principal 
#========================================
import Blender
#========================================
# Chargement des sous-modules
#========================================
from Blender import NMesh,Scene,Object,Text

from math import floor
#=========================================
# DEFINITION DES DONNEES
#=========================================
#=========================================
# Afficher les effets LOD en mode EDIT
# avec une valeuregalea zero l'effet  est
# limite au rendu
#=========================================
EDITMODE=0

#=========================================
# Nombre maximum de zone 
# limitee a 4 pour eviter un trop 
# grand nombre de surface 
#=========================================
MAXZONE=5

#=========================================
# Connexion  la scene courante.
#=========================================
SCENE=Scene.getCurrent()
VERSION=Blender.Get('version')

#=========================================
# Collecte des infos de camera
#=========================================
CAMERA=Scene.getCurrent().getCurrentCamera()
if CAMERA!=None:
   CAMPOS=CAMERA.mat[3][:]
   CAMclipST=CAMERA.getData().getClipStart()
   CAMclipEN=CAMERA.getData().getClipEnd()
   LIMITE= [abs(CAMPOS[n]+CAMclipEN) for n in [0,1,2]]
else:
    TEXT="Désolé mais le script ne trouve pas de caméra active  %%t|"
    TEXT+=" Ajoutez une camezra ou faites au moins un rendu pour activer "
    TEXT+="celle que vous utilisez actuellement ."
    Blender.Draw.PupMenu(TEXT)

FRAGMENT=4.0

#=========================================
# liaison automatique du script 
# a la scene ...
#=========================================
DEVELOPPEMENT=0
EVENT='FrameChanged'
NAME='LOD.py'

def Charge(NAME):
  try:
    WINDOWTEXTES=Text.Get(NAME)
    print WINDOWTEXTES
  except:
      DIR = Blender.Get('homedir')
      SCRIPTDIR=DIR+Blender.sys.sep+'scripts'+Blender.sys.sep
      Text.Load(SCRIPTDIR+NAME)

#=========================================
# ... seulement si developpement acheve
#=========================================
if not DEVELOPPEMENT: 
   scriptlink=SCENE.getScriptLinks(EVENT)
   #print scriptlink
   if scriptlink!=None and NAME not in scriptlink:
      if VERSION>=234:
            Charge(NAME)
      SCENE.addScriptLink(NAME,EVENT)
   elif  scriptlink==None :
      if VERSION>=234:
            Charge(NAME)
      SCENE.addScriptLink(NAME,EVENT)
   #print scriptlink

#=========================================
# DEFINITION DES FONCTIOND
#=========================================
#=========================================

#========================================= 
def longueur_vect(p1,p2):
    p3=[]
    p3.append(p2[0]-p1[0])
    p3.append(p2[1]-p1[1])
    p3.append(p2[2]-p1[2])
    return [abs(p3[0]**2+p3[1]**2+p3[2]**2)**0.5,1.0,1.0]

def in_LIMIT(O,CAMPOS):
  OMTRX=O.getMatrix()[3][:]
  OBB=  [l for l in O.getBoundBox() 
          if (LIMITE[0]-longueur_vect(l,CAMPOS)[0])>0]
  for l in OBB:
     #print l,CAMPOS,
     LOD=int(FRAGMENT-floor(longueur_vect(l,CAMPOS)[0]/(LIMITE[0]/FRAGMENT)))

     if EDITMODE:
        LOD0=LOD
     else :
       LOD0=0#O.getData().getSubDivLevels()[0]

     #print O.getName(),LOD, O.getData().getSubDivLevels()
     M=O.getData()
     M.setSubDivLevels([LOD0,LOD])
     M.update()
     #O.makeDisplayList()
  #return OBB

#=========================================
# On recupere tous les objets de la scene 
# courante mais seulement s'ils ont l'option 
# subsurf  activee
#=========================================
OBJ=[O for O in Object.Get() 
       if O.getType()=='Mesh' and
           (O.getData().mode |
           NMesh.Modes['SUBSURF']) ]

#print 'liste d\'objet :',OBJ

for O in OBJ:
    #print 'this :' ,
    in_LIMIT(O,CAMPOS) 

#if VERSION==234: 
Blender.Window.RedrawAll()

Variable EDITMODE : limiter les effets au rendu de l'animation

Cette modification permet d'alléger l'affichage en mode édition . On peut donc tester l'animation sans avoir la lourdeur d'un mesh subsurfé  . 
...
#=========================================
# Afficher les effets LOD en mode EDIT
# avec une valeur egale a zero l'effet  est
# limite au rendu
#=========================================
EDITMODE=0
...
...
     if EDITMODE:
        LOD0=LOD
     else :
       LOD0=0#O.getData().getSubDivLevels()[0]
...

 
précédentScript Contrainte
 Importer framstick Suivant
Vers le  Haut de page

Les questions concernant cette page  peuvent être posées sur  :
 news://news.zoo-logique.org/3D.Blender


 

 

Livre en français
Blender : apprenez, pratiquez, Créez, livre, Ed. Campus Press, coll. Starter Kit
Blender Starter Kit

Forum
FAQ
Lexique
Didacticiels
Compilations
Blender2KT
Débuter
Modelage
Blender python
Materiaux
Lumière
Animation
API python (eng)
Archives nzn
Statistiques
Doc flash Sculptris
Galerie Sculptris

mon site de démos sur youtube