Ajouter un rendu avancé à votre bloc via TESR
-
Ce tutoriel est également disponible en vidéo.
Sommaire
Introduction
Dans ce tutoriel nous allons ajouter un rendu avancé à un bloc en utilisant la méthode TileEntitySpecialRenderer (TESR).
Cette méthode a plusieurs avantages :- Elle permet de rendre un modèle fait avec techne.
- Elle permet de faire des animations.
- On peut facilement faire interagir le rendu avec l’entité du bloc en question.
Mais elle a aussi quelques limitations :
- Elle est plus lourde qu’un rendu simple du fait qu’elle est mise à jour à chaque tick. Faire un monde avec un bloc qui a un rendu TESR est donc une très mauvaise idée.
- Elle ne permet pas de faire le rendu dans l’inventaire, mais nous allons contourner ce problème avec un rendu ISBRH.
- Elle nécessite un bloc avec une entité.
Ce type de rendu est donc inévitable si vous souhaitez animer votre bloc ou passer par un modèle Techne. Dans les autres cas un rendu ISBRH sera plus intéressant car il est plus léger.
Pré-requis
- Ajouter une entité à votre bloc (Tile Entity)
- Un modèle Techne
- Rendre son bloc orientable (pour le bonus seulement)
Code
Finalisation du modèle :
SCAREX a créé un petit outil qui fait cette étape automatiquement. Il vous suffit d’entrer le code de votre modèle et mettre le nom de votre package, le script vous donnera le code corrigé. Lien : http://www.scarex.fr/model_corrector.php
Comme vous avez bien suivis les pré-requis, vous avez un modèle exporté au format Java et une texture. Déplacez le fichier .java dans le dossier forge/src/main/java/votre/package/client/ ou glissez/déposez-la directement dans votre package client sur Eclipse.
Malgré le rouge que vous voyez de partout, ça va aller très vite. Commencez par changer la déclaration du package pour cela il suffit de passer la souris sur l’erreur et prendre l’option “Change package declarations to …”. Ensuite corrigez la déclaration de la classe, il faudra aussi rectifier le nom dans le constructeur. Et voilà, toutes les erreurs restantes ne sont dû qu’à un bête problème d’importation, il suffit de faire ctrl + shift + o et de choisir net.minecraft.entity.Entity lorsqu’Eclipse vous demandera quelle classe “Entity” choisir.
Vous pouvez aussi retirer les commentaires (le //field et tout le blabla à propos de techne au dessus de la classe).
Il ne reste qu’une seule erreur à première vu, sur la dernière fonction. Pour faire simple, supprimez toute la fonction setRotationAngles, elle ne sert que dans le cas d’un mob. Du-coup vous allez avoir une erreur sur la fonction render. Modifiez aussi cette fonction, enlevez les deux lignes suivantes :super.render(entity, f, f1, f2, f3, f4, f5); setRotationAngles(f, f1, f2, f3, f4, f5);
Autre modification, remplacez la fonction :
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5)
par :
public void renderAll()
vous allez donc avoir une erreur sur chaque morceauDeModele.render(f5); remplacez donc tous les f5 par la valeur 0.0625F (utilisez ctrl + f et la fonction remplace pour aller plus vite ;))
Voilà, le modèle est maintenant prêt, sauf si vous avez utilisé la fonction mirror de techne. En effet techne fait n’importe quoi à ce niveau, si vous l’avez utilisé supprimez tous les morceauDeModele.mirror = true; ou morceauDeModele.mirror = false; présent dans votre code.
Ensuite, repérez les morceaux qui devrait être en miroir (d’où l’intérêt de les nommés et non de laisser le nom par défaut) et remettezmorceauDeModele.mirror = true;
en dessous de :
morceauDeModele = new ModelRenderer(this, valeur, valeur);
Par exemple ceci est correcte et fonctionnera :
leftPlate = new ModelRenderer(this, 0, 16); leftPlate.mirror = true; leftPlate.addBox(0F, 0F, 0F, 1, 12, 13); leftPlate.setRotationPoint(6F, 11F, -7F); leftPlate.setTextureSize(64, 64);
ceci ne fonctionnera pas :
leftPlate = new ModelRenderer(this, 0, 16); leftPlate.addBox(0F, 0F, 0F, 1, 12, 13); leftPlate.setRotationPoint(6F, 11F, -7F); leftPlate.setTextureSize(64, 64); leftPlate.mirror = true;
et ceci va causer un NullPointerException (crash) :
leftPlate.mirror = true; leftPlate = new ModelRenderer(this, 0, 16); leftPlate.addBox(0F, 0F, 0F, 1, 12, 13); leftPlate.setRotationPoint(6F, 11F, -7F); leftPlate.setTextureSize(64, 64);
(et c’est ce que fait techne alors qu’il ne devrait pas si vous avez cochez le .mirror sur un des morceaux)
Modification de la classe du bloc :
Après avoir corrigé les quelques problèmes de techne, nous allons nous attaquer à la classe du bloc. Premièrement sachez que si votre bloc a des métadatas, vous ne pourrez pas faire en sorte que certains metadatas ont un rendu normal et d’autres un rendu spécial. En revanche vous pouvez mettre plusieurs blocs avec un rendu TESR sur la même instance de bloc en utilisant des metadatas différents (jusqu’à 16, ça ne change pas des metadatas normaux).
Vous devrez normalement déjà avoir la fonction hasTileEntity et la fonction createTileEntity. Si ce n’est pas le cas, retournez voir les pré-requis car il vous faut une entité de bloc !
Ajoutez ces trois fonctions :
public boolean isOpaqueCube() { return false; } public boolean renderAsNormalBlock() { return false; } public int getRenderType() { return -1; }
La première signale que le bloc n’est pas opaque (donc que ce n’est pas un bloc remplit de 1x1x1), la deuxième signale qu’il ne doit pas être rendu normalement et la dernière dit que le type de rendu est -1 ce qui vaut à rien. Donc le bloc n’aura plus aucun rendu, et c’est voulu car c’est le TileEntitySpecialRenderer qui va se charger de tout (par défaut c’est RenderBlock qui s’en occupe).
Rendu dans le monde :
Nous allons maintenant nous occuper du rendu dans le monde. Rendez-vous dans votre classe ClientProxy. Si vous avez bien suivis nos tutoriels précédents, vous devrez dedans une fonction nommé registerRender avec peut être déjà des choses dedans.
Ajoutez en plus :ClientRegistry.bindTileEntitySpecialRenderer(TileEntityTutoriel.class, new TileEntityTutorielSpecialRenderer());
Où TileEntityTutoriel est l’entité de votre bloc et TileEntityTutorielSpecialRenderer est une nouvelle classe que nous allons tout de suite créer.
Pour une question d’organisation, je vous conseil de mettre cette classe dans votre package client (car les rendus ne concerne que le client, tout comme le modèle d’ailleurs).
Normalement Eclipse vous aura déjà mit le extends TileEntityInventorySpecialRenderer et la méthode renderTileEntityAt
Et comme je n’aime pas du tout les p_xxxx_x je vous conseil tout de suite de renommer les arguments comme ceci :public void renderTileEntityAt(TileEntity tile, double x, double y, double z, float partialRenderTick)
C’est déjà mieux
Maintenant dans cette fonction, appelez une autre fonction que vous allez nommer renderTileEntity<nom de votre entity>At avec les mêmes argument, sauf pour TileEntity que vous allez remplacez par votre TileEntity. Bien sûr créez cette fonction, puisqu’elle n’existe pas encore. Ça donne ça :@Override public void renderTileEntityAt(TileEntity tile, double x, double y, double z, float partialRenderTick) // la fonction qui était la de base { this.renderTileEntityTutorielAt((TileEntityTutoriel)tile, x, y, z, partialRenderTick); // j'appelle ma fonction renderTileEntityTutorielAt en castant TileEntityTutoriel à l'argument tile } private void renderTileEntityTutorielAt(TileEntityTutoriel tile, double x, double y, double z, float partialRenderTick) // ma propre fonction { }
Et c’est dans la fonction renderTileEntity<nom de votre entity>At que nous allons gérer le rendu.
En dessous de la déclaration de la classe (après l’accolade de la ligne public class …) créez une instance de votre modèle et de votre texture (avec un resource location) public et statique :
public static ModelBlockTutoriel model = new ModelBlockTutoriel(); public static ResourceLocation texture = new ResourceLocation(ModTutoriel.MODID, "textures/models/blocks/model_block_tutoriel.png");
Pour la première ligne je ne pense pas qu’il n’y ait besoin d’explication, remplacez juste par votre modèle.
Pour la deuxième ligne, ResourceLocation a deux arguments, le premier est le modid vous pouvez donc soit écrire directement “votre modid” ou appeler la constante que nous avons fait dans la classe principale.
Ensuite il faut mettre le chemin complet + le nom de la texture + l’extension à partir du dossier forge/src/main/resources/assets/votre modid/Dans mon cas la texture sera donc ici :
forge/src/main/resources/assets/modtutoriel/textures/models/blocks/ et sera nommé model_block_tutoriel.pngPour finir, ajoutez dans votre méthode renderTileEntity<nom de votre entity>At tout ceci :
GL11.glPushMatrix(); // ouvre une matrix GL11.glTranslated(x + 0.5D, y + 1.5D, z + 0.5D); // déplace le bloc sur les coordonnés et le centre GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); // met droit le bloc (par défaut il est à l'envers) this.bindTexture(texture); // affiche la texture model.renderAll(); // rend le modèle GL11.glPopMatrix(); // ferme la matrix
Il ne vous reste plu qu’à lancer le jeu et admirer le résultat.
Si jamais vous avez besoin d’ajuster, voici un repère :
(image trouver sur google provenant de ce site, vous pouvez faire un tour dessus, ça parle de 3D avec java)
GL11.glTranslatef(1.0F, 0.0F, 0.0F); déplace de 1 sur l’axe x.
GL11.glTranslatef(0.0F, 1.0F, 0.0F); déplace de 1 sur l’axe y.
GL11.glTranslatef(0.0F, 0.0F, 1.0F); déplace de 1 sur l’axe z.
Vous pouvez translater sur 3 en même temps dans la même fonction.
GL11.glRotatef(90F, 1.0F, 0.0F, 0.0F); effectue une rotation de 90° sur l’axe x.
GL11.glRotatef(90F, 0.0F, 1.0F, 0.0F); effectue une rotation de 90° sur l’axe y.
GL11.glRotatef(90F, 0.0F, 0.0F, 1.0F); effectue une rotation de 90° sur l’axe z.Tous ces codes sont à mettre avant “model.renderAll();”
Lancez le jeu en debug (cliquez sur le scarabée à côté de la flèche pour lancer normalement) cela vous permettra de voir les changements en directe (pas besoin de relancer le jeu à chaque fois)Rendu dans l’inventaire :
Vous l’avez surement remarqué, comme la méthode du TESR ne gère pas le rendu dans l’inventaire, c’est devenu un carré tout moche. Vous pouvez très bien modifier la texture de votre bloc pour faire en sorte qu’elle ressemble à votre modèle 3D si cela vous convient (je pense par exemple à faire une texture comme le lit ou la canne à sucre en main, ça rend pas trop mal). Mais avoir un rendu en 3D dans l’inventaire reste le mieux.
J’en profite aussi pour dire qu’il ne faut pas supprimer la texture original du bloc (celle qui est défini avec setBlockTextureName ou avec la fonction registerBlockIcons et getIcon si c’est un bloc avec metadata) car c’est elle qui va être utilisée pour les particules du bloc (celle qu’on voit lorsqu’on tape sur le bloc, le détruit ou lorsqu’on saute dessus).Je reviens au rendu dans l’inventaire (qui est au passage aussi celui du bloc lorsqu’il est jeté au sol sous la forme d’entité d’item), comme la méthode du TESR ne gère pas ça, nous allons passer par la méthode ISBRH. (ISimpleBlockRenderingHandler)
Allez à nouveau dans la classe du proxy client, et en dessous de la déclaration de la classe ajoutez :public static int tesrRenderId;
Cet id va correspondre à l’id de rendu pour **tous **vos blocs avec un rendu TESR. Et j’insiste sur le tous, car en effet ce que nous faisons dans cette partie ne sera à faire qu’une fois par mod, et pas une fois par bloc.
Ensuite dans la méthode registerRender(), ajoutez ces deux lignes :tesrRenderId = RenderingRegistry.getNextAvailableRenderId(); RenderingRegistry.registerBlockHandler(new TESRInventoryRenderer());
La première permet d’attribuer à notre variable tesrRenderId un id qui n’est pas encore occupé par un autre bloc.
La seconde ligne enregistre le rendu ISBRH.
Attention, ne changé pas l’ordre de ces deux lignes, tesrRenderId doit être initialisé avant que le rendu soit enregistré !
Créez la classe TESRInventoryRenderer, Eclipse devrait normalement automatiquement implémenter l’interface ISimpleBlockRenderingHandler et votre classe devrait ressembler à ça :package fr.minecraftforgefrance.tutoriel.client; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderBlocks; import net.minecraft.world.IBlockAccess; import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler; public class TESRInventoryRenderer implements ISimpleBlockRenderingHandler { @Override public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) { } @Override public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) { return false; } @Override public boolean shouldRender3DInInventory(int modelId) { return false; } @Override public int getRenderId() { return 0; } }
Il y a quatre méthodes, une que nous n’allons pas toucher, car elle ne nous intéresse pas. Il s’agit de renderWorldBlock, car si vous avez bien comprit le tutoriel, vous savez que le rendu dans le monde est déjà fait par le rendu TESR.
En revanche on peut déjà voir un petit problème, le but ici est de faire le rendu 3D dans l’inventaire, donc la troisième fonction n’est pas bonne par défaut. Remplacez simplement le false par true dans shouldRender3DInInventory.
Ensuite, dans la fonction getRenderId, remplacez le 0 par ClientProxy.tesrRenderIdDans la fonction renderInventoryBlock faite simplement le rendu du bloc si le bloc est bien le bloc voulu et que le metadata est le bon :
@Override public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) { if(block == ModTutoriel.blockTutoriel2 && metadata == 0) { GL11.glPushMatrix(); GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); GL11.glTranslatef(0.0F, -1.0F, 0.0F); Minecraft.getMinecraft().getTextureManager().bindTexture(TileEntityTutorielSpecialRenderer.texture); TileEntityTutorielSpecialRenderer.model.renderAll(); GL11.glPopMatrix(); } }
Et voila ! Le code est exactement le même qu’avant, il y a juste la translation qui est différente et la fonction pour bind la texture.
Si vous avez besoin d’ajuster à nouveau, les fonction GL11.glTranslatef et GL11.glRotatef fonctionne aussi ici.
Attention, il faut bien utiliser les variables qui se trouve dans la classe de votre TileEntitySpecialRenderer plutôt que de créer une nouvelle instance, car il faut appeler cette classe pour éviter un crash.
D’ailleurs en parlant de ça, allez dans la classe de votre TileEntityTutorielSpecialRenderer et ajoutez un constructeur avec ceci dedans :public NomDeLaClasse() // TileEntityTutorielSpecialRenderer dans mon cas, c'est la classe que nous avons fait dans la partie précédente { this.func_147497_a(TileEntityRendererDispatcher.instance); }
Sans cette ligne votre jeu va crasher.
Et une dernière modification, dans la classe de votre bloc, modifiez le return -1 dans la fonction getRenderType par return ClientProxy.tesrRenderId.
Voila, j’espère que vous avez réussi à suivre ! Comme il y a pleins de modification dans pleins de classes différentes ce tutoriel n’est pas l’un des plus facile.
Lorsque vous ajouterez d’autre rendu TESR à d’autre bloc, il faudra donc reprendre toute la partie 1, 2 et 3 du tutoriel (finalisation du modèle, modification dans la classe du bloc et rendu dans le monde). Par contre inutile de refaire toute cette partie, il vous suffira de mettre la fonction :@SideOnly(Side.CLIENT) public int getRenderType() { return ClientProxy.tesrRenderId; }
dans la classe de votre bloc, et dans la fonction renderInventoryBlock de la classe TESRInventoryRenderer ajoutez votre bloc. Par exemple :
@Override public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) { if(block == ModTutoriel.blockTutoriel2 && metadata == 0) { GL11.glPushMatrix(); GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); GL11.glTranslatef(0.0F, -1.0F, 0.0F); Minecraft.getMinecraft().getTextureManager().bindTexture(TileEntityTutorielSpecialRenderer.texture); TileEntityTutorielSpecialRenderer.model.renderAll(); GL11.glPopMatrix(); } else if(block == ModTutoriel.unDeuxiemeBlock && metadata == 0) // un deuxième bloc { GL11.glPushMatrix(); GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); GL11.glTranslatef(0.0F, -1.0F, 0.0F); Minecraft.getMinecraft().getTextureManager().bindTexture(TileEntityDeuxiemeSpecialRenderer.texture); TileEntityDeuxiemeSpecialRenderer.model.renderAll(); GL11.glPopMatrix(); } else if(block == ModTutoriel.unTroisiemeBlock) // un troisième bloc avec metadata { if(metadata == 0) // premier metadata { GL11.glPushMatrix(); GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); GL11.glTranslatef(0.0F, -1.0F, 0.0F); Minecraft.getMinecraft().getTextureManager().bindTexture(TileEntityTroisiemeMetadata0SpecialRenderer.texture); TileEntityTroisiemeMetadata0SpecialRenderer.model.renderAll(); GL11.glPopMatrix(); } if(metadata == 1) // deuxième metadata { GL11.glPushMatrix(); GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); GL11.glTranslatef(0.0F, -1.0F, 0.0F); Minecraft.getMinecraft().getTextureManager().bindTexture(TileEntityTroisiemeMetadata1SpecialRenderer.texture); TileEntityTroisiemeMetadata1SpecialRenderer.model.renderAll(); GL11.glPopMatrix(); } // etc ... } }
Adapter la hitbox :
La hitbox par défaut de Minecraft sera certainement pas adapté à votre modèle. Ce qui va faire que votre personnage va voler sur le bloc, et que lorsque vous allez placer la souris sur le bloc, vous allez avoir un truc énorme. Pour corriger ça, ajouter dans la classe de votre bloc cette méthode :
public void setBlockBoundsBasedOnState(IBlockAccess world, int x, int y, int z) { this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); }
Lancez votre jeu en debug puis il ne vous reste plu qu’à jouer avec ces valeurs. Augmenter les trois première et reduisez les trois dernière jusqu’à avoir ce que vous voulez.
Dans le cas d’un bloc directionnel il est possible qu’en fonction de la direction du bloc la hitbox devra avoir des valeurs différentes. Si c’est le cas, il suffit de faire :public void setBlockBoundsBasedOnState(IBlockAccess world, int x, int y, int z) { TileEntity tile = world.getTileEntity(x, y, z); if(tile instanceof TileEntityTutoriel) { TileEntityTutoriel tileTuto = (TileEntityTutoriel)tile; switch(tileTuto.getDirection()) { case 0: this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); //valeur pour la direction 0 break; case 1: this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); //valeur pour la direction 1 break; case 2: this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); //valeur pour la direction 2 break; case 3: this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); //valeur pour la direction 3 break; } } }
Rien de compliqué. Bien sûr pour voir directement en jeu si c’est bon, il faut d’abord suivre le bonus pour que le rendu se fasse en fonction de la direction.
Bonus : Faire tourner le modèle
Une fois de plus, il faut avoir bien suivis les pré-requis, vous devez avoir un bloc directionnel en passant par l’entité de bloc pour suivre cette partie.
En fait il n’y a rien de compliqué, dans la classe de votre TileEntitySpecialRenderer, dans la fonction render<votre entité de bloc>At avant :this.bindTexture(texture); model.renderAll();
ajoutez :
GL11.glRotatef(90F * tile.getDirection(), 0.0F, 1.0F, 0.0F);
Cela va effectuer une rotation de 90 dégré multiplié par la direction sur l’axe y.
Résultat
Voir le commit sur github
Le commit sur github montre clairement où ont été placés les fichiers, ainsi que ce qui a été ajouté et retiré dans le fichier.Et une petite image :
En vidéo
https://www.youtube.com/watch?v=KMFOGmje40A
Crédits
Rédaction :
Correction :
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 -
Tuto très utile, thanks!
-
Tien je me questionner est t’il possible de mettre une texture animé sur les blocs tesr?
Je suppose que oui mais on sais jamais je demande -
En mettant plusieurs resourcelocation et en faisant un bind d’une texture différente en fonction des ticks / autres choses ça doit être faisable oui.
-
Merci pour le tutoriel, mais ma texture ne se render pas inventory tu peux m’aider ?
:::package com.harrypotter.sosoh.common; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; public class BlockChaudron extends Block{ protected BlockChaudron(Material Material) { super(Material); this.setCreativeTab(CreativeTabs.tabBlock); // TODO Auto-generated constructor stub } @Override public TileEntity createTileEntity(World world, int metadata) { return new TileEntityChaudron(); } @Override public boolean hasTileEntity(int metadata) { return true; } public boolean isOpaqueCube() { return false; } public boolean renderAsNormalBlock() { return false; } public int getRenderType() { return -1; } public void setBlockBoundsBasedOnState(IBlockAccess world, int x, int y, int z) { this.setBlockBounds(0.1F, 0.0F, 0.1F, 0.9F, 0.8F, 0.9F); } }
package com.harrypotter.sosoh.client; import org.lwjgl.opengl.GL11; import com.harrypotter.sosoh.common.ModHarryPotter; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderBlocks; import net.minecraft.world.IBlockAccess; import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler; public class TESRInventoryRenderer implements ISimpleBlockRenderingHandler { @Override public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) { if(block == ModHarryPotter.blockChaudron && metadata == 0) { GL11.glPushMatrix(); GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); GL11.glTranslatef(0.0F, -1.0F, 0.0F); GL11.glRotatef(180F, 0.0F, 1.0F, 0.0F); Minecraft.getMinecraft().getTextureManager().bindTexture(TileEntityChaudronSpecialRenderer.texture); TileEntityChaudronSpecialRenderer.model.renderAll(); GL11.glPopMatrix(); } } @Override public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) { return false; } @Override public boolean shouldRender3DInInventory(int modelId) { return true; } @Override public int getRenderId() { return ClientProxy.tesrRenderId; } }
package com.harrypotter.sosoh.client; import org.lwjgl.opengl.GL11; import com.harrypotter.sosoh.common.ModHarryPotter; import com.harrypotter.sosoh.common.TileEntityChaudron; import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ResourceLocation; public class TileEntityChaudronSpecialRenderer extends TileEntitySpecialRenderer { public static ModelChaudron model = new ModelChaudron(); public static ResourceLocation texture = new ResourceLocation(ModHarryPotter.MODID, "textures/models/blocks/model_block_chaudron.png"); public TileEntityChaudronSpecialRenderer() { this.func_147497_a(TileEntityRendererDispatcher.instance); } @Override public void renderTileEntityAt(TileEntity tile, double x, double y, double z, float partialRenderTick) { this.renderTileEntityChaudronAt((TileEntityChaudron)tile, x, y, z, partialRenderTick); } private void renderTileEntityChaudronAt(TileEntityChaudron tile, double x, double y, double z, float partialRenderTick) { GL11.glPushMatrix(); GL11.glTranslated(x + 0.5D, y + 1.5D, z + 0.5D); GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); GL11.glRotatef((90F * tile.getDirection()) + 180F, 0.0F, 1.0F, 0.0F); this.bindTexture(texture); model.renderAll(); GL11.glPopMatrix(); } }
package com.harrypotter.sosoh.common; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; import net.minecraft.network.play.server.S35PacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; public class TileEntityChaudron extends TileEntity { private byte direction; @Override public void readFromNBT(NBTTagCompound compound) { super.readFromNBT(compound); this.direction = compound.getByte("Direction"); } @Override public void writeToNBT(NBTTagCompound compound) { super.writeToNBT(compound); compound.setByte("Direction", this.direction); } public byte getDirection() { return direction; } public void setDirection(byte direction) { this.direction = direction; this.worldObj.markBlockForUpdate(this.xCoord, this.yCoord, this.zCoord); } public Packet getDescriptionPacket() { NBTTagCompound nbttagcompound = new NBTTagCompound(); this.writeToNBT(nbttagcompound); return new S35PacketUpdateTileEntity(this.xCoord, this.yCoord, this.zCoord, 0, nbttagcompound); } public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) { this.readFromNBT(pkt.func_148857_g()); this.worldObj.markBlockRangeForRenderUpdate(this.xCoord, this.yCoord, this.zCoord, this.xCoord, this.yCoord, this.zCoord); } }
//Date: 16/11/2014 16:27:03 //Template version 1.1 //Java generated by Techne //Keep in mind that you still need to fill in some blanks //- ZeuX package com.harrypotter.sosoh.client; import net.minecraft.client.model.ModelBase; import net.minecraft.client.model.ModelRenderer; public class ModelChaudron extends ModelBase { //fields ModelRenderer Base; ModelRenderer Pied1; ModelRenderer Pied2; ModelRenderer Pied3; ModelRenderer Pied4; ModelRenderer Shape1; ModelRenderer Shape2; ModelRenderer Shape3; ModelRenderer Shape4; public ModelChaudron() { textureWidth = 64; textureHeight = 64; Base = new ModelRenderer(this, 0, 0); Base.addBox(0F, 0F, 0F, 10, 1, 10); Base.setRotationPoint(-5F, 22F, -5F); Base.setTextureSize(64, 64); setRotation(Base, 0F, 0F, 0F); Pied1 = new ModelRenderer(this, 55, 3); Pied1.addBox(0F, 0F, 0F, 1, 1, 1); Pied1.setRotationPoint(5F, 23F, -6F); Pied1.setTextureSize(64, 64); setRotation(Pied1, 0F, 0F, 0F); Pied2 = new ModelRenderer(this, 55, 0); Pied2.addBox(0F, 0F, 0F, 1, 1, 1); Pied2.setRotationPoint(-6F, 23F, 5F); Pied2.setTextureSize(64, 64); setRotation(Pied2, 0F, 0F, 0F); Pied3 = new ModelRenderer(this, 55, 0); Pied3.addBox(0F, 0F, 0F, 1, 1, 1); Pied3.setRotationPoint(5F, 23F, 5F); Pied3.setTextureSize(64, 64); setRotation(Pied3, 0F, 0F, 0F); Pied4 = new ModelRenderer(this, 55, 3); Pied4.addBox(0F, 0F, 0F, 1, 1, 1); Pied4.setRotationPoint(-6F, 23F, -6F); Pied4.setTextureSize(64, 64); setRotation(Pied4, 0F, 0F, 0F); Shape1 = new ModelRenderer(this, 27, 30); Shape1.addBox(0F, 0F, 0F, 12, 11, 1); Shape1.setRotationPoint(-6F, 12F, 5F); Shape1.setTextureSize(64, 64); setRotation(Shape1, 0F, 0F, 0F); Shape2 = new ModelRenderer(this, 0, 30); Shape2.addBox(0F, 0F, 0F, 12, 11, 1); Shape2.setRotationPoint(-6F, 12F, -6F); Shape2.setTextureSize(64, 64); setRotation(Shape2, 0F, 0F, 0F); Shape3 = new ModelRenderer(this, 0, 43); Shape3.addBox(0F, 0F, 0F, 1, 11, 10); Shape3.setRotationPoint(5F, 12F, -5F); Shape3.setTextureSize(64, 64); setRotation(Shape3, 0F, 0F, 0F); Shape4 = new ModelRenderer(this, 23, 43); Shape4.addBox(0F, 0F, 0F, 1, 11, 10); Shape4.setRotationPoint(-6F, 12F, -5F); Shape4.setTextureSize(64, 64); setRotation(Shape4, 0F, 0F, 0F); } public void renderAll() { Base.render(0.0625F); Pied1.render(0.0625F); Pied2.render(0.0625F); Pied3.render(0.0625F); Pied4.render(0.0625F); Shape1.render(0.0625F); Shape2.render(0.0625F); Shape3.render(0.0625F); Shape4.render(0.0625F); } private void setRotation(ModelRenderer model, float x, float y, float z) { model.rotateAngleX = x; model.rotateAngleY = y; model.rotateAngleZ = z; } }
package com.harrypotter.sosoh.client; import com.harrypotter.sosoh.common.CommonProxy; import com.harrypotter.sosoh.common.TileEntityChaudron; import cpw.mods.fml.client.registry.ClientRegistry; import cpw.mods.fml.client.registry.RenderingRegistry; public class ClientProxy extends CommonProxy { public static int tesrRenderId; @Override public void registerRender() { System.out.println("méthode côté client"); tesrRenderId = RenderingRegistry.getNextAvailableRenderId(); RenderingRegistry.registerBlockHandler(new TESRInventoryRenderer()); ClientRegistry.bindTileEntitySpecialRenderer(TileEntityChaudron.class, new TileEntityChaudronSpecialRenderer()); } }
:::
-
Et une dernière modification, dans la classe de votre bloc, modifiez le return -1 dans la fonction getRenderType par return ClientProxy.tesrRenderId.
Dans ta classe BlockCauldron :
public int getRenderType() { return -1; }
Devrait être :
public int getRenderType() { return ClientProxy.tesrRenderId; }
-
Merci ^^ C’est bon c’est ok !
-
Hello, superbe tuto. Je doit avouer que c’est quelques choses de très utilise dès qu’on souhaite faire un peu plus que de l’ajout de block/item basique.
Sinon, j’aurai une petite question. Je n’ai jamais vraiment trop causé sur le forum, malgré le temps que j’y ai passé… Mais, voila, j’aimerai savoir, si on souhaite faire un rendu d’un escalier en modifiant son block bounds, comment faire pour qu’il y ai encore la jointure dans les angles ?
Ça fait un bout de temps que je cherche mais je n’ai jamais réussi a résoudre mon problème. Et disons que passer par un rendu TESR, ça me parait lourd pour juste un escalier
En espérant trouver une réponse,
Salutation a vous,
0xPic0. -
Pour un escalier custom passe plutôt par un rendu ISBRH (il y a un tutoriel 1.6, il n’y a pas beaucoup de différence).
Il suffit d’adapter le rendu en fonction des blocs autours (avec world.getBlock(x, y, z +1) == TaClassePrincipale.tonEscalier par exemple) -
Okay, merci robin. Mon rendu tourne déjà en ISBRH, c’est pour cela que je demandais si TESR était mieux ^^ . Je vais essayer de corriger mon soucis d’angle dans ce cas. Si j’y arrive alors j’arriverai a résoudre le rendu de l’escalier quand il est mit a l’envers
Je reviendrai vers vous si jamais je ne m’en sort pas mais ça devrait aller
Merci beaucoup.
0xPic0
Edit: Heu, je devait avoir la tête ailleurs, m’enfin, ce que tu m’a proposé est déjà en place. Là ou je pêche, c’est sur le rendu vers le haut et les angles. Physiquement, Steve ressent l’angle, seulement, graphiquement, ce n’est pas correct. Quand au bloc a l’envers, c’est l’affichage qui ne se fait pas. J’ai chercher dans la classe BlockStairs mais, :fuuuu: qu’est-ce que c’est le bordel toutes des méthodes sans doc et sans nomination intelligente ^^, j’espère trouver
-
Plop!
J’ai comme qui dirait un p’tit problème avec TESR : ma méthode renderTileEntityAt(TileEntity, double, double, double, float) n’est jamais appelée…
J’ai vérifié avec des System.out.println, c’est bien à partir de là que ça marche pas; le bloc est là, la hitbox est visible, mais aucun rendu, aucune texture, il est totalement invisible…
Mon code
Mon ClientProxy
:::package sasukz.htmc.proxy; import sasukz.htmc.blocks.tileentities.TECopperCable; import sasukz.htmc.client.rendering.TESRCopperCable; import cpw.mods.fml.client.registry.ClientRegistry; public class ClientProxy extends CommonProxy { @Override public void registerRender() { System.out.println("[DEBUG] TESR chargé"); ClientRegistry.bindTileEntitySpecialRenderer(TECopperCable.class, new TESRCopperCable()); } }
:::
Mon TileEntity
:::package sasukz.htmc.blocks.tileentities; import java.util.ArrayList; import net.minecraft.block.Block; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import sasukz.htmc.blocks.tileentities.utils.TEElectrical; import sasukz.htmc.common.HTMC; public class TECopperCable extends TEElectrical { public ArrayList <forgedirection>getConnections() { System.out.println("[DEBUG] Méthode getConnections() appelée"); ArrayList <forgedirection>r = new ArrayList<forgedirection>(); Block cc = HTMC.t_CopperCable; World w = getWorldObj(); if (w.getBlock(xCoord, yCoord+1, zCoord).equals(cc)) r.add(ForgeDirection.UP); if (w.getBlock(xCoord, yCoord-1, zCoord).equals(cc)) r.add(ForgeDirection.DOWN); if (w.getBlock(xCoord+1, yCoord, zCoord).equals(cc)) r.add(ForgeDirection.EAST); if (w.getBlock(xCoord-1, yCoord, zCoord).equals(cc)) r.add(ForgeDirection.WEST); if (w.getBlock(xCoord, yCoord, zCoord+1).equals(cc)) r.add(ForgeDirection.SOUTH); if (w.getBlock(xCoord, yCoord, zCoord-1).equals(cc)) r.add(ForgeDirection.NORTH); return r; } @Override public int getMaxEnergy() { return 128; } @Override public int getMinPower() { return 2; } @Override public int getMaxPower() { return 64; } }
:::
Mon TESR
:::package sasukz.htmc.client.rendering; import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ResourceLocation; import org.lwjgl.opengl.GL11; import sasukz.htmc.blocks.tileentities.TECopperCable; import sasukz.htmc.common.HTMC; import sasukz.htmc.models.ModelCopperCable; public class TESRCopperCable extends TileEntitySpecialRenderer { public static ModelCopperCable model = new ModelCopperCable(); public static ResourceLocation texture = new ResourceLocation(HTMC.MODID, "textures/models/blocks/copper_cable.png"); @Override public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialRenderTick) { System.out.println("[HTMC] Méthode renderTileEntityAt() appelée"); GL11.glPushMatrix(); GL11.glTranslated(x + 0.5D, y + 1.5D, z + 0.5D); GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); bindTexture(texture); model.renderAll((TECopperCable)te); GL11.glPopMatrix(); } }
:::
Merci d’avance :)</forgedirection></forgedirection></forgedirection>
-
Ton bloc ? Ta classe principale ?
-
Mon bloc
:::package sasukz.htmc.blocks; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import sasukz.htmc.blocks.tileentities.TECopperCable; import sasukz.htmc.common.HTMC; public class BlockCopperCable extends Block { public BlockCopperCable(Material m) { super(m); setHardness(0.5F); setResistance(0.0F); setBlockName("copper_cable"); setCreativeTab(HTMC.ctab_mach); } @Override public boolean hasTileEntity() { return true; } @Override public TileEntity createTileEntity(World world, int metadata) { return new TECopperCable(); } @Override public boolean isOpaqueCube() { return false; } @Override public boolean renderAsNormalBlock() { return false; } @Override public int getRenderType() { return -1; } }
:::
Ma classe principale
:::package sasukz.htmc.common; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.item.Item; import sasukz.htmc.blocks.BlockCombiner; import sasukz.htmc.blocks.BlockCopperCable; import sasukz.htmc.blocks.BlockMacerator; import sasukz.htmc.blocks.BlockOreAluminum; import sasukz.htmc.blocks.BlockOreCopper; import sasukz.htmc.blocks.BlockOreLead; import sasukz.htmc.blocks.BlockOreSilicon; import sasukz.htmc.blocks.BlockOreSilver; import sasukz.htmc.blocks.BlockOreTin; import sasukz.htmc.blocks.BlockOreUranium; import sasukz.htmc.blocks.tileentities.TECombiner; import sasukz.htmc.blocks.tileentities.TECopperCable; import sasukz.htmc.blocks.tileentities.TEMacerator; import sasukz.htmc.generation.HTMCWorldGen; import sasukz.htmc.items.ItemAluminumPlate; import sasukz.htmc.items.ItemBasicCircuit; import sasukz.htmc.items.ItemDustAluminum; import sasukz.htmc.items.ItemDustCopper; import sasukz.htmc.items.ItemDustLead; import sasukz.htmc.items.ItemDustSilver; import sasukz.htmc.items.ItemDustTin; import sasukz.htmc.items.ItemIngotAluminum; import sasukz.htmc.items.ItemIngotBronze; import sasukz.htmc.items.ItemIngotCopper; import sasukz.htmc.items.ItemIngotLead; import sasukz.htmc.items.ItemIngotSilver; import sasukz.htmc.items.ItemIngotTin; import sasukz.htmc.items.ItemSilicon; import sasukz.htmc.items.ItemWrench; import sasukz.htmc.network.HTMCGUIHandler; import sasukz.htmc.proxy.CommonProxy; import cpw.mods.fml.common.Mod; import cpw.mods.fml.common.Mod.EventHandler; import cpw.mods.fml.common.Mod.Instance; import cpw.mods.fml.common.SidedProxy; import cpw.mods.fml.common.event.FMLInitializationEvent; import cpw.mods.fml.common.event.FMLPostInitializationEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; import cpw.mods.fml.common.network.NetworkRegistry; import cpw.mods.fml.common.registry.GameRegistry; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @Mod(modid="htmc", name="HTMC Mod", version="MC 1.7.10") public class HTMC { @Instance("htmc") public static HTMC instance; @SidedProxy(clientSide="sasukz.htmc.proxy.ClientProxy", serverSide="sasukz.htmc.proxy.CommonProxy") public static CommonProxy proxy; public static final String MODID = "htmc"; public static HTMCWorldGen worldgen = new HTMCWorldGen(); public static Block t_TinOre; public static Block t_CopperOre; public static Block t_SilverOre; public static Block t_UraniumOre; public static Block t_LeadOre; public static Block t_AluminumOre; public static Block t_SiliconOre; public static Block t_Combiner; public static Block t_Macerator; public static Block t_CopperCable; public static Item i_CopperIngot; public static Item i_TinIngot; public static Item i_SilverIngot; public static Item i_LeadIngot; public static Item i_AluminumIngot; public static Item i_BronzeIngot; public static Item i_Silicon; public static Item i_AluminumPlate; public static Item i_BasicCircuit; public static Item i_Wrench; public static Item i_CopperDust; public static Item i_TinDust; public static Item i_AluminumDust; public static Item i_LeadDust; public static Item i_SilverDust; public static CreativeTabs ctab_mat = new CreativeTabs("htmc_materials") { @Override public Item getTabIconItem() { return HTMC.i_CopperIngot; } @SideOnly(Side.CLIENT) public int func_151243_f() { return 0; } }; public static CreativeTabs ctab_comp = new CreativeTabs("htmc_components") { @Override public Item getTabIconItem() { return HTMC.i_BasicCircuit; } @SideOnly(Side.CLIENT) public int func_151243_f() { return 0; } }; public static CreativeTabs ctab_mach = new CreativeTabs("htmc_machines") { @Override public Item getTabIconItem() { return Item.getItemFromBlock(HTMC.t_Combiner); } @SideOnly(Side.CLIENT) public int func_151243_f() { return 0; } }; @EventHandler public static void preInit(FMLPreInitializationEvent e) { GameRegistry.registerWorldGenerator(worldgen, 0); // BLOCKS –- t_TinOre = new BlockOreTin(Material.rock); GameRegistry.registerBlock(t_TinOre, "ore_tin"); t_CopperOre = new BlockOreCopper(Material.rock); GameRegistry.registerBlock(t_CopperOre, "ore_copper"); t_SilverOre = new BlockOreSilver(Material.rock); GameRegistry.registerBlock(t_SilverOre, "ore_silver"); t_UraniumOre = new BlockOreUranium(Material.rock); GameRegistry.registerBlock(t_UraniumOre, "ore_uranium"); t_LeadOre = new BlockOreLead(Material.rock); GameRegistry.registerBlock(t_LeadOre, "ore_lead"); t_AluminumOre = new BlockOreAluminum(Material.rock); GameRegistry.registerBlock(t_AluminumOre, "ore_aluminum"); t_SiliconOre = new BlockOreSilicon(Material.rock); GameRegistry.registerBlock(t_SiliconOre, "ore_silicon"); t_Combiner = new BlockCombiner(Material.rock); GameRegistry.registerBlock(t_Combiner, "combiner"); t_Macerator = new BlockMacerator(Material.rock); GameRegistry.registerBlock(t_Macerator, "macerator"); t_CopperCable = new BlockCopperCable(Material.iron); GameRegistry.registerBlock(t_CopperCable, "copper_cable"); // ITEMS --- i_CopperIngot = new ItemIngotCopper(); GameRegistry.registerItem(i_CopperIngot, "ingot_copper"); i_TinIngot = new ItemIngotTin(); GameRegistry.registerItem(i_TinIngot, "ingot_tin"); i_SilverIngot = new ItemIngotSilver(); GameRegistry.registerItem(i_SilverIngot, "ingot_silver"); i_LeadIngot = new ItemIngotLead(); GameRegistry.registerItem(i_LeadIngot, "ingot_lead"); i_AluminumIngot = new ItemIngotAluminum(); GameRegistry.registerItem(i_AluminumIngot, "ingot_aluminum"); i_Silicon = new ItemSilicon(); GameRegistry.registerItem(i_Silicon, "silicon"); i_BasicCircuit = new ItemBasicCircuit(); GameRegistry.registerItem(i_BasicCircuit, "basic_circuit"); i_AluminumPlate = new ItemAluminumPlate(); GameRegistry.registerItem(i_AluminumPlate, "aluminum_plate"); i_Wrench = new ItemWrench(); GameRegistry.registerItem(i_Wrench, "wrench"); i_CopperDust = new ItemDustCopper(); GameRegistry.registerItem(i_CopperDust, "dust_copper"); i_TinDust = new ItemDustTin(); GameRegistry.registerItem(i_TinDust, "dust_tin"); i_BronzeIngot = new ItemIngotBronze(); GameRegistry.registerItem(i_BronzeIngot, "ingot_bronze"); i_AluminumDust = new ItemDustAluminum(); GameRegistry.registerItem(i_AluminumDust, "dust_aluminum"); i_LeadDust = new ItemDustLead(); GameRegistry.registerItem(i_LeadDust, "dust_lead"); i_SilverDust = new ItemDustSilver(); GameRegistry.registerItem(i_SilverDust, "dust_silver"); } @EventHandler public static void init(FMLInitializationEvent e) { proxy.registerRender(); GameRegistry.registerTileEntity(TECombiner.class, MODID+":combiner"); GameRegistry.registerTileEntity(TEMacerator.class, MODID+":macerator"); System.out.println("[DEBUG] TileEntity enregistré."); GameRegistry.registerTileEntity(TECopperCable.class, MODID+":copper_cable"); HTMCRecipes.addRecipes(); HTMCRecipes.addSmeltings(); } @EventHandler public static void postInit(FMLPostInitializationEvent e) { NetworkRegistry.INSTANCE.registerGuiHandler(instance, new HTMCGUIHandler()); } }
:::
-
Je ne vois aucun problème, essaye de créer un nouveau monde puis de replacer des blocs.
Le seul truc que je trouve étrange, c’est que tes fonctions init, preInit sont statics dans ta classe principale, normalement il n’y a pas besoin. -
ça marche toujours pas
Je suis certain que le problème vient de mon TESR, la méthode est jamais appelée.
-
Pourtant ton TESR est bien enregistré dans le client proxy, à la limite envoie un zip de ton dossier src, je vais regarder quand j’aurai le temps.
-
Bonjour,
J’ai donc ajouté un rendu Techne à mon bloc grâce au super tuto de robin mais j’ai un soucis quand je regarde mon block dans mon mod je lag énormément …
Par déduction je pense que ceci vient du nombre de tick par seconde et je voudrais savoir si il était possible de le diminuer.
et si oui comment.
Merci d’avance
-
Il me semble pas que ce soit possible de réduire ça.
Ton modèle ressemble à quoi ? Car j’ai jamais eu de grosse chute de fps à cause d’un TESR … -
Arf mon modèle est petit mais à 240 parties sous techne …
Tu pense que le problème vient de la ?
-
Surement oui, 240 ça fait beaucoup.