Files
Applied-Energistics-2/src/main/java/appeng/client/render/crafting/CraftingCubeBakedModel.java
T
2020-06-28 00:45:33 +02:00

289 lines
11 KiB
Java

/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.client.render.crafting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Random;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
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 net.minecraftforge.client.model.data.IDynamicBakedModel;
import appeng.client.render.cablebus.CubeBuilder;
import appeng.tile.crafting.CraftingCubeModelData;
import appeng.util.Platform;
/**
* The base model for baked models used by components of the crafting cube
* multi-block in it's formed state. Primarily this base class handles adding
* the "ring" that frames the multi-block structure and delegates rendering of
* the "inner" part of each block to the subclasses of this class.
*/
abstract class CraftingCubeBakedModel implements IDynamicBakedModel {
private final Sprite ringCorner;
private final Sprite ringHor;
private final Sprite ringVer;
CraftingCubeBakedModel(Sprite ringCorner, Sprite ringHor, Sprite ringVer) {
this.ringCorner = ringCorner;
this.ringHor = ringHor;
this.ringVer = ringVer;
}
@Nonnull
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand,
@Nonnull IModelData extraData) {
if (side == null) {
return Collections.emptyList(); // No generic quads for this model
}
EnumSet<Direction> connections = getConnections(extraData);
List<BakedQuad> quads = new ArrayList<>();
CubeBuilder builder = new CubeBuilder(quads);
builder.setDrawFaces(EnumSet.of(side));
// Add the quads for the ring that frames the entire multi-block structure
this.addRing(builder, side, connections);
// Calculate the bounds of the "inner" block that is framed by the border drawn
// above
float x2 = connections.contains(Direction.EAST) ? 16 : 13.01f;
float x1 = connections.contains(Direction.WEST) ? 0 : 2.99f;
float y2 = connections.contains(Direction.UP) ? 16 : 13.01f;
float y1 = connections.contains(Direction.DOWN) ? 0 : 2.99f;
float z2 = connections.contains(Direction.SOUTH) ? 16 : 13.01f;
float z1 = connections.contains(Direction.NORTH) ? 0 : 2.99f;
// On the axis of the side that we're currently drawing, extend the dimensions
// out to the outer face of the block
switch (side) {
case DOWN:
case UP:
y1 = 0;
y2 = 16;
break;
case NORTH:
case SOUTH:
z1 = 0;
z2 = 16;
break;
case WEST:
case EAST:
x1 = 0;
x2 = 16;
break;
}
this.addInnerCube(side, state, extraData, builder, x1, y1, z1, x2, y2, z2);
return quads;
}
private void addRing(CubeBuilder builder, @Nullable Direction side, EnumSet<Direction> connections) {
// Fill in the corners
builder.setTexture(this.ringCorner);
this.addCornerCap(builder, connections, side, Direction.UP, Direction.EAST, Direction.NORTH);
this.addCornerCap(builder, connections, side, Direction.UP, Direction.EAST, Direction.SOUTH);
this.addCornerCap(builder, connections, side, Direction.UP, Direction.WEST, Direction.NORTH);
this.addCornerCap(builder, connections, side, Direction.UP, Direction.WEST, Direction.SOUTH);
this.addCornerCap(builder, connections, side, Direction.DOWN, Direction.EAST, Direction.NORTH);
this.addCornerCap(builder, connections, side, Direction.DOWN, Direction.EAST, Direction.SOUTH);
this.addCornerCap(builder, connections, side, Direction.DOWN, Direction.WEST, Direction.NORTH);
this.addCornerCap(builder, connections, side, Direction.DOWN, Direction.WEST, Direction.SOUTH);
// Fill in the remaining stripes of the face
for (Direction a : Direction.values()) {
if (a == side || a == side.getOpposite()) {
continue;
}
// Select the horizontal or vertical ring texture depending on which side we're
// filling in
if ((side.getAxis() != Direction.Axis.Y)
&& (a == Direction.NORTH || a == Direction.EAST || a == Direction.WEST || a == Direction.SOUTH)) {
builder.setTexture(this.ringVer);
} else if (side.getAxis() == Direction.Axis.Y && (a == Direction.EAST || a == Direction.WEST)) {
builder.setTexture(this.ringVer);
} else {
builder.setTexture(this.ringHor);
}
// If there's an adjacent crafting cube block on side a, then the core of the
// block already extends
// fully to this side. So only bother drawing the stripe, if there's no
// connection.
if (!connections.contains(a)) {
// Note that since we're drawing something that "looks" 2-dimensional,
// two of the following will always be 0 and 16.
float x1 = 0, y1 = 0, z1 = 0, x2 = 16, y2 = 16, z2 = 16;
switch (a) {
case DOWN:
y1 = 0;
y2 = 3;
break;
case UP:
y1 = 13.0f;
y2 = 16;
break;
case WEST:
x1 = 0;
x2 = 3;
break;
case EAST:
x1 = 13;
x2 = 16;
break;
case NORTH:
z1 = 0;
z2 = 3;
break;
case SOUTH:
z1 = 13;
z2 = 16;
break;
}
// Constraint the stripe in the two directions perpendicular to a in case there
// has been a corner
// drawn in those directions. Since a corner is drawn if the three touching
// faces dont have adjacent
// crafting cube blocks, we'd have to check for a, side, and the perpendicular
// direction. But in this
// block, we've already checked for side (due to face culling) and a (see
// above).
Direction perpendicular = Platform.rotateAround(a, side);
for (Direction cornerCandidate : EnumSet.of(perpendicular, perpendicular.getOpposite())) {
if (!connections.contains(cornerCandidate)) {
// There's a cap in this direction
switch (cornerCandidate) {
case DOWN:
y1 = 3;
break;
case UP:
y2 = 13;
break;
case NORTH:
z1 = 3;
break;
case SOUTH:
z2 = 13;
break;
case WEST:
x1 = 3;
break;
case EAST:
x2 = 13;
break;
}
}
}
builder.addCube(x1, y1, z1, x2, y2, z2);
}
}
}
/**
* Adds a 3x3x3 corner cap to the cube builder if there are no adjacent crafting
* cubes on that corner.
*/
private void addCornerCap(CubeBuilder builder, EnumSet<Direction> connections, Direction side, Direction down,
Direction west, Direction north) {
if (connections.contains(down) || connections.contains(west) || connections.contains(north)) {
return;
}
// Only add faces for sides that can actually be seen (the outside of the cube)
if (side != down && side != west && side != north) {
return;
}
float x1 = (west == Direction.WEST ? 0 : 13);
float y1 = (down == Direction.DOWN ? 0 : 13);
float z1 = (north == Direction.NORTH ? 0 : 13);
float x2 = (west == Direction.WEST ? 3 : 16);
float y2 = (down == Direction.DOWN ? 3 : 16);
float z2 = (north == Direction.NORTH ? 3 : 16);
builder.addCube(x1, y1, z1, x2, y2, z2);
}
// Retrieve the cube connection state from the block state
// If none is present, just assume there are no adjacent crafting cube blocks
private static EnumSet<Direction> getConnections(IModelData modelData) {
if (!(modelData instanceof CraftingCubeModelData)) {
return EnumSet.noneOf(Direction.class);
}
return ((CraftingCubeModelData) modelData).getConnections();
}
protected abstract void addInnerCube(Direction facing, BlockState state, IModelData modelData, CubeBuilder builder,
float x1, float y1, float z1, float x2, float y2, float z2);
@Override
public boolean useAmbientOcclusion() {
return false;
}
@Override
public boolean hasDepth() {
return false;
}
@Override
public boolean isBuiltin() {
return false;
}
@Override
public Sprite getSprite() {
return this.ringCorner;
}
@Override
public boolean isSideLit() {
return false;
}
@Override
public ModelOverrideList getOverrides() {
return ModelOverrideList.EMPTY;
}
}