/* * This file is part of Applied Energistics 2. * Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved. * * Applied Energistics 2 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, either version 3 of the License, or * (at your option) any later version. * * Applied Energistics 2 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 Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>. */ package appeng.client.render.cablebus; import java.util.ArrayList; import java.util.List; import javax.vecmath.Matrix4f; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.util.EnumFacing; import appeng.client.render.FacingToRotation; import appeng.core.AELog; /** * Assuming a default-orientation of forward=NORTH and up=UP, this class rotates a given list of quads to the desired * facing */ public class QuadRotator { public List<BakedQuad> rotateQuads( List<BakedQuad> quads, EnumFacing newForward, EnumFacing newUp ) { if( newForward == EnumFacing.NORTH && newUp == EnumFacing.UP ) { return quads; // This is the default orientation } List<BakedQuad> result = new ArrayList<>( quads.size() ); for( BakedQuad quad : quads ) { result.add( rotateQuad( quad, newForward, newUp ) ); } return result; } private BakedQuad rotateQuad( BakedQuad quad, EnumFacing forward, EnumFacing up ) { // Sanitize forward/up if( forward.getAxis() == up.getAxis() ) { if( up.getAxis() == EnumFacing.Axis.Y ) { up = EnumFacing.NORTH; } else { up = EnumFacing.UP; } } FacingToRotation rotation = FacingToRotation.get( forward, up ); Matrix4f mat = rotation.getMat(); // Clone the vertex data used by the quad int[] newData = quad.getVertexData().clone(); // Figure out where the position is in the array VertexFormat format = quad.getFormat(); int posIdx = findPositionOffset( format ) / 4; int stride = format.getNextOffset() / 4; int normalIdx = format.getNormalOffset(); VertexFormatElement.EnumType normalType = null; // Figure out the type of the normals if( normalIdx != -1 ) { for( int i = 0; i < format.getElements().size(); i++ ) { VertexFormatElement element = format.getElement( i ); if( element.getUsage() == VertexFormatElement.EnumUsage.NORMAL ) { normalType = element.getType(); } } } for( int i = 0; i < 4; i++ ) { Point3f pos = new Point3f( Float.intBitsToFloat( newData[i * stride + posIdx] ) - 0.5f, Float.intBitsToFloat( newData[i * stride + posIdx + 1] ) - 0.5f, Float.intBitsToFloat( newData[i * stride + posIdx + 2] ) - 0.5f ); // Rotate stuff around mat.transform( pos ); // Write back newData[i * stride + posIdx] = Float.floatToIntBits( pos.getX() + 0.5f ); newData[i * stride + posIdx + 1] = Float.floatToIntBits( pos.getY() + 0.5f ); newData[i * stride + posIdx + 2] = Float.floatToIntBits( pos.getZ() + 0.5f ); // Transform the normal if one is present if( normalIdx != -1 ) { if( normalType == VertexFormatElement.EnumType.FLOAT ) { Vector3f normal = new Vector3f( Float.intBitsToFloat( newData[i * stride + normalIdx] ), Float.intBitsToFloat( newData[i * stride + normalIdx + 1] ), Float.intBitsToFloat( newData[i * stride + normalIdx + 2] ) ); // Rotate stuff around mat.transform( normal ); // Write back newData[i * stride + normalIdx] = Float.floatToIntBits( normal.getX() ); newData[i * stride + normalIdx + 1] = Float.floatToIntBits( normal.getY() ); newData[i * stride + normalIdx + 2] = Float.floatToIntBits( normal.getZ() ); } else if( normalType == VertexFormatElement.EnumType.BYTE ) { int idx = i * stride * 4 + normalIdx; Vector3f normal = new Vector3f( getByte( newData, idx ) / 127.0f, getByte( newData, idx + 1 ) / 127.0f, getByte( newData, idx + 2 ) / 127.0f ); // Rotate stuff around mat.transform( normal ); // Write back setByte( newData, idx, (int) ( normal.getX() * 127 ) ); setByte( newData, idx + 1, (int) ( normal.getY() * 127 ) ); setByte( newData, idx + 2, (int) ( normal.getZ() * 127 ) ); } else { AELog.warn( "Unsupported normal format: {}", normalType ); } } } EnumFacing newFace = rotation.rotate( quad.getFace() ); return new BakedQuad( newData, quad.getTintIndex(), newFace, quad.getSprite(), quad.shouldApplyDiffuseLighting(), quad.getFormat() ); } private static int getByte( int[] data, int offset ) { int idx = offset / 4; int subOffset = offset % 4; return (byte) ( data[idx] >> ( subOffset * 8 ) ); } private static void setByte( int[] data, int offset, int value ) { int idx = offset / 4; int subOffset = offset % 4; int mask = 0xFF << ( subOffset * 8 ); data[idx] = data[idx] & ( ~mask ) | ( ( value & 0xFF ) << ( subOffset * 8 ) ); } private int findPositionOffset( VertexFormat format ) { List<VertexFormatElement> elements = format.getElements(); for( int i = 0; i < elements.size(); i++ ) { VertexFormatElement e = elements.get( i ); if( e.isPositionElement() ) { if( e.getType() != VertexFormatElement.EnumType.FLOAT ) { throw new IllegalArgumentException( "Only floating point positions are supported" ); } return i; } } throw new IllegalArgumentException( "Vertex format " + format + " has no position attribute!" ); } }