Java tutorial
/* * The MIT License (MIT) * * Copyright (c) 2014 Ordinastie * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package net.malisis.core.renderer; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import net.malisis.core.MalisisCore; import net.malisis.core.renderer.element.Face; import net.malisis.core.renderer.element.Shape; import net.malisis.core.renderer.element.Vertex; import net.malisis.core.renderer.element.shape.Cube; import net.malisis.core.renderer.icon.MalisisIcon; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityClientPlayerMP; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.DestroyBlockProgress; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.RenderBlocks; import net.minecraft.client.renderer.RenderGlobal; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; import net.minecraft.item.Item; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.IIcon; import net.minecraft.util.ResourceLocation; import net.minecraft.util.StatCollector; import net.minecraft.world.IBlockAccess; import net.minecraftforge.client.IItemRenderer; import net.minecraftforge.client.MinecraftForgeClient; import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.common.util.ForgeDirection; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import cpw.mods.fml.client.registry.ClientRegistry; import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler; import cpw.mods.fml.client.registry.RenderingRegistry; import cpw.mods.fml.relauncher.ReflectionHelper; /** * Base class for rendering. Handle the rendering for {@link ISimpleBlockRenderingHandler}, {@link TileEntitySpecialRenderer}, and * {@link IItemRenderer}. Provides easy registration of the renderer, and automatically sets up the context for the rendering. * * @author Ordinastie * */ public class MalisisRenderer extends TileEntitySpecialRenderer implements ISimpleBlockRenderingHandler, IItemRenderer, IRenderWorldLast { //Reference to Minecraft.renderGlobal.damagedBlocks (lazy loaded) /** The damaged blocks. */ private static Map damagedBlocks; /** The damaged icons. */ protected static IIcon[] damagedIcons; /** Whether this {@link MalisisRenderer} initialized. (initialize() already called */ private boolean initialized = false; /** Id of this {@link MalisisRenderer}. */ protected int renderId = -1; /** Tessellator reference. */ protected Tessellator t = Tessellator.instance; /** Current world reference (ISBRH/TESR/IRWL). */ protected IBlockAccess world; /** RenderBlocks reference (ISBRH). */ protected RenderBlocks renderBlocks; /** Whether render coordinates already shifted (ISBRH). */ protected boolean isShifted = false; /** Block to render (ISBRH/TESR). */ protected Block block; /** Metadata of the block to render (ISBRH/TESR). */ protected int blockMetadata; /** Position of the block (ISBRH/TESR). */ protected int x, y, z; /** TileEntity currently drawing (for TESR). */ protected TileEntity tileEntity; /** Partial tick time (TESR/IRWL). */ protected float partialTick = 0; /** ItemStack to render (ITEM). */ protected ItemStack itemStack; /** ItemRenderType of item rendering (ITEM). */ protected ItemRenderType itemRenderType; /** RenderGlobal reference (IRWL) */ protected RenderGlobal renderGlobal; /** Type of rendering. */ protected RenderType renderType; /** Mode of rendering (GL constant). */ protected int drawMode; /** Current shape being rendered. */ protected Shape shape = new Cube(); /** Current face being rendered. */ protected Face face; /** Current parameters for the shape being rendered. */ protected RenderParameters rp = new RenderParameters(); /** Current parameters for the face being rendered. */ protected RenderParameters params; /** Base brightness of the block. */ protected int baseBrightness; /** An override texture set by the renderer. */ protected IIcon overrideTexture; /** Whether the damage for the blocks should be handled by this {@link MalisisRenderer} (for TESR). */ protected boolean getBlockDamage = false; /** Current block destroy progression (for TESR). */ protected DestroyBlockProgress destroyBlockProgress = null; /** Whether at least one vertex has been drawn. */ protected boolean vertexDrawn = false; /** * Instantiates a new {@link MalisisRenderer}. */ public MalisisRenderer() { this.renderId = RenderingRegistry.getNextAvailableRenderId(); this.t = Tessellator.instance; } /** * Gets the partialTick for this frame. Used for TESR and ITEMS * * @return the partial tick */ public float getPartialTick() { return partialTick; } // #region set() /** * Resets data so this {@link MalisisRenderer} can be reused. */ public void reset() { this.renderType = RenderType.UNSET; this.drawMode = 0; this.world = null; this.block = null; this.blockMetadata = 0; this.x = 0; this.y = 0; this.z = 0; this.overrideTexture = null; this.destroyBlockProgress = null; } /** * Sets informations for this {@link MalisisRenderer}. * * @param world the world * @param block the block * @param x the x * @param y the y * @param z the z * @param metadata the metadata */ public void set(IBlockAccess world, Block block, int x, int y, int z, int metadata) { this.world = world; this.block = block; this.blockMetadata = metadata; this.x = x; this.y = y; this.z = z; } /** * Sets informations for this {@link MalisisRenderer}. * * @param world the world */ public void set(IBlockAccess world) { this.world = world; } /** * Sets informations for this {@link MalisisRenderer}. * * @param block the block */ public void set(Block block) { set(world, block, x, y, z, blockMetadata); } /** * Sets informations for this {@link MalisisRenderer}. * * @param blockMetadata the block metadata */ public void set(int blockMetadata) { set(world, block, x, y, z, blockMetadata); } /** * Sets informations for this {@link MalisisRenderer}. * * @param block the block * @param blockMetadata the block metadata */ public void set(Block block, int blockMetadata) { set(world, block, x, y, z, blockMetadata); } /** * Sets informations for this {@link MalisisRenderer}. * * @param x the x * @param y the y * @param z the z */ public void set(int x, int y, int z) { set(world, block, x, y, z, blockMetadata); } /** * Sets informations for this {@link MalisisRenderer}. * * @param te the te * @param partialTick the partial tick */ public void set(TileEntity te, float partialTick) { set(te.getWorldObj(), te.getBlockType(), te.xCoord, te.yCoord, te.zCoord, te.getBlockMetadata()); this.partialTick = partialTick; this.tileEntity = te; } /** * Sets informations for this {@link MalisisRenderer}. * * @param type the type * @param itemStack the item stack */ public void set(ItemRenderType type, ItemStack itemStack) { if (itemStack.getItem() instanceof ItemBlock) set(Block.getBlockFromItem(itemStack.getItem())); this.itemStack = itemStack; this.itemRenderType = type; } // #end // #region ISBRH /** * Renders inventory block. * * @param block the block * @param metadata the metadata * @param modelId renderId * @param renderer RenderBlocks */ @Override public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) { set(block, metadata); renderBlocks = renderer; prepare(RenderType.ISBRH_INVENTORY); render(); clean(); } /** * Renders world block. * * @param world the world * @param x the x * @param y the y * @param z the z * @param block the block * @param modelId renderId * @param renderer RenderBlocks * @return true, if something was drawn, false otherwise */ @Override public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) { set(world, block, x, y, z, world.getBlockMetadata(x, y, z)); tileEntity = world.getTileEntity(x, y, z); renderBlocks = renderer; vertexDrawn = false; prepare(RenderType.ISBRH_WORLD); if (renderer.hasOverrideBlockTexture()) overrideTexture = renderer.overrideBlockTexture; render(); clean(); return vertexDrawn; } /** * Checks whether this {@link MalisisRenderer} should handle the rendering in inventory * * @param modelId renderId * @return true, if this {@link MalisisRenderer} should be used for rendering the block in inventory */ @Override public boolean shouldRender3DInInventory(int modelId) { return true; } // #end ISBRH // #region IItemRenderer /** * Checks whether to use this {@link MalisisRenderer} for the specified {@link net.minecraftforge.client.IItemRenderer.ItemRenderType}. * * @param item the item * @param type ItemRenderType * @return true, if this {@link MalisisRenderer} should be used for rendering the block in inventory */ @Override public boolean handleRenderType(ItemStack item, ItemRenderType type) { return true; } /** * Checks whether a render helper should be used for this {@link MalisisRenderer}. * * @param type the type * @param item the item * @param helper the helper * @return true, if successful */ @Override public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper) { return true; } /** * Renders the item. * * @param type the type * @param item the item * @param data the data */ @Override public void renderItem(ItemRenderType type, ItemStack item, Object... data) { set(type, item); prepare(RenderType.ITEM_INVENTORY); render(); clean(); } // #end IItemRenderer // #region TESR /** * Renders a {@link TileEntitySpecialRenderer}. * * @param te the TileEntity * @param x the x * @param y the y * @param z the z * @param partialTick the partial tick */ @Override public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialTick) { set(te, partialTick); prepare(RenderType.TESR_WORLD, x, y, z); render(); if (getBlockDamage) { destroyBlockProgress = getBlockDestroyProgress(); if (destroyBlockProgress != null) { next(); GL11.glEnable(GL11.GL_BLEND); OpenGlHelper.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_SRC_COLOR, GL11.GL_ONE, GL11.GL_ZERO); GL11.glAlphaFunc(GL11.GL_GREATER, 0); GL11.glColor4f(1.0F, 1.0F, 1.0F, 0.5F); t.disableColor(); renderDestroyProgress(); next(); GL11.glDisable(GL11.GL_BLEND); } } clean(); } // #end TESR // #region IRenderWorldLast @Override public boolean shouldSetViewportPosition() { return true; } @Override public boolean shouldRender(RenderWorldLastEvent event, IBlockAccess world) { return true; } @Override public void renderWorldLastEvent(RenderWorldLastEvent event, IBlockAccess world) { set(world); partialTick = event.partialTicks; renderGlobal = event.context; double x = 0, y = 0, z = 0; if (shouldSetViewportPosition()) { EntityClientPlayerMP p = Minecraft.getMinecraft().thePlayer; x = -(p.lastTickPosX + (p.posX - p.lastTickPosX) * partialTick); y = -(p.lastTickPosY + (p.posY - p.lastTickPosY) * partialTick); z = -(p.lastTickPosZ + (p.posZ - p.lastTickPosZ) * partialTick); } prepare(RenderType.WORLD_LAST, x, y, z); render(); clean(); } // #end IRenderWorldLast // #region prepare() /** * Prepares the {@link Tessellator} and the GL states for the <b>renderType</b>. <b>data</b> is only used for TESR and IRWL.<br> * TESR and IRWL rendering are surrounded by glPushAttrib(GL_LIGHTING_BIT) and block texture sheet is bound. * * @param renderType the render type * @param data the data */ public void prepare(RenderType renderType, double... data) { _initialize(); this.renderType = renderType; if (renderType == RenderType.ISBRH_WORLD) { tessellatorShift(); } else if (renderType == RenderType.ISBRH_INVENTORY) { GL11.glTranslatef(-0.5F, -0.5F, -0.5F); startDrawing(); } else if (renderType == RenderType.ITEM_INVENTORY) { GL11.glPushAttrib(GL11.GL_LIGHTING_BIT); startDrawing(); } else if (renderType == RenderType.TESR_WORLD) { GL11.glPushAttrib(GL11.GL_LIGHTING_BIT); RenderHelper.disableStandardItemLighting(); GL11.glEnable(GL11.GL_COLOR_MATERIAL); GL11.glShadeModel(GL11.GL_SMOOTH); GL11.glPushMatrix(); GL11.glTranslated(data[0], data[1], data[2]); bindTexture(TextureMap.locationBlocksTexture); startDrawing(); } else if (renderType == RenderType.WORLD_LAST) { GL11.glPushAttrib(GL11.GL_LIGHTING_BIT); RenderHelper.disableStandardItemLighting(); GL11.glEnable(GL11.GL_COLOR_MATERIAL); GL11.glShadeModel(GL11.GL_SMOOTH); GL11.glPushMatrix(); GL11.glTranslated(data[0], data[1], data[2]); bindTexture(TextureMap.locationBlocksTexture); startDrawing(); } } /** * Tells the {@link Tessellator} to start drawing GL_QUADS. */ public void startDrawing() { startDrawing(GL11.GL_QUADS); } /** * Tells the {@link Tessellator} to start drawing <b>drawMode</b>. * * @param drawMode the draw mode */ public void startDrawing(int drawMode) { t.startDrawing(drawMode); this.drawMode = drawMode; } /** * Triggers a draw and restart drawing with current {@link MalisisRenderer#drawMode}. */ public void next() { next(drawMode); } /** * Triggers a draw and restart drawing with <b>drawMode</b>. * * @param drawMode the draw mode */ public void next(int drawMode) { draw(); startDrawing(drawMode); } /** * Triggers a draw. */ public void draw() { t.draw(); } /** * Cleans the current renderer state. */ public void clean() { if (renderType == RenderType.ISBRH_WORLD) { tessellatorUnshift(); } else if (renderType == RenderType.ISBRH_INVENTORY) { draw(); GL11.glTranslatef(0.5F, 0.5F, 0.5F); } else if (renderType == RenderType.ITEM_INVENTORY) { draw(); GL11.glPopAttrib(); } else if (renderType == RenderType.TESR_WORLD) { draw(); GL11.glPopMatrix(); GL11.glPopAttrib(); } else if (renderType == RenderType.WORLD_LAST) { draw(); GL11.glPopMatrix(); GL11.glPopAttrib(); } reset(); } /** * Shifts the {@link Tessellator} for ISBRH rendering. */ public void tessellatorShift() { if (isShifted) return; isShifted = true; t.addTranslation(x, y, z); } /** * Unshifts the {@link Tessellator} for ISBRH rendering. */ public void tessellatorUnshift() { if (!isShifted) return; isShifted = false; t.addTranslation(-x, -y, -z); } /** * Enables the blending for the rendering. Ineffective for ISBRH. */ public void enableBlending() { if (renderType == RenderType.ISBRH_WORLD) return; GL11.glEnable(GL11.GL_BLEND); GL11.glEnable(GL11.GL_COLOR_MATERIAL); GL11.glShadeModel(GL11.GL_SMOOTH); OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); GL11.glAlphaFunc(GL11.GL_GREATER, 0.0F); } /** * Disables textures. */ public void disableTextures() { GL11.glDisable(GL11.GL_TEXTURE_2D); } /** * Enables textures */ public void enableTextures() { GL11.glEnable(GL11.GL_TEXTURE_2D); } @Override protected void bindTexture(ResourceLocation resourceLocaltion) { Minecraft.getMinecraft().getTextureManager().bindTexture(resourceLocaltion); } // #end prepare() /** * _initialize. */ protected final void _initialize() { if (initialized) return; initialize(); initialized = true; } /** * Initializes this {@link MalisisRenderer}. Does nothing by default.<br> * Called the first time a rendering is done and should be overridden if some setup is needed for the rendering (building shape and * parameters). */ protected void initialize() { } /** * Renders the block using the default Minecraft rendering system. */ public void renderStandard() { renderStandard(renderBlocks); } /** * Renders the blocks using the default Minecraft rendering system with the specified <b>renderer</b>. * * @param renderer the renderer */ public void renderStandard(RenderBlocks renderer) { if (renderer == null) return; boolean b = isShifted; if (b) tessellatorUnshift(); renderer.setRenderBoundsFromBlock(block); renderer.renderStandardBlock(block, x, y, z); if (b) tessellatorShift(); } /** * Main rendering method. Draws simple cube by default.<br> * Should be overridden to handle the rendering. */ public void render() { drawShape(shape, rp); } /** * Renders the destroy progress manually for TESR. Called if {@link MalisisRenderer#destroyBlockProgress} is not <code>null</code>. */ public void renderDestroyProgress() { overrideTexture = damagedIcons[destroyBlockProgress.getPartialBlockDamage()]; enableBlending(); render(); } /** * Draws a {@link Shape} without {@link RenderParameters} (default will be used). * * @param shape the shape */ public void drawShape(Shape shape) { drawShape(shape, null); } /** * Draws a {@link Shape} with specified {@link RenderParameters}. * * @param s the s * @param params the params */ public void drawShape(Shape s, RenderParameters params) { if (s == null) return; shape = s; rp = params != null ? params : new RenderParameters(); //apply transformations s.applyMatrix(); // vertex position if (rp.vertexPositionRelativeToRenderBounds.get()) calcVertexesPosition(getRenderBounds()); if (rp.applyTexture.get()) applyTexture(s, rp); for (Face face : s.getFaces()) drawFace(face, face.getParameters()); } /** * Draws a {@link Face} with its own {@link RenderParameters}. * * @param face the face */ public void drawFace(Face face) { drawFace(face, face.getParameters()); } /** * Draws a {@link Face} with specified {@link RenderParameters}. * * @param f the f * @param faceParams the face params */ protected void drawFace(Face f, RenderParameters faceParams) { if (f == null) return; int vertexCount = f.getVertexes().length; if (vertexCount != 4 && renderType == RenderType.ISBRH_WORLD) { MalisisCore.log.error( "[BaseRenderer] Attempting to render a face containing {} vertexes in ISBRH. Ignored", vertexCount); return; } face = f; params = RenderParameters.merge(rp, faceParams); if (!shouldRenderFace(face)) return; //use normals if available if ((renderType == RenderType.ITEM_INVENTORY || renderType == RenderType.ISBRH_INVENTORY || params.useNormals.get()) && params.direction.get() != null) t.setNormal(params.direction.get().offsetX, params.direction.get().offsetY, params.direction.get().offsetZ); baseBrightness = getBaseBrightness(); drawVertexes(face.getVertexes()); //we need to separate each face if (drawMode == GL11.GL_POLYGON || drawMode == GL11.GL_LINE || drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP) next(); } /** * * Draws an array of {@link Vertex vertexes} (usually {@link Face#getVertexes()}). * * @param vertexes the vertexes */ protected void drawVertexes(Vertex[] vertexes) { for (int i = 0; i < vertexes.length; i++) drawVertex(vertexes[i], i); } /** * Draws a single {@link Vertex}. * * @param vertex the vertex * @param number the offset inside the face. (Used for AO) */ protected void drawVertex(Vertex vertex, int number) { if (vertex == null) vertex = new Vertex(0, 0, 0); // brightness int brightness = calcVertexBrightness(vertex, (int[][]) params.aoMatrix.get(number)); vertex.setBrightness(brightness); // color int color = calcVertexColor(vertex, (int[][]) params.aoMatrix.get(number)); vertex.setColor(color); // alpha if (!params.usePerVertexAlpha.get()) vertex.setAlpha(params.alpha.get()); t.setColorRGBA_I(vertex.getColor(), vertex.getAlpha()); t.setBrightness(vertex.getBrightness()); if (params.useTexture.get()) t.addVertexWithUV(vertex.getX(), vertex.getY(), vertex.getZ(), vertex.getU(), vertex.getV()); else t.addVertex(vertex.getX(), vertex.getY(), vertex.getZ()); vertexDrawn = true; } /** * Draws a string at the specified coordinates, with color and shadow. The string gets translated. Uses FontRenderer.drawString(). * * @param text the text * @param x the x * @param y the y * @param z the z * @param color the color * @param shadow the shadow */ public void drawString(String text, float x, float y, float z, int color, boolean shadow) { if (MalisisRenderer.getFontRenderer() == null) return; text = StatCollector.translateToLocal(text); text = text.replaceAll("\r", ""); GL11.glPushMatrix(); float s = 0.010F; GL11.glTranslatef(x, y, z); GL11.glScalef(s, -s, s); // GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glDisable(GL12.GL_RESCALE_NORMAL); getFontRenderer().drawString(text, 0, 0, color, shadow); GL11.glPopMatrix(); GL11.glEnable(GL12.GL_RESCALE_NORMAL); // GL11.glEnable(GL11.GL_DEPTH_TEST); } /** * Gets the IIcon corresponding to the specified {@link RenderParameters}. * * @param params the params * @return the icon */ protected IIcon getIcon(RenderParameters params) { IIcon icon = params.icon.get(); if (params.useCustomTexture.get()) icon = new MalisisIcon(); //use a generic icon where UVs go from 0 to 1 else if (overrideTexture != null) icon = overrideTexture; else if (block != null && icon == null) { int side = 0; if (params.textureSide.get() != null) side = params.textureSide.get().ordinal(); if (world != null && params.useWorldSensitiveIcon.get()) icon = block.getIcon(world, x, y, z, side); else icon = block.getIcon(side, blockMetadata); } return icon; } /** * Checks if a {@link Face} should be rendered. {@link RenderParameters#direction} needs to be defined for the <b>face</b>. * * @param face the face * @return true, if successful */ protected boolean shouldRenderFace(Face face) { if (renderType != RenderType.ISBRH_WORLD || world == null || block == null) return true; if (rp != null && rp.renderAllFaces.get()) return true; if (renderBlocks != null && renderBlocks.renderAllFaces == true) return true; RenderParameters p = face.getParameters(); if (p.direction.get() == null || p.renderAllFaces.get()) return true; boolean b = block.shouldSideBeRendered(world, x + p.direction.get().offsetX, y + p.direction.get().offsetY, z + p.direction.get().offsetZ, p.direction.get().ordinal()); return b; } /** * Applies the texture to the {@link Shape}.<br> * Usually necessary before some shape transformations in conjunction with {@link RenderParameters#applyTexture} set to * <code>false</code> to prevent reapplying texture when rendering. * * @param shape the shape */ public void applyTexture(Shape shape) { applyTexture(shape, null); } /** * Applies the texture to the {@link Shape} with specified {@link RenderParameters}.<br> * Usually necessary before some shape transformations in conjunction with {@link RenderParameters#applyTexture} set to * <code>false</code> to prevent reapplying texture when rendering. * * @param shape the shape * @param parameters the parameters */ public void applyTexture(Shape shape, RenderParameters parameters) { //shape.applyMatrix(); for (Face f : shape.getFaces()) { RenderParameters params = RenderParameters.merge(f.getParameters(), parameters); IIcon icon = getIcon(params); if (icon != null) { boolean flipU = params.flipU.get(); if (params.direction.get() == ForgeDirection.NORTH || params.direction.get() == ForgeDirection.EAST) flipU = !flipU; f.setTexture(icon, flipU, params.flipV.get(), params.interpolateUV.get()); } } } /** * Calculates the ambient occlusion for a {@link Vertex} and also apply the side dependent shade.<br> * <b>aoMatrix</b> is the list of block coordinates necessary to compute AO. If it's empty, only the global face shade is applied.<br> * Also, <i>params.colorMultiplier</i> is applied as well. * * @param vertex the vertex * @param aoMatrix the ao matrix * @return the int */ protected int calcVertexColor(Vertex vertex, int[][] aoMatrix) { int color = 0xFFFFFF; if (params.usePerVertexColor.get()) //vertex should use their own colors color = vertex.getColor(); if (params.colorMultiplier.get() != null) //global color multiplier is set color = params.colorMultiplier.get(); else if (block != null) //use block color mulitplier color = world != null ? block.colorMultiplier(world, x, y, z) : block.getRenderColor(blockMetadata); if (drawMode == GL11.GL_LINE) //no AO for lines return color; if (renderType != RenderType.ISBRH_WORLD && renderType != RenderType.TESR_WORLD) //no AO for item/inventories return color; float factor = 1; //calculate AO if (params.calculateAOColor.get() && aoMatrix != null && Minecraft.isAmbientOcclusionEnabled() && block.getLightValue(world, x, y, z) == 0) { factor = getBlockAmbientOcclusion(world, x + params.direction.get().offsetX, y + params.direction.get().offsetY, z + params.direction.get().offsetZ); for (int i = 0; i < aoMatrix.length; i++) factor += getBlockAmbientOcclusion(world, x + aoMatrix[i][0], y + aoMatrix[i][1], z + aoMatrix[i][2]); factor /= (aoMatrix.length + 1); } //apply face dependent shading factor *= params.colorFactor.get(); int r = (int) ((color >> 16 & 255) * factor); int g = (int) ((color >> 8 & 255) * factor); int b = (int) ((color & 255) * factor); color = r << 16 | g << 8 | b; return color; } /** * Gets the base brightness for the current {@link Face}.<br> * If <i>params.useBlockBrightness</i> = false, <i>params.brightness</i>. Else, the brightness is determined based on * <i>params.offset</i> and <i>getBlockBounds()</i> * * @return the base brightness */ protected int getBaseBrightness() { //not in world if ((renderType != RenderType.ISBRH_WORLD && renderType != RenderType.TESR_WORLD) || world == null || !params.useBlockBrightness.get()) return params.brightness.get(); //no direction, we can only use current block brightness if (params.direction.get() == null) return block.getMixedBrightnessForBlock(world, x, y, z); AxisAlignedBB bounds = getRenderBounds(); ForgeDirection dir = params.direction.get(); int ox = x + dir.offsetX; int oy = y + dir.offsetY; int oz = z + dir.offsetZ; //use the brightness of the block next to it if (bounds != null) { if (dir == ForgeDirection.WEST && bounds.minX > 0) ox += 1; else if (dir == ForgeDirection.EAST && bounds.maxX < 1) ox -= 1; else if (dir == ForgeDirection.NORTH && bounds.minZ > 0) oz += 1; else if (dir == ForgeDirection.SOUTH && bounds.maxZ < 1) oz -= 1; else if (dir == ForgeDirection.DOWN && bounds.minY > 0) oy += 1; else if (dir == ForgeDirection.UP && bounds.maxY < 1) oy -= 1; } return getMixedBrightnessForBlock(world, ox, oy, oz); } /** * Calculates the ambient occlusion brightness for a {@link Vertex}. <b>aoMatrix</b> is the list of block coordinates necessary to * compute AO. Only first 3 blocks are used.<br> * * @param vertex the vertex * @param aoMatrix the ao matrix * @return the int */ protected int calcVertexBrightness(Vertex vertex, int[][] aoMatrix) { if (params.usePerVertexBrightness.get()) return vertex.getBrightness(); if (drawMode == GL11.GL_LINE) //no AO for lines return baseBrightness; if (renderType != RenderType.ISBRH_WORLD && renderType != RenderType.TESR_WORLD) //not in world return baseBrightness; if (!params.calculateBrightness.get() || aoMatrix == null) //no data return baseBrightness; if (!Minecraft.isAmbientOcclusionEnabled() || block.getLightValue(world, x, y, z) != 0) // emit light return baseBrightness; int[] b = new int[Math.max(3, aoMatrix.length)]; for (int i = 0; i < b.length; i++) b[i] += getMixedBrightnessForBlock(world, x + aoMatrix[i][0], y + aoMatrix[i][1], z + aoMatrix[i][2]); int brightness = getAoBrightness(b[0], b[1], b[2], baseBrightness); return brightness; } /** * Does the actual brightness calculation (copied from net.minecraft.client.renderer.BlocksRenderer.java) * * @param b1 the b1 * @param b2 the b2 * @param b3 the b3 * @param base the base * @return the ao brightness */ protected int getAoBrightness(int b1, int b2, int b3, int base) { if (b1 == 0) b1 = base; if (b2 == 0) b2 = base; if (b3 == 0) b3 = base; return b1 + b2 + b3 + base >> 2 & 16711935; } /** * Gets the block ambient occlusion value. Contrary to base Minecraft code, it's the actual block at the <b>x</b>, <b>y</b> and <b>z</b> * coordinates which is used to get the value, and not value of the block drawn. This allows to have different logic behaviors for AO * values for a block. * * @param world the world * @param x the x * @param y the y * @param z the z * @return the block ambient occlusion */ protected float getBlockAmbientOcclusion(IBlockAccess world, int x, int y, int z) { Block block = world.getBlock(x, y, z); if (block == null) return 1.0F; return block.getAmbientOcclusionLightValue(); } /** * Gets the mix brightness for a block (sky + block source). * * @param world the world * @param x the x * @param y the y * @param z the z * @return the mixed brightness for block */ protected int getMixedBrightnessForBlock(IBlockAccess world, int x, int y, int z) { // return world.getLightBrightnessForSkyBlocks(x, y, z, 0); return world.getBlock(x, y, z).getMixedBrightnessForBlock(world, x, y, z); } /** * Gets the rendering bounds. If <i>params.useBlockBounds</i> = false, <i>params.renderBounds</i> is used instead of the actual block * bounds. * * @return the render bounds */ protected AxisAlignedBB getRenderBounds() { if (block == null || !rp.useBlockBounds.get()) return rp.renderBounds.get(); if (world != null) block.setBlockBoundsBasedOnState(world, x, y, z); return AxisAlignedBB.getBoundingBox(block.getBlockBoundsMinX(), block.getBlockBoundsMinY(), block.getBlockBoundsMinZ(), block.getBlockBoundsMaxX(), block.getBlockBoundsMaxY(), block.getBlockBoundsMaxZ()); } /** * Modifies the {@link Vertex vertexes} coordinates relative to the bounds specified.<br> * Eg : if x = 0.5, minX = 1, maxX = 3, x becomes 2 * * @param bounds the bounds */ protected void calcVertexesPosition(AxisAlignedBB bounds) { if (bounds == null) return; for (Face f : shape.getFaces()) for (Vertex v : f.getVertexes()) v.interpolateCoord(bounds); } /** * Gets and hold reference to damagedBlocks from Minecraft.renderGlobal via reflection. * * @return the damaged blocks */ protected Map getDamagedBlocks() { if (damagedBlocks != null) return damagedBlocks; try { Field modifiers = Field.class.getDeclaredField("modifiers"); modifiers.setAccessible(true); Field f = ReflectionHelper.findField(RenderGlobal.class, MalisisCore.isObfEnv ? "field_94141_F" : "destroyBlockIcons"); modifiers.setInt(f, f.getModifiers() & ~Modifier.FINAL); damagedIcons = (IIcon[]) f.get(Minecraft.getMinecraft().renderGlobal); f = ReflectionHelper.findField(RenderGlobal.class, MalisisCore.isObfEnv ? "field_72738_E" : "damagedBlocks"); modifiers.setInt(f, f.getModifiers()); damagedBlocks = (HashMap) f.get(Minecraft.getMinecraft().renderGlobal); return damagedBlocks; } catch (Exception e) { e.printStackTrace(); } return null; } /** * Gets the destroy block progress for this rendering. Only used for TESR. * * @return the block destroy progress */ protected DestroyBlockProgress getBlockDestroyProgress() { if (renderType != RenderType.TESR_WORLD) return null; Map damagedBlocks = getDamagedBlocks(); if (damagedBlocks == null || damagedBlocks.isEmpty()) return null; Iterator iterator = damagedBlocks.values().iterator(); while (iterator.hasNext()) { DestroyBlockProgress dbp = (DestroyBlockProgress) iterator.next(); if (isCurrentBlockDestroyProgress(dbp)) return dbp; } return null; } /** * Checks whether the DestroyBlockProgress specified should apply for this TESR. * * @param dbp the dbp * @return true, if is current block destroy progress */ protected boolean isCurrentBlockDestroyProgress(DestroyBlockProgress dbp) { return dbp.getPartialBlockX() == x && dbp.getPartialBlockY() == y && dbp.getPartialBlockZ() == z; } /** * Gets the render id of this {@link MalisisRenderer}. * * @return the render id */ @Override public int getRenderId() { return renderId; } /** * Registers this {@link MalisisRenderer} to be used for rendering for specified classes.<br> * Classes have to extend Block or TileEntity.<br> * <font color="990000">A static <b>renderId</b> field is required inside the class extending Block !</font> * * @param listClass the list class */ public void registerFor(Class... listClass) { for (Class clazz : listClass) { if (Block.class.isAssignableFrom(clazz)) { try { clazz.getField("renderId").set(null, renderId); RenderingRegistry.registerBlockHandler(this); } catch (ReflectiveOperationException e) { MalisisCore.log.error( "[BaseRenderer] Tried to register ISBRH for block class {} that does not have renderId field", clazz.getSimpleName()); e.printStackTrace(); } } else if (TileEntity.class.isAssignableFrom(clazz)) { ClientRegistry.bindTileEntitySpecialRenderer(clazz, this); } } } /** * Registers this {@link MalisisRenderer} to be used for rendering the specified <b>item</b>. * * @param item the item */ public void registerFor(Item item) { MinecraftForgeClient.registerItemRenderer(item, this); } /** * Registers this {@link MalisisRenderer} to be used for {@link RenderWorldLastEvent}. */ public void registerForRenderWorldLast() { RenderWorldEventHandler.register(this); } public static FontRenderer getFontRenderer() { return Minecraft.getMinecraft().fontRenderer; } /** * Gets rendering width of a string. * * @param str the str * @return the string width */ public static int getStringWidth(String str) { str = StatCollector.translateToLocal(str); str = str.replaceAll("\r", ""); return (int) Math.ceil(getFontRenderer().getStringWidth(str) * 0.01F); } /** * Gets the rendering height of strings according to fontscale. * * @return the string height */ public static int getStringHeight() { return (int) Math.ceil(getFontRenderer().FONT_HEIGHT * 0.01F); } }