/* * 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 . */ package appeng.thirdparty.codechicken.lib.model; import net.minecraft.client.renderer.Vector3f; import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.util.math.Box; import net.minecraft.util.math.Direction; 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 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 Direction 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(Direction 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 (this.orientation == null) { this.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(Box 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); } this.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.set(this.v2.getX(), this.v2.getY(), this.v2.getZ()); this.normal.cross(this.v1); this.normal.normalize(); if (this.format.hasNormal && setNormal) { for (Vertex vertex : this.vertices) { vertex.normal[0] = this.normal.getX(); vertex.normal[1] = this.normal.getY(); vertex.normal[2] = this.normal.getZ(); vertex.normal[3] = 0; } } this.orientation = Direction.getFacingFromVector(this.normal.getX(), this.normal.getY(), this.normal.getZ()); } /** * 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() { if (format.format != DefaultVertexFormats.BLOCK) { throw new IllegalStateException("Unable to bake this quad to the specified format. " + format.format); } int[] packedData = new int[this.format.format.getSize()]; 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); } /** * 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[] overlay; 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 (format.hasOverlay) { overlay = raw[format.overlayIndex]; } 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(); } } }