Tu fais chercher le modèle dans /assets/obj/ alors qu’il est dans /assets/tonmod/obj/, à corriger
jglrxavpok
@jglrxavpok
Bannière: extrait d'un dessin de ma copine. Allez voir son profil DeviantArt! https://papaslender.deviantart.com/
Best posts made by jglrxavpok
-
RE: GLUtils, ou comment faire des modèles 3D facilement
-
RE: Vos plus beaux fails
Nous pouvons observer un phénomène rare mais magnifique de Mère Nature: un vol de textures manquantes.
Ces oiseaux gracieux volent en groupe pour migrer vers des pays plus chauds avant l’arrivée de l’hiver. -
RE: Modifier le menu principal
Je pense qu’il ne faut pas faire une classe fille de GuiMainMenu (extends GuiMainMenu) mais uniquement de GuiScreen.
Ça devrait régler (du moins en partie) le problème. -
RE: Load .obj
Pour des codes d’aide pour OpenGL: https://github.com/jglrxavpok/jglrxavpok_lwjgl_utils
Pour UNIQUEMENT le .jar pour charger les .obj: https://github.com/jglrxavpok/jglrxavpok_lwjgl_utils/raw/master/glutils.jarCharger un .obj:
model = new GLUtils("/cheminVersLeFichierObj");
Dessiner un .obj:
model.render();
-
RE: [Résolu] Redimensionnement d'image
Sinon, avec le code:
BufferedImage img = tonImage; Image newImage = img.getScaledInstance(newWidth, newHeight, BufferedImage.SCALE_FAST);
-
RE: Problème Timer et Tp
Le problème est finalement résolu!
private ChunkCoordinates findClosestBlockOfBiome(Entity e, BiomeGenBase b, double radius) { World w = e.worldObj; double min = Double.POSITIVE_INFINITY; ChunkCoordinates current = null; for(double x = -radius;x<=radius;x+=0.5) { for(double z = -radius;z<=radius;z+=0.5) { double dist = Math.sqrt(Math.pow((e.posX-(int)Math.floor(x+e.posX)),2)+Math.pow((e.posZ-(int)Math.floor(z+e.posZ)),2)); if(w.getBiomeGenForCoords((int)Math.floor(x+e.posX), (int)Math.floor(z+e.posZ)) == b && dist <= radius && min > dist) { min = dist; current = new ChunkCoordinates((int)Math.floor(x+e.posX), 0, (int)Math.floor(z+e.posZ)); } } } return current; }
-
RE: [Rémunéré] Moddeur
Comme dit ici:
http://minecraftforgefrance.fr/showthread.php?tid=745&pid=8265#pid8265Tu pourrais au moins essayer de présenter mieux ton serveur, et ici, nous ne sommes pas sur un marché aux moddeurs.
Essaie de te faire une place dans la communauté avant de recruter. -
RE: Uncrafting Table Mod - Décrafting pour tous les items
Le mod a été mis à jour pour la 1.7.2!
Changelog:
- Si vous donnez plus d’items que nécessaire, ces items seront eux aussi décraftés.
- Passage à la 1.7
-
RE: Créer un modèle avec techne et l'appliquer à un mob
Ton ModelTest est bien une classe avec “extends ModelBiped” ?
Sinon, au choix:-
Fais extends ton ModelTest de ModelBiped si tu veux un mob humanoïde. (je ne le recommanderai pas pour avoir 100% de contrôle)
-
Change ton constructeur pour accepter ModelBase ou ModelMobTest et non plus ModelBiped (surement le plus logique à faire)
-
Latest posts made by jglrxavpok
-
Optimiser (Partie 1): Les opérations lourdes
Sommaire
Introduction
Bonjour chers lecteurs,
Votre code est-il lent ? Vous voulez l’optimiser? Vous êtes arrivés à bon port! Découvrez l’Optimisator2000!
-Equipe Marketing de Minecraft Forge France
Ce tutoriel, qui sera découpé en plusieurs parties pour des questions de lisibilité et de simplicité, va vous expliquer certaines méthodes générales pour optimiser votre code.
Attention: Toutes les optimisations ne sont pas forcément bonnes à faire et je vous conseille de faire fonctionner votre mod avant de penser à l’optimisation!
La création d’objets
Une des premières raisons des ralentissements sur Minecraft est le Ramasse-Miettes de la JVM (Garbage Collector).
Le Ramasse-Miettes est, pour simplifier, un module de la JVM qui s’occupe de nettoyer la mémoire qui n’est plus utilisable (inaccessible pour votre code).Exemple:
void f() { uneAutreFonction(); // l'objet n'est plus accessible, donc la mémoire qu'il utilise est candidate pour être réutilisée } void uneAutreFonction() { Object o = new Object(); // une portion de la mémoire disponible est allouée pour cet objet System.out.println(o); }
Cependant, le Ramasse-Miettes n’est pas parfait: il ne s’active que de temps en temps, et le plus souvent en urgence parce qu’il n’y a plus de place (pour MC).
Ces activations à répétitions ont un coût: le Ramasse-Miettes doit réorganiser la mémoire utilisée, et cela prend un peu de temps.Comment éviter au Ramasse-Miettes tout ce travail?
Il existe plusieurs façons de gérer le problème, qui dépendent de vos préférences, de si on peut réutiliser l’objet et plein d’autres paramètres dépendants du contexte. Je vais vous en présenter quelque-unes.Moins d’objets
Ne pas créer d’objets quand vous n’avez pas besoin.
Une proposition vague, certes, mais c’est la base.Exemple (avec la librairie JOML)
Vector3f myVec = …; myVec.add(Vector3f(1f, 45f, 42f));
peut être réécrit en:
Vector3f myVec = ...; myVec.add(1f, 45f, 42f);
Créez certains objets qu’une seule fois!
Lorsqu’un objet n’est utilisé qu’à l’intérieur d’une fonction, réfléchissez à s’il est possible d’en faire une variable externe à la fonction.
Exemple avec encore JOMLvoid tourneMonVecteur(Vector3f vec) { Matrix3f myMat = new Matrix3f(); myMat.identity().rotate((float)Math.PI, 1f, 0f, 0f); myMat.transform(vec); }
peut se réécrire:
private Matrix3f myMat = new Matrix3f().identity().rotate((float)Math.PI, 1f, 0f, 0f); // on profite des mécanismes de chaînage de JOML ici void tourneMonVecteur(Vector3f vec) { myMat.transform(vec); }
Lorsque l’objet doit être configuré en plus d’être instancié, pensez aux constructeurs et au bloc ‘static’ (lorsque votre fonction est statique)
Reprise de l’exemple précédent
private Matrix3f myMat = new Matrix3f(); public MaClasse() { myMat.identity(); myMat.rotate((float)Math.PI, 1f, 0f, 0f); } void tourneMonVecteur(Vector3f vec) { myMat.transform(vec); }
ou
private static Matrix3f myMat = new Matrix3f(); // Exécuté lors du chargement de la classe par la JVM static { myMat.identity(); myMat.rotate((float)Math.PI, 1f, 0f, 0f); } static void tourneMonVecteur(Vector3f vec) { myMat.transform(vec); }
Utilisez les singletons!
Lorsque possible, utilisez le pattern Singleton: créez un objet dont l’instance sera la seule utilisée. Je vais simplement vous donner la structure la plus utilisée en Java:public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if(instance == null) instance = new Singleton(); return instance; } }
Vous pouvez l’utiliser en appelant ‘getInstance’ (c’est ce pattern qu’utilise Minecraft pour la classe… “Minecraft”)
La réutilisation d’objets
Une autre option est de réutiliser les mêmes objets. Imaginons que vous voulez faire une arme à feu à l’aide de projectiles, vous allez sûrement avoir besoin de créer un grand nombre de projectiles si votre arme a une cadence élevée. La méthode naïve consiste à créer un objet à chaque tir:
public class MonProjectile { private float tempsVivant; private float maxTempsVivant = 42f; public void onUpdate(float dt) { // … tempsVivant += dt; // vous pouvez aussi utiliser des ticks if(tempsVivant > maxTempsVivant) { this.meurt(); } } public void meurt() { // ... } public void prepare() { // ... } public void spawn() { // ... } } // ... public class MaSuperArme { public void tire() { MonProjectile projectile = new MonProjectile(); projectile.prepare(); projectile.spawn(); } }
Vous remarquerez sans doute que l’on va créer un nombre considérable d’objets dont on n’aura plus besoin quelques secondes après. Une solution est de remettre à zéro les objets lors qu’ils devraient être détruits et de les garder de côté pour les réutiliser et la méthode ‘tire’ ressemblerait à ceci:
public void tire() { MonProjectile projectile = recycle(); // c'est là que la magie va se passer! projectile.prepare(); projectile.spawn(); }
Mais comment implémenter cette méthode ‘recycle’, me direz-vous ?
Et bien on va utiliser ce qui s’appelle une piscine (Pool en anglais)!
Il existe plusieurs façons de les implémenter, et j’ai choisi de vous présenter une version simple avec une liste:
/** * License: Do whatever you want with this. -Xavier 'jglrxavpok' Niochaut */ import java.util.LinkedList; import java.util.List; public abstract class Pool<T>{ // On va utiliser une LinkedList parce que l'on va faire beaucoup d'ajouts et de retraits dans cette liste private List<T>ready = new LinkedList<>(); /** * 'Libère' un objet ie. le prépare pour sa réutilisation plus tard lors d'un appel à {@link #get()} et le * rajoute à cette piscine * @param object L'objet à réutiliser plus tard */ public void free(T object) { ready.add(object); } /** * Renvoie un objet qui a été soit: * * - recyclé * - créé car il n'y avait plus d'objets dans la piscine * @return l'objet recyclé ou créé */ public T get() { if(ready.isEmpty()) { return create(); } return ready.remove(0); // on retire le premier élément de la liste } /** * Crée un nouvel objet de type 'T' au cas où il n'y en aurait plus de libre. Ne le rajoute pas à la piscine. * @return l'object créé */ protected abstract T create(); } }
Créons notre piscine pour les projectiles:
public class ProjectilePool extends Pool <monprojectile>{ @Override protected MonProjectile create() { return new MonProjectile(); } }
Et utilisons-la:
public class MaSuperArme { private ProjectilePool projectilePool = new ProjectilePool(); public void tire() { MonProjectile projectile = recycle(); projectile.prepare(); projectile.spawn(); } public MonProjectile recycle() { return projectilePool.get(); } } public class MonProjectile { private float tempsVivant; private float maxTempsVivant = 42f; private ProjectilePool pool; public void onUpdate(float dt) { // ... tempsVivant += dt; // vous pouvez aussi utiliser des ticks if(tempsVivant > maxTempsVivant) { this.meurt(); } } public void meurt() { pool.free(this); } public void prepare(ProjectilePool pool) { // ... this.pool = pool; } public void spawn() { // ... } }
Et voilà! Vos projectiles sont maintenant recyclés!
Attention! N’oubliez pas de réinitialiser tous les propriétés (position, vitesse, vie, etc.) de vos objets avant de les utiliser!
Et voilà pour ce petit tutoriel sur la base gestion de la mémoire utilisée par votre mod, j’espère qu’il vous aura plu!
-
RE: Votre jeu indé du moment
En ce moment, je joue pas mal à
- Steredenn, un shooter fait par un studio breton
- Shenzhen I/O, un jeu basé sur la programmation et l’électronique
- TIS-100, un jeu basé aussi sur la programmation (même développeur), sur un ordinateur mystérieux…
- FTL: Faster Than Light, moi aussi j’aime beaucoup son style mais on ne le présente plus
-
Un remplaçant de Paulscode's SoundSystem pour LWJGL 3: Audiokode
#Introduction(Introduction)
Depuis la sortie de LWJGL 3.x, la (célèbre ?) librairie de Paulscode, 3DSoundSystem, n’est plus utilisable et elle n’a pas été mise à jour.
La solution la plus adoptée a donc été d’écrire son propre code pour OpenAL et le chargement de musiques/bruitages. Je vous propose de remédier à cela!#Installation(Installation)
Pour le moment, le seul moment est copier les sources et de compiler la librairie soit-même.Cependant, si vous avez Git d’installé et que vous utilisez Gradle, vous pouvez ajouter ceci dans .gitmodules (rajoutez le dans votre dossier racine si vous n’en n’avez pas):
[submodule "Audiokode"] path = Audiokode url = https://github.com/jglrxavpok/Audiokode.git
Vous pouvez aussi faire cette commande dans votre dossier racine:
git submodule add https://github.com/jglrxavpok/Audiokode.git
Cela téléchargera en plus directement le projet (et modifies le fichier .gitmodules en conséquence).
Ensuite, une fois ceci fait, ajoutez ceci dans votre settings.gradle (à créer si besoin):
include 'Audiokode'
Et dans votre fichier build.gradle:
// … dependencies { // ... compile project(':Audiokode') // ... } // ....
Un petit refresh des dépendances avec Gradle, et vous êtes prêts!
#Utilisation(Utilisation)
Initialisation du système:// Initialisation SoundEngine engine = new SoundEngine(); engine.initWithDefaultOpenAL();
Exemple pour jouer une musique:
// Une musique de fond Source source = engine.backgroundMusic("TestWav", false); source.play(); source.setGain(0.15f); // contrôler le volume
:::
engine.quickplayBackgroundMusic("TestWav");
:::
Attention! Si vous n’utilisez pas ThreadedSoundEngine, vous aurez besoin d’ajouter ceci dans la boucle principale de votre jeu:
engine.update();
Cela permet à Audiokode de faire boucler les musiques, de faire bouger l’écouter, etc.
Et voilà! Vous pouvez aussi utiliser ThreadedSoundEngine si vous voulez laisser Audiokode mettre à jour votre son tout seul dans son coin.
-
RE: Indicateur de chat
Il risque d’y avoir un problème vu comment c’est parti.
Minecraft.getMinecraft().ingameGUI.getChatGUI().getChatOpen() ``` ne permet de savoir que si le client sur lequel le mod tourne a son tchat ouvert. Il faudrait envoyer un message (via des packets) au serveur pour lui dire que le joueur est en train d'écrire, puis de retransmettre cette information à tous les clients connectés, et enfin d'afficher la bulle uniquement si ton client a reçu l'information que le joueur J est bien en train d'écrire. PS: N'hésite pas à poster tes retours sur le tuto OpenGL dans ses commentaires, il manque cruellement de retours ce tuto. Edit: J'ai lu trop en diagonale et quelqu'un d'autre a déjà soulevé ce problème, mais j'espère que mon explication pourra t'aider quand même.
-
Les bases pratiques d'OpenGL
Sommaire
Introduction
Bonjour chers lecteurs,
Vu les nombreuses demandes d’aide qui pourraient être réglées avec un minimum de connaissances des bases d’OpenGL, voici un petit tutoriel pratique sur les bases de cette librairie graphique.
XOXOXO -jglrxavpok
Le code de ce tutoriel est disponible ici.
Le but de ce tutoriel est de vous apprendre à utiliser les bases d’OpenGL pour vous aider à faire vos mods, il n’ira donc pas dans les détails du fonctionnement de la librairie.
Par soucis de simplicité, les diverses commandes vont être présentées pour le rendu dans un Gui mais les idées restent les mêmes dans n’importe quel contexte.Pré-requis
Pour ce tutoriel, vous devez avoir suivi au moins les tutoriels suivants:
- Installer les logiciels nécessaires au modding forge
- Installer et configurer l’espace de travail de Forge
- Créer la base de son mod
Code de base et ressources([size]Code de base et ressources)
Dans ce tutoriel nous utiliserons les deux images suivantes:
Et voici l’arborescence associée:
Voici maintenant les classes de base que l’on va utiliser pour le tutoriel, vous n’avez évidemment pas besoin d’avoir les mêmes, cela permet juste de suivre les modifications.
package fr.minecraftforgefrance.tutorial; import fr.minecraftforgefrance.tutorial.client.ClientEventHandler; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventHandler; import net.minecraftforge.fml.common.event.FMLInitializationEvent; @Mod(modid = OpenGLPratique.MODID, version = OpenGLPratique.VERSION) public class OpenGLPratique { public static final String MODID = "openglpratique"; public static final String VERSION = "1.0"; @EventHandler public void init(FMLInitializationEvent event) { MinecraftForge.EVENT_BUS.register(new ClientEventHandler()); } }
package fr.minecraftforgefrance.tutorial.client; import fr.minecraftforgefrance.tutorial.OpenGLPratique; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.gui.inventory.GuiInventory; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.util.ResourceLocation; import static org.lwjgl.opengl.GL11.*; /** * Ceci est une classe utilisée pour l'exemple et n'est probablement pas utilisable en pratique sans faire des gros changements. * * Permet de dessiner le logo de MinecraftForgeFrance et le modèle du joueur. */ public class Renderer { private static final ResourceLocation opaqueLogo = new ResourceLocation(OpenGLPratique.MODID, "logo_mff128x128.png"); private static final ResourceLocation transparentLogo = new ResourceLocation(OpenGLPratique.MODID, "transparentlogo_mff128x128.png"); private static final int WIDTH = 128; private static final int HEIGHT = 128; public static void drawOpaqueSprite(ScaledResolution resolution) { Minecraft.getMinecraft().getTextureManager().bindTexture(opaqueLogo); Tessellator tessellator = Tessellator.getInstance(); VertexBuffer buffer = tessellator.getBuffer(); buffer.begin(GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); float w = WIDTH / ((float)resolution.getScaleFactor()); float h = HEIGHT / ((float)resolution.getScaleFactor()); buffer.pos(0,0,0).tex(0, 0).color(1f, 1f, 1f, 1f).endVertex(); buffer.pos(0,h,0).tex(0, 1f).color(1f, 1f, 1f, 1f).endVertex(); buffer.pos(w, h,0).tex(1f, 1f).color(1f, 1f, 1f, 1f).endVertex(); buffer.pos(w,0,0).tex(1f, 0).color(1f, 1f, 1f, 1f).endVertex(); tessellator.draw(); } public static void drawTransparentSprite(ScaledResolution resolution) { Minecraft.getMinecraft().getTextureManager().bindTexture(transparentLogo); Tessellator tessellator = Tessellator.getInstance(); VertexBuffer buffer = tessellator.getBuffer(); buffer.begin(GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); float w = WIDTH / ((float)resolution.getScaleFactor()); float h = HEIGHT / ((float)resolution.getScaleFactor()); buffer.pos(0,0,0).tex(0, 0).color(1f, 1f, 1f, 1f).endVertex(); buffer.pos(0,h,0).tex(0, 1f).color(1f, 1f, 1f, 1f).endVertex(); buffer.pos(w, h,0).tex(1f, 1f).color(1f, 1f, 1f, 1f).endVertex(); buffer.pos(w,0,0).tex(1f, 0).color(1f, 1f, 1f, 1f).endVertex(); tessellator.draw(); } public static void drawModel(ScaledResolution resolution) { Minecraft mc = Minecraft.getMinecraft(); GuiInventory.drawEntityOnScreen(100,100,resolution.getScaleFactor()*10, 0f, 0f, mc.player); } }
Le gestionnaire d’événements
package fr.minecraftforgefrance.tutorial.client; import net.minecraft.client.gui.ScaledResolution; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; public class ClientEventHandler { @SubscribeEvent public void onGuiDrawing(RenderGameOverlayEvent.Pre event) { if(event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR) { draw(event.getResolution()); } } /** * C'est la méthode que l'on va modifier dans ce tutoriel */ private void draw(ScaledResolution resolution) { Renderer.drawTransparentSprite(resolution); Renderer.drawModel(resolution); } }
Remarquez bien la méthode ClientEventHandler::draw(ScaledResolution), c’est ici que tout va se passer par la suite.
Si vous lancez le jeu avec le code tel quel, vous devriez avoir quelque chose comme ceci:
Et voilà vous êtes prêts!
Commandes
Premier points
Avant de commencer à apprendre les commandes de base, il faut que je vous apprenne deux-trois choses sur la façon dont on accède à ces commandes. De plus, pour simplifier, le préfixe GL11. sera omis. C’est possible en ajoutant ceci dans les imports de la classe dans laquelle vous travaillez:
import static org.lwjgl.opengl.GL11.*;
Points importants:
- Il est possible (au moins jusqu’à la version 1.11, incluse, de Minecraft) de remplacer les commandes d’OpenGL par des fonctions de GlStateManager, elles sont même préférables à utiliser pour le modding. (les correspondances seront explicitées dans ce tutoriel). Cependant, pour rester général, ce tutoriel utilisera les commandes d’OpenGL directement; cela vous permettra d’utiliser OpenGL en dehors de Minecraft!
- Format d’une commande OpenGL:
gl<nom de la commande><type des paramètres><f, i, b, ub, d, ou vide>(<arguments>)
- Une commande OpenGL agit toujours sur le rendu fait après la commande. De plus certaines commandes ne remplacent pas mais ajoutent aux effets déjà présents dû à cette même commande (ce sera plus clair dans les exemples), ces fonctions seront explicitées dans ce tutoriel
- Juste pour ce tutoriel: tous les bouts de code donnés sont considérés comme le contenu de la fonction draw() de ClientEventHandler
- Juste pour ce tutoriel: tous les appels à la classe Renderer sont à remplacer par votre code de rendu (du genre model.render())
Voilà, vous êtes prêts pour de vrai cette fois!
glColor<nombre><type>: Multiplier la couleur
Cliquez pour avoir la branche de cette partie.
Propriétés à noter:- Remplace totalement la valeur précédente donnée
- Remplaçable par GlStateManager::color
La commande glColor permet de multiplier la couleur de rendu par des flottants, dissection avec un exemple:
glColor3f(multiplicateurRouge, multiplicateurVert, multiplicateurBleu); glColor4f(multiplicateurRouge, multiplicateurVert, multiplicateurBleu, multiplicateurAlpha);
Chaque couleur peut être décomposée en trois composantes rouge, vert et bleu (l’espace RGB en anglais) et cette commande permet d’agir sur chacune de ses composantes:
chacun des termes va multiplier la composante choisie par le terme donné.
Merci Wikipedia!Pour ne garder que le bleu du logo du site, on peut faire ainsi:
glColor3f(0f, 0f, 1f); Renderer.drawTransparentSprite(resolution); // rendu du logo du site avec uniquement le bleu
Et pour mettre le logo en opacité à 25%, sans bleu et ajouter le rouge et le vert à 50%:
glColor4f(0.5f, 0.5f, 0f, 0.25f); Renderer.drawTransparentSprite(resolution); // rendu du logo du site à 25% d'opacité, pas de bleu, et division par 2 du rouge et du bleu
Attention, cette méthode remplace la couleur précédente:
glColor3f(0f, 0f, 0f); glColor4f(1f, 1f, 1f, 1f); Renderer.drawTransparentSprite(resolution); // rendu du logo en couleurs normales!
Ici la couleur est normale au lieu de dessiner le logo en noir!
glTranslate<type>: faire une translation
Cliquez pour avoir la branche de cette partie.
Propriétés à noter:- Opération de transformation: on déplace le contenu
- Des translations successives s’ajoutent!
- Dans les Gui (à cause de la projection orthographique pour ceux qui veulent le détail), la coordonnée sur Z n’influence pas la taille de l’objet.
- Remplaçable par GlStateManager::translate
Avant de vous introduire à glTranslate, il faut que vous voyez glPushMatrix et glPopMatrix.
Ces deux méthodes vous permettent de sauvegarder la transformation courante:- glPushMatrix: sauvegarde la transformation sur une pile de sauvegarde
- glPopMatrix: restaure la pile en haut de la pile et la retire de cette pile
Elles s’utilisent donc ainsi:
glPushMatrix(); // transformations: translations, rotations, homothéties Renderer.drawTransparentSprite(resolution); glPopMatrix();
Explication de Wikipédia:
[quote]
informatique, une pile (en anglais stack) est une structure de données fondée sur le principe « dernier arrivé, premier sorti » (ou LIFO pour last in, first out), ce qui veut dire, qu’en général, le dernier élément ajouté à la pile sera le premier à en sortir. Le fonctionnement est similaire à celui d’une pile d’assiettes : on ajoute des assiettes sur la pile, et on les récupère dans l’ordre inverse, en commençant par la dernière ajoutée.
[/quote]
La commande glTranslate(x, y, z) permet de faire des translations au contenu qui va être dessiné selon les 3 axes X, Y et Z (locaux, on verra plus tard pourquoi).
L’unité des paramètres dépend énormément du contexte, par exemple pour les blocs, l’unité sera des blocs alors que pour les Gui, ce sera (à peu près) des pixels.
glPushMatrix(); // transformations: translations, rotations, homothéties // point important: dans un Gui, la coordonnée sur Z n'influence pas la taille // (mais permet d'afficher du contenu devant ou derrière quelque chose, selon la valeur choisie) glTranslatef(100f, 30f, -1f); Renderer.drawModel(resolution); glPopMatrix();
Avant le glTranslate:
Après le glTranslate
On peut voir que les effets s’ajoutent en décomposant selon les 3 axes, par exemple: les deux codes suivants sont équivalents
glTranslatef(100f, 0f, 0f); glTranslatef(0f, 30f, 0f); glTranslatef(0f, 0f, -1f);
glTranslatef(100f, 30f, -1f);
glRotate<type>: faire une rotation
Cliquez pour avoir la branche de cette partie.
Propriétés à noter:- Opération de transformation: on déplace le contenu
- Des rotations successives s’ajoutent!
- L’ordre des rotations a une importante!
*Remplaçable par GlStateManager::rotate
La commande glRotate est assez simple: elle prend 4 paramètres qui sont:
- L’angle de rotation, en degrés, avec les angles positifs allant dans le sens horaire. (attention, si vous utilisez des matrices de rotation plus tard, ce sera en radians et dans le sens trigonométrique)
- La coordonnée sur l’axe (Ox) de l’axe de rotation
- La coordonnée sur l’axe (Oy) de l’axe de rotation
- La coordonnée sur l’axe (Oz) de l’axe de rotation
Un petit schéma:
Exemple:
Essayez ceci (explication après la vidéo):glPushMatrix(); glTranslatef(100f, 100f, 0f); glRotatef(Minecraft.getSystemTime()*.125f, 0f, 0f, 1f); glTranslatef(-64f/resolution.getScaleFactor(), -64f/resolution.getScaleFactor(), 0f); Renderer.drawTransparentSprite(resolution); glPopMatrix();
Vous voudriez avoir quelque chose de similaire à ceci: [video]https://youtu.be/UkAMfLfvIzY[/video]
Dissection du code!
glTranslatef(100f, 100f, 0f);
On déplace notre contenu vers le centre (à peu près).
glRotatef(Minecraft.getSystemTime()*.125f, 0f, 0f, 1f);
Cette commande effectue une rotation d’un angle de Minecraft.getSystemTime().125f* (cela permet de faire un petit effet d’animation) autour de l’axe Z.
glTranslatef(-64f/resolution.getScaleFactor(), -64f/resolution.getScaleFactor(), 0f); // on divise par le facteur de résolution pour faire des positions au pixel près
Alors c’est cette ligne qui apporte quelque chose d’important. Si on la retire, on obtient ceci: [video]https://youtu.be/B_dvCYeH9GU[/video]
Pourquoi ça a changé ?
Il s’avère que l’ordre des transformations importe beaucoup! En effet, cette ligne permet en quelque sorte de définir un point d’origine sur le sprite pour la rotation.
Un petit schéma pour plus de détails:
La rotation change les axes X,Y,Z locaux et la dernière translation (en rouge) est faite dans la nouvelle base (représentée en rose). La rotation se fait donc à partir du point spécifié (et on se retrouve à la position en noir).
On peut bien évidemment faire des rotations autour des axes X et Y:
glPushMatrix(); glRotatef(-45f, 1f, 0f, 0f); glRotatef(-45f, 0f, 1f, 0f); Renderer.drawModel(resolution); glPopMatrix();
Cet exemple vous donnera ceci:
Comme dit précédemment, les rotations changent les systèmes d’axes. Ainsi le code suivant, qui inverse les rotations ne donne pas le même résultat:
glPushMatrix(); // inversion ! glRotatef(-45f, 0f, 1f, 0f); glRotatef(-45f, 1f, 0f, 0f); Renderer.drawModel(resolution); glPopMatrix();
Résultat:
glScale<type>: homothéties et distortions
Cliquez pour avoir la branche de cette partie.
Propriétés à noter:- Opération de transformation: on déplace le contenu
- Des homothéties successives s’ajoutent!
- Remplaçable par GlStateManager::scale
La commande glScale permet de faire des [infobulle=Une homothétie est une opération mathématique qui ‘allonge’ des vecteurs depuis un point de départ, un changement d’échelle, ou un redimensionnement en quelque sorte.]homothéties[/infobulle] sur chacun des trois axes. Voici les paramètres:
- Multiplicateur sur l’axe (Ox)
- Multiplicateur sur l’axe (Oy)
- Multiplicateur sur l’axe (Oz)
Merci Wikipédia encore!Pour chacun de ses paramètres, un multiplicateur égal à 1 équivaut à aucun changement. Une valeur entre -1 et 1 rétrécit l’objet alors qu’une valeur f telle que |f| > 1 agrandit l’objet.
Voici un petit exemple avec le logo du forum (je n’utilise pas le modèle du joueur ici car son code de rendu fait aussi des translations, et un simple glScale ne va pas que changer sa taille, essayez par vous même):
glPushMatrix(); glScalef(10f, 1f, 1f); Renderer.drawOpaqueSprite(resolution); glPopMatrix();
Avant:
Après:
Attention! Comme avec glRotate, cette commande affecte les suivantes: si vous multipliez par 0.5 sur l’axe X, votre prochaine translation sur cet axe sera elle aussi multipliée par 0.5!
De plus, comme avec glRotate, l’ordre importe!
Un exemple pour comparer:glPushMatrix(); glTranslatef(100f, 0f, 0f); glScalef(2f, 1f, 1f); // On applique l'homothétie **après** la translation Renderer.drawOpaqueSprite(resolution); glPopMatrix();
glPushMatrix(); glScalef(2f, 1f, 1f); // On applique l'homothéthie **avant** la translation glTranslatef(100f, 0f, 0f); Renderer.drawOpaqueSprite(resolution); glPopMatrix();
Il est aussi possible d’inverser des modèles et des images grâce à cette commande! Il suffit de donner une valeur négative dans l’un des paramètres:
GlStateManager.disableCull(); // necessaire pour que ce rendu ('draw opaque sprite') fonctionne, plus d'infos plus loin glPushMatrix(); // l'homothétie se fait depuis le point (0,0) et on décale l'image pour qu'elle apparaisse à l'écran // N'hésitez pas à mettre 10f sur l'axe Y pour que mieux comprendre glTranslatef(100f, 128f/resolution.getScaleFactor(), 0f); glScalef(1f, -1f, 1f); // On applique l'homothéthie **avant** la translation Renderer.drawOpaqueSprite(resolution); glPopMatrix(); GlStateManager.enableCull(); // necessaire pour que ce rendu ('draw opaque sprite') fonctionne, plus d'infos plus loin
glEnable: activer vos capacités!
Propriétés à noter:
- Change l’état d’OpenGL, pensez toujours à annuler des changements après votre rendu!
- Vous pouvez retrouver une version pour une bonne partie des capacités dans GlStateManager.
- Parfois remplaçable par GlStateManager::enable<capacité> ou GlStateManager::disable<capacité>
D’inverse glDisable, la commande glEnable prend un unique paramètre: la capacité à activer (ou à desactiver).
La liste de toutes les capacités possible est assez longue et il n’y aurait pas d’intérêt à tout expliquer ici.
Vous pouvez cliquer sur ce lien si vous voulez vraiment la liste complète.En voici quelques unes qui pourraient vous être utile:
- GL_SCISSOR_TEST: Permet de sélectionner la zone de dessin, une partie de ce tutoriel y est entièrement dédié.
- GL_DEPTH_TEST: Permet d’activer le test de profondeur: les faces les plus proches de la caméra cachent celle de derrière
- GL_CULL_FACE: Permet de n’afficher des polygones que dans un seul sens (voir section ‘VertexBuffer’)
- GL_BLEND: Permet d’activer le mélange des couleurs et la transparence
Points avancés
Avant de s’attaquer à cette partie, je vous conseille d’avoir bien compris les parties précédentes et d’avoir acquis un peu d’aisance avec OpenGL.
Utiliser plusieurs textures
Cliquez pour avoir la branche de cette partie.
Pour utiliser une texture, on doit l’attacher avec
glBindTexture(GL_TEXTURE_2D, <votre n° de texture>)
. Cependant, Minecraft propose un moyen plus simple d’attacher une texture avec des ResourceLocation:mc.getTextureManager().bindTexture(<la ressource>);
où mc est une instance de Minecraft (Minecraft.getMinecraft()) par exemple.
Pour utiliser plusieurs textures pour vos rendus, il faut grouper les éléments utilisant la même texture et utiliser la texture pour ce groupe.
Exemple avec deux textures:
private static final ResourceLocation opaqueLogo = new ResourceLocation(OpenGLPratique.MODID, "logo_mff128x128.png"); private static final ResourceLocation transparentLogo = new ResourceLocation(OpenGLPratique.MODID, "transparentlogo_mff128x128.png"); /** * C'est la méthode que l'on va modifier dans ce tutoriel */ private void draw(ScaledResolution resolution) { glPushMatrix(); Minecraft.getMinecraft().getTextureManager().bindTexture(transparentLogo); // on attache la 1ère texture drawRect(128, 128, resolution); // on dessine un 1er rectangle avec la texture transparente glTranslatef(100f, 100f, 0f); Minecraft.getMinecraft().getTextureManager().bindTexture(opaqueLogo); // on attache la 2ème texture drawRect(128, 128, resolution); // on dessine un 2nd rectangle avec la texture opaque glPopMatrix(); } // Ne faites pas trop attention à ça, sert à dessiner un rectangle à l'écran; cette méthode sera expliquée plus tard dans le tutoriel private void drawRect(int width, int height, ScaledResolution resolution) { Tessellator tessellator = Tessellator.getInstance(); VertexBuffer buffer = tessellator.getBuffer(); buffer.begin(GL_QUADS, DefaultVertexFormats.POSITION_TEX); float w = width / ((float)resolution.getScaleFactor()); float h = height / ((float)resolution.getScaleFactor()); buffer.pos(0,0,0).tex(0, 0).endVertex(); buffer.pos(0,h,0).tex(0, 1f).endVertex(); buffer.pos(w, h,0).tex(1f, 1f).endVertex(); buffer.pos(w,0,0).tex(1f, 0).endVertex(); tessellator.draw(); }
Résultat:
Le scissor test: sélectionner une zone de rendu
Cliquez pour avoir la branche de cette partie.
Points importants:- Ce système n’est pas affectée par les transformations.
- Les coordonnées données en paramètres sont des coordonnées en pixels correspondant à un rectangle sur l’écran.
- Pas de remplacement dans GlStateManager.
Vous voulez afficher votre contenu sur l’écran mais le garder dans un rectangle précis sur l’écran ?
Le scissor test vous permet de faire exactement ceci.
Il s’utilise ainsi:glEnable(GL_SCISSOR_TEST); glScissor(<x>,<y>, <largeur>,<hauteur>); // attention! Pour l'axe Y, le 0 est en **bas** de l'écran! // rendu glDisable(GL_SCISSOR_TEST);
Un petit schéma:
glEnable(GL_SCISSOR_TEST); glScissor(32,Minecraft.getMinecraft().displayHeight-128, 64,64); // attention! Pour l'axe Y, le 0 est en **bas** de l'écran! Renderer.drawOpaqueSprite(resolution); glDisable(GL_SCISSOR_TEST);
Résultat:
Bonus
Quelques informations bonus pour approfondir vos connaissances.
Le Vertex Buffer: prenez le pouvoir!
Cliquez pour avoir la branche de cette partie.
Précédemment dans ce tutoriel, on se basait soit sur la classe magique Renderer ou Gui pour le rendu; et si on choisissait nous-même comment on dessine notre contenu à l’écran?
Dites bonjour au VertexBuffer!
Ça en fait des membres !
Effectivement, et il est assez facile de s’y perdre. Ce tutoriel vous permettra d’apprendre à utiliser certaines des méthodes de cette classe pour afficher du contenu.
Accéder à une instance
Pour utiliser le VertexBuffer, il nous faut une instance, je vous recommande de le faire ainsi:Tessellator tessellator = Tessellator.getInstance(); VertexBuffer buffer = tessellator.getBuffer();
Ordre des sommets
Faites bien attention à définir vos sommets dans le sens inverse des aiguilles d’une montre, surtout lorsque vous faites un rendu 2D. En effet, Minecraft active par défaut le ‘culling’ et fais en sorte que seules les faces apparaissant dans le sens antihoraire, par rapport à la caméra, soient dessinées.
Vous pouvez désactiver ce comportement ainsi, même si je vous le déconseille:glDisable(GL_CULL_FACE);
begin
Cette méthode prend deux paramètres:
- Le mode de dessin: GL_QUADS, GL_TRIANGLES le plus généralement
- Le format des sommets à spécifier, vous pouvez trouver ceux par défaut dans DefaultVertexFormats
Cette méthode est à mettre obligatoirement avant chaque début de rendu!
Le mode de dessin permet de choisir le contenu: des quads, des triangles, etc.
Le format des sommets permet de choisir ce qui compose un sommet. Avec les différents formats disponibles dans DefaultVertexFormats, vous pouvez définir la position, les coordonnées de texture (optionnelles), la couleur du sommet (optionnelle), et un vecteur normal (optionnel).
Petite note sur la couleur: si vous donnez une couleur pour définir vos sommets, glColor n’a plus aucun effet!Tessellator::draw()
Cette méthode permet de terminer votre rendu (je vous conseille vivement de ne pas utiliser VertexBuffer::finishDrawing() si vous ne savez pas ce que vous faites).
Exemple:tessellator.draw();
Attention l’ordre d’utilisation des méthodes suivantes doit absolument suivre celui défini dans votre format!
Par exemple, avec le format POSITION_TEX_COLOR, l’ordre à suivre est: pos, tex, color!pos
Cette méthode prend une position (X,Y,Z) qui indique la position de votre sommet.
tex
Cette méthode prend une position (U,V) qui indique la coordonnée de texture:
Un triangle utilisant les coordonnées (0;0), (0.5;0), (0.5;0.5) utilisera la partie surlignée en rose dans cet exemple:
color
Vous permet de definir la couleur de votre sommet, soit en RGBA avec des flottants, ou RGBA avec des entiers compris entre 0(rien) et 255(max).
normal
Permet de définir la normale de votre sommet.
endVertex
Finit la définition de votre sommet. Doit impérativement figurer à la fin de la définition d’un sommet.
Exemple: dessiner le logo du forum
// on récupère le buffer Tessellator tessellator = Tessellator.getInstance(); VertexBuffer buffer = tessellator.getBuffer(); // on commence à dessiner des quadrilatères où on donne la position et les coordonnées de textures buffer.begin(GL_QUADS, DefaultVertexFormats.POSITION_TEX); float w = WIDTH / ((float)resolution.getScaleFactor()); float h = HEIGHT / ((float)resolution.getScaleFactor()); // on ajoute un sommet à (0,0) avec la coordonnée (0,0) sur la texture buffer.pos(0,0,0).tex(0, 0).endVertex(); buffer.pos(0,h,0).tex(0, 1f).endVertex(); buffer.pos(w, h,0).tex(1f, 1f).endVertex(); buffer.pos(w,0,0).tex(1f, 0).endVertex(); // on dessine le contenu tessellator.draw();
(on suppose que ‘WIDTH’=largeur du logo, ‘HEIGHT’=hauteur du logo, ‘opaqueLogo’=Localisation du logo)
Le résultat, vous le connaissez:
Un autre exemple avec l’attribut ‘COLOR’:
// on commence à dessiner des quadrilatères où on donne la position, les coordonnées de textures et la couleur buffer.begin(GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); float w = WIDTH / ((float)resolution.getScaleFactor()); float h = HEIGHT / ((float)resolution.getScaleFactor()); // on demande de ne garder que le vert de la couleur buffer.pos(0,0,0).tex(0, 0).color(0f, 1f, 0f, 1f).endVertex(); buffer.pos(0,h,0).tex(0, 1f).color(0f, 1f, 0f, 1f).endVertex(); buffer.pos(w, h,0).tex(1f, 1f).color(0f, 1f, 0f, 1f).endVertex(); buffer.pos(w,0,0).tex(1f, 0).color(0f, 1f, 0f, 1f).endVertex(); // on demande de ne garder que le rouge de la texture mais n'a aucun effet! glColor4f(1f, 0f, 0f, 1f); // on dessine le contenu tessellator.draw(); glColor4f(1f, 1f, 1f, 1f);
Le stencil buffer: sélectionner une zone de rendu, mais en mieux
Cliquez pour avoir la branche de cette partie.
Points importants:- Ce système peut être affecté par les transformations.
- Les utilisations du stencil buffer sont assez variées et ce tutoriel ne donnera qu’un exemple de ce qui est possible. Je vous invite à regarder plus en détails si cela vous intéresse et voici quelques liens: Open.gl: Depth Stencils et Wiki de Khronos sur le Stencil Test
- Pas de remplacement dans GlStateManager.
Une utilisation possible du stencil buffer est analogue à celle du scissor test: choisir une zone de rendu. Cependant, le stencil buffer permet de choisir la forme de la zone et ne vous oblige plus à utiliser des coordonnées en pixels.
Pour cet exemple, nous allons dessiner le logo du forum avec la forme du modèle du joueur.// Etape importante, permet de vérifier si le stencil buffer est activé et l'activer si ce n'est le cas Framebuffer framebuffer = Minecraft.getMinecraft().getFramebuffer(); if( ! framebuffer.isStencilEnabled()) { framebuffer.enableStencil(); } glEnable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS, 1, 0xFF); // Tous les pixels affichés vont avoir la valeur 1 dans le stencil buffer glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glStencilMask(0xFF); // On va écrire dans le stencil buffer glDepthMask(false); // On n'écrit plus dans le depth buffer glColorMask(false, false, false, false); // on désactive le rendu des couleurs glClear(GL_STENCIL_BUFFER_BIT); // On vide le buffer // rendu du modèle glPushMatrix(); glTranslatef(-80f, -40f, 0f); // on déplace le modèle en haut à gauche Renderer.drawModel(resolution); // On dessine le modèle du joueur glPopMatrix(); // rendu du logo // On ne dessine les pixels que si le pixel que l'on va dessiner est à une position où le stencil buffer a une valeur égale (GL_EQUAL) à 1 glStencilFunc(GL_EQUAL, 1, 0xFF); glStencilMask(0x00); // On n'écrit plus rien au stencil buffer glDepthMask(true); // On réactive l'écriture vers le depth buffer glColorMask(true, true, true, true); // On réactive le rendu des couleurs glPushMatrix(); glScalef(1.5f, 1.5f, 1f); // on agrandit un peu le logo pour que le modèle rentre entièremen dedans Renderer.drawOpaqueSprite(resolution); // On dessine notre logo glPopMatrix(); glDisable(GL_STENCIL_TEST);
Les explications sont dans les commentaires du code mais si vous voulez plus de détails, voici une liste d’explications des différentes fonctions utilisées:
Résultat:
Conclusion
Voilà, ce tutoriel est fini, j’espère qu’il vous a plu, et n’hésitez pas à poser des questions et à proposer des améliorations!
Crédits
Rédaction :
jglrxavpokCorrection :
Folgansky
Ce tutoriel de Minecraft Forge France est mis à disposition selon les termes de la licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International -
RE: Problème machine
Je pense que la question vient du fait que ton code est difficilement lisible et peu “propre”.
Essaie de détailler un peu plus ton problème aussi.