Applied-Energistics-2-tiler.../src/main/java/appeng/client/render/model/UVLModelLoader.java
Sebastian Hartte 59544993bd This reverts back to the old way of extending the vertex format with the necessary vertex element for lightmap data, if fullbright quads are enabled. Instead of then enabling the extended vertex format for Optifine, it is disabled if Optifine is detected.
Since the root cause is actually that the Vanilla lighting pipeline doesn't support such Vertex Formats, we disable it also if the Forge lighting pipeline is disabled.

This also relates to #2489.
2016-10-27 20:05:18 +02:00

379 lines
13 KiB
Java

/*
* 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.model;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.util.vector.Vector3f;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.BlockFaceUV;
import net.minecraft.client.renderer.block.model.BlockPart;
import net.minecraft.client.renderer.block.model.BlockPartFace;
import net.minecraft.client.renderer.block.model.BlockPartRotation;
import net.minecraft.client.renderer.block.model.FaceBakery;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.block.model.ItemOverride;
import net.minecraft.client.renderer.block.model.ItemTransformVec3f;
import net.minecraft.client.renderer.block.model.ModelBakery;
import net.minecraft.client.renderer.block.model.ModelBlock;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.JsonUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.ICustomModelLoader;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.animation.ModelBlockAnimation;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.client.model.pipeline.VertexLighterFlat;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.ITransformation;
import net.minecraftforge.fml.relauncher.ReflectionHelper;
import appeng.client.render.VertexFormats;
public enum UVLModelLoader implements ICustomModelLoader
{
INSTANCE;
private static final Gson gson = new Gson();
private static final Constructor<? extends IModel> vanillaModelWrapper;
private static final Field faceBakery;
private static final Object vanillaLoader;
private static final MethodHandle loaderGetter;
static
{
try
{
Field modifiers = Field.class.getDeclaredField( "modifiers" );
modifiers.setAccessible( true );
faceBakery = ReflectionHelper.findField( ModelBakery.class, "faceBakery", "field_177607_l" );
modifiers.set( faceBakery, faceBakery.getModifiers() & ( ~Modifier.FINAL ) );
Class clas = Class.forName( ModelLoader.class.getName() + "$VanillaModelWrapper" );
vanillaModelWrapper = clas.getDeclaredConstructor( ModelLoader.class, ResourceLocation.class, ModelBlock.class, boolean.class, ModelBlockAnimation.class );
vanillaModelWrapper.setAccessible( true );
Class<?> vanillaLoaderClass = Class.forName( ModelLoader.class.getName() + "$VanillaLoader" );
Field instanceField = vanillaLoaderClass.getField( "INSTANCE" );
// Static field
vanillaLoader = instanceField.get( null );
Field loaderField = vanillaLoaderClass.getDeclaredField( "loader" );
loaderField.setAccessible( true );
loaderGetter = MethodHandles.lookup().unreflectGetter( loaderField );
}
catch( Exception e )
{
throw Throwables.propagate( e );
}
}
private static Object deserializer( Class clas )
{
try
{
clas = Class.forName( clas.getName() + "$Deserializer" );
Constructor constr = clas.getDeclaredConstructor();
constr.setAccessible( true );
return constr.newInstance();
}
catch( Exception e )
{
throw Throwables.propagate( e );
}
}
private static <M extends IModel> M vanillaModelWrapper( ModelLoader loader, ResourceLocation location, ModelBlock model, boolean uvlock, ModelBlockAnimation animation )
{
try
{
return (M) vanillaModelWrapper.newInstance( loader, location, model, uvlock, animation );
}
catch( Exception e )
{
throw Throwables.propagate( e );
}
}
private static void setFaceBakery( ModelBakery modelBakery, FaceBakery faceBakery )
{
try
{
UVLModelLoader.faceBakery.set( modelBakery, faceBakery );
}
catch( Exception e )
{
throw Throwables.propagate( e );
}
}
private IResourceManager resourceManager;
public ModelLoader getLoader()
{
try
{
return (ModelLoader) loaderGetter.invoke( vanillaLoader );
}
catch( Throwable throwable )
{
throw new RuntimeException( throwable );
}
}
@Override
public void onResourceManagerReload( IResourceManager resourceManager )
{
this.resourceManager = resourceManager;
}
@Override
public boolean accepts( ResourceLocation modelLocation )
{
String modelPath = modelLocation.getResourcePath();
if( modelLocation.getResourcePath().startsWith( "models/" ) )
{
modelPath = modelPath.substring( "models/".length() );
}
try( InputStreamReader io = new InputStreamReader( Minecraft.getMinecraft().getResourceManager().getResource( new ResourceLocation( modelLocation.getResourceDomain(), "models/" + modelPath + ".json" ) ).getInputStream() ) )
{
return gson.fromJson( io, UVLMarker.class ).uvlMarker;
}
catch( IOException e )
{
}
return false;
}
@Override
public IModel loadModel( ResourceLocation modelLocation ) throws Exception
{
return new UVLModelWrapper( modelLocation );
}
public class UVLModelWrapper implements IModel
{
final Gson UVLSERIALIZER = ( new GsonBuilder() ).registerTypeAdapter( ModelBlock.class, deserializer( ModelBlock.class ) ).registerTypeAdapter( BlockPart.class, deserializer( BlockPart.class ) ).registerTypeAdapter( BlockPartFace.class, new BlockPartFaceOverrideSerializer() ).registerTypeAdapter( BlockFaceUV.class, deserializer( BlockFaceUV.class ) ).registerTypeAdapter( ItemTransformVec3f.class, deserializer( ItemTransformVec3f.class ) ).registerTypeAdapter( ItemCameraTransforms.class, deserializer( ItemCameraTransforms.class ) ).registerTypeAdapter( ItemOverride.class, deserializer( ItemOverride.class ) ).create();
private Map<BlockPartFace, Pair<Float, Float>> uvlightmap = new HashMap<>();
private final IModel parent;
public UVLModelWrapper( ResourceLocation modelLocation )
{
String modelPath = modelLocation.getResourcePath();
if( modelLocation.getResourcePath().startsWith( "models/" ) )
{
modelPath = modelPath.substring( "models/".length() );
}
ResourceLocation armatureLocation = new ResourceLocation( modelLocation.getResourceDomain(), "armatures/" + modelPath + ".json" );
ModelBlockAnimation animation = ModelBlockAnimation.loadVanillaAnimation( resourceManager, armatureLocation );
ModelBlock model;
{
Reader reader = null;
IResource iresource = null;
ModelBlock lvt_5_1_ = null;
try
{
String s = modelLocation.getResourcePath();
iresource = Minecraft.getMinecraft().getResourceManager().getResource( new ResourceLocation( modelLocation.getResourceDomain(), "models/" + modelPath + ".json" ) );
reader = new InputStreamReader( iresource.getInputStream(), Charsets.UTF_8 );
lvt_5_1_ = JsonUtils.gsonDeserialize( UVLSERIALIZER, reader, ModelBlock.class, false );
lvt_5_1_.name = modelLocation.toString();
}
catch( IOException e )
{
e.printStackTrace();
}
finally
{
IOUtils.closeQuietly( reader );
IOUtils.closeQuietly( (Closeable) iresource );
}
model = lvt_5_1_;
}
this.parent = vanillaModelWrapper( getLoader(), modelLocation, model, false, animation );
}
@Override
public Collection<ResourceLocation> getDependencies()
{
return parent.getDependencies();
}
@Override
public Collection<ResourceLocation> getTextures()
{
return parent.getTextures();
}
@Override
public IBakedModel bake( IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter )
{
setFaceBakery( getLoader(), new FaceBakeryOverride() );
IBakedModel model = parent.bake( state, format, bakedTextureGetter );
setFaceBakery( getLoader(), new FaceBakery() );
return model;
}
@Override
public IModelState getDefaultState()
{
return parent.getDefaultState();
}
public class BlockPartFaceOverrideSerializer implements JsonDeserializer<BlockPartFace>
{
@Override
public BlockPartFace deserialize( JsonElement p_deserialize_1_, Type p_deserialize_2_, JsonDeserializationContext p_deserialize_3_ ) throws JsonParseException
{
JsonObject jsonobject = p_deserialize_1_.getAsJsonObject();
EnumFacing enumfacing = this.parseCullFace( jsonobject );
int i = this.parseTintIndex( jsonobject );
String s = this.parseTexture( jsonobject );
BlockFaceUV blockfaceuv = (BlockFaceUV) p_deserialize_3_.deserialize( jsonobject, BlockFaceUV.class );
BlockPartFace blockFace = new BlockPartFace( enumfacing, i, s, blockfaceuv );
uvlightmap.put( blockFace, parseUVL( jsonobject ) );
return blockFace;
}
protected int parseTintIndex( JsonObject object )
{
return JsonUtils.getInt( object, "tintindex", -1 );
}
private String parseTexture( JsonObject object )
{
return JsonUtils.getString( object, "texture" );
}
@Nullable
private EnumFacing parseCullFace( JsonObject object )
{
String s = JsonUtils.getString( object, "cullface", "" );
return EnumFacing.byName( s );
}
protected Pair<Float, Float> parseUVL( JsonObject object )
{
if( !object.has( "uvlightmap" ) )
{
return null;
}
object = object.get( "uvlightmap" ).getAsJsonObject();
return new ImmutablePair<Float, Float>( JsonUtils.getFloat( object, "sky", 0 ), JsonUtils.getFloat( object, "block", 0 ) );
}
}
public class FaceBakeryOverride extends FaceBakery
{
@Override
public BakedQuad makeBakedQuad( Vector3f posFrom, Vector3f posTo, BlockPartFace face, TextureAtlasSprite sprite, EnumFacing facing, ITransformation modelRotationIn, BlockPartRotation partRotation, boolean uvLocked, boolean shade )
{
BakedQuad quad = super.makeBakedQuad( posFrom, posTo, face, sprite, facing, modelRotationIn, partRotation, uvLocked, shade );
Pair<Float, Float> brightness = uvlightmap.get( face );
if( brightness != null )
{
VertexFormat newFormat = VertexFormats.getFormatWithLightMap( quad.getFormat() );
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder( newFormat );
VertexLighterFlat trans = new VertexLighterFlat( Minecraft.getMinecraft().getBlockColors() ){
@Override
protected void updateLightmap( float[] normal, float[] lightmap, float x, float y, float z )
{
lightmap[0] = brightness.getRight();
lightmap[1] = brightness.getLeft();
}
@Override
public void setQuadTint( int tint )
{
// Tint requires a block state which we don't have at this point
}
};
trans.setParent( builder );
quad.pipe( trans );
builder.setQuadTint( quad.getTintIndex() );
builder.setQuadOrientation( quad.getFace() );
builder.setTexture( quad.getSprite() );
builder.setApplyDiffuseLighting( false );
return builder.build();
}
else
{
return quad;
}
}
}
}
class UVLMarker
{
boolean uvlMarker = false;
}
}