Ultimate 3D offre un ensemble de fonctions utiles, qui peuvent être utilisées afin de réaliser divers calculs sur les vecteurs et les matrices. Mathématiquement, les matrices peuvent être des objets très complexes et les vecteurs encore plus. Mais pour ce qui nous concerne nous travaillerons seulement avec deux cas spécifiques simples à comprendre. Pour ceux qui sont impatients, voici la description en une phrase. Les matrices peuvent être utilisées pour décrire les transformations et les vecteurs sont faits de trois valeurs flottantes qui peuvent être utilisés pour décrire une position ou une direction. Mais venons en à un petit peu plus de détails.
Un vecteur est fait d'une coordonnée x, d'une coordonnée y et d'une coordonée z. Vous devriez déjà être familié avec les vecteurs décrivants les positions à l'aide des variables x, y et z des différents objets. Mais les vecteurs peuvent aussi décrire une direction avec une longueur. Pour cette fonctionnalité, ils sont considérés comme relatifs à l'origine. Si vous dessinez une ligne depuis l'origine (le vecteur avec les coordonnées (0|0|0)) au vecteur décrivant la direction, la direction dans laquelle ira votre stylo imaginaire sera la direction vers lequel le vecteur pointera. Vous pouvez l'imaginer comme une flèche. Et si vous souhaitez déplacer un objet avec une position décrite par un vecteur de position, dans une direction décrite par un vecteur de direction, il vous suffit d'ajouter le vecteur de direction au vecteur de position. Si un vecteur de direction a une longueur de un, on dit que c'est un vecteur nomalisé.
Une
matrice peut être utilisée pour décrire une transformation. Si vous
avez oublié ce qu'est une transformation, voici une nouvelle
explication: dans Ultimate 3D, une transformation est décrite par trois
groupes de variables: x, y et z; rotx, roty, et rotz; et scalx, scaly, et scalz. Les variables rotx/y/z
effectuent une rotation, les variables scalx/y/z
effectuent une mise à l'échelle et les variables x, y et z
effectuent une translation (déplacement). Chaque transformation peut
donc être vue comme une combinaison d'une mise à l'échelle, d'un
déplacement et d'une rotation.
Ce qui est génial dans le fait de considérer les matrices comme une méthode pour décrire les transformations, est que vous pouvez les utiliser pour différents calculs, très efficacement. Elles peuvent aussi être utilisées pour résoudre des problèmes qui seraient impossible ou trop compliqué à résoudre sans matrices. Il existe une notation mathématique très utile qui peut être utilisée pour décrire la fonction de chaque matrice. Cette notation peut très bien être expliquée, à l'aide d'un petit exemple. Si vous avez un modèle dans Ultimate 3D, toutes les données à propos de sa géométrie est donnée dans le système de coordonnées du modèle. La modèle a son propre système de coordonnées, celui que vous avez créé dans le programme de modélisation. Ce système de coordonnées est nommé espace de modèle. La transformation qui est définie au travers des variables de transformation de ce modèle converti les données qui sont données en espace de modèle en coordonnées de l'espace global, que l'on appelle espace du niveau (ou l'ensemble du niveau). C'est pourquoi la transformation est nommée "model-to-world space transformation", c'est à dire une transformation de l'espace du modèle vers l'espace du niveau. La notation est donc qu'une transformation qui transforme quelque chose d'un espace A en un espace B se dit une transformation d'espace de A vers B ou une matrice d'espace de A vers B. Le terme matrice et transformation peut être utilisé plus ou moins comme un synonyme dans ce contexte.
Maintenant, si vous avez une transformation de l'espace A à l'espace B et une transformation d'espace de B vers C vous pouvez transformer la transformation d'espace de A vers B par la transformation d'espace de B vers C, vous obtiendrez ainsi pour résultat une transformation d'espace de A vers C. Donc si vous transformez un vecteur par cette matrice combinée, il sera transformé de l'espace A vers l'espace C directement! C'est pourquoi les matrices sont si incroyablements utiles. Vous pouvez les chainer de la manière que vous voulez. Si vous voulez transformer un groupe d'objets, vous pouvez créer une matrice qui décrit cette transformation et l'utiliser pour transformer chaque matrice de transformation des objets. Ensuite ils se comporteront comme s'ils avaient été collés ensemble, juste comme vous le souhaitiez. Avec les matrices de transformation vous pouvez facilement donner une rotation au niveau. Si vous le souhaitez, vous pouvez le retourner complètement. Vous pouvez le transformer dans n'importe quel système de coordonnées que vous voulez.
Maintenant cela fait beaucoup de théorie. Mais ce fond de connaissances théoriques rendra l'utilisation des fonctions de calculs des vecteurs et des matrices un peu plus facile. Donc passons à la partie pratique.
Puisque
Game Maker ne peut manipuler des structures de données telles que les
vecteurs et les matrices de lui même, Ultimate 3D se charge de cette
partie du travail. Game Maker reçoit seulement des identifiants comme
valeurs de retour. Ces identifiants peuvent être, soit la valeur de
retour d'une fonction, ou un des paramètres. Presque chaque fonction de
calcul de matrices ou de vecteurs prend un paramètre nommé OutputMatrixID
ou OutputVectorID
. Pour éviter des répétitions sans-fin, je vais vous expliquer comment ce paramètre fonctionne une bonne fois pour toutes.
OutputMatrixID / OutputVectorID
L'ID de la matrice ou du
vecteur où vous souhaitez que le retour de la fonction soit écrit. Si
vous ne souhaitez pas réécrire sur une matrice ou un vecteur existant
vous pouvez passer -1 à la place. Ultimate 3D créera alors une nouvelle
matrice ou un nouveau vecteur et retournera son ID. Vous devez
sauvegarder cet ID. Lorsque vous n'aurez plus besoin de ce vecteur ou
de cette matrice, il vous faudra libérer la matrice ou le vecteur, à
l'aide de ReleaseMatrix(MatrixID) ou de ReleaseVector(VectorID).
Autrement la matrice ou le vecteur ne sera pas supprimmé et Ultimate 3D
vous dira après un certain temps que trop de matrices ou de vecteurs on
été créés.
Passer
-1 à l'un de ces deux paramètres est le seul moyen de créer une
nouvelle matrice ou un nouveau vecteur. Mais beaucoup de vecteurs et de
matrices existent déjà. À savoir, les matrices de transformation de
tous les objets de modèles, des objets primitifs et des objets de
caméra. Il vous est possible d'accéder à ces matrices à l'aide d'une
simple fonction.
Cette fonction retourne l'ID de la matrice de transformation de l'objet avec l'ID donné:
GetObjectTransformation(
ObjectID
)
|
---|
ObjectID
L'ID Game Maker d'un objet de modèle, d'un objet primitif ou d'un objet de caméra.
Grâce à l'ID de matrice que GetObjectTransformation(...) vous donne, vous pouvez lire ou écrire dans la matrice d'un objet. De cette manière vous pouvez modifier sa transformation sans toucher à ses variables de transformation. Mais vous devriez toujours faire cela après l'appel de Step(), parce que Step() réécrit usuellement les matrices de transformations d'un objet. En plus des matrices de transformation, il y a un autre groupe de matrices qui existe déjà en très grand nombre: les matrices de transformation des os et des meshs. Elles peuvent être retrouvées elles aussi, mais vous ne pouvez pas seulement retrouver leurs IDs.
Cette fonction copie la matrice de transformation de l'espace des os à l'espace du niveau de l'os donné dans la matrice de sortie spécifiée.
GetBoneTransformation(
OutputMatrixID,
BoneIndex
)
|
---|
OutputMatrixID
Voir ci-dessus.
BoneIndex
L'indice de l'os dont vous souhaiter retrouver la transformation. Vous pouvez utiliser GetBoneIndex(...) pour obtenir cette valeur.
La matrice qui est retournée par cette fonction sera toujours la matrice de transformation de la frame qui peut être vue à l'écran à ce moment là, donc si vous utilisez cette fonction pour ajouter des objets aux os, il peut arriver que survienne un effet de discontinuité. Pour éviter ça vous pouvez utiliser UpdateSkeleton() pour mettre à jour la matrice de transformation des os de l'objet de modèle qui appelle cette fonction. Mais soyez prudent avec cela: cela prend beaucoup de temps de calcul. UpdateSkeleton() est également utile pour la fonction suivante.
Si vous utilisez les fonctions de manipulation de modèles vous aurez souvent besoin d'obtenir les matrices de transformation des meshs. Celles-ci sont requises pour obtenir les positions des vertex de l'espace de mesh à l'espace du niveau. Un même mesh peut être utilisé plusieurs fois dans un modèle, en particulier dans les modèles créés avec Anim8or. Par exemple, si vous avez modelé un objet pour la partie supérieure de la jambe et que vous ajoutez cet objet à l'os de la jambe gauche et à l'os de la jambe droite, ce mesh sera utilisé deux fois. C'est pourquoi deux fonctions sont nécessaires pour être à même de retrouver les transformations des meshs.
GetMeshOccurrenceCount(
MeshIndex
)
|
---|
MeshIndex
L'indice du mesh, pour
lequel vous voulez retrouver le nombre d'occurence. Cela peut être
n'importe quelle valeur entière, entre 0 et GetMeshCount()-1.
GetMeshOccurrenceTransformation(
OutputMatrixID,
MeshIndex,
OccurrenceIndex
)
|
---|
OutputMatrixID
Voir ci-dessus.
MeshIndex
L'indice du mesh, pour
lequel vous voulez récupérer la transformation de l'une de ses
occurences. Cela peut être n'importe quelle valeur entière allant de 0
à GetMeshCount()-1.
OccurrenceIndex
L'indice de l'occurence du
mesh donné, pour lequel vous souhaitez retrouver la matrice de
transformation. Cela peut être n'importe quelle valeur entière allant
de 0 à GetMeshOccurrenceCount()-1.
Maintenant que vous connaissez toutes les fonctions utiles pour récupérer les matrices existantes, il est temps de vous rendre familier avec les fonctions de création de matrices, qui vous permette de créer de nouvelles matrices de transformation. Elles sont toutes à peu près similaires, donc je vais juste vous les lister ici. Les paramètres sont expliqués plus bas.
Cette fonction crée une matrice qui effectue une translation (déplacement):
CreateTranslationMatrix(
OutputMatrixID,
TranslationX, TranslationY, TranslationZ
)
|
---|
Cette fonction crée une matrice qui effectue une mise à l'échelle:
CreateScalingMatrix(
OutputMatrixID,
ScalingX, ScalingY, ScalingZ
)
|
---|
Cette fonction crée une matrice qui effectue une rotation:
CreateRotationMatrix(
OutputMatrixID,
RotationX, RotationY, RotationZ
)
|
---|
CreateTransformationMatrix(
OutputMatrixID,
TranslationX, TranslationY, TranslationZ,
RotationX, RotationY, RotationZ,
ScalingX, ScalingY, ScalingZ
)
|
---|
OutputMatrixID
Voir ci-dessus.
TranslationX, TranslationY, TranslationZ
Ces variables définissent une translation (déplacement). Elles sont équivalentes aux variables de transformation x, y et z.
RotationX, RotationY, RotationZ
Ces variables définissent une rotation. Elles sont équivalentes aux variables de transformation: rotx, roty et rotz.
ScalingX, ScalingY, ScalingZ
Ces variables définissent une mise à l'échelle. Elles sont équivalentes aux variables de transformation: scalx, scaly et scalz.
Toutefois il ne suffit pas de créer des matrices; vous avez également besoin de quelques fonctions pour effectuer des calculs avec celles-ci. La première fonction disponible dans ce contexte ne calcule pas vraiment quelque chosen, elle copie seulement certaines valeurs. Cependant, cela peut être d'un grand secours dans de nombreux cas.
Cette fonction copie une matrice dans la matrice de sortie donnée.
CopyMatrix(
OutputMatrixID,
InputMatrixID
)
|
---|
OutputMatrixID
Voir ci-dessus.
InputMatrixID
L'identifiant de la matrice que vous voulez copier dans la matrice de sortie.
La fonction suivante transforme une matrice par une autre matrice:
TransformMatrix(
OutputMatrixID,
InputMatrixID1, InputMatrixID2
)
|
---|
OutputMatrixID
Voir ci-dessus.
InputMatrixID1, InputMatrixID2
Les deux matrices d'entrée.
La première matrice sera transformée par la seconde. Cela signifie que
si la première matrice est une matrice d'espace A vers B et la seconde
matrice est une matrice d'espace B vers C, la matrice résultante sera
une matrice d'espace A vers C. Notez donc que l'ordre des matrices a
une influence.
InvertMatrix(
OutputMatrixID,
InputMatrixID
)
|
---|
OutputMatrixID
Voir ci-dessus.
InputMatrixID
L'ID de la matrice que vous
souhaitez inverser. Remarquez que les matrices avec une valeurs de mise
à l'échelle (scaling) de 0 ne peut être inversée. Si la matrice
d'entrée ne peut être inversée la matrice d'entrée elle-même sera
retournée.
Cette fonction interpole entre elles deux matrices données avec une méthode d'interpolation très bonne. La translation et la mise à l'échelle sont interpollées de façon linéaire, la rotation est interpollées à l'aide d'une méthode dite interpolation linéaire sphérique.
InterpolateMatrices(
OutputMatrixID,
InputMatrixID1, InputMatrixID2,
InterpolationFactor
)
|
---|
OutputMatrixID
Voir ci-dessus.
InputMatrixID1, InputMatrixID2
Les deux matrices qui seront interpolées linéairement entre elles.
InterpolationFactor
Un facteur qui définir
l'influence de chacune des deux matrices. Généralement, c'est une
valeur allant de zero à un. À 0.0 la matrice de sortie sera égale à la
première matrice d'entrée, à 1.0 la matrice de sortie sera égale à la
seconde matrice.
GetMatrixEntry(
InputMatrixID,
LineIndex, ColumnIndex
)
|
---|
InputMatrixID
L'ID de la matrice dont vous souhaitez retrouver une entrée.
LineIndex, ColumnIndex
L'indice
de la ligne et de la colonne dont vous souhaitez récupérer une entrée.
Les deux valeurs doivent être des entiers allant de 0 à 3, où 0 se
réfère à la première ligne et colonne, et 3 à la dernière.
Maintenant vous pouvez vous détendre, parce que la partie vraiment compliquée est déjà dérrière vous. Vous pouvez utiliser maintenant vos connaissances à propos des matrices pour transformer des objets complets. Mais les matrices peuvent aussi être utilisées pour transformer un seul vecteur. Avant que vous puissiez faire ainsi, vous devez savoir comment créer des vecteurs. Passons donc à cela.
La fonction suivante crée un vecteur à partir des coordonnées x, y et z données.
CreateVector(
OutputVectorID,
X, Y, Z
)
|
---|
OutputVectorID
Voir ci-dessus.
X, Y, Z
Les coordonnées x, y et z avec lesquelles vous souhaitez créer le vecteur.
CreateDirectionVector(
OutputVectorID,
Longitude, Latitude
)
|
---|
OutputVectorID
Voir ci-dessus.
Longitude, Latitude
L'angle de longitude et de
latitude avec lesquels vous souhaitez créer ce vecteur. Pour obtenir
une description sur la façon dont les angles de longitude et de
latitude fonctionnent, regardez la description de la fonction Move(...).
Pour récupérer les données des vecteurs, vous pouvez utiliser la fonction suivante:
GetVector(
VectorID,
VectorElementID
)
|
---|
VectorID
L'identifiant du vecteur duquel vous souhaitez récupérer des données.
VectorElementID
Ce paramètre identifie la
coordonnée qui sera retournée. Cela peut être 1 pour la coordonnée x, 2
pour la coordonnée y ou 3 pour la coordonnée z.
Lorsque vous avez deux vecteurs, vous pouvez faire leur somme. Les vecteurs seront additionner ensemble élément par élément. Donc (x | y | z)+(x' | y' | z') resultera en (x+x' | y+y' | z+z'). C'est effectué par la fonction suivante:
CalculateVectorSum(
OutputVectorID,
InputVectorID1, InputVectorID2
)
|
---|
OutputVectorID
Voir ci-dessus.
InputVectorID1, InputVectorID2
Les identifiants des vecteurs que vous voulez additionner.
CalculateVectorDifference(
OutputVectorID,
InputVectorID1, InputVectorID2
)
|
---|
OutputVectorID
Voir ci-dessus.
InputVecorID1
L'ID du vecteur, qui aura le rôle du nombre à soustraire.
InputVectorID2
L'ID du vecteur, qui aura le rôle du nombre soustrayant.
CalculateVectorScalarProduct(
OutputVectorID,
InputVectorID,
ScalarFactor
)
|
---|
OutputVectorID
Voir ci-dessus.
InputVectorID
L'identifiant du vecteur qui sera multiplié par un scalaire.
ScalarFactor
Une valeur à virgule flottante qui sera multipliée par chacun des éléments du vecteur.
CalculateVectorLength(
VectorID
)
|
---|
VectorID
L'identifiant du vecteur dont vous voulez calculer la longueur.
Si vous travaillez avec des vecteurs de direction, il est souvent nécessaire de les convertir vers des valeurs de longitude et de latitude. Par exemple vous pouvez avoir besoin de les utiliser comme paramètre pour CheckRayIntersection(...). Calculer cela manuellement est un peu compliqué. C'est pourquoi Ultimate 3D a des fonctions maniables à cet effet.
CalculateVectorLongitude(
OutputVectorID
)
|
---|
OutputVectorID
L'ID du vecteur pour lequel vous souhaitez déterminer la longitude. Le vecteur n'a pas besoin d'être normalisé.
Cette fonction calcule l'angle de latitude, qui décrit correctement le vecteur et le retourne.
CalculateVectorLatitude(
OutputVectorID
)
|
---|
OutputVectorID
L'ID du vecteur pour lequel vous souhaitez déterminer la latitude. Le vecteur n'a pas besoin d'être normalisé.
Maintenant suit une fonction vraiment utile. Il n'est pas suprenant qu'elle existe, mais c'est tout de même fortement utile. Elle peut être utilisée pour transformer un vecteur par une matrice.
TransformVector(
OutputVectorID,
InputVectorID,
InputMatrixID
)
|
---|
OutputVectorID
Voir ci-dessus.
InputVectorID
L'identifiant du vecteur qui doit être transformé.
InputMatrixID
L'identifiant de la matrice par laquelle le vecteur doit être transformé.
À côté de ça, Ultimate 3D offre quelques fonctions pour effectuer des opérations mathématiques communes avec les vecteurs. L'opération la plus commune est le produit scalaire. Si vous avez deux vecteurs v=(x | y | z) et v'=(x' | y' | z') le produit scalaire <v, v'> est égal à x*x' + y*y' + z*z'. Ce qui est super avec les produits scalaires c'est que le produit scalaire est égal au cosinus de l'angle entre les deux vecteurs, multiplié par la longueur du premier et du second vecteur. Donc pour mettre cela dans une petite formule: si a est l'angle entre v et v' le produit scalaire est égal à cos(a) * |v| * |v'|. Donc si les vecteurs sont normalisés (qu'ils ont une longueur de un) le produit scalaire est égal au cosinus de l'angle. Cela signie que le produit scalaire retournera 1 si les vecteurs sont parallèles et 0 s'ils sont perpendiculaires. Et ce n'est qu'une seule des nombreuses utilisations du produit scalaire. Pour implémenter l'éclairage directionnel la seul chose qui doit être faite est de calculer un produit scalaire.
Cette fonction calcule le produit scalaire des deux vecteurs donné et le retourne.
CalculateDotProduct(
InputVectorID1, InputVectorID2
)
|
---|
InputVectorID1, InputVectorID2
Les identifiants des deux
vecteurs qui sont utilisés pour calculer le produit scalaire. L'ordre
des vecteurs n'a aucune influence.
Une autre opération qui est effectuée avec deux vecteurs très souvent est le calcul du produit vectoriel (ou croisé). Le produit vectoriel résulte en un nouveau vecteur, qui sera perpendiculaire aux deux vecteurs d'entrée. La direction de ce vecteur dépend de l'ordre dans lequel les deux vecteurs sont passés à la fonction. La longueur du vecteur égale deux fois la taille du triangle formé par les deux vecteurs d'entrée en relation avec le vecteur nulle (origine). Elle est aussi égale au produit de la longueur des deux vecteurs d'entrée et du sinus de l'angle entre eux. Si les vecteurs sont parallèles, le vecteur retourné sera le vecteur zero.
Cette fonction calcule le produit vectoriel de deux vecteurs.
CalculateCrossProduct(
OutputVectorID,
InputVectorID1, InputVectorID2
)
|
---|
OutputVectorID
Voir ci-dessus.
InputVectorID1, InputVectorID2
L'identifiant des vecteurs
pour lesquels vous voulez calculer le produit vectoriel. L'ordre a une
influence. Si vous échanger ces deux paramètres, cela rendra le vecteur
résultant négatif.
Il existe aussi un autre ensemble de fonctions utiles liées aux matrices, qui utilisent des vecteurs pour leurs valeurs de sortie. Elles convertissent des matrices en variables de transformation.
ComputeMatrixTranslation(
OutputVectorID,
InputMatrixID
)
|
---|
OutputVectorID
Voir ci-dessus. Après l'appel de cette fonction l'élément x contiendra la valeur x, l'élément y contiendra la valeur y et l'élément z contiendra la valeur z.
InputMatrixID
L'identifiant de la matrice pour laquelle vous souhaitez déterminer la translation (déplacement).
ComputeMatrixScaling(
OutputVectorID,
InputMatrixID
)
|
---|
OutputVectorID
Voir ci-dessus. Après l'appel de cette fonction l'élément x contiendra la valeur scalx, l'élément y contiendra la valeur scaly et l'élément z contiendra la valeur scalz.
InputMatrixID
L'identifiant de la matrice pour laquelle vous souhaitez déterminer la mise à l'échelle.
ComputeMatrixRotationAngles(
OutputVectorID,
InputMatrixID
)
|
---|
OutputVectorID
Voir ci-dessus. Après l'appel de cette fonction l'élément x contiendra la valeur rotx, l'élément y contiendra la valeur roty et l'élément z contiendra la valeur rotz.
InputMatrixID
L'identifiant de la matrice pour laquelle vous souhaitez déterminer les valeurs rotx, roty et rotz qui la décrive correctement.
Etant donné que c'est nécessaire très souvent, voici une autre fonction utile, qui combine les trois fonctions ci-dessus. Cette fonction applique la transformation décrite par la matrice donnée, aux variables de transformation de l'objet par lequel la fonction est appellée (x, y, z, rotx, roty, rotz, scalx, scaly, scalz).
ApplyTransformationMatrix(
InputMatrixID
)
|
---|
InputMatrixID
La matrice à partir de laquelle vous voulez déterminer la nouvelle transformation de l'objet.
Finalement voici quelques fonction mathématiques très spéciales. Elles peuvent être utilisées pour convertir coordonnées de l'écran en coordonnées du niveau.
ScreenCoordToVector(
OutputVectorID,
ScreenCoordX, ScreenCoordY,
CameraIndex
)
|
---|
OutputVectorID
Voir ci-dessus.
ScreenCoordX, ScreenCoordY
Une position sur l'écran en
pixels, relative au coin supérieur gauche de la bordure intérieure de
la fenêtre de Game Maker. C'est la position sur l'écran pour laquelle
le rayon correspondant sera calculé.
CameraIndex
L'indice de la caméra pour
laquelle l'opération sera effectuée. Si vous voulez calculer le rayon
pour la caméra par défaut passez 0 ou rien, autrement passez la valeur
de la variable number de l'objet caméra que vous souhaitez vérifier.
Cette fonction fait exactement l'opération opposée de ScreenCoordToVector(...). Elle donne en sortie un vecteur de position de l'écran d'une coordonnée 3D dans l'espace du niveau en entrée. Les éléments x et y du vecteur en sortie sont les coordonnées dans la limite de la vue et l'élément z est la coordonnée z de la position donnée transformée en espace de la caméra, qui peut être utilisée pour savoir si la position est derrière la caméra.
CoordToScreen(
OutputVector,
X, Y, Z,
CameraIndex
)
|
---|
OutputVectorID
Voir ci-dessus.
X, Y, Z
Les coordonnées dans l'ensemble du niveau de la position qui sera traduite dans l'espace de l'écran.
CameraIndex
L'indice de la caméra pour laquelle l'opération sera effectuée, qui est la valeur de la variable number de l'objet caméra correspondant. Passez 0 ou rien si vous voulez effectuer cette opération pour la caméra par défaut.
© Christoph Peters. Certains droits réservés. (Traduction FR 04/2008, Franck Vernel / Damien Buhl).