L’api ne fonctionne pas en 1.7… ^^
Caused by: java.lang.NoClassDefFoundError: net/minecraftforge/event/Event
Bonjour tout le monde! C’est l’ heure de “Présente ta création”!
–Attention : Des parties de ce tutoriel sont obsolètes pour les verions 1.8 et plus (notemment le rendu d’objet avec .obj)–
Beaucoup de personnes ont demandé à charger des fichiers .obj dans Minecraft.
Beaucoup ont réussi.
Mais beaucoup ont échoué.
Tout ça à cause d’un petit truc insignifiant mais tellement important: le fichier .mtl !
Ce dernier est crucial, sans lui, la plupart des textures ne s’appliqueront pas comme voulu! Surpris
Avant ce post, vous aviez plusieurs choix:
Mais maintenant, plus besoin de se casser la tête!
GLUtils est là pour vous aider!
Vous pouvez soit:
Prendre toutes les sources ici (contient aussi d’autres codes qui peuvent vous intéresser)
Ou télécharger directement le .jar ici pour les mods Minecraft | ici pour le reste
Ensuite, ajoutez le code au votre (si vous avez pris les sources) ou ajoutez le .jar dans le classpath (dans le cas du… .jar ^^')
C’est surement la partie la plus simple:
ajoutez cette ligne là où vous voulez créer l’instance du model:
model = new TessellatorModel("/assets/modid/models/ModelDeLaMortQuiTue.obj");
et celle-ci en dehors de toute méthode:
private TessellatorModel model;
(L’API assume que vous déposez les fichiers de textures et .mtl dans le même fichier)
Et voilà, vous avez fini cette partie, bravo!
Il suffit d’appeler la méthode render() sur l’instance de votre model:
model.render();
Voici un très simple exemple avec notre ami Yoshi qui sera aujourd’hui un bloc de Minecraft (dessiné avec TESR)! (parce que… pourquoi pas ?)
Bonjour Yoshi!
Code du rendu:
package tutorial.glutils; import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; import net.minecraft.tileentity.TileEntity; import org.jglrxavpok.glutils.TessellatorModel; import org.lwjgl.opengl.GL11; public class TileTestObjRenderer extends TileEntitySpecialRenderer { private TessellatorModel glmodel; public TileTestObjRenderer() { glmodel = new TessellatorModel("/assets/obj/Yoshi.obj"); } @Override public void renderTileEntityAt(TileEntity tileentity, double d0, double d1, double d2, float f) { GL11.glPushMatrix(); GL11.glTranslated(d0+0.5, d1, d2+0.5); GL11.glScaled(0.01, 0.01, 0.01); glmodel.render(); GL11.glPopMatrix(); } }
Et voilà, c’est tout ce qu’il y a à faire Grand sourire
Si vous avez regardé le résultat juste au dessus, vous avez surement remarqué que l’on voit très facilement les différentes faces du model.
Pour s’en débarrasser, ajoutez juste AVANT d’appeler la méthode render() du model:
GL11.glShadeModel(GL11.GL_SMOOTH);
Je vous conseille aussi d’appeler
model.regenerateNormals();
Après avoir créer le model afin de corriger les possibles problèmes dans le fichier du model et avoir une réflexion de la lumière correcte.
Code de Yoshi corrigé:
package tutorial.glutils; import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; import net.minecraft.tileentity.TileEntity; import org.jglrxavpok.glutils.TessellatorModel; import org.lwjgl.opengl.GL11; public class TileTestObjRenderer extends TileEntitySpecialRenderer { private TessellatorModel glmodel; public TileTestObjRenderer() { glmodel = new TessellatorModel("/assets/obj/Yoshi.obj"); glmodel.regenerateNormals(); } @Override public void renderTileEntityAt(TileEntity tileentity, double d0, double d1, double d2, float f) { GL11.glPushMatrix(); GL11.glTranslated(d0+0.5, d1, d2+0.5); GL11.glScaled(0.01, 0.01, 0.01); GL11.glShadeModel(GL11.GL_SMOOTH); glmodel.render(); GL11.glPopMatrix(); } }
Cela vous donnera un rendu beaucoup plus lisse:
Vidéo bonus :
https://www.youtube.com/watch?v=zWXUrGBsg4Y
Pour cette partie, nous prendons la Vorpal Blade du jeu Alice: Madness Returns modifié par mes soins et nous ferons basculer la lame d’avant en arrière.
Parce que je suis sympa, je vous donne le code pour l’afficher en tant que modèle fixe:
package fr.mff.tuto; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import net.minecraft.entity.Entity; import net.minecraft.item.ItemStack; import net.minecraftforge.client.IItemRenderer; import org.jglrxavpok.glutils.TessellatorModel; import org.jglrxavpok.glutils.TessellatorModelEvent; import org.jglrxavpok.glutils.TessellatorModelEvent.RenderGroupEvent; import org.lwjgl.opengl.GL11; public class ItemKnikeRenderer implements IItemRenderer { private TessellatorModel model; private double bladeAngle; private int direction; public ItemKnikeRenderer() { model = new TessellatorModel("/assets/obj/VorpalBlade.obj"); model.regenerateNormals(); } @Override public boolean handleRenderType(ItemStack item, ItemRenderType type) { switch(type) { case ENTITY: return true; case EQUIPPED_FIRST_PERSON: return true; case EQUIPPED: return true; case INVENTORY: return true; default: return false; } } @Override public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper) { if(type == ItemRenderType.INVENTORY && helper == ItemRendererHelper.INVENTORY_BLOCK) return true; return false; } @Override public void renderItem(ItemRenderType type, ItemStack item, Object... data) { GL11.glScaled(0.05,0.05,0.05); switch(type) { case ENTITY: { GL11.glPushMatrix(); GL11.glTranslated(0,0.1,0); GL11.glRotated(((Entity)data[1]).rotationYaw+=0.1, 0, 1, 0); GL11.glRotated(10, 0, 0, 1); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glPopMatrix(); break; } case EQUIPPED_FIRST_PERSON: { GL11.glPushMatrix(); GL11.glTranslated(10,10.0,0.0); GL11.glRotated(200, 0, 1, 0); GL11.glRotated(-10, 1, 0, 0); GL11.glRotated(200, 0, 1, 0); GL11.glRotated(20, 1, 0, 0); GL11.glRotated(-10, 0, 0, 1); GL11.glRotated(25, 0, 1, 0); GL11.glRotated(10, 1, 0, 0); GL11.glRotated(180, 0, 1, 0); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glPopMatrix(); break; } case EQUIPPED: { GL11.glPushMatrix(); GL11.glTranslated(10,0.0,0.0); GL11.glRotated(200, 0, 1, 0); GL11.glRotated(-10, 1, 0, 0); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glPopMatrix(); break; } case INVENTORY: { GL11.glPushMatrix(); GL11.glTranslated(10, -10, 0); GL11.glRotated(90, 0, 1, 0); GL11.glRotated(-45, 1, 0, 0); GL11.glRotated(180, 0, 1, 0); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glPopMatrix(); break; } } } }
Premièrement, vous devez savoir que les animations marchent à partir d’events Forge ! (yeah) Donc vous devez récupérer les events (nous utiliserons la classe de render pour ce tutoriel mais vous pouvez très bien utiliser une classe annexe).
Pour enregistrer votre classe et intercepter les events, ajoutez ceci dans votre classe:
TessellatorModel.MODEL_RENDERING_BUS.register(this);
Et
model.setID("votreIDDeModel");
Cet identifiant vous permet de savoir quel modèle est affiché.
Fil rouge:
package fr.mff.tuto; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import net.minecraft.entity.Entity; import net.minecraft.item.ItemStack; import net.minecraftforge.client.IItemRenderer; import org.jglrxavpok.glutils.TessellatorModel; import org.jglrxavpok.glutils.TessellatorModelEvent; import org.jglrxavpok.glutils.TessellatorModelEvent.RenderGroupEvent; import org.lwjgl.opengl.GL11; public class ItemKnikeRenderer implements IItemRenderer { private TessellatorModel model; private double bladeAngle; private int direction; public ItemKnikeRenderer() { model = new TessellatorModel("/assets/obj/VorpalBlade.obj"); model.regenerateNormals(); model.setID("blade"); TessellatorModel.MODEL_RENDERING_BUS.register(this); } @Override public boolean handleRenderType(ItemStack item, ItemRenderType type) { switch(type) { case ENTITY: return true; case EQUIPPED_FIRST_PERSON: return true; case EQUIPPED: return true; case INVENTORY: return true; default: return false; } } @Override public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper) { if(type == ItemRenderType.INVENTORY && helper == ItemRendererHelper.INVENTORY_BLOCK) return true; return false; } @Override public void renderItem(ItemRenderType type, ItemStack item, Object... data) { GL11.glScaled(0.05,0.05,0.05); switch(type) { case ENTITY: { GL11.glPushMatrix(); GL11.glTranslated(0,0.1,0); GL11.glRotated(((Entity)data[1]).rotationYaw+=0.1, 0, 1, 0); GL11.glRotated(10, 0, 0, 1); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glPopMatrix(); break; } case EQUIPPED_FIRST_PERSON: { GL11.glPushMatrix(); GL11.glTranslated(10,10.0,0.0); GL11.glRotated(200, 0, 1, 0); GL11.glRotated(-10, 1, 0, 0); GL11.glRotated(200, 0, 1, 0); GL11.glRotated(20, 1, 0, 0); GL11.glRotated(-10, 0, 0, 1); GL11.glRotated(25, 0, 1, 0); GL11.glRotated(10, 1, 0, 0); GL11.glRotated(180, 0, 1, 0); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glPopMatrix(); break; } case EQUIPPED: { GL11.glPushMatrix(); GL11.glTranslated(10,0.0,0.0); GL11.glRotated(200, 0, 1, 0); GL11.glRotated(-10, 1, 0, 0); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glPopMatrix(); break; } case INVENTORY: { GL11.glPushMatrix(); GL11.glTranslated(10, -10, 0); GL11.glRotated(90, 0, 1, 0); GL11.glRotated(-45, 1, 0, 0); GL11.glRotated(180, 0, 1, 0); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glPopMatrix(); break; } } } }
Il y a différents events:
Pour notre exemple, nous voulons balancer la lame donc avant d’afficher le modèle, nous gérons les angles:
@SubscribeEvent public void onPreRenderModel(TessellatorModelEvent.RenderPre event) { if(event.model.getID() != null && event.model.getID().equals("blade")) bladeAngle+=direction; if(bladeAngle > 65) { direction = -2; } if(bladeAngle < -65) { direction = 2; } if(direction == 0) direction = 2; }
Ainsi, la lame se balancera entre 65° et -65°.
Ensuite, il faut appliquer la rotation:
@SubscribeEvent public void onPreRenderGroup(RenderGroupEvent.Pre event) { if(event.model.getID() != null) { if(event.model.getID().equals("blade")) { if(event.group.equals("Blade")) { GL11.glPushMatrix(); double x = 0; double y = 6; double z = 0; GL11.glTranslated(x, y, z); GL11.glRotated(bladeAngle, 1, 0, 0); GL11.glTranslated(-x, -y, -z); } } } }
if(event.model.getID().equals("blade")) { if(event.group.equals("Blade")) {
Nous vérifions que le groupe à dessiner est la lame du couteau.
GL11.glPushMatrix(); double x = 0; double y = 6; double z = 0; GL11.glTranslated(x, y, z); GL11.glRotated(bladeAngle, 1, 0, 0); GL11.glTranslated(-x, -y, -z);
GL11.glPushMatrix(); on sauvegarde l’état d’OpenGL.
GL11.glRotated Application de la rotation.
GL11.glTranslated(x, y, z); et GL11.glTranslated(-x, -y, -z); permet de définir un point de rotation. (par défaut (0;0;0) )
Puisque l’on a sauvegardé l’état d’OpenGL, il faut le rétablir après avoir dessiné le groupe:
@SubscribeEvent public void onPostRenderGroup(RenderGroupEvent.Post event) { if(event.model.getID() != null) { if(event.model.getID().equals("blade")) { if(event.group.equals("Blade")) { GL11.glPopMatrix(); } } } }
Exemple complet :
package fr.mff.tuto; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import net.minecraft.entity.Entity; import net.minecraft.item.ItemStack; import net.minecraftforge.client.IItemRenderer; import org.jglrxavpok.glutils.TessellatorModel; import org.jglrxavpok.glutils.TessellatorModelEvent; import org.jglrxavpok.glutils.TessellatorModelEvent.RenderGroupEvent; import org.lwjgl.opengl.GL11; public class ItemKnikeRenderer implements IItemRenderer { private TessellatorModel model; private double bladeAngle; private int direction; public ItemKnikeRenderer() { model = new TessellatorModel("/assets/obj/VorpalBlade.obj"); model.regenerateNormals(); model.setID("blade"); TessellatorModel.MODEL_RENDERING_BUS.register(this); } @SubscribeEvent public void onPreRenderModel(TessellatorModelEvent.RenderPre event) { if(event.model.getID() != null && event.model.getID().equals("blade")) bladeAngle+=direction; if(bladeAngle > 65) { direction = -2; } if(bladeAngle < -65) { direction = 2; } if(direction == 0) direction = 2; } @SubscribeEvent public void onPreRenderGroup(RenderGroupEvent.Pre event) { if(event.model.getID() != null) { if(event.model.getID().equals("blade")) { if(event.group.equals("Blade")) { GL11.glPushMatrix(); double x = 0; double y = 6; double z = 0; GL11.glTranslated(x, y, z); GL11.glRotated(bladeAngle, 1, 0, 0); GL11.glTranslated(-x, -y, -z); } } } } @SubscribeEvent public void onPostRenderGroup(RenderGroupEvent.Post event) { if(event.model.getID() != null) { if(event.model.getID().equals("blade")) { if(event.group.equals("Blade")) { GL11.glPopMatrix(); } } } } @Override public boolean handleRenderType(ItemStack item, ItemRenderType type) { switch(type) { case ENTITY: return true; case EQUIPPED_FIRST_PERSON: return true; case EQUIPPED: return true; case INVENTORY: return true; default: return false; } } @Override public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper) { if(type == ItemRenderType.INVENTORY && helper == ItemRendererHelper.INVENTORY_BLOCK) return true; return false; } @Override public void renderItem(ItemRenderType type, ItemStack item, Object... data) { GL11.glScaled(0.05,0.05,0.05); switch(type) { case ENTITY: { GL11.glPushMatrix(); GL11.glTranslated(0,0.1,0); GL11.glRotated(((Entity)data[1]).worldObj.getB.rotationYaw+=0.1, 0, 1, 0); GL11.glRotated(10, 0, 0, 1); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glPopMatrix(); break; } case EQUIPPED_FIRST_PERSON: { GL11.glPushMatrix(); GL11.glTranslated(10,10.0,0.0); GL11.glRotated(200, 0, 1, 0); GL11.glRotated(-10, 1, 0, 0); GL11.glRotated(200, 0, 1, 0); GL11.glRotated(20, 1, 0, 0); GL11.glRotated(-10, 0, 0, 1); GL11.glRotated(25, 0, 1, 0); GL11.glRotated(10, 1, 0, 0); GL11.glRotated(180, 0, 1, 0); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glPopMatrix(); break; } case EQUIPPED: { GL11.glPushMatrix(); GL11.glTranslated(10,0.0,0.0); GL11.glRotated(200, 0, 1, 0); GL11.glRotated(-10, 1, 0, 0); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glPopMatrix(); break; } case INVENTORY: { GL11.glPushMatrix(); GL11.glTranslated(10, -10, 0); GL11.glRotated(90, 0, 1, 0); GL11.glRotated(-45, 1, 0, 0); GL11.glRotated(180, 0, 1, 0); GL11.glShadeModel(GL11.GL_SMOOTH); model.render(); GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glPopMatrix(); break; } } } }
N’hésitez pas à poser des questions!
Voilà! Ce tutoriel est maintenant fini!
@‘jglrxavpok’:
Avant ce post, vous aviez plusieurs choix:
Chercher pendant des heures comment charger correctement ce .mtl avec Forge sans rien trouver
Utiliser des librairies externes sans trop comprendre ce qu’il s’y passe
Abandonner
Super résumé de mon post, merci Clin d’oeil
J’arrivais nickel jusqu’au fichier .mtl, et là, aucun moyen d’appliquer du multitexturing, j’étais désespéré, et c’est la qu’est arrivé Super jglrxavpok et son tuto qui m’a sauvé la vie Sourire
Jte montrerai mes créations
Continue comme ça et merci Clin d’oeil
Merci
J’ai hâte de voir ce que tu va en faire
Je cherche quelque chose d’impressionnant
Ps: Maintenant si tu arrives à faire un tutoriel pour ajouter des objets animés, je t’appellerais maître toute ma vie
Possible d’avoir les sources du tuto ?
Parsque j’ai une ptit erreur:
java.lang.ArrayIndexOutOfBoundsException: -1
Fais voir ton code et le log
Je sais pourquoi, il y a un nombre de face limité pour le chargement
Adieu gros models à charger
Je vais regarder ça ;)___
Je ne crois pas qu’il y est une limite
J’arrive à charger ce gros modèle…
Envoie moi tes fichiers pour le modèle en MP
Avec maintenant une vidéo pour montrer les animations
Edit: Tutoriel modifié pour utiliser TessellatorModel.
Partie sur les animations bientôt!
[lien d’image mort …]
Un petit exemple bien simpa aussi !
Comment faire avec un mob ? Pour appliquer ce genre de model.obj à un mob ? Est-ce lourd pour minecraft ?
@‘ZeAmateis’:
Comment faire avec un mob ? Pour appliquer ce genre de model.obj à un mob ? Est-ce lourd pour minecraft ?
Pour un mob, utilise un render et appelle la méthode render() du model dans renderEntity() du render.
(Pour les renders, une petite partie est expliquée ici)
Pour le poids, tout dépend du model.
Très bien j’irais tester tout ça ! Mais j’ai un problème… Mon Model est blanc est-ce normal ?
Edit: Oublié le .mtl ><
Edit: 2 Une autre question niveau animations… Comment faire ? Est-ce possible ? Toujours pareil, est-ce lourd pour Minecraft ? Un tuto là dessus serait sympa de ta part jglrxavpok ^^
Je suis toujours en train de travailler là-dessus.
Peut-être un tuto dans 1 semaine ?
@‘jglrxavpok’:
Je suis toujours en train de travailler là-dessus.
Peut-être un tuto dans 1 semaine ?
Ah ! ça c’est cool ! Ça me sera utile pour mon projet !
L’api ne fonctionne pas en 1.7… ^^
Caused by: java.lang.NoClassDefFoundError: net/minecraftforge/event/Event
Elias, tu as du télécharger les mauvaises sources
En 1.7 la classe Event n’est plus la même https://github.com/jglrxavpok/MCGLUtils/commit/a2647cfdb3b08319e7c1955aa993e29f71324608
C’est génial! j’attend avec impatience la partie sur l’animation ^^
Elles est tj d’actualité? Et elle sera pour quelle version: 1.7 ou 1.6?
Gargan, étant donné que cette lib est une lib LWJGL et non Minecraft, elle devrait marcher tant que Minecraft utilise LWJGL.
Soit : pour toujours