package mekanism.client.render.obj; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.vecmath.Matrix4f; import mekanism.client.render.MekanismRenderer; import net.minecraft.block.state.IBlockState; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.IBakedModel; import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.util.EnumFacing; import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.client.model.obj.OBJModel; import net.minecraftforge.client.model.obj.OBJModel.Face; import net.minecraftforge.client.model.obj.OBJModel.Group; import net.minecraftforge.client.model.obj.OBJModel.Normal; import net.minecraftforge.client.model.obj.OBJModel.OBJBakedModel; import net.minecraftforge.client.model.obj.OBJModel.OBJState; import net.minecraftforge.client.model.obj.OBJModel.TextureCoordinate; import net.minecraftforge.client.model.obj.OBJModel.Vertex; import net.minecraftforge.client.model.pipeline.LightUtil; import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; import net.minecraftforge.common.model.IModelState; import net.minecraftforge.common.model.TRSRTransformation; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; public abstract class OBJBakedModelBase extends OBJBakedModel { protected IBakedModel baseModel; protected TextureAtlasSprite tempSprite = ModelLoader.White.INSTANCE; protected VertexFormat vertexFormat; protected ImmutableMap<String, TextureAtlasSprite> textureMap; protected HashMap<TransformType, Matrix4f> transformationMap = new HashMap<TransformType, Matrix4f>(); public OBJBakedModelBase(IBakedModel base, OBJModel model, IModelState state, VertexFormat format, ImmutableMap<String, TextureAtlasSprite> textures, HashMap<TransformType, Matrix4f> transform) { model.super(model, state, format, textures); baseModel = base; transformationMap = transform; textureMap = textures; vertexFormat = format; if(state instanceof OBJState) { updateStateVisibilityMap((OBJState)state); } } @Override public List<BakedQuad> getQuads(IBlockState blockState, EnumFacing side, long rand) { if(side != null) { return ImmutableList.of(); } List<BakedQuad> bakedQuads = new ArrayList<BakedQuad>(); Set<Face> faces = Collections.synchronizedSet(new LinkedHashSet<Face>()); Optional<TRSRTransformation> transform = Optional.absent(); Map<Face, String> groupNameMap = new HashMap<Face, String>(); for(Group g : getModel().getMatLib().getGroups().values()) { if(getState() instanceof OBJState) { OBJState state = (OBJState)getState(); if(state.parent != null) { transform = state.parent.apply(Optional.absent()); } updateStateVisibilityMap(state); if(state.getGroupsWithVisibility(true).contains(g.getName())) { Set<Face> groupFaces = g.applyTransform(transform); for(Face f : groupFaces) { groupNameMap.put(f, g.getName()); faces.add(f); } } } else { transform = getState().apply(Optional.absent()); Set<Face> groupFaces = g.applyTransform(transform); for(Face f : groupFaces) { groupNameMap.put(f, g.getName()); faces.add(f); } } } for(Face f : faces) { String groupName = groupNameMap.get(f); if(getOverrideTexture(f, groupName) != null) { tempSprite = getOverrideTexture(f, groupName); } else if(getModel().getMatLib().getMaterial(f.getMaterialName()).isWhite()) { for(Vertex v : f.getVertices()) { if(!v.getMaterial().equals(getModel().getMatLib().getMaterial(v.getMaterial().getName()))) { v.setMaterial(getModel().getMatLib().getMaterial(v.getMaterial().getName())); } } tempSprite = ModelLoader.White.INSTANCE; } else { tempSprite = textureMap.get(f.getMaterialName()); } float[] color = new float[] {1, 1, 1, 1}; if(getOverrideColor(f, groupName) != null) { color = getOverrideColor(f, groupName); } EnumFacing facing = EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z); UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(vertexFormat); builder.setContractUVs(true); builder.setQuadOrientation(facing); builder.setTexture(tempSprite); builder.setQuadTint(0); Normal faceNormal = f.getNormal(); boolean rotate = shouldRotate(f, groupName); putVertexData(builder, f.getVertices()[0], faceNormal, TextureCoordinate.getDefaultUVs()[0], tempSprite, vertexFormat, color); putVertexData(builder, f.getVertices()[1], faceNormal, TextureCoordinate.getDefaultUVs()[1], tempSprite, vertexFormat, color); putVertexData(builder, f.getVertices()[2], faceNormal, TextureCoordinate.getDefaultUVs()[2], tempSprite, vertexFormat, color); putVertexData(builder, f.getVertices()[3], faceNormal, TextureCoordinate.getDefaultUVs()[3], tempSprite, vertexFormat, color); BakedQuad quad = builder.build(); if(rotate) { quad = MekanismRenderer.rotate(quad, 1); } bakedQuads.add(quad); } List<BakedQuad> quadList = Collections.synchronizedList(Lists.newArrayList(bakedQuads)); return quadList; } public static final void putVertexData(UnpackedBakedQuad.Builder builder, Vertex v, Normal faceNormal, TextureCoordinate defUV, TextureAtlasSprite sprite, VertexFormat format, float[] color) { for(int e = 0; e < format.getElementCount(); e++) { switch(format.getElement(e).getUsage()) { case POSITION: builder.put(e, v.getPos().x, v.getPos().y, v.getPos().z, v.getPos().w); break; case COLOR: float d; if(v.hasNormal()) { d = LightUtil.diffuseLight(v.getNormal().x, v.getNormal().y, v.getNormal().z); } else { d = LightUtil.diffuseLight(faceNormal.x, faceNormal.y, faceNormal.z); } if(v.getMaterial() != null) { builder.put(e, d * v.getMaterial().getColor().x * color[0], d * v.getMaterial().getColor().y * color[1], d * v.getMaterial().getColor().z * color[2], v.getMaterial().getColor().w * color[3]); } else { builder.put(e, d, d, d, 1); } break; case UV: if(!v.hasTextureCoordinate()) { builder.put(e, sprite.getInterpolatedU(defUV.u * 16), sprite.getInterpolatedV((1 - defUV.v) * 16), 0, 1); } else { builder.put(e, sprite.getInterpolatedU(v.getTextureCoordinate().u * 16), sprite.getInterpolatedV((1 - v.getTextureCoordinate().v) * 16), 0, 1); } break; case NORMAL: if(!v.hasNormal()) { builder.put(e, faceNormal.x, faceNormal.y, faceNormal.z, 0); } else { builder.put(e, v.getNormal().x, v.getNormal().y, v.getNormal().z, 0); } break; default: builder.put(e); } } } private static Method m_updateStateVisibilityMap; protected void updateStateVisibilityMap(OBJState state) { try { if(m_updateStateVisibilityMap == null) { m_updateStateVisibilityMap = OBJBakedModel.class.getDeclaredMethod("updateStateVisibilityMap", OBJState.class); m_updateStateVisibilityMap.setAccessible(true); } m_updateStateVisibilityMap.invoke(this, state); } catch(Exception e) { e.printStackTrace(); } } private static Field f_textures; public static ImmutableMap<String, TextureAtlasSprite> getTexturesForOBJModel(IBakedModel model) { try { if(f_textures == null) { f_textures = OBJBakedModel.class.getDeclaredField("textures"); f_textures.setAccessible(true); } return (ImmutableMap<String, TextureAtlasSprite>)f_textures.get(model); } catch(Exception e) { e.printStackTrace(); } return null; } @Override public TextureAtlasSprite getParticleTexture() { return tempSprite; } protected float[] getOverrideColor(Face f, String groupName) { return null; } protected TextureAtlasSprite getOverrideTexture(Face f, String groupName) { return null; } protected boolean shouldRotate(Face f, String groupName) { return false; } }