/* * Minecraft Forge * Copyright (c) 2016. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.client.model.pipeline; import javax.vecmath.Vector3f; import net.minecraft.block.state.IBlockState; import net.minecraft.client.renderer.EntityRenderer; import net.minecraft.client.renderer.color.BlockColors; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; import com.google.common.base.Objects; public class VertexLighterFlat extends QuadGatheringTransformer { protected final BlockInfo blockInfo; private int tint = -1; private boolean diffuse = true; protected int posIndex = -1; protected int normalIndex = -1; protected int colorIndex = -1; protected int lightmapIndex = -1; public VertexLighterFlat(BlockColors colors) { this.blockInfo = new BlockInfo(colors); } @Override public void setParent(IVertexConsumer parent) { super.setParent(parent); if(Objects.equal(getVertexFormat(), parent.getVertexFormat())) return; setVertexFormat(getVertexFormat(parent)); for(int i = 0; i < getVertexFormat().getElementCount(); i++) { switch(getVertexFormat().getElement(i).getUsage()) { case POSITION: posIndex = i; break; case NORMAL: normalIndex = i; break; case COLOR: colorIndex = i; break; case UV: if(getVertexFormat().getElement(i).getIndex() == 1) { lightmapIndex = i; } break; default: } } if(posIndex == -1) { throw new IllegalArgumentException("vertex lighter needs format with position"); } if(lightmapIndex == -1) { throw new IllegalArgumentException("vertex lighter needs format with lightmap"); } if(colorIndex == -1) { throw new IllegalArgumentException("vertex lighter needs format with color"); } } private static VertexFormat getVertexFormat(IVertexConsumer parent) { VertexFormat format = parent.getVertexFormat(); if(format.hasNormal()) return format; format = new VertexFormat(format); format.addElement(new VertexFormatElement(0, VertexFormatElement.EnumType.FLOAT, VertexFormatElement.EnumUsage.NORMAL, 4)); return format; } @Override protected void processQuad() { float[][] position = quadData[posIndex]; float[][] normal = null; float[][] lightmap = quadData[lightmapIndex]; float[][] color = quadData[colorIndex]; // If all three normal values are either -1 or 0, normals must be generated if(quadData[normalIndex][0][0] != quadData[normalIndex][0][1] || quadData[normalIndex][0][1] != quadData[normalIndex][0][2] || (quadData[normalIndex][0][0] != -1 && quadData[normalIndex][0][0] != 0)) { normal = quadData[normalIndex]; } else { normal = new float[4][4]; Vector3f v1 = new Vector3f(position[3]); Vector3f t = new Vector3f(position[1]); Vector3f v2 = new Vector3f(position[2]); v1.sub(t); t.set(position[0]); v2.sub(t); v1.cross(v2, v1); v1.normalize(); for(int v = 0; v < 4; v++) { normal[v][0] = v1.x; normal[v][1] = v1.y; normal[v][2] = v1.z; normal[v][3] = 0; } } int multiplier = -1; if(tint != -1) { multiplier = blockInfo.getColorMultiplier(tint); } VertexFormat format = parent.getVertexFormat(); int count = format.getElementCount(); for(int v = 0; v < 4; v++) { position[v][0] += blockInfo.getShx(); position[v][1] += blockInfo.getShy(); position[v][2] += blockInfo.getShz(); float x = position[v][0] - .5f; float y = position[v][1] - .5f; float z = position[v][2] - .5f; //if(blockInfo.getBlock().isFullCube()) { x += normal[v][0] * .5f; y += normal[v][1] * .5f; z += normal[v][2] * .5f; } float blockLight = lightmap[v][0], skyLight = lightmap[v][1]; updateLightmap(normal[v], lightmap[v], x, y, z); if(dataLength[lightmapIndex] > 1) { if(blockLight > lightmap[v][0]) lightmap[v][0] = blockLight; if(skyLight > lightmap[v][1]) lightmap[v][1] = skyLight; } updateColor(normal[v], color[v], x, y, z, tint, multiplier); if(diffuse) { float d = LightUtil.diffuseLight(normal[v][0], normal[v][1], normal[v][2]); for(int i = 0; i < 3; i++) { color[v][i] *= d; } } if(EntityRenderer.anaglyphEnable) { applyAnaglyph(color[v]); } // no need for remapping cause all we could've done is add 1 element to the end for(int e = 0; e < count; e++) { VertexFormatElement element = format.getElement(e); switch(element.getUsage()) { case POSITION: // position adding moved to VertexBufferConsumer due to x and z not fitting completely into a float /*float[] pos = new float[4]; System.arraycopy(position[v], 0, pos, 0, position[v].length); pos[0] += blockInfo.getBlockPos().getX(); pos[1] += blockInfo.getBlockPos().getY(); pos[2] += blockInfo.getBlockPos().getZ();*/ parent.put(e, position[v]); break; case NORMAL: if(normalIndex != -1) { parent.put(e, normal[v]); break; } case COLOR: parent.put(e, color[v]); break; case UV: if(element.getIndex() == 1) { parent.put(e, lightmap[v]); break; } default: parent.put(e, quadData[e][v]); } } } tint = -1; } protected void applyAnaglyph(float[] color) { float r = color[0]; color[0] = (r * 30 + color[1] * 59 + color[2] * 11) / 100; color[1] = (r * 3 + color[1] * 7) / 10; color[2] = (r * 3 + color[2] * 7) / 10; } protected void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z) { float e1 = 1 - 1e-2f; float e2 = 0.95f; BlockPos pos = blockInfo.getBlockPos(); boolean full = blockInfo.getState().isFullCube(); if((full || y < -e1) && normal[1] < -e2) pos = pos.down(); if((full || y > e1) && normal[1] > e2) pos = pos.up(); if((full || z < -e1) && normal[2] < -e2) pos = pos.north(); if((full || z > e1) && normal[2] > e2) pos = pos.south(); if((full || x < -e1) && normal[0] < -e2) pos = pos.west(); if((full || x > e1) && normal[0] > e2) pos = pos.east(); int brightness = blockInfo.getState().getPackedLightmapCoords(blockInfo.getWorld(), pos); lightmap[0] = ((float)((brightness >> 0x04) & 0xF) * 0x20) / 0xFFFF; lightmap[1] = ((float)((brightness >> 0x14) & 0xF) * 0x20) / 0xFFFF; } protected void updateColor(float[] normal, float[] color, float x, float y, float z, float tint, int multiplier) { if(tint != -1) { color[0] *= (float)(multiplier >> 0x10 & 0xFF) / 0xFF; color[1] *= (float)(multiplier >> 0x8 & 0xFF) / 0xFF; color[2] *= (float)(multiplier & 0xFF) / 0xFF; } } public void setQuadTint(int tint) { this.tint = tint; } public void setQuadOrientation(EnumFacing orientation) {} public void setQuadCulled() {} public void setTexture( TextureAtlasSprite texture ) {} public void setApplyDiffuseLighting(boolean diffuse) { this.diffuse = diffuse; } public void setWorld(IBlockAccess world) { blockInfo.setWorld(world); } public void setState(IBlockState state) { blockInfo.setState(state); } public void setBlockPos(BlockPos blockPos) { blockInfo.setBlockPos(blockPos); } public void updateBlockInfo() { blockInfo.updateShift(); } }