Implemented the sky compass.
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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 javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Vector4f;
|
||||
|
||||
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.minecraftforge.client.model.pipeline.QuadGatheringTransformer;
|
||||
|
||||
|
||||
/**
|
||||
* Applies an arbitrary transformation matrix to the vertices of a quad.
|
||||
*/
|
||||
final class MatrixVertexTransformer extends QuadGatheringTransformer
|
||||
{
|
||||
|
||||
private final Matrix4f transform;
|
||||
|
||||
public MatrixVertexTransformer( Matrix4f transform )
|
||||
{
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processQuad()
|
||||
{
|
||||
VertexFormat format = parent.getVertexFormat();
|
||||
int count = format.getElementCount();
|
||||
|
||||
for( int v = 0; v < 4; v++ )
|
||||
{
|
||||
for( int e = 0; e < count; e++ )
|
||||
{
|
||||
VertexFormatElement element = format.getElement( e );
|
||||
if( element.getUsage() == VertexFormatElement.EnumUsage.POSITION )
|
||||
{
|
||||
parent.put( e, transform( quadData[e][v] ) );
|
||||
}
|
||||
else if( element.getUsage() == VertexFormatElement.EnumUsage.NORMAL )
|
||||
{
|
||||
parent.put( e, transformNormal( quadData[e][v] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.put( e, quadData[e][v] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuadTint( int tint )
|
||||
{
|
||||
parent.setQuadTint( tint );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuadOrientation( EnumFacing orientation )
|
||||
{
|
||||
parent.setQuadOrientation( orientation );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplyDiffuseLighting( boolean diffuse )
|
||||
{
|
||||
parent.setApplyDiffuseLighting( diffuse );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTexture( TextureAtlasSprite texture )
|
||||
{
|
||||
parent.setTexture( texture );
|
||||
}
|
||||
|
||||
private float[] transform( float[] fs )
|
||||
{
|
||||
switch( fs.length )
|
||||
{
|
||||
case 3:
|
||||
javax.vecmath.Vector3f vec = new javax.vecmath.Vector3f( fs[0], fs[1], fs[2] );
|
||||
vec.x -= 0.5f;
|
||||
vec.y -= 0.5f;
|
||||
vec.z -= 0.5f;
|
||||
transform.transform( vec );
|
||||
vec.x += 0.5f;
|
||||
vec.y += 0.5f;
|
||||
vec.z += 0.5f;
|
||||
return new float[] {
|
||||
vec.x,
|
||||
vec.y,
|
||||
vec.z
|
||||
};
|
||||
case 4:
|
||||
Vector4f vecc = new Vector4f( fs[0], fs[1], fs[2], fs[3] );
|
||||
vecc.x -= 0.5f;
|
||||
vecc.y -= 0.5f;
|
||||
vecc.z -= 0.5f;
|
||||
transform.transform( vecc );
|
||||
vecc.x += 0.5f;
|
||||
vecc.y += 0.5f;
|
||||
vecc.z += 0.5f;
|
||||
return new float[] {
|
||||
vecc.x,
|
||||
vecc.y,
|
||||
vecc.z,
|
||||
vecc.w
|
||||
};
|
||||
|
||||
default:
|
||||
return fs;
|
||||
}
|
||||
}
|
||||
|
||||
private float[] transformNormal( float[] fs )
|
||||
{
|
||||
Vector4f normal;
|
||||
|
||||
switch( fs.length )
|
||||
{
|
||||
case 3:
|
||||
normal = new Vector4f( fs[0], fs[1], fs[2], 0 );
|
||||
this.transform.transform( normal );
|
||||
normal.normalize();
|
||||
return new float[] {
|
||||
normal.x,
|
||||
normal.y,
|
||||
normal.z
|
||||
};
|
||||
|
||||
case 4:
|
||||
normal = new Vector4f( fs[0], fs[1], fs[2], fs[3] );
|
||||
this.transform.transform( normal );
|
||||
normal.normalize();
|
||||
return new float[] {
|
||||
normal.x,
|
||||
normal.y,
|
||||
normal.z,
|
||||
normal.w
|
||||
};
|
||||
|
||||
default:
|
||||
return fs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.vecmath.AxisAngle4f;
|
||||
import javax.vecmath.Matrix4f;
|
||||
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrideList;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
|
||||
import net.minecraftforge.common.property.IExtendedBlockState;
|
||||
|
||||
import appeng.block.misc.BlockSkyCompass;
|
||||
import appeng.hooks.CompassManager;
|
||||
import appeng.hooks.CompassResult;
|
||||
|
||||
|
||||
/**
|
||||
* This baked model combines the quads of a compass base and the quads of a compass pointer, which will be rotated
|
||||
* around the Y-axis to get the compass to point in the right direction.
|
||||
*/
|
||||
public class SkyCompassBakedModel implements IBakedModel
|
||||
{
|
||||
|
||||
private final IBakedModel base;
|
||||
|
||||
private final IBakedModel pointer;
|
||||
|
||||
private float fallbackRotation = 0;
|
||||
|
||||
public SkyCompassBakedModel( IBakedModel base, IBakedModel pointer )
|
||||
{
|
||||
this.base = base;
|
||||
this.pointer = pointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedQuad> getQuads( @Nullable IBlockState state, @Nullable EnumFacing side, long rand )
|
||||
{
|
||||
float rotation = 0;
|
||||
// Get rotation from the special block state
|
||||
if( state instanceof IExtendedBlockState )
|
||||
{
|
||||
Float rotationOpt = ( (IExtendedBlockState) state ).getValue( BlockSkyCompass.ROTATION );
|
||||
if( rotationOpt != null )
|
||||
{
|
||||
rotation = rotationOpt;
|
||||
}
|
||||
}
|
||||
else if( state == null )
|
||||
{
|
||||
// This is used to render a compass pointing in a specific direction when being held in hand
|
||||
rotation = fallbackRotation;
|
||||
}
|
||||
|
||||
// Pre-compute the quad count to avoid list resizes
|
||||
List<BakedQuad> quads = new ArrayList<>();
|
||||
|
||||
quads.addAll( base.getQuads( state, side, rand ) );
|
||||
|
||||
// We'll add the pointer as "sideless"
|
||||
if( side == null )
|
||||
{
|
||||
// Set up the rotation around the Y-axis for the pointer
|
||||
Matrix4f matrix = new Matrix4f();
|
||||
matrix.setIdentity();
|
||||
matrix.setRotation( new AxisAngle4f( 0, 1, 0, rotation ) );
|
||||
|
||||
MatrixVertexTransformer transformer = new MatrixVertexTransformer( matrix );
|
||||
for( BakedQuad bakedQuad : pointer.getQuads( state, side, rand ) )
|
||||
{
|
||||
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder( bakedQuad.getFormat() );
|
||||
|
||||
transformer.setParent( builder );
|
||||
transformer.setVertexFormat( builder.getVertexFormat() );
|
||||
bakedQuad.pipe( transformer );
|
||||
builder.setQuadOrientation( null ); // After rotation, facing a specific side cannot be guaranteed anymore
|
||||
BakedQuad q = builder.build();
|
||||
quads.add( q );
|
||||
}
|
||||
}
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmbientOcclusion()
|
||||
{
|
||||
return base.isAmbientOcclusion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGui3d()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBuiltInRenderer()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite getParticleTexture()
|
||||
{
|
||||
return base.getParticleTexture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemCameraTransforms getItemCameraTransforms()
|
||||
{
|
||||
return base.getItemCameraTransforms();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemOverrideList getOverrides()
|
||||
{
|
||||
/*
|
||||
This handles setting the rotation of the compass when being held in hand. If it's not held in hand, it'll animate using the
|
||||
spinning animation.
|
||||
*/
|
||||
return new ItemOverrideList( Collections.emptyList() )
|
||||
{
|
||||
|
||||
@Override
|
||||
public IBakedModel handleItemState( IBakedModel originalModel, ItemStack stack, World world, EntityLivingBase entity )
|
||||
{
|
||||
if( world != null && entity instanceof EntityPlayerSP )
|
||||
{
|
||||
EntityPlayer player = (EntityPlayer) entity;
|
||||
|
||||
float offRads = (float) ( player.rotationYaw / 180.0f * (float) Math.PI + Math.PI );
|
||||
|
||||
fallbackRotation = offRads + getAnimatedRotation( player.getPosition(), true );
|
||||
}
|
||||
else
|
||||
{
|
||||
fallbackRotation = getAnimatedRotation( null, false );
|
||||
}
|
||||
|
||||
return originalModel;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the effective, animated rotation for the compass given the current position of the compass.
|
||||
*/
|
||||
public static float getAnimatedRotation( @Nullable BlockPos pos, boolean prefetch )
|
||||
{
|
||||
|
||||
// Only query for a meteor position if we know our own position
|
||||
if( pos != null )
|
||||
{
|
||||
CompassResult cr = CompassManager.INSTANCE.getCompassDirection( 0, pos.getX(), pos.getY(), pos.getZ() );
|
||||
|
||||
// Prefetch meteor positions from the server for adjacent blocks so they are available more quickly when we're moving
|
||||
if( prefetch )
|
||||
{
|
||||
for( int i = 0; i < 3; i++ )
|
||||
{
|
||||
for( int j = 0; j < 3; j++ )
|
||||
{
|
||||
CompassManager.INSTANCE.getCompassDirection( 0, pos.getX() + i - 1, pos.getY(), pos.getZ() + j - 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( cr.isValidResult() )
|
||||
{
|
||||
if( cr.isSpin() )
|
||||
{
|
||||
long timeMillis = System.currentTimeMillis();
|
||||
// .5 seconds per full rotation
|
||||
timeMillis %= 500;
|
||||
return timeMillis / 500.f * (float) Math.PI * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (float) cr.getRad();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long timeMillis = System.currentTimeMillis();
|
||||
// 3 seconds per full rotation
|
||||
timeMillis %= 3000;
|
||||
return timeMillis / 3000.f * (float) Math.PI * 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.model.IModel;
|
||||
import net.minecraftforge.client.model.ModelLoaderRegistry;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
|
||||
|
||||
/**
|
||||
* The parent model for the compass baked model. Declares the dependencies for the base and pointer submodels mostly.
|
||||
*/
|
||||
public class SkyCompassModel implements IModel
|
||||
{
|
||||
|
||||
private static final ResourceLocation MODEL_BASE = new ResourceLocation( "appliedenergistics2:block/sky_compass_base" );
|
||||
|
||||
private static final ResourceLocation MODEL_POINTER = new ResourceLocation( "appliedenergistics2:block/sky_compass_pointer" );
|
||||
|
||||
private static final List<ResourceLocation> DEPENDENCIES = ImmutableList.of( MODEL_BASE, MODEL_POINTER );
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getDependencies()
|
||||
{
|
||||
return DEPENDENCIES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getTextures()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBakedModel bake( IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter )
|
||||
{
|
||||
IModel baseModel, pointerModel;
|
||||
try
|
||||
{
|
||||
baseModel = ModelLoaderRegistry.getModel( MODEL_BASE );
|
||||
pointerModel = ModelLoaderRegistry.getModel( MODEL_POINTER );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
|
||||
IBakedModel bakedBase = baseModel.bake( state, format, bakedTextureGetter );
|
||||
IBakedModel bakedPointer = pointerModel.bake( state, format, bakedTextureGetter );
|
||||
return new SkyCompassBakedModel( bakedBase, bakedPointer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public IModelState getDefaultState()
|
||||
{
|
||||
return TRSRTransformation.identity();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.tesr;
|
||||
|
||||
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
||||
import net.minecraft.client.renderer.VertexBuffer;
|
||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import net.minecraftforge.client.model.animation.FastTESR;
|
||||
import net.minecraftforge.common.property.IExtendedBlockState;
|
||||
import net.minecraftforge.common.property.Properties;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import appeng.block.misc.BlockSkyCompass;
|
||||
import appeng.client.render.model.SkyCompassBakedModel;
|
||||
import appeng.tile.misc.TileSkyCompass;
|
||||
|
||||
|
||||
@SideOnly( Side.CLIENT )
|
||||
public class SkyCompassTESR extends FastTESR<TileSkyCompass>
|
||||
{
|
||||
|
||||
private static BlockRendererDispatcher blockRenderer;
|
||||
|
||||
@Override
|
||||
public void renderTileEntityFast( TileSkyCompass te, double x, double y, double z, float partialTicks, int destroyStage, VertexBuffer buffer )
|
||||
{
|
||||
|
||||
if( !te.hasWorldObj() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( blockRenderer == null )
|
||||
{
|
||||
blockRenderer = Minecraft.getMinecraft().getBlockRendererDispatcher();
|
||||
}
|
||||
|
||||
BlockPos pos = te.getPos();
|
||||
IBlockAccess world = MinecraftForgeClient.getRegionRenderCache( te.getWorld(), pos );
|
||||
IBlockState state = world.getBlockState( pos );
|
||||
if( state.getPropertyNames().contains( Properties.StaticProperty ) )
|
||||
{
|
||||
state = state.withProperty( Properties.StaticProperty, false );
|
||||
}
|
||||
|
||||
if( state instanceof IExtendedBlockState )
|
||||
{
|
||||
IExtendedBlockState exState = (IExtendedBlockState) state;
|
||||
|
||||
IBakedModel model = blockRenderer.getBlockModelShapes().getModelForState( exState.getClean() );
|
||||
exState = exState.withProperty( BlockSkyCompass.ROTATION, getRotation( te ) );
|
||||
|
||||
buffer.setTranslation( x - pos.getX(), y - pos.getY(), z - pos.getZ() );
|
||||
|
||||
blockRenderer.getBlockModelRenderer().renderModel( world, model, exState, pos, buffer, false );
|
||||
}
|
||||
}
|
||||
|
||||
private static float getRotation( TileSkyCompass skyCompass )
|
||||
{
|
||||
float rotation;
|
||||
|
||||
if( skyCompass.getUp() == EnumFacing.UP || skyCompass.getUp() == EnumFacing.DOWN )
|
||||
{
|
||||
rotation = SkyCompassBakedModel.getAnimatedRotation( skyCompass.getPos(), false );
|
||||
}
|
||||
else
|
||||
{
|
||||
rotation = SkyCompassBakedModel.getAnimatedRotation( null, false );
|
||||
}
|
||||
|
||||
if( skyCompass.getUp() == EnumFacing.DOWN )
|
||||
{
|
||||
rotation = flipidiy( rotation );
|
||||
}
|
||||
|
||||
return rotation;
|
||||
}
|
||||
|
||||
private static float flipidiy( float rad )
|
||||
{
|
||||
float x = (float) Math.cos( rad );
|
||||
float y = (float) Math.sin( rad );
|
||||
return (float) Math.atan2( -y, x );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user