Introduction à Ultimate 3D

Le fait que vous lisiez ce texte me laisse penser que vous avez décidé d'essayer Ultimate 3D. C'est un bon choix. Si vous avez déjà essayé de faire des jeux 3D avec les fonctions de 3D Game Maker, vous aurez remarqué qu'il est incroyablement difficile de vraiment faire de la belle 3D avec ces dernières. Et si vous avez réussi à faire de très bonnes images avec celles-ci (ce qui a certainement pris beaucoup de temps), vous aurez remarqué que cela demande un PC qui pourrait lancer des jeux haut de gamme.

Game Maker est un programme permettant de réaliser des jeux facilement, mais ses fonctions d'affichage 3D sont très rudimentaires. Elles ne sont pas destinées à être utilisées pour créer des graphismes 3D complexes. Si les gens les utilisent à cette fin, ils auront beaucoup de difficultés. Donc, si vous voulez faire des graphiques 3D complexes avec Game Maker vous aurez besoin de l'extension. Vous avez peut-être déjà essayé auparavant une autre extension pour faire de la 3d avec Game Maker. Mais la plupart d'entre elles sont assez difficiles à utiliser, n'est-ce pas?

C'est pourquoi il existe Ultimate 3D. Ultimate 3D est une tentative d'extension simple à utiliser dans Game Maker, afin de faire des graphiques 3D extrêmement complexes. Avec les anciennes versions d'Ultimate 3D (c. 1.0 à 1.31), vous pouviez faire des graphiques 3D plutôt sympas, mais il n'y avait pas beaucoup de fonctionnalités pour gérer des effets spéciaux, et pas suffisamment de possibilités pour optimiser les jeux qui proposent de très gros niveaux. C'est la raison pour laquelle j'ai passé plus d'un an sur le développement d'Ultimate 3D c. 2.0.
Il possède des tonnes de nouvelles fonctions qui vous donnent la capacité de faire des jeux vidéos incluant de superbes effets spéciaux et des niveaux géants, sans que ces jeux vidéos ne souffrent de ralentissements. En fait, c'est une refonte complète. Ultimate 3D 2.0 est enfin devenu ce qu'il était censé être. Et Ultimate 3D 2.1 le complète avec de nouvelles fonctions pour la détection et la réponse des collisions. Ainsi que beaucoup d'autres fonctionalités, petites, mais fort utiles.

L'un des principes les plus élémentaires d'Ultimate 3D, est qu'il est censé être extrêmement facile et convivial à utiliser, les didacticiels sont tous très détaillés. Ils sont divisés en deux parties, l'une pour les utilisateurs débutants et l'autre pour les utilisateurs avancés. Cette subdivision a pour but de vous faciliter le démarrage avec Ultimate 3D. Les fonctions décrites dans la partie pour les utilisateurs débutants sont vraiment faciles à utiliser. Cela vous prendra du temps pour comprendre comment elles fonctionnent, mais elles vous permettront déjà de créer des jeux super sympas. Toutefois, quand vous apprendrez les fonctionnalités décrites dans la partie destinée aux utilisateurs avancés, cela vous donnera la possibilité de faire d'incroyables graphismes. En utilisant le système de gestion des shaders d'Ultimate 3d, vous pouvez créer des effets spéciaux vraiment évolués. Aussi les fonctionnalités intégrées telles que le bump mapping, le parallax mapping ou le moteur de rendu de terrain, sont vraiment impressionnantes, et simples à utiliser.

Quoi qu'il en soit, vous le verrez par vous-même. Amusez-vous tout en apprenant à développer des jeux 3D.

Crédits

Des remerciements spéciaux vont à Matthew Unrath (Harkathmaker), qui a contribué à la correction grammaticale de ce fichier d'aide et à MysteriXYZ, qui a amélioré ce fichier d'aide en termes de contenu et de mise en forme et qui a également écrit un bel épilogue. Aussi je tiens à remercier tous ceux qui ont aidé à trouver des erreurs dans ce fichier d'aide. Vos commentaires améliorent constamment la qualité de la documentation d'Ultimate 3D.

© Christoph Peters. Certains droits réservés. (Traduction FR 04/2008, Franck Vernel / Damien Buhl).

Creative Commons License XHTML 1.0 Transitional


Prise en main

Commençons par le début

Avant de pouvoir commencer à utiliser Ultimate 3D, vous avez besoin des fichiers Game Maker sur lesquels travailler. Il est évident que vous ne pourrez pas le faire avec un projet vide. Vous avez besoin des scripts et des objets de base d'Ultimate 3D. Si vous avez vu la démo qui accompagne Ultimate 3D, vous pouvez l'utiliser comme une base pour vos jeux. Mais je ne le recommande pas. Si vous voulez vraiment faire votre propre jeu, vous devrez commencer à partir de zéro, dans ce cas les éléments de la démo ne seront pas d'une grande utilité pour vous. Si vous voulez juste modifier la démo d'une certaine manière, vous pouvez l'utiliser. Mais les gens le remarqueront et cela risque d'être mal perçu lorsque vous publierez votre jeu.

Vous devriez commencer à faire vos premiers projets Ultimate 3D en créant un nouveau dossier et en copiant le fichier BasicGM5.gmd (cf. BasicGM6.gm6 ou BasicGM7.gmk). Ce fichier est présent dans le Ultimate 3D Software Development Kit (U3D SDK) qui peut être téléchargé sur Ultimate3D.org, sur FKTECH ou sur Le CBNA. Ce fichier Game Maker contient tout ce dont vous avez besoin pour démarrer avec Ultimate 3D (à l'exception des connaissances de base qui se trouve dans la documentation que vous lisez actuellement). Il y a par exemple, un exemple de chaque type d'objet 3D Ultimate, ainsi que l'objet control qui est nécessaire pour utiliser Ultimate 3D. Une seule room contient une combinaison indéfinie de ces objets. Lorsque vous commencez à faire un jeu, vous devez supprimer tous les objets, sauf l'objet de contrôle et l'objet caméra.

Il y a un certain nombre de choses que vous allez remarquer au sujet de cette room. Si vous créez une nouvelle room qui est destinée à être utilisé avec Ultimate 3D, elle doit satisfaire à ces exigences:
- La room utilise les vues (views) et la première vue (view) doit être activée.
- La couleur de fond (background) est désactivée. Ce n'est pas nécessaire mais c'est une perte de temps de calcul, si vous laissez Game Maker afficher la couleur du background.
- La room ne doit pas contenir plus d'un objet de contrôle et plus d'un objet de caméra.

Je vous recommande de créer deux sous-dossiers dans le dossier de votre projet. Appelez le premier "gfx" et utilisez le pour stocker les textures de votre jeu. Appelez le second "models", j'imagine que ce qu'il est censé contenir vous paraît évident. Mais c'est à vous de décider.

Le système de view

Il y a une chose au sujet de l'utilisation d'une extension 3D pour Game Maker que vous n'apprécierez pas. Lorsque vous utilisez Ultimate 3D, le résultat des traitements, s'affichera dans la première view de Game Maker. Tant qu'Ultimate 3D utilise la view, vous ne pourrez plus utiliser Game Maker pour dessiner sur cette même view. En effet, il n'est tout simplement pas possible pour deux programmes totalement différents, d'afficher leur rendu sur la même partie de l'écran simultanément. C'est pour cette raison qu'Ultimate 3D ne possède pas que des fonctions pour dessiner des scènes 3D, il y a aussi des fonctions de dessin 2D et de texte. Toutefois, la première view ne doit pas couvrir l'ensemble de la fenêtre de jeu. Vous pouvez utiliser plusieurs views, et aussi longtemps qu'elles ne couvriront pas la view d'Ultimate 3D, il n'y aura aucun problème pour les utiliser avec les fonctions de Game Maker. Une autre chose à savoir est que vous ne pourrez pas passer en mode plein écran en utilisant Ultimate 3D. Si vous désirez passer en mode plein écran, il vous faudra l'activer dans les options de GameMaker, ou dans le script Ultimate3DOptions ().

L'object Caméra

Bon, passons au premier élément du côté pratique : l'objet caméra. Par défaut, l'objet caméra possède un morceau de code pour le contrôle de la vue par la souris et fait plusieurs appels à la fonction move d'Ultimate 3D dans les évènements du clavier (keyboard events). Si vous ne les aimez pas, vous êtes libres de les enlever. Mais comme un impoli je ne vous ai pas présenté la fonction move. Jetez-y un coup d'oeil:

Cette fonction déplace dans un environnement en 3d l'objet qui l'appelle.

Move(
Longitude,
Latitude,
Speed
)

Longitude, Latitude

Ces deux paramètres décrivent la direction que l'objet doit emprunter. Cela fonctionne de la même façon que sur un globe. Longitude décrit de quelle façon l'objet se déplace vers le haut ou vers le bas. 0 signifie que cela ne change pas sa hauteur, 90 signifie qu'il se déplace en ligne droite vers le bas et -90 signifie qu'il se déplace en ligne droite vers le haut. Latitude donne la direction horizontale à l'objet de la même façon. Notez que celle-ci diffère de la variable direction de Game Maker. Pour convertir une valeur de direction Game Maker en une valeur de latitude Ultimate 3D, sans en changer le sens, il vous suffit d'utiliser cette petite formule: Latitude=direction+90.

Vitesse
Ce paramètre définit la vitesse de l'objet que vous souhaitez déplacer. Notez que vous devez appeler la fonction à chaque step afin que l'objet se déplace à cette vitesse.

A partir de maintenant, toutes les fonctions avec des paramètres seront décrites comme ceci. Si vous voulez seulement une information rapide tout en programmant, vous pouvez également ouvrir le script du même nom. Chaque script contient une brève description de la fonction et de ses paramètres.

Cette fonction déplace un objet appellé dans un espace 3D.

Move(
Longitude,
Latitude,
Speed
)

Longitude, Latitude

Ces deux paramètres décrivent la direction de l'objet devrait emprunter. Il fonctionne de la même façon que sur un globe. Longitude décrit comment l'objet se déplace vers le haut ou vers le bas. 0 signifie que cela ne change pas sa hauteur, 90 signifie qu'il se déplace en ligne droite vers le bas et -90 signifie qu'il se déplace en ligne droite vers le haut. Latitude donne la direction hrizontale à l'objet de la même façon. Notez que celle-ci diffère de la variable direction de Game Maker. Pour convertir une valeur de direction Game Maker en une valeur de latitude Ultimate 3D sans en changer le sens, vous avez juste à utiliser cette petite formule:Latitude=direction+90.

Vitesse

Ce paramètre donne la vitesse de l'objet que vous souhaitez déplacer. Notez que vous devez appeler cette fonction à chaque étape (step) pour faire se déplacer l'objet à cette vitesse.

A partir de maintenant, toutes les fonctions avec ces paramètres seront décrites comme ceci. Si vous voulez juste une information rapide tout en programmant, vous pouvez également ouvrir un script. Chaque script contient une brève description de la fonction et ses paramètres.

En passant, vous avez peut-être remarqué que Move(...) est écrit avec une majuscule toutes les autres fonctions d'Ultimate 3D. Cela a deux raisons. La première, je suis habitué à appeler mes fonctions comme ça en C++. La seconde, cela vous aide à voir si une fonction appartient à Game Maker ou à Ultimate 3D; c'est plus facile de cette façon. S'il n'y avait pas cette différence là, il y aurait eu un préfixe. Donc Move ne serait pas appelé Move mais u3d_move. À mon avis, il est beaucoup plus facile d'appuyer sur la touche Shift parfois que d'écrire u3d_blablabla... tout le temps. En plus, c'est mieux visuellement.

Mais revenons-en à notre sujet... l'objet caméra. Dans Ultimate 3D, tout objet 3D dans une scène doit être représenté par une instance d'objet de Game Maker. Vous pouvez alors modifier les propriétés des objets de la scène en changeant simplement les variables de leurs objets Game Maker. Une fonction Ultimate 3D telle que MoveCamera() les transmettra à Ultimate 3D automatiquement. Les objets caméras possèdent les variables suivantes:

x, y, z
Ces variables donnent la position de la caméra. Puisque nous sommes dans un espace 3D désormais, il n'y a plus que x et y seulement. Ils ont maintenant un nouvel ami qui s'appelle z. Il définit la hauteur de l'objet. Donc, si vous augmentez z, l'objet se déplacera vers le haut, si vous diminuez z, l'objet se déplacera vers le bas. On appelle coordonnée la combinaison de x, y et z. Vous devez retenir cette expression. Elle apparaîtra à nouveau.

height
Dans un jeu à la première personne, la caméra a habituellement une hauteur particulière au-dessus du sol. Cette hauteur peut être implémentée en utilisant cette variable. La coordonnée z de la caméra (celle avec laquelle travaille Ultimate 3D) n'est pas z, mais z+height.

rotx, roty, rotz
Ces variables définissent l'angle de rotation de la caméra. En bref, rotx décrit le "pitch" de l'objet, roty décrit le "yaw" et rotz décrit le "roll". Une description plus détaillée de ce que signifient ces variables est donnée dans la seconde partie du tutoriel "Utilisation des objets primitifs».

view
Cette variable définit le double angle de vue vertical de la caméra. Comme avec n'importe quel angle dans Ultimate 3D, il doit être en degré.

min_range, max_range
La caméra affichera uniquement les objets qui sont plus loin que min_range et plus proche que max_range. Tous les autres objets seront invisibles. Vous devrez utiliser des valeurs raisonnables pour ces variables. Si min_range est trop petit ou max_range trop grand, vous obtiendrez d'étranges erreurs d'affichage, telles que des objets se mettant à couvrir d'autres objets qui sont normalement devant eux.

falling, falling_speed (optionnel)
Si falling est égale à true, la coordonnée z de la caméra, sera augmentée par falling_speed à chaque step et falling_speed sera diminué par global.gravitation (en diminuant, je veux dire qu'à chaque step falling_speed deviendra plus petit. Les nombres négatifs baissent la vitesse et ceux positifs augmente la vitesse). Ces variables sont destinées à être utilisées en combinaison avec les fonctions de collisions prédéfinies: CollisionH(), CollisionV(), CollisionZ() et CollisionTerrainModel().

follow, perspective, rotatex, rotatey, distance (optionnel)
follow peut prendre l'ID d'une instance d'un objet GameMaker, afin que la caméra suive cet objet. L'objet suivi peut avoir une variable height, dans l'idée que la caméra suive un point au dessus ou en dessous de la position réelle de l'objet. perspective définit la façon dont la caméra suit l'objet. Cela peut être 0 pour une caméra à la première personne, 1 pour une caméra fixe à la troisième personne, 2 pour une caméra flexible à la troisième personne. Si perspective est égale à 1 ou 2, vous pouvez changer rotatex et rotatey pour compenser le centre de rotation. distance définit à quelle distance de l'objet doit se trouver la caméra pour le suivre.

Pour le moment, c'est tout ce que vous avez besoin de savoir sur l'objet caméra. En fait, il y a un peu plus de variables et de fonctions pour les caméras, mais ces dernières sont décrites dans les chapitres pour les utilisateurs avancés.

Maintenant vous savez comment créer un objet caméra. Malheureusement, il n'y a rien qui s'affichera pour le moment (du moins si vous avez nettoyé la room). Mais ne vous inquiétez pas, cela va changer très bientôt.

Charger des textures

Avant de créer les premiers objets visibles, il y a une chose que vous devez savoir à propos du chargement des textures. Pour ceux qui ne connaissent pas, voici une courte explication de ce qu'est une texture: Une texture est (généralement) une image en deux dimensions qui est créé par une application utilisant des graphismes 3d. Ensuite, cette image peut être appliquée à un objet. Sans textures, tous les objets graphiques 3D seraient constitués d'une seule couleur, ce serait vraiment ennuyeux. Pour cette raison, il est très important d'avoir de bonnes et de belles textures. Cela rend vos jeux beaucoup plus réalistes.

Pour charger une texture en mémoire vous avez seulement besoin d'une fonction très simple:

LoadTexture(
TextureFile,
TextureIndex,
Width, Height
)

TextureFile
Etant donné que depuis Ultimate 3D on ne peut pas accéder aux ressources de Game Maker, la texture doit être sauvegardé dans un fichier externe. Ce fichier peut être un bmp, jpg, tga, png, dds, ppm ou fichier dib (PAS .gif). Je vous recommande d'enregistrer les textures dans un sous dossier appelé "gfx". Mais si vous voulez cacher vos textures aux gens qui jouent à votre jeu, vous pouvez aussi les joindre à votre exécutable en utilisant les data file de Game Maker, et les charger à partir de son répertoire temporaire (Variable GameMaker: temp_directory).

TextureIndex
L'indice de la texture, que vous souhaitez associer à cette dernière. Cela peut être un nombre arbitraire dans la fourchette de 0 à 998. Un indice est semblable à un id. Si vous voulez utiliser la texture plus tard, vous devez connaître l'indice associé avec celle-ci.

Width, Height
D'usage, vous pourrez tout simplement ignorer ces deux paramètres. Si vous entrez 0 ou rien pour chacun d'eux, Ultimate 3D obtiendra la hauteur et la largeur des dimensions de la texture. Mais si vous entrez des valeurs qui diffèrent de la taille réelle de la texture, elle sera réduite à cette taille. Notez que seules les puissances de deux ou zéro peuvent être passées pour ces paramètres. Pour plus d'informations, lisez ce qui suit.

Il y a encore une chose importante que vous devez savoir à propos des textures. La plupart des périphériques graphiques fonctionnent uniquement avec des textures qui ont des dimensions composées de puissances de deux. En outre, certains périphériques graphiques ne peuvent fonctionner qu'avec des textures du second degré, mais c'est un cas rare. Quoi qu'il en soit, vos textures seront étendues si elles n'ont pas les dimensions composées de puissances de deux, afin de leur donner des dimensions valides. Pour éviter cette perte inutile de qualité, vous devez utiliser ces dimensions pour toutes vos textures. Quelques exemples valables pour les dimensions sont 256*256, 128*512 et 1024*64. Une texture d'une taille de 257*257 qui serait étendue jusqu'à 512*512 serait un véritable gaspillage de mémoire.

Une autre chose que vous avez besoin de savoir c'est comment faire des textures transparentes. Il y a deux façons de le faire. Le mieux est d'utiliser un format de fichier qui est favorable à la transparence, tels que le tga ou png. The Gimp (GNU Image Manipulation Program) accepte ces formats de fichier. C'est un superbe programme d'édition d'image Open Source. Si vous préférez utiliser d'autres formats de fichiers, vous pouvez obtenir la transparence de toutes façon. Il suffit d'utiliser la couleur de transparence qui est (r = 0, g = 128, b = 64). Tous les pixels qui auront exactement cette couleur seront définis comme transparents par Ultimate 3D.

Je vous conseille de charger toutes les textures dans l'évènement "create" de l'objet control. De cette façon, vous pouvez obtenir une liste de toutes les textures chargées et de leurs indices. Mais ça, c'est encore à vous de décider.

Voici un tout petit exemple de code qui montre comment utiliser cette fonction correctement :

LoadTexture("gfx/WallTexture.png",1);


Utiliser les objets primitifs

Le concept derrière les objets primitifs (en bref, primitives) est de vous donner la possibilité de créer facilement des scènes 3d simples. Un grand choix de primitives peut ainsi être rendues de manière très efficace. Définir un objet dans Ultimate 3D fonctionne de la façon suivante. Comme je l'ai déjà dit, chaque objet dans Ultimate 3D doit être représenté par une instance d'objet dans Game Maker. Donc, vous devez créer un nouvel objet d'abord. Ensuite, vous devez définir un ensemble de variables pour cet objet. Enfin vous appelez une fonction qui transmet les valeurs des variables à Ultimate 3D et crée votre objet. Nous pouvons commencer avec un premier objet visible.

L'Objet Wall (mur)

Un objet wall est totalement plat et orienté verticalement. Avant de le créer, vous devez définir les variables suivantes:

height
Cette variable indique la hauteur du mur.

x2, y2
Ces deux variables indiquent jusqu'où le mur va à partir de ses positions x et y. Le graphique suivant l'illustre:

Illustration not available

texture
Vous devez assigner cette variable à l'un des indices que vous aviez attribué précedemment à une texture lors de l'utilisation de la fonction LoadTexture(...). Cette texture sera alors utilisée pour le mur. Si vous ne souhaitez pas utiliser une texture, vous pouvez définir cette variable à -1.

texx1, texy1, texx2, texy2 (optionnel)
Ces variables sont facultatives. Si vous n'avez pas définit ces variables, toute la texture sera utilisée. Ils définissent les deux prétendues coordonnées de la texture. Les coordonnées de texture x = 0 et y = 0 (en bref (0|0)) se réfèrent à la partie supérieure gauche de la texture. Les coordonnées de texture (0.5|0.5) correspondent au milieu d'une texture, les coordonnées de texture (0|1) à la partie inférieure gauche de la texture, les coordonnées de texture (1|1) à la partie inférieure droite, et ainsi de suite. L'objet mur utilisera la partie de la texture qui se trouve entre les deux données des coordonnées de la texture. Les coordonnées de texture peuvent aussi être faites pour des valeurs plus grandes que 1. Ensuite, la texture sera appliquée (de la même façon que le tuilage mais pour les textures)

Il y a deux variables supplémentaires qui sont utilisées pour les murs mais pour l'instant, vous n'avez pas besoin de les connaitre. Les primitives Floor et Cube les possèdent elles aussi. Elles sont décrites dans le tutorial "Ajout de lumières".

Maintenant que vous avez mis en place toutes les variables requises, vous pouvez appeler CreateWall()une fois. Cette fonction ne prend aucun paramètre. Elle reçoit toutes les informations nécessaires à partir des variables. Cependant, il faut mettre Step() dans l'évènement step de l'objet et Destroy(), dans l'évènement destroy de ce dernier. Aucune de ces fonctions ne requièrent de paramètres. Destroy () doit être mis dans l'évènement destroy parce qu'autrement Ultimate 3D ne saurait jamais quand l'instance serait détruite. Dans ce cas, l'instance ne disparaîtrait tout simplement pas. Ce que Step () fait est le thème de la prochaine partie de ce tutoriel. Donc accrochez-vous ! ;) .

Transformation des primitives

Transformer... étrange mot. Cela paraît un peu compliqué, n'est-ce pas ? Eh bien, n'ayez pas peur, vous savez déjà comment transformer quelque chose; vous ne saviez juste pas que c'était appelé "transformation". Transformer quelque chose implique de changer son orientation. Si vous prenez un objet et le déplacez, c'est une transformation. Si vous le faite pivoter c'est une transformation aussi. Il y a une transformation qui ne peut se faire dans la vraie vie, mais vous le savez déjà de la 2D: scaler quelque chose. Scaler décrit les moyens de changer la taille d'un objet. Un objet peut être extrapolé (scalé) sur trois axes. Tous ces processus sont appelés transformations. Donc, la chose qu'il faudra retenir est que la transformation est la combinaison du scaling (changement de taille), la rotation et la translation (déplacement).

Et maintenant pour transformer les primitives. Vous savez comment le faire, car vous avez transformé un autre objet avant (si vous lisez ce tutorial complètement): l'objet caméra. Comme l'objet caméra, chaque objet primitif contient les variables x, y, z, rotx, roty et rotz. En outre, il gère également les variables scalx, scaly, scalz. Etant donné que l'explication de l'utilité de rotx, roty et rotz n'était pas détaillée dans le tutorial à propos des Objets caméras, voici une description plus détaillée, ainsi qu'une description de scalx, scaly, et scalz.

rotx, roty, rotz
Comme je l'ai déjà dit dans le tutoriel sur les Objets caméras, la variable rotx donne la rotation de l'objet (le pitch), la variable roty donne la rotation de lacet (yaw) et la variable rotz donne la rotation de roulis (roll). Le schéma ci-dessous illustre l'effet de ces variables.

Illustration not available

Incrémenter rotx pivotera le nez de l'avion vers les bas, décrémenter rotx pivotera le nez de l'avion vers les haut. La rotation de lacet : yaw (roty) dirige l'objet sans changer sa coordonnée z. Enfin, il y a la rotation de roulis (roll). Je pense qu'il n'y a rien de plus à dire à propos. Notez que la rotation de roulis (roll) est appliquée en premier, ensuite la rotation pitch et seulement la rotation de lacet (yaw) est réalisée.
Un bref commentaire à ce sujet: Si vous avez lu attentivement, vous aurez peut-être remarqué que roty tourne autour de l'axe z alors que rotz tourne autour de l'axe des ordonnées (y). Cette petite incohérence est un vestige de la première version d'Ultimate 3D. Ces variables sont nommées selon le système de coordonnées qui est utilisé par Ultimate 3D en interne. Vous n'avez pas besoin de pécisions à ce sujet, il suffit de garder à l'esprit que roty tourne autour de l'axe z et rotz tourne autour de l'axe des ordonnées (y).

scalx, scaly, scalz
La notion de changement d'échelle (scaling) est très facile à comprendre. Une échelle d'une valeur 1 signifie que rien ne change, une échelle de valeur de 2 signifie que tout devient deux fois plus important, et une échelle de valeur de 0.5 signifie que tout devient moitié de la taille. Habituellement, vous ne devez pas utiliser d'échelle de valeur négative. La seule chose qui me reste à vous expliquer est pourquoi, il y a trois valeurs d'échelle (scaling). Parce que de cette façon vous avez la possibilité de scalé la grosseur de l'objet indépendamment dans les trois axes (x, y et z) du système de coordonnées 3D. Mais dans la plupart des cas, vous voudrez scaler votre objet de la même manière tout au long des trois axes, vous devrez alors définir la même valeur pour les trois axes d'échelle. Notez que le scaling se fait avant la rotation.

Une autre chose que vous ne devez jamais oublier, c'est que chaque objet Ultimate 3D doit contenir un appel à la fonction Step() dans leur évènement step. Sinon, les variables n'auront pas d'incidence sur la transformation du modèle!

Objets Sol

Les objets Floor n'ont pas de taille le long de l'axe z, ce qui signifie qu'ils sont totalement plats et horizontaux. Les variables que vous devez mettre en place avant la création d'objets Floor sont les suivantes:

width, height
width décrit la taille du sol le long de l'axe x et height la taille du sol le long de l'axe y.

texture
Indice de la texture qui va être utilisée pour cet objet ou -1 pour aucune. Pour une description plus détaillée, allez jeter un oeil sur le texte d'Objets Wall.

texx1, texy1, texx2, texy2 (optionnel)
La partie de la texture qui va être utilisée. Pour une description plus détaillée, allez jeter un oeil sur le texte d'Objets Wall.

Comme vous pouvez le voir, il n'y a pas beaucoup de changement par rapport aux objets wall. À nouveau, il y a deux autres variables qui seront introduites dans le tutorial à propos des sources de lumière. Après l'affectation de ces variables, vous devez appeler CreateFloor()pour initialiser l'objet floor. Et comme d'habitude, vous avez besoin de Step() et Destroy() dans les événements correspondants.

Objets Cube

Les objets cube sont un peu plus complexes que les sols et les murs, mais ils peuvent être utilisés pour des choses plutôt cool. Dans l'ancienne démo d'Ultimate 3D v. 1.31 tous les bâtiments étaient constitués de simples cubes. Comme avec d'autres primitives, vous devez configurer plusieurs variables avant de pouvoir créer un cube. Voici une liste de ces dernières:

width, height, depth2
Les dimensions du cube. width donne la largeur du cube (axe x), depth2 donne la profondeur du cube, soit sa taille sur l'axe y et height donne la taille du cube sur l'axe z. depth2 s'appelle "depth2" au lieu de "depth", parce que la variable depth est déjà utilisée par Game Maker.

texx1, texy1, texz1, texx2, texy2, texz2 (optionnel)
La partie de la texture qui va être utilisée. Pour une description plus détaillée de ces paramètres, allez jeter un oeil sur le texte des Objets Wall. Etant donné que les objets cube sont en trois dimensions alors que les murs sont en deux dimensions, les objets ont trois paires de ces paramètres.

texture
L'indice de la texture qui va être utilisée pour cet objet ou -1 pour aucune. Pour une description plus détaillée allez jeter un oeil sur le texte sur les Objets Wall.

subdivide_texture (optional)
Comme chacun le sait, un cube a six faces. Si vous définissez cette variable à "false" toutes les faces utiliseront la même partie de la texture. Si vous la définissez à "true", la texture sera subdivisée en six parties et chaque côté du cube utilisera une autre partie de la texture. Le schéma suivant montre comment cela fonctionne.

Illustration not available

Quand subdivide_texture est "false", l'ensemble de la texture est affichée de chaque côté. Quand il est "true", chaque côté ne montre qu'une seule partie.

originx, originy, originz (optionnel)
Ces variables prennent habituellement des valeurs dans la fourchette de 0 à 1. Elles définissent où l'origine du cube se trouve. Par exemple, la saisie de 0.5 pour chacune d'entre elle voudrait dire que l'origine du cube se trouve exactement au milieu. Saisir les valeurs correctes pour ces variables est très important, surtout lorsque vous faites pivoter le cube. Le schéma suivant en illustre le mécanisme:

Illustration not available

Après l'affectation de ces variables, vous devez appeler la fonction CreateCube()une fois pour initialiser l'objet cube. Et comme d'habitude, vous avez besoin de Step() et Destroy() dans les événements correspondants.

Objets Billboard

Les billboards diffèrent un peu de tous les autres objets 3D. La raison en est simple. Ils ne sont pas en 3D. Il arrive souvent que de la géométrie extrêmement complexe soit requise pour certains objets. Les arbres sont le meilleur exemple. Pour éviter cette géométrie complexe, vous pouvez utiliser les billboards. Dans les jeux anciens, vous voyez souvent que les arbres sont en 2D alors que tout le reste est en 3D. Voici les billboards. Des billboards sont des objets 2D dans un espace 3D. Ils sont fixés à un point dans un espace 3D et leur taille sur l'écran change en fonction de la distance de la caméra.

Quoi qu'il en soit, la création de billboards n'est pas plus difficile que toute la création d'autres objets primitifs. Voici les variables qui doivent être mise en place pour cela:

width, height
Les dimensions du billboard.

originx, originy
Ces variables définissent l'origine du billboard. L'origine est le point du billboard qui ne change pas de position dans l'espace 3D lorsque la caméra pivote. En général il est quelque part au milieu du billboard. En principe, il fonctionne de la même façon que les variables originx/y/z des Objets Cube. Alors, jetez un coup d'œil à leur description.

texture
Indice de la texture qui va être utilisé pour cet objet ou -1 pour aucune. Pour une description plus détaillée de ces paramètres, allez jeter un oeil au texte d'Objets Wall.

texx1, texy1, texx2, texy2 (optionnel)
La partie de la texture qui va être utilisée. Pour une description plus détaillée de ces paramètres, allez jeter un oeil au texte d'Objets Wall.

Après la mise en place de ces variables, vous devez appeler la fonction Create2D() une fois pour initialiser l'objet billboard. Et comme d'habitude, vous avez besoin de Step() et Destroy() dans les événements correspondants.

Objets Polygônes

Dans la partie de cette documentation destinée aux utilisateurs avancés vous constaterez que tout modèle peut être manipulé comme un objet primitif. Cependant actuellement, vous ne savez pas que les objets polygones sont les primitives les plus complexes que vous allez connaître. Avant de vous dire ce qu'est un objet polygon, je vais vous expliquer que signifie le terme: vertex. Un vertex est un point dans un espace 3D, possèdant les coordonnées d'une texture, faisant partie d'un objet 3D. Un triangle est toujours constitué de trois vertices (sommets) et chaque objet 3D est constitué d'un ensemble de triangles. Donc on pourrait dire des vertices que c'est la plus petite unité des objets 3d.

Les polygones ne sont pas définis par un ensemble de variables. À la place de quoi vous appelez trois fonctions. Vous avez besoin d'une instance d'objet pour chaque objet polygon. La première fonction que vous devez appeler se nomme BeginPolygon(...). Cela informe Ultimate 3D que vous désirez créer un objet polygon et prépare tout ce qui est nécessaire dans ce but. Voici un description de la fonction:

BeginPolygon(
VertexNumber
)

VertexNumber
Le nombre de vertices (sommets) du polygone que vous vous apprêtez à créer. La valeur de cette variable doit toujours être supérieure ou égale à trois, sinon vous n'obtiendrez pas un véritable polygone.

Après avoir appellé BeginPolygon(...) vous devez appeler AddVertex(...) plusieurs fois en fonction de la valeur que vous avez passé à BeginPolygon(...). Voici la description de AddVertex(...):

AddVertex(
X,Y,Z,
U,V
)

X, Y, Z
La position du vertex. Notez que ces positions sont transformées selon les valeurs que vous avez choisies précedemment pour x, y, z, scalx/y/z et rotx/y/z. Ainsi, la position est donnée à l'échelle de l'objet.

U, V
La coordonnée de la texture du vertex. Pour obtenir une description détaillée de ce qu'est une coordonée de texture, lisez la description des variables texx1 et texy1 des Objets Wall.

Lorsque vous avez appelé AddVertex(...) suffisamment, vous pouvez appeler CreatePolygon()pour créer l'objet polygon. Comme pour tous les autres objets primitifs, il possède la variable texture, et les variables de transformation (pour plus d'information jetez un coup d'œil à la description des Objets Wall au sujet de la transformation des primitives). Pour cette raison, vous devez mettre Step() et Destroy() dans les évènements Game Maker correspondants.

Voici un exemple de code qui montre comment créer un objet polygon simple:

BeginPolygon(5);
AddVertex(0,0,0,0,1);
AddVertex(0,0,5,0,0.33);
AddVertex(0,2.5,7.5,0.5,0.0);
AddVertex(0,5,5,1,0.33);
AddVertex(0,5,0,1,1);
CreatePolygon();
texture=1;

Cette image montre le résultat de l'appel de ces fonctions:

Modifier des objets polygônes

Chacun des vertex d'un objet polygon peut être modifié après la creation du polygône. Pour faire cela, il y a deux fonctions: l'une pour récupérer des données à propos du polygône, et l'autre pour changer ces données. Voici une description de deux d'entre elles:

Cette fonction permet d'extraire des données de l'objet polygône qui appelle la fonction. Elle peut être appelée après CreatePolygon().

GetVertex(
VertexIndex,
ElementIndex
)

VertexIndex
Ce paramètre permet d'identifier le vertex duquel vous souhaitez obtenir des informations. Le vertex qui a été ajouté en premier au polygône a l'indice 0. L'indice le plus élevé valable est VertexNumber-1.

ElementIndex
Ce paramètre indique dans Ultimate 3D quel type d'information doit être récupéré.
Voici la signification de chaque valeur:
0 = X;
1 = Y;
2 = Z;
3 = U;
4 = V;

Cette fonction modifie les données de l'un des vertices d'un objet polygône qui appelle la fonction. Elle peut être appelée après CreatePolygon().

SetVertex(
VertexIndex,
X,Y,Z,
U,V
)

VertexIndex
Ce paramètre permet d'identifier le vertex dont vous souhaitez modifier les informations. Le vertex qui a été ajouté en premier au polygône a l'indice 0. L'indice le plus élevé valable est VertexNumber-1.

X, Y, Z
La position du vertex. Pour plus d'informations, jettez un oeil à la description de la fonction AddVertex().

U, V
Les coordonnée de la texture du vertex. Pour obtenir une description détaillée de ce qu'est une coordonnée de texture, jetez un œil à la description des variables texx1 et texy1 Objets Wall.

C'est tout ce dont vous avez besoin pour modifier la géométrie d'un polygone. Voici un exemple pour illustrer cela. Placé dans l'évènement step de l'objet polygone, le troisième vertex se déplace un peu vers le haut à chaque step.

SetVertex(2, GetVertex(2,0), GetVertex(2,1)+0.1, GetVertex(2,2), GetVertex(2,3), GetVertex(2,4));

Changer les matériaux (couleurs) des primitives

Dans sa forme la plus simple, en changeant les matériaux d'un objet on entend "changer sa couleur". Pour ce faire Ultimate 3D dispose d'une fonction très simple:

Cette fonction change l'étoffe (ses couleurs) de l'objet primitive qui est appellé.

SetMaterial(
Red,
Green,
Blue,
Alpha
)

Red, Green, Blue
Ces paramètres définissent la nouvelle couleur qui va être utilisée. Les valeurs des paramètres peuvent être de l'ordre de 0 à 255. (0 | 0 | 0), le noir et (255 | 255 | 255) signifie blanc.

Alpha
Ce paramètre donne l'opacité de l'objet. Il est défini de 0 à 255. 0 signifie qu'il n'y a pas d'opacité (totalement transparent) et 255 signifie qu'il est totalement opaque.

Cette fonction modifie la partie émissive d'un matériau. La partie émissive d'un matériau n'est pas influencée par l'éclairage. Un bon exemple pour un matériau qui devrait utiliser l'éclairage émissif est un écran de TV. Il émet de la lumière ainsi sa luminosité n'est pas relative à la lumière. Vous pouvez également utiliser des matériaux émissifs si vous ne voulez pas que des objets soient influencés par la lumière. Par exemple, il semble généralement assez bizarre que les billboards soient influencés par la lumière.

SetMaterialEmissive(
Red,
Green,
Blue
)

Red, Green, Blue
La couleur qui doit être utilisé pour la partie émissive de la matière. Là encore, les valeurs doivent être contenue entre 0 et 255.

Mise à jour des objets primitifs

Vous savez déjà que vous pouvez changer la transformation d'une primitive après sa création en utilisant les variables x, y, z, rotx/y/z, scalx/y/z. La même chose peut être faite avec la texture. Elle peut être modifiée à tout moment. Mais qu'en est-il des autres propriétés d'une primitive? Par exemple, si vous souhaitez animer une texture en changeant texx1, texy1, texx2 et texy2? Si vous changez ces variables après la création de la ligne primitive, l'objet ne change pas. Pour cette raison, il y a la fonction RecreatePrimitive(). Cette dernière fait exactement ce que son nom indique. Elle détruit les primitives qu'elle recrée ensuite avec les nouvelles données des variables.


Changer l'environnement

Désormais, vous êtes en mesure de créer des scènes simples en utilisant des objets primitifs. Mais une scène n'est pas seulement composée d'objets. Il y a plus de choses qui font partie d'une scène complète. Certaines d'entre elles vont être décrites maintenant. Ce chapitre est vraiment facile, parce Ultimate 3D fait tout automatiquement. Il vous suffit de lui dire ce que vous voulez.

Eclairage ambiant

L'éclairage ambiant est la lumière présente partout. Si vous regardez autour de vous, dans la pièce où vous êtes vous noterez probablement, qu'il n'y a pas un endroit qui soit totalement sombre. Même si quelque chose n'est pas directement éclairé, il y a toujours suffisamment de lumière pour le rendre un peu clair. Bien entendu tout est en fait éclairé par quelque chose d'autre. Les objets réflechissent la lumière, et c'est ce qui éclaire les objets qui ne sont pas directement éclairés par des sources de lumières. Cependant simuler ce comportement prendrait une terrible quantité de temps de calcul. Pour cette raison, nous utilisons la lumière ambiante.

L'éclairage ambiant est la forme d'éclairage la plus simple que l'on peut imaginer. Tout est éclairé par cette dernière avec la même intensité et la même couleur. C'est pourquoi l'éclairage ambiant est composé d'une seule couleur. Cette couleur peut être modifiée par la fonction suivante:

SetAmbient(
R,
G,
B
)

R, G, B
La nouvelle couleur de la lumière ambiante. Les valeurs de ces paramètres doivent être comprises dans une fourchette allant de 0 à 255.

Par défaut, l'éclairage ambiant est la couleur (128 | 128 | 128). Donc, si vous écrivez SetAmbient(255,0,0) tout s'illuminera en rouge. Si vous écrivez SetAmbient (0,0,0) tous les objets, sauf pour ceux qui ont un matériau émissif deviendront totalement noirs. En changeant la luminosité ambiante, vous pouvez influer fortement sur l'ambiance de votre jeu. Vous pouvez alterner entre le jour et la nuit, ou vous pouvez créer une atmosphère apocalyptique à l'aide de la lumière rouge.

Configuration des images de fond 3D

Une autre chose très importante pour l'atmosphère est l'image de fond (background). Ultimate 3D supporte trois différents types de backgrounds. Actuellement, vous avez utilisé le premier en couleur unie. La couleur de fond peut être modifiée en changeant les valeurs des variables control.BG_r, control.BG_g et control.BG_b. Vous pouvez leur donner des valeurs comprises dans la fourchette allant de 0 à 255.

Le deuxième type d'image de fond est appelé skysphere. Dans ce cas, vous avez une image 2D qui contient une image 360° de l'environnement. Cette image est utilisée comme la texture d'une sphère qui entoure la scène. L'inconvénient des skysphere est que vous obtenez d'étranges distorsions dans la partie supérieure. Tous les degrés de latitude se rencontrent en un point. Ce n'est pas très joli. Mais dans la plupart des jeux il n'est généralement pas nécessaire de regarder tout droit verticalement. Donc, la plupart du temps les skyspheres sont utiles quand même.

Mais qu'en est-il des jeux où le fond est bon à regarder dans toutes les directions? Le meilleur exemple en ce sens est un space shooter. Des mots tels qu'en haut ou en bas n'ont pas de signification dans l'espace. Pour les cas comme celui-ci, Ultimate 3D supporte un autre type de fond qui ne conduit pas à des distorsions: un skycube. Un skycube n'est pas composé d'une image 2D, mais de six images du second degré qui se mettent ensemble dans un cube. Comme dans la sphère, le cube entoure la scène entière. Créer les images nécessaires aux skycubes est plus difficile que de créer des cartes sphèriques. Toutefois les résultats n'en sont que meilleurs.

Maintenant que vous savez quels types de backgrounds sont supportés, passons à la réalisation pratique. Il n'y a qu'une seule fonction pour charger des backgrounds:

Cette fonction charge soit une sphère map ou six textures pour une cube map en fonction du paramètre donné.

LoadBackground(
File
)

File
Vous devez saisir une chaîne pour ce paramètre. Si la chaîne contient seulement un nom de fichier, ce fichier sera chargé et utilisé comme texture pour le ciel-sphère. Si le fichier contient le signe * dans sa chaîne de caractères, ce signe sera remplacé par la mention "Front", "Back", "Top", "Bottom", "Left" et "Right". Donc Ultimate 3D charge six différents fichiers. Ils seront utilisés pour un skycube.

Si vous ne voulez plus utiliser un fond, vous pouvez appeler UnloadBackground(). Suite à quoi, la couleur unie sera à nouveau utilisée.

Configuration du brouillard

Un épais brouillard est rare dans la plupart des régions du monde. Dans les jeux sur ordinateur, il se produit plus souvent. Mais en réalité, il y a presque toujours un peu de brouillard. Les jours de pluie tout ce qui est loin devient gris et incolore. Les très chaudes soirées, tout ce qui est loin a un ton orangé très foncé. Ainsi, vous pouvez presque toujours utiliser le brouillard, il semble généralement assez sympa. Il participe à l'impression d'une réelle profondeur. Cependant, utiliser trop de brouillard fera penser aux gens que vous ne pouviez pas offrir une plus grande portée visuelle. Dans certains cas, cela peut effectivement être le cas. Dans ce cas, le brouillard est le moindre des deux maux. C'est toujours mieux que de voir tout le décors simplement disparaître lorsque celui-ci est trop éloigné de la caméra.

Configurer le brouillard n'est pas plus difficile que de créer une image de fond. Il y a une fonction pour l'activer et une pour le désactiver. Voici celle qui l'active:

SetFog(
R,
G,
B,
StartDistance,
EndDistance
)

R, G, B
La couleur du brouillard. Les valeurs de ces variables doivent être comprises dans une fourchette allant de 0 à 255.

StartDistance, EndDistance
Les valeurs de la distance du brouillard. Tout ce qui est plus proche de la caméra que StartDistance ne sera pas influencé par le brouillard. Tout ce qui est plus éloigné de la caméra que EndDistance aura la couleur du brouillard. Ultimate 3d interpolera linéairement tous les éléments présents entre ces valeurs.

Si vous voulez désactiver le brouillard, appelez simplement DisableFog().


Ajouter des sources lumineuses

L'éclairage ambiant est beau, mais il est évident que vous n'allez pas loin avec. Pour une scène vraiment vivante, vous avez besoin, de sources de lumières plus dynamiques, de choses qui n'éclairent pas tout de la même manière. Pour cette raison Ultimate 3D supporte l'éclairage par vertex (per-vertex lighting). Cela fonctionne de la façon suivante. Vous devez ajouter un ensemble de sources de lumière à votre scène. Ultimate 3D les utilisera ensuite pour calculer une couleur pour chaque vertex. Ces couleurs seront interpolées sur le triangle qui est connecté à ce vertex. Ultimate 3D supporte également l'éclaire par pixel (per pixel lighting). Mais c'est une des techniques des utilisateurs avancés.

Il existe trois types de sources lumineuses. Le premier est appelé lumière directionnelle. L'exemple classique pour une lumière directionnelle est le soleil. Elle éclaire tout depuis la même direction. N'importe où que vous soyez (en ignorant le fait que la terre est une sphère), le soleil semble toujours au même endroit dans le ciel. L'opposé d'une lumière directionnelle est un point de lumière. Un point de lumière (light point) peut être comparée à une ampoule qui pend au milieu d'une pièce. Le mur de gauche sera éclairé depuis la droite et la paroi de droite sera éclairé à partir de la gauche, parce que l'ampoule est suspendu entre ces deux murs. Plus quelque chose sera éloigné du point de lumière, moins cette chose sera éclairée par la source lumineuse. Le troisième type de source lumineuse est une spot light. Un spot light est semblable à un point de lumière (point light). Mais contrairement à un point de lumière (point light), il n'éclaire pas tout autour de lui, seulement les éléments se trouvant dans un cône de lumière.

Comme pour les objets primitifs, vous avez besoin d'une instance d'objet pour chaque source de lumière. Et à nouveau, vous devez affecter quelques variables d'abord, après quoi vous ajoutez un appel à une fonction pour créer la source de lumière et finissez par mettre les commandes Step() et Destroy() dans les événements correspondants.

Lumière directionnelle

Donc voici les variables dont vous avez besoin pour régler une lumière directionnelle.

rotx, roty
Les noms de ces deux variables sont vraiment des reliques d'une ancienne version d'Ultimate 3D, parce que je ne voulais pas briser la compatibilité avec les versions précédentes. Aujourd'hui je les appellerai longitude et latitude. Elles définissent la direction de la lumière directionnelle. Pour avoir une description de ce que signifie longitude et latitude, jetez un oeil (NDT: Pas trop fort quand même...) à la fonction Move(...) dans le chapitre "Prise en main".

r, g, b
Ces variables définissent la couleur de la source lumineuse. Les valeurs sont en général comprises dans une fourchette allant de 0 à 255. En augmentant les valeurs de ces variables, vous augmentez l'intensité de la lumière. Vous pouvez également spécifier des valeurs négatives pour ces variables. Ensuite, la source de lumière va assombrir les objets qu'il éclaire.

Les lumières directionnelles n'ont pas de position et ni d'effet de portée, tout simplement parce que ces données n'ont aucune importance. Les lumières directionnelles sont omniprésentes. Après la mise en place de ces variables, vous pouvez appeler LightDirectional() afin d'initialiser la source de lumière directionnelle. Si vous n'avez pas oublié d'inclure Step() dans le évènement step, vous pourrez changer tous les paramètres de toutes les sources de lumière à tout moment.

Point lumineux

Allons-y avec les variables des points lumineux.

x, y, z
Ces variables définissent la position du point source de lumière. Plus une chose sera proche de ce point, plus elle sera éclairé par le point de lumière.

range
La portée du point de lumière. Tout ce qui est trop loin de la source de lumière ne sera pas affecté par celle-ci. Plus grosse est la portée, plus forte est l'intensité de la lumière.

r, g, b
Ces variables définissent la couleur de la source lumineuse. Les valeurs sont en général comprises dans une fourchette allant de 0 à 255. En augmentant les valeurs de ces variables, vous augmentez l'intensité de la lumière. Vous pouvez également spécifier des valeurs négatives pour ces variables. Ainsi, la source de lumière assombrira les objets qui sont éclairées par cette dernière.

Après la mise en place de ces variables, vous pouvez appeler LightPoint() pour initialiser le point de lumière. Encore une fois, vous pouvez changer toutes les propriétés de la source de lumière, à tout moment, si vous n'oubliez pas Step() dans l'évènement correspondant.

Spot de lumière

Nous arrivons au dernier type de source de lumière, les spot lights. ils sont un peu plus complexes que les points lights et les lumières directionnelles, et requèrent plus de temps de calcul. Voici leurs variables:

x, y, z
Ces variables définissent la position de la source du spot light. Plus la chose est proche de ce point, plus elle sera éclairée par la lumière.

range
La portée du spot de lumière. Tout ce qui est le plus éloigné de la source de lumière ne sera pas affecté par celle-ci. Plus grosse est la portée, plus forte est l'intensité de la lumière.

rotx, roty
Ces deux variables définissent l'orientation du spot light. rotx est équivalente à la longitude et roty est équivalente à la latitude, dans laquelle le spot light éclaire.

deg
Cela définit l'angle qui est éclairé par le spot light. Si l'angle, entre les rayons de la source lumineuse allant jusqu'aux vertex et le vecteur directeur de la source de lumière défini par rotx et roty, est plus important que cette variable, le vertex ne sera pas éclairé par la source lumineuse. En d'autres termes, il contrôle la largeur du cône du spot light.

r, g, b
Ces variables définissent la couleur de la source lumineuse. Les valeurs sont en général comprises dans une fourchette allant de 0 à 255. En augmentant les valeurs de ces variables, vous augmentez l'intensité de la lumière. Vous pouvez également spécifier des valeurs négatives pour ces variables. Ainsi, la source de lumière assombrira les objets qui seront éclairées par celle-ci.

Après avoir affectées ces variables, vous pouvez appeler LightSpot() pour initialiser le spot lumineux, et si vous n'avez pas oublié l'appel à Step() dans l'évènement step, vous pourrez changer toutes les propriétés à n'importe quel moment.

Subdivision de primitives

Il est temps de respecter une promesse. Dans le chapitre à propos des primitives, j'ai dit que je n'avais pas listé toutes les variables des différents objets primitifs, et que quelques variables supplémentaires seraient introduites ici. Vous n'auriez pas compris pourquoi ces variables existent sans connaître les techniques d'éclairage basées sur les vertex. Alors voici ces nouvelles variables:

partsx, partsy, (partsz)
Ces variables peuvent être utilisées pour subdiviser les primitives le long d'un axe particulier. partsz est listée entre parenthèses car seul l'objet cube en a besoin. Bien entendu les polygônes ne disposent pas de ces variables. En subdivisant un primitif vous augmentez son nombre de vertex. Ainsi un primitif subdivisé sera éclairé avec plus détails, étant donné que l'éclairage est réalisé sur la base des vecteurs. Les variables peuvent être définies à n'importe quel entier positif. Mais n'en abusez pas! Cela peut superbement diminuer vos FPS.


Utiliser des objets modèles

Utiliser des objets de modèle

Si vous avez seulement utilisé les fonctions 3D de Game Maker auparavant, cette fonctionnalité va vous épater. Avec Game Maker, il est vraiment compliqué de charger un modèle. Dans Ultimate 3D vous appellez simplement une fonction. Elle peut charger des modèles animés tels que *.an8, *.3ds, *.md2, *.x, *.ms3d et *.u3d. Juste en étant à même de charger des modèles, vous pouvez créer des scenes extrêmement complexes avec de nombreux objets vraiments cools. Et vous ne trouverez pas beaucoup de programmes de modélisation qui ne soient pas capables d'exporter dans un des formats de fichier supporté par Ultimate 3D. Ainsi, vous avez le choix.

Si vous n'avez pas eu d'expérience de modélisation jusqu'ici, je vous recommande de commencer à modeler en apprenant à utiliser Anim8or, de Steven Glanville sur www.Anim8or.com. C'est un programme de modélisation très accueillant qui peut être utilisé pour créer des modèles très complexes. Il inclus aussi un système vraiment sympathique utilisant des squelettes d'animation. Le meilleur est qu'Ultimate 3D supporte les fichiers d'Anim8or (*.an8). Si vous cherchez un programme de modélisation plus professionnel et gratuit, je vous recommande Blender, sur www.blender.org. Démarrer avec ce dernier est vraiment difficile, mais il y a vraiment de bons tutoriaux sur le site internet et une fois que vous savez comment l'utiliser, vous l'aimerez. Il peut exporter vers presque tous les formats populaires. Dans la Communauté d'Ultimate 3D, il y a un topic dans la sous-catégorie "Game Graphics" qui contient une liste de programmes de modélisation et d'autres choses, dont vous avez besoin pour faire des jeux 3D.

Les différents formats de fichiers

Chaque format de fichier supporté par Ultimate 3D a ses avantages et ses inconvénients. Voici une liste d'entre eux, incluant leurs caractéristiques individuelles.

*.3ds
Le format de fichier .3ds est bon pour une raison très simple. Il est supporté par de nombreux programmes de modélisation. A l'origine, c'était le format de fichier de 3D Studio Max, mais pour une raison inconnue, il est devenu un type de standard pour de nombreux programmes de modélisation. Les fichiers *. 3ds supportent l'animation par squelette. Malheureusement, il n'y a pas beaucoup de programmes non commerciaux qui sont en mesure d'exporter des fichiers d'animation *. 3ds. En outre, le format de fichier *. 3ds est un modèle un peu désuet, car il ne supporte pas les vertex skinning - ce qui est important lors de l'animation de personnages - et il ne stocke pas les normales des vertices.

*.an8
Le grand inconvénient du format de fichier d'Anim8or est qu'il est le seul programme de modélisation à le supporter. L'avantage réside dans le fait qu'il supporte l'animation par squelettes. De cette façon Anim8or peut être utilisé pour créer des modèles animés pour Ultimate 3D. Malheureusement, le support de l'animation d'Ultimate 3D pour les *.an8 n'est pas totalement complet, parce que Steven Glanville (le développeur de Anim8or) n'a pas documenté tous les morceaux nécessaires au chargement des scènes des fichiers d'Anim8or pour le moment. Mais les chiffres et les séquences sont supportées complètement, incluant les animations utilisant l'influence sur les os ou le poids des peaux. Il vous est donc possible de créer des animation pour vos personnages et autres objets en utilisant les fichiers *.an8. Avant de charger un *.an8 avec Ultimate 3D vous devez convertir toute la géométrie en Meshs (Build->Convert to Mesh). Malgré cela, vous devrez faire attention avec les facteurs de puissances dans le "Material Editor". Par défaut, elles sont réglées à 0.3, 0.7, 0.2, 0.0. C'est généralement mieux de les régler à 1.0, 1.0, 0.0, 0.0. Autrement votre modèle sera très sombre dans Ultimate 3D.

*.md2
Le format de fichier *.md2 est très spécial. À l'origine il était utilisé par le moteur de Quake II pour sauvegarder les modèles du joueur. L'élément inhabituel à son propos est qu'il utilise une méthode différente, de tous les autres fichiers de modèle supportés, pour la création des animations. Plutôt que de connecter de simples meshes en utilisant les "os" et en sauvegardant les transformations de ces os (cette technique se nomme l'animation de squelette), le *.md2 sauvegarde un mesh pour chaque image / frame de l'animation. Dans Ultimate 3D v 1.31 vous obtenez toujours des animations sacadées lorsque vous utilisez des *.md2, parce qu'ils ne contiennent pas suffisamment de frames. Ultimate 3D v 2.0 utilise une technique nommée vertex tweening pour interpoler les vertex entre chacune des frames du *.md2. De cette manière vous obtenez des animations totalement fluide. Les désavantages des *.md2 sont, qu'il prend plus de place qu'un autre modèle et que ses animations ne peuvent être manipulées.
Ses avantages, sont que certains programmes qui ne supportent aucun autre format animé, supportent les *.md2 animés. Et surtout parce qu'il y a des tonnes de *.md2 vraiments cool sur le web parce que les gens ont modélisés des personnages pour Quake II.

*.x
*.x est le format de ficher des modèles de DirectX. Ce format est très vieux, mais il est suffisamment joli, et la plupart des programmes de modélisation professionels, tels que Blender, 3D Studio Max, Maya, Milkshape 3D et beaucoup d'autres le supportent au travers de plugins. Un certain nombre de ces plugins peuvent êtres trouvés sur ce site. Il supporte les animations, avec ou sans vertex skinning, les matériaux, et tout ce qu'un développeur de jeu peut désirer.

*.ms3d
*.ms3d est le format de fichier des modèles du programme de modélisation Milkshape 3D. Il supporte les matériaux multiples, les animations, avec ou sans vertex skinning. Il existe des plugins pour exporter vers de nombreux programmes de modélisation. Si vous obtenez des erreurs avec l'export en *.x, le format de fichier *.ms3d pourra devenir la solution parfaite.

*.u3d
Comme son nom le sous-entend, *.u3d est le format de fichier des modèles d'Ultimate 3D. Actuellement il n'y a que deux programmes qui peuvent les exporter. Ultimate 3D lui même (et toutes les applications, qui sont basées dessus) et Blender au travers de ce plugin d'export (Ndt:Lien Serveur CBNA - BlenderToU3D (18/11/08)). Il y a tout un ensemble de raisons pour son existence.
La première, c'est que cela peut sauvegarder un objet modèle d'Ultimate 3D tel quel. Tous les effets et changements qui lui ont été appliqués seront sauvegardés. Lorsque vous lirez les chapitres avancés, vous verrez que cela peut rendre les choses plus aisées. Un exemple sympathique pour cela, est le feu de la Fire and Water Demo. Cela requiere beaucoup de code pour appliquer les effets à ces derniers, mais vous pouvez simplement exporter le modèle complet dans un *u3d. Lorsque vous chargerez ce fichier unique vous obtiendrez du feu animé. Ainsi, une grosse partie du temps de calcul intensif des données, telles que les multiples niveaux de détail, peuvent être sauvegardés dans les fichiers de modèles d'Ultimate 3D. Cela peut raccourci le temps de chargement de façon visible.
La seconde raison de son existence, est que vous pouvez exportez toutes les textures et fichiers d'effets qui sont utilisés par un objet modèle, vers des fichiers *.u3d. Ainsi vous pouvez cacher vos ressources.
Et enfin, la dernière raison, c'est que le format de fichier d'Ultimate 3D a une fonctionnalité de cryptage par mot de passe valable. Donc, si vous désirez cacher vos ressources, vous pouvez simplement exporter tous vos modèles et textures vers des fichiers *.u3d crypté.
Exporter les objets de modèles vers les fichiers *.u3d sera abordé au sein du chapitre à propos de la gestion avancée de modèles.
Commentaire: Si vous voulez ajouter le support pour les fichiers de modèles d'Ultimate 3D, vous pouvez trouvrer des descriptions sur Ultimate3D.org.

Chargement des modèles

Comme je l'ai déjà dit, vous avez seulement besoin d'une petite fonction pour charger un modèle. Mais puisque chaque objet model nécessite une instance d'un objet de Game Maker, auquel le modèle peut être associé, vous devez régler quelques variables:

file
Le chemin du fichier contenant le modèle 3d. Je vous conseille de mettre tous les modèles dans un sous-répertoire appelé "models", mais c'est à vous de décider. Si vous avez créé des textures pour des matériaux particuliers de votre modèle, vous devez les placer dans le dossier gfx. Ultimate 3D les chargera alors automatiquement à partir de ce repertoire.

password (optionnel)
Si des fichiers se réfère à un fichier de modèle Ultimate 3D crypté, cette variable doit être réglé sur une chaîne de caractères donnant le mot de passe. Sinon, vous n'avez pas besoin de créer cette variable.

Après avoir créé et réglé ces variables, vous pouvez appeller LoadMesh() pour créer le modèle de l'objet. Le nom de cette fonction est un autre reste des versions précédentes d'Ultimate 3D. Elles ne supportaient que les *.x, et ces derniers s'utilisaient comme un seul et unique mesh. Désormais LoadMesh() permet de charger des modèles qui sont constituées de beaucoup de meshes avec des squelettes et des animations. Etant donné que les objets de modèles peuvent être transformés tout comme les primitifs (en utilisant les variables x, y, z, scalx/y/z et rotx/y/z) vous devrez mettre Step() dans l'évènement step de l'objet. Comme d'habitude vous devrez appeler Destroy() dans l'évènement correspondant.

Il existe quelques variables supplémentaires pour gérer l'animation. Voici une liste de celles-ci:

frame
Si cette variable a une valeur positive, cette frame de l'animation sera utilisée. Si frame est négative, la valeur sera utilisée comme la vitesse d'animation. Donc si frame est égal à -1, vous obtenez une nouvelle image à chaque step.

first_frame, last_frame
Ces deux variables définissent une série d'images qu'Ultimate 3D joue en boucle si frame est négative. Si first_frame est plus grande que last_frame l'animation se déroulera en sens inverse.

Pour connaître quelle frame de l'animation du modèle est actuellement affiché, vous pouvez appeller GetCurrentFrame(). Elle ne prend aucun argument et retourne la frame qui est actuellement affichée pour le modèle de l'objet qui a appellé la fonction. Pour les modèles *.md2 il y a une autre variable que vous connaissez déjà des primitives. Etant donné que les fichiers *.md2 ne contiennent aucune informations à propos de leur texture, vous définissez la texture pour les *.md2 de la même façon que pour les objets primitifs: en réglant la variable texture à l'index de la texture chargée au démarrage, en utilisant LoadTexture(...).


Dessiner des objets 2D

Etant donné que Game Maker ne peut pas dessiner (fonctions draw_) dans la view de Ultimate 3D, vous devrez utiliser Ultimate 3D lui même si vous voulez afficher quelque chose en 2D à un endroit. Dans ce but, Ultimate 3D dispose de deux fonctions pour afficher des textures, et quatres fonctions pour afficher du texte.

Afficher des textures

Débutons avec les fonctions destinées à l'affichage de textures 2D sur l'écran:

DrawTex(
TextureIndex,
X,
Y,
Scaling,
Rotation,
Alpha
)

TextureIndex
Ce paramètre est l'identifiant de la texture qui va être affichée. L'identifiant de la texture est la valeur que vous avez entré en tant que second paramètre lors du chargement de la texture en utilisant LoadTexture(...).

X, Y
La position, sur la fenêtre, où la texture doit être affichée (par rapport au coin haut gauche de la première view).

Scaling
L'échelle (scale) qui sera utilisée. 1 signifie aucun changement (100%), 0.5 la moitié de la taille, et 2 le double de celle-ci.

Rotation
Cet argument peut être utilisé pour faire pivoter la texture dans le sens inverse des aiguilles d'une montre, une valeur en degré est requise. Si la valeur est égale à 0 la texture ne subira aucune rotation.

Alpha
Ce paramètre doit se situer dans la fourchette allant de 0 à 255, où 0 indique qu'il n'y a pas d'opacité (totalement transparent) et 255 indique que l'image sera totalement opaque.

Si cette fonction n'offre pas toutes les fonctionnalités dont vous avez besoin, il en existe une seconde pour afficher des textures plus complexes. Fondamentalement, elle fait la même chose, toutefois il y a plus de possibilités pour modifier le résultat.

DrawTexEx(
TextureIndex,
Left, Top,
Width, Height,
X, Y,
ScalingX, ScalingY,
Rotation,
R, G, B, A
)

Left, Top, Width, Height
Ces arguments définissent la partie de la texture qui sera utilisée. Les arguments doivent rester dans la fourchette allant de 0 à 1. Ce sont des coordonnées de la texture. Les coordonnées de texture du coin supérieur gauche seront définies par left et top et le coin inférieur droit sera défini par left+width et top+height.

ScalingX, ScalingY
L'échelle (scale) qui sera utilisée. 1 signifie aucun changement (100%), 0.5 la moitié de la taille, et 2 le double de celle-ci.

R, G, B, A
Ces arguments définissent la couleur à utiliser comme "matériel" pour afficher la texture. Ces valeurs doivent rester entre 0 et 255. A défini l'opacité, 0 pas d'opacité (totalement transparent) et 255 totalement opaque.

C'est tout ce que vous avez besoin connaitre pour afficher des textures.

Affichage du texte

L'affichage du texte est également très facile. Tout d'abord vous définissez une police d'écriture, puis vous l'utiliser pour afficher un texte. Pour définir la police, vous aurez besoin de la fonction suivante:

SetFont(
FontName,
FontIndex,
TextSize,
TextAttributes,
R,G,B,A
)

FontName
C'est le nom du fichier dans le répertoire des polices d'écriture de Windows, qui contient la police que vous souhaitez utiliser. Un exemple qui pourrait illustrer cela serait "Times New Roman" et "Arial".

FontIndex
Cet argument est l'identifiant auquel vous voulez associer la texture. Il fonctionne de la même façon que les identifiants des textures. L'indice valable le plus élevé est 99.

TextSize
La taille du texte, telle que vous la connaissez dans les programmes de bureautique.

TextAttributes
Les attributs du texte. 0 signifie que la police est normale, 1 signifie qu'elle est en gras, 2 en italique et 3 signifie gras et italique.

R, G, B, A
La couleur du texte. Ces valeurs doivent rester entre 0 et 255. A défini l'opacité, 0 pas d'opacité (totalement transparent) et 255 totalement opaque.

Un appel à cette fonction requière toujours un certain temps de calcul. C'est pour cette raison que je conseille de créer toutes les polices dans l'évènement create de l'objet control. Si vous voulez changer leur couleur, cela ne pose pas de problème. Vous pouvez, tout bonnement, utiliser la fonction suivante:

SetFontColor(
FontIndex,
R,G,B,A
)

FontIndex
L'identifiant de la police d'écriture que vous désirez modifier.

R, G, B, A
Les nouvelles couleurs de la police d'écriture. Ces valeurs doivent rester entre 0 et 255. A défini l'opacité, 0 pas d'opacité (totalement transparent) et 255 totalement opaque.

Lorsque vous avez terminé la création des polices d'écriture, vous pouvez les utiliser pour afficher du texte. Il y a deux façons d'afficher du texte. Vous pouvez choisir d'afficher, soit, du texte 2D standard, soit, afficher un texte dans la scène en 3D. Commençons par le plus simple: afficher du texte en 2D.

DrawText(
FontIndex,
X, Y,
Text
)

FontIndex
L'identifiant de la police d'écriture que vous allez utiliser pour afficher votre texte. (NdT: L'identifiant précedemment défini, lors du chargement des polices).

X, Y
La position du texte par rapport au bord supérieur gauche de la première view.

Text
Une chaîne de caractères qui contient le texte que vous désirez afficher.

Afficher un texte 3D est un petit peu plus compliqué. En fait, vous devez entrer une transformation complète (comme pour les primitives) à la place de n'entrer que X et Y.

Draw3DText(
Text,
FontIndex,
X, Y, Z,
RotX, RotY, RotZ,
ScalX, ScalY, ScalZ
)

Text
Une chaîne de caractères qui contient le texte que vous désirez afficher.

FontIndex
L'identifiant de la police d'écriture que vous allez utiliser pour afficher votre texte. (NdT: L'identifiant précedemment défini, lors du chargement des polices).

X, Y, Z, RotX, RotY, RotZ, ScalX, ScalY, ScalZ
La transformation du texte 3D. Pour obtenir des renseignements à propos de la signification des variables, jetez un coup d'oeil à la Transformation des primitives, du tutorial à propos des objets primitifs.

Notez que le texte 3D s'affichera toujours par dessus le reste de la scène, y compris au dessus des textures 2d et du texte.


Fonctions diverses

Quelques-unes des fonctions d'Ultimate 3D qui ne correspondent pas aux autres tutoriaux sont listées ici.

La fonction suivante fait exactement la même chose que "mouse_check_button", parce que cette fonction Game Maker n'est pas compatible avec Ultimate 3D.

MouseCheckButton(
MouseButton
)

MouseButton
Ce paramètre peut prendre les contantes de Game Maker "mb_left", "mb_right", "mb_middle" et "mb_none".

Etant donné qu'Ultimate 3D peut également être utilisé avec Game Maker 5.0, qui ne dispose pas de la fonction mouse_set_screen_position(...), Ultimate 3D propose une fonction pour remplacer cette dernière.

Cette fonction définit global.mouse_x et global.mouse_y à la position de la souris sur l'écran dans son ensemble.

GetMousePos()

La fonction suivante peut être utilisée pour réinitialiser la position de la souris à l'écran.

SetMousePos(
X, Y
)

X, Y
La nouvelle position du curseur sur l'écran dans son ensemble.

En dehors des fonctions pour travailler avec la souris, il y a deux fonctions pour prendre des captures d'écran. EIles sont très simples elles aussi.

TakeScreenshot(
BMPFile
)

BMPFile
La chaîne qui définit l'extension. Bmp. C'est le fichier que la fonction va utiliser pour enregistrer la capture d'écran.

La seconde fonction permet de définir la partie de l'écran que vous voulez capturer:

TakeScreenshotEx(
BMPFile,
Unused,
Left, Top, Right, Bottom
)

Unused
Ignorez seulement celui-ci. Il était utilisé dans une version antérieure et il est désormais obsolète.

Left, Top, Right, Bottom
Ces paramètres précisent la zone de l'écran qui doit être sauvegardée dans un fichier. Les valeurs sont données par rapport au coin supérieur gauche de la première view.


Fonctions avancées

Les fonctionnalités que vous avez vu jusqu'à présent sont plutôt faciles. Vous pouvez faire des jeux sympas avec ces dernières, mais les graphismes obtenus ne seront pas vraiment impressionants. D'ailleurs, vous aurez des problèmes de frame rate (vitesse de défilement) si vous ne connaissez pas les astuces pour optimiser votre jeu. Avec les techniques décrites dans la partie pour les utilisateurs avancés, vous pouvez créer des graphismes excellents et des niveaux gigantesques.
Vous ne devriez pas passer à la partie des utilisateurs avancés si vous n'avez pas compris la partie des utilisateurs débutants. Ce sont les connaissances de base pour progresser.

Certaines choses, que vous apprendrez dans les prochains chapitres, sont plus faciles que vous pouvez le penser (par exemple, la parallaxe et le bump mapping), alors que d'autres sont vraiment plus compliquées (apprendre à programmer des effets de shader par exemple). Mais pour tout ça, Ultimate 3D offre des fonctions qui sont aussi facile à utiliser que possible. La plupart des traitements sont réalisés par Ultimate 3D, vous avez juste à lui dire ce que vous voulez. Maintenant, amusez-vous en apprenant comment faire des graphismes incroyables.


Gestion avancées des modèles

À force de travailler avec Ultimate 3D, vous comprendrez l'importance des objets de modèle très rapidement. À l'instar des objets primitifs, les objets de modèles sont extrêmement flexibles. Toutefois, calculer le rendu d'un objet de modèle prend plus de temps et les modèles comportants un nombre important de triangles, peuvent grandement ralentir les performances de vos jeux. C'est pourquoi ce tutorial parlera de l'ensemble des fonctionnalités qui peuvent être utilisées pour calculer le rendu des modèles plus efficacement.

Gérer des fichiers de modèles

Parfois il arrive qu'il y ait des objets dans votre jeu qui n'existent pas depuis le début de la partie, mais qui sont créés pendant celle-ci. Si ces objets sont chargés au moment où ils sont requis, cela peut donner lieu à un petit ralentissement. Les fichiers *md2 et *.an8 peuvent prendre particulièrement plus de temps à se charger. Pour cette raison, il vous est possible de précharger les modèles en utilisant, la fonction suivante:

PreloadMesh(
File,
Password
)

File
Le fichier qui contient le modèle que vous souhaitez précharger.

Password
Si File est modèle 3d Ultimate 3d encrypté, vous pouvez passer le mot de passe pour le décryptage à ce paramètre. Sinon, vous avez le choix de mettre une chaîne vide ou même rien du tout.

Une fois que vous avez appelé cette fonction pour un modèle, créer un objet de modèle à partir du résultat de celle-ci ne prendra plus aucun temps de calcul significatif, en plus. De cette façon vous pouvez éviter un lag singulièrement ennuyeux. Lorsque vous chargez un modèle avec LoadMesh() et que vous détruisez l'objet après cela, vous obtenez le même effet. Ultimate 3D ne chargera jamais le même modèle deux fois parce qu'il garde les données du modèle en mémoire, même si les objets basés sur ce modèles sont détruits. Ainsi cela évite les temps de chargement interminables d'une façon sûre. Bien sûr, ce comportement n'est pas toujours apprécié. Si vous passez d'un niveau de jeu à un autre, ce serait un gaspillage de mémoire d'avoir toujours tous les modèles du dernier niveau en mémoire. C'est pourquoi Ultimate 3D vous permet de libérer les modèles de la mémoire explicitement.

La fonction suivante libère le fichier de modèle donné de la mémoire, et retourne vrai si cela réussit. Si il y a toujours un objet basé sur ce modèle lorsque vous appellez cette fonction, cela ne libèrera pas le modèle de la mémoire, dans l'idée d'éviter d'éventuels conflits et messages d'erreur. Dans ce cas la fonction retourne faux.

ReleaseModel(
File
)

File
Le fichier de modèle que vous souhaitez libérer de la mémoire.

Exportation de fichiers *.u3d

Il s'agit là d'une tâche facile. Il n'y a qu'une seule fonction permettant l'exportation de fichiers de modèles et elle ne prend que trois paramètres. Ainsi vous avez:

Cette fonction, exporte le modèle de l'objet, par lequel elle est appellé vers le fichier donné. Le fichier de modèle Ultimate 3d comprendra une réprésentation du modèle tel qu'il était au moment où cette fonction fut appellée. Les seules informations qui ne seront pas sauvegardées seront celles qui peuvent être modifiées à l'aide de variables.

ExportToU3DFile(
OutputFile,
Password,
ExportResources
)

OutputFile
Le fichier de sortie désiré pour le modèle de l'objet. Si l'appel à cette fonction se termine avec succès, ce fichier contiendra le fichier de modèle Ultimate 3D.

Password
Le mot de passe qui doit être utilisé pour crypter le fichier modèle Ultimate 3D. Si c'est une chaîne vide ou nulle, le fichier sera exporté en clair. Plus le mot de passe est long, plus sûre sera la protection.

ExportResources
Ce paramètre peut prendre pour valeur: true ou false. Si vous lui passez false, le fichier de modèle Ultimate 3D ne contiendra pas les textures et les effets. Ceux-ci seront chargés à partir des fichiers comme d'usage. Seules les données des fichiers *.ufx seront exportées. Si vous passez true, le fichier sera un fichier de modèle ne nécessitant aucun appel complémentaire. Toutes les textures et shaders seront inclus. Notez que les modèles utilisants une texture de rendu ( render target texture ) en tant que texture pour un des matériaux, ne peuvent exporter leurs ressources.

Bien entendu, aucune garantie ne peut être donnée sur la sûreté du mecanisme de cryptage par mot de passe d'Ultimate 3D. Les spécialistes seront probablement capable de décrypter des modèles Ultimate 3D. Cependant, cela peut être suffisamment sécurisé pour tenir éloigné les développeurs de jeux occasionels ou les hackers. Même si je possède le code qui fait le décryptage des modèles Ultimate 3D, je serai incapable d'en décrypter un seul sans le bon mot de passe.

Créer plusieurs niveaux de détails

Les modèles peuvent être extrêmement complexes. Leur complexité est directement liée à leur nombre de triangles. De fait, les modèles possèdant un nombre réellement important de triangles réduiront les performances de votre jeu, vous obtiendrez subséquemment un framerate moins élevé. Pour cette raison il est important de toujours utiliser le moins de triangles possible. Souvent les versions comportant tous les détails des modèles n'est pas requise à distance. Si l'objet est loin de la caméra, son niveau de détail peut être diminuée. Cette technique est appellée communément LoD (Level of Detail / Niveau de détails). Ultimate 3D peut créer des niveaux de détails automatiquement et les utiliser pour des distances données. L'image suivante montre un exemple de modèle simplifié, en usant du générateur de niveau de détail intégré à Ultimate 3d.

La différence entre la première et la seconde version du modèle est à peine perceptible, mais le nombre de triangles a été diminué de 40%. La troisième version du modèle a un nombre de triangles équivalent à 30% du premier modèle.
Cependant, la forme de base du modèle est toujours reconnaissable. À une très longue distance ce bas niveau de détails est parfaitement utilisable, même pour les jeux possédants des graphismes de grandes qualité. Utiliser les niveaux de détails est extrêmement simple. Vous appellez simplement une fonction dans laquelle vous dîtes à Ultimate 3d le niveau de détail que vous souhaitez utiliser et à quelle distance. Voici la syntaxe:

Cette fonction crée les niveaux de détails avec le pourcentage de triangles donné pour l'objet de modèle qui appelle la fonction et l'utilise pour une distance donnée :

CreateLODChain(
CameraDistance1, LoD1,
CameraDistance2, LoD2,
CameraDistance3, LoD3,
CameraDistance4, LoD4,
CameraDistance5, LoD5
)

CameraDistance1, CameraDistance2, ..., CameraDistance5
Les distances, de l'objet jusqu'à la caméra, où il faudra utiliser le niveau de détail. Ainsi le niveau de détail original est utilisé à des distances allant de 0 à CameraDistance1, LoD1 est utilisé à des distances allant de CameraDistance1 à CameraDistance2 et ainsi de suite.

LoD1, LoD2, LoD3, LoD4, LoD5
Les niveaux de détail qui doivent être créés. C'est une valeur sous forme de pourcentage, ainsi 100 équivaut à ne pas changer le nombre de triangles du modèle, et 0 le plus petit nombre de triangle possible (usuellement 3 triangles). Les paramètres doivent se suivre de cette manière:LoD1>LoD2>=LoD3>=LoD4>=LoD5. Si une de ces valeurs est 0, cela indique à Ultimate 3D de cesser de créer des niveaux de détails additionnels.

Les niveaux de détails peuvent considérablement augmenter la vitesse de défilement (le framerate) de votre jeu, mais ils requéreront de la mémoire et du temps de calcul (pour les créer). Souvenez vous que vous ne devez pas créer de niveaux de détails pour les fichiers *.md2. En créer pour chaque frame peut prendre quelques minutes. Pour finir, voici un petit code exemple pour créer des niveaux de détail pour un modèle. Ce code doit être utilisé à la fin d'un évènement create d'un objet de modèle:

CreateLODChain(100,70,500,40,1000,20);

Création d'animations à partir de plusieurs fichiers

Si vous le souhaitez, vous pouvez créer des animations à partir de plusieurs fichiers au lieu d'utiliser des formats de fichiers supportant les squelettes d'animation (*.3ds, *.x ou *.an8) ou des fichiers *.md2. Pour faire cela, vous devrez ajouter chaque frame de l'animation en appellant les fonction suivante, après la création de l'objet avec LoadMesh():

AddFrame(
File,
Password
)

File
Le fichier qui contient la frame qui doit être ajoutée.

Password
Si File est un fichier modèle Ultimate 3D crypté, vous pouvez lui donner le mot de passe correct pour le décryptage. Sinon, vous pouvez soit lui passer une chaîne vide ou rien.

Etant donné que vous aurez besoin d'un nombre important de frames pour obtenir une animation fluide, il est possible d'activer le vertex tweening pour les animations (le vertex tweening est la technique activée par défaut pour les modèles de type *.md2). Cela ne fonctionnera que si toutes les frames ajoutées auront le même nombre de meshs, vertices et triangles. La fonction qui active le vertex tweening s'appelle EnableVertexTweening() et ne prend pas de paramètres.

Création de primitives à partir de modèles

Comme je l'ai déjà dit dans le chapitre sur les objets primitifs, ces derniers sont affichés extrêmement rapidement et efficacement. Toutes les primitives qui utilisent le même matériel et la même texture sont rendues simultanéments. De cette façon le GPU peut les traiter plus efficacement.
Durant le jeu, il arrivera souvent que vous aurez de simples objets qui seront trop complexes pour êtres représentés par les objets primitifs usuels, mais apparaissant très souvent. Ainsi, usuellement vous aurez à les créer en tant qu'objets de modèles, mais cela sera peu rapide et donc inefficace. Pour cette raison, il existe une fonction pour créer des primitives depuis des fichiers de modèles. Avant de créer les objets primitifs depuis le modèle, vous aurez à définir deux variables:

file
Le fichier qui contient le modèle dont vous voulez créer un objet primitif.

material
L'indice du matériel du modèle qui doit être utilisé pour l'objet primitif.

password (optional)
Si File se réfère à un fichier de modèle Ultimate 3D crypté, cette variable doit être une chaîne de caractères donnant le mot de passe. Sinon, vous n'aurez pas besoin de créer cette variable.

Après avoir défini ces variables vous devez appeller CreatePrimitiveFromModel() pour créer l'objet primitif. Comme pour n'importe quel autre objet primitif vous devrez mettre un appel à Step () et Destroy () dans les évènements correspondants. Les objets primitifs créés depuis des modèles ont la variable texture afin de définir la texture qui sera utilisée. Notez que seul le premier mesh du modèle sera utilisé pour créer l'objet primitif. Il est utile de faire usage de fichiers *.an8 avec cette fonction parce que lors du chargement des fichiers *.an8, Ultimate 3D crée seulement un mesh par objet.

Création de fonds customisés

Le système d'arrière-plans intégré à Ultimate 3D est sympa, mais vraiment limité. Si vous voulez des backgrounds qui changent au travers du temps, ou des backgrounds utilisant des effets spéciaux, vous ne pourrez le faire avec le système d'arrière-plans intégrés. Dans ce cas, une technique plus souple est conseillée. Les objets qui peuvent être manipulés de manière complexe sont requis. Les objets de modèle sont des objets comme ceux là. Pour cette raison, Ultimate 3D vous donne la possibilité d'utiliser des objets de modèle comme des objets de background.

Chaque objet de modèle peut être défini en mode Background. Ensuite, cet objet de modèle ne sera plus affecté par le brouillard et sera couvert par tous les autres objets. Cela fait qu'il semblera être en arrière plan. Pour éviter qu'il ne paraisse évident qu'il n'est pas réellement en arrière plan, vous devrez le faire suivre par la caméra, en redéfinissant sa position à celle de la caméra à chaque step. Il est facile d'activer le mode Background.

Cette fonction active ou déactive le mode arrière-plan pour l'objet de modèle par lequel il est appellé.

SwitchBackgroundMode(
EnableBackgroundMode
)

EnableBackgroundMode
Si ce paramètre est vrai, le mode background sera activé pour l'objet de modèle qui appelle la fonction, sinon il sera désactivé.

Dès qu'un premier objet de modèle est défini en mode background, le système d'arrière-plans intégrés est désactivé complètement. Cela paraîtra très étrange, étant donné que tous les objets vont laisser des traînées, vous devriez donc plutôt créer votre objet de modèle de façon à ce qu'il couvre tout le champ de vision de la caméra. Vous pouvez utiliser plusieurs objets de modèle en mode background mais vous devrez être vigilants. Les objets de background sont recouverts par tout, même par les autres objets de background. Les objets de background se couvrent les uns les autres en fonction de l'ordre de rendu de ces derniers. Pour cette raison, vous devez être sûrs que les objets qui doivent avoir une plus faible profondeur (depth), seront créés plus tard. Aussi vous devez éviter que les objets de Background couvrent des parties d'eux-même, étant donné que cela donne lieu au même problème. Les meilleures formes pour les modèles de backgrounds sont définitivement les spheres et les cubes.

Si vous gardez ces éléments à l'esprit, vous pouvez obtenir d'excellents résultats avec cette fonctionnalité. Vous pouvez afficher un soleil qui se promène dans le ciel, changer les nuages, selon l'heure changer les effets, nuages éclairés en temps réel et plus encore. De bons backgrounds est une des clés pour proposer d'impressionants graphismes. Vous devriez utiliser cette fonctionnalités au maximum.


Matériaux et effets spéciaux

Les matériaux des modèles peuvent être très divers dans Ultimate 3D. Chaque matériel peut utiliser sa propre combinaison de textures, ainsi que des effets de shader. Le système d'effet de shader tout entier d'Ultimate 3D est directement lié au système de matériaux. Utiliser des effets de shader signifie définir des propriétés pour les matériaux. Mais les effets de shaders customisés ne sont pas le sujet de ce tutorial. Ici, vous allez apprendre les bases dont vous aurez besoin pour définir des shaders et d'autres effets plutôt cools qui fonctionnent sans shaders. Vous allez également apprendre deux superbes techniques d'effets spéciaux : l'environment mapping, le fameux éclairage par pixel (per pixel lighting) ainsi que le parallax mapping.

Chargement et création de textures spéciales

Il n'y a pas que les textures 2D dont nous avons parlé dans le tout premier chapitre. Il existe aussi deux autres types de textures. Le premier type est une height map. Les Height maps sont des images en niveaux de gris qui contiennent les informations sur la hauteur. Un pixel blanc est un pixel vraiment très haut, alors qu'un pixel noir sera très bas. Il est important que vous compreniez ce qu'est une height map, parce que vous en aurez besoin pour beaucoup d'effets. C'est pourquoi je vais utiliser un exemple pour l'expliquer.

Jetez un coup d'oeil à la prochaine texture. Elle représente un simple mur de briques. L'image de gauche défini les couleurs des pixels influencés par la lumière diffuse, on parle de diffuse map. Et sur la droite vous avez la height map.

Notez que les intervales entre les pierres sont noirs sur la height map, parce qu'elles sont plus faibles que le reste. Les pierres sont blanches parce qu'elles sont plus hautes. Mais certaines pierres ne sont pas parfaitement construites et c'est pour cette raison qu'elles sont plus foncées dans la height map. Elles sont plus basses que les autres.

Donc à quoi sont utiles les heights maps? Elles sont extrêmement utiles parce qu'elles peuvent être utilisées pour donner des informations à propos de la surface des objets. De cette façon vous pouvez simuler le relief qui rendent les graphismes de votre beaucoup plus détaillés et réalistes. C'est ce que l'éclairage par pixel (per pixel lighting) et le parallax mapping font. Malheureusement la height map seule est inutile pour ces effets. Ceux-ci nécessitent également une autre texture appellée : normal map. Mais c'est quelque chose dont vous n'avez pas besoin de vous soucier. Ultimate 3D la génére pour vous. Vous avez juste à utiliser une autre fonction pour charger des heights maps:

Cette fonction charge une height map depuis un fichier spécifié et calcule une normal map pour cette dernière. Une copie de la height map est sauvegardée dans le canal dédié à l'alpha de la texture qui en résulte.

LoadHeightMap(
TextureIndex,
HeightMapFile,
HeightFactor
)

TextureIndex
L'indice de la texture qui lui sera associé. Vous devriez être familiarisé avec ce paramètre depuis la description de la fonction LoadTexture(...).

HeightMapFile
Le nom du fichier qui contient la height map (en incluant le chemin du fichier et l'extension).

HeightFactor
C'est le seul paramètre nouveau. Quand Ultimate 3D calcule une normal map, ce dernier a besoin de savoir de combien est la hauteur de la texture par rapport à la largeur et la hauteur de cette dernière. On pourrait dire que cela définit la hauteur de la couleur blanche sur la height map. Une valeur adaptée pour ce paramètre est 16.

Il y a un autre type de texture que vous ne connaissez pas encore. Celui-ci est un peu étrange. Au début vous ne saurez pas quoi en faire, mais cela changera assez vite. Ces textures sont appellées cube maps. Elles ne sont pas seulement faites d'une image 2D, mais de six images carrées. En principe cela fonctionne de la même façon que les sky cubes (les images d'arrière-plans (backgrounds)). Vous avez six images qui sont assemblées pour former un cube. Dans ce cas vous ne devez surtout pas utiliser des coordonées 2D, mais 3D. Vous verrez comment faire cela plus tard et vous apprendrez à quoi elles servent. Charger des cube maps est similaire à charger des sky cubes. Il n'y a qu'une fonction pour le faire et elle ne prend qu'une chaîne de caractères pour identifier les six fichiers:

Cette fonction crée une texture de cube à l'aide des six fichiers spécifiés et y associe l'indice donné.

LoadCubeTexture(
FileScheme,
TextureIndex,
CubeWidth
)

FileScheme
La construction du nom de fichier. Cette chaîne doit contenir une "*". Elle sera remplacée par les mots "Left", "Right", "Top", "Bottom", "Front" et "Back". Ainsi si vous écrivez "gfx/CubeMap*.png" Ultimate 3D cherchera les fichiers : "CubeMapLeft.png", "CubeMapRight.png", "CubeMapTop.png" et ainsi de suite.

TextureIndex
L'indice de la texture qui lui sera associé. Vous devriez être familiarisé avec ce paramètre depuis la description de la fonction LoadTexture(...).

CubeWidth
L'échelle à laquelle sera agrandie chaque texture (Si elles n'ont pas déjà cette taille). Ce paramètre doit être une puissance de deux.

Les textures prennent beaucoup de mémoire, donc il n'est pas bon d'avoir un trop grand nombre de textures chargées en même temps. Pour vous donner la possibilité de vous débarrasser des textures chargées, il existe la fonction ReleaseTexture(...). Cette fonction supprime la texture spécifiée de la mémoire et redéfini l'indice associé à la texture à son état d'origine. Voici sa description:

ReleaseTexture(
TextureIndex
)

TextureIndex
L'indice de la texture que vous voulez libérer de la mémoire.

Remarquez que les cubes maps ne sont pas supportées par certaines cartes graphiques très anciennes. Pour cette raison il existe la fonction GetCubeMapSupport() qui retourne true si les cubes maps sont supportées et false si elles ne le sont pas.

Indices des matériaux

Beaucoup de fonctions décrites ci-dessous prennent un argument appelé MaterialIndex. Je vais vous expliquer ce que cela signifie ici une fois, pour éviter les répetitions sans fin. Comme vous devez le savoir par votre expérience en modélisation, un modèle peut utiliser différents matériaux pour les différentes surfaces du modèle. La plupart des programmes de modélisation offre la possibilité de donner un nom à chaque matériau. C'est vraiment utile parce que si vous utilisez un programme de modélisation qui n'offre pas cette possibilité, vous devez devinier l'indice du matériau en question. Je vous conseille de donner des noms qui ont du sens à vos matériaux. Ne les appellez pas seulement material01, material02 etc. Cela rendra votre code plus aisé à lire.

Les fichiers *.an8 et *.3ds contiennent des informations à propos des noms des matériaux. Les fichiers *.md2 ne peuvent avoir qu'un seul matériel qui est toujours nommé "material_md2". Les fichiers *.x ne contiennent pas les noms des matériaux. Ces matériaux sont appellés "material00" à "materialXX". Ce qui est cool avec les noms de matériaux c'est que vous n'avez pas besoin de vous occuper de la signification des indices des matériaux si vous connaissez leurs noms, parce que vous pouvez simplement utiliser une fonction pour en obtenir l'indice.

Cette fonction retourne l'indice du matériau avec le nom spécifié, ou l'indice du premier matériau (0) si aucun matériau avec ce nom existe. Ce paramètre est insensible à la casse.

GetMaterialIndex(
MaterialName
)

MaterialName
Le nom du matériau dont vous voulez récupérer l'indice.

Parfois il arrive aussi que vous souhaitez faire quelque chose pour chacun des matériaux d'un modèle. Dans ce cas vous pouvez simplement utiliser une boucle for. Pour obtenir le nombre de matériaux présents dans le modéle, vous pouvez utiliser GetMaterialCount().

Changer la couleur d'un matériau

Comme pour les objets primitifs, vous pouvez changer la couleur des matériaux des modèles quand vous le voulez. À nouveau, il y a deux fonctions destinées à cette fin. Une pour définir la partie diffuse du matériau et l'autre pour définir la partie émissive de ce matériau. Les voici.

Cette fonction change la couleur diffuse du matériau donné, de l'objet qui l'appelle.

SetModelMaterial(
MaterialIndex,
R, G, B, A
)

MaterialIndex
L'indice du matériau dont vous souhaitez changer la couleur diffuse.

R, G, B, A
La nouvelle couleur diffuse du matériau. Les valeurs de ces paramètres doivent se situer dans une fourchette allant de 0 à 255.

Cette fonction change la couleur emissive du matériau de l'objet par lequel il est appellé.

SetModelMaterialEmissive(
MaterialIndex,
R, G, B
)

MaterialIndex
L'indice du matériau pour lequel vous voulez changer la couleur émissive.

R, G, B
La nouvelle couleur émissive du matériau. Les valeurs pour ce paramètre doivent se situer entre 0 et 255.

En addition, vous pouvez définir une couleur spéculaire (specular color) et une puissance spéculaire (specular power) pour les matériaux du modèle. Les éclairages spéculaires donnent à l'objet un aspect rutilant, en les éclaircissant très fort à la position où ils reflètent la source de lumière. Elles sont utiles pour les surfaces humides ou pour les matières plastiques. Avant de pouvoir parler de la fonction qui défini la couleur spéculaire d'un matériau d'un modèle, il vous faut connaître trois variables additionelles pour les objets de lumières, qui n'ont pas été mentionnées dans le chapitre sur les sources de lumière, étant donné qu'elles appartiennent à la partie avancée de cette documentation. Voici leur description.

spec_r, spec_g, spec_b
Ces variables définissent la couleur spéculaire pour une source de lumière. Elles doivent toutes se situer dans une fourchette allant de 0 à 255, mais elles peuvent aussi être supérieures ou inférieures. Elles peuvent être changées à tout moment (si Step () est à sa place). Cette couleur est multipliée par la couleur spéculaire, définie pour le matériau du modèle, afin de déterminer la véritable couleur spéculaire. Ces variables vous permettent de contrôler quelles sources de lumière influent sur l'éclaircissement spéculaire et lesquelles n'influent pas, de quelle force sont les éclaircissements spéculaires et de quelles couleurs sont ces éclaircissements.

Une fois que vous avez défini des valeurs positives pour ces variables dans quelques sources de lumières, il est logique d'appeller la fonction suivante pour certains modèles. Il n'est pas nécessaire d'utiliser cette fonction cependant, Ultimate 3D peut charger la couleur spéculaire et la puissance spéculaire depuis les fichiers *.u3d, *.an8, *.3ds et *.ms3d, ainsi vous pouvez simplement définir ces valeurs dans votre programme de modélisation.

Cette fonction définit une nouvelle couleur et puissance spéculaire pour le matériel donné du modèle de l'objet, par lequel la fonction est appellé.

SetModelMaterialSpecular(
MaterialIndex,
R, G, B,
Power
)

MaterialIndex
L'indice du matériau dont vous voulez changer la couleur spéculaire et la puissance spéculaire.

R, G, B
La couleur spéculaire qui sera définie. Les valeurs doivent être dans une fourchette allant de 0 à 255.

Power
La puissance spéculaire. Il s'agit d'une puisssance mathématique. Juste après son calcul, le facteur d'éclaircissement spéculaire est aussi mat que la lumière diffuse. Ensuite il est élevé à la puissance donnée. De cette façon l'éclaircissement spéculaire est plus fort. Plus forte est la puissance, plus fort est l'éclaircissement spéculaire. Une bonne valeur pour un éclaircissement spéculaire fort est 32.

Que l'éclaircissement spéculaire rende bien est une question de goût. À mon avis cela ne donne pas un bel effet. Cela donne un aspect artificiel et irréel aux choses. Les seuls matériaux pour lesquels cela doit être utilisé sont les matières plastiques ou humides. Sur quoi que ce soit d'autre, cela rend vraiment mal. Pour les matières réfléchissantes vous devriez plutôt utiliser l'environment mapping. Donc lisez ce qui vient.

Environment mapping

Nom mystèrieux, technique géniale. En principe, le nom la décrit très bien. L'environment mapping est une technique où vous prenez des photos de l'environnement et vous les appliquez interactivement sur un objet. Lorsque vous utilisez l'environment mapping vous n'avez plus de coordonnées statiques de texture. À la place, le périphérique graphique les calcule à chaque frame, selon la position du vertex (sommet), l'orientation du triangle et la position de la caméra.

Il y a deux types d'environment mapping différent. Le premier s'appelle spherical environment mapping, ou en plus court, sphere mapping. Pour cette technique, vous avez besoin d'une sphere map. C'est une texture qui contient une image de l'environnement telle qu'il serait refletée par une sphère en chrome. Sur le site d'Ultimate 3D, ainsi que sur Le CBNA, vous trouverez un programme pour créer ces spheres maps.
L'autre technique est appellée cubical environment mapping, ou en plus court, cube mapping. Pour cette technique, vous avez besoin des cubes maps dont je vous ai parlé dans un des précédents sous-chapitres.

Activer l'environment mapping est très simple. Il vous suffit d'appeller une fonction dans laquelle vous dîtes à Ultimate 3D quel matériau utiliser pour l'environment mapping et quelle texture contient l'environment map:

Cette fonction établit l'environment mapping pour le matériau donné de l'objet qui appelle cette fonction.

SetMaterialEnvironmentMap(
MaterialIndex,
TextureIndex
)

MaterialIndex
L'indice du matériau sur lequel vous voulez appliquer l'environment mapping.

TextureIndex
L'indice de la texture qui contient l'environment map. Si c'est l'indice d'une texture normale, le spherical environment mapping sera activé, sinon ce sera le cubical environment mapping qui le sera. Si vous voulez désactiver l'environment mapping pour ce matériau passez -1.

Lors de l'utilisation de l'environment mapping vous devriez soit, utiliser une image très sombre en tant qu'environment map ou alors vous devrez définir une couleur très sombre pour le matériau du modèle, parce que l'environment map est appliqué par l'addition de la couleur du texel (pixel d'une texture) à la précédente couleur du pixel dans l'environment map. Si vous n'aimez pas ça, vous pouvez le changer en utilisant la fonction SetMaterialStageTextureOperation(...) (voir ci-dessous).

Bump mapping et parallax mapping

Maintenant cela devient réellement intéressant. Cette technique est incroyablement populaire parce qu'elle rend vraiment bien. En réalité, je n'aime pas le terme "bump mapping" parce qu'il représente de très très nombreuses techniques différentes. Ce dont je vais parler ici est aussi connu sous le nom d'éclairage par pixel (per pixel lighting), de normal mapping, ou dot3 bump mapping. C'est une technique pour éclairer les matériaux, en simulant la hauteur de chaque pixel en utilisant les height maps. Le parallax mapping est une technique où une height map est utilisée pour déformer une texture selon la position de la caméra. Et donne l'impression que c'est réellement en trois dimensions en procurant une sensation de profondeur. Pour vous donner une idée de ce que cela signifie, voici un petit screenshot:

Ce screenshot représente un modèle éclairé par trois sources de lumière (représentées par les petites sphères). Le modèle ne fait pas plus de 12 triangles. L'effet de profondeur et d'éclairage détaillé est dû aux effets spéciaux. Cela peut mieux se voir au bas de la pièce. La lumière verte n'éclaire que le côté droit des briques sur la gauche et seulement le côté gauche des briques sur la droite. De fait, il vous est impossible de voir l'arrière des briques, à cause du parallax mapping. En revanche, l'avant des briques semblent plus gros.

Le parallax mapping et le bump mapping sont des effets de shader extrêmement complexes. Ils requièrent beaucoup de mathématique et de programmation de bas niveau pour les réaliser (surtout lorsqu'on utilise Direct 3D 8.1). Heureusement vous utilisez Ultimate 3D ;). Vous n'avez pas plus à faire qu'à appeller une fonction. Le reste du travail, c'est UItimate 3D qui le fait.

Cette fonction active l'effet de bump mapping et/ou de parallax mapping pour le matériel donné de l'objet par lequel elle est appelée.

ApplyParallaxAndBumpMapping(
MaterialIndex,
HeightMapIndex,
ParallaxFactor,
LightSourceID1,LightSourceID2,LightSourceID3
)

MaterialIndex
L'indice du matériel qui sera appliqué.

HeightMapIndex
L'indice de la height map que vous avez chargé précedemment à l'aide de LoadHeightMap(...).
(Alternativement vous pouvez utiliser LoadTexture(...) pour charger une normal map précalculée avec la height map dans l'alpha de la normal map). Vous pouvez passer -1 pour ce paramètre si vous ne voulez pas simuler de surface à la texture, mais cela serait un gaspillage de temps de calcul et le parallax mapping ne rendrait pas de façon réaliste.

ParallaxFactor
Ce paramètre indique à Ultimate 3D de combien l'effet de parallaxe doit être. Cela dépend de la façon dont la texture est mappée sur le modèle. Usuellement la valeur est très petite (pour le screenshot ci-dessus j'ai utilisé 0.02). Si vous entrez une valeur trop grande pour ce paramètre, il semble évident que la texture ne sera qu'étirée et tout aura l'air étrange. Soyez donc prudent avec cela. Si vous passez 0 ici le parallax mapping ne sera pas utilisé, ce qui prendra moins de temps de calcul.

LightSourceID1, ..., LightSourceID3
Le nombre de source de lumière qui peut être utilisé pour l'éclairage par pixel est limité du fait des limitations du modèle de shader 1.4. Il est impossible d'implémenter plus de sources de lumière avec ce modèle de shader. En implémenter trois était déjà suffisamment difficile. Pour cette raison vous devez entrer les ids d'instance des objets servants de source de lumière pour l'effet. LightSourceID1 peut être une lumière directionnelle ou un point de lumière (light point). Les deux autres sources ne peuvent être que des points de lumière (light point). Moins vous utiliserez de sources de lumière, moins le temps de calcul sera important. Si vous ne passez aucune source de lumière le bump mapping sera désactivé.

Comme je l'ai déjà dit, ces deux effets sont véritablement complexes. Ultimate 3D vous cache beaucoup du fonctionnement de ces derniers, pour les rendre plus simple d'usage. Lorsque vous lirez le chapitre sur les programmation des shaders vous acquiererez une meilleure compréhension de ce que ces effets font vraiment. Pour cette raison, il vous faut apprendre un certain nombre de choses à ne pas faire lors de l'utilisation de cet effet.
Avant tout vous devez savoir que l'éclairage spéculaire par pixel (per pixel specular lighting) n'est pas supporté comme une fonctionnalité intégrée. C'est pourquoi tous les éclairages spéculaires disparaîtront si vous activez le bump mapping avec ApplyParallaxAndBumpMapping(...).
Vous ne pouvez pas utiliser l'environment mapping ou d'autres effets de multi texturage simultanément avec l'effet de parallaxe et de bump mapping, ni d'autres effets de shader pour ce matériau.
De surcroît, vous ne pouvez appliquer d'effet de parallaxe, ni de bump mapping aux objets utilisants le vertex tweening (ce qui est toujours le cas pour les fichiers *.md2). Aussi, les modèles utilisant le vertex skinning ne doivent pas avoir plus de 28 os s'ils utilisent le bump map ou le parallax mapping. Tous les os avec un indice supérieur ne seront pas transformés correctement.

Pour finir, vous devez savoir que toutes les cartes graphiques ne supportent pas le parallax mapping et le bump mapping. Ces effets sont des effets de shader requérant le modèle de shader 1.4. C'est pourquoi vous devrez appeller GetSupportedPSVersion() avant tout. Cette fonction retourne la version la plus haute des shaders supportée en tant que nombre décimal. Cela peut être 1.0, 1.1, 1.2, 1.3 ou 1.4. Ainsi pour vérifier que le parallax mapping ou le bump mapping est supporté, il vous faudra écrire:

if(GetSupportedPSVersion()>=1.4){
// Le parallax mapping et le bump mapping sont supportés
}
else{
// Le parallax mapping et le bump mapping ne sont pas supportés
}

Fonctions de multi-texturing

Un matériau n'est pas contraint de n'utiliser qu'une texture. Vous avez déjà vu deux exemples de multi texturing. Lorsque vous utilisez l'environment mapping, un matériau peut utiliser une texture diffuse, et une environment map en même temps. Lorsque vous utilisez le parallax mapping ou le bump mapping, un matériau utilise une texture diffuse et une height map en même temps. Selon la carte graphique, un PC peut utiliser jusqu'à huit textures simultanément. La fonction GetSimultaneousTextureCount()retourne combien de textures peuvent être utilisées simultanément sur le PC l'exécutant. Dans le pire de cas (ndt: vraiment le pire ^^) c'est seulement une.

Utiliser plusieurs textures, c'est à dire la technique de multi texturing, peut être utilisée pour faire de superbes effets et la programmation des shaders n'est pas imaginable sans le multi texturing. Une importante implémentation du multi texturing est le light mapping, une technique où vous créez une texture décrivant l'éclairage d'un objet en utilisant un programme tel que Deled et où vous l'appliquez à l'objet. De cette façon, vous obtiendrez un éclairage très détaillé, mais statique, ne requèrant aucun temps de calcul.

Mais comment cela fonctionne? En principe c'est très simple. Selon la carte graphique, vous pouvez aller jusqu'à huits niveaux de texture. Chaque niveau de texture utilise une texture, un ensemble de coordonnées de texture du mesh et une opération sur la texture. La chose la plus compliquée ici est l'opération sur la texure. Cela défini, de quelle façon sera appliquée la texture utilisée à ce niveau au résultat des niveaux précédents. Par exemple la couleur du pixel peut être additionnée (c'est ainsi que c'est réalisé lors de l'utilisation de l'environment mapping) ou multipliée. Ce sont les deux opérations de texture les plus communes. Pour définir ces trois propriétés de niveau de texture, il existe trois fonctions:

Cette fonction définit la texture de l'objet l'appellant, qui sera utilisée pour le niveau de texture donné du matériau indiqué.

SetMaterialStageTexture(
MaterialIndex,
TextureStage,
TextureIndex
)

MaterialIndex
L'indice du matériau que vous désirez modifier.

TextureStage
Le niveau de texture que vous souhaitez modifier. Ce paramètre peut prendre des valeurs comprises entre 0 et 7, inclus.

TextureIndex
L'indice de la nouvelle texture à utiliser pour le niveau de texture.

Cette fonction change, pour l'objet qui l'appelle, l'ensemble des coordonnées de la texture, à utiliser au niveau de texture donné pour le matériau spécifié.

SetMaterialStageTexCoord(
MaterialIndex,
TextureStage,
TextureCoordinateSetIndex
)

MaterialIndex
L'indice du matériau que vous souhaitez modifier.

TextureStage
Le niveau de texture que vous souhaitez modifier. Ce paramètre peut prendre des valeurs comprises entre 0 et 7, inclus.

TextureCoordinateSetIndex
Dans le chapitre sur la manipulation des meshs, vous avez appris qu'un mesh peut possèder jusqu'à huit ensembles de coordonnées de texture. Ce paramètre donne l'indice de l'ensemble de coordonnées de texture à utiliser pour le niveau de texture précedemment spécifié. Par défaut ce paramètre est 0 (le premier ensemble de coordonnées de texture). Selon le nombre d'ensemble de coordonnées de texture que vous avez créé pour le mesh en question, ce paramètre peut être compris entre 0 et 7, inclus. Aussi, vous pouvez entrer -1 pour obtenir l'ensemble de coordonnées de texture de l'environment mapping, -2 pour obtenir l'ensemble de coordonnées de texture de l'environment mapping calculé avec une autre technique, ou -3 pour obtenir les coordonnées du vertex sur l'écran en tant que coordonnées de texture.

Cette fonction change l'opération à effectuer sur la texture, de l'objet qui l'appelle, pour le niveau de texture donné du matériau précisé.

SetMaterialStageTextureOperation(
MaterialIndex,
TextureStage,
TextureOperation
)

MaterialIndex
L'indice du matériau à modifier.

TextureStage
Le niveau de texture que vous souhaitez modifier. Ce paramètre peut prendre des valeurs comprises entre 0 et 7, inclus.

La texture de scène qui doit être modifié. Ce paramètre peut prendre les valeurs comprises dans une fourchette de 0 à 7.

TextureOperation
Comme énoncé plus haut, l'opération de la texture défini comment la texture est appliquée au résultat des niveaux de texture précédents. Le premier niveau de texture multiplie toujours la couleur diffuse, calculée en utilisant les données du matériau et les sources de lumière, avec la couleur de la texture et en multipliant le résultat par deux. Le deuxième niveau de texture prend les couleur du niveau de texture précédent, et les combine avec la couleur de la nouvelle texture en utilisant l'opération de texture que vous avez défini. Cela va de même pour les autres niveaux de texture. Chaque valeur entière que vous entrez pour ce paramètre correspond à une opération de texture. Voici la liste des valeurs possibles et à quelles opérations elles correspondent:
1: Cette opération de texture multiplie la couleur actuelle avec la couleur de la texture: CouleurTexture*CouleurActuelle.
2: Cette opération de texture multiplie la couleur actuelle avec la couleur de la texture et multiplie le résultat par deux: CouleurTexture*CouleurActuelle*2.
3: Cette opération de texture multiplie la couleur actuelle avec la couleur de la texture et multiplie le résultat par quatre: CouleurTexture*CouleurActuelle*4.
4: Cette opération de texture additionne les couleurs actuelles à la couleur de la texture: CouleurTexture+CouleurActuelle.
5: Cette opération de texture prend la couleur actuelle et soustrait 128 des données r,v,b de celle-ci. Ensuite elle ajoute la couleur de la texture: CouleurTexture+CouleurActuelle-128.
6: Cette opération de texture prend la couleur actuelle et soustrait 128 des données r,v,b de celle-ci. Ensuite elle ajoute la couleur de la texture et mutiplie le tout par deux: (CouleurTexture+CouleurActuelle-128)*2.
7: Cette opération de texture prend la couleur actuelle et la soustrait à la couleur de la texture: CouleurTexture-CouleurActuelle.
8: Cette opération de texture prend la couleur actuelle et calcule un produit scalaire avec les données de la couleur élevées ou réduites dans une fourchette de -1 à 1: RVB = ( (CouleurTexture.R-128)/128)*(CouleurActuelle.R-128)/128 + (CouleurTexture.V-128)/128*(CouleurActuelle.V-128)/128 + (CouleurTexture.B-128)/128*(CouleurActuelle.B-128)/128 )*255.

Lors de l'utilisation du multi texturing la donnée alpha utilisera toujours une opération de texture multiplicatrice (CouleurFinale.a = CouleurTexture.a*CouleurActuelle.a). Cela signifie donc que vous pouvez utiliser jusqu'à huit textures pour faire de l'alpha blending. Cela peut être utile dans certain cas, étant donné que cela vous permet de modifier l'alpha pour chaque niveau de texture.

Le mutli texturing peut être utilisé pour réaliser des effets sympas, je vous conseille donc de vous amuser un peu avec. Mais ce qui est encore plus important, c'est que cette technique va vous permettre d'utiliser des effets de shader. Restez attentif, car le prochain tutorial va véritablement avoir un niveau avancé. Vous allez apprendre comment utiliser vos propres effets de shader avec Ultimate 3D. Il est difficile d'écrire ses propres shaders mais il faut absolument que vous essayiez. Il existe de très bons tutoriaux sur Internet et une fois que vous saurez comment écrire vos propres shaders, plus rien ne pourra vous arrêter. Ils peuvent être utilisés pour n'importe quels effets imaginables. En gros, un pixel shader prend la place de l'opération de texture. Cela signifie que vous pouvez utiliser autant d'opérations arithmétiques que vous le souhaitez pour réaliser des calculs avec les valeurs des différentes textures. Vous pouvez réaliser n'importe quel effet que vous souhaitez créer. C'est la solution ultime pour tous les effets spéciaux dont vous avez besoin ;).


Utiliser les effets de shader

Si vous avez joué à certains jeux récents, vous connaissez probablement le terme shaders. Une définition simple des shaders, est qu'ils permettent la création d'effets visuels. Mais cette réponse n'est pas suffisamment précise, en voici une plus détaillée. Il y a deux différents type de shaders: les vertex shaders et les pixel shaders. Les vertex shaders peuvent être utilisés seuls, tandis que les pixel shaders sont toujours utilisés avec les vertex shaders.

Usuellement, Ultimate 3D utilise le ffp (fixed function pipeline) pour afficher les modèles. Le ffp fait partie de Direct3D. Il calcule les données des vertex, cela signifie qu'il calcule les positions des vertex sur l'écran, l'éclairage des vertex et un ensemble d'autres bits de données. Une fois calculées, les données sont transférées et sont utilisées pour afficher chacun des triangles pixel par pixel. Lors de l'utilisation des shaders, on n'utilise plus le ffp, mais le programmable function pipeline. Cela signifie que la façon dont Ultimate 3D calcule les données des vertex et les couleurs des pixels peut être changée comme on le souhaite.

Les Vertex shaders sont de minuscules programmes qui sont exécutés pour chacun des vertex à chaque frame, afin de calculer leurs données. De cette manière, il vous est possible de changer la géométrie des objets sans les fonctions de manipulation des meshs d'Ultimate 3D, vous pouvez changer la façon dont les textures sont appliquées à l'objet en modifiant la façon dont les coordonnées des textures finales sont calculées. En gros, vous pouvez créer votre propre moteur de lumière. Les pixel shaders sont des programmes encore plus petits, éxécutés pour chacun des pixels à chaque frame, afin de calculer les données de ces derniers. Ils peuvent retrouver les coordonnées des textures qui ont été calculées par le vertex shader et peuvent accéder aux textures. Le résultat des calculs d'un pixel shader ne peut être autre qu'une seule couleur qui sera utilisée comme couleur du pixel. Notez que les shaders peuvent être créés et édités dans n'importe quel éditeur de texte, par exemple le Bloc Note. La figure suivante en explique le fonctionnement:

Pour commencer les vertices sont calculés, soit par les transformations et l'éclairage, soit par les vertex shader. Ensuite ces vertices sont rastérisés (Ndt: La rastérisation consiste à transformer une image vectorielle en image matricielle, affichable donc sur un écran). Cela détermine quels pixels doivent être affichés pour les triangles du mesh. Finalement le ffp (fixed function pipeline) ou le pixel shader calculent les couleurs des pixels et les affiche à l'écran. En réalité, il y a quelques étapes supplémentaires dans le pipeline de rendu, comme le brouillard qui est appliqué après les pixel shader, mais les mentionner sur le schéma l'aurait rendu plus compliqué qu'il ne le faut.

Juste pour vous donner une idée de comment se présente un shader, voici un exemple. Celui-ci présente un shader très simple. Il affiche seulement le rendu de l'objet en appliquant sa couleur de matériau et sa première texture. Un effet de shader est toujours constitué de deux ou trois fichiers. Un fichier de vertex shader (*.vsh) doit être disponible; un fichier de pixel shader (*.psh) lui, est optionel. Il y a également un fichier d'effet Ultimate 3D (*.ufx) qui contient les informations permettant à Ultimate 3D de savoir comment le vertex shader et le pixel shader doivent être utilisés.

Voici un vertex shader:

// Cela doit toujours être la première ligne de tous vos vertex shader
vs.1.1

// Cette ligne convertit la position du vertex dans les meshs en position sur
// l'écran. m4x4 est la commande utilisée à cet effet. oPos est le nom du
// registre de sortie dans lequel le shader doit sauvegarder la position sur
// l'écran. La position du vertex dans les meshs est toujours dans le
// registre v0. c0 est une variable qui est définie avant que le modèle ne
// soit rendu, qu'on appelle un registre constant. Les registres constants
// sont définis par le fichier *.ufx. Il contient le début de matrice de
// conversion requise pour calculer la position sur l'écran.
m4x4 oPos, v0, c0

// Cette ligne transmet la couleur du matériau, en le copiant simplement depuis
// le cinquième registre constant, dans le registre de sortie de la couleur
// diffuse.
mov oD0, c4

// Cette ligne transmet la première coordonnée de texture du mesh en le copiant depuis le septième registre de vertex, dans le registre de sortie des données de texture.
mov oT0, v7

Voici un pixel shader:

// Cela peut être ps.1.0, ps.1.1, ps.1.2, ps.1.3 ou ps.1.4. Si vous utilisez
// une plus haute version de pixel shader, moins de PCs supporteront
// l'effet, mais vous aurez plus de possibilités. Cette exemple utilise
// ps.1.4 parce que le code est plus simple dans cette version.
ps.1.4

// Utilise la première coordonnée de texture (t0), pour obtenir la couleur de la
// première texture (r0) et sauvegarde le résultat dans le registre temporaire
// r0. Commentaire: r0 a deux utilisés ici: il identifie la texture (cela sera r1
// pour la deuxième texture) et il est utilisé comme registre de sortie.
texld r0, t0

// Multiplie la couleur de la texture (r0) par la couleur du matériau (v0) et
// sauvegarde le résultat dans le registre de sortie (r0). La valeur de r0 à la
// fin du code sera utilisée comme couleur pour le pixel.
mul r0, r0, v0

Et voici, un fichier d'effet Ultimate 3D:

// Cela doit être la première ligne de tout fichier d'effet Ultimate 3D
Ultimate3DEffectFile 1.0

// Utilise le vertex shader (dans la mesure où il a été sauvegardé dans ce
// fichier)
VertexShaderFile effects/VertexShader.vsh
// Utilise le pixel shader. Comme je l'ai déjà dit le vertex shader peut être
// utilisé sans pixel shader, en tous cas il est utilisé ici.
PixelShaderFile effects/PixelShader.psh

// Les registres constants c0 à c3 doivent contenir les bonnes matrices de conversion dans le vertex shader.
VSConstant 0 VS_CONST_WORLD_VIEW_PROJ_TRANS_1
VSConstant 1 VS_CONST_WORLD_VIEW_PROJ_TRANS_2
VSConstant 2 VS_CONST_WORLD_VIEW_PROJ_TRANS_3
VSConstant 3 VS_CONST_WORLD_VIEW_PROJ_TRANS_4
// c4 doit contenir la couleur du matériau dans le vertex shader.
VSConstant 4 VS_CONST_MAT_DIFFUSE

Cela ne fait que sept lignes de code de shader et deux des sept lignes sont toujours les mêmes. Donc, comme vous pouvez le voir, la programmation de shader n'est pas si difficile. Cependant, cela nécessite quelques connaissances en mathématiques. Certaines de ces connaissances peuvent être lues dans le chapitre à propos des Fonctions Mathématiques d'Ultimate 3D. Si vous passez ce chapitre, il n'y a aucun problème. Mais le jour où vous vous rendrez compte que vous êtes devenu un meilleur programmeur, et vous sentirez prêt, vous devriez revenir pour étendre votre horizon.

Dans les jeux modernes, pratiquement tout ce que vous voyez utilise les effets de shader. Ils sont extrêmements importants pour les graphismes modernes. Une fois que vous êtes à même de programmer des shaders, vous avez la solution ultime pour tous vos effets spéciaux. Juste pour vous donner une idée de ce que vous ratez en passant outre ce chapitre, voici quelques exemples de techniques pouvant êtres réalisées facilement avec les shaders:
- La réflection et la réfraction pour une eau parfaite.
- Les Post Screen Shaders pour les effets tels que le HDR ou le motion blur.
- La perturbation de textures pour de superbes effets météorologiques et du feu extrêmement réaliste.
- Les effets d'étirements de l'écran.
- Et bien plus encore. Il n'y a probablement personne qui connaîsse toutes les techniques possibles.

Aussi, Ultimate 3D demo contient un ensemble d'effets de shaders plutôts sympas. Malheureusement cela prendrait trop de temps d'écrire une introduction complète à la programmation de shader. Heureusement d'autres personnes l'ont fait avant, notamment Wolfgang Engel. Il a écrit de superbes tutoriaux pour démarrer avec la programmation des shaders.

Il s'agit là d'un bon tutoriel sur la programmation des vertex shaders par Wolfgang Engel.

Un autre tutoriel de Wolfgang Engel à propos de la programmation des pixels shaders (Notez que vous ne pouvez utiliser les pixels shaders sans vertex shaders).

Et voici un troisième tutoriel du même auteur, qui donne un exemple sympathique sur la programmation des shaders, en expliquant comment réaliser différentes techniques d'éclairage par pixel (per pixel lighting).

Vous ne devriez pas passer à côté de la documentation de référence sur les vertex shader de Microsoft, ni de la documentation de référence sur les pixel shaders. Ce n'est pas un tutoriel mais un endroit où vous pouvez chercher n'importe quoi sur la programmation des shaders et où vous recevrez des informations vraiment fiables.

Une fois que vous aurez lu et compris les tutoriaux de Wolfgang Engel's, vous devriez avoir les bases théoriques nécessaires pour programmer des shaders. Quoi qu'il en soit, apprendre par la pratique est le meilleur moyen d'apprendre la programmation des shaders. Donc amusez vous avec. Tous les shaders que vous faîtes ne sont pas obligés d'être utiles, du moment que vous y apprenez quelque chose en les écrivants. Si vous avez n'importe quelle question à propos de la programmation des shaders vous pouvez les poser sur la Communauté Ultimate 3D. Afin de programmer des shaders compatibles avec Ultimate 3D, vous avez besoin de quelques précisions: vous devez savoir quel type de donnée est présente dans les différents registres d'entrée des vertex shaders. En voici une petite liste:
v0 - Position dans l'espace de meshs.
v1 - Le facteur d'influence d'un os pour les modèles supportant le skinning, la deuxième position pour les modèles utilisant le tweening.
v2 - Quatre indices de matrice pour les modèles supportant le skinning, La deuxième normale pour les modèles supportant le vertex tweening (Si vous souhaitez des éclaircissements sur les normales cliquez ici ).
v3 - La première normale
v7 à v14 - Jusqu'à huit coordonnées de texture à quatres dimensions.

Introduction au format de fichier *.ufx

La programmation de shaders est suffisamment difficile, cela serait donc difficile si vous deviez calculer les valeurs pour les constantes du shader également. C'est pourquoi, j'ai inventé le format de fichier *.ufx. C'est un format de fichier qui gère les effets de shader, en incluant toutes les informations qui leurs appartiennent. Si vous utilisez un fichier *.ufx pour vos effets de shader, vous n'aurez rien à faire après l'avoir chargé. Vous n'aurez pas à calculer un grand nombre de constantes pour le shader, parce que vous pourrez en choisir depuis une longue liste de constantes communément utilisées pour chacun des registres constants. Les constantes qui sont vraiment constantes pour chaque utilisation du shader peuvent être définies dans le fichier *.ufx ou en utilisant l'instruction def du shader. Vous pouvez aussi définir des effets alternatifs qui devront êtres utilisés, si la version des shaders requise n'est pas supportée (On appelle cela les techniques de "fallback", autrement dit techniques de repli).

Vous voyez donc que le format de fichier *.ufx peut vous épargner beaucoup de travail. Malheureusement il n'est pas encore vraiment intuitif. Mais ne vous inquiétez pas, j'écrirai un outil de création d'effets lorsque j'aurai le temps. Dans cet outil, il y aura un système de drag and drop pour créer des fichiers *.ufx et un aperçu du rendu de l'effet montrant le résultat immédiatement.

Expliquons le format de fichier *.ufx. Chaque fichier *.ufx doit être fait d'un ensemble de lignes. Chaque ligne peut contenir exactement une commande. Une commande commence toujours par un nom de commande. Puis, suivent un ensemble de paramètres, qui peuvent être des chaînes de caractères ou des nombres. Notez que vous pouvez mettre des commentaires partout ou vous le voulez en écrivant "//". La première commande qui doit être dans le fichier *.ufx est:

Ultimate3DEffectFile %version

version
Actuellement il existe les versions 1.0 et 1.1. Chaque fichier d'effet 1.0 est un shader 1.1 valide et sera compilé en conséquence. Vous devez donc toujours écrire 1.1 ici.

Ensuite vous devez définir les fichiers de shader à utiliser pour l'effet (cela doit être des fichiers *.vsh et *.psh). Il y a deux commandes différentes pour cela:

VertexShaderFile %FileNameWithPath

FileNameWithPath
Le nom de fichier et le chemin du fichier de shader relatif au dossier qui contient l'éxécutable du jeu, par exemple effects\EffectVertexShader.vsh.

Si vous avez travaillé avec le format de fichier d'effet 1.0 d'Ultimate 3D, vous vous demandez peut être où sont passés les paramètres TextureCoordinateSize. En fait Ultimate 3D les détermine automatiquement désormais, selon la donnée de vertex disponible dans le modèle. Cela rend le système de shader plus robuste. Si vous essayez d'appliquer un effet de shader pour un modèle qui n'a pas les données de vertex nécessaires, un message d'erreur apparaîtra. Après avoir définit le vertex shader, vous pouvez définir un pixel shader:

PixelShaderFile %FileNameWithPath

FileNameWithPath
Le nom de fichier et le chemin du fichier de shader relatif au dossier qui contient l'éxécutable du jeu, par exemple effects\EffectPixelShader.psh.

Remarquez que vous n'êtes pas obligé de définir un pixel shader. Après quoi, vous pouvez configurer les constantes qui doivent être définies pour les shaders. Il n'y a qu'une seule commande pour cela, mais elle peut prendre une longue liste de paramètres. Pour comprendre ce que les paramètres signifient, cela requièrt une connaissance des calculs des matrices et des vecteurs. Voici la commande de définition d'une constante pour les vertex shaders:

VSConstant %ConstantIndex %ConstantIdentifier %ManuallySettableValues

ConstantIndex
L'indice du registre constant auquel cette constante sera associée. Cela peut être une valeur allant de 0 à 95.

ConstantIdentifier
Ce qui suit est une longue liste des registres constants qu'Ultimate 3D peut calculer à votre place automatiquement. Toures les matrices et les vecteurs référant aux directions ou aux positions des autres objets sont déjà convertis en positions dans l'espace de meshs! Il est important de garder cela à l'esprit. Changer les directions ou les positions dans l'espace de meshs avant de les passer au vertex shader est bien plus efficace que de le faire pour chacun des vertex dans le vertex shader. Et usuellement, la position dans l'espace n'est pas requise. Au cas où vous en auriez tout de même besoin, vous pouvez la récupérer en la convertissant à l'aide de la matrice de conversion des positions de l'ensemble des meshs aux positions dans l'espace. L'exception ici est les modèles utilisants le vertex skinning, parce que ces derniers ont trop de conversions différentes entre les positions dans l'ensemble des meshs et les positions dans l'espace. Pour ceux dont les données des constantes sont passées sous formes de positions dans l'espace, et pour la matrice de conversion des positions de l'ensemble des meshs aux positions dans l'espace sont toujours la matrice d'identité. Les données des couleurs sont toujours passées en utilisant des valeurs allant de 0 à 1, à la place des valeurs allant de 0 à 255.

VS_CONST_MANUAL_SETTABLE: Ce paramètre dit à Ultimate 3D qu'il est supposé définir les constantes à l'aide des valeurs données dans le paramètre suivant (ManualSettableValues).
VS_CONST_WORLD_VIEW_PROJ_TRANS_1: La première colonne du produit des transformations de l'espace, de la vue et de la projection qui résultent dans la matrice de conversion des positions de l'ensemble des meshs aux positions dans l'espace.
VS_CONST_WORLD_VIEW_PROJ_TRANS_2: La deuxième colonne de cette même matrice.
VS_CONST_WORLD_VIEW_PROJ_TRANS_3: La troisième colonne de cette même matrice.
VS_CONST_WORLD_VIEW_PROJ_TRANS_4: La quatrième colonne de cette même matrice.
VS_CONST_WORLD_VIEW_TRANS_1: La première colonne du produit des transformations de l'espace et de la vue qui résultent dans la matrice de conversion des positions de l'ensemble des meshs aux positions dans l'espace.
VS_CONST_WORLD_VIEW_TRANS_2: La deuxième colonne de cette même matrice.
VS_CONST_WORLD_VIEW_TRANS_3: La troisième colonne de cette même matrice.
VS_CONST_WORLD_VIEW_TRANS_4: La quatrième colonne de cette même matrice.
VS_CONST_WORLD_TRANS_1: La première colonne de la matrice de conversion des positions de l'ensemble des meshs aux positions dans l'espace.
VS_CONST_WORLD_TRANS_2: La deuxième colonne de la matrice de conversion des positions de l'ensemble des meshs aux positions dans l'espace.
VS_CONST_WORLD_TRANS_3: La troisième colonne de la matrice de conversion des positions de l'ensemble des meshs aux positions dans l'espace.
VS_CONST_WORLD_TRANS_4: La quatrième colonne de la matrice de conversion des positions de l'ensemble des meshs aux positions dans l'espace.
VS_CONST_INV_WORLD_TRANS_1: La première colonne de la matrice de conversion des positions dans l'espace aux positions dans l'ensemble des meshs.
VS_CONST_INV_WORLD_TRANS_2: La seconde colonne de la matrice de conversion des positions dans l'espace aux positions dans l'ensemble des meshs.
VS_CONST_INV_WORLD_TRANS_3: La troisième colonne de la matrice de conversion des positions dans l'espace aux positions dans l'ensemble des meshs.
VS_CONST_INV_WORLD_TRANS_4: La quatrième colonne de la matrice de conversion des positions dans l'espace aux positions dans l'ensemble des meshs.
VS_CONST_VIEW_TRANS_1: La première colonne de la matrice de conversion des positions dans l'espace aux positions sur l'écran.
VS_CONST_VIEW_TRANS_2: La deuxième colonne de la matrice de conversion des positions dans l'espace aux positions sur l'écran.
VS_CONST_VIEW_TRANS_3: La troisième colonne de la matrice de conversion des positions dans l'espace aux positions sur l'écran.
VS_CONST_VIEW_TRANS_4: La quatrième colonne de la matrice de conversion des positions dans l'espace aux positions sur l'écran.
VS_CONST_PROJ_TRANS_1: La première colonne de la matrice de conversion des positions sur l'écran aux positions dans l'espace de projection.
VS_CONST_PROJ_TRANS_2: La deuxième colonne de la matrice de conversion des positions sur l'écran aux positions dans l'espace de projection.
VS_CONST_PROJ_TRANS_3: La troisième colonne de la matrice de conversion des positions sur l'écran aux positions dans l'espace de projection.
VS_CONST_PROJ_TRANS_4: La quatrième colonne de la matrice de conversion des positions sur l'écran aux positions dans l'espace de projection.
VS_CONST_MATRIX_STACK: Cette constante est nécessaire pour les modèles utilisants le vertex skinning. Elle diffère de toutes les autres constantes parce qu'elle n'utilise par qu'un seul registre constant, mais tous les registres constants présents après celui défini. Le registre constant défini pour cette constant contiendra les valeurs x=nombre de matrices passées, y=registre constant de la première matrice, z=3, w=1. Ces valeurs sont requises pour convertir les indices de matrice de v2 en indices de registres constants décalés. Tous les registres constants derrière VS_CONST_MATRIX_STACK seront écrasés avec, les matrices de conversion transposées 4x3 des positions de l'ensemble des meshs aux positions de l'espace, de chacun des os. Vous pouvez utiliser m4x3 pour transformer les vecteurs avec celles-ci.
VS_CONST_CAMERA_POS: La position de la caméra (dans l'ensemble de meshs).
VS_CONST_MAT_AMBIENT: La couleur ambiante du matériau.
VS_CONST_MAT_DIFFUSE: La couleur diffuse du matériau.
VS_CONST_MAT_EMISSIVE: La couleur emissive du matériau.
VS_CONST_LIGHT_AMBIENT_COLOR: La couleur de la lumière ambiante.
VS_CONST_LIGHT0_COLOR: La couleur de la lumière de la première source de lumière définie pour ce shader (voir plus bas).
VS_CONST_LIGHT1_COLOR: La couleur de la lumière de la deuxième source de lumière définie pour ce shader (voir plus bas).
VS_CONST_LIGHT2_COLOR: La couleur de la lumière de la troisième source de lumière définie pour ce shader (voir plus bas).
VS_CONST_LIGHT_DIRECTIONAL_DIRECTION: La direction de la source de lumière directionnelle définie en tant que première source de lumière (voir plus bas).
VS_CONST_LIGHT_POINT0_POSITION_INV_RANGE: Ce paramètre dira à Ultimate 3D de passer la position dans l'ensemble des meshs de la première source de lumière aux éléments x,y et z de la constante du shader, et le nombre inverse de la portée de la lumière à l'élémént w.
VS_CONST_LIGHT_POINT1_POSITION_INV_RANGE: Même chose que ci-dessus mais pour la seconde source de lumière.
VS_CONST_LIGHT_POINT2_POSITION_INV_RANGE: Même chose que ci-dessus mais pour la troisième source de lumière.
VS_CONST_FOG_DISTANCE: Ce paramètre dira à Ultimate 3D de passer la distance de départ du brouillard à l'élément x, la distance de fin du brouillard à l'élément y, le nombre inverse de la distance de départ du brouillard à l'élément z et le nombre inverse de fin du brouillard l'élément w.
VS_CONST_CURRENT_TIME: Ce paramètre dira à Ultimate 3D de passer le temps, en secondes, qui s'est déroulé depuis que le système a démarré à l'élément x, le temps qui s'est déroulé depuis qu'Ultimate 3D a été initialisé à l'élément y, 0 à l'élément z et 1 à l'élément w.
VS_CONST_CURRENT_FRAME: Ce paramètre demandera à Ultimate 3D de passer la partie fractionnaire de la valeur actuelle de frame à l'élément x, et un moins le contenu de l'élément x à l'élément y. L'élément z contiendra 0 et l'élément w contiendra 1. Cette constante est prévue pour permettre l'implémentation du vertex tweening.

ManuallySettableValues
Ce paramètre n'est obligatoire que si vous avez passé VS_CONST_MANUAL SETTABLE pour le paramètre précédent. Dans ce cas vous devrez donner dix valeurs sous forme de réels, qui seront définies comme valeurs pour les éléments x, y, z et w de la constante du shader.

Définir des constantes de pixel shader est plus facile, parce que vous n'avez pas ce large choix de constantes différentes. Les pixel shaders fonctionnent à un niveau moins élevé que les vertex shaders, et peuvent prendre au plus huit constantes de shader. C'est pourquoi le choix est plus petit ici. Voici la commande requise pour définir des constantes de pixel shader:

PSConstant %ConstantIndex %ConstantIdentifier %ManuallySettableValues

ConstantIndex
L'indice du registre de constante que vous souhaitez définir. Cette valeur peut être comprise entre 0 et 7.

ConstantIdentifier
Comme je l'ai déjà dit qu'il n'y a pas tellement d'identifiants différents que pour les constantes des vertex shader. En voici la liste:

PS_CONST_MANUAL_SETTABLE: Ce paramètre dit à Ultimate 3D qu'il est supposé définir la constante à la valeur donnée dans le paramètre suivant (ManualSettableValues).
PS_CONST_LIGHT_AMBIENT_COLOR: La couleur de la lumière ambiante.
PS_CONST_LIGHT_AMBIENT_COLOR_MATERIAL: La couleur résultant des couleurs de la lumière ambiante et de la couleur ambiante du matériau.
PS_CONST_LIGHT0_COLOR: La couleur de la première source de lumière définie pour cet effet (voir plus bas).
PS_CONST_LIGHT1_COLOR: La couleur de la deuxième source de lumière définie pour cet effet (voir plus bas).
PS_CONST_LIGHT2_COLOR: La couleur de la troisième source de lumière définie pour cet effet (voir plus bas).
PS_CONST_LIGHT0_COLOR_MATERIAL: La couleur de la première source de lumière définie pour ce shader, multipliée par la couleur diffuse du matériau.
PS_CONST_LIGHT1_COLOR_MATERIAL: La couleur de la deuxième source de lumière définie pour ce shader, multipliée par la couleur diffuse du matériau.
PS_CONST_LIGHT2_COLOR_MATERIAL: La couleur de la troisième source de lumière définie pour ce shader, multipliée par la couleur diffuse du matériau.
PS_CONST_MAT_AMBIENT: La couleur ambiante du matériau.
PS_CONST_MAT_DIFFUSE: La couleur diffuse du matériau.
PS_CONST_MAT_EMISSIVE: La couleur émissive du matériau.

ManuallySettableValues
Ce paramètre n'est obligatoire que si vous avez passé PS_CONST_MANUAL_SETTABLE au paramètre précédent. Dans ce cas vous devrez donner quatre valeurs réelles qui seront définies comme valeurs pour les éléments x, y, z et w pour la constante du shader.

Ce sont toutes les commandes dont vous avez besoin pour créer un effet de shader. Mais qu'en est-il des techniques de fallback (de repli)? En réalité, elles sont très simples à mettre en place. Il vous suffit d'écrire:

FallBackTechnique{
}

Entre les deux accolades, vous pouvez mettre une commande de définition d'un fichier de Vertex Shader et une commande de définition d'un fichier de Pixel Shader et autant de constantes de vertex shader et de pixel shader que vous voulez.

Etant donné que cela fait beaucoup de définitions, voici un exemple de fichier *.ufx valide (dans la mesure, bien sûr, où les fichiers de shaders auxquels il se réfère existent).

// Voici un exemple de fichier *.ufx
Ultimate3DEffectFile 1.0

// Le pixel shader ci-dessous utilise la version 1.4 des pixels shaders
VertexShaderFile shaders/ShaderEffect1_4.vsh
PixelShaderFile shaders/ShaderEffect1_4.psh

VSConstant 0 VS_CONST_WORLD_VIEW_PROJ_1
VSConstant 1 VS_CONST_WORLD_VIEW_PROJ_2
VSConstant 2 VS_CONST_WORLD_VIEW_PROJ_3
VSConstant 3 VS_CONST_WORLD_VIEW_PROJ_4

VSConstant 4 VS_CONST_MAT_DIFFUSE

// Le shader a besoin de ces valeurs pour calculer quelque chose. Elles peuvent aussi être définies avec la commande def du vertex shader.
VSConstant 5 VS_CONST_MANUAL_SETTABLE 3.14 6.28 -1 5

// Cette technique de fallback (de repli) sera utilisée si la version 1.4 des pixels shaders n'est pas supportée
FallBackTechnique{
// Le pixel shader ci-dessous utilise la version 1.2 des pixels shaders
VertexShaderFile shaders/ShaderEffect1_2.vsh
PixelShaderFile shaders/ShaderEffect1_2.psh
VSConstant 0 VS_CONST_WORLD_VIEW_PROJ_1
VSConstant 1 VS_CONST_WORLD_VIEW_PROJ_2
VSConstant 2 VS_CONST_WORLD_VIEW_PROJ_3
VSConstant 3 VS_CONST_WORLD_VIEW_PROJ_4

VSConstant 4 VS_CONST_MAT_DIFFUSE

VSConstant 5 VS_CONST_MANUAL_SETTABLE 3.14 6.28 -1 5
}

Application d'un shader

C'était beaucoup de théorie. Revenons en à l'utilisation pratique des effets de shader. Définir un effet de shader est très simple. Chacun des matériaux d'un modèle peut avoir son propre effet de shader. Pour le définir, une seule fonction suffit:

Cette fonction charge le fichier *.ufx donné et l'utilise pour le matériau précisé de l'objet qui appelle cette fonction.

LoadMaterialEffect(
MaterialIndex,
UFXFile
)

MaterialIndex
L'indice du matériau pour lequel vous souhaitez appliquer l'effet de shader.

UFXFile
Le fichier *.ufx à utiliser pour ce matériau. Si vous passez une chaîne vide ("") pour cet argument, l'effet actuel sera désactivé.

Si vous avez utilisé les vertex shaders avec Ultimate 3D v.1.31 vous vous souvenez sûrement qu'il n'y avait pas de fichier *.ufx. À la place, vous chargiez juste le shader et il recevait un ensemble de constantes de shader prédéfinies. Etant donné que je ne voulais pas rendre complètement incompatible les anciens codes sources avec la nouvelle version d'Ultimate 3D, il existe une fonction dans Ultimate 3D 2.0 et plus, qui fonctionne de la même façon. Elle ne demande pas de fichier *.ufx mais un fichier *.vsh.

Cette fonction définit le vertex shader donné pour le matériau choisi, en utilisant l'ensemble de constantes suivant:

0: VS_CONST_VIEW_TRANS_1
1: VS_CONST_VIEW_TRANS_2
2: VS_CONST_VIEW_TRANS_3
3: VS_CONST_VIEW_TRANS_4
4: VS_CONST_WORLD_VIEW_PROJ_TRANS_1
5: VS_CONST_WORLD_VIEW_PROJ_TRANS_2
6: VS_CONST_WORLD_VIEW_PROJ_TRANS_3
7: VS_CONST_WORLD_VIEW_PROJ_TRANS_4
8: VS_CONST_WORLD_TRANS_1
9: VS_CONST_WORLD_TRANS_2
10: VS_CONST_WORLD_TRANS_3
11: VS_CONST_WORLD_TRANS_4
12: VS_CONST_MAT_DIFFUSE

CreateMaterialVSEffect(
MaterialIndex,
VertexShaderFile
)

MaterialIndex
L'indice du matériau sur lequel le vertex shader sera appliqué.

VertexShaderFile
Le nom de fichier et le chemin du vertex shader qui sera utilisé (par exemple "effects\VSEffect.vsh").

Je vous déconseille d'utiliser cette fonction à moins que vous ne voulez utiliser des shaders développés pour Ultimate 3D 1.31. Si ce n'est pas le cas, vous devriez créer un fichier *.ufx. Ce qui est intéressant avec les fichiers *.ufx c'est qu'ils font la plupart du travail pour vous. Si vous souhaitez aider la communauté Ultimate 3D et que vous savez comment écrire des effets de shaders, vous pourriez faire un pack d'effets de shader sympas, incluant les fichiers *.ufx et le poster sur le forum. Avoir une collection d'effets de shaders sympas serait vraiment cool.

Définition des contantes de shader

La plupart des constantes de shader requises peuvent être définies en utilisant les fichiers *.ufx. Mais qu'en est-il de celles ne pouvant être calculées automatiquement par Ultimate 3D? Pour celles là, il existe deux fonctions très simple, permettant de modifier les valeurs des constanstes de vertex et de pixel shader, qui ne sont pas définies automatiquement par Ultimate 3D. Voici une description de celles-ci:

Cette fonction définit une nouvelle valeur pour les registres constants de vertex shader ayant l'indice donné, dans la mesure où il n'est pas défini automatiquement par Ultimate 3D.

SetMaterialEffectVSConstant(
MaterialIndex,
VSConstantIndex,
XValue, YValue, ZValue, WValue
)

MaterialIndex
L'indice du matériau qui utilise l'effet.

VSConstantIndex
L'indice du registre constant que vous souhaitez modifier. Cette valeur doit être inclue entre 0 et 95.

XValue, YValue, ZValue, WValue
Les nouvelles valeurs du registre constant. Cela peut être n'importe quelle valeur réelle.

La fonction SetMaterialEffectPSConstant(...) fait exactement la même chose pour les constantes de pixel shader. La seule différence est que l'indice du registre constant doit être entre 0 et 7. C'est pourquoi je ne l'expliquerai pas en détail ici.

Un peu plus haut dans ce chapitre, j'ai dit de regarder 'ci-dessous' un certain nombre de fois, lorsque je parlais des registres constants donnant des informations à propos des sources de lumière. Nous y voici enfin, lisez ce qui suit. Vous pouvez définir trois sources de lumière à utiliser pour l'effet de shader. Pour cela, il y a la fonction suivante:

Cette fonction définit les trois sources de lumière à utiliser avec l'effet de shader du matériau donné de l'objet de modèle.

SetMaterialEffectLightSource(
MaterialIndex,
LightSourceID1, LightSourceID2, LightSourceID3
)

MaterialIndex
L'indice du matériau pour lequel les sources de lumière seront définies.

LightSourceID1, LightSourceID2, LightSourceID3
L'Id d'instance Game Maker des sources de lumière, qui seront utilisées pour l'effet de shader. Vous pouvez entrer l'id des objets pour ces paramètres. Si vous n'avez pas besoin des trois sources de lumières, mettez 0 pour celles que vous ne voulez pas.

Si vous avez lu l'ensemble du chapitre et des chapitres à propos de la programmation des shaders, vous devez être vraiment content. Vous savez maintenant comment utiliser la plus flexible et adaptable des fonctionnalités d'Ultimate 3D. Ne la sous-estimez pas. Au début, il se peut que vous ne sachiez pas quoi faire avec les shaders, mais bien vite vous aurez des idées d'application de cette technique. D'autant qu'il y a des tonnes de tutoriaux sur internet à propos d'effets vraiment superbes qui peuvent être réalisés à l'aide de shaders. En général, il y a un exemple de donné. Je vous conseille de jouer énormément avec les shaders pour les apprendre. C'est vraiment la meilleure façon d'amasser de l'expérience. Tous les effets que vous faites ne doivent pas nécessairement avoir de sens, du moment que vous apprennez quelque chose en les faisant. En changeant seulement quelques lignes dans le code d'un shader vous pouvez obtenir des effets vraiments intéressants, que vous ne pourriez réaliser sans shaders. Par exemple, vous pouvez donner les normales des vertex en tant que couleur diffuse des vertex (dans le vertex shader).

Maintenant, amusez-vous bien :D .


Post screen shaders

C'est un sujet réellement intéressant car il traite d'une fonctionnalité très puissante. Une fois que vous saurez ce qu'est un post screen shader vous serez d'accord avec moi. Donc voici l'explication. Vous savez déjà ce qu'un effet de shader standard. Si ce n'est pas le cas, lisez le chapitre qui en parle. Si vous ne savez pas comment écrire des shaders vous ne pourrez pas écrire de post screen shader, et si vous ne savez pas ce que c'est, vous ne pouvez pas les utiliser non plus. Les effets de shader normaux sont géniaux, parce qu'ils peuvent être utilisés pour de nombreux effets vraiment corrects. Cependant, leurs possibilités sont limitées, parce qu'ils peuvent être appliqués seulement à des matériaux des modèles. C'est un désavantage que les post screen shaders n'ont pas. Ils ne sont pas appliqués à des matériaux particuliers ou à des modèles, mais à l'écran tout entier. Après qu'Ultimate 3D ait rendu la scène en entier, le post screen shader est appliqué au rendu. Cela les rends puissants.

Un exemple simple pour cela est que vous souhaitez que tout soit noir et blanc pour certaines raisons. Vous pouvez le faire en créant des copies noires de toutes les textures et en changeant toutes les couleurs des matériaux en niveaux de gris. Vous pouvez utiliser un effet de pixel shader pour absolument tout ce qui est visible. Ce n'est pas non plus souhaitable, parce que cela implique de changer chacun des objets de votre scène. Avec un post screen shader, cela peut être réalisé plus facilement. Vous écrivez un simple (très simple) post screen shader qui implémente l'effet noir et blanc. Ensuite vous l'appliquez et vous avez fini, sans toucher à un seul objet de la scène. Le post screen shader prend seulement l'image rendue, modifie la couleur de chacun des pixels et l'affiche à l'écran.

C'est un exemple où le post screen shader vous épargne beaucoup de travail, mais il ne fait rien qui ne soit impossible sans lui. Voici donc un autre exemple, qui ne pourrait être réalisé sans les post screen shaders: un simple effet de blur. Imaginez que le personnage principal de votre jeu a été empoisonné. Vous avez donc besoin d'un effet sympa pour montrer au joueur que le personnage ne sent pas bien. Un effet de blur qui s'applique à l'écran tout entier, est parfait pour cela. Avec les effets de shader usuels, cela ne pourrait pas être implémenté, mais avec les post screen shaders, cela ne pose aucun problème.

Le nombre d'effets possibles est très grand. Bloom, blur et différents effets de motion blur sont des usages communs des post screen shaders. De plus, les post screen shaders peuvent être utilisés pour des effets de déformation. Divers effets exotiques peuvent aussi être réalisés. Par exemple vous pouvez inverser les couleurs, changer le gamma, doubler l'image (pour les personnages principaux soûls ;) ), assombrir ou éclaircir des zones particulières de l'écran, surligner les contours pour donner un effet de bande dessinée, ou bien vous pouvez créer un post screen shader qui donne l'impression que tout est comme dans un vieux film en noir et blanc qui a été regardé des milliers de fois. Ils peuvent aussi être utilisés pour les effets de profondeur de champ, mais ce sont des techniques très avancées, et requérant du temps de calcul intensif.

La façon dont les post screen shaders fonctionnent

C'est un court chapitre et les processus qui seront décrits ici sont réalisés automatiquement par Ultimate 3D. Néanmoins, il est important de connaître cela, parce que sinon, vous ne pourrez pas comprendre pourquoi les post screen shaders doivent ressembler à ce qu'ils sont censés êtres. Lorsque vous commencez à utiliser un post screen shader, Ultimate 3D n'affichera plus la scène directement à l'écran. À la place, il l'affichera à une texture. Une fois que c'est fait, cette texture contient tout ce que vous voyez habituellement à l'écran. Ensuite, cela affiche un rectangle qui couvre tout l'écran et qui utilise cette texture. Ce rectangle utilise votre post screen shader. De cette façon, vous pouvez définir comment la texture, qui contient le rendu de la scene, sera affichée à l'écran.

Il est également possible d'utiliser plusieurs post screen shaders. Ils seront appliqués l'un après l'autre. Si vous utilisez deux post screen shaders, Ultimate 3D rendra la scène complète dans une texture. Ensuite, cela calculera le rendu d'un rectangle qui utilise cette texture de scène dans une seconde texture utilisant le premier post screen shader que vous avez défini. À l'étape suivante, cela rendra cette seconde texture à l'écran en utilisant le second post screen shader que vous avez défini. S'il y avait eu un troisième post screen shader, le second aurait été rendu dans la première texture à nouveau et cette première texture aurait été affichée à l'écran en utilisant le troisième post screen shader. Cela signifie qu'il n'y aura jamais plus de deux textures pour tous les post screen shaders, en terme d'utilisation de mémoire les post screen shaders sont donc vraiment économiques.

Une autre chose que vous devez remarquer à propos des post screen shaders est que l'ordre dans lequel vous les appliquez a une incidence. Voici un exemple simple pour cela. Admettons que vous avez une scène dans laquelle tout est bleu pour une raison quelconque (irréaliste, mais c'est un bon exemple ici). Le canal de rouge et de vert sont toujours à zero. Et disons que vous avez deux post screen shaders. Le premier transforme tout en niveaux de gris en prenant la signification arithmétique des trois canaux, et le second ignore les canaux vert et bleu et donne aux trois canaux la valeur du canal de rouge. Si vous appliquez le post screen shader qui donne la valeur du canal rouge à tout les canaux avant le post screen shader chargé de tout transformer en niveaux de gris, le premier post screen shader donnera une image noire. Si vous utilisez le post screen shader qui donne la valeur du canal rouge à tout les canaux après le post screen shader chargé de tout transformer en niveaux de gris, le premier post screen shader appliqué donnera une version plus sombre du canal bleu aux trois canaux et le second post screen shader appliqué aura quelque données positives à afficher. Ainsi les résultats diffèrents selon l'ordre des effets. Pour cette raison, vous pouvez définir une priorité pour chacun des post screen shaders.

L'utilisation des post screen shaders

Cela peut sembler un peu étrange, mais les post screen shaders sont représentés par des objets Game Maker au même titre que les modèles, les primitifs et les terrains. La raison de ce choix est qu'il rend l'utilisation des post screen shaders très confortable. Vous avez simplement un objet pour chaque effet de post screen shader et lorsque vous en avez besoin d'un, vous créez simplement une instance de l'objet correspondant. Le reste est fait par l'objet automatiquement. Aussi il arrive très souvent que le même post screen shader doit être utilisé plusieurs fois pour atteindre un effet suffisamment fort. Dans ce cas, il vous suffit de créer de multiples instances de l'objet du post screen shader. Et si une room dans Game Maker utilise toujours un post screen shader particulier, il vous suffit de placer une instance de l'objet du post screen shader dans cette room et vous en avez fini. Pas de programmation élaborée.

Donc, la seule chose que vous devez savoir maintenant, c'est comment créer ces objets de post screen shader. C'est très facile. La partie compliquée est d'écrire les shaders, mais si c'est trop difficile pour vous, vous pouvez toujours utiliser des shaders faits par d'autres. La création de post screen shader valides sera abordée plus bas. La création d'objet de post screen shader est similaire à la création de tous les autres objets Ultimate 3D. À la première étape, vous devez définir un ensemble de variables. Ensuite, vous appellez une fonction pour créer le post screen shader. Après quoi, vous pouvez appeller quelques autres fonctions pour définir d'autres options pour le post screen shader. Finalement vous devez mettre un appel au script Destroy() dans l'évènement destroy. La fonction Step() n'est pas requise. Voici une liste des variables qui sont disponibles pour les post screen shaders. Il n'y en a que quatre:

vertex_shader
Le vertex shader qui appartient à ce post screen shader. Cette variable doit être une chaîne, par exemple "effects/BlurPSS.vsh".

pixel_shader
Le pixel shader qui appartient à ce post screen shader. Cette variable doit être une chaîne, par exemple "effects/BlurPSS.psh". Remarquez que la création d'effets de post screen shader échouera sans afficher la moindre erreur si la version des pixel shaders n'est pas supportée. Pour cette raison vous devez appeller GetSupportedPSVersion() avant de créer le post screen shader, afin d'être sûr que la version des pixel shaders requise est supportée.

camera_index (optional)
Chaque post screen shader s'applique à l'image rendue par un objet caméra particulier. Si ce n'est pas la caméra par défaut, vous devez définir la variable à l'indice de la caméra en question, qui est la valeur de la variable number de cette caméra (dans la fourchette de 1 à 31, étant donné que 0 se réfère à la caméra par défaut), et non pas l'id de l'objet.

priority (optional)
Comme expliqué plus avant, l'ordre dans lequel les post screen shaders sont appliqué a une incidence. C'est pourquoi vous pouvez utiliser cette variable pour assigner des priorités aux objets de post screen shader. Cela peut être n'importe quelle valeur flottante. Plus haut est la valeur de cette variable, plus tôt est appliqué le post screen shader à chaque frame.

Une fois que vous avez fini de définir toutes ces variables, vous pouvez utiliser la fonction CreatePostScreenShader() pour créer le post screen shader. Maintenant vous vous demandez peut être pourquoi les post screen shaders n'utilisent pas le format de fichier d'effet Ultimate 3D (*.ufx). Parce qu'en fait, cela aurait rendu les choses inutilement compliquées. Etant donné que les post screen shaders ne sont pas appliqués aux objets 3D mais à l'écran tout entier, il n'y pas beaucoup d'informations constantes précalculées qui pourraient être définies pour ces derniers. Il n'y a pas de données à propos des matériaux, les transformations des objets de la scene n'influent pas et les sources de lumières sont déjà appliquées. En général, tout ce dont vous avez besoin, c'est quelques informations sur les couleurs, ou quelques ensembles de facteurs pour changer la force de l'effet. Cela peut être défini soit au travers de l'instruction def d'un shader particulier, ou au travers des deux fonctions suivantes:

Cette fonction définie un registre constant d'un vertex shader particulier pour l'objet de post screen shader par lequel elle est appellée. Elle est à même d'écraser les registres constants définis au travers de l'instruction def des vertex shaders.

SetPSSVSConstant(
VSConstantIndex,
Value1, Value2, Value3, Value4
)

VSConstantIndex
L'indice du registre constant de vertex shader que vous voulez modifier. Cela peut être une valeur arbitraire comprise entre 0 et 95.

Value1, Value2, Value3, Value4
Les valeurs que vous voulez définir pour ces registres constants. Value1 sera copié dans l'élément x, Value2 sera copié dans l'élément y, Value3 sera copié dans l'élément z, Value4 sera copié dans l'élément w.

Cette fonction définit un registre constant de pixel shader particulier pour l'objet par lequel il est appellé. Il n'est pas à même d'écraser les registres constants qui ont été définis au travers de l'instruction def des pixel shaders.

SetPSSPSConstant(
PSConstantIndex,
Value1, Value2, Value3, Value4
)

PSConstantIndex
L'indice du registre constant de pixel shader que vous souhaitez modifier. Cela peut être n'importe quelle valeur entière située entre 0 et 7.

Value1, Value2, Value3, Value4
Les valeurs que vous voulez définir pour ce registre constant.

Cependant les constantes de shader ne sont pas les seules choses que vous pouvez définir pour les post screen shaders. Ils peuvent également avoir une texture définie pour chaque niveau de texture. Seule la texture du premier niveau de texture (niveau 0) ne peut être changée car ce niveau utilise toujours la texture de la scene. Les textures de tous les autres niveaux peuvent être définies à l'aide de la fonction suivante.

Cette fonction définit la texture donnée en tant que texture pour le niveau de texture spécifié de l'objet de post screen shader par lequel elle est appellée:

SetPSSTexture(
TextureStage,
TextureIndex
)

TextureStage
L'indice du niveau pour lequel vous voulez définir une autre texture. Etant donné que le premier niveau de texture utilise toujours la texture de la scène et ne peut être modifié, cela doit être une valeur comprise entre 1 et 7.

TextureIndex
L'indice de la texture que vous voulez définir pour ce niveau, qui est la valeur de retour de la fonction de chargement de texture.

Lors de l'écriture de post screen shaders, il arrive assez souvent que vous puissiez tirer un avantage d'un filtre de texture particulier, afin de donner un meilleur aspect au rendu de vos effets. Par exemple, si vous utilisez un filtrage linéaire pour la texture de la scène, cela la rendra un peu floue, ce qui peut être voulu ou non. Pour cette raison, vous pouvez définir un filtre de texture individuel pour chacun de niveau de texture des post screen shaders. Par défaut, le premier niveau utilise le point de filtrage le plus proche et tous les autres niveaux utilisent le filtre de texture qui a été défini au travers de SetFilter(...), qui est le filtrage linéaire par défaut. Si vous voulez définir d'autre filtre, vous pouvez utiliser la fonction suivante:

Cette fonction définit un filtre de texture différent pour un niveau de texture particulier de l'objet de post screen shader qui appelle cette fonction:

SetPSSFilter(
TextureStage,
FilterType
)

TextureStage
Le niveau de texture pour lequel vous voulez définir un nouveau filtre. Cela peut être n'importe quelle valeur comprise entre 0 et 7.

FilterType
Le filtre que vous voulez définir pour ce niveau. Pour obtenir une description de la signification des différentes valeurs que vous pouvez passer à ce paramètre, veuillez vous réferer à la description de la fonction SetFilter(...).

Ecrire des post screen shaders

Les post screen shaders sont écrits dans le même langage assembleur que les effets de shader habituels. Cependant, il sont généralement plutôt différents. Les vertex shaders en particulier, peuvent différer très fortement. La première raison est qu'aucun calcul de matrice n'a besoin d'être effectué, parce que les registres de position des vertex sont déjà dans l'espace de projection. La deuxième raison est que les rectangles, qui sont utilisés pour appliquer les post screen shaders, ne sont pas fait de plus de quatre vertices. Ainsi chaque vertex shader d'un post screen shader ne calcule pas plus de quatre vertices, ce qui n'est presque rien en comparaison des centaines et des milliers qui doivent être calculés pour les shaders utilisés pour les modèles 3D. C'est pourquoi vous n'avez pas besoin de vous soucier de l'optimisation des post screen vertex shaders. Si dix lignes supplémentaires de code de vertex shader peuvent vous épargner une ligne de code de pixel shader, ou si elles rendent l'utilisation de l'effet de post screen shader un peu plus confortable, vous devriez les utiliser.

La chose la plus importante que vous devez savoir, pour être en mesure d'écrire un post screen shader valide, est les registres de données des vertex disponibles. Ce sont v0, v7 et v8. Voici une description de leur contenu:

v0 contient la position du vertex dans l'espace de projection. Etant donné qu'il est déjà dans l'espace de projet, vous pouvez simplement utiliser une instruction mov pour le passer au registre de sortie oPos. Aucune matrice n'est nécessaire.

v7 contient les coordonnées de texture qui seront utilisées pour la texture de la scène.

v8 contient les coordonnées de textures dans la fourchette allant de 0 à 1, qui peut être utilisée pour toutes les autres textures.

La raison pour laquelle v7 et v8 sont différents, est que les textures doivent toujours avoir des dimensions faites de puissances de deux, alors que les cibles de rendu peuvent avoir une taille arbitraire. Ainsi si vous avez une fenêtre de 800*600, la texture de scène aura les dimensions 1024*1024, ainsi les coordonnées de textures de v7 seront dans la fourchette allant de 0 à 800/1024 pour la coordonnée x, et dans la fourchette allant de 0 à 600/1024 pour la coordonnée y.

Après tant de théorie, voici une petit exemple de post screen shader. Il utilise la carte de distorsion des coordonnées de la texture qui sont supposées être définies pour le niveau 1 afin de déformer l'image à l'écran. Ce petit post screen shader exemple est déjà vraiment puissant. Il peut être utilisé pour des effets de distorsions plutôt cools.

Voici le code du vertex shader:

vs.1.1

mov oPos, v0
mov oT0, v7
mov oT1, v8

On ne peut plus simple, pourrait-on :D ? Voici le code du pixel shader:

ps.1.4

texcrd r0.rgb, t0
texld r1, t1

mad r1.rgb, r1, c0, r0

phase

texld r0, r1

Le pixel shader fait les choses suivantes. D'abord il obtient les premières coordonnées de texture pour ce pixel et la donnée de la carte de distorsion. Ensuite il multiplie la donnée de la carte de distorsion par un facteur et ajoute la première coordonnée de texture du pixel. Pour finir, il utilise cette nouvelle coordonnée de texture afin de chercher un texel dans la texture de la scène. Cela sera l'affichage de ce shader. Pour utiliser ce shader pour devez définir une carte de distorsion pour le niveau 1 et un facteur de distorsion pour la première constante de pixel shader. C'est tout.


Les ombres en temps réel

Oh cool, les ombres en temps réel. J'ai attendu si longtemps pour elles, et maintenant je vais les utiliser pour absolument tout dans mon jeu. Chaque feuille d'un arbre va jeter son ombre sur le terrain. Cela va rendre tellement bien! Absolument tout aura sa propre ombre, comme dans la réalité!

Si c'est ce que vous êtes en train de penser, je vais vous décevoir. C'est tout simplement irrealisable. Aucun PC ne sera à même de faire tourner cela de façon fluide. Quoi qu'il en soit, les stencils d'ombres en temps-réel d'Ultimate 3D sont une fonctionnalité vraiment cool. Des scènes sans ombres n'ont pas l'air authentiques. Les choses doivent être ombrées pour avoir l'air réaliste. Les ombres aident l'oeil humain à comprendre la structure tridimensionnelle des images bidimensionnelles vues à l'écran. Dans les jeux choquants, les ombres peuvent contribuer grandement à l'effet dramatique des évènements. Ils peuvent vous donner une idée des choses que l'on ne voit pas directement. De cette façon, une ombre peut effrayer les gens qui jouent au jeu, du moins... si c'est bien fait.

Ce n'est pas tout à fait faux de voir la projection des ombres en temps réel comme une méthode pour améliorer le potentiel dramatique de votre jeu, mais cela nécessite d'être utilisé avec prudence pour obtenir une bonne performance. Dans ce contexte la leçon la plus importante est que les ombres en temps réel doivent être utilisées uniquement pour les objets et les sources de lumières non statiques. Pour des ombres statiques vous pouvez utiliser des techniques telles que le light mapping, qui peut être réalisée à l'aide des fonctions de multi texturage, qui sont beaucoup plus économiques.

Ultimate 3D utilise l'accélération matérielle des stencils d'ombre. Vous n'avez pas besoin de savoir comment ces ombres fonctionnent, mais vous devriez connaître les différents avantages et désavantages de cette technique. Les avantages sont principalement l'extrême qualité des ombres résultantes, qui sont très précises pour chaque pixel, une capacité matérielle faible requise (pas de support des pixel shader n'est requise), la fiabilité, la combination sans soucis avec les effets de shaders tels que le per pixel lighting et étant donné que les ombres en temps réel sont complètement accélérées matériellement, elles ne stressent pas le CPU plus que ça. Les désavantages sont que vos modèles doivent respecter certaines conditions, pour être des moules d'ombres valides, elles stressent vraiment le GPU; des ombres douces ne sont pas réalisables.

Utilisation des ombres en temps réel

L'utilisation d'ombres en temps réel dans Ultimate 3D est très facile. La première chose que vous devez faire est de vérifier qu'elles sont prises en charge. Si c'est le cas, vous pouvez activer le moulage d'ombre (shadow casting) pour des sources de lumière particulières, définir un objet de modèle particulier en tant que moule d'ombre et définir des objets de modèles, des objets de terrain ou des objets primitifs en tant que receveurs d'ombre. La première chose que vous avez à faire pour pouvoir utiliser les ombres, est de définir global.u3d_z_buffer_format à 32 dans le script Ultimate3DOptions(). Pour plus d'informations sur la signification de cette variable, référez vous au chapitre sur les options d'Ultimate 3D. La raison pour laquelle il ne peut y avoir d'ombres sans un buffer z de 32 bits est que 8 bits du buffer z est utilisé en tant que buffer de stencil, qui est requis pour calculer les ombres. Si le PC que vous êtes en train d'utiliser est assez récent et si le buffer z a le bon format, la fonction GetShadowSupport() retournera true, cela signifie que vous pouvez utiliser les ombres en temps réel. Autrement vous ne pourrez pas, toutes les fonctions relatives aux ombres ne feront rien dans ce cas.

Maintenant que vous êtes sûr que les ombres sont supportées, vous pouvez appeller la fonction suivante pour activer les ombres pour une source de lumière particulière.

Cette fonction active ou désactive les ombres pour l'objet de source de lumière par lequel elle est appellée. Cela doit être appellé après LightDirectional(), LightPoint() ou LightSpot().

SwitchShadows(
Enable
)

Enable
Si vous passez true pour ce paramètre, Ultimate 3D activera les ombres pour cette source de lumière, autrement il les désactivera.

Ensuite vous pouvez définir quel objet de modèle projette des ombres pour quelles sources de lumière.

Cette fonction active ou désactive la projection d'ombre pour l'objet de modèle par lequel il est appellé. Si le modèle utilise le vertex skinning, il ne doit pas utiliser plus de 28 os, étant donné que tout os supplémentaire serait transformé incorrectement. Ce qui explique ce comportement est le nombre limité de constantes de vertex shader disponible dans la version de Direct3D utilisée.

SwitchShadowCasting(
LightSourceID,
Enable
)

LightSourceID
L'id Game Maker de l'objet de source de lumière pour lequel ce modèle doit projeter des ombres.

Enable
Si la projection d'ombre doit être activée ou désactivée.

Finalement vous devriez être en mesure de définir quels objets peuvent recevoir les ombres. C'est tout aussi facile.

Cette fonction active ou désactive la réception d'ombres projetées par une source de lumière particulière. Cela l'applique à l'objet de terrain, à l'objet de modèle ou à l'objet primitif par lequel elle est appellée.

SwitchShadowReceiving(
LightSourceID,
Enable
)

LightSourceID
La source de lumière que vous voulez activer ou désactiver pour la réception des ombres.

Enable
Si vous passez true, l'objet recevra des ombres de la source de lumière donnée, sinon non. Notez que les terrains ne recevront aucune ombre tant qu'ils n'auront pas appellé CalculateTerrainLightMap(...) au moins une fois après avoir appellé cette fonction.

Maintenant vous pouvez activer les ombres pour les sources de lumières, la projection d'ombre pour les modèles, et la réception d'ombres pour différents types d'objets. C'est tout ce dont vous avez besoin pour utiliser les ombres avec Ultimate 3D. Cependant, le résultat que vous obtiendrez avec ces connaissances sera souvent pauvre dans la majorité des cas. Cela s'explique par le fait que les stencils d'ombres requièrent une géométrie spécifique pour fonctionner correctement. La géométrie ne doit avoir aucun trou et devrait avoir des triangles pour représenter les bords, à moins qu'elle ne dispose d'un nombre élevé de triangles. Cela aura l'air réaliste uniquement si cette géométrie est disponible. Par chance, Ultimate 3D possède une fonction permettant d'altérer la géométrie des modèles de sorte qu'ils respectent ces conditions. Comme d'habitude vous n'avez pas beaucoup de travail à faire, parce qu'Ultimate 3D fait tout automatiquement. Vous avez juste à lui demander ce service.

Cette fonction crée la géométrie optimisée pour la projection d'ombres de l'objet de modèle qui l'appelle. Elle doit être appellée après LoadMesh() (et après l'appel à CreateLODChain(...) si un appel y est fait).

CreateShadowOptimizedGeometry(
LODIndex,
CreateConnectionPatches
)

LODIndex
Grâce à ce paramètre, vous pouvez définir pour quel niveau de détail vous souhaitez créer la géométrie optimisée pour les ombres. Souvent il est logique d'utiliser des niveaux de détail faibles pour la projection d'ombre puisque les ombres n'ont pas besoin d'être très précises. Le plus haut niveau de détail avec une géométrie optimisée sera utilisée pour la projection d'ombre. Pour ce paramètre, 0 se réfère au plus haut niveau de détail (le modèle inchangé), 1 équivaut au niveau de détail plus bas suivant, et ainsi de suite.

CreateConnectionPatches
Si vous passez true pour ce paramètre, cette fonction créera la géométrie qui donnera une ombre extrêmement précise, mais le prix à payer est cher. Cela nécessite la création de deux triangles pour chaque bord du modèle. Si vous passez false ces patchs de connexion ne seront pas créés, cela correspond à une meilleure performance mais à des ombres moins détaillées.

Malheureusement, même cette fonction ne parvient pas à optimiser correctement la géométrie dans certains cas. Si cela arrive, vous obtiendrez des résultats extrêmement laids. L'ombre ne s'arrêtera pas derrière le modèle. Au contraire, elle ira à l'infini en longues bandes noires. Donc, quelles sont les conditions que le modèle doit respecter pour éviter cet effet? C'est très simple à faire, mais un peu difficile à comprendre. Tous les bords du modèle ne doivent être partagés par pas plus de deux triangles. Voici une petite illustration pour clarifier cela.

La géométrie sur la gauche pourrait être optimisée parfaitement, parce qu'elle est faite de bords simples (bleu) et de bords doubles (vert) uniquement. Mais le morceau de géométrie sur la droite a deux bords qui sont partagés par trois triangles (rouge). Pour cette raison le mécanisme utilisé par Ultimate 3D pour remplir les trous dans la géométrie des modèles ne peut trouver de meilleure solution et de fait, la fonction ne retournera pas une géométrie optimisée pour l'ombre valide dans ce cas. C'est pourquoi aucun des modèles projetants de l'ombre dans votre scène ne peut avoir des triangles configurés ainsi. C'est à vous de vérifier cela. Ultimate 3D ne peut le faire pour vous, parce qu'un procédé mathématique qui donne une solution unique ne peut être trouvé dans cette situation. Mais c'est peu coûteux pour des ombres en temps réel extrêmement réalistes, n'est-ce pas ;) ?

Shadow checklist

Récemment des problèmes avec le système de projection d'ombre ont été rapportés assez souvent sur la Communauté Ultimate 3D. Dans la majorité des cas, c'était le résultat d'une mauvaise utilisation. Il reste cependant qu'il est difficile de déboguer les problèmes liés aux ombres, parce que si un seul élément est faux le système d'ombres ne fonctionnera pas complètement et il n'y a aucun moyen de trouver si c'est la source de lumière, le projeteur d'ombre ou celui qui la reçoit qui ne fonctionne pas. Pour cette raison, voici une checklist avec laquelle vous pouvez travailler si vos ombres ne fonctionne pas. Elle explique toutes les erreurs communes et précise comment les corriger. Lisez ceci avant de poster n'importe quelle question liée au non fonctionnement des stencils d'ombre.

L'erreur la plus commune est que SwitchShadowCasting(...) et SwitchShadowReceiving(...) sont appellés avant que les sources de lumière n'aient étés créés et initialisées. Dans ce cas, le paramètre LightSourceID sera invalide et cela fera que la projection d'ombre ou la réception d'ombre ne fonctionnera pas. La manière la plus sûre d'éviter ce problème est de placer ces appels dans le step event des objets. Elles ne font aucun calcul intensif, donc cela ne sera pas mauvais pour les performances.

Une autre erreur qui a été faite à plusieurs reprises est que CreateShadowOptimizedGeometry(...) n'a pas été appellé pour l'objet de modèle projetant les ombres. Vous devriez toujours appeller cette fonction pour la projection d'ombre. Sans elle, vous obtiendrez des ombres fausses, imprecises ou carrément pas d'ombres du tout dans la plupart des cas. La seule exception ici est les modèles avec un très haut nombre de triangles. Pour eux, cela devrait fonctionner sans appel à cette fonction, mais si les ombres ne fonctionnent pas et que vous n'utilisez pas CreateShadowOptimizedGeometry(...), vous savez ce qu'il vous reste à faire immédiatement.

Si vous avez des problèmes avec l'utilisation les objets de terrain comme receveurs d'ombres, vous devriez vérifier la valeur de retour de GetSimultaneousTextureCount(...). Si c'est inférieur à trois, votre système ne supporte pas le light mapping des terrains en combinaison avec le texture splatting. Et sans light mapping, vous ne pouvez pas obtenir d'ombres. Une autre chose que vous devriez noter: les terrains avec une light map personnalisée ne peuvent recevoir d'ombres.

Si les ombres fonctionnent, mais que vous obtenez d'étranges bandes d'ombres, qui vont jusqu'à l'infini, lisez l'explication des bords avec trois faces accollés ci-cessus. C'est souvent la cause de ce problème. Une autre cause possible de ce problème est que vous n'appellez pas CreateShadowOptimizedGeometry(...) correctement.

Une dernière cause possible de non-fonctionnement des ombres est une proportion inappropriée entre camera.min_range et camera.max_range. Si le résultat de camera.max_range/camera.min_range est très élevé, il peut arriver que les erreurs d'arrondis font aller le champ de vision à l'infini. Etant donné que le champ de vision joue un role important pour les projeteurs d'ombres, la projection d'ombres ne fonctionnera pas. Pour régler ce problème, vous devez simplement augmenter camera.min_range et/ou diminuer camera.max_range.


Manipulation des modèles

Dans certains cas, il peut arriver que vous deviez modifier la géométrie d'un objet pendant que le jeu est en cours d'exécution; par exemple, si vous avez un objet pouvant être déformé avec une arme particulière. Cette déformation peut être réalisée à l'aide de vertex shader. Mais c'est plutôt compliqué. C'est pourquoi Ultimate 3D vous donne la possibilité de manipuler la géométrie d'un mesh en temps réel. Vous pouvez éditer chacun des vertex du mesh. Vous pouvez aussi modifier la façon dont les vertices sont mis ensembles en triangles. Vous pouvez changer le matériau en cours d'utilisation par un triangle. En fait, vous pourriez utiliser Ultimate 3D pour faire un programme de modélisation étant donné qu'il est possible de créer des meshs à partir de rien.

Il est également assez courant que l'animation d'un modèle doive être modifiée. Par exemple, dans les jeux à la troisième personne, vous voulez peut-être que le torse ou la tête du personnage tourne lorsqu'il regarde dans une autre direction. Cela peut être fait dans Ultimate 3D de deux façons différentes.

Manipulation de la géométrie des meshs

Avant que je n'explique comment manipuler la géométrie des meshs, je vais donner une explication du terme mesh lui même, car il se peut que vous n'ayez pas une idée exacte de ce que c'est. Un mesh est un morceau de géométrie. Il peut être composé d'un nombre arbitraire de triangles et de vertices. Si vous avez un fichier *.md2, chaque frame est un mesh. Si vous avez un fichier *.3ds, il se peut que votre modèle soit fait de plusieurs meshs. Si vous avez un fichier animé *.an8, chaque objet que vous avez créé dans Anim8or est un mesh. De plus, une copie de chacun des meshs sera créée lorsque vous ajouter un nouveau LoD (niveau de détail) à votre modèle.

Il est très important de garder à l'esprit qu'Ultimate 3D ne créera jamais le même mesh deux fois! Si vous charger un modèle d'un fichier, tous les objets basés sur ce modèle utiliseront les mêmes meshs. Cela serait une énorme gaspillage de mémoire si cela n'avait pas été fait ainsi. Donc si vous modifier le mesh d'un objet, ce mesh sera modifié pour chaque objet qui utilisent le même fichier. Pour d'autres données, telles que les matériaux et les os, ce n'est pas le cas parce qu'il ne prennent pas beaucoup de mémoire.

Désormais, il y a un autre terme nouveau que vous devez connaître. Jusqu'à présent vous avez appris que chaque objet 3D est fait de vertices et que ces vertices sont mis ensembles en triangles. Donc que chaque triangle est fait de trois vertices. Maintenant regardez l'illustration suivante.

Comme vous pouvez le voir, ce rectangle est composé de neuf vertices différents (marqués par les petits carrés) et de huit triangles. Etant donné que chaque triangle est fait de trois vertices, vous supposez probablement que ce petit mesh requière 8*3 = 24 vertices pour être affichés. Mais il n'y a que 9 vertices différents, et seulement deux sont utilisés par seulement un seul triangle (marqués en vert). Le vertex du milieu est utilisé par six triangles. Et des choses comme ça arrivent vraiment souvent. Heureusement une simple méthode fut inventée il y a longtemps pour éviter ce gaspillage de mémoire. Si vous avez des modèles complexes, il peut arriver que le modèle a deux fois plus de triangles que de vertices. Mais comment cela fonctionne? C'est très simple. Vous sauvegardez chacun des vertex exactement une fois. L'ordre ne joue pas. Ensuite vous ajoutez un buffer nommé buffer d'index (et c'est le terme que vous devez vous rappeller) qui contient trois indices (des nombres entiers qui correspondent aux vertices) pour chaque triangle. Donc en réalité, un triangle n'est pas fait de trois vertices, mais de trois références à des vertices.

Après ce peu de théorie, il est temps de pratiquer. Dans Ultimate 3D, vous pouvez vérouiller n'importe quel mesh de n'importe quel objet de modèle à n'importe quel moment. Verouiller un mesh, signifie le préparer pour retrouver et changer ses données. Cela est nécessaire parce que la mémoire où les données du mesh sont sauvegardées peuvent être gérée par le périphérique graphique. Il est vraiment important que vous déverrouilliez chaque mesh que vous verrouillez. Si vous oubliez de le faire, il est possible qu'une violation d'accès se produise lorsque le mesh sera rendu à l'écran, et cela peut amener à de nombreuses erreurs de rendu ou à une erreur de Game Maker "unexpected errors". Pour verouiller les meshs, il existe la fonction suivante:

Cette fonction verouille le mesh donné de l'objet par lequel elle est appellée.

LockMesh(
LockedMeshIndex,
MeshIndex,
Frame,
LoD
)

LockedMeshIndex
Dans Ultimate 3D, vous ne pouvez pas vérouiller plus de 32 meshs à la fois. Cela n'aurait pas posé de problème de définir une limite plus haute ici, mais si vous n'oubliez pas de débloquer les meshs, vous n'aurez jamais plus de 32 meshs bloqués en même temps de toute façon. La valeur de ce paramètre doit être entre 0 et 31. C'est l'indice qui sera associé au mesh vérouillé.

MeshIndex
L'indice du mesh que vous voulez verrouiller. Je vous conseille d'écrire une boucle for pour boucler à travers tout les meshs de l'objet. Vous pouvez utiliser la fonction GetMeshCount() pour obtenir le nombre de mesh qu'un objet a par frame.

Frame
La Frame dans laquelle vous voulez modifier un mesh. Si vous utilisez cette fonction pour un modèle qui n'a pas été chargé depuis une fichier *.md2 ou pour un objet créé avec AddFrame(...), vous devez toujours mettre 0 pour ce paramètre.

LoD
Le niveau de détail que vous voulez modifier. Le niveau de détail le plus élevé a l'indice 0. C'est aussi la valeur que vous devez entrer si vous n'utilisez pas plusieurs niveaux de détails pour le modèle.

Cette fonction déverrouille un mesh et redéfinit les données associées à l'indice du mesh verrouillé aux données par défaut. Elle doit être appellée une fois pour chaque appell de LockMesh(...)!

UnlockMesh(
LockedMeshIndex
)

LockedMeshIndex
L'indice du mesh que vous voulez déverrouiller.

Une fois que vous avez verrouillé un mesh, vous pouvez le modifier de toutes les manières imaginables. Vous pouvez modifier ses vertex, ses indices de triangles et les matériaux qui sont associés avec chacun des triangles. Vous pouvez aussi référencer toutes ces informations. Pour chacune de ces tâches, il y a une fonction différente:

Cette fonction modifie un vertex du mesh verrouillé avec l'index donné:

SetLockedMeshVertex(
LockedMeshIndex,
VertexIndex,
ValueIndex,
NewValue
)

LockedMeshIndex
L'indice du mesh vérouillé que vous souhaitez modifier. C'est la valeur que vous mettez en premier paramètre lorsque vous appellez LockMesh(...) pour verrouiller le mesh.

VertexIndex
L'indice du vertex que vous voulez modifier. Je vous recommande d'écrire une boucle for pour boucler au travers de tous les vertices. Vous pouvez utiliser GetLockedMeshVertexCount(LockedMeshIndex) pour obtenir le nombre de vertices que le mesh vérouillé donné possède.

ValueIndex
Ce paramètre donne la partie de la donnée du vertex qui doit être changée. Etant donné qu'Ultimate 3D utilise un système de coordonnées différent que Game Maker, cette donnée est ordonnée de la façon suivante:
0 = VertexPositionX
1 = VertexPositionZ
2 = VertexPositionY
3 = VertexNormalX
4 = VertexNormalZ
5 = VertexNormalY
6 = VertexTextureCoordinate1U
7 = VertexTextureCoordinate1V
Ensuite les coordonnées de texture qui ont été ajoutées en utilisant AddTextureCoordinateSet(...) suivent. La normale d'un vertex est un vecteur requis pour calculer l'éclairage des objets. Habituellement, il n'y a pas besoin de les modifier manuellement. Si vous changer la géométrie du mesh, vous devriez appeller une fois RecalculateNormals() pour mettre à jour les normales.

NewValue
La nouvelle valeur que vous voulez définir pour la partie donnée du vertex spécifié.

Cette fonction retourne une partie des données d'un vertex du mesh spécifié:

GetLockedMeshVertex(
LockedMeshIndex,
VertexIndex,
ValueIndex
)

LockedMeshIndex
L'indice du mesh verrouillé dont vous voulez récupérer des données. C'est la valeur que vous mettez comme premier paramètre lors de l'appel de la fonction LockMesh(...)

VertexIndex
L'indice du vertex dont vous voulez récupérer des données. Je vous recommande d'écrire une boucle for pour boucler au travers de tous les vertices. Vous pouvez utiliser GetLockedMeshVertexCount (LockedMeshIndex) pour obtenir le nombre de vertices que le mesh vérouillé donné possède.

ValueIndex
Ce paramètre est exactement le même que ValueIndex dans SetLockedMeshVertex(...), regardez donc sa description.

Cette fonction modifie les vertices qui sont utilisés par le triangle ayant l'indice donné du mesh verrouillé spécifié.

SetLockedMeshTriangle(
LockedMeshIndex,
TriangleIndex,
VertexIndexA, VertexIndexB, VertexIndexC
)

LockedMeshIndex
L'indice du mesh verrouillé que vous voulez modifier. C'est la valeur que vous mettez en tant que premier paramètre lors de l'appel de LockMesh(...) pour verrouiller le mesh.

TriangleIndex
L'indice du triangle que vous souhaitez modifier. Je vous conseille d'écrire une boucle for pour boucler au travers de tous les triangles. Vous pouvez utiliser la fonction GetLockedMeshTriangleCount(LockedMeshIndex) pour obtenir le nombre de triangle du mesh verrouillé spécifié.

VertexIndexA, VertexIndexB, VertexIndexC
Les indices des vertices qui sont utilisés pour le triangle. Remarquez que l'ordre de ces paramètres a une influence, à cause du back face culling. Vous ne devez jamais entrer une valeur plus grande que le nombre de vertex du mesh verrouillé ici. Cela pourrait donner lieu à de sérieux problèmes d'affichage.

Cette fonction permet de retrouver l'indice d'un vertex d'un triangle dans le mesh verrouillé donné:

GetLockedMeshTriangle(
LockedMeshIndex,
TriangleIndex,
RequestedVertexIndex
)

LockedMeshIndex
L'indice du mesh verouillé dont vous voulez extraire des données. C'est la valeur que vous avez entré pour le premier paramètre de LockMesh(...) lorsque vous avez verrouillé le mesh.

TriangleIndex
L'indice du triangle dont vous voulez extraire les données. Je vous conseille d'écrire une boucle for pour boucler au travers de tous les triangles. Vous pouvez utiliser la fonction GetLockedMeshTriangleCount (LockedMeshIndex) pour obtenir le nombre de triangles du mesh verrouillé spécifié.

RequestedVertexIndex
Pour ce paramètre, vous pouvez passer 0, 1 ou 2 pour récupérer le premier, le deuxième ou le troisième indice de vertex du triangle.

Cette fonction change le matériau utilisé pour le triangle donné du mesh spécifié:

SetLockedMeshTriangleMaterial(
LockedMeshIndex,
TriangleIndex,
NewMaterialIndex
)

LockedMeshIndex
L'indice du mesh verrouillé pour lequel vous voulez changer le matériau du triangle. C'est la valeur que vous avez entré pour le premier paramètre de LockMesh(...) lors du verrouillage du mesh.

TriangleIndex
L'indice du triangle pour lequel vous voulez changer le matériau. Je vous conseille d'écrire une boucle for pour boucler au travers de tous les triangles. Vous pouvez utiliser la fonction GetLockedMeshTriangleCount (LockedMeshIndex) pour obtenir le nombre de triangles du mesh verrouillé spécifié.

NewMaterialIndex
Le nouvel indice du matériau que vous voulez définir pour le triangle. Vous pouvez utiliser GetMaterialIndex(...) pour obtenir cette valeur.

Cette fonction retourne l'indice du matériau qui est actuellement utilisé par le triangle donné du mesh verouillé spécifié.

GetLockedMeshTriangleMaterial(
LockedMeshIndex,
TriangleIndex
)

LockedMeshIndex
L'indice du mesh verrouillé dont vous voulez extraire les données. C'est la valeur que vous avez entré pour le premier paramètre de LockMesh(...) lors du verrouillage du mesh.

TriangleIndex
L'indice du triangle dont vous voulez extraire les données. Je vous conseille d'écrire une boucle for pour boucler au travers de tous les triangles. Vous pouvez utiliser la fonction GetLockedMeshTriangleCount (LockedMeshIndex) pour obtenir le nombre de triangles du mesh verrouillé spécifié.

Il existe une autre fonction de manipulation de modèles qui a été mentionnée à quelques reprises auparavant. Elle peut être utilisée pour ajouter un nouvel ensemble de coordonnées de texture à tous les meshs d'un objet (les meshs ne doivent pas être verrouillés lorsque vous faites cela). Vous avez déjà vu comment remplir les coordonnées de texture ajoutées avec des données en utilisant la fonction SetLockedMeshVertex(...). Un moyen simple d'obtenir les données est de créer deux versions du même modèle avec deux ensembles de coordonnées de texture différents. Ensuite, vous pouvez créer une instance temporaire de la seconde version du modèle, bloquer ses meshs et les meshs du premier modèle, copier les coordonnées de texture du second modèle dans le premier modèle en quelques boucles for. Lorsque vous en aurez fini, vous pourrez détruire le second modèle temporaire. Mais venons en à la fonction.

Cette fonction peut être utilisée pour ajouter un nouvel ensemble de coordonnées de texture multidimensionnelles à tous les meshs de l'objet qui appelle la fonction (et tous les objets basés sur le même modèle!).

AddTextureCoordinateSet(
DimensionCount
)

DimensionCount
Le nombre de dimensions de l'ensemble de coordonnées de texture qu'il devrait avoir. Cette valeur peut être un entier entre 1 et 4. Une dimension signifie qu'il n'y aura qu'une seule valeur réelle dans la coordonnée de texture (U), deux dimensions signifie qu'il y en a deux (U, V), trois dimensions signifie qu'il y en a trois (U, V, W) et quatre signifie qu'il y en a quatre (U, V, W, Q).

Finalement, il y a une fonction qui permet l'utilisation de plusieurs ensembles de coordonnées de texture beaucoup plus facilement. Souvent vous avez besoin de ne charger simplement que deux ensembles de coordonnées de texture différents pour un modèle. Par exemple, le light mapping requiert un ensemble de coordonnées de texture pour la diffuse map et un second ensemble de coordonnées de texture pour la light map. Le problème est qu'aucun des formats de fichier supporté, mis à part les *.u3d, ne supportent plusieurs ensembles de coordonnées de texture. La solution pour ce problème est de créer deux versions du modèle, chacun d'eux ayant un ensemble de coordonnées de texture différent. Ensuite vous pouvez utiliser la fonction suivante pour combiner les deux.

Cette fonction copie tous les ensembles de coordonnées de texture du modèle donné aux meshs de l'objet de modèle qui appelle cette fonction et à tous les objets qui sont basés sur le même fichier de modèle. Les meshs de cet objet ne doivent pas être verrouillés.

AddTextureCoordinateSetsFromModel(
ModelFile, Password
)

ModelFile
Le modèle de fichier qui possède les ensembles de coordonnées de texture qui sont à ajouter. Ce modèle de fichier doit avoir le même nombre de meshs que l'objet de modèle qui appelle cette fonction, et tous les meshs doivent avoir le même nombre de vertices.

Password
Si ModelFile se réfère à un modèle Ultimate 3D crypté, vous pouvez passer le mot de passe à ce paramètre. Autrement, vous ne devez rien passer ou une chaîne vide.

Manipulation d'animation

En particulier, dans les jeux à la troisième personne, il arrive très souvent que le personnage doive se déplacer interactivement. Par exemple, si vous avez un personnage avec une arme qui vise quelque part, son torse doit se lever et se baisser quand le joueur vise en haut ou en bas pour le rendre réaliste. Ultimate 3D propose deux façons différentes de faire cela. La première change simplement la rotation d'un os particulier. Cela peut être utilisé pour atteindre l'effet désiré assez facilement, mais cela ne rendra pas forcément bien. Pour cette raison, il y a aussi la possibilité de faire en sorte que des os particuliers utilisent d'autres valeurs de frame que les autres os. De cette façon, vous pouvez par exemple faire en sorte que la partie supérieure du corps d'un personnage utilise une animation différente que le reste. Pour les deux méthodes, vous avez besoin d'une fonction pour récupérer l'indice de l'os que vous voulez modifier.

Cette fonction retourne l'indice de l'os avec le nom donné, ou 0 s'il n'existe pas.

GetBoneIndex(
BoneName
)

BoneName
Le nom de l'os. Cela dépend du programme de modélisation que vous utilisez.

Si vous voulez faire quelque chose automatiquement pour tous les os dans un modèle, il est utile d'écrire une boucle, qui passe au travers de tous les os d'un modèle. Pour ce faire, vous avez besoin de savoir combien d'os font partie du modèle. Cette information peut être récupérée à l'aide de la fonction GetBoneCount() qui retourne le nombre d'os disponibles de l'objet de modèle qui l'appelle. Toutes les valeurs entières dans la fourchette allant de 0 à GetBoneCount()-1 sont des indices d'os valides. Une autre fonction, qui est utile lorsque vous voulez avoir un code qui fonctionne automatiquement avec les os est la suivante.

Cette fonction retourne l'indice de l'os parent de l'os avec l'indice donné. Si l'os n'a pas d'os parent -1 est retourné.

GetBoneParent(
BoneIndex
)

BoneIndex
L'indice de l'os pour lequel vous voulez retrouver l'indice de l'os parent.

La fonction suivante est une de celles qui peuvent être utilisées pour faire pivoter un os. Cela donne habituellement un résultat assez irréaliste lorsque c'est utilisé pour des modèles de personnage, mais cela peut être très utile pour objets mécaniques tels que les tanks. Par exemple, il est très simple de donner une rotation à la tourelle d'un tank avec cette fonction.

Cette fonction supprimme toutes les clés de rotation de l'os avec l'indice donné, de l'objet qui appelle la fonction et crée une seule nouvelle clé de rotation avec la rotation spécifiée.

SetBoneRotation(
BoneIndex,
RotationX, RotationY, RotationZ
)

BoneIndex
L'indice de l'os que vous voulez modifier.

RotationX, RotationY, RotationZ
La nouvelle rotation. Les variables sont interprétées de la même façon que les variables rotx, roty et rotz des objets de modèle.

Voici la dernière fonction qui peut être utilisée pour des animations de personnages vraiment interactive et authentiques. Par exemple, vous pourriez faire un modèle qui a une animation pour la partie supérieure du corps dans laquelle il vise vers le haut et vers le bas et differentes animations lorsqu'il court pour les jambes. Ensuite cette fonction pourrait être utilisée pour faire en sorte que la partie supérieure du corps vise vers le haut ou le bas selon la position du viseur du joueur, tandis que les jambes seraient en train de courrir dans des directions différentes ou même en train d'attendre.

Cette fonction peut être utilisée pour définir une autre valeur de frame pour un os particulier, de l'objet qui l'appelle.

SetBoneFrame(
BoneIndex,
Frame,
AffectChildBones

BoneIndex
L'indice de l'os dont vous voulez changer la valeur de frame.

Frame
Une valeur positive donnant une valeur de frame absolue qui est destinée à être utilisée pour l'os. Elle sera utilisée à la place de la valeur de la variable frame, qui correspond à la valeur de retour de GetCurrentFrame().

AffectChildBones
Si vous passez true pour ce paramètre, la valeur de frame précisée sera utilisée non selon pour l'os avec l'indice BoneIndex, mais aussi pour tous les autres os qui sont attachés à cet os. Sinon, seul cet os sera affecté. Si l'un des os enfant a sa propre valeur de frame définie à l'aide de SetBoneFrame(...) celle-ci sera utilisée à la place, que vous passiez true ou false.

Un autre type important de la manipulation d'animation est l'interpolation entre deux poses dans une animation. Imaginez que votre personnage est en train de courrir. Le joueur arrête d'appuyer la touche qui le fait courrir, de sorte que le personnage doit s'arrêter. Maintenant cela serait assez laid si l'animation sauterait seulement à l'animation d'arrêt. Il serait plus agréable si cela s'interpolait doucement entre l'animation de course et l'animation d'arrêt. C'est là l'utilité de l'interpolation des poses.

Cette fonction commence une interpolation entre la pose actuelle et la pose du modèle à la frame de destination donnée.

StartPoseInterpolation(
DestinationFrame,
InterpolationDuration
)

DestinationFrame
La frame du modèle qui a la pose de destination.

InterpolationDuration
Le temps que prendra l'interpolation en steps. Au cours cet interval de temps, l'objet procèdera à l'interpolation entre les poses. Après quoi, cela continuera à jouer l'animation à partir de DestinationFrame, à moins que les valeurs first_frame et last_frame ne le permettent pas. Si vous voulez annuler l'interpolation d'une pose vous pouvez appeller à nouveau cette fonction et passer 0 pour ce paramètre.

Lorsque vous avez un modèle avec de très nombreuses animations différentes, vous trouverez peut-être inconfortable d'avoir toutes ces animations dans un seul fichier, avec une séquence d'animation après l'autre. Il serait plus agréable d'avoir un fichier pour chaque animation. Ultimate 3D propose une fonctionnalité à cette fin. Ce n'est pas une solution parfaite, mais ça fonctionne. Cela vous permet de charger des animations depuis un fichier de modèle.

Cette fonction copie une animation d'un fichier de modèle donné dans l'objet de modèle par lequel elle est appellée. Elle fait ça en remplaçant toutes les frames dans l'animation de cet objet de modèle, par celles sauvegardées dans modèle de fichier donné.

CopyAnimation(
SourceFile,
SourceFilePassword,
CopyRotationKeysOnly
)

SourceFile
Le fichier source depuis lequel vous souhaitez copier l'animation. Vous voulez peut-être précharger ce fichier de modèle à l'aide de PreloadMesh(...) pour éviter un lag.

SourceFilePassword
Si le fichier source est un fichier de modèle Ultimate 3D crypté (*.u3d), vous pouvez entrer le mot de passe pour ce paramètre. Sinon, vous devez passer une chaîne vide.

CopyRotationKeysOnly
Si ce paramètre est true, la fonction copiera exclusivement les clés de rotation, autrement toutes les clés seront copiées. Si le squelette de l'objet de modèle de destination ne correspond pas exactement au squelette du modèle source, cela peut être souhaitable.

Cette fonction doit être utilisée avec prudence. Cela ne peut fonctionner correctement seulement si le modèle source et l'objet de modèle de destination ont des squelettes identiques. Si vous passez true pour CopyRotationKeysOnly cela peut aussi fonctionner pour les modèles avec un squelette un tantinet différent, mais vous ne devriez pas vous appuyer là desssus.

Création de meshs particuliers

Dans certains cas, il est nécessaire de créer des meshs à partir de zéro. Cela peut être fait facilement avec Ultimate 3D. Vous créer ces objets comme tous les autres. D'abord vous définissez un certain nombre de variables, ensuite vous appellez une fonction de création une fois et enfin vous devez ajouter Step() et Destroy() dans les évènements correspondants.

material_count
Le nombre de matériaux que le nouvel objet aura. Cette valeur ne peut être zéro.

vertex_count
Le nombre de vertices que l'unique mesh du nouvel objet aura. Cela doit être supérieur à trois.

triangle_count
Le nombre de triangle qu'aura le seul mesh du nouvel objet. Cette valeur ne peut pas être égale à zéro.

Après avoir défini ces variables, vous pouvez appeller CreateEmptyMesh(). Ensuite un objet avec le matériau donné, le nombre de vertices et de triangles sera créé et associé avec l'objet qui a appellé la fonction. Après que vous ayez ajouté Step() et Destroy() l'objet sera prêt à être utilisé. Mais avant que vous obteniez des résultats visibles, vous devez utiliser les fonctions de manipulation de modèle que j'ai introduit ci-dessus pour remplir le mesh avec une géométrie valide. Par défaut toutes les données de vertex et toutes les données d'indices seront remplies avec des zéros et tous les matériaux seront blancs et non texturés.

Fonctions de manipulation de modèles préconçues

Il y a un ensemble d'opérations de manipulation de mesh qui doivent être faites très souvent. Ultimate 3D propose quelques fonction facile à utiliser pour réaliser ces opérations. La plus importante recalcule les normales des vecteurs qui sont nécessaires pour obtenir un éclairage par pixel correct. Une autre adoucit les normales des meshs. Cela signifie qu'elle cherche les vertices ayant la même position mais des normales différentes pour fixer cela. La troisième calcule quelque chose nommé matrices inverse de l'espace tangent pour chacun des vertex. Une brève explication de ce que cela signifie peut être trouvée ci-dessous.

Cette fonction calcule un vertex de la normale de chacun des vertex de chaque mesh de l'objet qui l'appelle. Elle devrait être appellée à chaque fois que vous changez la géométrie du mesh, parce que les anciennes normales ne seront plus d'actualités. Elle ne prend pas d'arguments.

RecalculateNormals()

Il arrive très souvent que des plis apparaissent à la surface si les normales sont calculées de cette façon. Etant donné que les normales ne sont pas seulement requises pour le calcul de l'éclairage mais aussi pour les effets tels que le cel-shading et l'environment mapping, c'est vraiment indésirable. L'illustration suivante montre un modèle qui utilise l'environment mapping, regardez s'il y a des plis sur celui-ci.

Pour éviter cela, Ultimate 3D propose une fonction simple. Elle cherche les plis dans le modèle et si elle en trouve elle les adoucit. Voici sa description.

Cette fonction cherche les vertices avec positions identiques, mais avec des normales différentes. Si elle en trouve, elle vérifie que l'angle entre leurs normales est plus petit que l'angle donné, et si c'est le cas elle les adoucit. Noter que ce processus est très intensif en temps de calcul.

SmoothNormals(
SmoothingAngle
)

SmoothingAngle
L'angle de lissage en degrés. La fonction adoucit les normales des deux vertices, uniquement, si l'angle entre elles est plus petit que celui-ci.

La fonction suivante a un but très spécifique. À moins que vous ne vouliez vous mettre à la programmation de pixel shader, vous n'avez pas besoin de la comprendre. Vous pouvez passer l'explication suivante. Beaucoup d'effets spéciaux, celui-ci en particulier, qui implémente une sorte de per pixel lighting, utilise les maps de normales. Les normal maps donnent les normales dans l'espace de la texture. C'est espace est appellé espace tangent. Les informations telles que celles de position des sources de lumières sont usuellement données dans l'espace de l'ensemble de mesh. Pour cette raison une méthode est requise pour obtenir les informations à propos des sources de lumières de l'ensemble de mesh en espace tangent. Une matrice (regardez le chapitre à propos des fonctions mathématiques) est nécessaire pour apporter des informations depuis l'ensemble des meshs à l'espace tangent. Et étant donné que chaque triangle est texturé différemment, cette matrice est requise pour chaque vertex.

Cette fonction calcule la matrice inverse de l'espace tangent (de l'ensemble de mesh à l'espace tangent) pour chaque vertex de chaque mesh de l'objet qui appelle la fonction, cela signifie qu'elle écrase les normales des meshs avec la troisième colonne de la matrice. Habituellement la troisième colonne est très proche des normales, cela ne fait donc pas d'énorme changement. Cela ajoute aussi deux nouveaux ensembles 3D de coordonées de texture. Le premier sera utilisé pour sauvegarder la première colonne de la matrice inverse de l'espace tangent, et le deuxième pour sauvegarder la deuxième colonne. Cette fonction ne prend pas de paramètres.

AddInverseTangentSpaceMatrices()

Cel-shading

Le Cel-shading est une technique courramment utilisée pour créer des graphismes qui ont l'air de faire partie d'une bande déssinée. Cela peut aussi être utilisé pour marquer un objet particulier en créant un surlignement autour de lui. Fondamentalement le Cel-shading crée une version extrudée de chaque mesh et calcule son rendu en utilisant un back face culling inversé. Cela consiste en un contour marqué autour du modèle. Lors de l'application du Cel-shading, vous pouvez r la largeur du contour dans l'espace de mesh et la couleur du contour. Communément la couleur du contour est un noir solide. Mais vous pouvez aussi utiliser d'autres couleurs pour réaliser d'autre effets super-cools. Par exemple, vous pouvez utiliser un rouge transparent pour mettre une accent sur l'objet auquel il est appliqué. Activer le cel-shading est très simple. Voici la fonction qui le fait:

Cette fonction active le cel-shading pour l'objet de modèle par lequel elle est appellée.

ApplyCelShading(
LineWidth,
LineColorR, LineColorG, LineColorB, LineColorA
)

LineWidth
La largeur du contour que vous voulez créer dans l'ensemble de mesh.

LineColorR, LineColorG, LineColorB, LineColorA
La couleur du contour. Les valeurs pour ces paramètres doivent être comprises entre 0 et 255. LineColorA définit l'opacité du contour. 0 signifie aucune opacité (complètement transparent) et 255 complètement opaque.

Notez que le cel-shading utilise un vertex shader. Pour cette raison, les modèles utilisant le vertex skinning et du cel-shading ne doivent pas utiliser plus de 28 os, sinon, quelques os ne seront pas appliqués correctements.


Le moteur de rendu de terrain

Le moteur de rendu de terrain peut être utilisé pour créer des terrains géants à l'aide des height maps. Il utilise la technique la plus efficace existante pour calculer le rendu de terrains grands et détaillés. Ces méthodes sont nommées geo-mip-mapping et texture splatting. Etant donné qu'il est difficile de comprendre comment utiliser le moteur de rendu de terrain correctement sans connaître comment ces techniques fonctionnent, je vais vous les expliquer maintenant.

Geo-mip-mapping et texture splatting

Ainsi qu'est-ce que le geo-mip-mapping? Il s'agit d'une technique utilisée pour créer efficacement différents niveaux de détails pour les terrains. Le terrain est divisé en un ensemble de carreaux (on nomme cela le tiling) de puissance de deux. Chaque carreau a 5 niveaux de détails différents avec un nombre de triangle allant de 512 à 2. Du fait de la structure efficace des niveaux des LoDs (Niveaux de détails) tous peuvent utiliser le même vertex buffer. La figure suivante illustre à quoi ressemblent les différents niveaux de détails.

En fonction de la différence de hauteur du carreau et de la distance de la caméra, Ultimate 3D décidera quel niveau de détails utiliser pour afficher le carreau en question. Cette grande variété de LoDs rend le moteur de rendu de terrain incroyablement rapide. Un problème auquel vous êtes fréquemment confronté lorsque vous afficher un terrain, et comment obtenir de bonnes textures pour le terrain. Chaque partie du terrain devrait avoir une couleur différente, mais une texture qui couvre l'ensemble du terrain serait soit extrêmement grande, soit très peu détaillée. Aucune des deux solutions n'est intéressante. Une technique pour éviter cela est l'utilisation d'une carte de détails (detail map), mais cela n'est pas très beau.

C'est pourquoi il existe une technique nommée texture splatting. Lorsque vous utilisez le texture splatting, vous pouvez utiliser autant de textures, étirées sur le terrain (cet étirement correspond aux tiling mais pour les textures), que vous souhaitez. Il vous est donc, ensuite possible d'utiliser les maps alpha pour définir des transparences différentes des textures en différents points. L'image suivante en illustre le fonctionnement:

Le texture splatting vous donne la possibilité de rendre votre terrain vraiment détaillé et vivant. Vous pouvez utiliser une texture de pierre pour les collines et des textures d'herbe ou de boue pour les plateaux. Ainsi, vous pouvez également mettre la neige sur les mont de vos montagnes et de plus, mais pas seulement, vous avez la possibilité d'utiliser une texture séparée pour les chemins sur lesquels le personnage peut marcher. Remarquez que le texture splatting requière le support du multi texturing avec, au moins, deux texture simultanées. Cela signifie que la valeur de retour de GetSimultaneousTextureCount() doit être >= 2.

Utiliser le moteur de rendu de terrain

Utiliser le moteur de rendu de terrain est vraiment facile. Le point difficile est de créer les diffuse maps, height maps et les alpha maps (il existe des programme destinés à cela). Mais ce n'est pas le sujet pour le moment. Ici vous apprendrez comment créer un terrain, et comment définir les différentes textures pour ce dernier. Comme avec n'importe quel autre objet Ultimate 3D, un terrain requière son propre objet Game Maker. Et comme d'usage vous devez définir un certain nombre de variables avant de créer le terrain. En voici une liste:

x, y, z
L'origine du terrain. Chaque point sur le terrain aura une coordonée x, y et z qui sera supérieure à la valeur de la variable correspondante.

height_map
Le fichier qui contient la height map que vous voulez utiliser pour créer le terrain. Cela pourrait être, par exemple, "gfx/TerrainHeightMap.png". La height map peut être en niveaux de gris, mais si vous avez besoin de plus de précision, vous pouvez aussi utiliser tous les canaux pour définir la hauteur. La formule utilisée pour convertir une couleur R, G, B, A ayant toutes ses valeurs entre 0 et 255 en une hauteur pour le vertex est z+height*(R*2^24+G*2^16+B*2^8+A)/2^32.

tile_count_x, tile_count_y
Le nombre de carreaux le long de l'axe x et y. Ce sont deux des trois tailles importantes qui donnent la taille du terrain. Elle définissent également à quel point le terrain sera détaillé et combien de temps de calcul et de mémoire sera nécessaire pour calculer son rendu et le créer. Etant donné qu'un terrain est constitué d'une géométrie complexe et détaillée vous ne devriez pas utiliser de trop grandes valeurs ici. 100*100 serait déjà très gros. Sa géométrie prendrait environ 57 MB de RAM.

tile_size
C'est la troisième valeur qui est importante pour la taille du terrain. Elle donne la taille le long de l'axe x et y de chaque carreau dans l'espace. Etant donné que les carreaux sont toujours des carrés, une valeur suffit.

height
La hauteur du terrain. Un pixel noir dans la height map correspondra à la position z sur le terrain égale à la valeur de la variable z, tandis qu'un pixel blanc dans la height map correspondra à une position z sur le terrain, qui sera égale à z+height.

base_texture
Une chaîne de caractères donnant le fichier qui contient la première texture qui est destinée à être appliquée au terrain. Cette texture ne requière pas de map alpha étant donné qu'elle ne recrouvre aucune autre texture.

wrap_count_x, wrap_count_y
L'étirement le long de l'axe x et y pour la première texture. L'étirement est une valeur qui définie à quelle fréquence la texture sera répétée. Habituellement les valeurs de ces variables sont plutôt semblables à celles que vous avez définies pour les variables tile_count_x et tile_count_y.

terrain_lod (optionel)
Le facteur de niveau de détail pour le terrain, qui influence la manière dont Ultimate 3D choisi les LoDs lorsqu'il calcule le rendu des carreaux. Cette valeur est optionelle, vous n'avez donc pas besoin de la définir. Si effectivement elle n'est pas définie, Ultimate 3D utilisera 1 comme facteur de LoD. Si vous définissez une valeur inférieure à 1, des LoDs plus élevés seront utilisés, si vous entrez une valeur supérieure, des LoD's moins élevés seront utilisés.

light_map_resolution (optionnel)
La résolution qui sera utilisée pour la light map (regardez plus bas pour plus d'informations sur les light maps). Par défaut cette variable a 256 pour valeur. Une light map est utilisée pour le terrain tout entier. La valeur pour cette variable doit être une puissance de deux.

alpha_map_resolution (optionnel)
La résolution qui sera utilisée pour la map alpha de chaque carreau. Par défaut, la valeur est de 32. Chaque carreau du terrain a son propre ensemble de maps alpha, donc si vous utilisez de grandes valeurs, cela requèrera beaucoup de mémoire. Une bonne valeur ici, se trouve en général autour de 2*light_map_resolution/(tile_count_x+tile_count_y). La valeur pour cette variable doit être une puissance de deux.

Après la déclaration et la définition de ces variables vous pouvez appeler CreateTerrain(), une seule fois, afin de créer le terrain et l'associer avec l'objet qui a appellé la fonction. Ensuite vous devez ajouter les autres textures au terrain. S'il n'y a pas d'autres textures à appliquer, il vous suffit d'appeler ApplyTerrainTextures(). Etant donné que les terrains sont des objets vraiments statiques, il est inutile de mettre Step() dans l'évènement step cette fois-ci. Cependant, il vous faut toujours mettre Destroy() dans l'évènement correspondant. Si vous voulez utiliser plus d'une texture, à l'aide du texture splatting, il vous faudra mettre d'autres appels de fonctions entre les appels à CreateTerrain() et ApplyTerrainTextures. Etant donné que chaque texture couvre les textures qui ont été appliquées auparavant , l'odre d'appel des fonctions a une influence sur le rendu final. Voici la fonction:

Cette fonction ajoute une texture au terrain par lequel il est appelé, en utilisant le texture splatting:

AddTerrainTexture(
DiffuseMap,
AlphaMap,
WrapCountX, WrapCountY,
AlphaMapChannel
)

DiffuseMap
Une chaîne de caractères donne la fichier contenant la map diffuse que vous voulez ajouter au terrain.

AlphaMap
Une chaîne de caractères indiquant le fichier qui contient la map alpha que vous souhaitez utiliser.

WrapCountX, WrapCountY
L'étirement le long de l'axe x et y pour la map diffuse. Le wrap count est une valeur définissant à quelle fréquence la texture doit être répétée. Pour qu'il soit moins évident de voir que la texture se répète, vous devriez utiliser des étirements différents pour les différentes textures.

AlphaMapChannel
La canal de la map alpha, dont on va récupérer les données. Cela peut être 0 pour utiliser la luminosité (pour les maps alpha en niveaux de gris), 1 pour utiliser le canal rouge, 2 pour utiliser le canal vert, 3 pour utiliser le canal bleu ou 4 pour utiliser le canal alpha. Grâce à ce paramètre un même ficher de map alpha peut contenir les maps alphas de plusieurs maps diffuses.

Voilà donc, tout ce que vous devez savoir sur la création de terrains. Avec ces quelques fonctions vous pouvez créer des paysages géants déjà bien détaillés. Cela ne serait pas possible avec des modèles de terrains usuels. Le moteur de rendu de terrain est donc destiné aux gens souhaitants créer des jeux avec de nombreux grands niveaux se jouant à l'exétérieur.

Il existe également deux fonctions utiles pour récupérer des données à propros d'un objet de terrain à une position donnée. L'une d'elle permet de récupérer la hauteur du terrain à une position particulière, et l'autre peut être utilisée pour savoir quelle texture est utilisée à quelle force à une poistion particulière. Voici leurs descriptions:

Cette fonction retourne la hauteur d'un objet de terrain donné à une position donnée. Elle prend très peu de temps de calcul.

GetTerrainHeightAtPos(
TerrainID,
X, Y
)

TerrainID
L'ID de l'objet de terrain dont vous souhaitez retrouver une valeur de hauteur.

X, Y
La position pour laquelle vous souhaitez déterminer la hauteur.

Cette fonction retourne l'intensité d'une texture donnée à la position spécifiée, du terrain donné. La valeur de retour est un réel allant de 0 à 1, où 0 signifie que la texture ne peut être vue à cet endroit et 1 signifie que la texture est la seule pouvant être vue à cette position.

GetTextureStrengthAtPosition(
TerrainID,
TextureIndex,
X, Y
)

TerrainID
L'ID de l'objet de terrain dont vous voulez retrouver l'intensité de la texture.

TextureIndex
L'indice de la texture dont vous voulez récupérer des informations. 0 se réfère la diffuse map de base, 1 se refère à la première texture ajoutée et ainsi de suite.

X, Y
La position pour laquelle vous voulez connaître l'intensité de la texture.

Ces deux fonctions sont très utiles. La première peut être utiliser pour une detection simple et efficace des collisions avec le terrain, la deuxième peut être utilisée pour déterminer la matière présente à tel endroit. Si vous souhaitez mettre de la végétation sur un terrain vous pouvez utiliser GetTextureStrengthAtPosition(...) pour ne pas créer de végétation sur les rocher ou sur les routes.

Calculer la light map

Si vous avez déjà créé un terrain vous avez peut être remarqué qu'il n'est pas éclairé par les sources de lumières. Vous devriez en être heureux; car cela serait affreux si le vertex lighting était utilisé parce que le nombre de vertex de chaque carreaux change lorsque le LoD change. Et étant donné que l'oeil humain est très sensible aux changements rapides, cela ne donnerait rien d'esthétique. C'est pourquoi les terrains utilisent une light map à la place. Une light map est une texture qui est appliquée sur tout le terrain à l'aide d'une opération de texture multiplicatrice. Remarquez que cela requière le support du multi texturing avec trois textures, ainsi la valeur de retour de GetSimultaneousTextureCount() doit être >=3.

Ultimate 3D calcule la light map automatiquement. Il vous suffit de lui dire quelles sources de lumières doivent être utilisées pour créer la light map. Souvent, il n'est pas bon d'utiliser des sources de lumières qui bougent beaucoup pour calculer la light map, parce que la recalculer à chaque frame prend énormément de temps de calcul. Vous devriez donc n'utiliser que les sources de lumière qui ne changent pas rapidement. Je vous conseille d'utiliser une morceau de code qui dit à Ultimate 3D de recalculer la light map fréquemment (par exemple une fois pas seconde). Voici la fonction dont vous avez besoin pour faire calculer la light map par Ultimate 3D.

Cette fonction dit à Ultimate 3D de recalculer la light map pour l'objet de terrain qui appelle la fonction.

CalculateTerrainLightMap(
LightSourceID1, LightSourceID2, ..., LightSourceID10
)

LightSourceID1, LightSourceID2, ..., LightSourceID10
Les identifiants Game Maker des objets de source de lumière que vous souhaitez utiliser pour calculer la light map. Si vous mettez 0 pour chacun d'eux, Utilmate 3D utilisera toutes les sources de lumière qui font partie de la scène.

Si la fonction de calcul de light map intégrée ne satisfait pas vos attentes, ce n'est pas un problème. Ultimate 3D vous permet d'utiliser votre propre light map à la place. Vous la créer dans n'importe quelle logiciel d'édition d'image ou dans un programme tel que Earth Sculptor. De cette manière vous pouvez utiliser des lights maps avec des ombres précalculées ou avec toute autre méthode de calcul. S'il y a une forêt quelque part sur votre terrain vous pouvez assombrir votre light map à cet endroit, pour obtenir un sol ombragé. Des choses comme celle-ci ne peuvent être faites efficacement sans avoir la possibilité de définir vos propres lights maps. Donc, comme vous pouvez le voir, cette fonction est très puissante.

Cette fonction va définir une texture comme light map de votre terrain.

SetCustomTerrainLightMap(
TextureIndex
)

TextureIndex
L'indice de la texture, qui sera définie comme nouvelle light map pour le terrain. Si vous souhaitez revenir à la light map calculée automatiquement, vous pouvez passer un indice inutilisé à ce paramètre.

Déformer un terrain

Dans les jeux proposant des armes lourdes, vous n'irez nulle part sans déformer le terrain quelques fois. Si une bombe explose, un cratère doit être créé. Cela peut contribuer directement au game play, étant donné que le joueur pourra peut être se cacher dedans ensuite. Ultimate 3D offre une fonction vraiment très simple pour la déformation des terrains, qui donne le résultat souhaité dans la plupart des cas.

Cette fonction aggrandi ou rapetissi le terrain autour de la position donnée.

DeformTerrain(
DeformationCenterX, DeformationCenterY,
DeformationRadius,
RaiseLevel
)

DeformationCenterX, DeformationCenterY
L'origine de la déformation dans le niveau. C'est la position à laquelle la déformation sera la maximale.

DeformationRadius
Le rayon autour de l'origine de déformation, dans lequel le terrain sera déformé.

RaiseLevel
Le niveau de déformation du terrain, définissant de combien le terrain sera relevé (valeur positive) ou abaissé (valeur négative).

À l'origine de la déformation, le terrain est relevé ou abaissé par RaiseLevel, et à une distance supérieure ou égale à DeformationRadius le terrain ne change pas. Dans cet intervale une courbe cosinoïdale est utilisé pour obtenir une forme lisse. Le graphique suivant en illustre le principe.

Illustration not available

Cette méthode donne des cratère sphériques très sympas. Elle est suffisamment rapide pour être appellée pendant le gameplay, mais elle ne devrait pas être appellée à chaque step ou trop fréquemment. Remarquez que cette méthode ne peut abaisser les terrain en dessous de z et ne peut pas les élever au dessus de z+height. Si vous utilisez cette méthode, vous devriez être sûr qu'il y ait un peu de place avant que les limites ne soient atteintes.

Création de décalcomanie sur le terrain

Souvent il peut être utile de couvrir le terrain avec d'autres textures à certains endroits. Par exemple aux endroits où une explosion a eu lieue, une texture avec de la terre brûlée pourrait apparaître. Le texturage du terrain lui même ne peut être changé, mais il est possible de créer des textures superposées sur le terrain, qui le couvre parfaitement et utilisent la texture désirée. Ces superpositions sont nommées décalcomanies de terrains. Ultimate 3D permet la création automatiquie de décalcomanie pour les terrains. Les décalcomanies de terrains sont des objets de modèle et de fait ils requièrent leur propre objet Game Maker. Dans cet objet vous devrez définir quatres variables avant la création du décalcomanie:

terrain_object
L'ID de l'objet de terrain sur lequel vous souhaitez créer une décalcomanie.

center_x, center_y
Le position centrale de la décalcomanie dans le niveau.

size
La taille de la décalcomanie. Les décalcomanies de terrain sont toujours des carrés, à moins qu'elles aient besoins d'être recadrées lorsqu'elles sont au bord du terrain.

Après avoir défini ces variables, vous pouvez appeller CreateTerrainDecal(), afin de dé créer la décalcomanie. Comme d'usage il vous faut appeller Destroy() dans l'évènement destroy. Et Step() peut être appellé dans l'évènement step, mais d'usage ce n'est pas nécessaire étant donné que les décalcomanies de terrain sont statiques. Modifier les les variables de transformation n'est généralement pas souhaité. Les variables x, y, z, rotx, roty et rotz devrait être égales à zero et scalx, scaly et scalz devraient être égales à un. Sinon le terrain sera à la mauvaise place.

L'objet de modèle créé peut être modifié comme tout autre objet de modèle. Vous pouvez utiliser SetMaterialStageTexture(...) pour lui fournir une texture, vous pouvez utiliser SetModelMaterial(...) pour changer sa couleur et vous pouvez même modifier sa géométrie avec les fonctions de manipulation de modèles. L'objet a plusieurs niveaux de détails de façon à ce qu'il corresponde exactement à la forme du terrain, sans que le LoD actuel de cette partie du terrain n'y change quoi que ce soit. Cela fonctionne vraiment bien, mais il peut quand même encore arriver parfois que la décalcomanie soit couverte par le terrain. Dans ce cas vous pouvez un peu augmenter la valeur de la variable z et appeller Step(). Cela réglera le problème. C'est à vous de trouver la valeur parfaite pour z. Cela dépend du terrain et de la taille de la décalcomanie.

Création de height map et de map alpha

Pour la création de height maps et de maps alpha, je peux vous conseiller un programme très convivial, nommé Earth Sculptor. Son utilisation est très intuitive et il est disponible avec un manuel détaillé. Malheureusement la version non enregistrée est un peu limitée. Une fois que vous aurez dessiné un terrain dans Earth Sculptor vous pourrez le sauvegarder. Le programme sauvegardera ensuite une height map, une map alpha compressée, et une light map. La height map peut être utilisée par Ultimate 3D telle qu'elle. Vous pouvez aussi utiliser la light map en tant que light map personnalisée si vous le souhaitez. La map alpha peut aussi être utilisée si elle est exportée grâce au paramètre AlphaMapChannel de AddTerrainTexture(...).

D'autres programmes gratuits permettant la création de terrains sont Terragen, World Machine, Terranim8or et L3DT. Une explication sur comment exporter les heights maps depuis Terranim8or est disponible sur la communauté Ultimate 3D et un turorial sur l'utilisation de L3DT par Ruud v A peut être lu dans le Wiki d'Ultimate 3D.


Création d'effets de particules

Les effets de particules. Quelle superbe chose. Vous pouvez avoir des milliers de petits panneaux calculés interactivement et basés sur les données que vous voulez pour le comportement souhaité des particules. Le système d'effet de particules a complètement été repensé pour Ultimate 3D 2.0 et a été amélioré un petit peu dans Ultimate 3D 2.1. Il est réellement flexible. Tout d'abord, il y a de nombreux paramètres que vous pouvez modifier pour définir les propriétés de vos particules tels que l'origine, la vélocité, l'accélération, la taille et la couleur. Deuxièmement, vous pouvez ajouter de émetteurs de gravité qui fonctionne de manière réaliste. En fait, vous pourriez utiliser le système d'effets de particules avec les émétteurs de gravité pour faire un simulateur de système solaire :D. Pour finir, vous pouvez définir des modificateurs qui changent le comportement de vos particules lorsqu'elles sont dans une zone que vous avez défini. Cela peut être utilisé, par exemple, pour créer une room qui renvoie les particules lorsqu'elles frappent l'un des murs en changeant leur couleur. De plus, chacune des particules dans Ultimate 3D peut avoir une taille illimitée. Et utiliser les effets de particule est très simple, donc ne manquez pas le chapitre qui suit ;).

Création d'un système de particules et configuration de ses propriétés basiques

La création d'un système de particules est certainement la chose la plus simple que vous pouvez imaginer. Vous n'avez pas à définir de variables, tout ce que vous avez à faire est de mettre un appel à CreateParticleSystem() dans l'evènement create d'un objet. Mais il est évident que ce ne peut être tout. Vous devez dire à Ultimate 3D à quoi vous voulez que le système de particules ressemble. Pour cette raison, il vous faut faire appel à un certain nombre d'autres fonctions après l'appel de CreateParticleSystem(). Il y a aussi deux variables que vous pouvez définir à n'importe quel moment. La première est texture, qui définit l'indice de la texture à utiliser. La deuxième est optionelle et est nommée aspect_ratio. Vous pouvez l'utiliser si vous ne voulez pas de particules carrés. La hauteur des particules est toujours égale à leur taille, la largeur est la taille multipliée par aspect_ratio. Les autres options sont définies à l'aide des fonctions. Les deux premières fonctions définissent le nombre de particules qui seront créés à chaque step, et le temps de vie des particules. Les fonctions qui suivront auront toujours quelques paramètres communs, un avec une valeur minimale et un avec une valeur maximale. Ultimate 3D choisira une valeur aléatoire entre la valeur minimale et maximale lorsqu'il aura besoin de cette valeur. De cette façon tout ira de façon un peu aléatoire et chaque particule aura son propre comportement. Voici les deux fonctions:

Cette fonction définit le nombre de particules que le système de particules créera.

SetParticleCount(
MinimumParticleCount,
MaximumParticleCount,
MaximumTotalParticleCount
)

MinimumParticleCount, MaximumParticleCount
Le nombre de particules qui doivent être créées à chaque step.

MaximumTotalParticleCount
Le nombre maximum de particules qui peuvent exister en même temps. Si le nombre total de particules approche cette valeur, Ultimate 3D stoppera la création de particules jusqu'à ce que suffisamment de particules aient dépassées leur durée de vie. Habituellement une bonne valeur pour ce paramètre est:

( MaximumLifetime+MinimumLifetime)/2*(MaximumParticleCount+MinimumParticleCount)/2

Cette fonction définit la durée de vie des particules du système de particules qui appelle la fonction. La durée de vie (en step) est le temps passé depuis leur création jusqu'à ce qu'elles soient détruites.

SetParticleLifetime(
MinimumParticleLifetime,
MaximumParticleLifetime
)

MinimumParticleLifetime, MaximumParticleLifetime
La durée de vie des particules qui seront créées.

Maintenant, cela devient un petit peu plus compliqué. Vous allez définir où les particules seront créées. Si vous le souhaitez, vous pouvez simplement passer cette étape. Dans ce cas, toutes les particules seront créées à la même position. Mais cela serait ennuyeux à terme. Pour définir l'origine, vous devez donner une transformation et une valeur réelle. Imaginez que vous avez un modèle de sphère avec un rayon de un. Si vous appliquez la transformation que vous entrez dans la fonction suivante de ce modèle de sphère, le modèle vous montrera les zones où les particules peuvent être créees. La deuxième valeur réelle sera utilisée comme une mise à l'échelle pour une deuxième sphère qui utilise la même transformation. C'est l'autre bordure qui limite la zone où les particules seront créées. Le schéma suivant illustre ce principe.

Illustration not available

La première sphère sera transformée en utilisant la transformation que vous entrerez dans la fonction suivante. Ensuite la seconde sphère sera transformée de la même façon et scalée par le rayon donné de deuxième sphère. Ensuite la zone entre ces deux sphères est l'origine des particules. Voici la fonction pour définir ces propriétés:

Cette fonction définit l'origine des particules. La position de l'origine n'est pas définie par cette fonction étant donné que cela doit être modifié très souvent. Pour cette raison vous devez définir les variables x, y et z pour chaque système de particules.

SetParticleOrigin(
SecondSphereRadius,
RotationX, RotationY, RotationZ,
ScalingX, ScalingY, ScalingZ
)

SecondSphereRadius
Le rayon de la deuxième sphère qui sert de deuxième bordure pour délimiter l'origine des particules. Si vous mettez 0 pour ce paramètre, l'origine des particules sera une sphère exacte.

RotationX, RotationY, RotationZ
La rotation à utiliser pour la transformation des deux sphères. Habituellement vous n'avez pas besoin de ces paramètres, étant donné que faire pivoter l'origine n'a de sens que si vous n'entrez pas la même valeurs pour les trois paramètres de scaling (mise à l'échelle).

ScalingX, ScalingY, ScalingZ
Le scaling qui doit être appliqué à ces deux sphères.

Les deux prochaines fonctions définissent la vélocité des particules et leur accelération (l'accelération est la modification de la vélocité à chaque step). C'est très simple. La seule chose que vous devez garder à l'esprit à propos de ces fonctions est que la longitude doit être située entre -90 et 90 parce qu'autrement la méthode qu'Ultimate 3D utilise pour réaliser une diffusion régulière des particules ne fonctionnera pas correctement.

Cette fonction définit la vélocité initiale des particules qui sont créées par le système de particules.

SetParticleVelocity(
MinLongitude, MinLatitude,
MinVelocity,
MaxLongitude, MaxLatitude,
MaxVelocity
)

MinLongitude, MinLatitude, MaxLongitude, MaxLatitude
La direction des mouvements donnée en degrés. Les valeurs de longitude doivent être entre -90 et 90.

MinVelocity, MaxVelocity
La vitesse du mouvement par step.

Cette fonction définit l'accélération initiale des particules qui sont créées par le système de particules.

SetParticleAcceleration(
MinLongitude, MinLatitude,
MinAcceleration,
MaxLongitude, MaxLatitude,
MaxAcceleration
)

MinLongitude, MinLatitude, MaxLongitude, MaxLatitude
La direction de changement du mouvement en degrés. Les valeurs de longitude doivent être entre -90 et 90.

MinAcceleration, MaxAcceleration
La force de changement du mouvement à chaque step.

Ensuite vous pouvez définir quelque chose qui se nomme centre de répulsion initial. Il arrive très souvent que toutes les particules sont sensées s'éloigner de l'origine du système de particule. Un exemple pour cela serait un système de particule pour les explosions. C'est pour cela que vous avez besoin d'un centre de répulsion initial. Lorsqu'une nouvelle particule est créée le facteur du centre de répulsion initial est multiplié par la différence de position de la particule et du système de particule. Puis le résultat est ajouté à la vélocité initiale de la particule ( VelociteInitiale += (PositionParticule - PositionSystemeParticule)*FacteurRepulsion). Le facteur de répulsion est défini individuellement pour chaque axe. Voici la fonction:

Elle définit le facteur du centre de répulsion initial:

SetInitialCenterRepulsion(
RepulsionFactorX, RepulsionFactorY, RepulsionFactorZ
)

RepulsionFactorX, RepulsionFactorY, RepulsionFactorZ
Le facteur de répulsion le long de chaque axe. Pour obtenir une description de ce qu'est le facteur de répulsion, jetez un oeil au texte ci-dessus.

Ensuite vous pouvez définir la couleur et la taille des particules. Pour chacune d'elle vous pouvez définir une fourchette de valeurs initiales possibles et une fourchette de valeurs finales possibles. Les valeurs finales seront utilisées par les particules à la fin de leur durée de vie. Voici les fonctions disponibles pour définir ces valeurs:

Cette fonction définit un éventail de valeurs de couleurs initiales possibles pour les particules.

SetInitialParticleColor(
MinimumR, MinimumG, MinimumB, MinimumA,
MaximumR, MaximumG, MaximumB, MaximumA
)

MinimumR, MinimumG, MinimumB, MinimumA
Les valeurs de couleurs minimales pour les couleurs initiales des particules. Ces valeurs doivent être dans la fourchette allant de 0 à 255. MinimumA définit le minimum de l'opacité (0 pas d'opacité (totalement transparent), 255 pour une couleur complètement opaque).

MaximumR, MaximumG, MaximumB, MaximumA
La valeur de couleur maximale pour la couleur initiale de la particule. Ces valeurs doivent également rester dans la fourchette allant de 0 à 255.

Cette fonction définit un éventail de valeurs de couleurs finales possibles pour les particules.

SetFinalParticleColor(
MinimumR, MinimumG, MinimumB, MinimumA,
MaximumR, MaximumG, MaximumB, MaximumA
)

MinimumR, MinimumG, MinimumB, MinimumA, MaximumR, MaximumG, MaximumB, MaximumA
Ces paramètres ont la même signification que pour SetInitialParticleColor(...) mais cette fois-ci, ils se réfèrent à la couleur finale des particules.

Cette fonction définit un éventail de tailles de particules initiales et finales possibles. Les valeurs de tailles sont données dans l'espace du monde, elles sont donc interprétées de la même façon que les paramètres que vous pouvez définir pour les billboards.

SetParticleSize(
MinInitialSize, MaxInitialSize,
MinFinalSize, MaxFinalSize
)

MinInitialSize, MaxInitialSize
L'éventail de tailles initiales possibles pour les particules.

MinFinalSize, MaxFinalSize
L'éventail de tailles possibles que les particules peuvent avoir à la fin de leur durée de vie.

Enfin vous pouvez déterminer une rotation pour les particules. Faire pivoter les particules rend le système de particule un peu plus chaotique et cela s'ajoute surperbement à leur apparence naturelle.

Cette fonction détermine la rotation initiale des particules et leur vitesse de rotation.

SetParticleRotation(
MinInitialRotation, MaxInitialRotation,
MinRotationSpeed, MaxRotationSpeed
)

MinInitialRotation, MaxInitialRotation
L'éventail possible des rotations initiales pour les particules en degrés. Une rotation de zero, signifie que le haut de la texture est en haut de la particule. Des rotations plus grandes font pivoter les particules sur l'écran dans le sens des aiguilles d'une montre.

MinRotationSpeed, MaxRotationSpeed
L'éventail possible des vitesses de rotation pour les particules en degrés. La vitesse de rotation sera ajoutée à l'angle de rotation de la particule une fois par step.

Maintenant que vous savez comment définir les propriétés d'un système de particules, il est temps de le faire vivre. Pour ce faire vous devez mettre un appel de fonction dans l'évènement step de l'objet que vous avez créé pour le système de particule, mais cette fois ci ce n'est pas Step(). Ce choix est dû au fait que la fonction step des systèmes de particules prend un paramètre et ne requiert pas d'être appelé à chaque step, parce qu'elle prend un bon petit temps de calcul selon le nombre de particules, d'émetteurs de gravité et de modificateurs. Cela n'a pas de sens de réaliser un step pour un système de particules complexe s'il n'est pas actuellement visible. Voici la fonction:

Cette fonction effectue un step de la longueur donné pour le système de particules qui appelle la fonction.

ParticleSystemStep(
StepLength
)

StepLength
La longueur du step que vous voulez effectuer. On pourrait également nommer cela un facteur de temps. Si vous mettez un ici, un step sera effectué à chaque appel de cette fonction. Si vous mettez 0.5, tout fonctionnera à la moitié de la vitesse, et si vous mettez 2 tout ira deux fois plus vite. Notez qu'un facteur de temps élevé peut donner des calculs imprécis, spécialement lors de l'usage de modificateurs et d'émetteurs de gravité.

Enfin, il vous faudra faire appel à Destroy() dans l'évènement destroy de l'objet de système de particule, sinon le système de particule sera toujours visible après la destruction de l'objet avec lequel il était associé.

Si vous voulez obtenir des effets de particules très brillants avec un effet volumétrique vous voulez peut être utiliser l'additive blending pour les systèmes d'effets de particules. L'additive blending signife que chaque particule s'ajoute à la couleur de l'écran, à la place de la couvrir. Sans additive blending cinq particules les unes sur les autres, apparaissent exactement de la même façon qu'une particule seule, étant donné que la particule devant couvre les quatres autres. Avec l'additive blending les couleurs des particules sont ajoutées ensembles, menant à un spot très brillant (plus brillant qu'une seule particule). Activer l'additive blending est facile.

Cette fonction active ou désactive l'additive blending pour les objets qui l'appelle.

SwitchAdditiveBlending(
UseAdditiveBlending
)

UseAdditiveBlending
Si ce paramètre est true, l'additive blending sera utilisé pour cet objet, s'il est false, l'additive blending de sera pas utilisé.

Cette fonction ne fonctionne pas seulement pour les systèmes de particule, mais aussi pour les objets de modèles, les objets de terrains et les objets primitifs. Vous ne devriez l'utiliser seulement pour ces types d'objets lorsqu'ils sont partiellement transparents. Sinon le résultat pourrait ne pas être celui escompté du fait du z-buffering.

Emetteurs de gravité

Les émetteurs de gravité sont vraiment cools. Ils peuvent faire en sorte que les particules se déplacent sur des chemins qui sont assez fréquents mais jamais les mêmes. Les émetteurs de gravité peuvent aussi émettre de la gravité "négative", ainsi, elles font s'éloigner les particules, ce qui est utile pour de nombreux effets différents. Les émetteurs de gravité utilisent la physique, ainsi même si vous ne connaissez rien de la théorie de Newton sur la gravité, cela donnera une sensation de comportement naturel. Remarquez que les émetteurs de gravité ne sont pas conçus pour être utilisés pour faire de la gravité qui vient toujours de la même direction. Cela peut être réalisé plus facilement en définissant une accelération constante.

Utiliser les émetteurs de gravité est très facile. Il vous suffit de définir leur position dans l'espace du monde et l'intensité de la gravité qu'ils doivent émettre. Le reste se fait automatiquement. Les émetteurs de gravité doivent être utilisés prudemment étant donné qu'ils seront appliqués à chaque particule, ce qui prend un peu de temps de calcul. Ultimate 3D définit une limite de 32 émetteurs de gravité par système de particules. Voici la fonction que vous devez utiliser pour créer les émetteurs de gravité:

Cette fonction ajoute un nouvel émetteur de gravité au système de particules qui appelle la fonction et retourne son ID, qui peut être utilisé pour le modifier après coup.

AddGravityEmitter(
PositionX, PositionY, PositionZ,
GravityEmitterMass
)

PositionX, PositionY, PositionZ
La position de l'émetteur de gravité dans l'espace.

GravityEmitterMass
La masse de l'émetteur de gravité. Une bonne valeur pour ce paramètre dépend de la distance de votre système de particules, vous devriez en trouver une en faisant divers essais.

Cette fonction peut être utilisée pour modifier ou supprimer un émetteur de gravité que vous avez créé auparavant.

SetGravityEmitter(
GravityEmitterID,
PositionX, PositionY, PositionZ,
GravityEmitterMass
)

GravityEmitterID
L'ID de l'émetteur de gravité que vous souhaitez modifier. C'est la valeur retournée par AddGravityEmitter(...) lors de la création de l'émetteur de gravité.

PositionX, PositionY, PositionZ
La nouvelle position de l'émetteur de gravité dans l'espace.

GravityEmitterMass
La nouvelle masse de l'émetteur de gravité. Si vous entrez 0 pour ce paramètre l'émetteur de gravité sera supprimé.

Modificateur de particules

Souvent les particules doivent changer lorsqu'elles arrivent dans une région particulière. Par exemple, si vous faites un système de particules pour ajouter de l'eau pulvérisée à une chute d'eau, les particules doivent remonter vers le haut après avoir touché la surface de l'eau. Des choses comme celle là peuvent être faites facilements à l'aide des modificateurs de particules. Les modificateurs de particules changent les particules lorsqu'elles sont dans la région que vous avez définie. Ils peuvent changer n'importe quelle propriété des particules (durée de vie, vélocité, accelération, couleur et taille). Cela rend le système de particules vraiment flexible et vivant.

Définir les propriétés des modificateurs de particules équivaut à peu près, à définir les propriétés sur système de particules. Tout est fait au travers d'appels de fonctions et beaucoup de propriétés sont similaires à celles que vous pouvez définir pour le système de particules. Mais avant de définir ces propriétés pour les modificateurs de particules, vous devez les créer. Pour ce faire, il vous faut appeller AddModifier(). Cette fonction retournera l'ID du modificateur qui devra être sauvegardé dans une variable, pour être à même de définir n'importe quelle propriété du modificateur. Voici toutes les fonctions dont vous avez besoin pour définir les propriétés du modificateur:

Cette fonction définit la zone qui est affectée par le modificateur de particules. Cela fonctionne de la même façon que la définition de l'origine des particules (voir SetParticleOrigin(...) ). Lorsqu'une particule est dans la zone, elle est modifiée:

SetModifierEffectedArea(
ModifierID,
SecondSphereRadius,
PositionX, PositionY, PositionZ,
RotationX, RotationY, RotationZ,
ScalingX, ScalingY, ScalingZ
)

ModifierID
L'ID du modificateur que vous voulez modifier.

SecondSphereRadius
Le rayon de la seconde sphère qui est la deuxième frontière de la zone affectée.

PositionX/Y/Z, RotationX/Y/Z, ScalingX/Y/Z
La transformation des deux sphères qui sont et qui servent de frontières à la zone affectée.

Cette fonction définit un modificateur qui changera la durée de vie d'une particule lorsqu'elle entre dans la zone de ce modificateur. Donc si vous le définissiez à un, une particule ne moura jamais dans la zone affectée, parce que sa durée de vie sera incrémentée (et décrémentée) par un à chaque step.

SetModifierLifetimeChange(
ModifierID,
LifetimeChange
)

ModifierID
L'ID du modificateur que vous souhaitez modifier.

LifetimeChange
Le changement de la durée de vie par step.

Les deux fonctions suivantes définissent comment le modificateur change la vélocité et l'accélération des particules qui sont dans sa zone affectée. Les deux prennent un facteur et une valeur destinée à être ajoutée. Lorsqu'une particule est dans la zone affectée, sa vélocité et son accelération sera changée en utilisant la formule suivante: Valeur=Valeur*Facteur+Somme. Dans cette formule "Valeur" se réfère à la vélocité ou à l'accelération. Donc, en définissant un facteur de vélocité qui est un peu inférieur à 1, vous pourriez implémenter une friction. En définissant un facteur de vélocité négatif vous pouvez renvoyer les particules. Voici les fonctions pour définir ces propriétés:

Cette fonction définit le modificateur de vélocité des particules dans la zone affectée:

SetModifierVelocityChange(
ModifierID,
VelocityFactorX, VelocityFactorY, VelocityFactorZ,
VelocitySummandX, VelocitySummandY, VelocitySummandZ
)

ModifierID
L'ID du modificateur que vous souhaitez modifier.

VelocityFactorX, VelocityFactorY, VelocityFactorZ
Le facteur par lequel la vélocité de la particule doit être multipliée à chaque step lorsqu'elle est dans la zone du modificateur. Il peut être défini individuellement pour chaque axe.

VelocitySummandX, VelocitySummandY, VelocitySummandZ
La valeur qui sera ajoutée à la vélocité de la particule à chaque step lorsqu'il est dans la zone du modificateur. Elle peut être définie individuellement pour chaque axe.

Cette fonction définit le modificateur d'accélération des particules dans la zone affectée:

SetModifierAccelerationChange(
ModifierID,
AccelerationFactorX, AccelerationFactorY, AccelerationFactorZ,
AccelerationSummandX, AccelerationSummandY, AccelerationSummandZ
)

ModifierID
L'ID du modificateur que vous souhaitez modifier.

AccelerationFactorX, AccelerationFactorY, AccelerationFactorZ
Le facteur qui sera multiplié par l'accélération de chaque particule à chaque step, lorsque ces particules sont dans la zone affectée au modificateur. Ce facteur peut être défini individuellement pour chaque axe.

AccelerationSummandX, AccelerationSummandY, AccelerationSummandZ
La valeur qui sera ajouté à l'accélération de chaque particule à chaque step, lorsque ces particules sont dans la zone affectée au modificateur. Elle peut être définie individuellement pour chaque axe.

Ensuite vous pouvez définir une nouvelle couleur finale et une nouvelle taille finale pour les particules qui entrent dans la zone du modificateur de particules. Etant donné que vous modifiez seulement les nouvelles valeurs finales, il n'y aura pas de changement rapide. À la place, il y aura une interpolation adoucie. Voici les fonctions:

Cette fonction établit un éventail de couleurs possibles qui peuvent être utilisées en tant que nouvelles couleurs finales pour les particules qui entrent dans la zone du modificateur de particules donné.

SetModifierNewFinalColor(
ModifierID,
MinimumR, MinimumG, MinimumB, MinimumA,
MaximumR, MaximumG, MaximumB, MaximumA
)

ModifierID
L'ID du modificateur que vous souhaitez modifier.

MinimumR/G/B/A, MaximumR/G/B/A
L'éventail de couleurs possibles qui peuvent être définies en tant que nouvelle couleur finale pour les particules qui entrent dans la zone du modificateur. Les valeurs pour ces paramètres doivent être dans l'éventail allant de 0 à 255.

Cette fonction modifie la taille finale des particules pénétrant dans la zone du modificateur.

SetModifierNewFinalSize(
ModifierID,
MinimumNewFinalSize, MaximumNewFinalSize
)

ModifierID
L'ID du modificateur que vous souhaitez modifier.

MinimumNewFinalSize, MaximumNewFinalSize
L'éventail des nouvelles tailles possibles données.


Multiples cameras

Plusieurs Caméras

Par défaut vous avez un seul objet camera dans votre jeu. Mais il y a des situations particulières dans lesquels plus d'objets de caméra sont requis. Par exemple vous pouvez vouloir faire un jeu multijoueur avec écran splitté. Ou vous voulez peut être ajouter des caméra de surveillance à votre jeu, qui prenne des photos qui peuvent être affichées sur des moniteurs. Aussi, vous voulez peut être ajouter une reflexion réelle du monde à vos plans d'eau ou autres objets. Pour toutes ces choses vous avez besoin de caméras supplémentaires.

Dans Ultimate 3D vous pouvez utiliser jusqu'à 32 objets caméras simultanément. Les utiliser est très simple. En principe, tout les objets caméras fonctionne de la même manière que la première. La seule différence est qu'il vous faut définir la destination du rendu des autres objets de caméras. Il y a deux type de destinations. Vous pouvez afficher le rendu, soit sur une partie de l'écran de l'utilisateur, soit sur une texture spéciale. Ces textures spéciales peuvent être appliquées aux objets, de la même façon que les textures standards. Elles peuvent aussi être affichées à l'écran à l'aide de DrawTex(...) ou DrawTexEx(...).

Ajouter une nouvelle caméra à la scène

Chaque objet de caméra a les mêmes variables que le premier objet caméra, qui sont les suivantes: x, y, z, height, rotx, roty, rotz, view, min_range, max_range, falling, falling_speed, follow, perspective, et distance. Pour obtenir plus d'informations sur ces variables, jetez un oeil au tutorial  Commençons par le début . Il y a aussi un ensemble de nouvelles variables que vous devez connaîtres. Le premier objet caméra les possède également, mais si vous ne le définissez pas manuellement elles sont définies avec des valeurs par défaut. Donc voici les nouvelles variables:

number
L'indice de cet objet caméra. La première caméra a 0 pour indice (il doit toujours y avoir une caméra avec number=0). La valeur valide la plus haute est 31. Vous ne devez jamais avoir deux objets caméra avec le même indice, au même moment.

v_left, v_top, v_width, v_height
La position et les dimensions de la vue que vous voulez utiliser pour la caméra. Ce sera la zone où le rendu de la caméra sera affiché. Remarquez que ces valeurs doivent être relatives à la coin haut gauche de la première view Game Maker (NdT: view[0]).

actual_aspect
Si vous n'avez pas besoin de cette variable, vous pouvez simplement l'ignorer. Par défaut, Ultimate 3D calculera l'aspect de la vue en divisant v_width/v_height. Mais étant donné que les textures de destination doivent avoir des dimensions faites de puissances de deux, il apparaîtra que ce quotient n'est pas l'aspect que vous souhaitez réellement. Dans ce cas vous pouvez utiliser cette variable pour en définir un autre.

render_target
Si vous ne voulez pas afficher le rendu dans une texture, vous pouvez simplement ignorer cette variable. Sinon, vous devrez la définir à l'indice de la texture cible de rendu (render target texture) que vous voulez. Les textures cible de rendu peuvent être créées à l'aide de la fonction CreateTextureRenderTarget(...) (voir plus bas).

Chaque objet caméra doit appeler MoveCamera() dans son évènement step et Destroy() dans son évènement destroy. MoveCamera() transmettra n'importe quel changement des variables à Ultimate 3D. Si la caméra a une texture cible de rendu de définie, vous devrez appeller RenderToTexture() pour lui faire calculer son rendu. Bien entendu, vous n'êtes pas contraint de faire cet appel à chaque step. Si les objets utilisants la textures ne sont pas visible, ce serait un gigantesque gaspillage de temps de calcul d'appeler cette fonction. Aussi, les caméras de sécurités, n'ont pas besoin de mettre à jour leur image à chaque step. Dix fois par seconde devrait suffire. De cette façon vous pouvez rendre votre programme beaucoup plus rapide.

Plans de détourage et vues orthogonales

Parfois, certaines caméras n'ont pas besoin de rendre tout. Il y a deux possibilité de programmer cela. La plus facile est l'utilisation des fonctions de culling avancées. Avec ces fonctions vous pouvez définir quelles caméras rendront quels objets. Mais quelque fois il arrive que ce ne soit pas suffisamment précis. Par exemple, lors de la programmation d'effets pour le reflechissement de l'eau, vous avez besoin d'une caméra qui ne rend que le monde au dessus de la surface de l'eau. Pour ce faire, vous pouvez utiliser les plans de détourage. Vous définissez simplement un plan arbitraire. Tout ce qui est au dessus du plan sera calculé et rendu, et le reste sera invisible. Voici la fonction, vous permettant de définir un plan de détourage pour la caméra qui l'appelle.

SetClippingPlane(
ClippingPlaneIndex,
PlaneOriginX, PlaneOriginY, PlaneOriginZ,
PlaneOrientationLongitude, PlaneOrientationLatitude
)

ClippingPlaneIndex
Il peut y avoir jusqu'à six plans de détourage par caméra, selon la carte graphique. GetClippingPlaneSupport() retourne le nombre de plans de détourage supportés. Au travers de ce paramètre vous définissez quel plan de détourage vous souhaitez. Cela peut être une valeur entre 0 et 5.

PlaneOriginX/Y/Z
C'est un point arbitraire du plan que vous souhaitez définir comme plan de détourage.

PlaneOrientationLongitude, PlaneOrientationLatitude
Ces deux variablesn définissent l'orientation du plan (la direction vers laquelle le plan se dresse).

Une autre fonctionnalité petite mais utile d'Ultimate 3D est que le moteur permet l'utilisation de caméras avec une projection orthogonale. Cette dernière est principalement utilisée pour les utilitaires tels que les éditeurs de niveaux ou les programmes de modélisation. Dans une vue orthogonale, les objets qui sont loin derrière n'ont pas l'air d'être plus petits (ni plus grands). Il n'y a pas de ligne d'horizon. Vous n'avez pas d'angle donnant le champ de vision, parce que les limites de la zone visible sont toujours parallèles. À la place vous donnez une zone rectangulaire qui devra être visible en donnant une hauteur et une largeur. Les caméras orthogonales ne peuvent exister dans la réalité, mais pour différents types d'éditeurs elles sont vraiment utiles, parce qu'elles réduisent l'image vue à deux axes.

Cette fonction active la projection orthogonale pour la caméra qui l'appelle.

SetOrtho(
UseOrthogonalProjection,
SeenAreaWidth, SeenAreaHeight
)

UseOrthogonalProjection
Si vous passez true pour ce paramètre, Ultimate 3D activera la projection orthogonale pour cette caméra, si vous passez autre chose elle sera désactivée.

SeenAreaWidth, SeenAreaHeight
La largeur et la hauteur de la zone qui devra être visible. Ces valeurs sont utilisées au lieu de la valeur de la variable view.

Remarquez que les caméras orthogonales seront incapables de voir quelque chose se trouvant derrière elles, tout comme les caméras habituelle. Pour cette raison il vous faut l'éloigner suffisamment de la scène.

Création de textures (cible) de rendu et calculer des rendus pour les cube maps

Calculer des rendus vers une texture est une technique s'appliquant à un grand éventail de situation. Vous pouvez calculer le rendu d'une image vers une texture et l'utiliser comme n'importe quelle autre texture. Aussi, les fonctions de calcul du rendu vers les textures sont nécessaires pour faire des post screen shaders, qui peuvent être utilisées pour des effets tels que le HDR, motion blur ou les effets qui donnent l'aspect d'un très vieux film à votre jeu. Utiliser un post screen shader signifie que vous calculez le rendu de la scène vers une texture, au lieu d'afficher le rendu directement à l'écran. Ensuite vous calculez le rendu de la texture qui contient une image de la scène à l'écran, en y appliquant quelques effets de shaders. Une prochaine version d'Ultimate 3D rendra l'utilisation des post screen shaders un peu plus simple.

Etant donné que vous savez déjà comment définir une texture comme une texture cible de rendu pour une caméra, la seule chose que vous devez apprendre maintenant est comment créer une texture cible de rendu. Pour ce faire vous n'aurez besoin que d'une simple fonction:

Cette fonction crée une texture (cible) de rendu avec les dimensions données et l'associe avec l'indice donné.

CreateTextureRenderTarget(
TextureIndex,
RequestedWidth, RequestedHeight
)

TextureIndex
L'indice qui sera associé à la texture. Cela peut être une valeur entière tout à fait arbitraire dans la plage allant de 0 à 998.

RequestedWidth, RequestedHeight
Les dimensions souhaitées pour la texture de rendu. Comme beaucoup d'autre type de texture, la plupart des periphériques graphiques ne supportent pas les textures de rendu qui ne sont pas faites de puissances de deux. Certains périphériques graphiques très anciens ne supportent que les textures carrées!

Il est aussi possible de calculer le rendu ver les textures cubiques. De cette façon vous pouvez créer des effets de reflets extrêmement réalistes. Vous calculez le rendu d'une image de l'environnement vers une texture cubique et vous utilisez ensuite l'environment mapping cubique pour appliquer cette image de l'environnement sur un objet. C'est la technique parfaite pour faire des réflections, et ce n'est pas difficile à utiliser. La première étape est la création d'une texture cubique qui peut être utilisée comme cible de rendu. Pour ce faire il vous suffit d'appeler une fonction:

Cette fonction crée une texture cubique avec les dimensions données qui peut être utilisée comme cible de rendu et l'associe avec l'indice donné.

CreateCubeTextureRenderTarget(
TextureIndex,
RequestedEdgeLength
)

TextureIndex
L'indice qui sera associé à la texture. Cela peut être une valeur entière, arbitraire, entre 0 et 998.

RequestedEdgeLength
La longueur des côtés de la texture cubique que vous souhaitez créer. La texture cubique sera faite de six textures 2D avec une taille de RequestedEdgeLength * RequestedEdgeLength. Vous devriez entrer une puissance de deux pour ce paramètre étant donné que la plupart des périphériques graphiques ne supportent pas d'autres tailles de texture.

Calculer le rendu vers des textures cubiques n'est pas fait au moyen d'un objet caméra. C'est bien plus facile. Etant donné que toutes les directions sont représentées par une texture cubique, il n'est pas nécessaire de donner une direction à la vue ou encore un angle. La seule information qui joue est la position, et éventuellement une couleur avec une valeur alpha. Une fois que le rendu de la scène toute entière a été calculé vers une texture cubique, des carrés de vertex avec cette couleur et cette transparence seront affiché sur la texture finie. Vous devriez toujours entrer une valeur alpha plus petite que 255, sinon le processus de calcul du rendu n'aura aucun sens étant donné que les résultats ne seront pas visibles. Vous devriez d'usage mettre quelque chose comme r=g=b=0 et a=128. Voici la fonction:

Cette fonction calcule le rendu de la scène telle qu'elle est vue depuis la position donnée vers la texture cubique cible de rendu.

RenderToCubeTexture(
CubeTextureIndex,
PositionX, PositionY, PositionZ,
ColorR, ColorG, ColorB, ColorA
RoomIndex1, RoomIndex2, ..., RoomIndex8
)

CubeTextureIndex
L'indice de la texture cubique cible de rendu vers laquelle vous voulez calculer le rendu. Cela doit être l'indice d'une texture cubique de rendu qui a été créée à l'aide de CreateCubeTextureRenderTarget(...), autrement cette fonction échouera.

PositionX, PositionY, PositionZ
La position depuis laquelle la scène doit être rendue.

ColorR, ColorG, ColorB, ColorA
La couleur des carrés de vertex qui devront être rendu sur la texture cubique après avoir calculé le rendu de la scène. Les valeurs pour ces paramètres doivent être entre 0 et 255. Si vous ne souhaitez pas que ces carrés de vertex soient affichés, définissez seulement 0 pour ColorA. Passer 255 pour ColorA n'a aucun sens, parce que vous ne verriez pas la scène.

RoomIndex1, RoomIndex2, ..., RoomIndex8
Les indices des pièces qui devront être rendues. Pour plus d'informatiions sur les indices des pièces, jetez un oeil aux fonctions de culling avancées. Room 0 sera toujours rendue, quelque soit les valeurs que vous passez pour ce paramètre.

Dessiner des graphiques 2D en textures

J'aurai appeller ce sous-chapitre "Dessiner des textures et des textures sur les textures" si cela n'avait pas été aussi lourd. Mais c'est seulement ce que vous pourrez faire une fois que vous l'aurez lu et compris. Imaginez que vous ayez un panneau de contrôle dans votre jeu sur lequel différents réglages sont affichés. Il vous faut donc afficher des graphismes 2D sur une texture pouvant être utilisée comme texture pour le moniteur. C'est tout juste l'usage auquel est destiné cette fonctionnalité. Vous connaissez déjà les fonctions qui peuvent être utilisées pour dessinner des textures et du texte sur les textures: DrawTexEx(...) et DrawText(...). Il n'y a qu'un seul paramètre additionnel dont je ne vous ai pas parlé dans la section débutant pour éviter de vous troubler. Voici donc la syntaxe complète de ces deux fonctions.

DrawTexEx(
TextureIndex, Left,Top, Width,Height, X,Y, ScalingX,ScalingY, Rotation, R,G,B,A,
CameraIndex
)

CameraIndex
L'indice de la caméra de la cible de rendu sur laquelle vou souhaitez dessiner la texture.

DrawText(
FontIndex, X, Y, Text,
CameraIndex
)

CameraIndex
Voir ci-dessus.

Notez que la texture ne sera pas affichée sur la texture cible de rendu avant que l'objet de caméra correspondant n'appelle RenderToTexture().


Fonctions mathématiques

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.

Fonctions de calcul des vecteurs et des matrices

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.

Cette fonction retourne le nombre d'occurence du mesh donné dans l'objet de modèle par lequel la fonction est appellée.

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.

Cette fonction donne la transformation actuelle de l'espace des meshs à l'espace du niveau de l'occurence donné du mesh spécifié:

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
)

Cette fonction crée une matrice d'une transformation complète, qui effectue une mise à l'échelle, une translation (déplacement) et une rotation:

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.

Cette fonction inverse une matrice. L'inverse d'une matrice est une matrice qui fait exactement la transformation opposée à la matrice originale. Donc si la matrice originale est une matrice d'espace A vers B, l'inverse de cette matrice sera une matrice d'espace B à A. Si vous transformer une matrice par son inverse, la matrice résultante sera une matrice qui ne fait rien du tout (la matrice d'identité). Cela est dû au fait que si vous allez d'un espace A à un espace B et ensuite d'un espace B vers un espace A, le résultat sera une transformation d'un espace A vers un espace A, ce qui n'est pas du tout une transformation.

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.

La fonction qui suit peut être utilisée pour récupérer les entrées d'une matrice. Vous devez savoir comment fonctionnent les matrices de transformation pour être à même d'avoir une utilité pour cette fonction. L'usage le plus commun de cette fonction, consiste à s'en servir pour passer les matrices aux vertex shaders.

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.

Une autre façon de créer des vecteurs, est de les créer à partir d'une longitude et d'une lattitude. Pour ce faire vous pouvez utiliser la fonction suivante:

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.

Souvent il vous est nécessaire de calculer la différence entre deux vecteurs. Cela peut se faire à l'aide de la fonction suivante.

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.

L'autre opération mathématique de base des vecteurs est de les multiplier par un scalaire. Si vous avez un vecteur v = (x | y | z) et un scalaire (ou valeur flottante) k, k*v sera égal à (k*x | k*y | k*z). Cette multiplication scalaire est faite à l'aide de la fonction suivante:

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.

Souvent la longueur d'un vecteur est nécessaire. La longueur d'un vecteur v = (x [ y [ z), auquel on se réfère par |v|, est définie par sqrt(x*x+y*y+z*z), il s'agit donc de la distance de l'origine vers le vecteur (vu comme un vecteur de position). La fonction suivante peut être utilisée pour obtenir la longueur d'un vecteur. La longueur est sa valeur de retour.

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.

Cette fonction calcule l'angle de longitude, qui décrit correctement le vecteur donné et le retourne.

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.

La fonction suivante transforme le vecteur donné par la matrice spécifiée. Si le vecteur est donné dans l'espace 1 et la matrice décrit une transformation de l'espace A vers B, le vecteur résultant sera le même vecteur mais dans l'espace B.

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.

Cette fonction calcule la translation (déplacement) d'une matrice de transformation donnée et la donne en sortie sous forme de vecteur.

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).

Cette fonction calcule la mise à l'échelle de matrice de transformation donnée et la donne sous forme de vecteur en sortie.

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.

Cette fonction calcule la rotation de la matrice de transformation donnée et la donne en sortie sous forme de vecteur.

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.

Cette fonction calcule un vecteur de direction, qui décrit le rayon commençant à la caméra et allant vers quoi que ce soit pouvant être vu au pixel donné de l'écran. Notez que le vecteur retourné n'est généralement pas normalisé. L'élément y du vecteur retourné est toujours un.

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.


Performance et optimisation

Optimisation des performances

Si vous travaillez sur un grand projet, il est extrêmement important que vous vous préoccupiez des bonnes performances. C'est une croyance répandue que les graphismes qui peuvent êtres faits, dépendent seulement du moteur 3D qui est utilisé. Bien entendu un meilleur moteur peut conduire à de meilleur résultats. Mais plus un moteur a de potentiel, plus il est difficile d'utiliser toutes les fonctionnalités qui sont requises pour faire tourner le jeu de manière fluide. La complexité d'un jeu qui peut être fait avec un moteur dépend seulement de combien de possibilités sont offertes pour optimiser les performances du jeu. Généralement la façon la plus facile de réaliser quelque chose n'est pas la plus efficace, ni la plus rapide. Il faut de l'expérience et des connaissances pour trouver la solution optimale. Vous aurez aussi besoin de comprendre comment Ultimate 3D fonctionne de façon interne. Ce tutorial vous aidera à apprendre ces choses.

Conseils généraux

Pour les programmeurs avancés, les conseils généraux suivants devraient être tout à fait évidents. Mais étant donné qu'il est extrêmement important de les connaître ils seront listés ici à nouveau. Une des choses les plus importantes qu'il vous faut savoir est que le nombre de vertex et de triangles sont des facteurs extrêmement importants pour les performance de votre jeu. Calculer toutes les données qui sont requises pour un vertex prend un certain temps de calcul, vous devriez donc utiliser le moins de vertices possibles lors de la création d'un nouveau modèle pour votre jeu. Si vous travaillez dans un programme de modélisation professionnel tel que Blender ou 3D Studio Max vous pouvez utiliser des plugins pour optimiser vos meshs (Dans Blender c'est le Decimate Modifier). Les programmes de modélisation moins complexes auront au moins une fonctionnalité qui vous permet de voir de combien de vertices le modèles est fait actuellement. C'est utile pour réduire le nombre de vertex manuellement. Bien entendu, la façon la plus facile pour obtenir des modèles avec peu de vertex est de ne pas gaspiller de vertices dès le début de la modélisation. Soyez extrêmement prudents avec les fonctionnalités qui subdivisent les faces des modèles pour les rendre plus agréables. Aussi, n'utilisez pas un nombre élevé de subdivision lors de la création d'objets primitifs (les variables partsx, partsy et partsz).

Le prochain conseil est moins évident. Les performances de votre jeu dépendent aussi de combien de pièces de géométrie doivent être rendues séparement. Si vous avez deux pièces de géométrie dans votre modèle, Ultimate 3D les rendra seulement ensemble si elles utilisent le même matériau et si elles font parties du même mesh. Le pire des cas sera un modèle dans lequel vous avez une quarantaine de meshs différents, fait chacun de 20 triangles tout au plus mais utilisant encore trois matériau différents. Dans ce cas Ultimate 3D ne serait pas capable d'afficher plus de 10 triangles simultanément. Donc soyez parcimonieux avec le nombre de matériaux différents et utilisez des meshs qui sont faits de beaucoup de géométries lorsque vous modelez. Souvent l'utilisation d'un matériau texturé est plus efficace et rapide que l'utilisation de trois ou quatres matériaux différents et généralement c'est même plus beau de cette façon.

Vous ne devriez pas utiliser des textures trop grosses étant données qu'elles prennent beaucoup de mémoire. Plus vos textures prennent de mémoire, plus tôt la mémoire vidéo du périphérique graphique sera pleine. Ensuite la mémoire système devra être utilisée et dans ce cas la texture devra être passée de la mémoire système à la mémoire vidéo à chaque step, ce qui est très intensif et trop long. Donc si vous avez beaucoup de textures chargées simultanément, vous devriez libérer autant de mémoire que possible en appellant ReleaseTexture(...). En dehors de cela, vous devriez vraiment utiliser des dimensions qui sont faites de puissances de deux pour toutes vos textures. Si vous chargez une texture de la taille de 512*512 elle sera chargée exactement avec cette taille, mais si vous chargez une texture avec une taille de 513*513 elle sera mise à l'échelle de la puissance de deux supérieure, qui est 1024*1024. Il est très clair que c'est un gaspillage énorme de mémoire et de temps de calcul.

Soyez économe avec le nombre de sources de lumières dans votre scène. Si vous avez un nombre extrêment haut de sources de lumières, cela prendra beaucoup de temps de calcul pour chacun des vertex. Dans ce cas il peut être plus rapide d'apprendre à utiliser Deled ou d'autre logiciels de modélisation pour réaliser le light mapping. Pour obtenir un jeu efficace et rapide vous devrez souvent trouver un compromis entre les solutions flexibles et statiques. La flexibilité requière toujours beaucoup de temps de calcul, mais fait généralement moins de travail et rend le jeu plus vivant. Les solutions statiques prennent plus de mémoire étant donné que vous avez quelques données précalculées, mais elles prendront beaucoup moins de temps de calcul et pourront paraîtres plus belles, puisque le temps de précalcul des données statiques n'aura aucune influence. La comparaison entre l'éclaire par pixel ou l'éclairage par vertex et le light mapping est un très bon exemple, dans ce cas. Une répetition du coût élevé en terme de temps de calcul de la projection d'ombres n'est, je l'espère, pas requise ici.

Trouver le moyen le plus efficace de dessiner une pièce de géométrie

Ultimate 3D offre un ensemble de possibilités différentes pour manipuler une pièce de géométrie chargée depuis un fichier de modèle. Vous pouvez créer les LoD (Niveaux de Détail) pour ce dernier ou vous pouvez le manipuler comme un objet primitif. L'une ou l'autre de ces possibilités peuvent être la meilleure selon la situation. Quelque fois ce sera peut être plus efficace d'afficher un modèle avec des LoDs; quelque fois l'opposé pourrait être la solution. Il existe aussi des cas où il est beaucoup plus efficace et rapide de manipuler la pièce de géométrie comme un objet primitif; dans d'autres cas cela ne ferait aucun sens. Ce sous-chapitre contient quelques informations à propos de l'utilisation correcte des différentes façons de manipuler les modèles et donne des indications sur les solutions les meilleures dans différents scénarios.

Commençons avec l'utilisation correcte et propre des différents niveaux de détail. C'est une fonctionnalité très pratique qu'Ultimate 3D peut faire automatiquement et souvent cela peut augmenter considérablement les performances de votre jeu. En général, cela devrait être utilisé pour n'importe quel modèle qui doit paraître très détaillé lorsqu'il est proche de la caméra. Mais comme je l'ai déjà dit, vous ne devriez jamais l'utiliser pour les modèles utilisant les frames d'animation (que ce soit les modèles chargé depuis des fichiers *.md2 ou des animations créées à l'aide de AddFrame(...)). Il ne fait non plus aucun sens d'utiliser des LoDs si vous ne pouvez aller suffisamment loin du modèle. Par exemple, si vous avez un jeu quelconque de tir à la première personne dans lequel vous êtes toujours et seulement dans de petites pièces, l'utilisation de LoDs sera inefficace. Dans un tel cas il y a une façon bien meilleure d'optimiser le jeu. Cela sera introduit plus bas.

Il y a certains cas où l'utiilisation de la fonction CreatePrimitiveFromModel() pourra conduire à une nette amélioration de la performance, et d'autres cas où cela se révèlera tout à fait inefficace. En général les primitifs sont parfait pour tout les types d'objets simples ou statiques qui constituent le niveau. Par exemple, si vous avez de simples modèles de murs qui ne requièrent aucun effet pour donner une bonne apparence et qui sont faits de moins de 100 vertices, l'utilisation de primitifs serait parfaite dans un tel scénario. Les primitifs sont aussi parfaits pour calculer le rendu de la végétation. Vous devriez donc vraiment l'utiliser si c'est possible. Bien entendu il est un peu plus difficile de faire un modèle complexe avec seulement une texture mais le jeu en vaut la chandèle. Je vous conseille vivement d'utiliser des fichiers *.an8 pour les modèles que vous voulez utiliser avec cette fonction, parce que pour ces derniers Ultimate 3D les mettra, automatiquement, tous ensembles dans un même mesh.

Désactiver l'alpha blending

Contrairement aux versions antérieures d'Ultimate 3D, Ultimate 3D 2.0 beta 3 et les nouvelles versions n'ont aucun problème avec les textures partiellements transparentes. Dans les versions antérieures, ces dernières conduisaient très souvent à des problèmes d'affichage. Malheureusement le prix pour cela est plutôt élevé. L'objet tout entier doit être rendu deux fois dans le cas où il utilise une texture partiellement transparente. Au premier passage, on ne rend que les parties totalement opaques, au second passage on rend seulement les parties complètement transparentes. Etant donné que cela peut réduire considérablement les performances, Ultimate 3D vous permet de désactiver ces méthodes pour les objets particuliers. Les choses partiellements transparentes vont devenir complètement transparentes, mais cela accelèrera beaucoup les choses.

Cette fonction active ou non l'alpha blending pour l'objet de modèle ou le primitif donné. Par défaut l'alpha blending est activé.

SwitchAlphaBlending(
EnableDisable
)

EnableDisable
Si vous passez true pour ce paramètres, les parties partiellements transparentes seront rendues durant un second calcul de rendu (coûteux en performances), autrement les parties partiellements transparentes seront complètement invisible (rapide et efficace).

Mesurer les facteurs de performance importants

Pour être à même de dire, si vous avez bien optimisé votre projet, vous devez être capable de mesurer les facteurs, qui influent sur la performance. Il existe essentiellement deux facteurs. Le premier est le bien connu nombre de triangles. Le second est le nombre d'appel de traçage, qui était nécessaire pour calculer le rendu de la scène. À chaque fois qu'Ultimate 3D passe une pièce de géométrie au périphérique graphique, pour en calculer le rendu, c'est qu'un appel de traçage a été fait. Par exemple chaque groupe d'objets primitifs avec des propriétés identiques, requièrent un appel de traçage par step et par caméra, et chaque objet de modèle requière un appel de traçage par step par matériau et par caméra. C'est tailles indicatrices de la performance, peuvent être récupérées très facilement dans Ultimate 3D.

Cette fonction retourne le nombre de triangles qui ont été tracés au step précédent. Cela inclus également les triangles, dont le rendu n'a pas été calculé du fait du backface culling.

GetDrawnTriangleCount()

Cette fonction retourne le nombre d'appel de traçage qui ont été nécessaires au step précédent pour dessiner la scène.

GetDrawCallCount()

Les valeurs de retour de ces fonctions peuvent être très intéressantes. Vous pouvez voir combien les techniques de culling utilisées (voir plus bas) fonctionnent et quels objets ont un fort impact sur le nombre de triangles tracés. Vous pouvez voir combien l'utilisation de LoDs réduit le nombre de triangles et comment l'utilisation d'objets primitifs réduits le nombre d'appel de traçage. Tandis que vous modifiez les performances de votre application vous devriez ajouter un peu de code, qui affiche ces valeurs à l'écran, de sorte que vous puissiez toujours avoir un oeil sur elles.

Les fonctions avancées de culling

Le concept qui se cache derrière le culling est plutôt simple. Il consiste à éviter de calculer le rendu de ce qui n'est pas actuellement visible. Par défaut Ultimate 3D utilise deux techniques de culling; le back face culling et le frustum culling. Le Back face culling signifie que seul le rendu du côté face à nous de chaque triangle est calculé. De cette façon pas plus de la moitié du rendu de la géométrie doit être calculé. Le Frustum culling est la technique par laquelle on va éliminer du rendu les polygones ou objets se trouvant en dehors du champ de vision, on appelle cela une view frustum. L'image suivante explique ce qu'est une view frustum:

Illustration not available

Vous pouvez aussi directement accéder au frustum culling d'Ultimate 3D. C'est très utile si du code doit être éxécuté seulement si un objet particulier est visible. Par exemple si vous avez un moniteur de sécurité, qui utilise une caméra avec une texture cible de rendu, cette caméra ne doit calculer le rendu de la scène qu'elle voit, seulement si le moniteur de sécurité peut être vu. Pour déterminer si un objet est dans le view frustum ou pas, vous pouvez utiliser la fonction suivante.

Cette fonction retourne "true" si la bounding box donnée est dans le view frustum de la caméra, ou "false" si elle ne l'est pas.

CheckBoundingBoxVisibility(
BoundingBoxMinimumVectorID, BoundingBoxMaximumVectorID,
BoundingBoxTransformationID,
CameraIndex
)

BoundingBoxMinimumVectorID, BoundingBoxMaximumVectorID
L'ID de deux vecteurs définissant la bounding box. La bounding box sera cubique, où le coin inférieur arrière gauche aura la coordonnée BoundingBoxMinimumVectorID et le coin droit supérieur avant aura la coordonnée BoundingBoxMaximumVectorID. Si BoundingBoxMinimumVectorID est négatif, le vecteur (-1, -1, -1) sera utilisé, si BoundingBoxMaximumVectorID est négatif, le vecteur (1, 1, 1) sera utilisé.

BoundingBoxTransformationID
L'ID de la matrice de transformation, qui sera utilisée pour transformer la bounding box spécifiée par les deux paramètre précedents. Si ce paramètre est négatif, aucune transformation ne sera utilisée.

CameraIndex
L'indice de la caméra, qui a le view frustum qui doit être utilisé. C'est la valeur de la variable number, et non pas l'ID de l'objet caméra.

Quoi qu'il en soit, il est temps de revenir aux fonctions de culling avancées. Si certaines géométries qui sont rendues ne sont pas visibles, cela signifie qu'elles ont été couvertes par d'autres objets. Pour éviter que les géométries soient couvertes dans ce cas, une trois technique de culling est nécessaire. Et c'est exactement à quoi sont destinées les fonctions de culling avancées. Elles fonctionnent de la façon suivante. Tout d'abord, vous assignez chaque objet de la scène - que ce soit un primitif, un modèle, un terrain, un système de particule ou une source de lumière - à une room. Une room n'est rien qu'un indice et la somme d'objet qui y est associée. Par défaut chaque objet est assigné à la room avec l'indice 0. Ensuite vous devrez définir quelle room peut être vue par quelle caméra. Par défaut seule la room avec l'indice 0 est définie comme visible pour chaque caméra. Cela peut être fait soit manuellement, ou Ultimate 3D peut le faire automatiquement.

Dans les niveaux d'intérieur cela peut être particulièrement utile. Lorsque le joueur ouvre une porte, une nouvelle room est définie comme visible. Si le joueur passe à travers la porte et la ferme, l'ancienne room est définie comme invisible. De cette façon vous pouvez réduire le nombre de rooms qui doivent être rendues, par un ou deux. Toutes les autres rooms n'ont pas d'intérêt. La source de lumière éclairera seulement les objets qui sont associés à la même room, ce qui conduit bien sûr à une augmentation des performances. La seule exception à cette règle sont les sources de lumières qui sont associées avec la room 0. Celles-ci éclaireront absolument tous les objets.

Les fonctions qui sont nécessaires pour cette fonctionnalité sont vraiment simples. Il n'y a que deux fonctions, une pour assigner les objets aux rooms, et une pour définir une room à l'état visible ou invisible pour une caméra particulière.

Cette fonction assigne l'objet qui l'appelle à une room.

SetObjectRoom(
RoomIndex
)

RoomIndex
L'indice de la room à laquelle vous souhaitez assigner l'objet. Cela peut être une valeur entière arbitraire allant de 0 à 1999.

Cette fonction définit la room donnée à l'état visible ou invisible pour l'objet de caméra qui l'appelle, ou pour la première caméra si ce n'est pas appellé par un objet de caméra. Remarquez que cette fonction doit être appellée après MoveCamera().

SetRoomVisibility(
RoomIndex,
NewVisibilityState
)

RoomIndex
L'indice de la room que vous voulez définir à l'état visible. Cela peut être un entier arbitraire entre 0 et 1999.

NewVisibilityState
L'état de visibilité que vous voulez définir pour la room donnée. Cela peut être soit "true" pour définir la room comme visible ou "false" pour définir la room comme invisible.

Plus haut, j'ai dit que le changement d'état visible/invisible des rooms pouvait être fait manuellement ou automatique. Comme vous pouvez le voir, la manière manuelle est vraiment simple, mais cela vous fera beaucoup de travail. Et vous avez déjà suffisamment de travail, à assigner les objets aux différentes rooms. La façon automatique devrait donc être préférée en général. Elle est basée sur ce qu'on appelle les portails.

Un portail peut être une porte, une fenêtre, un portail, une rue, un canyon ou toute autre chose au travers de laquelle on peut passer ou regarder. C'est quelque chose qui peut conduire l'objet de caméra ou un autre objet, d'une room A vers une room B. Les portails sont représentés par la plus simple des formes, un cercle. Si une caméra passe au travers d'un portail, elle ira dans une nouvelle room, ce qui signifie que cette room sera visible. Et toutes les rooms qui se cachent derrière les portails qui sont visibles, seront bien entendu visibles. Etant donné que cette définition est plutôt abstraite, cela va se clarifier à l'aide d'un ensemble d'illustrations montrant différentes situations.

Il s'agit d'un cas très simple. Il ya quatre rooms différentes, avec quatres portes entre elles. Chaque porte a un portail (bleu) défini pour elle. Initialement la caméra est dans la room 1, mais ensuite elle se déplace au travers du portail de room 1 vers room 2, donc après elle est dans room 2 et Ultimate 3D est à même de le détecter, puisqu'il a les données du portail. À peu près la même chose arrive pour l'objet de modèle. Au début il est dans room 1, donc il ne sera visible que si la room 1 est visible. Ensuite il se déplace vers la room 4 et Ultimate 3D le détecte, grâce au portail. Donc après ça il ne sera visible que si room 4 est visible. Pour les objets de modèle et les autres objets de géométrie c'est tout ce qui est nécessaire, mais pour la caméra c'est un peu différent, tout en restant aussi simple, parce que la caméra peut voir plus de room que celle dans laquelle elle est. Lorsque la caméra est dans room 1 elle peut aussi voir la room 2 au travers de la porte. Pour être plus précis, elle peut voir room 2 lorsqu'elle peut voir le portail allant de room 1 à room 2. Donc room 2 sera visible lorsque la porte vers room 2 est visible. Le schéma suivant illustre ce concept.

Sur la gauche, la caméra ne regarde aucun portail, donc aucune room, mis à part celle où est la caméra est actuellement visible. Sur la droite, la caméra regarde vers le portail allant de room 1 à room 2, donc room 2 est aussi visible. Suit un dernier cas spécifique. La caméra ne peut pas uniquement voir les rooms qui sont derrière les portails visibles, qui conduit de la room actuelle à une autre room, mais aussi les rooms qui sont derrière un portail visible qui conduit de n'importe quelle room visible à une autre room. À nouveau un graphique explique cela.

Comme vous pouvez le constater room 1, room 2 et room 3 sont visibles. Room 1 est visible, parce que la caméra est dans cette room, room 2 est visible parce que le portail allant de room 1 à room 2 est dans l'angle de vision de la caméra et room 3 est visible, parce qu'un portail présent dans une room visible et conduisant à la room 3 est dans l'angle de vision de la caméra. Quelque chose ne va pas ici cependant, parce qu'il est très facile de voir que la room 3 n'est en réalité pas visible. C'est une petite erreur qui peut qui peut facilement arriver avec le moteur de portails d'Ultimate 3D. Mais il existe un moyen d'éviter ce problème dans de nombreux cas. D'usage les portes se ferme derrière le personnage une fois qu'il les a passé. Si elle est fermée, vous ne pouvez plus voir ce qui est derrière elle et cet effet est utilisé par Ultimate 3D. Dans la mesure où un portail est défini comme fermé, les rooms qui sont derrière ce portail ne seront plus visibles, même si le portail lui même est dans la zone de vision de la caméra.

À l'aide de tous ces exemples, vous pouvez très bien comprendre l'utilité du moteur de portail. Dans un niveau habituel dans un plus gros jeu, il peut aisément y avoir une vingtaine de room dans un niveau. Calculer le rendu pour chacune d'elle à chaque frame serait un suicide total pour le CPU et le périphérique graphique. En utilisant le moteur de portails vous pouvez réduire le nombre de rooms dont le rendu doit être calculé simultanément à une simple room, qui est tout de même un gain de performances énorme. Cela va même mieux, parce que l'utilisation du moteur de portail ne prend pas de temps de calcul, ou presque pas. Le seul désavantage, réside dans le fait que cela fait énormément de travail pour définir tous les portails, et d'assigner tous les objets (ou du moins le plus grand nombre) aux différentes rooms. Différents méchanismes, qui aurait pu assigner les objets aux différentes rooms automatiquement, furent considérés durant le développement d'Ultimate 3D 2.0, mais la plupart d'entre eux se révélèrent trop compliqués, ou pas suffisamment fiables. Et sérieusement, combien de travail passez-vous sur votre jeu? Et comparé au gain de performance gigantesque, le peu de travail supplémentaire est insignifiant. Aussi, un éditeur de niveau qui est pensé pour être utilisé avec Ultimate 3D est prévu, et il rendra l'utilisation du moteur de portail bien plus facile. Quoiqu'il en soit ce projet n'est rien d'autre qu'une idée pour le moment, ne l'attendez donc pas pour tout de suite. (NdT: Rien ne vous empêche de créer le votre! ^^)

Eh bien, cela faisait à nouveau beaucoup de théorie, mais si l'avez entièrement comprise, les fonctions seront presque évidentes pour vous. La première fonction que vous devez connaître, active le moteur de portail pour un objet de caméra particulier.

Cette fonction active le moteur de portail pour l'objet de caméra qui l'appelle.

SwitchPortalEngine(
EnableDisable
)

EnableDisable
Si le moteur de portail doit être activé (true) ou désactivé (false).

Une fois que le moteur de portail est activé n'importe quel appel à SetRoomVisibility(...) sera ignoré. Peu importe il existe une bonne remplaçante pour cela. Cette fonction peut être utilisée pour forcer la visibilité d'une room pour la caméra qui l'appelle, même si le moteur de portail est activé.

SetRoomVisibilityEnforcement(
RoomIndex,
NewVisibilityEnforcementState
)

RoomIndex
L'indice de la room pour laquelle vous souhaitez forcer l'état de visibilité. Cela peut être un nombre arbitraire allant de 0 à 1999.

NewVisibilityEnforcementState
L'état de visibilité forcé, que vous voulez définir pour la room donnée. Si ce paramètre est "true" la room sera visible en toutes circonstances, autrement la visibilité dépendra des portails (ou de SetRoomVisibility(...) si le moteur de portail est désactivé). Pour la room 0 l'état de visibilité est forcé sur visible par défaut, pour toutes les autres c'est désactivé par défaut.

Les quatres prochaines fonctions peuvent être utilisées pour créer, modifier, fermer ou détruire des portails. Comme je l'ai dit plus haut, les portails sont représentés par des cercles. Un cercle est défini par une orientation (la direction vers laquelle il fait face), un centre et un rayon. De plus les portails contiennent deux indices de room.

Cette fonction crée un portail en utilisant les données fournies et retourne son identifiant.

CreatePortal(
CenterX, CenterY, CenterZ,
FacingDirectionLongitude, FacingDirectionLatitude,
Radius,
RoomIndex1, RoomIndex2
)

CenterX/Y/Z
Le centre du cercle qui définit le portail.

FacingDirectionLongitude, FacingDirectionLatitude
La direction vers laquelle le cercle fait face, qui définit le portail. Si vous créez un rayon à partir de ces deux angles, le cercle sera perpendiculaire à ce rayon.

Radius
Le rayon du cercle définissant le portail.

RoomIndex1, RoomIndex2
Les indices des rooms qui sont des différents côtés du cercle. Ces paramètres ne doivent jamais être égal à 0, parce que la room avec l'indice 0 est toujours visible lorsque le moteur de portail est activé et ne devrait pas être considéré comme une véritable room. L'ordre de ces paramètres n'a aucune influence. Si une caméra est dans une room avec RoomIndex1 et se déplace au travers du portail il sera dans la room avec RoomIndex2, et vice versa.

Cette fonction peut être utilisée pour modifier un portail après sa création.

SetPortal(
PortalID,
FacingDirectionLongitude, FacingDirectionLatitude, Radius, RoomIndex1, RoomIndex2
)

PortalID
L'identifiant du portail que vous souhaitez modifier. C'est la valeur de retour de l'appel à CreatePortal(...) qui a créé le portail.

FacingDirectionLongitude, FacingDirectionLatitude, Radius, RoomIndex1, RoomIndex2
Voir ci-dessus.

Cette fonction peut être utilisée pour ouvrir un portail (il est ouvert par défaut) ou pour en fermer un.

OpenPortal(
PortalID,
OpenClose
)

PortalID
L'identifiant du portail que vous souhaitez modifier. C'est la valeur de retour de l'appel à CreatePortal(...) qui a créé le portail.

OpenClose
Passez "true" pour ouvrir ce portail ou "false" pour le fermer. Si un portail est fermé la caméra sera toujours capable de passer à travers pour aller dans une autre pièce, parce que les portails ne font pas de détection de collision, mais les rooms qui auraient pu être visibles à travers le portail ne le seront plus.

Cette fonction détruit le portail avec l'identifiant spécifié.

DestroyPortal(
PortalID
)

PortalID
L'identifiant du portail que vous voulez détruire. C'est la valeur de retour de l'appel à CreatePortal(...) qui a créé le portail.

Une donnée qui ne peut être trouvée au travers de l'utilisation des portails est, dans quelle room est la caméra initialement. Pour cette raison vous devez être à même de le définir manuellement. Cela peut être fait à l'aide de la fonction SetObjectRoom(...) juste comme n'importe quels autres objets.

Le moteur de portails ne vous apporte pas seulement un gain de performance, il peut aussi être très utiles pour programmer les choses liées au gameplay. Par exemple vous pouvez démarrer une cinématique particulière si une caméra passe pour la première fois à travers un portail particulier. Cela peut aussi être utile pour la programmation des IA. Si la caméra est dans la même room que l'ennemi, l'ennemi doit être actif. Pour réaliser des choses comme celles-ci, vous devez être à même de trouver dans quelle room se trouve actuellement un objet. Cette information peut être retrouvée à l'aide de la fonction suivante.

Cette fonction récupère l'indice de la room dans laquelle est l'objet qui l'appelle.

GetObjectRoom()

Une autre chose à ce propos, vous pouvez éviter beaucoup de calculs inutiles en les réalisant uniquement pour les objets qui sont dans une room visible. Par exemple les systèmes de particules n'ont pas besoin d'être mis à jour sans qu'ils ne soient visibles par la caméra, et les les objets n'ont pas besoin de chercher les collisions avec les autres objets dans les autres rooms. Pour déterminer si une room peut être vue par une caméra particulière vous pouvez utiliser la fonction suivante.

Cette fonction retourne si la room avec l'indice donné peut être vue par la caméra qui l'appelle.

GetRoomVisibility(
RoomIndex
)

RoomIndex
L'indice de la room dont vous souhaitez récupérer l'état de visibilité.

Beaucoup d'avantages sur l'utilisation des fonctions avancées de culling ont déjà été mentionnées. Elles augmentent fortement la performance, et peuvent être utilisées pour éviter l'exécution de code inutile, elles rendent le système de détection des collisions plus rapide, peuvent être utiles pour l'IA et elles vous donnent la possibilité de contrôler quelle source de lumière affecte quels objets. Voici maintenant, un autre avantage. L'utilisation des fonctions de culling avancées vous donne également la possibilité d'avoir différents éclairages ambiants pour différents objets. SetAmbient(...) spécifie une couleur d'éclaire ambiant particulière pour toutes les rooms. Vous pouvez changer cette couleur pour chaque room en usant de la fonction suivante.

Cette fonction change la couleur de l'éclairage ambiant pour une room donnée.

SetRoomAmbient(
RoomIndex,
AmbientColorR, AmbientColorG, AmbientColorB
)

RoomIndex
L'indice de la room pour laquelle vous voulez spécifier une nouvelle couleur d'éclairage.

AmbientColorR, AmbientColorG, AmbientColorB
La couleur de l'éclairage ambiant, qui doit être définie pour cette room. Pour plus d'informations sur l'éclairage ambiant, référez vous à la description de SetAmbient(...).

Remarquez que tout appel à SetAmbient(...) écrasera les changement fait avec cette fonction.


Détection de collision et réponse de collision

Avez-vous déjà vu quelqu'un passer à travers un mur? Probablement pas. Dans la réalité il est impossible de passer au travers des surfaces solides, et étant donné que les jeux cherchent généralement à copier la réalité ce comportement doit être reproduit. C'est pourquoi une bonne détection des collisions et des réponses de collision sont requises. La détection de collision doit déterminer si les objets se sont déplacés au travers ou dans des obstacles à chaque step et si c'est le cas la réponse de collision doit définir la position à laquelle l'objet peut se déplacer sans rentrer dans un obstacle.

La réponse de collision la plus courante pour les jeux à la première et troisième personne font glisser les objets le long des murs, s'ils essaient de se déplacer à travers. Cela a l'avantage que le joueur ne s'arrête pas de se déplacer lorsqu'il va contre un mur, ce qui est important, étant donné qu'un blocage soudain peut être vraiment irritant dans les jeux au gameplay rapide. Avec Ultimate 3D 2.0 et son système de détection de collisions basé sur le ray tracing cela fut très difficile d'obtenir une détection des collisions fiable. C'était fondamentalement la plus grande faiblesse d'Ultimate 3D. Pour Ultimate 3D 2.1 j'ai complètement revu le système de détection de collision et développé un système de réponse de collision intégré. Maintenant la détection de collisions et la réponse de collision est une des forces d'Ultimate 3D. Les credits pour cette réalisation vont à Kasper Fauerby qui a écrit un document décrivant la technique sur laquelle sont basés les mécanismes de détection et de réponse de collision.

Puisque le système est intégré au moteur, il est très facile à utiliser et bien assez rapide, puisque qu'aucun code GML lent n'a besoin d'être éxécuté. Le système de réponse de colision glissant, décrit plus haut, peut être implémenté avec seulement cinq lignes de code. Il vous suffit simplement de définir tous les objets qui doivent subir le contrôle des fonctions de détection de collision comme solide, au travers d'une fonction Ultimate 3D et appeller une fonctions pour les objets, qui devront effectuer la détection de collision et la réponse de collision. Et vous êtes bon.

Détection de collision basée sur les ellipsoïdes

Avant que nous puissions en venir à cette fonction magique, qui résout tous vos problèmes (du moins ceux liés aux collisions), nous avons besoins de faire un peu de théorie. La première considération nécessaire est, quelle forme peut être utilisée pour une bonne réponse de collision. Elle ne doit pas avoir de coins, pour éviter que cela puisse s'accrocher quelque part, elle devra être suffisamment polyvalente pour contenir des objets avec des formes très différentes et elle doit être facile à décrire. Le choix évident est un éllipsoïde (une sphère mise à l'échelle). Ainsi nous n'aurons plus jamais besoin de penser aux détections de collisions pour les modèles, en mouvement, des personnages, nous aurons uniquement à penser à la détection des collisions pour les ellipsoïdes en mouvement. L'illustration suivante décrit un éllipsoïde.

Les jeux sont basés habituellement sur des grilles (frame based) et de ce fait les objets ne se déplacent pas véritablement en continu, ils sautent d'une position à une autre à chaque step. Durant un step l'objet n'est peut être pas en collision avec quoi que ce soit, et au step suivant il peut déjà se retrouver à l'intérieur d'une géométrie. La figure suivante explique cela.

Illustration not available

Il ne suffit pas de vérifier si la zone verte est traversée par un obstacle, il n'est pas suffisant de vérifier si la zone jaune est traversée par un obstabcle et il n'est pas suffisant non plus, de vérifier si la zone rouge est traversée par un obstacle. Toutes ces zones doivent être vérifiées. Ainsi la forme pour laquelle les collisions doivent être détectées en réalité, n'est pas un éllipsoïde, mais un ellipsoïde étendu (swept ellipsoid). Un ellipsoide étendu est fait de deux ellipsoïdes, et d'un cylindre mis à l'échelle, les reliants. C'est la forme avec laquelle fonctionne Ultimate 3D. Cela devrait être suffisamment de théorie pour maintenant.

La fonction suivante effectue un test d'intersection entre la géométrie donnée, et l'ellipsoïde étendu (swept ellipsoid) donné, puis retourne si une collision a été détectée (true) ou non (false).

CheckSweptEllipsoidIntersection(
ObjectID,
StartX,StartY,StartZ,
DestinationX,DestinationY,DestinationZ,
RotX,RotY,RotZ,ScalingX,ScalingY,ScalingZ,
RoomIndex
)

ObjectID
L'ID de l'objet pour lequel vous voulez effectuer la détection de collision. Cela peut être l'ID d'un modèle, d'un terrain ou d'un objet primitif ou même le mot clé all. Dans ce dernier cas tous les objets, qui sont définis comme solides à l'aide de la fonction SetObjectSolidity(...) et qui sont dans la bonne room seront vérifiés et contrôlés. En addition à cela, tous les objets qui sont définis comme globalement solides au travers de SetObjectSolidity(...), seront vérifiés et contrôlés, quelle que soit la room dans laquelle ils sont (Ndt: Rendez vous au chapitre précédent, pour comprendre le fonctionnement des rooms, ce ne sont pas les rooms de Game Maker).

StartX, StartY, StartZ
La position de départ de l'ellipsoïde étendu (voir l'illustration ci-dessus).

DestinationX, DestinationY, DestinationZ
La position de destination de l'ellipsoïde étendu (voir la figure ci-dessus). Ces paramètres peuvent égaler StartX/Y/Z. Dans ce cas là, Ultimate 3D effectuera un test d'intersection d'ellipsoïde non-étendu, qui sera donc moins intensif, en matière de temps de calcul.

RotX, RotY, RotZ, ScalingX, ScalingY, ScalingZ
La rotation et l'échelle de l'ellipsoïde. L'ellipsoïde est une sphere (une sphère de rayon un) mise à l'échelle par ScalingX/Y/Z et pivotée par RotX/Y/Z.

RoomIndex
Ce paramètre n'a de sens que si all est spécifié pour le premier paramètre. Si ce n'est pas le cas les objets de toutes les rooms seront contrôlés et vérifiés. Sinon, seuls les objets présents dans la room avec l'indice donné et les objets globalement solides seront contrôlés et vérifiés (Voyez SetObjectRoom(...)).

Juste savoir qu'une collision s'est produite reste cependant tout à fait inutile. Plus d'informations sont nécessaires pour permettre de répondre à la collision (dans la mesure où vous n'utilisez pas le système de réponse de collision intégré). Pour cette raison il existe un ensemble de fonctions, qui retournent des données pertinentes sur les collisions qui ont lieux. Toutes ces fonctions retournent des données à propos des résultats des derniers appels à CheckSweptEllipsoidIntersection(...).

Cette fonction retourne le nombre de triangles, qui ont été traversés par l'ellipsoïde étendu, défini dans le dernier appel à CheckSweptEllipsoidIntersection(...).

GetTriangleIntersectionCount()

Cette fonction retourne le moment où le triangle traversé donné a été traversé par l'ellipsoïde étendu spécifié dans le dernier appel à CheckSweptEllipsoidIntersection(...). La valeur de retour reste dans l'interval allant de 0.0 à 1.0, ou 0.0 se réfère au moment auquel l'ellipsoïde était à la position de départ de la sphère étendue et 1.0 se réfère au moment auquel il était à la position de destination.

GetIntersectionTime(
IntersectedTriangleIndex
)

IntersectedTriangleIndex
L'indice du triangle traversé pour lequel vous souhaitez retrouver le moment de collision. C'est une valeur entière allant de 0 à GetTriangleIntersectionCount()-1.

Cette fonction donne la position à laquelle l'ellipsoïde étendu spécifié au dernier appel de CheckSweptEllipsoidIntersection(...), a traversé en premier le premier triangle traversé.

GetIntersectionPosition(
OutputVectorID,
IntersectedTriangleIndex,
)

OutputVectorID
Référez vous à la description dans le chapitre à propos des fonctions mathématiques.

IntersectedTriangleIndex
L'indice du triangle traversé (référez vous à la description de GetIntersectionTime(...) pour plus de détails).

Cette fonction sort les normales du triangle traversé donné. La normale d'un triangle est un vecteur normalisé, qui reste perpendiculaire au triangle et pointe loin devant depuis sa face avant.

GetIntersectedTriangleNormal(
OutputVectorID,
IntersectedTriangleIndex
)

OutputVectorID
Voir la description dans le chapitre sur les fonctions mathématiques.

IntersectedTriangleIndex
L'indice du triangle traversé (référez vous à la description de GetIntersectionTime(...) pour plus de détails).

Cette fonction sort la normale du triangle traversé donné multiplié par un facteur, de sorte que l'ellipsoïde aura quitté le triangle si le vecteur retourné est ajouté à la position de destination de l'ellipsoïde. Cela peut être utilisé pour des réponses de collisions très simple, mais cela ratera rapidement, si plusieurs triangles sont impliqués dans la collision.

GetPushAwayVector(
OutputVectorID,
IntersectedTriangleIndex
)

OutputVectorID
Voir la description dans le chapitre sur les fonctions mathématiques.

IntersectedTriangleIndex
L'indice du triangle traversé (référez vous à la description de GetIntersectionTime(...) pour plus de détails).

Avec ces fonctions vous pouvez programmer des réponses de collision personnalisées. Par exemple vous pouvez faire rebondir les objets en arrière s'ils rencontrent un obstacle, ou vous pouvez les déformer dépendemment de la façon dont ils se sont rencontrés. Quoi qu'il en soit ces mécanismes de réponse de collision ne sont usuellement pas ce qui est voulu. Le mécanisme de réponse de collision le plus commun reste encore celui avec la glissement. Et c'est celui auquel on va venir désormais. Comme je l'ai déjà dit seulement une fonction est requise pour cela. Elle réalise la détection de collision et la réponse de collision automatiquement. La voici.

Cette fonction vérifie si l'ellipsoïde donné entre en collision avec le(s) objet(s) donnés, sur son chemin de la position de départ à la position de destination. Si le vecteur n'est pas traversé, ce qui est retourné est égal à la position de destination, sinon une position libre à laquelle l'ellipsoïde peut se déplacer est retournée.

AttemptMoveToPosition(
OutputVectorID,
ObjectID,
StartX,StartY,StartZ,
DestinationX,DestinationY,DestinationZ,
RotX,RotY,RotZ,ScalingX,ScalingY,ScalingZ,
RoomIndex
)

OutputVectorID
Voir la description dans le chapitre sur les fonctions mathématiques.

ObjectID, StartX, StartY, StartZ, DestinationX, DestinationY, DestinationZ, RotX, RotY, RotZ, ScalingX, ScalingY, ScalingZ, RoomIndex
Voir la description de CheckSweptEllipsoidIntersection(...).

Pour un exemple sur la manière d'utiliser cette fonction, regardez le code dans l'évènement step de l'objet de caméra dans le SDK. Bien entendu une fonction de plus nécessite d'être expliquée ici. Elle a déjà été mentionée plus haut et elle est réellement simple, quoi qu'il en soit en voici une explication.

Cette fonction change l'état de solidité du modèle, du primitif ou de l'objet de terrain par lequel elle est appellée.

SetObjectSolidity(
SolidityState,
GlobalSolidityState,
)

SolidityState
Le nouvel état de solidité, qui peut être défini. Cela peut être true, pour obtenir le comportement où l'objet sera vérifié, si all est spécifié comme paramètre pour ObjectID et si RoomIndex correspond à l'indice de room de cet objet; lors d'un appel à CheckSweptEllipsoidIntersection(...), AttemptMoveToPosition(...) ou CheckRayIntersection(...). Cela peut être false pour obtenir le comportement où l'objet ne sera pas vérifié si all est spécifié comme paramètre pour ObjectID.

GlobalSolidityState
Le nouvel état de solidité globale à définir. Cela peut être true, pour faire en sorte que l'objet sera toujours vérifié et contrôlé, si all est spécifié comme ObjectID. Ou bien false pour faire en sorte que cela ne sera pas vérifié dans ce cas. La différence entre SolidityState et GlobalSolidityState réside dans l'idée que GlobalSolidityState est indépendant du paramètre RoomIndex. L'utilisation de cet état fait sens pour les objets, qui sont dans les rooms, qui utilisent la visibilité forcée, comme les portes (reportez vous à SetRoomVisibilityEnforcement(...)).

Cette fonction combinée avec le moteur de portail (en particulier avec SetObjectRoom(...)) offre un moyen vraiment commode et efficace pour spécifier quels objets ont besoins d'êtres pris en compte par la detection automatique de collision. Vous pouvez l'utiliser de sorte que les objets effectuent toujours la détection de collision seulement pour les objets, qui sont dans la même room et pour les objets qui sont entre les rooms, comme les portes (celles-ci peuvent avoir la solidité globale de définie). Cela rend la detection de collision vraiment efficace.

Ray tracing

Une autre méthode, qui est importante pour la détection des collisions est le ray tracing ou pour être plus précis le test d'intersection entre les mesh et les rayons, qu'on appellera plus communément le ray-mesh intersection test. Un test de ray tracing vérifie si un rayon donné entre en collision avec une pièce de géométrie. Ceci est utile, par exemple, si vous souhaitez vérifier si une arme pointe sur quelque objet, pour définir ce qui sera touché lorsque l'arme tirera. Pour vous donner une meilleure compréhension de ce qu'est le ray tracing, voici une explication plus approfondie.

Imaginez que vous avez un rayon. Si vous ne vous en souvenez pas des maths à l'école, un rayon est une ligne droit qui débute en un point et qui est infiniment longue. Une bonne manière d'imaginer cela est un pointeur laser. La position à laquelle est le pointeur laser, est l'origine du rayon et le laser émet un rayon infiniment long (en théorie). Si vous utiliser le pointeur laser pour pointer un objet quelconque, vous verrez un petit point à la position où le rayon rencontre l'objet. C'est le point d'intérêt, en anglais Point of Interest (POI). L'illustration suivante évoque cela.

Illustration not available

Un test d'intersection ray-mesh utilise une méthode mathématique pour tester si le rayon donné traverse les triangles de l'objet donné. Si c'est le cas, cette méthode calcule la position à laquelle le rayon entre en collision en premier avec l'objet (le point bleu sur l'illustration). La fonction de ray tracing est très similaire à CheckSweptEllipsoidIntersection(...), mais étant donné que les rayons sont plus rapides à décrire que les ellipsoïdes étendus, elle est nettement plus simple.

Cette fonction vérifie si le rayon donné traverse la géométrie de les ou l'objet donné(s). Si c'est le cas elle retourne la distance depuis l'origine du rayon à la position d'intersection la plus proche, sinon elle retourne 100,000.

CheckRayIntersection(
ObjectID,
RayOriginX,RayOriginY,RayOriginZ,
RayDirectionLongitude,RayDirectionLatitude,
RoomIndex
)

ObjectID
Voir la description de CheckSweptEllipsoidIntersection(...).

RayOriginX, RayOriginY, RayOriginZ
L'originie du rayon.

RayDirectionLongitude, RayDirectionLatitude
La direction du rayon. Pour obtenir de plus amples informations sur ces deux paramètres, jetez un oeil à la description de la fonction Move(...).

RoomIndex
Reportez vous à la description de CheckSweptEllipsoidIntersection(...).

Une fois encore, il existe quelques fonctions pour obtenir plus d'informations sur le triangle traversé.

Cette fonction sort la normale d'un triangle, qui a été traversée par le rayon spécifié dans le dernier appel à CheckRayIntersection(...). La normale d'un triangle est un vecteur normalisé, qui reste perpendiculaire au triangle et pointe loin devant depuis la face avant de ce dernier.

GetRayTracingNormal(
OutputVectorID
)

OutputVectorID
Voir la description dans le chapitre sur les fonctions mathématiques.

Cette fonction retourne l'indice du matériau, qui est utilisé par le triangle traversé lors du dernier appel à CheckRayIntersection(...), qui a été utilisé avec un objet de modèle comme paramètre pour ObjectID.

GetIntersectedMaterialIndex()

En utilisant ces fonctions, vous pouvez en apprendre davantage à propos de ce qui entoure un objet. Par exemple vous pouvez envoyer un rayon droit sous les hanches du personnage pour définir à quelle distance est le sol. Ensuite vous pouvez utiliser GetRayTracingNormal(...) pour déterminer la force du dénivelé du sol à cette position. Et après quoi vous pouvez utiliser GetIntersectedMaterialIndex() pour déterminer, si le personnage se tient sur du métal ou de l'herbe.


Options d'Ultimate 3D

Il y a un certain nombre d'options dans Ultimate 3D pour modifier la façon avec laquelle il charge les données, il calcule le rendu des scènes et la façon dont il gère les erreurs. Quelques-unes de ces options doivent être définies avant qu'Ultimate 3D soit initialisé, tandis que d'autres peuvent être changées n'importe quand en utilisant les fonctions correspondantes. Celles qui doivent être définies avant qu'Ultimate 3D soit initiliasé sont définies dans le script Ultimate3DOptions(). Ce script n'est pas prévu pour être appellé par l'utilisateur. Il sera appellé une fois qu'Ultimate 3D sera initialisé, c'est tout. Le fait que ce soit un script plutôt qu'un certain nombre de constantes Game Maker résulte en un avantage pour vous, parce que de cette manière vous pouvez charger les options depuis un fichier. Actuellement il y a six options qui sont définies par des variables dans le script Ultimate3DOptions():

global.u3d_splash_screen_file
Il vous faudra définir cette variable comme string. Si vous ne souhaitez pas qu'Ultimate 3D n'affiche un écran pendant le chargement des ressources, vous pouvez définir cette variable à une chaîne vide (""). Sinon, si vous souhaiter afficher une image durant le chargement des ressources, il vous suffira de définir cette variable au nom de fichier, en y incluant le chemin du fichier, pour cette variable.

global.u3d_z_buffer_format
Cette variable peut avoir soit la valeur 16 ou 32. Si vous la définissez à 32, Ultimate 3D tentera de créer un périphérique utilisant un z-buffering de 32 bits. Le Z-buffering est une technique utilisée pour éviter que les objets qui sont devants d'autres objets soient couverts par les objets dérrière eux. Si vous le définissez à 16 Ultimate 3D utilisera un z-buffering de 16 bits, bien entendu.

global.u3d_multi_sample_type
Ce variable peut prendre une valeur entière allant de 2 à 16, ou bien 0. Elle définit le nombre d'échantillonages qui seront utilisés pour l'antialiasing. Dans le cas où elle est égale à 0 aucun antialiasing ne sera utilisé. Plus la valeur est élevée, plus fort sera l'antialiasing. Notez que le nombre d'échantillonage diffère de carte graphique en carte graphique. Certains périphériques graphiques ne supportent même pas l'antialiasing. Une autre chose que vous devriez noter est que l'antialiasing n'aura aucun effet si les post screen shaders sont activés.

global.u3d_device_type
Si vous n'avez pas une bonne raison de ne pas le faire, vous devriez définir cette variable à 0. Ultimate 3D créera ensuite l'interface la plus efficace et rapide qui offre toutes les fonctionalités requises. Si vous la définissez à 1, Ultimate 3D créera une interface qui calculera les vertex à l'aide du logiciel. Cela signifie que cela sera le processeur principal (CPU) qui calculera les données des vertex, plutôt que le processeur graphique. Habituellement cela n'a pas du tout de sens. L'unique cas dans lequel vous devriez utiliser cette option est lorsqu'il y a un bug dans les drivers du périphérique graphique, qui donne lieu à un crash lors de l'utilisation de fonctionnalités particulières avec le calcul de vertex supporté matériellement. J'ai connu cela avec un driver ATI en utilisant le vertex tweening. Pour finir vous pouvez initialiser cette variable à 2 pour créer une interface complètement logicielle (en utilisant la couche Direct 3D de référence). Cela vous donnera un frame rate extrêmement lent mais toutes les fonctionalités (cube mapping, shadow casting et les modèles de pixel shader 1.4) seront correctements prises en charges.

global.u3d_log_file
Cette variable peut être définie avec un chemin de fichier d'un fichier texte, par exemple "LogFile.txt". Si elle est initialisée en tant que string, Ultimate 3D créera un fichier de journal, pendant que l'exécutable fonctionnera. Beaucoup d'informations utiles sortiront dans ce fichier de journal (en particulier, tous les messages d'erreur et d'avertissement, les informations à propos du périphériques et de ses capacités et quelques données à propos du succès de certaines opérations). Si vous même, ou un utilisateur d'une de vos applications basées sur Ultimate 3D a des problèmes avec votre application, le fichier de journal rendra la résolution du problème bien plus aisée.

global.u3d_few_message_output
Cette variable peut être définie à true, afin de réduire le nombre de messages d'erreur qu'Ultimate 3D affichera à l'écran. Si elle est définit à true, Ultimate 3D affichera seulement les messages d'erreur, s'ils sont cruciaux et s'ils sont suceptibles de faire crasher l'application. Le fichier de journal n'est pas affecté par ce paramétrage.

Comme je l'ai déjà dit, les autres options peuvent être changées à n'importe quel moment en utilisant des fonctions. La première option disponible est vraiment facile à comprendre. Si vous êtes en train d'utiliser Ultimate 3D, il vous est nécessaire de savoir qu'il recherche les textures des modèles dans le dossier "gfx" par défaut. Si vous souhaitez qu'Ultimate 3D les cherche ailleurs, vous pouvez utiliser la fonction suivante, pour donner un nouveau chemin (en incluant le slash de fin).

Cette fonction modifie le dossier dans lequel Ultimate 3D cherche les textures des modèles.

SetTexturePath(
TextureDirectory
)

TextureDirectory
Le dossier contenant les texture, en y incluant le slash à la fin de l'adresse.

La fonction suivante ne change pas vraiment une option, mais désactive le rendu de sortie d'Ultimate 3D pour permettre à Game Maker d'afficher sur la view[0]. Elle s'appelle SwitchView() et ne prend aucun paramètre.

La fonction suivante change la méthode de filtrage utilisée pour le niveau de texturage donné. La méthode de filtrage définie comment Ultimate 3D calculera une couleur d'une texture et une coordonnée de texture. La méthode de filtrage par défaut, est le filtrage linéaire.

Cette fonction définie une nouvelle méthode de filtrage pour le niveau de texturage donné.

SetFilter(
TextureFilter,
TextureStage
)

TextureFilter
Le filtre qui est destiné à être utilisé pour filtrer les textures. Voici une liste des significations des différentes valeurs que vous pouvez entrer pour cet argument:
1: Filtrage par les points les plus proches: La couleur des texels les plus proches seront utilisés.
2: Filtrage Linéaire: La couleur des quatres texels les plus proches sera interpolée pour obtenir un résultat plus lisse.
3: Filtrage Anisotropique: Une méthode de filtrage plus complexe qui peut amener à de meilleurs résultats. Ce n'est pas supporté par certains périphériques graphiques.

TextureStage
Le niveau de texturage pour lequel vous souhaitez définir le filtrage de texture. Cela peut être une valeur entière arbitraire allant de 0 à 7. Par défaut chaque niveau de texture utilise le filtrage linéaire. Si vous passez 1 pour ce paramètre, le filtre de texture spécifié sera utilisé pour afficher les textures DrawTex(en utilisant (...) et DrawTexEx(...)). En définissant un filtrage par les points les plus proches pour l'affichage des textures, vous pouvez éviter une perte de qualité.

La fonction suivante définit combien de mip maps sont à créer pour les textures qui seront chargées. Créer des mip maps consiste à créer des versions de résolution plus basse pour chaque texture qui peut être utilisée, dépendemment de la distance de l'objet qui l'utilise. Chaque mip map aura une résolution moitié moins grande que la résolution de la mip map précédente. (ex.: Un nombre de Mip map définit à trois, donnera: 512*256; 256*128; 128*64).

Cette fonction définit le nombre de mip maps à créer pour les textures qui seront chargées.

SetMipMapCount(
NewMipMapCount
)

NewMipMapCount
Le nouveau nombre de mip maps, et donc de copies à des résolutions plus basses qui doivent être créées pour chacune des textures qui seront chargées.

Vous pouvez aussi spécifier de quelle façon Ultimate 3D doit interpoler les mip maps entre elles. Par défaut le filtrage par le point le plus proche est utilisé à cette fin. Cela signifie que chaque pixel utilise seulement une mip map. Entre les mip maps il y des de changements soudains, qui se voient parfaitements, particulièrement sur les textures détaillées. Vous pouvez dire exactement quand est-ce que le programme doit cesser d'utiliser une mip map et doit en prendre une plus basse. Définir un filtrage linéaire pour interpoler les mip maps entre elles, peut permettre d'éviter cet effet indésirable.

Cette fonction définit le filtre, destiné à être utiliser pour interpoler les mip maps au niveau de texturage donné.

SetMipMapFilter(
TextureFilter,
TextureStage
)

TextureFilter
Le filtre destiné à être utilisé pour l'interpolation des mip maps entre elles. Par défaut, le filtrage par le point le plus proche est activé (1). L'alternative commune est le filtrage linéaire (2). Pour une liste détaillée de tous les filtres disponibles, référez vous à la description de SetFilter(...).

TextureStage
Le niveau de texturage pour lequel vous souhaitez définir le filtre de mip map. Cela peut être un nombre entier arbitraire allant de 0 à 7.

Pour finir, il existe une fonction pour qu'Ultimate 3D passe en mode de rendu fil de fer (wireframe) pour des objets spécifiques. Le rendu en mode fil de fer, consiste à ne calculer que le rendu des contours des triangles. Cette fonctionnalité est prévue pour être utilisée par des choses telles que les editeurs de niveaux. Utiliser le rendu en mode fil de fer pour implémenter un effet ingame, ne rend habituellement pas très bien.

Cette fonction peut être utilisée pour activer ou désactiver le rendu en mode fil de fer pour un modèle, un terrain ou un objet primitif, appelant cette fonction.

SwitchWireFrameMode(
EnableDisable
)

EnableDisable
Si vous passez true pour cet argument, le rendu en mode fil de fer sera activé, sinon, il sera désactivé.

Si vous utiliser Game Maker 6.0 ou supérieur il y a une variable supplémentaire qu'il vous faut connaître. Vous avez pu remarquer qu'Ultimate 3D n'affiche par le curseur, même si l'option correspondante dans Game Maker est activée. La raison de ce comportement est que Game Maker 6.0 ou supérieur (à l'instar de Game Maker 5.x) n'offre pas la possibilité de déterminer si il affiche le curseur ou non. Pour cette raison il vous faut donner l'information manuellement. Cela peut être fait en changeant la valeur de control.show_cursor. En la définissant à true, Ultimate 3D affichera le curseur, en la définissant à false Ultimate 3D désactivera le curseur.


Mise à jour des projets Ultimate 3D

À chaque fois qu'une nouvelle version d'Ultimate 3D est publiée, certains scripts ont changés dans cette version et d'autres peuvent avoir été ajoutés. C'est pourquoi il vous faut les changer dans votre projet si vous voulez mettre à jour un projet vers une nouvelle version d'Ultimate 3D. Ce tutorial vous guide au travers de cette procédure pour chaque version. Il vous suffit de trouver le sous-chapitre qui correspond à votre cas et de suivre les étapes. S'il vous faut mettre à jour une version vraiment ancienne, vous pouvez également enchaîner les étapes de mise à jour, pour parvenir par exemple à mettre à jour un projet Ultimate 3D 2.0 beta 3 vers Ultimate 3D 2.0 final. Etant donné que je fais de mon mieux pour garder une rétro compatibilité, la procédure de mise à jour ne dure, généralement, par plus d'un quart d'heure.

Mise à jour d'un projet Ultimate 3D final vers Ultimate 3D 2.1 beta

Ultimate 3D 2.1 a de nombreuses petites fonctionnalités nouvelles, et un nouveau système de détection de collision basique ainsi qu'un nouveau système de réponse de collision. De ce fait, de nombreux scripts doivent être ajoutés, certains doivent être modifiés et certains (liés à la détection de collision) doivent être supprimés. Vous devrez probablement réécrire l'essentiel de votre code de détection de collision et de réponse de collision, mais avec les nouvelles fonctions c'est vraiment très simple.

Voici une liste de tous les scripts qui doivent être ajoutés. Tous ces scripts sont contenus dans le fichier NewScripts.gml, qui est distribué avec le SDK d'Ultimate 3D 2.1, ainsi vous pouvez importer ce fichier de scripts pour les ajouter automatiquement. (Ndt: Menu Scripts > Import Scripts)

  • SwitchBackgroundMode(...)
  • GetBoneCount()
  • GetBoneParent(...)
  • StartPoseInterpolation(...)
  • CopyAnimation(...)
  • SetModelMaterialSpecular(...)
  • SetObjectSolidity(...)
  • AttemptMoveToPosition(...)
  • CheckSweptEllipsoidIntersection(...)
  • GetTriangleIntersectionCount(...)
  • GetIntersectionTime(...)
  • GetIntersectionPosition(...)
  • GetIntersectedTriangleNormal(...)
  • GetPushAwayVector(...)
  • CheckRayIntersection(...)
  • GetIntersectedMaterialIndex(...)
  • GetTextureStrengthAtPosition(...)
  • DeformTerrain(...)
  • CreateTerrainDecal(...)
  • SetParticleRotation(...)
  • SwitchAdditiveBlending(...)
  • SetRoomVisibilityEnforcement(...)
  • SetRoomAmbient(...)
  • GetMeshOccurrenceCount(...)
  • GetMeshOccurrenceTransformation(...)
  • InterpolateMatrices(...)
  • GetMatrixEntry(...)
  • CoordToScreen(...)
  • CheckBoundingBoxVisibility(...)
  • CalculateVectorDifference(...)
  • CalculateVectorLongitude(...)
  • CalculateVectorLatitude(...)
  • ComputeMatrixTranslation(...)
  • ComputeMatrixScaling(...)
  • ApplyTransformationMatrix(...)
  • GetDrawnTriangleCount()
  • GetDrawCallCount()
  • SetMipMapFilter(...)

Voici une liste de tous les scripts qui ont besoin d'être remplacés par de nouvelles versions:

  • Step()
  • SetAmbient(...)
  • LightPoint()
  • LightDirectional()
  • LightSpot()
  • SmoothNormals(...)
  • SetModelMaterialEmissive(...)
  • CreateTerrain()
  • AddTerrainTexture(...)
  • CreateParticleSystem()
  • ParticleSystem Step(...)
  • MoveCamera()
  • SwitchWireFrameMode(...)
  • Init()
  • InitializeLightVariables() (Game Maker 6.0 et versions supérieures seulement)

Pour certaines de ces fonctions leur utilisation a également changée. SmoothNormals(...) et AddTerrainTexture(...) on un paramètre supplémentaire désormais, mais si vous ignorez ce paramètre des valeurs par défaut raisonnables seront utilisées. SwitchWireFrameMode(...) n'active ou ne désactive plus tout les objets de la scène. Maintenant cette fonction active ou désactive le rendu en mode fil de fer pour tous les objets de la scène. Maintenant elle l'active ou le désactive uniquement pour l'objet qui appelle cette fonction. Assurez vous de bien faire attention à ces modifications d'interface, lorsque vous mettez à jour votre projet. Enfin, voici une liste des scripts qui doivent être supprimmés:

  • GetDistanceToModel(...)
  • GetDistanceToPrimitive(...)
  • GetDistanceToAnyPrimitive(...)
  • GetDistanceToTerrain(...)
  • CoordToScreenX(...)
  • CoordToScreenY(...)
  • CoordToScreenZ(...)

Ne soyez pas choqués par cela toutefois. Il y a toujours une fonction pour effectuer un test d'intersection ray-mesh pour tous ces types d'objets et c'est même plus souple que ces vieilles fonctions, étant donné qu'elle peut automatiquement faire le test de ray-tracing pour un ensemble d'objets solides prédéfinis. La nouvelle fonction est nommée CheckRayIntersection(...). Quoi qu'il en soit, pour la détection de collision et pour la réponse de collision, il y a une solution encore meilleure désormais. Vous pouvez en lire plus dans le chapitre réécrit du fichier d'aide à ce sujet. C'est un peu plus fiable, facile à utiliser et dans les bonnes circonstances, bien plus efficace que l'ancienne méthode.

CoordToScreenX/Y/Z(...) a été remplacée désormais par une version basée sur les vecteurs appellée CoordToScreen(...). Plutôt que d'appeler trois fonctions différentes, vous en appellez seulement une, qui retourne désormais un vecteur.

A part tout cela, il y a quelques changements vraiments mineurs. Si vous souhaitez rendre la room zéro invisible il vous faut utiliser SetRoomVisibilityEnforcement(...) à la place de SetRoomVisibility(...) maintenant. Les height maps des terrains sont interprétées différemments désormais, pour ajouter le support des height maps 32-bit. Si votre height map de terrain n'est pas exactement en niveaux de gris vous obtiendrez des résultats indésirés. Le canal rouge de la height map a une influence très importante sur les résultats désormais. Si vos heights maps sont de parfaits niveaux de gris, vous n'aurez aucun problème avec la nouvelle façon dont les données sont interprétées.

Une dernière chose, qui doit être dite ici, est que l'ordre des matrices des tangentes inversées de l'espace a changé. Dans Ultimate 3D 2.0 final, les normales étaient écrasées par la deuxième colonne des matrices et le troisième ensemble de coordonnées de textures était écrasé par la troisième colonne. Désormais les normales sont écrasées par la troisième colonne et le troisième ensemble de coordonnées de textures est écrasé par la deuxième colonne. Le grand avantage dans tout cela, est que la troisième colonne des matrices des tangentes inversées de l'espace, égale habituellement, les normales. C'est grâce à cela que vous pouvez, désormais, avoir des matériaux qui utilisent l'éclairage par pixel et d'autres qui ne l'utilisent pas sur un même mesh en même temps. Malgré cela, vous n'êtes pas obligés d'appeler RecalculateNormals() si vous désactivez l'éclairage par pixel. Cela signifie, que si vous utilisez des vertex shaders, qui utilisent les matrices des tangentes inversées de l'espace, vous devez inverser v3 et v9. Si vous avez exporté les fichiers de modèle Ultimate 3D avec les matrices des tangentes inversées de l'espace, avec les versions récentes d'Ultimate 3D 2.0, vous devez soit, les réexporter, soit appeler AddInverseTangentSpaceMatrices() après les avoir chargés.

Mise à jour d'un projet Ultimate 3D 2.0 RC vers Ultimate 3D 2.0 final

Etant donné qu'Ultimate 3D 2.0 final n'a aucune nouvelle fonctionnalité, les seuls scripts qu'il vous faut mettre à jour sont GetObjectTransformation() et Destroy(). Quoi qu'il en soit, il est recommandé de mettre à jour / d'ajouter également les scripts suivants si vous utilisez Game Maker 6.0 ou plus. Cela vous permettra d'utiliser Ultimate 3D sans l'option de Game Maker "Treat uninitialized variables as value 0". Pour parvenir à cette fin, les scripts suivants devront êtres mis à jour:

  • CreateWall()
  • CreateFloor()
  • CreateCube()
  • Create2D()
  • CreatePolygon()
  • LightDirectional()
  • LightPoint()
  • LightSpot()
  • LoadMesh()
  • CreatePrimitiveFromModel()
  • CreateEmptyMesh()
  • ApplyParallaxAndBumpMapping()
  • SetMaterialEffectLightSource()
  • CreateTerrain()
  • CalculateTerrainLightMap(...)
  • CreateParticleSystem()
  • MoveCamera()
  • CreatePostScreenShader()
  • Init()

    Malgré cela, les quatres scripts suivants devront êtres ajoutés au groupe "Private functions":

  • InitializePrimitiveVariables()
  • InitializeLightVariables()
  • InitializeVariable()
  • InitializeVariableGlobal()

Une autre mise à jour facultative inclut quelques changements au code de l'objet "control". Le code dans l'évènement destroy et dans l'évènement game end devrait être mis à jour et le code dans l'évènement room end devrait être ajouté pour permettre à Ultimate 3D de fonctionner proprement lors du changement de rooms.

Mise à jour d'un projet Ultimate 3D 2.0 beta 3 vers Ultimate 3D 2.0 RC

Ultimate 3D 2.0 RC est ce qu'Ultimate 3D 2.0 était originalement sensé être. Etant donné que le développement d'Ultimate 3D 2.0 pris beaucoup plus de temps que prévu, les versions beta furent rendues publiques avec certaines fonctionalités manquantes, dans l'idée de réduire les délai de publication. Elles ont désormais été ajoutées. Pour être à même de les utiliser, il vous faudra mettre un peu à jour votre projet Game Maker. Prenez donc cinq minutes pour lire ce chapitre, et prenez en dix autres pour faire ce qu'il dit, et vous pourrez utiliser Ultimate 3D 2.0 RC avec toutes ses nouvelles fonctionnalités.

La première chose que vous aurez à faire est de télécharger la version la plus récente du SDK sur Ultimate3D.org (Ndt: Sur http://u3d.gamemaker.fr/ vous pourrez également les retrouver). Une fois que vous l'avez, vous pouvez remplacer la dll Ultimate3D.dll de votre projet Ultimate 3D 2.0 beta 3 dans votre projet, avec celle de la version release candidate (Ndt: RC). Après cela, plus rien ne fonctionnera. D'abord vous devez remplacer un certain nombre de scripts par leur version actuelle. Ouvrez simplement le vieux BasicGM5.gmd (respectivement BasicGM6.gm6 ou BasicGM7.gmk) et votre projet Game Maker, ouvrez les scripts qui doivent être remplacés, et copiez leur nouveau contenu depuis la nouvelle version vers l'ancienne. Vous avez ici une liste de tous les scripts qui doivent être remplacés par les nouvelles versions:

  • CreatePrimitiveFromModel(...)
  • PreloadMesh(...)
  • LoadMesh()
  • AddFrame(...)
  • Step()
  • Destroy()
  • Init()
  • SetFilter()
  • GetBoneTransformation()
  • CreateTerrain()
  • DrawText()
  • CreateCube()
  • DrawTex()
  • DrawTexEx()
  • RenderToCubeTexture()
  • GetObjectTransformation()
  • MoveCamera()
  • SetObjectRoom()

Ensuite il vous faut ajouter les nouveaux scripts, pour être à même d'utiliser les nouvelles fonctionnalités. Encore une fois, en voici une liste.

  • ExportToU3DFile(...)
  • SwitchAlphaBlending(...)
  • SetBoneFrame(...)
  • GetDistanceToTerrain(...)
  • SetCustomTerrainLightMap(...)
  • GetClippingPlaneSupport()
  • SwitchWireFrameMode(...)
  • UpdateSkeleton()
  • ReleaseModel(...)
  • AddTextureCoordinateSetsFromModel(...)
  • ComputeMatrixRotationAngles(...)
  • GetRayTracingNormal(...)
  • ScreenCoordToVector(...)
  • Tous ceux dans le groupe "Shadow and post screen shader functions":
    • GetShadowSupport()
    • SwitchShadows(...)
    • SwitchShadowCasting(...)
    • CreateShadowOptimizedGeometry(...)
    • SwitchShadowReceiving(...)
    • CreatePostScreenShader()
    • SetPSSTexture(...)
    • SetPSSVSConstant(...)
    • SetPSSPSConstant(...)
    • SetPSSFilter(...)
  • Les fonctions du moteur de portails:
    • SwitchPortalEngine(...)
    • GetObjectRoom()
    • GetRoomVisibility(...)
    • CreatePortal(...)
    • SetPortal(...)
    • OpenPortal(...)
    • DestroyPortal(...)

Tous ces scripts sont disponibles dans un fichier de scripts Game Maker exportés, qui est livré avec le SDK. L'utiliser peut vous permettre d'économiser du temps en les ajoutants. Dependemment du fichier sur lequel vous avez basé votre travail, vous pourrez avoir besoin d'ajouter GetBoneTransformation(...), parce que ce script était manquant dans certains des fichiers Game Maker qui vinrent avec Ultimate 3D 2.0 beta 3. Aussi vous devriez supprimmer LoadAnimated3DS(), car il s'agit d'un reste obsolète de versions plus anciennes d'Ultimate 3D. Cela ne change rien, mais appellez LoadMesh() quoiqu'il en soit. Une fois que vous aurez fait toutes ces choses votre projet Ultimate 3D devrait fonctionner correctement et vous pourrez commencer à utiliser les fonctionnalités d'Ultimate 3D 2.0 RC.

Mise à jour d'un projet Ultimate 3D 1.31 vers Ultimate 3D 2.0 beta 1

Ultimate 3D 2.0 est une refonte complète d'Ultimate 3D. Il n'y pas une seule ligne de code qui soit la même que dans Ultimate 3D 1.31. C'est pourquoi, il n'était pas possible de garder tout pareil. Il y a un ensemble de choses qui ont changées parce que les anciennes solutions, n'étaient par suffisamment ergonomique et intuitive, un ensemble de choses qui ont changées parce que les nouvelles fonctionnalités requéraient ces changements et un ensemble de choses qui ont simplement étés supprimmées pour améliorer les performances d'Ultimate 3D. Ce texte listera tous ces changements et donnera une description détaillée étape par étape, sur comment mettre à jour les projets Ultimate 3D 1.31 vers Ultimate 3D 2.0. Cela prend une heure ou deux de convertir votre projet.

En ce qui concerne les fonctionnalités qui font partie de la section novice d'Ultimate 3D, quasiment rien n'a changé. Les seuls changements sont les objets cubique qui sont définis différemments et les lumières qui sont calculées d'une manière plus naturelle. Quelques variables ont également été ajoutées pour les objets primitifs et LoadAnimated3DS() n'existe plus (c'est LoadMesh() désormais). La signification des variables rotx, roty et rotz a changé pour les objets de caméra et pour les sources de lumière. Ces variables fonctionnent désormais exactement de la même manière que les variables correspondantes des objets primitifs et de modèles. Aussi, vous ne pouvez plus utiliser de vertex shaders pour les objets primitifs, ce fut un changement nécessaire pour permettre un calcul de rendu des primitifs bein plus rapide.

La plupart des fonctionnalités de la section avancée n'existaient pas dans Ultimate 3D 1.31, donc il n'y pas grand chose qui pourrait avoir changé. Les système d'effets de particules est complètement neuf et les paramètres des fonctions de ray tracing ont un peu changées, du fait du changement des significations des variables rotx et roty pour les objets de caméra. Elles fonctionnent toutes de la même façon désormais. Cela signifie qu'une valeur de 90 pour rotx ferait pointer la caméra droit vers le bas. Mais venons en à la conversion des projets.

Première étape: Remplacer et ajouter tous les scripts

En étant bien conscient que cette partie est vraiment ennuyeuse et prendra beaucoup de temps. Mais lorsque vous en aurez fini avec cela, votre projet sera quasiment prêt pour Ultimate 3D 2.0. Je conseille d'ouvrir le nouveau BasicGM5.gmd/gm6 et votre projet Ultimate 3D 1.31 dans deux instances de Game Maker et de placer leurs deux fenêtres à la moitié gauche et à la moitié droite de l'écran. La première chose que vous avez à faire pour avoir les nouveaux script dans votre projet est de supprimmer les scripts anciens suivants qui n'existent plus désormais:

  • CreateParticleSystem()
  • DestroyParticle(...)
  • GetParticleColorR/G/B/A(...)
  • GetParticleNumber(...)
  • GetParticlePosX/Y/Z(...)
  • GetParticleSize(...)
  • GetVSSupport()
  • LoadAnimated3DS()
  • LoadVS(...)
  • SetParticleColor(...)
  • SetParticleNumber(...)
  • SetParticlePos(...)
  • SetParticleColorEnd(...)
  • SetParticleColorStart(...)
  • SetParticleSize(...)
  • SetParticlesLifetime(...)
  • SetParticlesMovement(...)
  • SetParticlesMovementInc(...)
  • SetParticlesMovementOut(...)
  • SetParticleSystemBase(...)
  • SetViewport(...)
  • SetVS(...)
  • SetVSConstant(...)

Ensuite il vous faut renommer les scripts suivants avec les nouveaux noms donnés:

  • collision_h() en CollisionH()
  • collision_model() en CollisionTerrainModel()
  • collision_v() en CollisionV()
  • collision_z() en CollisionZ()
  • GetMaterialNumber(...) en GetMaterialIndex(...)
  • move(...) en Move(...)
  • SetMaterialSphereMap(...) en SetMaterialEnvironmentMap(...)
  • Snapshot(...) en TakeScreenshot(...)
  • SnapshotEx(...) en TakeScreenshotEx(...)

Quand vous en avez fini avec ces changements vous devriez essayer de lancer votre jeu une fois en utilisant l'ancienne dll d'Ultimate 3D 1.31, afin de voir quelles parties du programme devront êtres changées ou refaites. Si vous avez des systèmes de particule fait avec Ultimate 3D 1.31 il sera plus facile de le supprimer et de les refaire complètement. Si vous utilisez les vertex shaders pour les objets primitifs, il vous faudra trouver un moyen d'obtenir le même effet sans les shaders, ou vous devrez remplacer les objets primitifs avec des objets de modèle. Lorsque vous en avez fini avec cela, le jeu devrait fonctionner.

Maintenant, la partie vraiment très ennuyeuse arrive. Vous devez ouvrir chacun des scripts dans le nouveau fichier BasicGM5.gmd/gm6 copier son contenu, et remplacer le contenu du script correspondant dans votre projet avec ce code. Si le script ne fait pas partie de l'ancienne version d'Ultimate 3D vous devrez le créer. Vous devriez refaire la structure des groupes également. Ce n'est pas tant de travail de les créer, et cela rendra tout un peu plus simple.

Deuxième étape: changer les objets

Lorsque vous avez fini de remplacer et d'ajouter tous les scripts vous pouvez démarrer le jeu en utilisant la nouvelle dll, mais le résultat ne sera pas très motivant. Vous ne verrez rien du tout. Mais ne vous inquiétez pas, il est très facile de résoudre ce souci. Vous devez simplement changer appels de fonction qui sont fait par l'objet de control. Dans l'évènement end-step vous trouverez un appel à MoveCamera() et un appel à la fonction Game Maker external_call(...). Il vous faut remplacer ces deux appels avec le morceau de code suivant, après avoir supprimé l'évaluation "if" qui est au dessus de ces appels:

external_call(global.u3d_transmit_controller_information,BG_r,BG_g,BG_b,show_cursor);
external_call(global.u3d_render);

Dans l'évènement begin-step, vous devez à nouveau supprimmer l'évalation "if" et vous devez remplacer l'appel à external_call(...) avec l'appel suivant:

external_call(global.u3d_present);

Vous devez également remplacer les choses de l'évènement destroy et de l'évènement game end avec le morceau de code suivant:

external_call(global.u3d_cleanup);

Quand vous en avez fini avec ces changements, vous pouvez à nouveau lancer le jeu et il devrait y avoir quelques résultats visibles. Mais certaines choses peuvent être un peu étranges. La caméra ne se comportera probablement pas de la manière désirée et les détections de collision ne fonctionneront pas correctement. Cela est dû au changement de la signification des variables rotx, roty et rotz et des paramètres de GetDistanceToModel(...) et de Move(...). Il n'y a pas de truc avec lequel je pourrai vous aider dans ce contexte. Vous devez simplement rechercher les codes qui utilisent ces variables ou ces fonctions et les corriger. Dans de nombreux cas, ajouter quelques "moins" sera tout ce que vous aurez à faire. Une autre chose que vous devez faire, consiste à mettre à jour tous les objets cubiques que vous pouvez avoir. Pour voir comment ils fonctionnent désormais, référez vous s'il vous plaît, au chapitre à propos des objets primitifs.

Il y a aussi un ensemble de très petit changements:

  • control.range a changé en camera.max_range
  • global.fall et global.speed (les variables qui peuvent êtres utilisées pour faire sauter ou tomber la caméra) ont changées en camera.falling and camera.falling_speed
  • global.x et global.y (les variables de sortie de GetMousePos(...)) ont changées en global.mouse_x et global.mouse_y
  • L'Environment mapping additive fonctionne désormais. Cela signifie qu'un environment map noir ne devrait rien changer du tout, tant qu'un environment map blanc éclairera tout très fortement.
  • Le paramètre Alpha de DrawTex(...) prend désormais des valeurs allant de 0 à 255 à la place de valeurs allant de 0 à 1.

Quand vous en avez fini avec ces changements tout devrait fonctionner et votre projet est prêt pour être utilisé avec Ultimate 3D 2.0. S'il reste encore des problèmes, postez quelque part sur la Communauté d'Ultimate 3D.

Convertir un projet Ultimate 3D de Game Maker 5.x à Game Maker 6.0 ou supérieur

En raison du manque de rétro-compatibilité de Game Maker 6.0 vous devez mettre certains scripts à jour lorsque vous convertissez un projet Ultimate 3D de Game Maker 5.x à Game Maker 6.0 ou supérieur. Malgré que de nombreux scripts diffèrent, parce que la version Game Maker 6.0 d'Ultimate 3D est faite pour fonctionner sans l'option "Treat uninitialized variable as value 0". Cex scripts là, doivent être remplacés par les versions correctes, également. Voici une liste de tous les scripts qui doivent êtres remplacés par leur version correspondante du fichier BasicGM6.gm6 (respectivement BasicGM7.gmk):

  • MoveCamera()
  • SetSoundPosition()
  • Init()
  • CreateWall()
  • CreateFloor()
  • CreateCube()
  • Create2D()
  • CreatePolygon()
  • LightDirectional()
  • LightPoint()
  • LightSpot()
  • LoadMesh()
  • CreatePrimitiveFromModel()
  • CreateEmptyMesh()
  • ApplyParallaxAndBumpMapping()
  • SetMaterialEffectLightSource()
  • CreateTerrain()
  • CalculateTerrainLightMap(...)
  • CreateTerrainDecal()
  • CreateParticleSystem()
  • CreatePostScreenShader()

En outre, les quatres scripts suivants devront être ajoutés au groupe "Private functions":

  • InitializePrimitiveVariables()
  • InitializeLightVariables()
  • InitializeVariable()
  • InitializeVariableGlobal()

Malgré cela, certains codes dans les objets devront être modifiés. Les morceaux de code dans le fichier BasicGM5.gmd qui ont besoins d'être modifiés pour fonctionner avec Game Maker 6.0 ou supérieur font partie de l'évènement room end de l'objet de contrôle et de l'évènement step de la caméra.


Epilogue - Ecrit par MysteriXYZ

Pourquoi devriez-vous lire ce manuel à fond ...

Ainsi vous avez un peu joué avec Ultimate 3D, mais malheureusement, vous êtes tombé sur un problème que vous ne pouvez simplement pas résoudre. Mais que faire? Le forum d'Ultimate 3D est probablement la première chose qui vous vient à l'esprit. Et nous vous encourageons, en effet, à devenir un membre de notre communauté sympathique, car c'est le meilleur endroit pour s'entraider avec tout ce qui est lié à Ultimate 3D. Et après tout le remue-méninges, vous pouvez même vous défouler dans notre section hors sujets remplie de choses amusantes.

Mais si votre seule raison de vous inscrire est d'obtenir des réponses rapides que vous pouvez de toutes façons facilement trouver dans ces chapitres, alors ne soyez pas surpris si vous ne faites pas bonne impression ici.

Bien, donc vous préférez avoir les membres de la communauté de votre côté, cependant être obligé de parcourir des pages et des pages de texte n'est pas exactement votre façon, à vous, de vous amuser. Par chance, il y a la liste de fonctions qui existe, à travers laquelle vous pouvez naviguer, et il y a certainement quelque chose dedans qui pourrait bien être la solution de votre problème. Ainsi, en lisant la description d'une fonction particulière, vous en viendrez à la conclusion que c'est seulement ce dont vous avez besoin. Oh oui bien sûr, certaines choses étaient mentionnées que vous n'aviez pas bien comprises - probablement expliquées dans un chapitre précédent ou quelque chose comme ça - mais cela vous aurait distrait de ce quoi vous êtiez en train d'accomplir juste à l'instant, de telle manière que cela ne pouvait attendre. Plein d'enthousiasme vous essayez et... oops, quelque chose va horriblement mal. Cela doit être un bug! Fâché, vous vous connectez sur le forum et parce que vous souhaitez partager votre mécontentement avec tout le monde, vous démarrez un nouveau topic et demandez à savoir ce qu'il se passe. Après un ensemble de réponses éclairantes, vous réalisez qu'il aurait été bien plus simple (et moins embarassant) de lire simplement ces informations complémentaires dans ce manuel.

Gardez simplement à l'esprit, qu'avec le temps qu'il vous aura fallu pour inventer de nouveaux prétextes et excuses de ne pas lire ce fichier d'aide, vous auriez pu en lire un chapitre supplémentaire... ;) . Une bonne compréhension de cette documentation peut vous permettre de gagner beaucoup en efficacité d'utilisation d'Ultimate 3D. Maintenant jetez-vous, et prenez beaucoup de plaisir avec l'un des moteurs 3D les plus faciles à utiliser, que vous êtes susceptible, de ne jamais plus rencontrer!


Ultimate 3D 2.1 Package d'extensions Game Maker

Le package d'extension pour Ultimate 3D, qui peut être obtenu depuis Ultimate3D.org (Ndt : http://u3d.gamemaker.fr/download/U3DGEX.zip), offre toutes les fonctionnalités requises pour utiliser Ultimate 3D avec Game Maker 7.0. Pour être plus précis il contient la dll Ultimate3D.dll, toutes les fonctions qui sont expliquées dans ce fichier d'aide et deux librairies d'actions. Son utilisation peut faciliter le travail avec Ultimate 3D et en rend l'utilisation plus confortable.

Avertissement: Le package d'extension est vraiment nouveau et, par conséquent, il n'a pas subi une longue phase de beta test, à l'instar de la version normale d'Ultimate 3D. Bein que j'ai testé tout les composants du package avec soin, il est probablement plus sûr d'utiliser la version normale. Vous devriez aussi noter que les gens qui n'ont pas installé l'extension ne seront pas à même de voir vos fichiers de projet correctement, si vous l'avez installée.

Premier pas avec l'extension

Il y a de nombreuses choses dont Ultimate 3D a toujours besoin, qui ne peuvent êtres incluses dans le package d'extension. Pour cette raison le package d'extension est distribué avec un BasicGEX.gmk, qui contient ces choses. Il est évident que la première chose que vous devez faire pour être à même d'utiliser l'extension est de l'installer, au travers de l'extension package manager de Game Maker. Une fois que ce sera fait, le BasicGEX.gmk fonctionnera et affichera les mêmes choses que BasicGM7.gmk du SDK. Vous devriez baser vos projets sur ce fichier. Si vous ne souhaitez pas faire ainsi, il vous faudra copier le script Ultimate3DOptions() depuis le fichier BasicGEX.gmk vers votre projet et vous devrez définir la première room correctement (comme expliqué dans Commençons par le début).

Les librairies d'actions

Les librairies d'actions offrent un accès à la plupart des fonctionnalités basiques de la partie novice d'Ultimate 3D. Ne vous attendez pas à aller très loin avec elles. Utiliser ces librairies intuitives et ergonomiques ne compense pas une étude approfondie de ce fichier d'aide. Ne vous attendez pas à être à même d'utiliser Ultimate 3D sans être capable d'écrire du code en GML. Les librairies d'action peuvent vous économiser certains travaux qui sont requis vraiment souvent (comme Step() et Destroy()), mais elles ne sont pas pensées pour permettre l'accès à toutes les fonctionnalités d'Ultimate 3D et elles ne doivent pas le faire.

Il y a deux librairies, la première nommée U3D main, et la seconde nommée U3D misc. U3D main contient de nombreuses actions pour gérer les objets, les créer et changer l'environnement. U3D misc. contient des actions pour modifier et transformer les objets, pour dessiner des graphismes 2D et bien plus encore. Ce qui suit est une explication de chaque action.

U3D main

Load texture
Cette action charge une texture à partir d'un fichier donné et l'associe avec un indice de texture. Pour plus d'informations consulter la description de LoadTexture(...).

Step
Ultimate 3D associe des objets Ultimate 3D (ex: un seul mur, un seul panneau ou un seul modèle) avec les instances des objets Game Maker. Ces objets Game Maker ont de nombreux variables qui peuvent être utilisées pour modifier les objets Ultimate 3D. Cette action transmet les données de ces variables à Ultimate 3D. Elle devrait être placée dans l'évènement step pour chacun des objets Game Maker, qui sont associés avec un objet Ultimate 3D, à moins que ce ne soit une caméra. Cette action correspond à Step().

Destroy
Cela détruit l'objet Ultimate 3D qui est associé avec l'objet Game Maker. Afin que les objets Ultimate 3D soient supprimés lorsque les instances qui y sont associées le sont, vous devriez placer cela dans l'évènement destroy de chacun des objets Game Maker qui est associé avec un objet Ultimate 3D. Cette action correspond à Destroy().

Associate with primary camera
Défini certaines variables et associe cet objet avec la caméra primaire (1ère). Pour être plus précis, cette action définie les variables height et view au valeurs données, les variables de viewport (portée de la vue) en correspondance avec celles de la vue primaire de Game Maker, min_range à 1, max_range à 100000 et number à 0 et fini par appeller MoveCamera(...).

Camera step
Cette action est l'équivalent de step pour les caméras. Elle transmet toutes les données, qui sont sauvées dans des variables, de l'instance de Game Maker à l'objet de caméra d'Ultimate 3D. Elle doit être placée dans l'évènement step d'un objet caméra. Cette action correspond à MoveCamera().

Create wall
Cette action crée un mur de la position x, y, z à la position x + x2, y + y2, z+height avec la texture portant l'indice donné et l'associe avec cette instance de Game Maker. Il fait cela en définissant les variables x2, y2, height et texture et en appellant CreateWall() pour finir.

Create floor
Cette action crée un mur depuis la position x, y, z à la position x+width, y+height, z avec la texture portant l'indice donné, et associe ce mur avec cette instance de Game Maker. Elle fait cela en définissant les variables width, height et texture puis en appellant CreateFloor().

Create cube
Cette action crée un cube à partir de la position x, y, z jusqu'à la position x+width, y+height, z+depth avec la texture définie à l'indice donné et associe ce cube, à cette instance de Game Maker. Elle fait cela en définissant les variables width, height, depth2 et texture, puis en finissant par appeller CreateCube().

Create billboard
Cette action crée un panneau (billboard), avec x, y, z pour origine, avec la largeur et la hauteur donnée, l'origine relative donnée du panneau et la texture avec l'indice donné et l'associe avec cette instance de Game Maker. Elle le fait en définissant les variables width, height, originx, originy et texture et en finissant par appeler Create2D().

Create model
Cette action crée un objet de modèle à l'aide du fichier *.3ds, *.an8, *.md2, *.ms3d, *.u3d ou *.x donné et l'associe avec cette instance de Game Maker. Elle fait cela en définissant la variable file et en appellant LoadMesh().

Create light directional
Cette action crée une source de lumière directionnelle avec la direction d'éclairage et la couleur de lumière donnée et associe cette source de lumière avec cette instance de Game Maker. Elle le fait en définissant les variables r, g, b, rotx et roty et en appellant LightDirectional().

Create light point
Cette action crée un point lumineux avec la portée et la couleur donnée, puis l'associe avec cette instance de Game Maker. Elle le fait en définissant les variables r, g, b et range, puis en appellant LightPoint().

Create spot light
Cette action crée une spot light, avec les direction d'éclairage donnée, la portée, l'angle d'éclairage et la couleur de lumière donnée, et l'associe avec l'instance actuelle de Game Maker. Elle se charge de cela en définissant les variables r, g, b, range, rotx, roty ainsi que deg, et en finissant par appeller LightPoint().

Set ambient lighting
Cette action modifie l'intensité et la couleur de l'éclairage ambiant. L'éclairage ambiant affecte tous les objets avec la même force, indépendamment de leur position. Cette action correspond à SetAmbient(...).

Set fog properties
Cette action met en place un brouillard avec la couleur donnée, qui débute à la distance de départ donnée et fini à la distance de fin donnée. À la distance de fin, tout aura la couleur du brouillard. C'est action correspond à SetFog(...).

Disable fog
Cette action désactive complètement le brouillard. Elle correspond à DisableFog().

Set solid background color
Cette action définit une couleur de background solide et désactive le skysphere/skycube, dans la mesure où ils étaient utilisés avant cela.

Load sky sphere
Cette action charge un skysphere d'un fichier image donné et l'utilise comme background. Cela correspond à LoadBackground(...).

U3D misc

Create instance 3D
Cette action étent l'action de création d'instance de Game Maker avec un paramètre additionel vous permettant de spécifier la position z de l'objet. Si relative est coché, les coordonnées seront interprétées de manière relative aux coordonnées de l'instance, cela signifie qu'elles seront additionnées.

Move through 3D angles
Cette action déplace l'objet dans l'espace en 3D en utilisant les angles. Pour plus d'informations à ce propos, lisez la description de la fonction Move(...).

Move through 3D coordinates
Cette action déplace l'objet dans l'espace en 3D en utilisant les trois coordonnées données. Si relative n'est pas coché l'objet sera défini à la position donnée, autrement les coordonnées données seront ajoutées aux coordonnées actuelles. Cette action n'affectera pas l'objet Ultimate 3D, à moins que l'action step soit placée dans l'évènement step.

Rotate using Euler angles
Cette action fait pivoter l'objet en utilisant les trois angles d'Euler donnés. Si relative n'est pas coché les variables rotx, roty et rotz de l'objet sont définies au valeurs données, autrement les angles donnés sont additionnés à rotx, roty et rotz. Cette action n'affectera pas l'objet Ultimate 3D tant que l'action step ne sera pas placée dans l'évènement step.

Scale object
Cette action met l'objet à l'échelle avec le facteur donné. Tant que relative n'est pas coché scalx, scaly et scalz seront définies à la valeur spécifiée, autrement scalx, scaly et scalz seront multipliées par les valeurs données. Cette action n'affectera pas l'objet Ultimate 3D tant que l'action step ne sera pas placée dans l'évènement step.

Set animation speed and animation area
Cette action définit la vitesse de l'animation, ainsi que les frames de début et de fin du cycle d'animation. La frame de début et de fin sont optionnelles, vous pouvez donc avoir un 0 ici si vous souhaiter jouer l'animation en entier. Tant que relative n'est pas coché la variable frame est définie à la valeur négative de la vitesse d'animation donnée, autrement la variable frame est soustraite par la vitesse d'animation. Dans les deux cas les variables first_frame et last_frame sont définies aux valeurs données. Cette action n'affectera pas l'objet Ultimate 3D si l'action step n'est pas présente dans l'évènement step.

Draws a texture to the screen
Cette action dessine la texture avec l'indice donné à la position spécifiée et en utilisant l'échelle, la rotation et la transparence donnée. Pour plus d'informations référez vous à la description de DrawTex(...).

Load and prepare true type font
Cette action charge les polices true type avec le nom donné et les prépare pour le dessin rapide et efficace du texte. La police résultante sera associée avec l'indice donné et aura la taille de texte spécifiée, sera en gras ou en italique si cela est demandé et aura la couleur donnée. Pour plus d'information référez vous à la description de SetFont(...).

Use a prepared true type font to draw text
Utilise la police avec l'indice donné pour affiche le texte donné à la position spécifiée. Pour plus d'information jetez un oeil à la description de DrawText(...).

Take screenshot
Prend une capture d'écran de la vue d'Ultimate 3D et l'enregistre dans le fichier bitmap donné.


Traduction

Traduction Française de la documentation d'Ultimate 3D

Crédits

Nous sommes heureux de vous présenter la traduction de la documentation complète d'Ultimate 3D qui, nous l'espèrons, vous permettra de développer des jeux d'exception et de qualité avec ce moteur robuste, fiable, facile à appréhender et sans limites !

A la base, cette traduction est une idée excellente de Franck, qui est venu vers moi pour l'aider dans cette tâche longue et compliquée. En effet, traduire une telle documentation en utilisant des termes qui n'ont jamais été pensés pour être exprimés en Français n'est pas une mince affaire. C'est ainsi que nous avons traduits ensemble et que nous nous sommes recorrigés dans l'idée de vous fournir une documentation de qualité, cohérente et utile.

Toutefois, si vous trouviez des erreurs, nous vous invitons à nous en faire part au plus vite, afin que nous puissions mettre à jour la documentation pour une qualité optimale.

A présent, nous vous invitons tous et toutes à profiter au maximum de cette documentation en fançais et nous vous souhaitons beaucoup de plaisir à créer des jeux avec GameMaker & Ultimate 3d.

Franck & daminetreg

Rapporter une erreur

Pour effectuer cette démarche de contribution, merci d'utiliser ce formulaire.


Function list

AddFrame(File,Password)

AddGravityEmitter(PositionX, PositionY, PositionZ,GravityEmitterMass)

AddInverseTangentSpaceMatrices()

AddModifier()

AddTerrainTexture(DiffuseMap,AlphaMap,WrapCountX, WrapCountY,AlphaMapChannel)

AddTextureCoordinateSet(DimensionCount)

AddTextureCoordinateSetsFromModel(ModelFile, Password)

AddVertex(X,Y,Z,U,V)

ApplyCelShading(LineWidth,LineColorR, LineColorG, LineColorB, LineColorA)

ApplyParallaxAndBumpMapping(MaterialIndex,HeightMapIndex,ParallaxFactor,LightSourceID1,LightSourceID2,LightSourceID3)

ApplyTerrainTextures()

ApplyTransformationMatrix(InputMatrixID)

AttemptMoveToPosition(OutputVectorID,ObjectID,StartX,StartY,StartZ,DestinationX,DestinationY,DestinationZ,RotX,RotY,RotZ,ScalingX,ScalingY,ScalingZ,RoomIndex)

BeginPolygon(VertexNumber)

CalculateCrossProduct(OutputVectorID,InputVectorID1, InputVectorID2)

CalculateDotProduct(InputVectorID1, InputVectorID2)

CalculateTerrainLightMap(LightSourceID1, LightSourceID2, , LightSourceID10)

CalculateVectorDifference(OutputVectorID,InputVectorID1, InputVectorID2)

CalculateVectorLatitude(OutputVectorID)

CalculateVectorLength(VectorID)

CalculateVectorLongitude(OutputVectorID)

CalculateVectorScalarProduct(OutputVectorID,InputVectorID,ScalarFactor)

CalculateVectorSum(OutputVectorID,InputVectorID1, InputVectorID2)

CheckBoundingBoxVisibility(BoundingBoxMinimumVectorID, BoundingBoxMaximumVectorID,BoundingBoxTransformationID,CameraIndex)

CheckRayIntersection(ObjectID,RayOriginX,RayOriginY,RayOriginZ,RayDirectionLongitude,RayDirectionLatitude,RoomIndex)

CheckSweptEllipsoidIntersection(ObjectID,StartX,StartY,StartZ,DestinationX,DestinationY,DestinationZ,RotX,RotY,RotZ,ScalingX,ScalingY,ScalingZ,RoomIndex)

ComputeMatrixRotationAngles(OutputVectorID,InputMatrixID)

ComputeMatrixScaling(OutputVectorID,InputMatrixID)

ComputeMatrixTranslation(OutputVectorID,InputMatrixID)

CoordToScreen(OutputVector,X, Y, Z,CameraIndex)

CopyAnimation(SourceFile,SourceFilePassword,CopyRotationKeysOnly)

CopyMatrix(OutputMatrixID,InputMatrixID)

Create2D()

CreateCube()

CreateCubeTextureRenderTarget(TextureIndex,RequestedEdgeLength)

CreateDirectionVector(OutputVectorID,Longitude, Latitude)

CreateEmptyMesh()

CreateFloor()

CreateLODChain(CameraDistance1, LoD1,CameraDistance2, LoD2,CameraDistance3, LoD3,CameraDistance4, LoD4,CameraDistance5, LoD5)

CreateMaterialVSEffect(MaterialIndex,VertexShaderFile)

CreateParticleSystem()

CreatePolygon()

CreatePortal(CenterX, CenterY, CenterZ, FacingDirectionLongitude, FacingDirectionLatitude, Radius, RoomIndex1, RoomIndex2)

CreatePostScreenShader()

CreatePrimitiveFromModel()

CreateRotationMatrix(OutputMatrixID,RotationX, RotationY, RotationZ)

CreateScalingMatrix(OutputMatrixID,ScalingX, ScalingY, ScalingZ)

CreateShadowOptimizedGeometry(LODIndex,CreateConnectionPatches)

CreateTerrain()

CreateTerrainDecal()

CreateTextureRenderTarget(TextureIndex,RequestedWidth, RequestedHeight)

CreateTransformationMatrix(OutputMatrixID,TranslationX, TranslationY, TranslationZ,RotationX, RotationY, RotationZ,ScalingX, ScalingY, ScalingZ)

CreateTranslationMatrix(OutputMatrixID,TranslationX, TranslationY, TranslationZ)

CreateVector(OutputVectorID,X, Y, Z)

CreateWall()

DeformTerrain(DeformationCenterX, DeformationCenterY, DeformationRadius,RaiseLevel)

Destroy()

DestroyPortal(PortalID)

DisableFog()

Draw3DText(Text,FontIndex,X, Y, Z, RotX, RotY, RotZ, ScalX, ScalY, ScalZ)

DrawTex(TextureIndex,X,Y,Scaling,Rotation,Alpha)

DrawTexEx(TextureIndex, Left,Top, Width,Height, X,Y, ScalingX,ScalingY, Rotation, R,G,B,A,CameraIndex)

DrawTexEx(TextureIndex,Left, Top,Width, Height,X, Y,ScalingX, ScalingY,Rotation,R, G, B, A)

DrawText(FontIndex,X, Y,Text)

DrawText(FontIndex, X, Y, Text,CameraIndex)

EnableVertexTweening()

ExportToU3DFile(OutputFile,Password,ExportResources)

GetBoneCount()

GetBoneIndex(BoneName)

GetBoneParent(BoneIndex)

GetBoneTransformation(OutputMatrixID,BoneIndex)

GetClippingPlaneSupport()

GetCubeMapSupport()

GetCurrentFrame()

GetDrawCallCount()

GetDrawnTriangleCount()

GetIntersectedMaterialIndex()

GetIntersectedTriangleNormal(OutputVectorID,IntersectedTriangleIndex)

GetIntersectionPosition(OutputVectorID,IntersectedTriangleIndex,)

GetIntersectionTime(IntersectedTriangleIndex)

GetLockedMeshTriangle(LockedMeshIndex,TriangleIndex,RequestedVertexIndex)

GetLockedMeshTriangleCount(LockedMeshIndex)

GetLockedMeshTriangleMaterial(LockedMeshIndex,TriangleIndex)

GetLockedMeshVertex(LockedMeshIndex,VertexIndex,ValueIndex)

GetLockedMeshVertexCount(LockedMeshIndex)

GetMaterialCount()

GetMaterialIndex(MaterialName)

GetMatrixEntry(InputMatrixID,LineIndex, ColumnIndex)

GetMeshCount()

GetMeshOccurrenceCount(MeshIndex)

GetMeshOccurrenceTransformation(OutputMatrixID,MeshIndex,OccurrenceIndex)

GetMousePos()

GetObjectRoom()

GetObjectTransformation(ObjectID)

GetPushAwayVector(OutputVectorID,IntersectedTriangleIndex)

GetRayTracingNormal(OutputVectorID)

GetRoomVisibility(RoomIndex)

GetShadowSupport()

GetSimultaneousTextureCount()

GetSupportedPSVersion()

GetTerrainHeightAtPos(TerrainID,X, Y)

GetTextureStrengthAtPosition(TerrainID,TextureIndex,X, Y)

GetTriangleIntersectionCount()

GetVector(VectorID,VectorElementID)

GetVertex(VertexIndex,ElementIndex)

InterpolateMatrices(OutputMatrixID,InputMatrixID1, InputMatrixID2,InterpolationFactor)

InvertMatrix(OutputMatrixID,InputMatrixID)

LightDirectional()

LightPoint()

LightSpot()

LoadBackground(File)

LoadCubeTexture(FileScheme,TextureIndex,CubeWidth)

LoadHeightMap(TextureIndex,HeightMapFile,HeightFactor)

LoadMaterialEffect(MaterialIndex,UFXFile)

LoadMesh()

LoadTexture(TextureFile,TextureIndex,Width, Height)

LockMesh(LockedMeshIndex,MeshIndex,Frame,LoD)

MouseCheckButton(MouseButton)

Move(Longitude,Latitude,Speed)

MoveCamera()

OpenPortal(PortalID,OpenClose)

ParticleSystemStep(StepLength)

PreloadMesh(File,Password)

RecalculateNormals()

RecreatePrimitive()

ReleaseMatrix(MatrixID)

ReleaseModel(File)

ReleaseTexture(TextureIndex)

ReleaseVector(VectorID)

RenderToCubeTexture(CubeTextureIndex,PositionX, PositionY, PositionZ,ColorR, ColorG, ColorB, ColorARoomIndex1, RoomIndex2, , RoomIndex8)

RenderToTexture()

ScreenCoordToVector(OutputVectorID,ScreenCoordX, ScreenCoordY,CameraIndex)

SetAmbient(R,G,B)

SetBoneFrame(BoneIndex,Frame,AffectChildBonesdeclcodepbBoneIndexbbrThe index of the bone you want to change the frame value forppbFramebbrA positive value giving an absolute frame value, which is to be used for the bone It will be used instead of the value of the varframevar variable, resp the return value of GetCurrentFrame()

SetBoneRotation(BoneIndex,RotationX, RotationY, RotationZ)

SetClippingPlane(ClippingPlaneIndex,PlaneOriginX, PlaneOriginY, PlaneOriginZ,PlaneOrientationLongitude, PlaneOrientationLatitude)

SetCustomTerrainLightMap(TextureIndex)

SetFilter(TextureFilter,TextureStage)

SetFinalParticleColor(MinimumR, MinimumG, MinimumB, MinimumA,MaximumR, MaximumG, MaximumB, MaximumA)

SetFog(R,G,B,StartDistance,EndDistance)

SetFont(FontName,FontIndex,TextSize,TextAttributes,R,G,B,A)

SetFontColor(FontIndex,R,G,B,A)

SetGravityEmitter(GravityEmitterID,PositionX, PositionY, PositionZ,GravityEmitterMass)

SetInitialCenterRepulsion(RepulsionFactorX, RepulsionFactorY, RepulsionFactorZ)

SetInitialParticleColor(MinimumR, MinimumG, MinimumB, MinimumA,MaximumR, MaximumG, MaximumB, MaximumA)

SetLockedMeshTriangle(LockedMeshIndex,TriangleIndex,VertexIndexA, VertexIndexB, VertexIndexC)

SetLockedMeshTriangleMaterial(LockedMeshIndex,TriangleIndex,NewMaterialIndex)

SetLockedMeshVertex(LockedMeshIndex,VertexIndex,ValueIndex,NewValue)

SetMaterial(Red,Green,Blue,Alpha)

SetMaterialEffectLightSource(MaterialIndex,LightSourceID1, LightSourceID2, LightSourceID3)

SetMaterialEffectPSConstant()

SetMaterialEffectVSConstant(MaterialIndex,VSConstantIndex,XValue, YValue, ZValue, WValue)

SetMaterialEmissive(Red,Green,Blue)

SetMaterialEnvironmentMap(MaterialIndex,TextureIndex)

SetMaterialStageTexCoord(MaterialIndex,TextureStage,TextureCoordinateSetIndex)

SetMaterialStageTexture(MaterialIndex,TextureStage,TextureIndex)

SetMaterialStageTextureOperation(MaterialIndex,TextureStage,TextureOperation)

SetMipMapCount(NewMipMapCount)

SetMipMapFilter(TextureFilter,TextureStage)

SetModelMaterial(MaterialIndex,R, G, B, A)

SetModelMaterialEmissive(MaterialIndex,R, G, B)

SetModelMaterialSpecular(MaterialIndex,R, G, B,Power)

SetModifierAccelerationChange(ModifierID,AccelerationFactorX, AccelerationFactorY, AccelerationFactorZ,AccelerationSummandX, AccelerationSummandY, AccelerationSummandZ)

SetModifierEffectedArea(ModifierID,SecondSphereRadius,PositionX, PositionY, PositionZ,RotationX, RotationY, RotationZ,ScalingX, ScalingY, ScalingZ)

SetModifierLifetimeChange(ModifierID,LifetimeChange)

SetModifierNewFinalColor(ModifierID,MinimumR, MinimumG, MinimumB, MinimumA,MaximumR, MaximumG, MaximumB, MaximumA)

SetModifierNewFinalSize(ModifierID,MinimumNewFinalSize, MaximumNewFinalSize)

SetModifierVelocityChange(ModifierID,VelocityFactorX, VelocityFactorY, VelocityFactorZ,VelocitySummandX, VelocitySummandY, VelocitySummandZ)

SetMousePos(X, Y)

SetObjectRoom(RoomIndex)

SetObjectSolidity(SolidityState,GlobalSolidityState,)

SetOrtho(UseOrthogonalProjection,SeenAreaWidth, SeenAreaHeight)

SetParticleAcceleration(MinLongitude, MinLatitude,MinAcceleration,MaxLongitude, MaxLatitude,MaxAcceleration)

SetParticleCount(MinimumParticleCount,MaximumParticleCount,MaximumTotalParticleCount)

SetParticleLifetime(MinimumParticleLifetime,MaximumParticleLifetime)

SetParticleOrigin(SecondSphereRadius,RotationX, RotationY, RotationZ,ScalingX, ScalingY, ScalingZ)

SetParticleRotation(MinInitialRotation, MaxInitialRotation,MinRotationSpeed, MaxRotationSpeed)

SetParticleSize(MinInitialSize, MaxInitialSize,MinFinalSize, MaxFinalSize)

SetParticleVelocity(MinLongitude, MinLatitude,MinVelocity,MaxLongitude, MaxLatitude,MaxVelocity)

SetPortal(PortalID,FacingDirectionLongitude, FacingDirectionLatitude, Radius, RoomIndex1, RoomIndex2)

SetPSSFilter(TextureStage,FilterType)

SetPSSPSConstant(PSConstantIndex,Value1, Value2, Value3, Value4)

SetPSSTexture(TextureStage,TextureIndex)

SetPSSVSConstant(VSConstantIndex,Value1, Value2, Value3, Value4)

SetRoomAmbient(RoomIndex,AmbientColorR, AmbientColorG, AmbientColorB)

SetRoomVisibility(RoomIndex,NewVisibilityState)

SetRoomVisibilityEnforcement(RoomIndex,NewVisibilityEnforcementState)

SetTexturePath(TextureDirectory)

SetVertex(VertexIndex,X,Y,Z,U,V)

SmoothNormals(SmoothingAngle)

StartPoseInterpolation(DestinationFrame,InterpolationDuration)

Step()

SwitchAdditiveBlending(UseAdditiveBlending)

SwitchAlphaBlending(EnableDisable)

SwitchBackgroundMode(EnableBackgroundMode)

SwitchPortalEngine(EnableDisable)

SwitchShadowCasting(LightSourceID,Enable)

SwitchShadowReceiving(LightSourceID,Enable)

SwitchShadows(Enable)

SwitchView()

SwitchWireFrameMode(EnableDisable)

TakeScreenshot(BMPFile)

TakeScreenshotEx(BMPFile,Unused,Left, Top, Right, Bottom)

TransformMatrix(OutputMatrixID,InputMatrixID1, InputMatrixID2)

TransformVector(OutputVectorID,InputVectorID,InputMatrixID)

Ultimate3DOptions()

UnloadBackground()

UnlockMesh(LockedMeshIndex)

UpdateSkeleton()