Files
Applied-Energistics-2/src/main/java/appeng/thirdparty/codechicken/lib/model/Quad.java
T

565 lines
13 KiB
Java

/*
* This file is part of CodeChickenLib.
* Copyright (c) 2018, covers1624, All rights reserved.
*
* CodeChickenLib 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 2.1 of the License, or
* (at your option) any later version.
*
* CodeChickenLib 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 CodeChickenLib. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.thirdparty.codechicken.lib.model;
import javax.vecmath.Vector3f;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
import net.minecraftforge.client.model.pipeline.IVertexProducer;
import net.minecraftforge.client.model.pipeline.LightUtil;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import appeng.thirdparty.codechicken.lib.math.InterpHelper;
/**
* A simple easy to manipulate quad format. Can be reset and then used on a different format.
*
* @author covers1624
*/
public class Quad implements IVertexProducer, ISmartVertexConsumer
{
public CachedFormat format;
public int tintIndex = -1;
public EnumFacing orientation;
public boolean diffuseLighting = true;
public TextureAtlasSprite sprite;
public Vertex[] vertices = new Vertex[4];
public boolean full;
// Not copied.
private int vertexIndex = 0;
// Cache for normal computation.
private Vector3f v1 = new Vector3f();
private Vector3f v2 = new Vector3f();
private Vector3f t = new Vector3f();
private Vector3f normal = new Vector3f();
/**
* Use this if you reset the quad each time you use it.
*/
public Quad()
{
}
/**
* use this if you want to initialize the quad with a format.
*
* @param format The format.
*/
public Quad( CachedFormat format )
{
this.format = format;
}
@Override
public VertexFormat getVertexFormat()
{
return this.format.format;
}
@Override
public void setQuadTint( int tint )
{
this.tintIndex = tint;
}
@Override
public void setQuadOrientation( EnumFacing orientation )
{
this.orientation = orientation;
}
@Override
public void setApplyDiffuseLighting( boolean diffuse )
{
this.diffuseLighting = diffuse;
}
@Override
public void setTexture( TextureAtlasSprite texture )
{
this.sprite = texture;
}
@Override
public void put( int element, float... data )
{
if( this.full )
{
throw new RuntimeException( "Unable to add data when full." );
}
Vertex v = this.vertices[this.vertexIndex];
if( v == null )
{
v = new Vertex( this.format );
this.vertices[this.vertexIndex] = v;
}
System.arraycopy( data, 0, v.raw[element], 0, data.length );
if( element == ( this.format.elementCount - 1 ) )
{
this.vertexIndex++;
if( this.vertexIndex == 4 )
{
this.vertexIndex = 0;
this.full = true;
if(orientation == null)
{
calculateOrientation(false);
}
}
}
}
@Override
public void put( Quad quad )
{
this.copyFrom( quad );
}
@Override
public void pipe( IVertexConsumer consumer )
{
if( consumer instanceof ISmartVertexConsumer )
{
( (ISmartVertexConsumer) consumer ).put( this );
}
else
{
consumer.setQuadTint( this.tintIndex );
consumer.setQuadOrientation( this.orientation );
consumer.setApplyDiffuseLighting( this.diffuseLighting );
consumer.setTexture( this.sprite );
for( Vertex v : this.vertices )
{
for( int e = 0; e < this.format.elementCount; e++ )
{
consumer.put( e, v.raw[e] );
}
}
}
}
/**
* Used to reset the interpolation values inside the provided helper.
*
* @param helper The helper.
* @param s The axis. side >> 1;
*
* @return The same helper.
*/
public InterpHelper resetInterp( InterpHelper helper, int s )
{
helper.reset( //
this.vertices[0].dx( s ), this.vertices[0].dy( s ), //
this.vertices[1].dx( s ), this.vertices[1].dy( s ), //
this.vertices[2].dx( s ), this.vertices[2].dy( s ), //
this.vertices[3].dx( s ), this.vertices[3].dy( s ) );
return helper;
}
/**
* Clamps the Quad inside the box.
*
* @param bb The box.
*/
public void clamp( AxisAlignedBB bb )
{
for( Vertex vertex : this.vertices )
{
float[] vec = vertex.vec;
vec[0] = (float) MathHelper.clamp( vec[0], bb.minX, bb.maxX );
vec[1] = (float) MathHelper.clamp( vec[1], bb.minY, bb.maxY );
vec[2] = (float) MathHelper.clamp( vec[2], bb.minZ, bb.maxZ );
}
calculateOrientation(true);
}
/**
* Re-calculates the Orientation of this quad,
* optionally the normal vector.
*
* @param setNormal If the normal vector should be updated.
*/
public void calculateOrientation( boolean setNormal )
{
this.v1.set( this.vertices[3].vec );
this.t.set( this.vertices[1].vec );
this.v1.sub( this.t );
this.v2.set( this.vertices[2].vec );
this.t.set( this.vertices[0].vec );
this.v2.sub( this.t );
this.normal.cross( this.v2, this.v1 );
this.normal.normalize();
if( this.format.hasNormal && setNormal)
{
for( Vertex vertex : this.vertices )
{
vertex.normal[0] = this.normal.x;
vertex.normal[1] = this.normal.y;
vertex.normal[2] = this.normal.z;
vertex.normal[3] = 0;
}
}
this.orientation = EnumFacing.getFacingFromVector( this.normal.x, this.normal.y, this.normal.z );
}
/**
* Used to create a new quad complete copy of this one.
*
* @return The new quad.
*/
public Quad copy()
{
if( !this.full )
{
throw new RuntimeException( "Only copying full quads is supported." );
}
Quad quad = new Quad( this.format );
quad.tintIndex = this.tintIndex;
quad.orientation = this.orientation;
quad.diffuseLighting = this.diffuseLighting;
quad.sprite = this.sprite;
quad.full = true;
for( int i = 0; i < 4; i++ )
{
quad.vertices[i] = this.vertices[i].copy();
}
return quad;
}
/**
* Copies the data inside the given quad to this one. This ignores VertexFormat, please make sure your quads are in
* the same format.
*
* @param quad The Quad to copy from.
*
* @return This quad.
*/
public Quad copyFrom( Quad quad )
{
this.tintIndex = quad.tintIndex;
this.orientation = quad.orientation;
this.diffuseLighting = quad.diffuseLighting;
this.sprite = quad.sprite;
this.full = quad.full;
for( int v = 0; v < 4; v++ )
{
for( int e = 0; e < this.format.elementCount; e++ )
{
System.arraycopy( quad.vertices[v].raw[e], 0, this.vertices[v].raw[e], 0, 4 );
}
}
return this;
}
/**
* Reset the quad to the new format.
*
* @param format The new format.
*/
public void reset( CachedFormat format )
{
this.format = format;
this.tintIndex = -1;
this.orientation = null;
this.diffuseLighting = true;
this.sprite = null;
for( int i = 0; i < this.vertices.length; i++ )
{
Vertex v = this.vertices[i];
if( v == null )
{
this.vertices[i] = v = new Vertex( format );
}
v.reset( format );
}
this.vertexIndex = 0;
this.full = false;
}
/**
* Bakes this Quad to a BakedQuad.
*
* @return The BakedQuad.
*/
public BakedQuad bake()
{
int[] packedData = new int[this.format.format.getNextOffset()];
for( int v = 0; v < 4; v++ )
{
for( int e = 0; e < this.format.elementCount; e++ )
{
LightUtil.pack( this.vertices[v].raw[e], packedData, this.format.format, v, e );
}
}
return new BakedQuad( packedData, this.tintIndex, this.orientation, this.sprite, this.diffuseLighting, this.format.format );
}
/**
* Bakes this quad to an UnpackedBakedQuad.
*
* @return The UnpackedBakedQuad.
*/
public UnpackedBakedQuad bakeUnpacked()
{
UnpackedBakedQuad.Builder quad = new UnpackedBakedQuad.Builder( this.format.format );
this.pipe( quad );
return quad.build();
}
/**
* A simple vertex format.
*/
public static class Vertex
{
public CachedFormat format;
/**
* The raw data.
*/
public float[][] raw;
// References to the arrays inside raw.
public float[] vec;
public float[] normal;
public float[] color;
public float[] uv;
public float[] lightmap;
/**
* Create a new Vertex.
*
* @param format The format for the vertex.
*/
public Vertex( CachedFormat format )
{
this.format = format;
this.raw = new float[format.elementCount][4];
this.preProcess();
}
/**
* Creates a new Vertex using the data inside the other. A copy!
*
* @param other The other.
*/
public Vertex( Vertex other )
{
this.format = other.format;
this.raw = other.raw.clone();
for( int v = 0; v < this.format.elementCount; v++ )
{
this.raw[v] = other.raw[v].clone();
}
this.preProcess();
}
/**
* Pulls references to the individual element's arrays inside raw. Modifying the individual element arrays will
* update raw.
*/
public void preProcess()
{
if( this.format.hasPosition )
{
this.vec = this.raw[this.format.positionIndex];
}
if( this.format.hasNormal )
{
this.normal = this.raw[this.format.normalIndex];
}
if( this.format.hasColor )
{
this.color = this.raw[this.format.colorIndex];
}
if( this.format.hasUV )
{
this.uv = this.raw[this.format.uvIndex];
}
if( this.format.hasLightMap )
{
this.lightmap = this.raw[this.format.lightMapIndex];
}
}
/**
* Gets the 2d X coord for the given axis.
*
* @param s The axis. side >> 1
*
* @return The x coord.
*/
public float dx( int s )
{
if( s <= 1 )
{
return this.vec[0];
}
else
{
return this.vec[2];
}
}
/**
* Gets the 2d Y coord for the given axis.
*
* @param s The axis. side >> 1
*
* @return The y coord.
*/
public float dy( int s )
{
if( s > 0 )
{
return this.vec[1];
}
else
{
return this.vec[2];
}
}
/**
* Interpolates the new color values for this Vertex using the others as a reference.
*
* @param interpHelper The InterpHelper to use.
* @param others The other Vertices to use as a template.
*
* @return The same Vertex.
*/
public Vertex interpColorFrom( InterpHelper interpHelper, Vertex[] others )
{
for( int e = 0; e < 4; e++ )
{
float p1 = others[0].color[e];
float p2 = others[1].color[e];
float p3 = others[2].color[e];
float p4 = others[3].color[e];
// Only interpolate if colors are different.
if( p1 != p2 || p2 != p3 || p3 != p4 )
{
this.color[e] = interpHelper.interpolate( p1, p2, p3, p4 );
}
}
return this;
}
/**
* Interpolates the new UV values for this Vertex using the others as a reference.
*
* @param interpHelper The InterpHelper to use.
* @param others The other Vertices to use as a template.
*
* @return The same Vertex.
*/
public Vertex interpUVFrom( InterpHelper interpHelper, Vertex[] others )
{
for( int e = 0; e < 2; e++ )
{
float p1 = others[0].uv[e];
float p2 = others[1].uv[e];
float p3 = others[2].uv[e];
float p4 = others[3].uv[e];
if( p1 != p2 || p2 != p3 || p3 != p4 )
{
this.uv[e] = interpHelper.interpolate( p1, p2, p3, p4 );
}
}
return this;
}
/**
* Interpolates the new LightMap values for this Vertex using the others as a reference.
*
* @param interpHelper The InterpHelper to use.
* @param others The other Vertices to use as a template.
*
* @return The same Vertex.
*/
public Vertex interpLightMapFrom( InterpHelper interpHelper, Vertex[] others )
{
for( int e = 0; e < 2; e++ )
{
float p1 = others[0].lightmap[e];
float p2 = others[1].lightmap[e];
float p3 = others[2].lightmap[e];
float p4 = others[3].lightmap[e];
if( p1 != p2 || p2 != p3 || p3 != p4 )
{
this.lightmap[e] = interpHelper.interpolate( p1, p2, p3, p4 );
}
}
return this;
}
/**
* Copies this Vertex to a new one.
*
* @return The new Vertex.
*/
public Vertex copy()
{
return new Vertex( this );
}
/**
* Resets the Vertex to a new format. Expands the raw array if needed.
*
* @param format The format to reset to.
*/
public void reset( CachedFormat format )
{
// If the format is different and our raw array is smaller, then expand it.
if( !this.format.equals( format ) && format.elementCount > this.raw.length )
{
this.raw = new float[format.elementCount][4];
}
this.format = format;
this.vec = null;
this.normal = null;
this.color = null;
this.uv = null;
this.lightmap = null;
// for (float[] f : raw) {
// Arrays.fill(f, 0F);
// }
this.preProcess();
}
}
}