Introduce abstract superclass for LayerFrost and LayerStone, and fix some weirdness in the process
This commit is contained in:
@@ -689,8 +689,8 @@ public class ClientProxy extends CommonProxy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialiseLayers(){
|
public void initialiseLayers(){
|
||||||
LayerStone.initialiseLayers();
|
LayerTiledOverlay.initialiseLayers(LayerStone::new);
|
||||||
LayerFrost.initialiseLayers();
|
LayerTiledOverlay.initialiseLayers(LayerFrost::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import net.minecraft.client.renderer.OpenGlHelper;
|
|||||||
import net.minecraft.client.renderer.entity.Render;
|
import net.minecraft.client.renderer.entity.Render;
|
||||||
import net.minecraft.client.renderer.entity.RenderLivingBase;
|
import net.minecraft.client.renderer.entity.RenderLivingBase;
|
||||||
import net.minecraft.client.renderer.entity.RenderPlayer;
|
import net.minecraft.client.renderer.entity.RenderPlayer;
|
||||||
import net.minecraft.client.renderer.entity.layers.LayerRenderer;
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityLivingBase;
|
import net.minecraft.entity.EntityLivingBase;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
@@ -23,154 +22,39 @@ import net.minecraft.util.math.MathHelper;
|
|||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layer used to render the frost texture on a creature with the frostbite effect. Handles dynamic tiling of the texture.
|
* Layer used to render the frost texture on creatures with the frostbite effect.
|
||||||
*
|
*
|
||||||
* @author Electroblob
|
* @author Electroblob
|
||||||
* @since Wizardry 1.2
|
* @since Wizardry 1.2
|
||||||
*/
|
*/
|
||||||
public class LayerFrost implements LayerRenderer<EntityLivingBase> {
|
public class LayerFrost extends LayerTiledOverlay<EntityLivingBase> {
|
||||||
|
|
||||||
protected ModelBase model;
|
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/frost_overlay.png");
|
||||||
private final RenderLivingBase<?> renderer;
|
|
||||||
|
|
||||||
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/frost_overlay.png");
|
|
||||||
|
|
||||||
public static void initialiseLayers(){
|
|
||||||
|
|
||||||
for(Render<? extends Entity> renderer : Minecraft.getMinecraft().getRenderManager().entityRenderMap.values()){
|
|
||||||
// Because the zombie classes are now split properly, their renderers play nicely like everything else.
|
|
||||||
if(renderer instanceof RenderLivingBase){
|
|
||||||
// Adds a frost layer to all the living entity renderers in the game. Whether it is actually rendered
|
|
||||||
// is decided in doRenderLayer below on a per-entity basis.
|
|
||||||
((RenderLivingBase<?>)renderer).addLayer(new LayerFrost((RenderLivingBase<?>)renderer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(RenderPlayer renderer : Minecraft.getMinecraft().getRenderManager().getSkinMap().values()){
|
|
||||||
renderer.addLayer(new LayerFrost(renderer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public LayerFrost(RenderLivingBase<?> renderer){
|
public LayerFrost(RenderLivingBase<?> renderer){
|
||||||
this.renderer = renderer;
|
super(renderer);
|
||||||
this.model = renderer.getMainModel();
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRender(EntityLivingBase entity, float partialTicks){
|
||||||
|
return entity.isPotionActive(WizardryPotions.frost) || entity.getEntityData().getBoolean(BlockStatue.FROZEN_NBT_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTexture(EntityLivingBase entity, float partialTicks){
|
||||||
|
return TEXTURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
||||||
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
||||||
|
|
||||||
if(entity.isPotionActive(WizardryPotions.frost) || entity.getEntityData().getBoolean(BlockStatue.FROZEN_NBT_KEY)){
|
GlStateManager.enableBlend();
|
||||||
|
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
GlStateManager.enableLighting();
|
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
|
||||||
int i = this.getBlockBrightnessForEntity(entity, partialTicks);
|
|
||||||
|
|
||||||
int j = i % 65536;
|
GlStateManager.disableBlend();
|
||||||
int k = i / 65536;
|
|
||||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F);
|
|
||||||
|
|
||||||
// Frost texture
|
|
||||||
GlStateManager.enableBlend();
|
|
||||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
|
||||||
this.renderer.bindTexture(texture);
|
|
||||||
this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw,
|
|
||||||
headPitch, scale);
|
|
||||||
GlStateManager.disableBlend();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getBlockBrightnessForEntity(Entity entity, float partialTicks){
|
|
||||||
|
|
||||||
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(MathHelper.floor(entity.posX), 0,
|
|
||||||
MathHelper.floor(entity.posZ));
|
|
||||||
|
|
||||||
if(entity.world.isBlockLoaded(pos)){
|
|
||||||
pos.setY(MathHelper.floor(entity.posY + (double)entity.getEyeHeight()));
|
|
||||||
return entity.world.getCombinedLight(pos, 0);
|
|
||||||
}else{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderEntityModel(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
|
||||||
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
|
||||||
|
|
||||||
GlStateManager.pushMatrix();
|
|
||||||
// Enables tiling (Also used for guardian beam, beacon beam and ender crystal beam)
|
|
||||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
|
|
||||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
|
|
||||||
|
|
||||||
GlStateManager.depthMask(true); // Some entities set depth mask to false (i.e. no sorting of faces by depth)
|
|
||||||
// In particular, LayerSpiderEyes sets it to false when the spider is invisible, for some reason.
|
|
||||||
|
|
||||||
// Changes the scale at which the texture is applied to the model. See LayerCreeper for a similar example,
|
|
||||||
// but with translation instead of scaling.
|
|
||||||
// NOTE: You can do all sorts of fun stuff with this, just by applying transformations in the 2D texture space.
|
|
||||||
GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
|
||||||
GlStateManager.loadIdentity();
|
|
||||||
double scaleX = 1, scaleY = 1;
|
|
||||||
// It's more logical to use the model's texture size, but some classes don't bother setting it properly
|
|
||||||
// (e.g. ModelVillager), so to get the correct dimensions I'm getting them from the first box instead.
|
|
||||||
if(model.boxList != null && model.boxList.size() >= 1 && model.boxList.get(0) != null){
|
|
||||||
scaleX = (double)model.boxList.get(0).textureWidth / 16d;
|
|
||||||
scaleY = (double)model.boxList.get(0).textureHeight / 16d;
|
|
||||||
}else{ // Fallback to model fields; should never be needed
|
|
||||||
scaleX = (double)model.textureWidth / 16d;
|
|
||||||
scaleY = (double)model.textureHeight / 16d;
|
|
||||||
}
|
|
||||||
GlStateManager.scale(scaleX, scaleY, 1);
|
|
||||||
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
|
||||||
|
|
||||||
boolean headWearHidden = false;
|
|
||||||
boolean bodyWearHidden = false;
|
|
||||||
boolean leftArmWearHidden = false;
|
|
||||||
boolean rightArmWearHidden = false;
|
|
||||||
boolean leftLegWearHidden = false;
|
|
||||||
boolean rightLegWearHidden = false;
|
|
||||||
|
|
||||||
// Hides the hat layer for bipeds
|
|
||||||
if(this.model instanceof ModelBiped){
|
|
||||||
headWearHidden = ((ModelBiped)this.model).bipedHeadwear.isHidden;
|
|
||||||
((ModelBiped)this.model).bipedHeadwear.isHidden = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.model instanceof ModelPlayer){
|
|
||||||
bodyWearHidden = ((ModelPlayer)this.model).bipedBodyWear.isHidden;
|
|
||||||
leftArmWearHidden = ((ModelPlayer)this.model).bipedLeftArmwear.isHidden;
|
|
||||||
rightArmWearHidden = ((ModelPlayer)this.model).bipedRightArmwear.isHidden;
|
|
||||||
leftLegWearHidden = ((ModelPlayer)this.model).bipedLeftLegwear.isHidden;
|
|
||||||
rightLegWearHidden = ((ModelPlayer)this.model).bipedRightLegwear.isHidden;
|
|
||||||
((ModelPlayer)this.model).bipedBodyWear.isHidden = true;
|
|
||||||
((ModelPlayer)this.model).bipedLeftArmwear.isHidden = true;
|
|
||||||
((ModelPlayer)this.model).bipedRightArmwear.isHidden = true;
|
|
||||||
((ModelPlayer)this.model).bipedLeftLegwear.isHidden = true;
|
|
||||||
((ModelPlayer)this.model).bipedRightLegwear.isHidden = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.model.setLivingAnimations(entity, limbSwing, limbSwingAmount, partialTicks);
|
|
||||||
this.model.render(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
|
|
||||||
|
|
||||||
if(this.model instanceof ModelBiped) ((ModelBiped)this.model).bipedHeadwear.isHidden = headWearHidden;
|
|
||||||
|
|
||||||
if(this.model instanceof ModelPlayer){
|
|
||||||
((ModelPlayer)this.model).bipedBodyWear.isHidden = bodyWearHidden;
|
|
||||||
((ModelPlayer)this.model).bipedLeftArmwear.isHidden = leftArmWearHidden;
|
|
||||||
((ModelPlayer)this.model).bipedRightArmwear.isHidden = rightArmWearHidden;
|
|
||||||
((ModelPlayer)this.model).bipedLeftLegwear.isHidden = leftLegWearHidden;
|
|
||||||
((ModelPlayer)this.model).bipedRightLegwear.isHidden = rightLegWearHidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Undoes the texture scaling
|
|
||||||
GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
|
||||||
GlStateManager.loadIdentity();
|
|
||||||
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
|
||||||
|
|
||||||
GlStateManager.popMatrix();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldCombineTextures(){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,16 +2,13 @@ package electroblob.wizardry.client.renderer;
|
|||||||
|
|
||||||
import electroblob.wizardry.block.BlockStatue;
|
import electroblob.wizardry.block.BlockStatue;
|
||||||
import electroblob.wizardry.client.ClientProxy;
|
import electroblob.wizardry.client.ClientProxy;
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.model.ModelBase;
|
import net.minecraft.client.model.ModelBase;
|
||||||
import net.minecraft.client.model.ModelBiped;
|
import net.minecraft.client.model.ModelBiped;
|
||||||
import net.minecraft.client.renderer.GlStateManager;
|
import net.minecraft.client.renderer.GlStateManager;
|
||||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||||
import net.minecraft.client.renderer.OpenGlHelper;
|
import net.minecraft.client.renderer.OpenGlHelper;
|
||||||
import net.minecraft.client.renderer.entity.Render;
|
|
||||||
import net.minecraft.client.renderer.entity.RenderLivingBase;
|
import net.minecraft.client.renderer.entity.RenderLivingBase;
|
||||||
import net.minecraft.client.renderer.entity.layers.LayerRenderer;
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityLivingBase;
|
import net.minecraft.entity.EntityLivingBase;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
@@ -20,35 +17,30 @@ import net.minecraft.util.math.MathHelper;
|
|||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layer used to render the stone texture on a petrified creature. Handles dynamic tiling of the stone texture.
|
* Layer used to render the stone texture on a petrified creature.
|
||||||
*
|
*
|
||||||
* @author Electroblob
|
* @author Electroblob
|
||||||
* @since Wizardry 1.2
|
* @since Wizardry 1.2
|
||||||
*/
|
*/
|
||||||
public class LayerStone implements LayerRenderer<EntityLivingBase> {
|
public class LayerStone extends LayerTiledOverlay<EntityLivingBase> {
|
||||||
|
|
||||||
protected ModelBase model;
|
private static final ResourceLocation TEXTURE = new ResourceLocation("textures/blocks/stone.png");
|
||||||
private final RenderLivingBase<?> renderer;
|
|
||||||
|
|
||||||
private static final ResourceLocation texture = new ResourceLocation("textures/blocks/stone.png");
|
|
||||||
|
|
||||||
public static void initialiseLayers(){
|
|
||||||
|
|
||||||
for(Render<? extends Entity> renderer : Minecraft.getMinecraft().getRenderManager().entityRenderMap.values()){
|
|
||||||
// Because the zombie classes are now split properly, their renderers play nicely like everything else.
|
|
||||||
if(renderer instanceof RenderLivingBase){
|
|
||||||
// Adds a stone layer to all the living entity renderers in the game. Whether it is actually rendered
|
|
||||||
// is decided in doRenderLayer below on a per-entity basis.
|
|
||||||
((RenderLivingBase<?>)renderer).addLayer(new LayerStone((RenderLivingBase<?>)renderer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public LayerStone(RenderLivingBase<?> renderer){
|
public LayerStone(RenderLivingBase<?> renderer){
|
||||||
this.renderer = renderer;
|
super(renderer);
|
||||||
this.model = renderer.getMainModel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRender(EntityLivingBase entity, float partialTicks){
|
||||||
|
return entity.getEntityData().getBoolean(BlockStatue.PETRIFIED_NBT_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTexture(EntityLivingBase entity, float partialTicks){
|
||||||
|
ResourceLocation breakingTexture = ClientProxy.renderStatue.getBlockBreakingTexture();
|
||||||
|
return breakingTexture == null ? TEXTURE : breakingTexture;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Does not work with zombie pigmen, I have no idea why.
|
// FIXME: Does not work with zombie pigmen, I have no idea why.
|
||||||
// I believe the issue is with the TESR actually, since LayerFrost works fine
|
// I believe the issue is with the TESR actually, since LayerFrost works fine
|
||||||
|
|
||||||
@@ -56,96 +48,19 @@ public class LayerStone implements LayerRenderer<EntityLivingBase> {
|
|||||||
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
||||||
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
||||||
|
|
||||||
if(entity.getEntityData().getBoolean(BlockStatue.PETRIFIED_NBT_KEY)){
|
// N.B. It's one or the other because this effectively gets called twice via RenderStatue
|
||||||
|
if(ClientProxy.renderStatue.getBlockBreakingTexture() != null){
|
||||||
|
|
||||||
GlStateManager.enableLighting();
|
GlStateManager.enableBlend();
|
||||||
int i = this.getBlockBrightnessForEntity(entity, partialTicks);
|
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
int j = i % 65536;
|
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
|
||||||
int k = i / 65536;
|
|
||||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F);
|
|
||||||
|
|
||||||
ResourceLocation breakingTexture = ClientProxy.renderStatue.getBlockBreakingTexture();
|
GlStateManager.disableBlend();
|
||||||
|
|
||||||
if(breakingTexture != null){
|
|
||||||
// Block breaking animation
|
|
||||||
// TODO: Spider eyes and enderman eyes (any others?) show through the stone when the block is being
|
|
||||||
// broken...
|
|
||||||
GlStateManager.enableBlend();
|
|
||||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
|
||||||
this.renderer.bindTexture(breakingTexture);
|
|
||||||
this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw,
|
|
||||||
headPitch, scale);
|
|
||||||
GlStateManager.disableBlend();
|
|
||||||
}else{
|
|
||||||
// Stone texture
|
|
||||||
this.renderer.bindTexture(texture);
|
|
||||||
this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw,
|
|
||||||
headPitch, scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getBlockBrightnessForEntity(Entity entity, float partialTicks){
|
|
||||||
|
|
||||||
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(MathHelper.floor(entity.posX), 0,
|
|
||||||
MathHelper.floor(entity.posZ));
|
|
||||||
|
|
||||||
if(entity.world.isBlockLoaded(pos)){
|
|
||||||
pos.setY(MathHelper.floor(entity.posY + (double)entity.getEyeHeight()));
|
|
||||||
return entity.world.getCombinedLight(pos, 0);
|
|
||||||
}else{
|
}else{
|
||||||
return 0;
|
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderEntityModel(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
|
||||||
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
|
||||||
|
|
||||||
GlStateManager.pushMatrix();
|
|
||||||
// Enables tiling (Also used for guardian beam, beacon beam and ender crystal beam)
|
|
||||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
|
|
||||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
|
|
||||||
|
|
||||||
GlStateManager.depthMask(true); // Some entities set depth mask to false (i.e. no sorting of faces by depth)
|
|
||||||
// In particular, LayerSpiderEyes sets it to false when the spider is invisible, for some reason.
|
|
||||||
|
|
||||||
// Changes the scale at which the texture is applied to the model. See LayerCreeper for a similar example,
|
|
||||||
// but with translation instead of scaling.
|
|
||||||
// NOTE: You can do all sorts of fun stuff with this, just by applying transformations in the 2D texture space.
|
|
||||||
GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
|
||||||
GlStateManager.loadIdentity();
|
|
||||||
double scaleX = 1, scaleY = 1;
|
|
||||||
// It's more logical to use the model's texture size, but some classes don't bother setting it properly
|
|
||||||
// (e.g. ModelVillager), so to get the correct dimensions I'm getting them from the first box instead.
|
|
||||||
if(model.boxList != null && model.boxList.get(0) != null){
|
|
||||||
scaleX = (double)model.boxList.get(0).textureWidth / 16d;
|
|
||||||
scaleY = (double)model.boxList.get(0).textureHeight / 16d;
|
|
||||||
}else{ // Fallback to model fields; should never be needed
|
|
||||||
scaleX = (double)model.textureWidth / 16d;
|
|
||||||
scaleY = (double)model.textureHeight / 16d;
|
|
||||||
}
|
|
||||||
GlStateManager.scale(scaleX, scaleY, 1);
|
|
||||||
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
|
||||||
|
|
||||||
// Hides the hat layer for bipeds
|
|
||||||
if(this.model instanceof ModelBiped) ((ModelBiped)this.model).bipedHeadwear.isHidden = true;
|
|
||||||
|
|
||||||
this.model.setLivingAnimations(entity, limbSwing, limbSwingAmount, partialTicks);
|
|
||||||
this.model.render(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
|
|
||||||
|
|
||||||
if(this.model instanceof ModelBiped) ((ModelBiped)this.model).bipedHeadwear.isHidden = false;
|
|
||||||
|
|
||||||
// Undoes the texture scaling
|
|
||||||
GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
|
||||||
GlStateManager.loadIdentity();
|
|
||||||
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
|
||||||
|
|
||||||
GlStateManager.popMatrix();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldCombineTextures(){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
package electroblob.wizardry.client.renderer;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.model.ModelBase;
|
||||||
|
import net.minecraft.client.model.ModelBiped;
|
||||||
|
import net.minecraft.client.model.ModelPlayer;
|
||||||
|
import net.minecraft.client.renderer.GlStateManager;
|
||||||
|
import net.minecraft.client.renderer.OpenGlHelper;
|
||||||
|
import net.minecraft.client.renderer.entity.Render;
|
||||||
|
import net.minecraft.client.renderer.entity.RenderLivingBase;
|
||||||
|
import net.minecraft.client.renderer.entity.RenderPlayer;
|
||||||
|
import net.minecraft.client.renderer.entity.layers.LayerRenderer;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.EntityLivingBase;
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for layers that overlay a tiled texture onto the entity model. Handles dynamic tiling of the texture,
|
||||||
|
* scaling to preserve in-world pixel size (1/16 of a block), and the necessary GL state changes.
|
||||||
|
*
|
||||||
|
* @author Electroblob
|
||||||
|
* @since Wizardry 4.3
|
||||||
|
*/
|
||||||
|
public abstract class LayerTiledOverlay<T extends EntityLivingBase> implements LayerRenderer<T> {
|
||||||
|
|
||||||
|
private final RenderLivingBase<?> renderer;
|
||||||
|
|
||||||
|
public LayerTiledOverlay(RenderLivingBase<?> renderer){
|
||||||
|
this.renderer = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this layer should be rendered for the given entity, false if not.
|
||||||
|
* @param entity The entity being rendered
|
||||||
|
* @param partialTicks The current partial tick time
|
||||||
|
* @return True if this layer should be rendered, false if not.
|
||||||
|
*/
|
||||||
|
public abstract boolean shouldRender(T entity, float partialTicks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the texture to use for the overlay.
|
||||||
|
* @param entity The entity being rendered
|
||||||
|
* @param partialTicks The current partial tick time
|
||||||
|
* @return A {@link ResourceLocation} specifying the texture to use.
|
||||||
|
*/
|
||||||
|
public abstract ResourceLocation getTexture(T entity, float partialTicks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the model to use for the overlay. Defaults to the renderer's main model.
|
||||||
|
* @param entity The entity being rendered
|
||||||
|
* @param partialTicks The current partial tick time
|
||||||
|
* @return The model to use. Swapping different models in dynamically is fine, but don't create new ones each time.
|
||||||
|
*/
|
||||||
|
public ModelBase getModel(T entity, float partialTicks){
|
||||||
|
return renderer.getMainModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the width of the texture to use for the overlay. Defaults to 16.
|
||||||
|
* @param entity The entity being rendered
|
||||||
|
* @param partialTicks The current partial tick time
|
||||||
|
* @return The width of the texture in pixels.
|
||||||
|
*/
|
||||||
|
public int getTextureWidth(T entity, float partialTicks){
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the height of the texture to use for the overlay. Defaults to 16.
|
||||||
|
* @param entity The entity being rendered
|
||||||
|
* @param partialTicks The current partial tick time
|
||||||
|
* @return The height of the texture in pixels.
|
||||||
|
*/
|
||||||
|
public int getTextureHeight(T entity, float partialTicks){
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether to render the second layer of mob/player skins (which is not an <i>actual</i> render layer, but
|
||||||
|
* part of the model) as part of this layer. Defaults to false.
|
||||||
|
* @param entity The entity being rendered
|
||||||
|
* @param partialTicks The current partial tick time
|
||||||
|
* @return True to render any non-hidden second layer boxes, false to always hide them from this layer.
|
||||||
|
*/
|
||||||
|
public boolean renderSecondLayer(T entity, float partialTicks){
|
||||||
|
return false; // Normally it looks kind of weird because second layers typically have holes in them
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doRenderLayer(T entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
||||||
|
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
||||||
|
|
||||||
|
if(shouldRender(entity, partialTicks)){
|
||||||
|
|
||||||
|
GlStateManager.enableLighting();
|
||||||
|
// TODO: Why is this here? Why don't other layers have it? What does RenderLivingBase do about brightness?
|
||||||
|
// And why was there a partialTicks parameter?
|
||||||
|
int i = getBlockBrightnessForEntity(entity);
|
||||||
|
int j = i % 65536;
|
||||||
|
int k = i / 65536;
|
||||||
|
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F);
|
||||||
|
|
||||||
|
this.renderer.bindTexture(getTexture(entity, partialTicks));
|
||||||
|
this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw,
|
||||||
|
headPitch, scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldCombineTextures(){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int getBlockBrightnessForEntity(Entity entity){
|
||||||
|
|
||||||
|
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(MathHelper.floor(entity.posX), 0,
|
||||||
|
MathHelper.floor(entity.posZ));
|
||||||
|
|
||||||
|
if(entity.world.isBlockLoaded(pos)){
|
||||||
|
pos.setY(MathHelper.floor(entity.posY + (double)entity.getEyeHeight()));
|
||||||
|
return entity.world.getCombinedLight(pos, 0);
|
||||||
|
}else{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderEntityModel(T entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
||||||
|
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
||||||
|
|
||||||
|
GlStateManager.pushMatrix();
|
||||||
|
// Enables texture tiling (Also used for guardian beam, beacon beam and ender crystal beam, amongst others)
|
||||||
|
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
|
||||||
|
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
|
||||||
|
|
||||||
|
GlStateManager.depthMask(true); // Some entities set depth mask to false (i.e. no sorting of faces by depth)
|
||||||
|
// In particular, LayerSpiderEyes sets it to false when the spider is invisible, for some reason.
|
||||||
|
|
||||||
|
// Changes the scale at which the texture is applied to the model. See LayerCreeper for a similar example,
|
||||||
|
// but with translation instead of scaling.
|
||||||
|
// You can do all sorts of fun stuff with this, just by applying transformations in the 2D texture space.
|
||||||
|
GlStateManager.matrixMode(GL11.GL_TEXTURE); // Switch to the 2D texture space
|
||||||
|
GlStateManager.loadIdentity();
|
||||||
|
|
||||||
|
ModelBase model = getModel(entity, partialTicks);
|
||||||
|
|
||||||
|
int width = getTextureWidth(entity, partialTicks);
|
||||||
|
int height = getTextureHeight(entity, partialTicks);
|
||||||
|
|
||||||
|
// Calculate the scaling required in each direction
|
||||||
|
double scaleX = 1, scaleY = 1;
|
||||||
|
// It's more logical to use the model's texture size, but some classes don't bother setting it properly
|
||||||
|
// (e.g. ModelVillager), so to get the correct dimensions I'm getting them from the first box instead.
|
||||||
|
if(model.boxList != null && model.boxList.get(0) != null){
|
||||||
|
scaleX = (double)model.boxList.get(0).textureWidth / width;
|
||||||
|
scaleY = (double)model.boxList.get(0).textureHeight / height;
|
||||||
|
}else{ // Fallback to model fields; should never be needed
|
||||||
|
scaleX = (double)model.textureWidth / width;
|
||||||
|
scaleY = (double)model.textureHeight / height;
|
||||||
|
}
|
||||||
|
|
||||||
|
GlStateManager.scale(scaleX, scaleY, 1);
|
||||||
|
|
||||||
|
GlStateManager.matrixMode(GL11.GL_MODELVIEW); // Switch back to the 3D model space
|
||||||
|
|
||||||
|
boolean headWearHidden = false;
|
||||||
|
boolean bodyWearHidden = false;
|
||||||
|
boolean leftArmWearHidden = false;
|
||||||
|
boolean rightArmWearHidden = false;
|
||||||
|
boolean leftLegWearHidden = false;
|
||||||
|
boolean rightLegWearHidden = false;
|
||||||
|
|
||||||
|
if(!renderSecondLayer(entity, partialTicks)){
|
||||||
|
// Hide the hat layer for bipeds, and all the other body part overlays for players
|
||||||
|
if(model instanceof ModelBiped){
|
||||||
|
headWearHidden = ((ModelBiped)model).bipedHeadwear.isHidden;
|
||||||
|
((ModelBiped)model).bipedHeadwear.isHidden = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(model instanceof ModelPlayer){
|
||||||
|
bodyWearHidden = ((ModelPlayer)model).bipedBodyWear.isHidden;
|
||||||
|
leftArmWearHidden = ((ModelPlayer)model).bipedLeftArmwear.isHidden;
|
||||||
|
rightArmWearHidden = ((ModelPlayer)model).bipedRightArmwear.isHidden;
|
||||||
|
leftLegWearHidden = ((ModelPlayer)model).bipedLeftLegwear.isHidden;
|
||||||
|
rightLegWearHidden = ((ModelPlayer)model).bipedRightLegwear.isHidden;
|
||||||
|
((ModelPlayer)model).bipedBodyWear.isHidden = true;
|
||||||
|
((ModelPlayer)model).bipedLeftArmwear.isHidden = true;
|
||||||
|
((ModelPlayer)model).bipedRightArmwear.isHidden = true;
|
||||||
|
((ModelPlayer)model).bipedLeftLegwear.isHidden = true;
|
||||||
|
((ModelPlayer)model).bipedRightLegwear.isHidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually render the model
|
||||||
|
model.setLivingAnimations(entity, limbSwing, limbSwingAmount, partialTicks);
|
||||||
|
model.render(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
|
||||||
|
|
||||||
|
if(!renderSecondLayer(entity, partialTicks)){
|
||||||
|
// Reset the hidden status of everything to how it was before
|
||||||
|
if(model instanceof ModelBiped){
|
||||||
|
((ModelBiped)model).bipedHeadwear.isHidden = headWearHidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(model instanceof ModelPlayer){
|
||||||
|
((ModelPlayer)model).bipedBodyWear.isHidden = bodyWearHidden;
|
||||||
|
((ModelPlayer)model).bipedLeftArmwear.isHidden = leftArmWearHidden;
|
||||||
|
((ModelPlayer)model).bipedRightArmwear.isHidden = rightArmWearHidden;
|
||||||
|
((ModelPlayer)model).bipedLeftLegwear.isHidden = leftLegWearHidden;
|
||||||
|
((ModelPlayer)model).bipedRightLegwear.isHidden = rightLegWearHidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Undo the texture scaling
|
||||||
|
GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
||||||
|
GlStateManager.loadIdentity();
|
||||||
|
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
||||||
|
|
||||||
|
GlStateManager.popMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to initialise a layer renderer for all applicable renderers. This method will work for any layer
|
||||||
|
* renderer, not just instances of {@code LayerTiledOverlay} (it is stored here purely for convenience).
|
||||||
|
* @param entityType The class of entity to initialise this layer renderer for (including subclasses)
|
||||||
|
* @param layerFactory A function that creates a layer for a given renderer, usually a constructor reference
|
||||||
|
* @param <T> The type of entity this layer renderer is applicable for
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends EntityLivingBase> void initialiseLayers(Class<T> entityType, Function<RenderLivingBase<T>, LayerRenderer<T>> layerFactory){
|
||||||
|
|
||||||
|
for(Class<? extends Entity> c : Minecraft.getMinecraft().getRenderManager().entityRenderMap.keySet()){
|
||||||
|
if(entityType.isAssignableFrom(c)){
|
||||||
|
Render<T> renderer = Minecraft.getMinecraft().getRenderManager().getEntityClassRenderObject(c);
|
||||||
|
if(renderer instanceof RenderLivingBase){ // Should always be true
|
||||||
|
// For some reason IntelliJ is quite happy with this cast, even without suppress warnings
|
||||||
|
((RenderLivingBase<T>)renderer).addLayer(layerFactory.apply((RenderLivingBase<T>)renderer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Players have a separate renderer map
|
||||||
|
if(entityType.isAssignableFrom(EntityPlayer.class)){
|
||||||
|
for(RenderPlayer renderer : Minecraft.getMinecraft().getRenderManager().getSkinMap().values()){
|
||||||
|
renderer.addLayer(layerFactory.apply((RenderLivingBase<T>)renderer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to initialise a layer renderer for all living entity renderers. This is a shorthand version of
|
||||||
|
* {@link LayerTiledOverlay#initialiseLayers(Class, Function)}.
|
||||||
|
* @param layerFactory A function that creates a layer for a given renderer, usually a constructor reference
|
||||||
|
*/
|
||||||
|
public static void initialiseLayers(Function<RenderLivingBase<EntityLivingBase>, LayerRenderer<EntityLivingBase>> layerFactory){
|
||||||
|
initialiseLayers(EntityLivingBase.class, layerFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user