Pattern Rendering and other Rendering

This commit is contained in:
Sebastian Hartte
2020-07-20 20:13:20 +02:00
parent d053c9b1bc
commit 019f570331
69 changed files with 994 additions and 1398 deletions
@@ -20,6 +20,7 @@ package appeng.block;
import java.util.List;
import appeng.block.networking.WirelessBlock;
import net.fabricmc.api.EnvType;
import net.minecraft.block.Block;
import net.minecraft.client.item.TooltipContext;
@@ -84,7 +85,7 @@ public class AEBaseBlockItem extends BlockItem {
} else {
forward = Direction.UP;
}
} else if (/* FIXME FABRIC this.blockType instanceof WirelessBlock || */ this.blockType instanceof SkyCompassBlock) {
} else if (this.blockType instanceof WirelessBlock || this.blockType instanceof SkyCompassBlock) {
forward = side;
if (forward == Direction.UP || forward == Direction.DOWN) {
up = Direction.SOUTH;
@@ -152,7 +152,7 @@ public class SkyCompassBlock extends AEBaseTileBlock<SkyCompassBlockEntity> {
@Override
public BlockRenderType getRenderType(BlockState state) {
return BlockRenderType.ENTITYBLOCK_ANIMATED;
return BlockRenderType.MODEL;
}
}
@@ -0,0 +1,255 @@
package appeng.block.qnb;
import appeng.client.render.cablebus.CubeBuilder;
import appeng.core.Api;
import appeng.core.AppEng;
import com.google.common.collect.ImmutableList;
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.minecraft.block.Block;
import net.minecraft.block.BlockState;
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.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.SpriteIdentifier;
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.EnumSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
class QnbFormedBakedModel implements BakedModel, FabricBakedModel {
private static final SpriteIdentifier TEXTURE_LINK = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "block/quantum_link"));
private static final SpriteIdentifier TEXTURE_RING = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "block/quantum_ring"));
private static final SpriteIdentifier TEXTURE_RING_LIGHT = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "block/quantum_ring_light"));
private static final SpriteIdentifier TEXTURE_RING_LIGHT_CORNER = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "block/quantum_ring_light_corner"));
private static final SpriteIdentifier TEXTURE_CABLE_GLASS = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "parts/cable/glass/transparent"));
private static final SpriteIdentifier TEXTURE_COVERED_CABLE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "parts/cable/covered/transparent"));
private static final float DEFAULT_RENDER_MIN = 2.0f;
private static final float DEFAULT_RENDER_MAX = 14.0f;
private static final float CORNER_POWERED_RENDER_MIN = 3.9f;
private static final float CORNER_POWERED_RENDER_MAX = 12.1f;
private static final float CENTER_POWERED_RENDER_MIN = -0.01f;
private static final float CENTER_POWERED_RENDER_MAX = 16.01f;
private final BakedModel baseModel;
private final Block linkBlock;
private final Sprite linkTexture;
private final Sprite ringTexture;
private final Sprite glassCableTexture;
private final Sprite coveredCableTexture;
private final Sprite lightTexture;
private final Sprite lightCornerTexture;
public QnbFormedBakedModel(BakedModel baseModel, Function<SpriteIdentifier, Sprite> bakedTextureGetter) {
this.baseModel = baseModel;
this.linkTexture = bakedTextureGetter.apply(TEXTURE_LINK);
this.ringTexture = bakedTextureGetter.apply(TEXTURE_RING);
this.glassCableTexture = bakedTextureGetter.apply(TEXTURE_CABLE_GLASS);
this.coveredCableTexture = bakedTextureGetter.apply(TEXTURE_COVERED_CABLE);
this.lightTexture = bakedTextureGetter.apply(TEXTURE_RING_LIGHT);
this.lightCornerTexture = bakedTextureGetter.apply(TEXTURE_RING_LIGHT_CORNER);
this.linkBlock = Api.instance().definitions().blocks().quantumLink().maybeBlock().orElse(null);
}
@Override
public boolean isVanillaAdapter() {
return false;
}
@Override
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
}
@Override
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
QnbFormedState formedState = getState(blockView, pos);
if (formedState == null) {
context.fallbackConsumer().accept(this.baseModel);
return;
}
buildQuads(context.getEmitter(), formedState, state);
}
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction face, Random random) {
return baseModel.getQuads(state, face, random);
}
private static QnbFormedState getState(BlockRenderView view, BlockPos pos) {
if (!(view instanceof RenderAttachedBlockView)) {
return null;
}
Object attachment = ((RenderAttachedBlockView) view).getBlockEntityRenderAttachment(pos);
if (attachment instanceof QnbFormedState) {
return (QnbFormedState) attachment;
}
return null;
}
private void buildQuads(QuadEmitter emitter, QnbFormedState formedState, BlockState state) {
CubeBuilder builder = new CubeBuilder(emitter);
if (state.getBlock() == this.linkBlock) {
Set<Direction> sides = formedState.getAdjacentQuantumBridges();
this.renderCableAt(builder, 0.11f * 16, this.glassCableTexture, 0.141f * 16, sides);
this.renderCableAt(builder, 0.188f * 16, this.coveredCableTexture, 0.1875f * 16, sides);
builder.setTexture(this.linkTexture);
builder.addCube(DEFAULT_RENDER_MIN, DEFAULT_RENDER_MIN, DEFAULT_RENDER_MIN, DEFAULT_RENDER_MAX,
DEFAULT_RENDER_MAX, DEFAULT_RENDER_MAX);
} else {
if (formedState.isCorner()) {
this.renderCableAt(builder, 0.188f * 16, this.coveredCableTexture, 0.05f * 16,
formedState.getAdjacentQuantumBridges());
builder.setTexture(this.ringTexture);
builder.addCube(DEFAULT_RENDER_MIN, DEFAULT_RENDER_MIN, DEFAULT_RENDER_MIN, DEFAULT_RENDER_MAX,
DEFAULT_RENDER_MAX, DEFAULT_RENDER_MAX);
if (formedState.isPowered()) {
builder.setTexture(this.lightCornerTexture);
builder.setRenderFullBright(true);
for (Direction facing : Direction.values()) {
// Offset the face by a slight amount so that it is drawn over the already drawn
// ring texture
// (avoids z-fighting)
float xOffset = Math.abs(facing.getOffsetX() * 0.01f);
float yOffset = Math.abs(facing.getOffsetY() * 0.01f);
float zOffset = Math.abs(facing.getOffsetZ() * 0.01f);
builder.setDrawFaces(EnumSet.of(facing));
builder.addCube(DEFAULT_RENDER_MIN - xOffset, DEFAULT_RENDER_MIN - yOffset,
DEFAULT_RENDER_MIN - zOffset, DEFAULT_RENDER_MAX + xOffset,
DEFAULT_RENDER_MAX + yOffset, DEFAULT_RENDER_MAX + zOffset);
}
}
} else {
builder.setTexture(this.ringTexture);
builder.addCube(0, DEFAULT_RENDER_MIN, DEFAULT_RENDER_MIN, 16, DEFAULT_RENDER_MAX, DEFAULT_RENDER_MAX);
builder.addCube(DEFAULT_RENDER_MIN, 0, DEFAULT_RENDER_MIN, DEFAULT_RENDER_MAX, 16, DEFAULT_RENDER_MAX);
builder.addCube(DEFAULT_RENDER_MIN, DEFAULT_RENDER_MIN, 0, DEFAULT_RENDER_MAX, DEFAULT_RENDER_MAX, 16);
if (formedState.isPowered()) {
builder.setTexture(this.lightTexture);
builder.setRenderFullBright(true);
for (Direction facing : Direction.values()) {
// Offset the face by a slight amount so that it is drawn over the already drawn
// ring texture
// (avoids z-fighting)
float xOffset = Math.abs(facing.getOffsetX() * 0.01f);
float yOffset = Math.abs(facing.getOffsetY() * 0.01f);
float zOffset = Math.abs(facing.getOffsetZ() * 0.01f);
builder.setDrawFaces(EnumSet.of(facing));
builder.addCube(-xOffset, -yOffset, -zOffset, 16 + xOffset, 16 + yOffset, 16 + zOffset);
}
}
}
}
}
private void renderCableAt(CubeBuilder builder, float thickness, Sprite texture, float pull,
Set<Direction> connections) {
builder.setTexture(texture);
if (connections.contains(Direction.WEST)) {
builder.addCube(0, 8 - thickness, 8 - thickness, 8 - thickness - pull, 8 + thickness, 8 + thickness);
}
if (connections.contains(Direction.EAST)) {
builder.addCube(8 + thickness + pull, 8 - thickness, 8 - thickness, 16, 8 + thickness, 8 + thickness);
}
if (connections.contains(Direction.NORTH)) {
builder.addCube(8 - thickness, 8 - thickness, 0, 8 + thickness, 8 + thickness, 8 - thickness - pull);
}
if (connections.contains(Direction.SOUTH)) {
builder.addCube(8 - thickness, 8 - thickness, 8 + thickness + pull, 8 + thickness, 8 + thickness, 16);
}
if (connections.contains(Direction.DOWN)) {
builder.addCube(8 - thickness, 0, 8 - thickness, 8 + thickness, 8 - thickness - pull, 8 + thickness);
}
if (connections.contains(Direction.UP)) {
builder.addCube(8 - thickness, 8 + thickness + pull, 8 - thickness, 8 + thickness, 16, 8 + thickness);
}
}
@Override
public ModelTransformation getTransformation() {
return ModelTransformation.NONE;
}
@Override
public boolean useAmbientOcclusion() {
return this.baseModel.useAmbientOcclusion();
}
@Override
public boolean hasDepth() {
return true;
}
@Override
public boolean isSideLit() {
return false;
}
@Override
public boolean isBuiltin() {
return false;
}
@Override
public Sprite getSprite() {
return this.baseModel.getSprite();
}
@Override
public ModelOverrideList getOverrides() {
return this.baseModel.getOverrides();
}
public static List<SpriteIdentifier> getRequiredTextures() {
return ImmutableList.of(TEXTURE_LINK, TEXTURE_RING, TEXTURE_CABLE_GLASS, TEXTURE_COVERED_CABLE,
TEXTURE_RING_LIGHT, TEXTURE_RING_LIGHT_CORNER);
}
}
@@ -0,0 +1,40 @@
package appeng.block.qnb;
import appeng.client.render.BasicUnbakedModel;
import appeng.core.AppEng;
import com.google.common.collect.ImmutableSet;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.function.Function;
import java.util.stream.Stream;
public class QnbFormedModel implements BasicUnbakedModel {
private static final Identifier MODEL_RING = new Identifier(AppEng.MOD_ID, "block/qnb/ring");
@Nullable
@Override
public BakedModel bake(ModelLoader loader, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) {
BakedModel ringModel = loader.bake(MODEL_RING, rotationContainer);
return new QnbFormedBakedModel(ringModel, textureGetter);
}
@Override
public Collection<Identifier> getModelDependencies() {
return ImmutableSet.of(MODEL_RING);
}
@Override
public Stream<SpriteIdentifier> getAdditionalTextures() {
return QnbFormedBakedModel.getRequiredTextures().stream();
}
}
+18 -14
View File
@@ -1,6 +1,9 @@
package appeng.client;
import appeng.api.parts.CableRenderMode;
import appeng.block.crafting.AbstractCraftingUnitBlock;
import appeng.block.paint.PaintSplotchesModel;
import appeng.block.qnb.QnbFormedModel;
import appeng.bootstrap.ModelsReloadCallback;
import appeng.bootstrap.components.IClientSetupComponent;
import appeng.bootstrap.components.IItemColorRegistrationComponent;
@@ -8,9 +11,9 @@ import appeng.bootstrap.components.IModelBakeComponent;
import appeng.client.gui.implementations.*;
import appeng.client.render.SimpleModelLoader;
import appeng.client.render.cablebus.CableBusModelLoader;
import appeng.client.render.crafting.CraftingCubeModel;
import appeng.client.render.effects.*;
import appeng.client.render.model.DriveModel;
import appeng.client.render.model.SkyCompassModel;
import appeng.client.render.model.*;
import appeng.client.render.spatial.SpatialPylonModel;
import appeng.client.render.tesr.InscriberTESR;
import appeng.client.render.tesr.SkyChestTESR;
@@ -239,28 +242,29 @@ public final class AppEngClient extends AppEngBase {
ModelLoadingRegistry.INSTANCE.registerResourceProvider(rm -> new CableBusModelLoader((PartModels) Api.INSTANCE.registries().partModels()));
// FIXME FABRIC addBuiltInModel("glass", GlassModel::new);
addBuiltInModel("block/quartz_glass", GlassModel::new);
addBuiltInModel("block/sky_compass", SkyCompassModel::new);
addBuiltInModel("item/sky_compass", SkyCompassModel::new);
// FIXME FABRIC addBuiltInModel("item/dummy_fluid_item", DummyFluidItemModel::new);
// FIXME FABRIC addBuiltInModel("memory_card", MemoryCardModel::new);
// FIXME FABRIC addBuiltInModel("biometric_card", BiometricCardModel::new);
addBuiltInModel("item/memory_card", MemoryCardModel::new);
addBuiltInModel("item/biometric_card", BiometricCardModel::new);
addBuiltInModel("block/drive", DriveModel::new);
// FIXME FABRIC addBuiltInModel("color_applicator", ColorApplicatorModel::new);
addBuiltInModel("color_applicator", ColorApplicatorModel::new); // FIXME need to wire this up
addBuiltInModel("block/spatial_pylon", SpatialPylonModel::new);
// FIXME FABRIC addBuiltInModel("paint_splotches", PaintSplotchesModel::new);
// FIXME FABRIC addBuiltInModel("quantum_bridge_formed", QnbFormedModel::new);
addBuiltInModel("block/paint", PaintSplotchesModel::new);
addBuiltInModel("block/qnb/qnb_formed", QnbFormedModel::new);
// FIXME FABRIC addBuiltInModel("p2p_tunnel_frequency", P2PTunnelFrequencyModel::new);
// FIXME FABRIC addBuiltInModel("facade", FacadeItemModel::new);
// FIXME FABRIC ModelLoaderRegistry.registerLoader(new Identifier(AppEng.MOD_ID, "encoded_pattern"),
// FIXME FABRIC EncodedPatternModelLoader.INSTANCE);
// FIXME FABRIC ModelLoaderRegistry.registerLoader(new Identifier(AppEng.MOD_ID, "part_plane"),
// FIXME FABRIC PlaneModelLoader.INSTANCE);
// FIXME FABRIC ModelLoaderRegistry.registerLoader(new Identifier(AppEng.MOD_ID, "crafting_cube"),
// FIXME FABRIC CraftingCubeModelLoader.INSTANCE);
addBuiltInModel("block/crafting/1k_storage_formed", () -> new CraftingCubeModel(AbstractCraftingUnitBlock.CraftingUnitType.STORAGE_1K));
addBuiltInModel("block/crafting/4k_storage_formed", () -> new CraftingCubeModel(AbstractCraftingUnitBlock.CraftingUnitType.STORAGE_4K));
addBuiltInModel("block/crafting/16k_storage_formed", () -> new CraftingCubeModel(AbstractCraftingUnitBlock.CraftingUnitType.STORAGE_16K));
addBuiltInModel("block/crafting/64k_storage_formed", () -> new CraftingCubeModel(AbstractCraftingUnitBlock.CraftingUnitType.STORAGE_64K));
addBuiltInModel("block/crafting/accelerator_formed", () -> new CraftingCubeModel(AbstractCraftingUnitBlock.CraftingUnitType.ACCELERATOR));
addBuiltInModel("block/crafting/monitor_formed", () -> new CraftingCubeModel(AbstractCraftingUnitBlock.CraftingUnitType.MONITOR));
addBuiltInModel("block/crafting/unit_formed", () -> new CraftingCubeModel(AbstractCraftingUnitBlock.CraftingUnitType.UNIT));
// FIXME FABRIC ModelLoaderRegistry.registerLoader(new Identifier(AppEng.MOD_ID, "uvlightmap"), UVLModelLoader.INSTANCE);
// FIXME FABRIC ModelLoaderRegistry.registerLoader(new Identifier(AppEng.MOD_ID, "cable_bus"),
// FIXME FABRIC new CableBusModelLoader());
}
private static <T extends UnbakedModel> void addBuiltInModel(String id, Supplier<T> modelFactory) {
@@ -0,0 +1,311 @@
/*
* 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.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import javax.annotation.Nullable;
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.minecraft.block.BlockState;
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.Sprite;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockRenderView;
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 BakedModel, FabricBakedModel {
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;
}
@Override
public boolean isVanillaAdapter() {
return false;
}
@Override
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
CraftingCubeModelData modelData = getModelData(blockView, pos);
EnumSet<Direction> connections = modelData.getConnections();
CubeBuilder builder = new CubeBuilder(context.getEmitter());
for (Direction side : Direction.values()) {
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, modelData, builder, x1, y1, z1, x2, y2, z2);
}
}
@Override
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
}
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction face, Random random) {
return Collections.emptyList();
}
@Override
public ModelTransformation getTransformation() {
return ModelTransformation.NONE;
}
private void addRing(CubeBuilder builder, 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 CraftingCubeModelData getModelData(BlockRenderView blockRenderView, BlockPos pos) {
if (!(blockRenderView instanceof RenderAttachedBlockView)) {
return null;
}
Object attached = ((RenderAttachedBlockView) blockRenderView).getBlockEntityRenderAttachment(pos);
if (attached instanceof CraftingCubeModelData) {
return (CraftingCubeModelData) attached;
}
return null;
}
protected abstract void addInnerCube(Direction facing, BlockState state, CraftingCubeModelData 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;
}
}
@@ -0,0 +1,118 @@
/*
* 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 appeng.block.crafting.AbstractCraftingUnitBlock;
import appeng.client.render.BasicUnbakedModel;
import appeng.core.AppEng;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier;
import javax.annotation.Nullable;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* The built-in model for the connected texture crafting cube.
*/
public class CraftingCubeModel implements BasicUnbakedModel {
private final static SpriteIdentifier RING_CORNER = texture("ring_corner");
private final static SpriteIdentifier RING_SIDE_HOR = texture("ring_side_hor");
private final static SpriteIdentifier RING_SIDE_VER = texture("ring_side_ver");
private final static SpriteIdentifier UNIT_BASE = texture("unit_base");
private final static SpriteIdentifier LIGHT_BASE = texture("light_base");
private final static SpriteIdentifier ACCELERATOR_LIGHT = texture("accelerator_light");
private final static SpriteIdentifier STORAGE_1K_LIGHT = texture("1k_storage_light");
private final static SpriteIdentifier STORAGE_4K_LIGHT = texture("4k_storage_light");
private final static SpriteIdentifier STORAGE_16K_LIGHT = texture("16k_storage_light");
private final static SpriteIdentifier STORAGE_64K_LIGHT = texture("64k_storage_light");
private final static SpriteIdentifier MONITOR_BASE = texture("monitor_base");
private final static SpriteIdentifier MONITOR_LIGHT_DARK = texture("monitor_light_dark");
private final static SpriteIdentifier MONITOR_LIGHT_MEDIUM = texture("monitor_light_medium");
private final static SpriteIdentifier MONITOR_LIGHT_BRIGHT = texture("monitor_light_bright");
private final AbstractCraftingUnitBlock.CraftingUnitType type;
public CraftingCubeModel(AbstractCraftingUnitBlock.CraftingUnitType type) {
this.type = type;
}
@Override
public Stream<SpriteIdentifier> getAdditionalTextures() {
return Stream.of(RING_CORNER, RING_SIDE_HOR, RING_SIDE_VER, UNIT_BASE, LIGHT_BASE, ACCELERATOR_LIGHT,
STORAGE_1K_LIGHT, STORAGE_4K_LIGHT, STORAGE_16K_LIGHT, STORAGE_64K_LIGHT, MONITOR_BASE,
MONITOR_LIGHT_DARK, MONITOR_LIGHT_MEDIUM, MONITOR_LIGHT_BRIGHT);
}
@Nullable
@Override
public BakedModel bake(ModelLoader loader, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) {
// Retrieve our textures and pass them on to the baked model
Sprite ringCorner = textureGetter.apply(RING_CORNER);
Sprite ringSideHor = textureGetter.apply(RING_SIDE_HOR);
Sprite ringSideVer = textureGetter.apply(RING_SIDE_VER);
switch (this.type) {
case UNIT:
return new UnitBakedModel(ringCorner, ringSideHor, ringSideVer, textureGetter.apply(UNIT_BASE));
case ACCELERATOR:
case STORAGE_1K:
case STORAGE_4K:
case STORAGE_16K:
case STORAGE_64K:
return new LightBakedModel(ringCorner, ringSideHor, ringSideVer, textureGetter.apply(LIGHT_BASE),
getLightTexture(textureGetter, this.type));
case MONITOR:
return new MonitorBakedModel(ringCorner, ringSideHor, ringSideVer, textureGetter.apply(UNIT_BASE),
textureGetter.apply(MONITOR_BASE), textureGetter.apply(MONITOR_LIGHT_DARK),
textureGetter.apply(MONITOR_LIGHT_MEDIUM), textureGetter.apply(MONITOR_LIGHT_BRIGHT));
default:
throw new IllegalArgumentException("Unsupported crafting unit type: " + this.type);
}
}
private static Sprite getLightTexture(Function<SpriteIdentifier, Sprite> textureGetter,
AbstractCraftingUnitBlock.CraftingUnitType type) {
switch (type) {
case ACCELERATOR:
return textureGetter.apply(ACCELERATOR_LIGHT);
case STORAGE_1K:
return textureGetter.apply(STORAGE_1K_LIGHT);
case STORAGE_4K:
return textureGetter.apply(STORAGE_4K_LIGHT);
case STORAGE_16K:
return textureGetter.apply(STORAGE_16K_LIGHT);
case STORAGE_64K:
return textureGetter.apply(STORAGE_64K_LIGHT);
default:
throw new IllegalArgumentException("Crafting unit type " + type + " does not use a light texture.");
}
}
private static SpriteIdentifier texture(String name) {
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "block/crafting/" + name));
}
}
@@ -0,0 +1,64 @@
/*
* 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 net.fabricmc.api.Environment;
import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.util.math.Direction;
import net.fabricmc.api.EnvType;
import appeng.api.storage.data.IAEItemStack;
import appeng.client.render.TesrRenderHelper;
import appeng.tile.crafting.CraftingMonitorBlockEntity;
/**
* Renders the item currently being crafted
*/
@Environment(EnvType.CLIENT)
public class CraftingMonitorTESR extends BlockEntityRenderer<CraftingMonitorBlockEntity> {
public CraftingMonitorTESR(BlockEntityRenderDispatcher rendererDispatcherIn) {
super(rendererDispatcherIn);
}
@Override
public void render(CraftingMonitorBlockEntity te, float partialTicks, MatrixStack matrixStack,
VertexConsumerProvider buffers, int combinedLight, int combinedOverlay) {
Direction facing = te.getForward();
IAEItemStack jobProgress = te.getJobProgress();
if (jobProgress != null) {
matrixStack.push();
matrixStack.translate(0.5, 0.5, 0.5); // Move to the center of the block
TesrRenderHelper.rotateToFace(matrixStack, facing, (byte) 0);
matrixStack.translate(0, 0.08, 0.5);
TesrRenderHelper.renderItem2dWithAmount(matrixStack, buffers, jobProgress, 0.3f, -0.18f, 15728880,
combinedOverlay);
matrixStack.pop();
}
}
}
@@ -0,0 +1,59 @@
/*
* 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 appeng.tile.crafting.CraftingCubeModelData;
import net.minecraft.block.BlockState;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;
import appeng.block.crafting.AbstractCraftingUnitBlock;
import appeng.client.render.cablebus.CubeBuilder;
/**
* Crafting cube baked model that adds a full-bright light texture on top of a
* normal base texture onto the inner cube. The light texture is only drawn
* fullbright if the multiblock is currently powered.
*/
class LightBakedModel extends CraftingCubeBakedModel {
private final Sprite baseTexture;
private final Sprite lightTexture;
LightBakedModel(Sprite ringCorner, Sprite ringHor, Sprite ringVer,
Sprite baseTexture, Sprite lightTexture) {
super(ringCorner, ringHor, ringVer);
this.baseTexture = baseTexture;
this.lightTexture = lightTexture;
}
@Override
protected void addInnerCube(Direction facing, BlockState state, CraftingCubeModelData modelData, CubeBuilder builder, float x1,
float y1, float z1, float x2, float y2, float z2) {
builder.setTexture(this.baseTexture);
builder.addCube(x1, y1, z1, x2, y2, z2);
boolean powered = state.get(AbstractCraftingUnitBlock.POWERED);
builder.setRenderFullBright(powered);
builder.setTexture(this.lightTexture);
builder.addCube(x1, y1, z1, x2, y2, z2);
}
}
@@ -0,0 +1,104 @@
/*
* 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 appeng.tile.crafting.CraftingCubeModelData;
import net.minecraft.block.BlockState;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;
import appeng.api.util.AEColor;
import appeng.block.crafting.CraftingMonitorBlock;
import appeng.client.render.cablebus.CubeBuilder;
import appeng.tile.crafting.CraftingMonitorModelData;
/**
* The baked model for the crafting monitor. Please note that this model doesn't
* handle the item being displayed. That is handled by a TESR. Instead, this
* model adds 3 layered light textures using the [dark|medium|bright] color
* variants of the attached bus color. The textures are full-bright if the cube
* is powered.
*/
public class MonitorBakedModel extends CraftingCubeBakedModel {
private final Sprite chassisTexture;
private final Sprite baseTexture;
private final Sprite lightDarkTexture;
private final Sprite lightMediumTexture;
private final Sprite lightBrightTexture;
MonitorBakedModel(Sprite ringCorner, Sprite ringHor, Sprite ringVer,
Sprite chassisTexture, Sprite baseTexture, Sprite lightDarkTexture,
Sprite lightMediumTexture, Sprite lightBrightTexture) {
super(ringCorner, ringHor, ringVer);
this.chassisTexture = chassisTexture;
this.baseTexture = baseTexture;
this.lightDarkTexture = lightDarkTexture;
this.lightMediumTexture = lightMediumTexture;
this.lightBrightTexture = lightBrightTexture;
}
@Override
protected void addInnerCube(Direction side, BlockState state, CraftingCubeModelData modelData, CubeBuilder builder, float x1,
float y1, float z1, float x2, float y2, float z2) {
Direction forward = modelData.getForward();
// For sides other than the front, use the chassis texture
if (side != forward) {
builder.setTexture(this.chassisTexture);
builder.addCube(x1, y1, z1, x2, y2, z2);
return;
}
builder.setTexture(this.baseTexture);
builder.addCube(x1, y1, z1, x2, y2, z2);
// Now add the three layered light textures
AEColor color = getColor(modelData);
boolean powered = state.get(CraftingMonitorBlock.POWERED);
builder.setRenderFullBright(powered);
builder.setColorRGB(color.whiteVariant);
builder.setTexture(this.lightBrightTexture);
builder.addCube(x1, y1, z1, x2, y2, z2);
builder.setColorRGB(color.mediumVariant);
builder.setTexture(this.lightMediumTexture);
builder.addCube(x1, y1, z1, x2, y2, z2);
builder.setColorRGB(color.blackVariant);
builder.setTexture(this.lightDarkTexture);
builder.addCube(x1, y1, z1, x2, y2, z2);
}
private static AEColor getColor(CraftingCubeModelData modelData) {
if (modelData instanceof CraftingMonitorModelData) {
return ((CraftingMonitorModelData) modelData).getColor();
}
return AEColor.TRANSPARENT;
}
}
@@ -0,0 +1,48 @@
/*
* 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 appeng.tile.crafting.CraftingCubeModelData;
import net.minecraft.block.BlockState;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;
import appeng.client.render.cablebus.CubeBuilder;
/**
* A simple crafting unit model that uses an un-lit texture for the inner block.
*/
class UnitBakedModel extends CraftingCubeBakedModel {
private final Sprite unitTexture;
UnitBakedModel(Sprite ringCorner, Sprite ringHor, Sprite ringVer,
Sprite unitTexture) {
super(ringCorner, ringHor, ringVer);
this.unitTexture = unitTexture;
}
@Override
protected void addInnerCube(Direction facing, BlockState state, CraftingCubeModelData modelData, CubeBuilder builder, float x1,
float y1, float z1, float x2, float y2, float z2) {
builder.setTexture(this.unitTexture);
builder.addCube(x1, y1, z1, x2, y2, z2);
}
}
@@ -0,0 +1,147 @@
package appeng.client.render.model;
import appeng.api.implementations.items.IBiometricCard;
import appeng.api.util.AEColor;
import appeng.client.render.cablebus.CubeBuilder;
import com.mojang.authlib.GameProfile;
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.minecraft.block.BlockState;
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.Sprite;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockRenderView;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
class BiometricCardBakedModel implements BakedModel, FabricBakedModel {
private final BakedModel baseModel;
private final Sprite texture;
BiometricCardBakedModel(BakedModel baseModel, Sprite texture) {
this.baseModel = baseModel;
this.texture = texture;
}
@Override
public boolean isVanillaAdapter() {
return false;
}
@Override
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
// Not intended as a block
}
@Override
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
context.fallbackConsumer().accept(this.baseModel);
// Get the player's name hash from the card
int hash = getHash(stack);
emitColorCode(context.getEmitter(), hash);
}
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand) {
return this.baseModel.getQuads(state, side, rand);
}
private void emitColorCode(QuadEmitter emitter, int hash) {
CubeBuilder builder = new CubeBuilder(emitter);
builder.setTexture(this.texture);
AEColor col = AEColor.values()[Math.abs(3 + hash) % AEColor.values().length];
if (hash == 0) {
col = AEColor.BLACK;
}
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 6; y++) {
final boolean isLit;
// This makes the border always use the darker color
if (x == 0 || y == 0 || x == 7 || y == 5) {
isLit = false;
} else {
isLit = (hash & (1 << x)) != 0 || (hash & (1 << y)) != 0;
}
if (isLit) {
builder.setColorRGB(col.mediumVariant);
} else {
final float scale = 0.3f / 255.0f;
builder.setColorRGB(((col.blackVariant >> 16) & 0xff) * scale,
((col.blackVariant >> 8) & 0xff) * scale, (col.blackVariant & 0xff) * scale);
}
builder.addCube(4 + x, 6 + y, 7.5f, 4 + x + 1, 6 + y + 1, 8.5f);
}
}
}
@Override
public boolean useAmbientOcclusion() {
return this.baseModel.useAmbientOcclusion();
}
@Override
public boolean hasDepth() {
return this.baseModel.hasDepth();
}
@Override
public boolean isSideLit() {
return false; // This is an item model
}
@Override
public boolean isBuiltin() {
return this.baseModel.isBuiltin();
}
@Override
public Sprite getSprite() {
return this.baseModel.getSprite();
}
@Override
public ModelTransformation getTransformation() {
return this.baseModel.getTransformation();
}
@Override
public ModelOverrideList getOverrides() {
return ModelOverrideList.EMPTY;
}
private static int getHash(ItemStack stack) {
String username = "";
if (stack.getItem() instanceof IBiometricCard) {
final GameProfile gp = ((IBiometricCard) stack.getItem()).getProfile(stack);
if (gp != null) {
if (gp.getId() != null) {
username = gp.getId().toString();
} else {
username = gp.getName();
}
}
}
return !username.isEmpty() ? username.hashCode() : 0;
}
}
@@ -0,0 +1,49 @@
package appeng.client.render.model;
import appeng.client.render.BasicUnbakedModel;
import appeng.core.AppEng;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* Model wrapper for the biometric card item model, which combines a base card
* layer with a "visual hash" of the player name
*/
public class BiometricCardModel implements BasicUnbakedModel {
public static final Identifier MODEL_BASE = new Identifier(AppEng.MOD_ID, "item/biometric_card_base");
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "item/biometric_card_hash"));
@Override
public Collection<Identifier> getModelDependencies() {
return Collections.singleton(MODEL_BASE);
}
@Override
public Stream<SpriteIdentifier> getAdditionalTextures() {
return Stream.of(TEXTURE);
}
@Nullable
@Override
public BakedModel bake(ModelLoader loader, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) {
Sprite texture = textureGetter.apply(TEXTURE);
BakedModel baseModel = loader.bake(MODEL_BASE, rotationContainer);
return new BiometricCardBakedModel(baseModel, texture);
}
}
@@ -0,0 +1,83 @@
package appeng.client.render.model;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Random;
import javax.annotation.Nullable;
import appeng.mixins.BakedQuadAccessor;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.texture.Sprite;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.util.math.Direction;
/**
* This baked model will take the generated item model for the colored color applicator,
* and associate tint indices with the added layers that correspond to the light/medium/dark
* variants of the {@link appeng.api.util.AEColor}.
* <p>
* Using the color provider registered in {@link appeng.items.tools.powered.ColorApplicatorItemRendering},
* this results in the right color being multiplied with the corresponding layer.
*/
class ColorApplicatorBakedModel extends ForwardingBakedModel {
private final EnumMap<Direction, List<BakedQuad>> quadsBySide;
private final List<BakedQuad> generalQuads;
ColorApplicatorBakedModel(BakedModel baseModel, Sprite texDark,
Sprite texMedium, Sprite texBright) {
this.wrapped = baseModel;
// Put the tint indices in... Since this is an item model, we are ignoring rand
this.generalQuads = this.fixQuadTint(null, texDark, texMedium, texBright);
this.quadsBySide = new EnumMap<>(Direction.class);
for (Direction facing : Direction.values()) {
this.quadsBySide.put(facing, this.fixQuadTint(facing, texDark, texMedium, texBright));
}
}
private Sprite getSprite(BakedQuad quad) {
return ((BakedQuadAccessor) quad).getSprite();
}
private List<BakedQuad> fixQuadTint(Direction facing, Sprite texDark, Sprite texMedium,
Sprite texBright) {
List<BakedQuad> quads = this.wrapped.getQuads(null, facing, new Random(0));
List<BakedQuad> result = new ArrayList<>(quads.size());
for (BakedQuad quad : quads) {
int tint;
if (getSprite(quad) == texDark) {
tint = 1;
} else if (getSprite(quad) == texMedium) {
tint = 2;
} else if (getSprite(quad) == texBright) {
tint = 3;
} else {
result.add(quad);
continue;
}
BakedQuad newQuad = new BakedQuad(quad.getVertexData(), tint, quad.getFace(), getSprite(quad),
quad.hasShade());
result.add(newQuad);
}
return result;
}
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand) {
if (side == null) {
return this.generalQuads;
}
return this.quadsBySide.get(side);
}
}
@@ -0,0 +1,56 @@
package appeng.client.render.model;
import appeng.client.render.BasicUnbakedModel;
import appeng.core.AppEng;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* A color applicator uses the base model, and extends it with additional layers
* that are colored according to the selected color of the applicator.
*/
public class ColorApplicatorModel implements BasicUnbakedModel {
private static final Identifier MODEL_BASE = new Identifier(AppEng.MOD_ID,
"item/color_applicator_colored");
private static final SpriteIdentifier TEXTURE_DARK = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "item/color_applicator_tip_dark"));
private static final SpriteIdentifier TEXTURE_MEDIUM = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "item/color_applicator_tip_medium"));
private static final SpriteIdentifier TEXTURE_BRIGHT = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "item/color_applicator_tip_bright"));
@Override
public Collection<Identifier> getModelDependencies() {
return Collections.singleton(MODEL_BASE);
}
@Override
public Stream<SpriteIdentifier> getAdditionalTextures() {
return Stream.of(TEXTURE_DARK, TEXTURE_MEDIUM, TEXTURE_DARK);
}
@Nullable
@Override
public BakedModel bake(ModelLoader loader, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) {
BakedModel baseModel = loader.bake(MODEL_BASE, rotationContainer);
Sprite texDark = textureGetter.apply(TEXTURE_DARK);
Sprite texMedium = textureGetter.apply(TEXTURE_MEDIUM);
Sprite texBright = textureGetter.apply(TEXTURE_BRIGHT);
return new ColorApplicatorBakedModel(baseModel, texDark, texMedium, texBright);
}
}
@@ -0,0 +1,301 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.client.render.model;
import appeng.decorative.solid.GlassState;
import appeng.decorative.solid.QuartzGlassBlock;
import com.google.common.base.Strings;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
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.minecraft.block.BlockState;
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.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.client.util.math.Vector3f;
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.util.math.MathHelper;
import net.minecraft.world.BlockRenderView;
import net.minecraft.world.BlockView;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.IntStream;
class GlassBakedModel implements BakedModel, FabricBakedModel {
private static final byte[][][] OFFSETS = generateOffsets();
// Alternating textures based on position
static final SpriteIdentifier TEXTURE_A = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier("appliedenergistics2:block/glass/quartz_glass_a"));
static final SpriteIdentifier TEXTURE_B = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier("appliedenergistics2:block/glass/quartz_glass_b"));
static final SpriteIdentifier TEXTURE_C = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier("appliedenergistics2:block/glass/quartz_glass_c"));
static final SpriteIdentifier TEXTURE_D = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier("appliedenergistics2:block/glass/quartz_glass_d"));
// Frame texture
static final SpriteIdentifier[] TEXTURES_FRAME = generateTexturesFrame();
private final RenderMaterial material = RendererAccess.INSTANCE.getRenderer().materialFinder()
.disableDiffuse(0, true)
.disableAo(0, true)
.disableColorIndex(0, true)
.blendMode(0, BlendMode.TRANSLUCENT)
.find();
// Generates the required textures for the frame
private static SpriteIdentifier[] generateTexturesFrame() {
return IntStream.range(1, 16).mapToObj(Integer::toBinaryString).map(s -> Strings.padStart(s, 4, '0'))
.map(s -> new Identifier("appliedenergistics2:block/glass/quartz_glass_frame" + s))
.map(rl -> new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX, rl)).toArray(SpriteIdentifier[]::new);
}
private final Sprite[] glassTextures;
private final Sprite[] frameTextures;
public GlassBakedModel(Function<SpriteIdentifier, Sprite> bakedTextureGetter) {
this.glassTextures = new Sprite[] { bakedTextureGetter.apply(TEXTURE_A),
bakedTextureGetter.apply(TEXTURE_B), bakedTextureGetter.apply(TEXTURE_C),
bakedTextureGetter.apply(TEXTURE_D) };
// The first frame texture would be empty, so we simply leave it set to null
// here
this.frameTextures = new Sprite[16];
for (int i = 0; i < TEXTURES_FRAME.length; i++) {
this.frameTextures[1 + i] = bakedTextureGetter.apply(TEXTURES_FRAME[i]);
}
}
@Override
public boolean isVanillaAdapter() {
return false;
}
@Override
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
final GlassState glassState = getGlassState(blockView, pos);
// TODO: This could just use the Random instance we're given...
final int cx = Math.abs(glassState.getX() % 10);
final int cy = Math.abs(glassState.getY() % 10);
final int cz = Math.abs(glassState.getZ() % 10);
int u = OFFSETS[cx][cy][cz] % 4;
int v = OFFSETS[9 - cx][9 - cy][9 - cz] % 4;
int texIdx = Math.abs((OFFSETS[cx][cy][cz] + (glassState.getX() + glassState.getY() + glassState.getZ())) % 4);
if (texIdx < 2) {
u /= 2;
v /= 2;
}
final Sprite glassTexture = this.glassTextures[texIdx];
QuadEmitter emitter = context.getEmitter();
// Render the glass side
for (Direction side : Direction.values()) {
final List<Vector3f> corners = RenderHelper.getFaceCorners(side);
this.emitQuad(emitter, side, corners, glassTexture, u, v);
/*
* This needs some explanation: The bit-field contains 4-bits, one for each
* direction that a frame may be drawn. Converted to a number, the bit-field is
* then used as an index into the list of frame textures, which have been
* created in such a way that their filenames indicate, in which directions they
* contain borders. i.e. bitmask = 0101 means a border should be drawn up and
* down (in terms of u,v space). Converted to a number, this bitmask is 5. So
* the texture at index 5 is used. That texture had "0101" in its filename to
* indicate this.
*/
final int edgeBitmask = makeBitmask(glassState, side);
final Sprite sideSprite = this.frameTextures[edgeBitmask];
if (sideSprite != null) {
this.emitQuad(emitter, side, corners, sideSprite, 0, 0);
}
}
}
@Override
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
}
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction face, Random random) {
return Collections.emptyList();
}
@Override
public ModelTransformation getTransformation() {
return ModelTransformation.NONE;
}
@Override
public boolean isSideLit() {
return false; // Irrelvant because not used as item model
}
/**
* Creates the bitmask that indicates, in which directions (in terms of u,v
* space) a border should be drawn.
*/
private static int makeBitmask(GlassState state, Direction side) {
switch (side) {
case DOWN:
return makeBitmask(state, Direction.SOUTH, Direction.EAST, Direction.NORTH, Direction.WEST);
case UP:
return makeBitmask(state, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.EAST);
case NORTH:
return makeBitmask(state, Direction.UP, Direction.WEST, Direction.DOWN, Direction.EAST);
case SOUTH:
return makeBitmask(state, Direction.UP, Direction.EAST, Direction.DOWN, Direction.WEST);
case WEST:
return makeBitmask(state, Direction.UP, Direction.SOUTH, Direction.DOWN, Direction.NORTH);
case EAST:
return makeBitmask(state, Direction.UP, Direction.NORTH, Direction.DOWN, Direction.SOUTH);
default:
throw new IllegalArgumentException("Unsupported side!");
}
}
private static int makeBitmask(GlassState state, Direction up, Direction right, Direction down, Direction left) {
int bitmask = 0;
if (!state.isFlushWith(up)) {
bitmask |= 1;
}
if (!state.isFlushWith(right)) {
bitmask |= 2;
}
if (!state.isFlushWith(down)) {
bitmask |= 4;
}
if (!state.isFlushWith(left)) {
bitmask |= 8;
}
return bitmask;
}
private void emitQuad(QuadEmitter emitter, Direction side, List<Vector3f> corners, Sprite sprite, float uOffset,
float vOffset) {
this.emitQuad(emitter, side, corners.get(0), corners.get(1), corners.get(2), corners.get(3), sprite, uOffset,
vOffset);
}
private void emitQuad(QuadEmitter emitter, Direction side, Vector3f c1, Vector3f c2, Vector3f c3, Vector3f c4, Sprite sprite,
float uOffset, float vOffset) {
// Apply the u,v shift.
// This mirrors the logic from OffsetIcon from 1.7
float u1 = sprite.getFrameU(MathHelper.clamp(0 - uOffset, 0, 16));
float u2 = sprite.getFrameU(MathHelper.clamp(16 - uOffset, 0, 16));
float v1 = sprite.getFrameV(MathHelper.clamp(0 - vOffset, 0, 16));
float v2 = sprite.getFrameV(MathHelper.clamp(16 - vOffset, 0, 16));
emitter.nominalFace(side);
emitter.cullFace(side);
emitter.material(material);
emitter.pos(0, c1).sprite(0, 0, u1, v1);
emitter.pos(1, c2).sprite(1, 0, u1, v2);
emitter.pos(2, c3).sprite(2, 0, u2, v2);
emitter.pos(3, c4).sprite(3, 0, u2, v1);
emitter.spriteColor(0, -1, -1, -1, -1);
emitter.emit();
}
@Override
public ModelOverrideList getOverrides() {
return ModelOverrideList.EMPTY;
}
@Override
public boolean useAmbientOcclusion() {
return false;
}
@Override
public boolean hasDepth() {
return false;
}
@Override
public boolean isBuiltin() {
return false;
}
@Override
public Sprite getSprite() {
return this.frameTextures[this.frameTextures.length - 1];
}
private static byte[][][] generateOffsets() {
final Random r = new Random(924);
final byte[][][] offset = new byte[10][10][10];
for (int x = 0; x < 10; x++) {
for (int y = 0; y < 10; y++) {
r.nextBytes(offset[x][y]);
}
}
return offset;
}
@Nonnull
private static GlassState getGlassState(BlockRenderView world, BlockPos pos) {
EnumSet<Direction> flushWith = EnumSet.noneOf(Direction.class);
// Test every direction for another glass block
for (Direction facing : Direction.values()) {
if (isGlassBlock(world, pos, facing)) {
flushWith.add(facing);
}
}
return new GlassState(pos.getX(), pos.getY(), pos.getZ(), flushWith);
}
private static boolean isGlassBlock(BlockView world, BlockPos pos, Direction facing) {
return world.getBlockState(pos.offset(facing)).getBlock() instanceof QuartzGlassBlock;
}
}
@@ -0,0 +1,53 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.client.render.model;
import appeng.client.render.BasicUnbakedModel;
import com.google.common.collect.ImmutableSet;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier;
import javax.annotation.Nullable;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* Model class for the connected texture glass model.
*/
public class GlassModel implements BasicUnbakedModel {
@Nullable
@Override
public BakedModel bake(ModelLoader loader, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) {
return new GlassBakedModel(textureGetter);
}
@Override
public Stream<SpriteIdentifier> getAdditionalTextures() {
return ImmutableSet
.<SpriteIdentifier>builder().add(GlassBakedModel.TEXTURE_A, GlassBakedModel.TEXTURE_B,
GlassBakedModel.TEXTURE_C, GlassBakedModel.TEXTURE_D)
.add(GlassBakedModel.TEXTURES_FRAME).build().stream();
}
}
@@ -0,0 +1,63 @@
package appeng.client.render.model;
import appeng.api.implementations.items.IMemoryCard;
import appeng.api.util.AEColor;
import appeng.client.render.cablebus.CubeBuilder;
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack;
import java.util.Random;
import java.util.function.Supplier;
class MemoryCardBakedModel extends ForwardingBakedModel implements FabricBakedModel {
private static final AEColor[] DEFAULT_COLOR_CODE = new AEColor[] { AEColor.TRANSPARENT, AEColor.TRANSPARENT,
AEColor.TRANSPARENT, AEColor.TRANSPARENT, AEColor.TRANSPARENT, AEColor.TRANSPARENT, AEColor.TRANSPARENT,
AEColor.TRANSPARENT, };
private final Sprite texture;
public MemoryCardBakedModel(BakedModel baseModel, Sprite texture) {
this.wrapped = baseModel;
this.texture = texture;
}
@Override
public boolean isVanillaAdapter() {
return false;
}
@Override
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
context.fallbackConsumer().accept(wrapped);
AEColor[] colorCode = getColorCode(stack);
CubeBuilder builder = new CubeBuilder(context.getEmitter());
builder.setTexture(this.texture);
for (int x = 0; x < 4; x++) {
for (int y = 0; y < 2; y++) {
final AEColor color = colorCode[x + y * 4];
builder.setColorRGB(color.mediumVariant);
builder.addCube(7 + x, 8 + (1 - y), 7.5f, 7 + x + 1, 8 + (1 - y) + 1, 8.5f);
}
}
}
private static AEColor[] getColorCode(ItemStack stack) {
if (stack.getItem() instanceof IMemoryCard) {
final IMemoryCard memoryCard = (IMemoryCard) stack.getItem();
return memoryCard.getColorCode(stack);
}
return DEFAULT_COLOR_CODE;
}
}
@@ -0,0 +1,48 @@
package appeng.client.render.model;
import appeng.client.render.BasicUnbakedModel;
import appeng.core.AppEng;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* Model wrapper for the memory card item model, which combines a base card
* layer with a "visual hash" of the part/tile.
*/
public class MemoryCardModel implements BasicUnbakedModel {
public static final Identifier MODEL_BASE = new Identifier(AppEng.MOD_ID, "item/memory_card_base");
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "item/memory_card_hash"));
@Override
public Collection<Identifier> getModelDependencies() {
return Collections.singleton(MODEL_BASE);
}
@Nullable
@Override
public BakedModel bake(ModelLoader loader, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) {
Sprite texture = textureGetter.apply(TEXTURE);
BakedModel baseModel = loader.bake(MODEL_BASE, rotationContainer);
return new MemoryCardBakedModel(baseModel, texture);
}
@Override
public Stream<SpriteIdentifier> getAdditionalTextures() {
return Stream.of(TEXTURE);
}
}
@@ -0,0 +1,89 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.client.render.model;
import java.util.EnumMap;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
// TODO: Investigate use of CubeBuilder instead
final class RenderHelper {
private static EnumMap<Direction, List<Vector3f>> cornersForFacing = generateCornersForFacings();
private RenderHelper() {
}
static List<Vector3f> getFaceCorners(Direction side) {
return cornersForFacing.get(side);
}
private static EnumMap<Direction, List<Vector3f>> generateCornersForFacings() {
EnumMap<Direction, List<Vector3f>> result = new EnumMap<>(Direction.class);
for (Direction facing : Direction.values()) {
List<Vector3f> corners;
float offset = (facing.getDirection() == Direction.AxisDirection.NEGATIVE) ? 0 : 1;
switch (facing.getAxis()) {
default:
case X:
corners = Lists.newArrayList(new Vector3f(offset, 1, 1), new Vector3f(offset, 0, 1),
new Vector3f(offset, 0, 0), new Vector3f(offset, 1, 0));
break;
case Y:
corners = Lists.newArrayList(new Vector3f(1, offset, 1), new Vector3f(1, offset, 0),
new Vector3f(0, offset, 0), new Vector3f(0, offset, 1));
break;
case Z:
corners = Lists.newArrayList(new Vector3f(0, 1, offset), new Vector3f(0, 0, offset),
new Vector3f(1, 0, offset), new Vector3f(1, 1, offset));
break;
}
if (facing.getDirection() == Direction.AxisDirection.NEGATIVE) {
corners = Lists.reverse(corners);
}
result.put(facing, ImmutableList.copyOf(corners));
}
return result;
}
private static Vec3d adjust(Vec3d vec, Direction.Axis axis, double delta) {
switch (axis) {
default:
case X:
return new Vec3d(vec.x + delta, vec.y, vec.z);
case Y:
return new Vec3d(vec.x, vec.y + delta, vec.z);
case Z:
return new Vec3d(vec.x, vec.y, vec.z + delta);
}
}
}
@@ -20,10 +20,6 @@ package appeng.client.render.model;
import appeng.hooks.CompassManager;
import appeng.hooks.CompassResult;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
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.QuadView;
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.block.BlockState;
@@ -47,7 +43,6 @@ import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -85,30 +80,18 @@ public class SkyCompassBakedModel implements BakedModel, FabricBakedModel {
@Override
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
MeshBuilder mb = RendererAccess.INSTANCE.getRenderer().meshBuilder();
mb.getEmitter().square(Direction.UP, 0, 0, 1, 1, 0).emit();
Mesh build = mb.build();
context.meshConsumer().accept(build);
float rotation = getAnimatedRotation(pos, false);
emitQuads(context, rotation);
// Pre-compute the quad count to avoid list resizes
context.fallbackConsumer().accept(this.base);
}
@Override
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
// This is used to render a compass pointing in a specific direction when being
// held in hand
emitQuads(context, this.fallbackRotation);
}
private void emitQuads(RenderContext context, float rotation) {
// Pre-compute the quad count to avoid list resizes
context.fallbackConsumer().accept(this.base);
context.fallbackConsumer().accept(base);
// This is used to render a compass pointing in a specific direction when being held in hand
// Set up the rotation around the Y-axis for the pointer
context.pushTransform(quad -> {
Quaternion quaternion = new Quaternion(0, rotation, 0, false);
Quaternion quaternion = new Quaternion(0, this.fallbackRotation, 0, false);
Vector3f pos = new Vector3f();
for (int i = 0; i < 4; i++) {
quad.copyPos(i, pos);
@@ -43,7 +43,7 @@ public class ItemRenderable<T extends BlockEntity> implements Renderable<T> {
if (pair != null && pair.getLeft() != null) {
matrixStack.push();
if (pair.getRight() != null) {
pair.getRight().apply(true, matrixStack); // FIXME: check left handed
pair.getRight().apply(false, matrixStack); // FIXME: check left handed
}
MinecraftClient.getInstance().getItemRenderer().renderItem(pair.getLeft(),
ModelTransformation.Mode.GROUND, combinedLight, combinedOverlay, matrixStack, buffers);
@@ -67,7 +67,6 @@ public class SkyCompassTESR extends BlockEntityRenderer<SkyCompassBlockEntity> {
return;
}
BakedModel baseModel = skyCompassModel.getBase();
BakedModel pointerModel = skyCompassModel.getPointer();
Direction forward = te.getForward();
@@ -78,17 +77,14 @@ public class SkyCompassTESR extends BlockEntityRenderer<SkyCompassBlockEntity> {
if (forward == Direction.UP || forward == Direction.DOWN) {
up = Direction.NORTH;
}
// Flip forward/up for rendering, the base model is facing up without any
// rotation
ms.push();
VertexConsumer buffer = buffers.getBuffer(RenderLayer.getSolid());
modelRenderer.render(te.getWorld(), model, blockState, te.getPos(), ms, buffer, false, new Random(), 42L, combinedOverlayIn);
// modelRenderer.render(ms.peek(), buffer, null, baseModel, 1, 1, 1, combinedLightIn, combinedOverlayIn);
float rotation = getRotation(te);
ms.translate(0.5D, 0.5D, 0.5D);
// Flip forward/up for rendering, the base model
// is facing up without any rotation
FacingToRotation.get(up, forward).push(ms);
ms.multiply(new Quaternion(0, rotation, 0, false));
ms.translate(-0.5D, -0.5D, -0.5D);
@@ -43,6 +43,8 @@ import static appeng.block.crafting.AbstractCraftingUnitBlock.CraftingUnitType;
import appeng.bootstrap.components.IInitComponent;
import appeng.bootstrap.definitions.TileEntityDefinition;
import appeng.client.render.crafting.CraftingCubeRendering;
import appeng.client.render.crafting.CraftingMonitorTESR;
import appeng.client.render.crafting.MonitorBakedModel;
import appeng.client.render.model.AutoRotatingBakedModel;
import appeng.client.render.spatial.SpatialPylonRendering;
import appeng.client.render.tesr.ChestTileEntityRenderer;
@@ -465,7 +467,7 @@ public final class ApiBlocks implements IBlocks {
@Environment(EnvType.CLIENT)
@Override
public void customize(TileEntityRendering<CraftingMonitorBlockEntity> rendering) {
// FIXME FABRIC rendering.tileEntityRenderer(CraftingMonitorTESR::new);
rendering.tileEntityRenderer(CraftingMonitorTESR::new);
}
}).build())
.rendering(new BlockRenderingCustomizer() {
@@ -475,9 +477,9 @@ public final class ApiBlocks implements IBlocks {
rendering.renderType(RenderLayer.getCutout());
rendering.modelCustomizer((path, model) -> {
// The formed model handles rotations itself, the unformed one does not
// FIXME FABRIC if (model instanceof MonitorBakedModel) {
// FIXME FABRIC return model;
// FIXME FABRIC }
if (model instanceof MonitorBakedModel) {
return model;
}
return new AutoRotatingBakedModel(model);
});
}
@@ -42,7 +42,7 @@ public final class BlockToolDispenseItemBehavior extends ItemDispenserBehavior {
if (w instanceof ServerWorld) {
ItemUsageContext context = new AutomaticItemPlacementContext(w, dispenser.getBlockPos().offset(direction),
direction, dispensedItem, direction.getOpposite());
tm.onItemUse(context);
tm.useOnBlock(context);
}
}
return dispensedItem;
+1 -1
View File
@@ -23,6 +23,6 @@ import net.minecraft.util.ActionResult;
public interface IBlockTool {
// Workaround for dispenser logic.
ActionResult onItemUse(ItemUsageContext itemUseContext);
ActionResult useOnBlock(ItemUsageContext itemUseContext);
}
@@ -0,0 +1,48 @@
package appeng.hooks;
import appeng.items.misc.EncodedPatternItem;
import appeng.mixins.ItemRendererAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.item.ItemStack;
public final class ItemRendererHooks {
// Prevents recursion in the hook below
private static final ThreadLocal<ItemStack> OVERRIDING_FOR = new ThreadLocal<>();
private ItemRendererHooks() {
}
/**
* This hook will exchange the rendered item model for encoded patterns to the item being crafted by them
* if shift is held.
*/
public static boolean onRenderGuiItemModel(ItemRenderer renderer, ItemStack stack, int x, int y, BakedModel model) {
if (stack.getItem() instanceof EncodedPatternItem && OVERRIDING_FOR.get() != stack) {
boolean shiftHeld = Screen.hasShiftDown();
ClientWorld world = MinecraftClient.getInstance().world;
if (shiftHeld && world != null) {
EncodedPatternItem iep = (EncodedPatternItem) stack.getItem();
ItemStack output = iep.getOutput(world, stack);
if (!output.isEmpty()) {
BakedModel realModel = MinecraftClient.getInstance().getItemRenderer().getModels()
.getModel(output);
ItemRendererAccessor self = (ItemRendererAccessor) renderer;
OVERRIDING_FOR.set(stack);
try {
self.callRenderGuiItemModel(stack, x, y, realModel);
} finally {
OVERRIDING_FOR.remove();
}
return true;
}
}
}
return false;
}
}
@@ -35,7 +35,6 @@ import appeng.api.util.DimensionalCoord;
import appeng.block.networking.CableBusBlock;
import appeng.block.paint.PaintSplotchesBlock;
import appeng.core.AEConfig;
import appeng.core.Api;
import appeng.core.AppEng;
import appeng.core.localization.GuiText;
import appeng.helpers.IMouseWheelItem;
@@ -118,7 +117,7 @@ public class ColorApplicatorItem extends AEBasePoweredItem
}
@Override
public ActionResult onItemUse(ItemUsageContext context) {
public ActionResult useOnBlock(ItemUsageContext context) {
World w = context.getWorld();
BlockPos pos = context.getBlockPos();
ItemStack is = context.getStack();
@@ -219,7 +219,7 @@ public class EntropyManipulatorItem extends AEBasePoweredItem implements IBlockT
if (state.getMaterial() == Material.LAVA || state.getMaterial() == Material.WATER) {
if (Platform.hasPermissions(new DimensionalCoord(w, pos), p)) {
ItemUsageContext context = new ItemUsageContext(p, hand, target);
this.onItemUse(context);
this.useOnBlock(context);
}
}
}
@@ -228,7 +228,7 @@ public class EntropyManipulatorItem extends AEBasePoweredItem implements IBlockT
}
@Override
public ActionResult onItemUse(ItemUsageContext context) {
public ActionResult useOnBlock(ItemUsageContext context) {
World w = context.getWorld();
ItemStack item = context.getStack();
BlockPos pos = context.getBlockPos();
@@ -0,0 +1,14 @@
package appeng.mixins;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.texture.Sprite;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(BakedQuad.class)
public interface BakedQuadAccessor {
@Accessor
Sprite getSprite();
}
@@ -0,0 +1,15 @@
package appeng.mixins;
import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(ItemRenderer.class)
public interface ItemRendererAccessor {
@Invoker
void callRenderGuiItemModel(ItemStack stack, int x, int y, BakedModel model);
}
@@ -0,0 +1,26 @@
package appeng.mixins;
import appeng.hooks.ItemRendererHooks;
import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* This mixin specifically targets rendering of items in the user interface to allow us to
* customize _only_ the UI representation of an item, and none of the others (held items, in-world, etc.)
*/
@Mixin(ItemRenderer.class)
public abstract class RenderEncodedPatternMixin {
@Inject(method = "renderGuiItemModel", at = @At("HEAD"), cancellable = true)
protected void renderGuiItemModel(ItemStack stack, int x, int y, BakedModel model, CallbackInfo ci) {
if (ItemRendererHooks.onRenderGuiItemModel((ItemRenderer) (Object) this, stack, x, y, model)) {
ci.cancel();
}
}
}
@@ -19,6 +19,9 @@
package appeng.tile.inventory;
import alexiil.mc.lib.attributes.Simulation;
import alexiil.mc.lib.attributes.item.FixedItemInv;
import alexiil.mc.lib.attributes.item.LimitedFixedItemInv;
import alexiil.mc.lib.attributes.item.compat.FixedInventoryVanillaWrapper;
import alexiil.mc.lib.attributes.item.filter.ConstantItemFilter;
import alexiil.mc.lib.attributes.item.filter.ItemFilter;
import alexiil.mc.lib.attributes.item.impl.DirectFixedItemInv;
@@ -61,6 +64,29 @@ public class AppEngInternalInventory extends DirectFixedItemInv implements Itera
this.filter = filter;
}
public FixedItemInv createFiltered(IAEItemFilter filter) {
LimitedFixedItemInv limitedFixedInv = this.createLimitedFixedInv();
for (int i = 0; i < getSlotCount(); i++) {
final int slot = i;
limitedFixedInv.getRule(i)
.filterExtracts(stack -> filter.allowExtract(this, slot, stack.getCount()));
limitedFixedInv.getRule(i)
.filterInserts(stack -> {
if (stack.isEmpty()) {
ItemStack current = this.getInvStack(slot);
if (current.isEmpty()) {
return true; // Replacing empty with empty... okay
}
return filter.allowExtract(this, slot, current.getCount());
} else {
return filter.allowInsert(this, slot, stack);
}
});
}
return limitedFixedInv;
}
@Override
public int getMaxAmount(int slot, ItemStack stack) {
return Math.min(maxStack[slot], super.getMaxAmount(slot, stack));
@@ -61,7 +61,9 @@ public class ChargerBlockEntity extends AENetworkPowerBlockEntity implements ICr
private static final int POWER_THRESHOLD = POWER_MAXIMUM_AMOUNT - 1;
private static final int POWER_PER_CRANK_TURN = 160;
private final AppEngInternalInventory inv = new AppEngInternalInventory(this, 1, 1, new ChargerInvFilter());
private final AppEngInternalInventory inv = new AppEngInternalInventory(this, 1, 1);
private final FixedItemInv externalInv = inv.createFiltered(new ChargerInvFilter());
public ChargerBlockEntity(BlockEntityType<?> tileEntityTypeIn) {
super(tileEntityTypeIn);
@@ -134,7 +136,7 @@ public class ChargerBlockEntity extends AENetworkPowerBlockEntity implements ICr
@Override
public FixedItemInv getInternalInventory() {
return this.inv;
return externalInv;
}
@Override
@@ -18,6 +18,7 @@
package appeng.tile.misc;
import appeng.client.render.model.AEModelData;
import net.minecraft.block.entity.BlockEntityType;
import appeng.tile.AEBaseBlockEntity;
@@ -28,4 +29,9 @@ public class SkyCompassBlockEntity extends AEBaseBlockEntity {
super(tileEntityTypeIn);
}
@Override
public Object getRenderAttachmentData() {
// For compasses, the forward/up are flipped
return new AEModelData(getForward(), getUp());
}
}
@@ -39,7 +39,7 @@ import net.minecraft.util.math.Direction;
*/
public abstract class InventoryAdaptor implements Iterable<ItemSlot> {
public static InventoryAdaptor getAdaptor(final BlockEntity te, final Direction d) {
FixedItemInv inv = ItemAttributes.FIXED_INV.get(te.getWorld(), te.getPos().offset(d), SearchOptions.inDirection(d.getOpposite()));
FixedItemInv inv = ItemAttributes.FIXED_INV.get(te.getWorld(), te.getPos(), SearchOptions.inDirection(d.getOpposite()));
if (inv == ItemAttributes.FIXED_INV.defaultValue) {
return null;