Moving to source sets

This commit is contained in:
Sebastian Hartte
2020-07-01 23:36:51 +02:00
parent f2e3d81fd7
commit 2642ced86b
2924 changed files with 794 additions and 796 deletions
@@ -0,0 +1,727 @@
/*
* 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.cablebus;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.function.Function;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;
import net.minecraft.util.Identifier;
import appeng.api.util.AECableType;
import appeng.api.util.AEColor;
import appeng.core.AppEng;
/**
* A helper class that builds quads for cable connections.
*/
class CableBuilder {
// Textures for the cable core types, one per type/color pair
private final EnumMap<CableCoreType, EnumMap<AEColor, Sprite>> coreTextures;
// Textures for rendering the actual connection cubes, one per type/color pair
private final EnumMap<AECableType, EnumMap<AEColor, Sprite>> connectionTextures;
private final SmartCableTextures smartCableTextures;
CableBuilder(Function<SpriteIdentifier, Sprite> bakedTextureGetter) {
this.coreTextures = new EnumMap<>(CableCoreType.class);
for (CableCoreType type : CableCoreType.values()) {
EnumMap<AEColor, Sprite> colorTextures = new EnumMap<>(AEColor.class);
for (AEColor color : AEColor.values()) {
colorTextures.put(color, bakedTextureGetter.apply(type.getTexture(color)));
}
this.coreTextures.put(type, colorTextures);
}
this.connectionTextures = new EnumMap<>(AECableType.class);
for (AECableType type : AECableType.VALIDCABLES) {
EnumMap<AEColor, Sprite> colorTextures = new EnumMap<>(AEColor.class);
for (AEColor color : AEColor.values()) {
colorTextures.put(color, bakedTextureGetter.apply(getConnectionTexture(type, color)));
}
this.connectionTextures.put(type, colorTextures);
}
this.smartCableTextures = new SmartCableTextures(bakedTextureGetter);
}
static SpriteIdentifier getConnectionTexture(AECableType cableType, AEColor color) {
String textureFolder;
switch (cableType) {
case GLASS:
textureFolder = "parts/cable/glass/";
break;
case COVERED:
textureFolder = "parts/cable/covered/";
break;
case SMART:
textureFolder = "parts/cable/smart/";
break;
case DENSE_COVERED:
textureFolder = "parts/cable/dense_covered/";
break;
case DENSE_SMART:
textureFolder = "parts/cable/dense_smart/";
break;
default:
throw new IllegalStateException("Cable type " + cableType + " does not support connections.");
}
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, textureFolder + color.name().toLowerCase()));
}
/**
* Adds the core of a cable to the given list of quads.
*
* The type of cable core is automatically deduced from the given cable type.
*/
public void addCableCore(AECableType cableType, AEColor color, List<BakedQuad> quadsOut) {
switch (cableType) {
case GLASS:
this.addCableCore(CableCoreType.GLASS, color, quadsOut);
break;
case COVERED:
case SMART:
this.addCableCore(CableCoreType.COVERED, color, quadsOut);
break;
case DENSE_COVERED:
case DENSE_SMART:
this.addCableCore(CableCoreType.DENSE, color, quadsOut);
break;
default:
}
}
public void addCableCore(CableCoreType coreType, AEColor color, List<BakedQuad> quadsOut) {
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
Sprite texture = this.coreTextures.get(coreType).get(color);
cubeBuilder.setTexture(texture);
switch (coreType) {
case GLASS:
cubeBuilder.addCube(6, 6, 6, 10, 10, 10);
break;
case COVERED:
cubeBuilder.addCube(5, 5, 5, 11, 11, 11);
break;
case DENSE:
cubeBuilder.addCube(3, 3, 3, 13, 13, 13);
break;
}
}
public void addGlassConnection(Direction facing, AEColor cableColor, AECableType connectionType,
boolean cableBusAdjacent, List<BakedQuad> quadsOut) {
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
// We render all faces except the one on the connection side
cubeBuilder.setDrawFaces(EnumSet.complementOf(EnumSet.of(facing)));
// For to-machine connections, use a thicker end-cap for the connection
if (connectionType != AECableType.GLASS && !cableBusAdjacent) {
Sprite texture = this.connectionTextures.get(AECableType.COVERED).get(cableColor);
cubeBuilder.setTexture(texture);
this.addBigCoveredCableSizedCube(facing, cubeBuilder);
}
Sprite texture = this.connectionTextures.get(AECableType.GLASS).get(cableColor);
cubeBuilder.setTexture(texture);
switch (facing) {
case DOWN:
cubeBuilder.addCube(6, 0, 6, 10, 6, 10);
break;
case EAST:
cubeBuilder.addCube(10, 6, 6, 16, 10, 10);
break;
case NORTH:
cubeBuilder.addCube(6, 6, 0, 10, 10, 6);
break;
case SOUTH:
cubeBuilder.addCube(6, 6, 10, 10, 10, 16);
break;
case UP:
cubeBuilder.addCube(6, 10, 6, 10, 16, 10);
break;
case WEST:
cubeBuilder.addCube(0, 6, 6, 6, 10, 10);
break;
}
}
public void addStraightGlassConnection(Direction facing, AEColor cableColor, List<BakedQuad> quadsOut) {
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
// We render all faces except the connection caps. We can do this because the
// glass cable is the smallest one
// and its ends will always be covered by something
cubeBuilder.setDrawFaces(EnumSet.complementOf(EnumSet.of(facing, facing.getOpposite())));
Sprite texture = this.connectionTextures.get(AECableType.GLASS).get(cableColor);
cubeBuilder.setTexture(texture);
switch (facing) {
case DOWN:
case UP:
cubeBuilder.addCube(6, 0, 6, 10, 16, 10);
break;
case NORTH:
case SOUTH:
cubeBuilder.addCube(6, 6, 0, 10, 10, 16);
break;
case EAST:
case WEST:
cubeBuilder.addCube(0, 6, 6, 16, 10, 10);
break;
}
}
public void addConstrainedGlassConnection(Direction facing, AEColor cableColor, int distanceFromEdge,
List<BakedQuad> quadsOut) {
// Glass connections reach only 6 voxels from the edge
if (distanceFromEdge >= 6) {
return;
}
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
Sprite texture = this.connectionTextures.get(AECableType.GLASS).get(cableColor);
cubeBuilder.setTexture(texture);
switch (facing) {
case DOWN:
cubeBuilder.addCube(6, distanceFromEdge, 6, 10, 6, 10);
break;
case EAST:
cubeBuilder.addCube(10, 6, 6, 16 - distanceFromEdge, 10, 10);
break;
case NORTH:
cubeBuilder.addCube(6, 6, distanceFromEdge, 10, 10, 6);
break;
case SOUTH:
cubeBuilder.addCube(6, 6, 10, 10, 10, 16 - distanceFromEdge);
break;
case UP:
cubeBuilder.addCube(6, 10, 6, 10, 16 - distanceFromEdge, 10);
break;
case WEST:
cubeBuilder.addCube(distanceFromEdge, 6, 6, 6, 10, 10);
break;
}
}
public void addCoveredConnection(Direction facing, AEColor cableColor, AECableType connectionType,
boolean cableBusAdjacent, List<BakedQuad> quadsOut) {
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
// We render all faces except the one on the connection side
cubeBuilder.setDrawFaces(EnumSet.complementOf(EnumSet.of(facing)));
Sprite texture = this.connectionTextures.get(AECableType.COVERED).get(cableColor);
cubeBuilder.setTexture(texture);
// Draw a covered connection, if anything but glass is requested
if (connectionType != AECableType.GLASS && !cableBusAdjacent) {
this.addBigCoveredCableSizedCube(facing, cubeBuilder);
}
addCoveredCableSizedCube(facing, cubeBuilder);
}
public void addStraightCoveredConnection(Direction facing, AEColor cableColor, List<BakedQuad> quadsOut) {
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
Sprite texture = this.connectionTextures.get(AECableType.COVERED).get(cableColor);
cubeBuilder.setTexture(texture);
setStraightCableUVs(cubeBuilder, facing, 5, 11);
addStraightCoveredCableSizedCube(facing, cubeBuilder);
}
private static void setStraightCableUVs(CubeBuilder cubeBuilder, Direction facing, int x, int y) {
switch (facing) {
case DOWN:
case UP:
cubeBuilder.setCustomUv(Direction.NORTH, x, 0, y, x);
cubeBuilder.setCustomUv(Direction.EAST, x, 0, y, x);
cubeBuilder.setCustomUv(Direction.SOUTH, x, 0, y, x);
cubeBuilder.setCustomUv(Direction.WEST, x, 0, y, x);
break;
case EAST:
case WEST:
cubeBuilder.setCustomUv(Direction.UP, 0, x, x, y);
cubeBuilder.setCustomUv(Direction.DOWN, 0, x, x, y);
cubeBuilder.setCustomUv(Direction.NORTH, 0, x, x, y);
cubeBuilder.setCustomUv(Direction.SOUTH, 0, x, x, y);
break;
case NORTH:
case SOUTH:
cubeBuilder.setCustomUv(Direction.UP, x, 0, y, x);
cubeBuilder.setCustomUv(Direction.DOWN, x, 0, y, x);
cubeBuilder.setCustomUv(Direction.EAST, 0, x, x, y);
cubeBuilder.setCustomUv(Direction.WEST, 0, x, x, y);
break;
}
}
public void addConstrainedCoveredConnection(Direction facing, AEColor cableColor, int distanceFromEdge,
List<BakedQuad> quadsOut) {
// The core of a covered cable reaches up to 5 voxels from the block edge, so
// drawing a connection can only occur from there onwards
if (distanceFromEdge >= 5) {
return;
}
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
Sprite texture = this.connectionTextures.get(AECableType.COVERED).get(cableColor);
cubeBuilder.setTexture(texture);
addCoveredCableSizedCube(facing, distanceFromEdge, cubeBuilder);
}
public void addSmartConnection(Direction facing, AEColor cableColor, AECableType connectionType,
boolean cableBusAdjacent, int channels, List<BakedQuad> quadsOut) {
if (connectionType == AECableType.COVERED || connectionType == AECableType.GLASS) {
this.addCoveredConnection(facing, cableColor, connectionType, cableBusAdjacent, quadsOut);
return;
}
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
// We render all faces except the one on the connection side
cubeBuilder.setDrawFaces(EnumSet.complementOf(EnumSet.of(facing)));
Sprite texture = this.connectionTextures.get(AECableType.SMART).get(cableColor);
cubeBuilder.setTexture(texture);
Sprite oddChannel = this.smartCableTextures.getOddTextureForChannels(channels);
Sprite evenChannel = this.smartCableTextures.getEvenTextureForChannels(channels);
// For to-machine connections, use a thicker end-cap for the connection
if (connectionType != AECableType.GLASS && !cableBusAdjacent) {
this.addBigCoveredCableSizedCube(facing, cubeBuilder);
// Render the channel indicators brightly lit at night
cubeBuilder.setRenderFullBright(true);
cubeBuilder.setTexture(oddChannel);
cubeBuilder.setColorRGB(cableColor.blackVariant);
this.addBigCoveredCableSizedCube(facing, cubeBuilder);
cubeBuilder.setTexture(evenChannel);
cubeBuilder.setColorRGB(cableColor.whiteVariant);
this.addBigCoveredCableSizedCube(facing, cubeBuilder);
// Reset back to normal rendering for the rest
cubeBuilder.setRenderFullBright(false);
cubeBuilder.setTexture(texture);
}
addCoveredCableSizedCube(facing, cubeBuilder);
// Render the channel indicators brightly lit at night
cubeBuilder.setRenderFullBright(true);
cubeBuilder.setTexture(oddChannel);
cubeBuilder.setColorRGB(cableColor.blackVariant);
addCoveredCableSizedCube(facing, cubeBuilder);
cubeBuilder.setTexture(evenChannel);
cubeBuilder.setColorRGB(cableColor.whiteVariant);
addCoveredCableSizedCube(facing, cubeBuilder);
}
public void addStraightSmartConnection(Direction facing, AEColor cableColor, int channels,
List<BakedQuad> quadsOut) {
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
Sprite texture = this.connectionTextures.get(AECableType.SMART).get(cableColor);
cubeBuilder.setTexture(texture);
setStraightCableUVs(cubeBuilder, facing, 5, 11);
addStraightCoveredCableSizedCube(facing, cubeBuilder);
Sprite oddChannel = this.smartCableTextures.getOddTextureForChannels(channels);
Sprite evenChannel = this.smartCableTextures.getEvenTextureForChannels(channels);
// Render the channel indicators brightly lit at night
cubeBuilder.setRenderFullBright(true);
cubeBuilder.setTexture(oddChannel);
cubeBuilder.setColorRGB(cableColor.blackVariant);
addStraightCoveredCableSizedCube(facing, cubeBuilder);
cubeBuilder.setTexture(evenChannel);
cubeBuilder.setColorRGB(cableColor.whiteVariant);
addStraightCoveredCableSizedCube(facing, cubeBuilder);
}
public void addConstrainedSmartConnection(Direction facing, AEColor cableColor, int distanceFromEdge, int channels,
List<BakedQuad> quadsOut) {
// Same as with covered cables, the smart cable's core extends up to 5 voxels
// away from the edge.
// Drawing a connection to any point before that point is fruitless
if (distanceFromEdge >= 5) {
return;
}
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
Sprite texture = this.connectionTextures.get(AECableType.SMART).get(cableColor);
cubeBuilder.setTexture(texture);
addCoveredCableSizedCube(facing, distanceFromEdge, cubeBuilder);
Sprite oddChannel = this.smartCableTextures.getOddTextureForChannels(channels);
Sprite evenChannel = this.smartCableTextures.getEvenTextureForChannels(channels);
// Render the channel indicators brightly lit at night
cubeBuilder.setRenderFullBright(true);
cubeBuilder.setTexture(oddChannel);
cubeBuilder.setColorRGB(cableColor.blackVariant);
addCoveredCableSizedCube(facing, distanceFromEdge, cubeBuilder);
cubeBuilder.setTexture(evenChannel);
cubeBuilder.setColorRGB(cableColor.whiteVariant);
addCoveredCableSizedCube(facing, distanceFromEdge, cubeBuilder);
}
public void addDenseCoveredConnection(Direction facing, AEColor cableColor, AECableType connectionType,
boolean cableBusAdjacent, List<BakedQuad> quadsOut) {
// Dense cables only render their connections as dense if the adjacent blocks
// actually wants that
if (connectionType == AECableType.COVERED || connectionType == AECableType.SMART
|| connectionType == AECableType.GLASS) {
this.addCoveredConnection(facing, cableColor, connectionType, cableBusAdjacent, quadsOut);
return;
}
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
// We render all faces except the one on the connection side
cubeBuilder.setDrawFaces(EnumSet.complementOf(EnumSet.of(facing)));
Sprite texture = this.connectionTextures.get(AECableType.DENSE_COVERED).get(cableColor);
cubeBuilder.setTexture(texture);
addDenseCableSizedCube(facing, cubeBuilder);
// Reset back to normal rendering for the rest
cubeBuilder.setRenderFullBright(false);
cubeBuilder.setTexture(texture);
}
public void addDenseSmartConnection(Direction facing, AEColor cableColor, AECableType connectionType,
boolean cableBusAdjacent, int channels, List<BakedQuad> quadsOut) {
// Dense cables only render their connections as dense if the adjacent blocks
// actually wants that
if (connectionType == AECableType.SMART) {
this.addSmartConnection(facing, cableColor, connectionType, cableBusAdjacent, channels, quadsOut);
return;
} else if (connectionType == AECableType.COVERED || connectionType == AECableType.GLASS) {
this.addCoveredConnection(facing, cableColor, connectionType, cableBusAdjacent, quadsOut);
return;
} else if (connectionType == AECableType.DENSE_COVERED) {
this.addDenseCoveredConnection(facing, cableColor, connectionType, cableBusAdjacent, quadsOut);
return;
}
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
// We render all faces except the one on the connection side
cubeBuilder.setDrawFaces(EnumSet.complementOf(EnumSet.of(facing)));
Sprite texture = this.connectionTextures.get(AECableType.DENSE_SMART).get(cableColor);
cubeBuilder.setTexture(texture);
addDenseCableSizedCube(facing, cubeBuilder);
// Dense cables show used channels in groups of 4, rounded up
channels = (channels + 3) / 4;
Sprite oddChannel = this.smartCableTextures.getOddTextureForChannels(channels);
Sprite evenChannel = this.smartCableTextures.getEvenTextureForChannels(channels);
// Render the channel indicators brightly lit at night
cubeBuilder.setRenderFullBright(true);
cubeBuilder.setTexture(oddChannel);
cubeBuilder.setColorRGB(cableColor.blackVariant);
addDenseCableSizedCube(facing, cubeBuilder);
cubeBuilder.setTexture(evenChannel);
cubeBuilder.setColorRGB(cableColor.whiteVariant);
addDenseCableSizedCube(facing, cubeBuilder);
// Reset back to normal rendering for the rest
cubeBuilder.setRenderFullBright(false);
cubeBuilder.setTexture(texture);
}
public void addStraightDenseCoveredConnection(Direction facing, AEColor cableColor, List<BakedQuad> quadsOut) {
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
Sprite texture = this.connectionTextures.get(AECableType.DENSE_COVERED).get(cableColor);
cubeBuilder.setTexture(texture);
setStraightCableUVs(cubeBuilder, facing, 5, 11);
addStraightDenseCableSizedCube(facing, cubeBuilder);
}
public void addStraightDenseSmartConnection(Direction facing, AEColor cableColor, int channels,
List<BakedQuad> quadsOut) {
CubeBuilder cubeBuilder = new CubeBuilder(quadsOut);
Sprite texture = this.connectionTextures.get(AECableType.DENSE_SMART).get(cableColor);
cubeBuilder.setTexture(texture);
setStraightCableUVs(cubeBuilder, facing, 5, 11);
addStraightDenseCableSizedCube(facing, cubeBuilder);
// Dense cables show used channels in groups of 4, rounded up
channels = (channels + 3) / 4;
Sprite oddChannel = this.smartCableTextures.getOddTextureForChannels(channels);
Sprite evenChannel = this.smartCableTextures.getEvenTextureForChannels(channels);
// Render the channel indicators brightly lit at night
cubeBuilder.setRenderFullBright(true);
cubeBuilder.setTexture(oddChannel);
cubeBuilder.setColorRGB(cableColor.blackVariant);
addStraightDenseCableSizedCube(facing, cubeBuilder);
cubeBuilder.setTexture(evenChannel);
cubeBuilder.setColorRGB(cableColor.whiteVariant);
addStraightDenseCableSizedCube(facing, cubeBuilder);
}
private static void addDenseCableSizedCube(Direction facing, CubeBuilder cubeBuilder) {
switch (facing) {
case DOWN:
cubeBuilder.addCube(4, 0, 4, 12, 5, 12);
break;
case EAST:
cubeBuilder.addCube(11, 4, 4, 16, 12, 12);
break;
case NORTH:
cubeBuilder.addCube(4, 4, 0, 12, 12, 5);
break;
case SOUTH:
cubeBuilder.addCube(4, 4, 11, 12, 12, 16);
break;
case UP:
cubeBuilder.addCube(4, 11, 4, 12, 16, 12);
break;
case WEST:
cubeBuilder.addCube(0, 4, 4, 5, 12, 12);
break;
}
}
// Adds a cube to the given cube builder that has the size of a dense cable
// connection and spans the entire block
// for the given direction
private static void addStraightDenseCableSizedCube(Direction facing, CubeBuilder cubeBuilder) {
switch (facing) {
case DOWN:
case UP:
cubeBuilder.setUvRotation(Direction.EAST, 3);
cubeBuilder.addCube(3, 0, 3, 13, 16, 13);
cubeBuilder.setUvRotation(Direction.EAST, 0);
break;
case EAST:
case WEST:
cubeBuilder.setUvRotation(Direction.SOUTH, 3);
cubeBuilder.setUvRotation(Direction.NORTH, 3);
cubeBuilder.addCube(0, 3, 3, 16, 13, 13);
cubeBuilder.setUvRotation(Direction.SOUTH, 0);
cubeBuilder.setUvRotation(Direction.NORTH, 0);
break;
case NORTH:
case SOUTH:
cubeBuilder.setUvRotation(Direction.EAST, 3);
cubeBuilder.setUvRotation(Direction.WEST, 3);
cubeBuilder.addCube(3, 3, 0, 13, 13, 16);
cubeBuilder.setUvRotation(Direction.EAST, 0);
cubeBuilder.setUvRotation(Direction.WEST, 0);
break;
}
}
// Adds a cube to the given cube builder that has the size of a covered cable
// connection from the core of the cable
// to the given face
private static void addCoveredCableSizedCube(Direction facing, CubeBuilder cubeBuilder) {
switch (facing) {
case DOWN:
cubeBuilder.addCube(6, 0, 6, 10, 5, 10);
break;
case EAST:
cubeBuilder.addCube(11, 6, 6, 16, 10, 10);
break;
case NORTH:
cubeBuilder.addCube(6, 6, 0, 10, 10, 5);
break;
case SOUTH:
cubeBuilder.addCube(6, 6, 11, 10, 10, 16);
break;
case UP:
cubeBuilder.addCube(6, 11, 6, 10, 16, 10);
break;
case WEST:
cubeBuilder.addCube(0, 6, 6, 5, 10, 10);
break;
}
}
// Adds a cube to the given cube builder that has the size of a covered cable
// connection and spans the entire block
// for the given direction
private static void addStraightCoveredCableSizedCube(Direction facing, CubeBuilder cubeBuilder) {
switch (facing) {
case DOWN:
case UP:
cubeBuilder.setUvRotation(Direction.EAST, 3);
cubeBuilder.addCube(5, 0, 5, 11, 16, 11);
cubeBuilder.setUvRotation(Direction.EAST, 0);
break;
case EAST:
case WEST:
cubeBuilder.setUvRotation(Direction.SOUTH, 3);
cubeBuilder.setUvRotation(Direction.NORTH, 3);
cubeBuilder.addCube(0, 5, 5, 16, 11, 11);
cubeBuilder.setUvRotation(Direction.SOUTH, 0);
cubeBuilder.setUvRotation(Direction.NORTH, 0);
break;
case NORTH:
case SOUTH:
cubeBuilder.setUvRotation(Direction.EAST, 3);
cubeBuilder.setUvRotation(Direction.WEST, 3);
cubeBuilder.addCube(5, 5, 0, 11, 11, 16);
cubeBuilder.setUvRotation(Direction.EAST, 0);
cubeBuilder.setUvRotation(Direction.WEST, 0);
break;
}
}
private static void addCoveredCableSizedCube(Direction facing, int distanceFromEdge, CubeBuilder cubeBuilder) {
switch (facing) {
case DOWN:
cubeBuilder.addCube(6, distanceFromEdge, 6, 10, 5, 10);
break;
case EAST:
cubeBuilder.addCube(11, 6, 6, 16 - distanceFromEdge, 10, 10);
break;
case NORTH:
cubeBuilder.addCube(6, 6, distanceFromEdge, 10, 10, 5);
break;
case SOUTH:
cubeBuilder.addCube(6, 6, 11, 10, 10, 16 - distanceFromEdge);
break;
case UP:
cubeBuilder.addCube(6, 11, 6, 10, 16 - distanceFromEdge, 10);
break;
case WEST:
cubeBuilder.addCube(distanceFromEdge, 6, 6, 5, 10, 10);
break;
}
}
/**
* This renders a slightly bigger covered cable connection to the specified
* side. This is used to connect cable cores with adjacent machines that do not
* want to be connected to using a glass cable connection. This applies to most
* machines (interfaces, etc.)
*/
private void addBigCoveredCableSizedCube(Direction facing, CubeBuilder cubeBuilder) {
switch (facing) {
case DOWN:
cubeBuilder.addCube(5, 0, 5, 11, 4, 11);
break;
case EAST:
cubeBuilder.addCube(12, 5, 5, 16, 11, 11);
break;
case NORTH:
cubeBuilder.addCube(5, 5, 0, 11, 11, 4);
break;
case SOUTH:
cubeBuilder.addCube(5, 5, 12, 11, 11, 16);
break;
case UP:
cubeBuilder.addCube(5, 12, 5, 11, 16, 11);
break;
case WEST:
cubeBuilder.addCube(0, 5, 5, 4, 11, 11);
break;
}
}
// Get all textures needed for building the actual cable quads
public static List<SpriteIdentifier> getTextures() {
List<SpriteIdentifier> locations = new ArrayList<>();
for (CableCoreType coreType : CableCoreType.values()) {
for (AEColor color : AEColor.values()) {
locations.add(coreType.getTexture(color));
}
}
for (AECableType cableType : AECableType.VALIDCABLES) {
for (AEColor color : AEColor.values()) {
locations.add(getConnectionTexture(cableType, color));
}
}
Collections.addAll(locations, SmartCableTextures.SMART_CHANNELS_TEXTURES);
return locations;
}
public Sprite getCoreTexture(CableCoreType coreType, AEColor color) {
return this.coreTextures.get(coreType).get(color);
}
}
@@ -0,0 +1,367 @@
/*
* 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.cablebus;
import appeng.api.parts.IPartModel;
import appeng.api.util.AECableType;
import appeng.api.util.AEColor;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.json.ModelOverrideList;
import net.minecraft.client.render.model.json.ModelTransformation;
import net.minecraft.client.texture.MissingSprite;
import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockRenderView;
import javax.annotation.Nullable;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.Supplier;
public class CableBusBakedModel implements BakedModel, FabricBakedModel {
// FIXME: This entire cache seems dumb as shit
private static final Map<CableBusRenderState, Mesh> CABLE_MODEL_CACHE = new HashMap<>();
private final CableBuilder cableBuilder;
private final FacadeBuilder facadeBuilder;
private final Map<Identifier, BakedModel> partModels;
private final Sprite particleTexture;
CableBusBakedModel(CableBuilder cableBuilder, FacadeBuilder facadeBuilder,
Map<Identifier, BakedModel> partModels, Sprite particleTexture) {
this.cableBuilder = cableBuilder;
this.facadeBuilder = facadeBuilder;
this.partModels = partModels;
this.particleTexture = particleTexture;
}
@Override
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
// This model will only ever be used for blocks
}
private CableBusRenderState getRenderState(BlockRenderView blockView, BlockPos pos) {
RenderAttachedBlockView renderAttachedBlockView = (RenderAttachedBlockView) blockView;
Object renderAttachment = renderAttachedBlockView.getBlockEntityRenderAttachment(pos);
if (renderAttachment instanceof CableBusRenderState) {
return (CableBusRenderState) renderAttachment;
}
return null;
}
@Override
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
CableBusRenderState renderState = getRenderState(blockView, pos);
if (renderState == null) {
return;
}
RenderLayer layer = RenderLayer.getCutout(); // FIXME: Fabric can only render within one layer (?)
// The core parts of the cable will only be rendered in the CUTOUT layer.
// Facades will add them selves to what ever the block would be rendered with,
// except when transparent facades are enabled, they are forced to TRANSPARENT.
if (layer == RenderLayer.getCutout()) {
// First, handle the cable at the center of the cable bus
final Mesh cableModel = CABLE_MODEL_CACHE.computeIfAbsent(renderState, this::buildCableModel);
if (cableModel != null) {
context.meshConsumer().accept(cableModel);
}
// Then handle attachments
for (Direction facing : Direction.values()) {
final IPartModel partModel = renderState.getAttachments().get(facing);
if (partModel == null) {
continue;
}
Object partModelData = renderState.getPartModelData().get(facing);
for (Identifier model : partModel.getModels()) {
BakedModel bakedModel = this.partModels.get(model);
if (bakedModel == null) {
throw new IllegalStateException("Trying to use an unregistered part model: " + model);
}
context.pushTransform(QuadRotator.get(facing, Direction.UP));
if (bakedModel instanceof FabricBakedModel) {
((FabricBakedModel) bakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context);
} else {
context.fallbackConsumer().accept(bakedModel);
}
context.popTransform();
}
}
}
this.facadeBuilder.buildFacadeQuads(layer, renderState, randomSupplier, context, this.partModels::get);
}
// Determines whether a cable is connected to exactly two sides that are
// opposite each other
private static boolean isStraightLine(AECableType cableType, EnumMap<Direction, AECableType> sides) {
final Iterator<Entry<Direction, AECableType>> it = sides.entrySet().iterator();
if (!it.hasNext()) {
return false; // No connections
}
final Entry<Direction, AECableType> nextConnection = it.next();
final Direction firstSide = nextConnection.getKey();
final AECableType firstType = nextConnection.getValue();
if (!it.hasNext()) {
return false; // Only a single connection
}
if (firstSide.getOpposite() != it.next().getKey()) {
return false; // Connected to two sides that are not opposite each other
}
if (it.hasNext()) {
return false; // Must not have any other connection points
}
final AECableType secondType = sides.get(firstSide.getOpposite());
return firstType == secondType && cableType == firstType && cableType == secondType;
}
private Mesh buildCableModel(CableBusRenderState renderState) {
AECableType cableType = renderState.getCableType();
if (cableType == AECableType.NONE) {
return null;
}
AEColor cableColor = renderState.getCableColor();
EnumMap<Direction, AECableType> connectionTypes = renderState.getConnectionTypes();
MeshBuilder builder = IndigoRenderer.INSTANCE.meshBuilder();
QuadEmitter emitter = builder.getEmitter();
// FIXME
// FIXME // If the connection is straight, no busses are attached, and no covered core
// FIXME // has been forced (in case of glass
// FIXME // cables), then render the cable as a simplified straight line.
// FIXME boolean noAttachments = !renderState.getAttachments().values().stream()
// FIXME .anyMatch(IPartModel::requireCableConnection);
// FIXME if (noAttachments && isStraightLine(cableType, connectionTypes)) {
// FIXME Direction facing = connectionTypes.keySet().iterator().next();
// FIXME
// FIXME switch (cableType) {
// FIXME case GLASS:
// FIXME this.cableBuilder.addStraightGlassConnection(facing, cableColor, emitter);
// FIXME break;
// FIXME case COVERED:
// FIXME this.cableBuilder.addStraightCoveredConnection(facing, cableColor, emitter);
// FIXME break;
// FIXME case SMART:
// FIXME this.cableBuilder.addStraightSmartConnection(facing, cableColor,
// FIXME renderState.getChannelsOnSide().get(facing), emitter);
// FIXME break;
// FIXME case DENSE_COVERED:
// FIXME this.cableBuilder.addStraightDenseCoveredConnection(facing, cableColor, emitter);
// FIXME break;
// FIXME case DENSE_SMART:
// FIXME this.cableBuilder.addStraightDenseSmartConnection(facing, cableColor,
// FIXME renderState.getChannelsOnSide().get(facing), emitter);
// FIXME break;
// FIXME default:
// FIXME break;
// FIXME }
// FIXME
// FIXME return null; // Don't render the other form of connection
// FIXME }
// FIXME
// FIXME this.cableBuilder.addCableCore(renderState.getCoreType(), cableColor, emitter);
// FIXME
// FIXME // Render all internal connections to attachments
// FIXME EnumMap<Direction, Integer> attachmentConnections = renderState.getAttachmentConnections();
// FIXME for (Direction facing : attachmentConnections.keySet()) {
// FIXME int distance = attachmentConnections.get(facing);
// FIXME int channels = renderState.getChannelsOnSide().get(facing);
// FIXME
// FIXME switch (cableType) {
// FIXME case GLASS:
// FIXME this.cableBuilder.addConstrainedGlassConnection(facing, cableColor, distance, emitter);
// FIXME break;
// FIXME case COVERED:
// FIXME this.cableBuilder.addConstrainedCoveredConnection(facing, cableColor, distance, emitter);
// FIXME break;
// FIXME case SMART:
// FIXME this.cableBuilder.addConstrainedSmartConnection(facing, cableColor, distance, channels, emitter);
// FIXME break;
// FIXME case DENSE_COVERED:
// FIXME case DENSE_SMART:
// FIXME // Dense cables do not render connections to parts since none can be attached
// FIXME break;
// FIXME default:
// FIXME break;
// FIXME }
// FIXME }
// FIXME
// FIXME // Render all outgoing connections using the appropriate type
// FIXME for (final Entry<Direction, AECableType> connection : connectionTypes.entrySet()) {
// FIXME final Direction facing = connection.getKey();
// FIXME final AECableType connectionType = connection.getValue();
// FIXME final boolean cableBusAdjacent = renderState.getCableBusAdjacent().contains(facing);
// FIXME final int channels = renderState.getChannelsOnSide().get(facing);
// FIXME
// FIXME switch (cableType) {
// FIXME case GLASS:
// FIXME this.cableBuilder.addGlassConnection(facing, cableColor, connectionType, cableBusAdjacent,
// FIXME emitter);
// FIXME break;
// FIXME case COVERED:
// FIXME this.cableBuilder.addCoveredConnection(facing, cableColor, connectionType, cableBusAdjacent,
// FIXME emitter);
// FIXME break;
// FIXME case SMART:
// FIXME this.cableBuilder.addSmartConnection(facing, cableColor, connectionType, cableBusAdjacent, channels,
// FIXME emitter);
// FIXME break;
// FIXME case DENSE_COVERED:
// FIXME this.cableBuilder.addDenseCoveredConnection(facing, cableColor, connectionType, cableBusAdjacent,
// FIXME emitter);
// FIXME break;
// FIXME case DENSE_SMART:
// FIXME this.cableBuilder.addDenseSmartConnection(facing, cableColor, connectionType, cableBusAdjacent,
// FIXME channels, emitter);
// FIXME break;
// FIXME default:
// FIXME break;
// FIXME }
// FIXME }
return builder.build();
}
/**
* Gets a list of texture sprites appropriate for particles (digging, etc.)
* given the render state for a cable bus.
*/
public List<Sprite> getParticleTextures(CableBusRenderState renderState) {
CableCoreType coreType = CableCoreType.fromCableType(renderState.getCableType());
AEColor cableColor = renderState.getCableColor();
List<Sprite> result = new ArrayList<>();
if (coreType != null) {
result.add(this.cableBuilder.getCoreTexture(coreType, cableColor));
}
// If no core is present, just use the first part that comes into play
for (Direction side : renderState.getAttachments().keySet()) {
IPartModel partModel = renderState.getAttachments().get(side);
for (Identifier model : partModel.getModels()) {
BakedModel bakedModel = this.partModels.get(model);
if (bakedModel == null) {
throw new IllegalStateException("Trying to use an unregistered part model: " + model);
}
Sprite particleTexture = bakedModel.getSprite();
// If a part sub-model has no particle texture (indicated by it being the
// missing texture),
// don't add it, so we don't get ugly missing texture break particles.
if (!isMissingTexture(particleTexture)) {
result.add(particleTexture);
}
}
}
return result;
}
private boolean isMissingTexture(Sprite particleTexture) {
return particleTexture instanceof MissingSprite;
}
@Override
public boolean useAmbientOcclusion() {
return true;
}
@Override
public boolean hasDepth() {
return false;
}
@Override
public boolean isSideLit() {
return false;// TODO
}
@Override
public boolean isBuiltin() {
return false;
}
@Override
public Sprite getSprite() {
return this.particleTexture;
}
@Override
public ModelTransformation getTransformation() {
return ModelTransformation.NONE;
}
@Override
public ModelOverrideList getOverrides() {
return ModelOverrideList.EMPTY;
}
public static void clearCache() {
CABLE_MODEL_CACHE.clear();
}
@Override
public boolean isVanillaAdapter() {
return false;
}
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction face, Random random) {
throw new IllegalStateException();
}
}
@@ -0,0 +1,57 @@
package appeng.client.render.cablebus;
import net.minecraft.client.particle.ParticleTextureSheet;
import net.minecraft.client.particle.SpriteBillboardParticle;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.world.ClientWorld;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
// Derived from Vanilla's BreakingParticle, but allows
// a texture to be specified directly rather than via an itemstack
@Environment(EnvType.CLIENT)
public class CableBusBreakingParticle extends SpriteBillboardParticle {
private final float field_217571_C;
private final float field_217572_F;
public CableBusBreakingParticle(ClientWorld world, double x, double y, double z, double speedX, double speedY,
double speedZ, Sprite sprite) {
super(world, x, y, z, speedX, speedY, speedZ);
this.setSprite(sprite);
this.gravityStrength = 1.0F;
this.scale /= 2.0F;
this.field_217571_C = this.random.nextFloat() * 3.0F;
this.field_217572_F = this.random.nextFloat() * 3.0F;
}
public CableBusBreakingParticle(ClientWorld world, double x, double y, double z, Sprite sprite) {
this(world, x, y, z, 0, 0, 0, sprite);
}
@Override
public ParticleTextureSheet getType() {
return ParticleTextureSheet.TERRAIN_SHEET;
}
@Override
protected float getMinU() {
return this.sprite.getFrameU((this.field_217571_C + 1.0F) / 4.0F * 16.0F);
}
@Override
protected float getMaxU() {
return this.sprite.getFrameU(this.field_217571_C / 4.0F * 16.0F);
}
@Override
protected float getMinV() {
return this.sprite.getFrameV(this.field_217572_F / 4.0F * 16.0F);
}
@Override
protected float getMaxV() {
return this.sprite.getFrameV((this.field_217572_F + 1.0F) / 4.0F * 16.0F);
}
}
@@ -0,0 +1,97 @@
/*
* 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.cablebus;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import com.google.common.collect.ImmutableMap;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.Identifier;
import appeng.api.util.AEColor;
import appeng.core.AELog;
import appeng.core.features.registries.PartModels;
import javax.annotation.Nullable;
/**
* The built-in model for the cable bus block.
*/
public class CableBusModel implements UnbakedModel {
private final PartModels partModels;
public CableBusModel(PartModels partModels) {
this.partModels = partModels;
}
@Override
public Collection<Identifier> getModelDependencies() {
return Collections.emptyList();
}
@Nullable
@Override
public BakedModel bake(ModelLoader loader, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) {
Map<Identifier, BakedModel> partModels = this.loadPartModels(loader, rotationContainer);
CableBuilder cableBuilder = new CableBuilder(textureGetter);
FacadeBuilder facadeBuilder = new FacadeBuilder();
// This should normally not be used, but we *have* to provide a particle texture
// or otherwise damage models will
// crash
Sprite particleTexture = cableBuilder.getCoreTexture(CableCoreType.GLASS, AEColor.TRANSPARENT);
return new CableBusBakedModel(cableBuilder, facadeBuilder, partModels, particleTexture);
}
@Override
public Collection<SpriteIdentifier> getTextureDependencies(Function<Identifier, UnbakedModel> unbakedModelGetter, Set<Pair<String, String>> unresolvedTextureReferences) {
return Collections.unmodifiableList(CableBuilder.getTextures());
}
private Map<Identifier, BakedModel> loadPartModels(ModelLoader loader,
ModelBakeSettings rotationContainer) {
ImmutableMap.Builder<Identifier, BakedModel> result = ImmutableMap.builder();
for (Identifier location : this.partModels.getModels()) {
BakedModel bakedModel = loader.bake(location, rotationContainer);
if (bakedModel == null) {
AELog.warn("Failed to bake part model {}", location);
} else {
result.put(location, bakedModel);
}
}
return result.build();
}
}
@@ -0,0 +1,31 @@
package appeng.client.render.cablebus;
import appeng.core.AppEng;
import appeng.core.features.registries.PartModels;
import net.fabricmc.fabric.api.client.model.ModelProviderContext;
import net.fabricmc.fabric.api.client.model.ModelProviderException;
import net.fabricmc.fabric.api.client.model.ModelResourceProvider;
import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.util.Identifier;
public class CableBusModelLoader implements ModelResourceProvider {
private static final Identifier CABLE_BUS_MODEL = AppEng.makeId("block/cable_bus");
private final PartModels partModels;
public CableBusModelLoader(PartModels partModels) {
this.partModels = partModels;
}
@Override
public UnbakedModel loadModelResource(Identifier resourceId, ModelProviderContext context) throws ModelProviderException {
if (CABLE_BUS_MODEL.equals(resourceId)) {
CableBusBakedModel.clearCache();
return new CableBusModel(partModels);
} else {
return null;
}
}
}
@@ -0,0 +1,212 @@
/*
* 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.cablebus;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
import appeng.api.parts.IPartModel;
import appeng.api.util.AECableType;
import appeng.api.util.AEColor;
/**
* This class captures the entire rendering state needed for a cable bus and
* transports it to the rendering thread for processing.
*/
public class CableBusRenderState {
// The cable type used for rendering the outgoing connections to other blocks
// and attached parts
private AECableType cableType = AECableType.NONE;
// The type to use for rendering the core of the cable.
private CableCoreType coreType;
private AEColor cableColor = AEColor.TRANSPARENT;
// Describes the outgoing connections of this cable bus to other blocks, and how
// they should be rendered
private EnumMap<Direction, AECableType> connectionTypes = new EnumMap<>(Direction.class);
// Indicate on which sides signified by connectionTypes above, there is another
// cable bus. If a side is connected,
// but it is absent from this
// set, then it means that there is a Grid host, but not a cable bus on that
// side (i.e. an interface, a controller,
// etc.)
private EnumSet<Direction> cableBusAdjacent = EnumSet.noneOf(Direction.class);
// Specifies the number of channels used for the connection to a given side.
// Only contains entries if
// connections contains a corresponding entry.
private EnumMap<Direction, Integer> channelsOnSide = new EnumMap<>(Direction.class);
private EnumMap<Direction, IPartModel> attachments = new EnumMap<>(Direction.class);
// For each attachment, this contains the distance from the edge until which a
// cable connection should be drawn
private EnumMap<Direction, Integer> attachmentConnections = new EnumMap<>(Direction.class);
// Contains the facade to use for each side that has a facade attached
private EnumMap<Direction, FacadeRenderState> facades = new EnumMap<>(Direction.class);
// Used for Facades.
private WeakReference<BlockRenderView> world;
private BlockPos pos;
// Contains the bounding boxes of all parts on the cable bus to allow facades to
// cut out holes for the parts. This
// list is only populated if there are
// facades on this cable bus
private List<Box> boundingBoxes = new ArrayList<>();
// Additional model data passed to the part models
private EnumMap<Direction, Object> partModelData = new EnumMap<>(Direction.class);
public CableCoreType getCoreType() {
return this.coreType;
}
public void setCoreType(CableCoreType coreType) {
this.coreType = coreType;
}
public AECableType getCableType() {
return this.cableType;
}
public void setCableType(AECableType cableType) {
this.cableType = cableType;
}
public AEColor getCableColor() {
return this.cableColor;
}
public void setCableColor(AEColor cableColor) {
this.cableColor = cableColor;
}
public EnumMap<Direction, Integer> getChannelsOnSide() {
return this.channelsOnSide;
}
public EnumMap<Direction, AECableType> getConnectionTypes() {
return this.connectionTypes;
}
public void setConnectionTypes(EnumMap<Direction, AECableType> connectionTypes) {
this.connectionTypes = connectionTypes;
}
public void setChannelsOnSide(EnumMap<Direction, Integer> channelsOnSide) {
this.channelsOnSide = channelsOnSide;
}
public EnumSet<Direction> getCableBusAdjacent() {
return this.cableBusAdjacent;
}
public void setCableBusAdjacent(EnumSet<Direction> cableBusAdjacent) {
this.cableBusAdjacent = cableBusAdjacent;
}
public EnumMap<Direction, IPartModel> getAttachments() {
return this.attachments;
}
public EnumMap<Direction, Integer> getAttachmentConnections() {
return this.attachmentConnections;
}
public EnumMap<Direction, FacadeRenderState> getFacades() {
return this.facades;
}
public BlockRenderView getWorld() {
return this.world.get();
}
public void setWorld(BlockRenderView world) {
this.world = new WeakReference<>(world);
}
public BlockPos getPos() {
return this.pos;
}
public void setPos(BlockPos pos) {
this.pos = pos;
}
public List<Box> getBoundingBoxes() {
return this.boundingBoxes;
}
public EnumMap<Direction, Object> getPartModelData() {
return this.partModelData;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.attachmentConnections == null) ? 0 : this.attachmentConnections.hashCode());
result = prime * result + ((this.cableBusAdjacent == null) ? 0 : this.cableBusAdjacent.hashCode());
result = prime * result + ((this.cableColor == null) ? 0 : this.cableColor.hashCode());
result = prime * result + ((this.cableType == null) ? 0 : this.cableType.hashCode());
result = prime * result + ((this.channelsOnSide == null) ? 0 : this.channelsOnSide.hashCode());
result = prime * result + ((this.connectionTypes == null) ? 0 : this.connectionTypes.hashCode());
result = prime * result + ((this.coreType == null) ? 0 : this.coreType.hashCode());
result = prime * result + ((this.partModelData == null) ? 0 : this.partModelData.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final CableBusRenderState other = (CableBusRenderState) obj;
return this.cableColor == other.cableColor && this.cableType == other.cableType
&& this.coreType == other.coreType
&& Objects.equals(this.attachmentConnections, other.attachmentConnections)
&& Objects.equals(this.cableBusAdjacent, other.cableBusAdjacent)
&& Objects.equals(this.channelsOnSide, other.channelsOnSide)
&& Objects.equals(this.connectionTypes, other.connectionTypes)
&& Objects.equals(this.partModelData, other.partModelData);
}
}
@@ -0,0 +1,80 @@
/*
* 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.cablebus;
import java.util.EnumMap;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.util.Identifier;
import appeng.api.util.AECableType;
import appeng.api.util.AEColor;
import appeng.core.AppEng;
/**
* AE can render the core of a cable (the core that connections are made to, in
* case the cable is not a straight line) in three different ways: - Glass -
* Covered (also used by the Smart Cable) - Dense
*/
public enum CableCoreType {
GLASS("parts/cable/core/glass"), COVERED("parts/cable/core/covered"), DENSE("parts/cable/core/dense_smart");
private static final Map<AECableType, CableCoreType> cableMapping = generateCableMapping();
/**
* Creates the mapping that assigns a cable core type to an AE cable type.
*/
private static Map<AECableType, CableCoreType> generateCableMapping() {
Map<AECableType, CableCoreType> result = new EnumMap<>(AECableType.class);
result.put(AECableType.GLASS, CableCoreType.GLASS);
result.put(AECableType.COVERED, CableCoreType.COVERED);
result.put(AECableType.SMART, CableCoreType.COVERED);
result.put(AECableType.DENSE_COVERED, CableCoreType.DENSE);
result.put(AECableType.DENSE_SMART, CableCoreType.DENSE);
return ImmutableMap.copyOf(result);
}
private final String textureFolder;
CableCoreType(String textureFolder) {
this.textureFolder = textureFolder;
}
/**
* @return The type of core that should be rendered when the given cable isn't
* straight and needs to have a core to attach connections to. Is null
* for the NULL cable.
*/
public static CableCoreType fromCableType(AECableType cableType) {
return cableMapping.get(cableType);
}
public SpriteIdentifier getTexture(AEColor color) {
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, this.textureFolder + "/" + color.name().toLowerCase()));
}
}
@@ -0,0 +1,464 @@
/*
* 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.cablebus;
import java.util.*;
import java.util.stream.Collectors;
import com.google.common.base.Preconditions;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
import net.minecraft.client.render.*;
import net.minecraft.client.util.math.Vector4f;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;
/**
* Builds the quads for a cube.
*/
@Environment(EnvType.CLIENT)
public class CubeBuilder {
private final List<BakedQuad> output;
private final EnumMap<Direction, Sprite> textures = new EnumMap<>(Direction.class);
private EnumSet<Direction> drawFaces = EnumSet.allOf(Direction.class);
private final EnumMap<Direction, Vector4f> customUv = new EnumMap<>(Direction.class);
private final byte[] uvRotations = new byte[Direction.values().length];
private int color = 0xFFFFFFFF;
private boolean useStandardUV = false;
private boolean renderFullBright;
private final MeshBuilder meshBuilder;
private final QuadEmitter emitter;
private int vertexIndex = 0;
public CubeBuilder(List<BakedQuad> output) {
this.output = output;
meshBuilder = IndigoRenderer.INSTANCE.meshBuilder();
emitter = meshBuilder.getEmitter();
emitter.emit();
meshBuilder.build();
ModelHelper.toQuadLists(meshBuilder.build());
}
public CubeBuilder() {
this(new ArrayList<>(6));
}
public void addCube(float x1, float y1, float z1, float x2, float y2, float z2) {
x1 /= 16.0f;
y1 /= 16.0f;
z1 /= 16.0f;
x2 /= 16.0f;
y2 /= 16.0f;
z2 /= 16.0f;
for (Direction face : this.drawFaces) {
this.putFace(face, x1, y1, z1, x2, y2, z2);
}
}
public void addQuad(Direction face, float x1, float y1, float z1, float x2, float y2, float z2) {
this.putFace(face, x1, y1, z1, x2, y2, z2);
}
private static final class UvVector {
float u1;
float u2;
float v1;
float v2;
}
private void putFace(Direction face, float x1, float y1, float z1, float x2, float y2, float z2) {
Sprite texture = this.textures.get(face);
QuadEmitter emitter = this.emitter;
emitter.colorIndex(-1)
.nominalFace(face);
UvVector uv = new UvVector();
// The user might have set specific UV coordinates for this face
Vector4f customUv = this.customUv.get(face);
if (customUv != null) {
uv.u1 = texture.getFrameU(customUv.getX());
uv.v1 = texture.getFrameV(customUv.getY());
uv.u2 = texture.getFrameU(customUv.getZ());
uv.v2 = texture.getFrameV(customUv.getW());
} else if (this.useStandardUV) {
uv = this.getStandardUv(face, texture, x1, y1, z1, x2, y2, z2);
} else {
uv = this.getDefaultUv(face, texture, x1, y1, z1, x2, y2, z2);
}
switch (face) {
case DOWN:
this.putVertexTR(face, x2, y1, z1, uv);
this.putVertexBR(face, x2, y1, z2, uv);
this.putVertexBL(face, x1, y1, z2, uv);
this.putVertexTL(face, x1, y1, z1, uv);
break;
case UP:
this.putVertexTL(face, x1, y2, z1, uv);
this.putVertexBL(face, x1, y2, z2, uv);
this.putVertexBR(face, x2, y2, z2, uv);
this.putVertexTR(face, x2, y2, z1, uv);
break;
case NORTH:
this.putVertexBR(face, x2, y2, z1, uv);
this.putVertexTR(face, x2, y1, z1, uv);
this.putVertexTL(face, x1, y1, z1, uv);
this.putVertexBL(face, x1, y2, z1, uv);
break;
case SOUTH:
this.putVertexBL(face, x1, y2, z2, uv);
this.putVertexTL(face, x1, y1, z2, uv);
this.putVertexTR(face, x2, y1, z2, uv);
this.putVertexBR(face, x2, y2, z2, uv);
break;
case WEST:
this.putVertexTL(face, x1, y1, z1, uv);
this.putVertexTR(face, x1, y1, z2, uv);
this.putVertexBR(face, x1, y2, z2, uv);
this.putVertexBL(face, x1, y2, z1, uv);
break;
case EAST:
this.putVertexBR(face, x2, y2, z1, uv);
this.putVertexBL(face, x2, y2, z2, uv);
this.putVertexTL(face, x2, y1, z2, uv);
this.putVertexTR(face, x2, y1, z1, uv);
break;
}
if (renderFullBright) {
// Force Brightness to 15, this is for full bright mode
// this vertex element will only be present in that case
int lightmap = LightmapTextureManager.pack(15, 15);
emitter.lightmap(lightmap, lightmap, lightmap, lightmap);
}
// FIXME: this is unnecessarily inefficient
emitter.emit();
List<BakedQuad>[] quads = ModelHelper.toQuadLists(meshBuilder.build());
this.output.addAll(Arrays.stream(quads).flatMap(Collection::stream).collect(Collectors.toList()));
}
private UvVector getDefaultUv(Direction face, Sprite texture, float x1, float y1, float z1, float x2,
float y2, float z2) {
UvVector uv = new UvVector();
switch (face) {
case DOWN:
uv.u1 = texture.getFrameU(x1 * 16);
uv.v1 = texture.getFrameV(z1 * 16);
uv.u2 = texture.getFrameU(x2 * 16);
uv.v2 = texture.getFrameV(z2 * 16);
break;
case UP:
uv.u1 = texture.getFrameU(x1 * 16);
uv.v1 = texture.getFrameV(z1 * 16);
uv.u2 = texture.getFrameU(x2 * 16);
uv.v2 = texture.getFrameV(z2 * 16);
break;
case NORTH:
uv.u1 = texture.getFrameU(x1 * 16);
uv.v1 = texture.getFrameV(16 - y1 * 16);
uv.u2 = texture.getFrameU(x2 * 16);
uv.v2 = texture.getFrameV(16 - y2 * 16);
break;
case SOUTH:
uv.u1 = texture.getFrameU(x1 * 16);
uv.v1 = texture.getFrameV(16 - y1 * 16);
uv.u2 = texture.getFrameU(x2 * 16);
uv.v2 = texture.getFrameV(16 - y2 * 16);
break;
case WEST:
uv.u1 = texture.getFrameU(z1 * 16);
uv.v1 = texture.getFrameV(16 - y1 * 16);
uv.u2 = texture.getFrameU(z2 * 16);
uv.v2 = texture.getFrameV(16 - y2 * 16);
break;
case EAST:
uv.u1 = texture.getFrameU(z2 * 16);
uv.v1 = texture.getFrameV(16 - y1 * 16);
uv.u2 = texture.getFrameU(z1 * 16);
uv.v2 = texture.getFrameV(16 - y2 * 16);
break;
}
return uv;
}
private UvVector getStandardUv(Direction face, Sprite texture, float x1, float y1, float z1, float x2,
float y2, float z2) {
UvVector uv = new UvVector();
switch (face) {
case DOWN:
uv.u1 = texture.getFrameU(x1 * 16);
uv.v1 = texture.getFrameV(16 - z1 * 16);
uv.u2 = texture.getFrameU(x2 * 16);
uv.v2 = texture.getFrameV(16 - z2 * 16);
break;
case UP:
uv.u1 = texture.getFrameU(x1 * 16);
uv.v1 = texture.getFrameV(z1 * 16);
uv.u2 = texture.getFrameU(x2 * 16);
uv.v2 = texture.getFrameV(z2 * 16);
break;
case NORTH:
uv.u1 = texture.getFrameU(16 - x1 * 16);
uv.v1 = texture.getFrameV(16 - y1 * 16);
uv.u2 = texture.getFrameU(16 - x2 * 16);
uv.v2 = texture.getFrameV(16 - y2 * 16);
break;
case SOUTH:
uv.u1 = texture.getFrameU(x1 * 16);
uv.v1 = texture.getFrameV(16 - y1 * 16);
uv.u2 = texture.getFrameU(x2 * 16);
uv.v2 = texture.getFrameV(16 - y2 * 16);
break;
case WEST:
uv.u1 = texture.getFrameU(z1 * 16);
uv.v1 = texture.getFrameV(16 - y1 * 16);
uv.u2 = texture.getFrameU(z2 * 16);
uv.v2 = texture.getFrameV(16 - y2 * 16);
break;
case EAST:
uv.u1 = texture.getFrameU(16 - z2 * 16);
uv.v1 = texture.getFrameV(16 - y1 * 16);
uv.u2 = texture.getFrameU(16 - z1 * 16);
uv.v2 = texture.getFrameV(16 - y2 * 16);
break;
}
return uv;
}
// uv.u1, uv.v1
private void putVertexTL(Direction face, float x, float y, float z, UvVector uv) {
float u, v;
switch (this.uvRotations[face.ordinal()]) {
default:
case 0:
u = uv.u1;
v = uv.v1;
break;
case 1: // 90° clockwise
u = uv.u1;
v = uv.v2;
break;
case 2: // 180° clockwise
u = uv.u2;
v = uv.v2;
break;
case 3: // 270° clockwise
u = uv.u2;
v = uv.v1;
break;
}
this.putVertex(face, x, y, z, u, v);
}
// uv.u2, uv.v1
private void putVertexTR(Direction face, float x, float y, float z, UvVector uv) {
float u, v;
switch (this.uvRotations[face.ordinal()]) {
default:
case 0:
u = uv.u2;
v = uv.v1;
break;
case 1: // 90° clockwise
u = uv.u1;
v = uv.v1;
break;
case 2: // 180° clockwise
u = uv.u1;
v = uv.v2;
break;
case 3: // 270° clockwise
u = uv.u2;
v = uv.v2;
break;
}
this.putVertex(face, x, y, z, u, v);
}
// uv.u2, uv.v2
private void putVertexBR(Direction face, float x, float y, float z, UvVector uv) {
float u;
float v;
switch (this.uvRotations[face.ordinal()]) {
default:
case 0:
u = uv.u2;
v = uv.v2;
break;
case 1: // 90° clockwise
u = uv.u2;
v = uv.v1;
break;
case 2: // 180° clockwise
u = uv.u1;
v = uv.v1;
break;
case 3: // 270° clockwise
u = uv.u1;
v = uv.v2;
break;
}
this.putVertex(face, x, y, z, u, v);
}
// uv.u1, uv.v2
private void putVertexBL(Direction face, float x, float y, float z, UvVector uv) {
float u;
float v;
switch (this.uvRotations[face.ordinal()]) {
default:
case 0:
u = uv.u1;
v = uv.v2;
break;
case 1: // 90° clockwise
u = uv.u2;
v = uv.v2;
break;
case 2: // 180° clockwise
u = uv.u2;
v = uv.v1;
break;
case 3: // 270° clockwise
u = uv.u1;
v = uv.v1;
break;
}
this.putVertex(face, x, y, z, u, v);
}
private void putVertex(Direction face, float x, float y, float z, float u, float v) {
emitter.pos(vertexIndex, x, y, z);
emitter.pos(vertexIndex, face.getOffsetX(), face.getOffsetY(), face.getOffsetZ());
// Color format is RGBA
emitter.spriteColor(vertexIndex, this.color);
emitter.sprite(vertexIndex, 0, u, v);
vertexIndex++;
}
public void setTexture(Sprite texture) {
for (Direction face : Direction.values()) {
this.textures.put(face, texture);
}
}
public void setTextures(Sprite up, Sprite down, Sprite north,
Sprite south, Sprite east, Sprite west) {
this.textures.put(Direction.UP, up);
this.textures.put(Direction.DOWN, down);
this.textures.put(Direction.NORTH, north);
this.textures.put(Direction.SOUTH, south);
this.textures.put(Direction.EAST, east);
this.textures.put(Direction.WEST, west);
}
public void setTexture(Direction facing, Sprite sprite) {
this.textures.put(facing, sprite);
}
public void setDrawFaces(EnumSet<Direction> drawFaces) {
this.drawFaces = drawFaces;
}
public void setColor(int color) {
this.color = color;
}
/**
* Sets the vertex color for future vertices to the given RGB value, and forces
* the alpha component to 255.
*/
public void setColorRGB(int color) {
this.setColor(color | 0xFF000000);
}
public void setColorRGB(float r, float g, float b) {
this.setColorRGB((int) (r * 255) << 16 | (int) (g * 255) << 8 | (int) (b * 255));
}
public void setRenderFullBright(boolean renderFullBright) {
this.renderFullBright = renderFullBright;
}
public void setCustomUv(Direction facing, float u1, float v1, float u2, float v2) {
this.customUv.put(facing, new Vector4f(u1, v1, u2, v2));
}
public void setUvRotation(Direction facing, int rotation) {
if (rotation == 2) {
rotation = 3;
} else if (rotation == 3) {
rotation = 2;
}
Preconditions.checkArgument(rotation >= 0 && rotation <= 3, "rotation");
this.uvRotations[facing.ordinal()] = (byte) rotation;
}
/**
* CubeBuilder uses UV optimized for cables by default. This switches to
* standard UV coordinates.
*/
public void useStandardUV() {
this.useStandardUV = true;
}
public List<BakedQuad> getOutput() {
return this.output;
}
}
@@ -1,80 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.cablebus;
import javax.annotation.Nullable;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.fluid.IFluidState;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.lighting.WorldLightManager;
/**
* This is used to retrieve the ExtendedState of a block for facade rendering.
* It fakes the block at BlockPos provided as the BlockState provided.
*
* @author covers1624
*/
public class FacadeBlockAccess implements BlockRenderView {
private final BlockRenderView world;
private final BlockPos pos;
private final Direction side;
private final BlockState state;
public FacadeBlockAccess(BlockRenderView world, BlockPos pos, Direction side, BlockState state) {
this.world = world;
this.pos = pos;
this.side = side;
this.state = state;
}
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos) {
return this.world.getBlockEntity(pos);
}
@Override
public BlockState getBlockState(BlockPos pos) {
if (this.pos == pos) {
return this.state;
}
return this.world.getBlockState(pos);
}
@Override
public IFluidState getFluidState(BlockPos pos) {
return world.getFluidState(pos);
}
@Override
public WorldLightManager getLightingProvider() {
return world.getLightingProvider();
}
@Override
public int getBlockColor(BlockPos blockPosIn, ColorResolver colorResolverIn) {
return world.getBlockColor(blockPosIn, colorResolverIn);
}
}
@@ -0,0 +1,428 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.cablebus;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderLayers;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.block.BlockRenderManager;
import net.minecraft.client.color.block.BlockColors;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Direction.Axis;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
import appeng.api.AEApi;
import appeng.api.util.AEAxisAlignedBB;
import appeng.parts.misc.CableAnchorPart;
/**
* The FacadeBuilder builds for facades..
*
* @author covers1624
*/
public class FacadeBuilder {
public static final double THICK_THICKNESS = 2D / 16D;
public static final double THIN_THICKNESS = 1D / 16D;
public static final Box[] THICK_FACADE_BOXES = new Box[] {
new Box(0.0, 0.0, 0.0, 1.0, THICK_THICKNESS, 1.0),
new Box(0.0, 1.0 - THICK_THICKNESS, 0.0, 1.0, 1.0, 1.0),
new Box(0.0, 0.0, 0.0, 1.0, 1.0, THICK_THICKNESS),
new Box(0.0, 0.0, 1.0 - THICK_THICKNESS, 1.0, 1.0, 1.0),
new Box(0.0, 0.0, 0.0, THICK_THICKNESS, 1.0, 1.0),
new Box(1.0 - THICK_THICKNESS, 0.0, 0.0, 1.0, 1.0, 1.0) };
public static final Box[] THIN_FACADE_BOXES = new Box[] {
new Box(0.0, 0.0, 0.0, 1.0, THIN_THICKNESS, 1.0),
new Box(0.0, 1.0 - THIN_THICKNESS, 0.0, 1.0, 1.0, 1.0),
new Box(0.0, 0.0, 0.0, 1.0, 1.0, THIN_THICKNESS),
new Box(0.0, 0.0, 1.0 - THIN_THICKNESS, 1.0, 1.0, 1.0),
new Box(0.0, 0.0, 0.0, THIN_THICKNESS, 1.0, 1.0),
new Box(1.0 - THIN_THICKNESS, 0.0, 0.0, 1.0, 1.0, 1.0) };
//FIXME private final ThreadLocal<BakedPipeline> pipelines = ThreadLocal.withInitial(() -> BakedPipeline.builder()
//FIXME // Clamper is responsible for clamping the vertex to the bounds specified.
//FIXME .addElement("clamper", QuadClamper.FACTORY)
//FIXME // Strips faces if they match a mask.
//FIXME .addElement("face_stripper", QuadFaceStripper.FACTORY)
//FIXME // Kicks the edge inner corners in, solves Z fighting
//FIXME .addElement("corner_kicker", QuadCornerKicker.FACTORY)
//FIXME // Re-Interpolates the UV's for the quad.
//FIXME .addElement("interp", QuadReInterpolator.FACTORY)
//FIXME // Tints the quad if we need it to. Disabled by default.
//FIXME .addElement("tinter", QuadTinter.FACTORY, false)
//FIXME // Overrides the quad's alpha if we are forcing transparent facades.
//FIXME .addElement("transparent", QuadAlphaOverride.FACTORY, false, e -> e.setAlphaOverride(0x4C / 255F)).build()//
//FIXME );
//FIXME private final ThreadLocal<Quad> collectors = ThreadLocal.withInitial(Quad::new);
public void buildFacadeQuads(RenderLayer layer, CableBusRenderState renderState, Supplier<Random> rand,
RenderContext context, Function<Identifier, BakedModel> modelLookup) {
//FIXME BakedPipeline pipeline = this.pipelines.get();
//FIXME Quad collectorQuad = this.collectors.get();
boolean transparent = AEApi.instance().partHelper().getCableRenderMode().transparentFacades;
Map<Direction, FacadeRenderState> facadeStates = renderState.getFacades();
List<Box> partBoxes = renderState.getBoundingBoxes();
Set<Direction> sidesWithParts = renderState.getAttachments().keySet();
BlockRenderView parentWorld = renderState.getWorld();
BlockPos pos = renderState.getPos();
BlockColors blockColors = MinecraftClient.getInstance().getBlockColors();
boolean thinFacades = isUseThinFacades(partBoxes);
for (Entry<Direction, FacadeRenderState> entry : facadeStates.entrySet()) {
Direction side = entry.getKey();
int sideIndex = side.ordinal();
FacadeRenderState facadeRenderState = entry.getValue();
boolean renderStilt = !sidesWithParts.contains(side);
if (layer == RenderLayer.getCutout() && renderStilt) {
context.pushTransform(QuadRotator.get(side, Direction.UP));
for (Identifier part : CableAnchorPart.FACADE_MODELS.getModels()) {
BakedModel partModel = modelLookup.apply(part);
context.fallbackConsumer().accept(partModel);
}
context.popTransform();
}
// If we are forcing transparency and this isn't the Translucent layer.
if (transparent && layer != RenderLayer.getTranslucent()) {
continue;
}
BlockState blockState = facadeRenderState.getSourceBlock();
// If we aren't forcing transparency let the block decide if it should render.
if (!transparent && layer != null) {
// FIXME FABRIC only one layer per block
// FIXME FABRIC if (!RenderLayers.canRenderInLayer(blockState, layer)) {
// FIXME FABRIC continue;
// FIXME FABRIC }
}
Box fullBounds = thinFacades ? THIN_FACADE_BOXES[sideIndex] : THICK_FACADE_BOXES[sideIndex];
Box facadeBox = fullBounds;
// If we are a transparent facade, we need to modify out BB.
if (facadeRenderState.isTransparent()) {
double offset = thinFacades ? THIN_THICKNESS : THICK_THICKNESS;
AEAxisAlignedBB tmpBB = null;
for (Direction face : Direction.values()) {
// Only faces that aren't on our axis
if (face.getAxis() != side.getAxis()) {
FacadeRenderState otherState = facadeStates.get(face);
if (otherState != null && !otherState.isTransparent()) {
if (tmpBB == null) {
tmpBB = AEAxisAlignedBB.fromBounds(facadeBox);
}
switch (face) {
case DOWN:
tmpBB.minY += offset;
break;
case UP:
tmpBB.maxY -= offset;
break;
case NORTH:
tmpBB.minZ += offset;
break;
case SOUTH:
tmpBB.maxZ -= offset;
break;
case WEST:
tmpBB.minX += offset;
break;
case EAST:
tmpBB.maxX -= offset;
break;
default:
throw new RuntimeException("Switch falloff. " + String.valueOf(face));
}
}
}
}
if (tmpBB != null) {
facadeBox = tmpBB.getBoundingBox();
}
}
AEAxisAlignedBB cutOutBox = getCutOutBox(facadeBox, partBoxes);
List<Box> holeStrips = getBoxes(facadeBox, cutOutBox, side.getAxis());
// FIXME BlockRenderView facadeAccess = new FacadeBlockAccess(parentWorld, pos, side, blockState);
// FIXME FABRIC BlockRenderManager dispatcher = MinecraftClient.getInstance().getBlockRenderManager();
// FIXME FABRIC BakedModel model = dispatcher.getModel(blockState);
// FIXME FABRIC IModelData modelData = model.getModelData(facadeAccess, pos, blockState, EmptyModelData.INSTANCE);
// FIXME FABRIC
// FIXME FABRIC List<BakedQuad> modelQuads = new ArrayList<>();
// FIXME FABRIC // If we are forcing transparent facades, fake the render layer, and grab all
// FIXME FABRIC // quads.
// FIXME FABRIC if (transparent || layer == null) {
// FIXME FABRIC for (RenderLayer forcedLayer : RenderLayer.getBlockRenderTypes()) {
// FIXME FABRIC // Check if the block renders on the layer we want to force.
// FIXME FABRIC if (RenderLayers.canRenderInLayer(blockState, forcedLayer)) {
// FIXME FABRIC // Force the layer and gather quads.
// FIXME FABRIC ForgeHooksClient.setRenderLayer(forcedLayer);
// FIXME FABRIC modelQuads.addAll(gatherQuads(model, blockState, rand, modelData));
// FIXME FABRIC }
// FIXME FABRIC }
// FIXME FABRIC
// FIXME FABRIC // Reset.
// FIXME FABRIC ForgeHooksClient.setRenderLayer(layer);
// FIXME FABRIC } else {
// FIXME FABRIC modelQuads.addAll(gatherQuads(model, blockState, rand, modelData));
// FIXME FABRIC }
// FIXME FABRIC
// FIXME FABRIC // No quads.. Cool, next!
// FIXME FABRIC if (modelQuads.isEmpty()) {
// FIXME FABRIC continue;
// FIXME FABRIC }
// FIXME FABRIC
// FIXME FABRIC // Grab out pipeline elements.
// FIXME FABRIC QuadClamper clamper = pipeline.getElement("clamper", QuadClamper.class);
// FIXME FABRIC QuadFaceStripper edgeStripper = pipeline.getElement("face_stripper", QuadFaceStripper.class);
// FIXME FABRIC QuadTinter tinter = pipeline.getElement("tinter", QuadTinter.class);
// FIXME FABRIC QuadCornerKicker kicker = pipeline.getElement("corner_kicker", QuadCornerKicker.class);
// FIXME FABRIC
// FIXME FABRIC // Set global element states.
// FIXME FABRIC
// FIXME FABRIC // calculate the side mask.
// FIXME FABRIC int facadeMask = 0;
// FIXME FABRIC for (Entry<Direction, FacadeRenderState> ent : facadeStates.entrySet()) {
// FIXME FABRIC Direction s = ent.getKey();
// FIXME FABRIC if (s.getAxis() != side.getAxis()) {
// FIXME FABRIC FacadeRenderState otherState = ent.getValue();
// FIXME FABRIC if (!otherState.isTransparent()) {
// FIXME FABRIC facadeMask |= 1 << s.ordinal();
// FIXME FABRIC }
// FIXME FABRIC }
// FIXME FABRIC }
// FIXME FABRIC // Setup the edge stripper.
// FIXME FABRIC edgeStripper.setBounds(fullBounds);
// FIXME FABRIC edgeStripper.setMask(facadeMask);
// FIXME FABRIC
// FIXME FABRIC // Setup the kicker.
// FIXME FABRIC kicker.setSide(sideIndex);
// FIXME FABRIC kicker.setFacadeMask(facadeMask);
// FIXME FABRIC kicker.setBox(fullBounds);
// FIXME FABRIC kicker.setThickness(thinFacades ? THIN_THICKNESS : THICK_THICKNESS);
// FIXME FABRIC
// FIXME FABRIC for (BakedQuad quad : modelQuads) {
// FIXME FABRIC // lookup the format in CachedFormat.
// FIXME FABRIC CachedFormat format = CachedFormat.lookup(VertexFormats.POSITION_COLOR_TEXTURE_LIGHT_NORMAL);
// FIXME FABRIC // If this quad has a tint index, setup the tinter.
// FIXME FABRIC if (quad.hasTintIndex()) {
// FIXME FABRIC tinter.setTint(blockColors.getColor(blockState, facadeAccess, pos, quad.getColorIndex()));
// FIXME FABRIC }
// FIXME FABRIC for (Box box : holeStrips) {
// FIXME FABRIC // setup the clamper for this box
// FIXME FABRIC clamper.setClampBounds(box);
// FIXME FABRIC // Reset the pipeline, clears all enabled/disabled states.
// FIXME FABRIC pipeline.reset(format);
// FIXME FABRIC // Reset out collector.
// FIXME FABRIC collectorQuad.reset(format);
// FIXME FABRIC // Enable / disable the optional elements
// FIXME FABRIC pipeline.setElementState("tinter", quad.hasTintIndex());
// FIXME FABRIC pipeline.setElementState("transparent", transparent);
// FIXME FABRIC // Prepare the pipeline for a quad.
// FIXME FABRIC pipeline.prepare(collectorQuad);
// FIXME FABRIC
// FIXME FABRIC // Pipe our quad into the pipeline.
// FIXME FABRIC quad.pipe(pipeline);
// FIXME FABRIC // Check if the collector got any data.
// FIXME FABRIC if (collectorQuad.full) {
// FIXME FABRIC // Add the result.
// FIXME FABRIC quads.add(collectorQuad.bake());
// FIXME FABRIC }
// FIXME FABRIC }
// FIXME FABRIC }
}
}
/**
* This is slow, so should be cached.
*
* @return The model.
*/
public List<BakedQuad> buildFacadeItemQuads(ItemStack textureItem, Direction side) {
List<BakedQuad> facadeQuads = new ArrayList<>();
BakedModel model = MinecraftClient.getInstance().getItemRenderer().getHeldItemModel(textureItem, null,
null);
List<BakedQuad> modelQuads = gatherQuads(model, null, new Random());
//FIXME BakedPipeline pipeline = this.pipelines.get();
//FIXME Quad collectorQuad = this.collectors.get();
// Grab pipeline elements.
// FIXME QuadClamper clamper = pipeline.getElement("clamper", QuadClamper.class);
// FIXME QuadTinter tinter = pipeline.getElement("tinter", QuadTinter.class);
for (BakedQuad quad : modelQuads) {
// Lookup the CachedFormat for this quads format.
// FIXME CachedFormat format = CachedFormat.lookup(VertexFormats.POSITION_COLOR_TEXTURE_LIGHT_NORMAL);
// Reset the pipeline.
// FIXME pipeline.reset(format);
// Reset the collector.
//FIXME collectorQuad.reset(format);
// If we have a tint index, setup the tinter and enable it.
// FIXME if (quad.hasTintIndex()) {
// FIXME tinter.setTint(MinecraftClient.getInstance().getItemColors().getColor(textureItem, quad.getColorIndex()));
// FIXME pipeline.enableElement("tinter");
// FIXME }
// Disable elements we don't need for items.
// FIXME pipeline.disableElement("face_stripper");
// FIXME pipeline.disableElement("corner_kicker");
// FIXME // Setup the clamper
// FIXME clamper.setClampBounds(THICK_FACADE_BOXES[side.ordinal()]);
// FIXME // Prepare the pipeline.
// FIXME pipeline.prepare(collectorQuad);
// FIXME // Pipe our quad into the pipeline.
// FIXME quad.pipe(pipeline);
// FIXME // Check the collector for data and add the quad if there was.
// FIXME if (collectorQuad.full) {
// FIXME facadeQuads.add(collectorQuad.bake());
// FIXME }
}
return facadeQuads;
}
// Helper to gather all quads from a model into a list.
private static List<BakedQuad> gatherQuads(BakedModel model, BlockState state, Random rand) {
List<BakedQuad> modelQuads = new ArrayList<>();
for (Direction face : Direction.values()) {
modelQuads.addAll(model.getQuads(state, face, rand));
}
modelQuads.addAll(model.getQuads(state, null, rand));
return modelQuads;
}
/**
* Given the actual facade bounding box, and the bounding boxes of all parts,
* determine the biggest union of AABB that intersect with the facade's bounding
* box. This AABB will need to be "cut out" when the facade is rendered.
*/
@Nullable
private static AEAxisAlignedBB getCutOutBox(Box facadeBox, List<Box> partBoxes) {
AEAxisAlignedBB b = null;
for (Box bb : partBoxes) {
if (bb.intersects(facadeBox)) {
if (b == null) {
b = AEAxisAlignedBB.fromBounds(bb);
} else {
b.maxX = Math.max(b.maxX, bb.maxX);
b.maxY = Math.max(b.maxY, bb.maxY);
b.maxZ = Math.max(b.maxZ, bb.maxZ);
b.minX = Math.min(b.minX, bb.minX);
b.minY = Math.min(b.minY, bb.minY);
b.minZ = Math.min(b.minZ, bb.minZ);
}
}
}
return b;
}
/**
* Generates the box segments around the specified hole. If the specified hole
* is null, a Singleton of the Facade box is returned.
*
* @param fb The Facade's box.
* @param hole The hole to 'cut'.
* @param axis The axis the facade is on.
*
* @return The box segments.
*/
private static List<Box> getBoxes(Box fb, AEAxisAlignedBB hole, Axis axis) {
if (hole == null) {
return Collections.singletonList(fb);
}
List<Box> boxes = new ArrayList<>();
switch (axis) {
case Y:
boxes.add(new Box(fb.minX, fb.minY, fb.minZ, hole.minX, fb.maxY, fb.maxZ));
boxes.add(new Box(hole.maxX, fb.minY, fb.minZ, fb.maxX, fb.maxY, fb.maxZ));
boxes.add(new Box(hole.minX, fb.minY, fb.minZ, hole.maxX, fb.maxY, hole.minZ));
boxes.add(new Box(hole.minX, fb.minY, hole.maxZ, hole.maxX, fb.maxY, fb.maxZ));
break;
case Z:
boxes.add(new Box(fb.minX, fb.minY, fb.minZ, fb.maxX, hole.minY, fb.maxZ));
boxes.add(new Box(fb.minX, hole.maxY, fb.minZ, fb.maxX, fb.maxY, fb.maxZ));
boxes.add(new Box(fb.minX, hole.minY, fb.minZ, hole.minX, hole.maxY, fb.maxZ));
boxes.add(new Box(hole.maxX, hole.minY, fb.minZ, fb.maxX, hole.maxY, fb.maxZ));
break;
case X:
boxes.add(new Box(fb.minX, fb.minY, fb.minZ, fb.maxX, hole.minY, fb.maxZ));
boxes.add(new Box(fb.minX, hole.maxY, fb.minZ, fb.maxX, fb.maxY, fb.maxZ));
boxes.add(new Box(fb.minX, hole.minY, fb.minZ, fb.maxX, hole.maxY, hole.minZ));
boxes.add(new Box(fb.minX, hole.minY, hole.maxZ, fb.maxX, hole.maxY, fb.maxZ));
break;
default:
// should never happen.
throw new RuntimeException("switch falloff. " + String.valueOf(axis));
}
return boxes;
}
/**
* Determines if any of the part's bounding boxes intersects with the outside 2
* voxel wide layer. If so, we should use thinner facades (1 voxel deep).
*/
private static boolean isUseThinFacades(List<Box> partBoxes) {
final double min = 2.0 / 16.0;
final double max = 14.0 / 16.0;
for (Box bb : partBoxes) {
int o = 0;
o += bb.maxX > max ? 1 : 0;
o += bb.maxY > max ? 1 : 0;
o += bb.maxZ > max ? 1 : 0;
o += bb.minX < min ? 1 : 0;
o += bb.minY < min ? 1 : 0;
o += bb.minZ < min ? 1 : 0;
if (o >= 2) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,28 @@
package appeng.client.render.cablebus;
import net.minecraft.block.BlockState;
/**
* Captures the state required to render a facade properly.
*/
public class FacadeRenderState {
// The block state to use for rendering this facade
private final BlockState sourceBlock;
private final boolean transparent;
public FacadeRenderState(BlockState sourceBlock, boolean transparent) {
this.sourceBlock = sourceBlock;
this.transparent = transparent;
}
public BlockState getSourceBlock() {
return this.sourceBlock;
}
public boolean isTransparent() {
return this.transparent;
}
}
@@ -1,116 +0,0 @@
package appeng.client.render.cablebus;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.json.ModelOverrideList;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;
import appeng.api.util.AEColor;
import appeng.util.Platform;
public class P2PTunnelFrequencyBakedModel implements FabricBakedModel {
private final Sprite texture;
private final static Cache<Long, List<BakedQuad>> modelCache = CacheBuilder.newBuilder().maximumSize(100).build();
private static final int[][] QUAD_OFFSETS = new int[][] { { 4, 10, 2 }, { 10, 10, 2 }, { 4, 4, 2 }, { 10, 4, 2 } };
public P2PTunnelFrequencyBakedModel(final Sprite texture) {
this.texture = texture;
}
@Override
public List<BakedQuad> getQuads(BlockState state, Direction side, Random rand, IModelData modelData) {
if (side != null || !(modelData instanceof P2PTunnelFrequencyModelData)) {
return Collections.emptyList();
}
P2PTunnelFrequencyModelData freqModelData = (P2PTunnelFrequencyModelData) modelData;
return this.getPartQuads(freqModelData.getFrequency());
}
private List<BakedQuad> getQuadsForFrequency(final short frequency, final boolean active) {
final AEColor[] colors = Platform.p2p().toColors(frequency);
final CubeBuilder cb = new CubeBuilder();
cb.setTexture(this.texture);
cb.useStandardUV();
cb.setRenderFullBright(active);
for (int i = 0; i < 4; ++i) {
final int[] offs = QUAD_OFFSETS[i];
for (int j = 0; j < 4; ++j) {
final AEColor c = colors[j];
if (active) {
cb.setColorRGB(c.dye.getColorValue());
} else {
final float[] cv = c.dye.getColorComponentValues();
cb.setColorRGB(cv[0] * 0.5f, cv[1] * 0.5f, cv[2] * 0.5f);
}
final int startx = j % 2;
final int starty = 1 - j / 2;
cb.addCube(offs[0] + startx, offs[1] + starty, offs[2], offs[0] + startx + 1, offs[1] + starty + 1,
offs[2] + 1);
}
}
return cb.getOutput();
}
private List<BakedQuad> getPartQuads(long partFlags) {
try {
return modelCache.get(partFlags, () -> {
short frequency = (short) (partFlags & 0xffffL);
boolean active = (partFlags & 0x10000L) != 0;
return this.getQuadsForFrequency(frequency, active);
});
} catch (ExecutionException e) {
return Collections.emptyList();
}
}
@Override
public boolean useAmbientOcclusion() {
return false;
}
@Override
public boolean hasDepth() {
return false;
}
@Override
public boolean isSideLit() {
return false;// TODO
}
@Override
public boolean isBuiltin() {
return true;
}
@Override
public Sprite getSprite() {
return this.texture;
}
@Override
public ModelOverrideList getOverrides() {
return ModelOverrideList.EMPTY;
}
}
@@ -1,46 +0,0 @@
package appeng.client.render.cablebus;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.function.Function;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.IModelTransform;
import net.minecraft.client.render.model.IUnbakedModel;
import net.minecraft.client.render.model.json.ModelOverrideList;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.Identifier;
import net.minecraftforge.client.model.IModelConfiguration;
import net.minecraftforge.client.model.geometry.IModelGeometry;
import appeng.core.AppEng;
public class P2PTunnelFrequencyModel implements IModelGeometry<P2PTunnelFrequencyModel> {
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "parts/p2p_tunnel_frequency"));
@Override
public BakedModel bake(IModelConfiguration owner, ModelLoader bakery,
Function<SpriteIdentifier, Sprite> spriteGetter, IModelTransform modelTransform,
ModelOverrideList overrides, Identifier modelLocation) {
try {
final Sprite texture = spriteGetter.apply(TEXTURE);
return new P2PTunnelFrequencyBakedModel(texture);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Collection<SpriteIdentifier> getTextures(IModelConfiguration owner,
Function<Identifier, IUnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors) {
return Collections.singleton(TEXTURE);
}
}
@@ -1,21 +0,0 @@
package appeng.client.render.cablebus;
import net.minecraftforge.client.model.data.ModelProperty;
import appeng.client.render.model.AEInternalModelData;
public final class P2PTunnelFrequencyModelData extends AEInternalModelData {
public static final ModelProperty<Long> FREQUENCY = new ModelProperty<>();
private final long frequency;
public P2PTunnelFrequencyModelData(long frequency) {
this.frequency = frequency;
}
public long getFrequency() {
return frequency;
}
}
@@ -0,0 +1,93 @@
/*
* 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.cablebus;
import appeng.client.render.FacingToRotation;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Matrix4f;
/**
* Assuming a default-orientation of forward=NORTH and up=UP, this class rotates
* a given list of quads to the desired facing
*/
@Environment(EnvType.CLIENT)
public class QuadRotator implements RenderContext.QuadTransform {
// FIXME private static final ThreadLocal<BakedPipeline> pipelines = ThreadLocal.withInitial(() -> //
// FIXME BakedPipeline.builder()//
// FIXME .addElement("transformer", QuadMatrixTransformer.FACTORY)//
// FIXME .build());
// FIXME private static final ThreadLocal<Quad> collectors = ThreadLocal.withInitial(Quad::new);
private static final RenderContext.QuadTransform NULL_TRANSFORM = quad -> true;
private final FacingToRotation rotation;
public QuadRotator(FacingToRotation rotation) {
this.rotation = rotation;
}
public static RenderContext.QuadTransform get(Direction newForward, Direction newUp) {
if (newForward == Direction.NORTH && newUp == Direction.UP) {
return NULL_TRANSFORM; // This is the default orientation
}
FacingToRotation rotation = getRotation(newForward, newUp);
if (rotation.isRedundant()) {
return NULL_TRANSFORM;
}
return new QuadRotator(rotation);
}
@Override
public boolean transform(MutableQuadView quad) {
// FIXME: Temporary rotation fix
Matrix4f mat = new Matrix4f();
mat.addToLastColumn(new Vector3f(-0.5f, -0.5f, -0.5f));
mat.multiply(rotation.getMat());
mat.addToLastColumn(new Vector3f(0.5f, 0.5f, 0.5f));
// FIXME ROTATION pipeline.reset(format);
// FIXME ROTATION collector.reset(format);
// FIXME ROTATION
// FIXME ROTATION transformer.setMatrix(mat);
// FIXME ROTATION pipeline.prepare(collector);
// FIXME ROTATION quad.pipe(pipeline);
return true;
}
private static FacingToRotation getRotation(Direction forward, Direction up) {
// Sanitize forward/up
if (forward.getAxis() == up.getAxis()) {
if (up.getAxis() == Direction.Axis.Y) {
up = Direction.NORTH;
} else {
up = Direction.UP;
}
}
return FacingToRotation.get(forward, up);
}
}
@@ -0,0 +1,88 @@
/*
* 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.cablebus;
import java.util.Arrays;
import java.util.function.Function;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.Identifier;
import appeng.core.AppEng;
/**
* Manages the channel textures for smart cables.
*/
@Environment(EnvType.CLIENT)
public class SmartCableTextures {
public static final SpriteIdentifier[] SMART_CHANNELS_TEXTURES = Arrays
.stream(new Identifier[] { new Identifier(AppEng.MOD_ID, "parts/cable/smart/channels_00"), //
new Identifier(AppEng.MOD_ID, "parts/cable/smart/channels_01"), //
new Identifier(AppEng.MOD_ID, "parts/cable/smart/channels_02"), //
new Identifier(AppEng.MOD_ID, "parts/cable/smart/channels_03"), //
new Identifier(AppEng.MOD_ID, "parts/cable/smart/channels_04"), //
new Identifier(AppEng.MOD_ID, "parts/cable/smart/channels_10"), //
new Identifier(AppEng.MOD_ID, "parts/cable/smart/channels_11"), //
new Identifier(AppEng.MOD_ID, "parts/cable/smart/channels_12"), //
new Identifier(AppEng.MOD_ID, "parts/cable/smart/channels_13"), //
new Identifier(AppEng.MOD_ID, "parts/cable/smart/channels_14")//
}).map(e -> new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX, e)).toArray(SpriteIdentifier[]::new);
// Textures used to display channels on smart cables. There's two sets of 5
// textures each, and
// one of each set are composed together to get even/odd colored channels
private final Sprite[] textures;
public SmartCableTextures(Function<SpriteIdentifier, Sprite> bakedTextureGetter) {
this.textures = Arrays.stream(SMART_CHANNELS_TEXTURES)//
.map(bakedTextureGetter)//
.toArray(Sprite[]::new);
}
/**
* The odd variant is used for displaying channels 1-4 as in use.
*/
public Sprite getOddTextureForChannels(int channels) {
if (channels < 0) {
return this.textures[0];
} else if (channels <= 4) {
return this.textures[channels];
} else {
return this.textures[4];
}
}
/**
* The odd variant is used for displaying channels 5-8 as in use.
*/
public Sprite getEvenTextureForChannels(int channels) {
if (channels < 5) {
return this.textures[5];
} else if (channels <= 8) {
return this.textures[1 + channels];
} else {
return this.textures[9];
}
}
}