Reorganized Imports/Packages

This commit is contained in:
Frank 2022-05-18 23:56:23 +02:00
parent cb9459f176
commit 3ee10482ab
721 changed files with 34873 additions and 33558 deletions

View file

@ -0,0 +1,45 @@
package org.betterx.bclib.client;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.resources.ResourceLocation;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.model.*;
import org.betterx.bclib.api.ModIntegrationAPI;
import org.betterx.bclib.api.PostInitAPI;
import org.betterx.bclib.api.dataexchange.DataExchangeAPI;
import org.betterx.bclib.client.models.CustomModelBakery;
import org.betterx.bclib.registry.BaseBlockEntityRenders;
import org.jetbrains.annotations.Nullable;
public class BCLibClient implements ClientModInitializer, ModelResourceProvider, ModelVariantProvider {
public static CustomModelBakery modelBakery;
@Override
public void onInitializeClient() {
ModIntegrationAPI.registerAll();
BaseBlockEntityRenders.register();
DataExchangeAPI.prepareClientside();
PostInitAPI.postInit(true);
modelBakery = new CustomModelBakery();
ModelLoadingRegistry.INSTANCE.registerResourceProvider(rm -> this);
ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> this);
}
@Override
public @Nullable UnbakedModel loadModelResource(ResourceLocation resourceId,
ModelProviderContext context) throws ModelProviderException {
return modelBakery.getBlockModel(resourceId);
}
@Override
public @Nullable UnbakedModel loadModelVariant(ModelResourceLocation modelId,
ModelProviderContext context) throws ModelProviderException {
return modelId.getVariant().equals("inventory")
? modelBakery.getItemModel(modelId)
: modelBakery.getBlockModel(modelId);
}
}

View file

@ -0,0 +1,112 @@
package org.betterx.bclib.client.models;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.model.geom.PartPose;
import net.minecraft.client.model.geom.builders.*;
public class BaseChestBlockModel {
public final ModelPart partA;
public final ModelPart partC;
public final ModelPart partB;
public final ModelPart partRightA;
public final ModelPart partRightC;
public final ModelPart partRightB;
public final ModelPart partLeftA;
public final ModelPart partLeftC;
public final ModelPart partLeftB;
public static LayerDefinition getTexturedModelData() {
MeshDefinition modelData = new MeshDefinition();
PartDefinition modelPartData = modelData.getRoot();
CubeDeformation deformation_partC = new CubeDeformation(0.0f);
modelPartData.addOrReplaceChild(
"partC",
CubeListBuilder.create().texOffs(0, 19).addBox(1.0f, 0.0f, 1.0f, 14.0f, 9.0f, 14.0f, deformation_partC),
PartPose.ZERO
);
CubeDeformation deformation_partA = new CubeDeformation(0.0f);
modelPartData.addOrReplaceChild(
"partA",
CubeListBuilder.create().texOffs(0, 0).addBox(1.0f, 0.0f, 0.0f, 14.0f, 5.0f, 14.0f, deformation_partA),
PartPose.offset(0.0f, 9.0f, 1.0f)
);
CubeDeformation deformation_partB = new CubeDeformation(0.0f);
modelPartData.addOrReplaceChild(
"partB",
CubeListBuilder.create().texOffs(0, 0).addBox(7.0f, -1.0f, 15.0f, 2.0f, 4.0f, 1.0f, deformation_partB),
PartPose.offset(0.0f, 8.0f, 0.0f)
);
CubeDeformation deformation_partRightC = new CubeDeformation(0.0f);
modelPartData.addOrReplaceChild(
"partRightC",
CubeListBuilder.create()
.texOffs(0, 19)
.addBox(1.0f, 0.0f, 1.0f, 15.0f, 9.0f, 14.0f, deformation_partRightC),
PartPose.ZERO
);
CubeDeformation deformation_partRightA = new CubeDeformation(0.0f);
modelPartData.addOrReplaceChild(
"partRightA",
CubeListBuilder.create()
.texOffs(0, 0)
.addBox(1.0f, 0.0f, 0.0f, 15.0f, 5.0f, 14.0f, deformation_partRightA),
PartPose.offset(0.0f, 9.0f, 1.0f)
);
CubeDeformation deformation_partRightB = new CubeDeformation(0.0f);
PartDefinition partRightB = modelPartData.addOrReplaceChild(
"partRightB",
CubeListBuilder.create()
.texOffs(0, 0)
.addBox(15.0f, -1.0f, 15.0f, 1.0f, 4.0f, 1.0f, deformation_partRightB),
PartPose.offset(0.0f, 8.0f, 0.0f)
);
CubeDeformation deformation_partLeftC = new CubeDeformation(0.0f);
modelPartData.addOrReplaceChild(
"partLeftC",
CubeListBuilder.create()
.texOffs(0, 19)
.addBox(0.0f, 0.0f, 1.0f, 15.0f, 9.0f, 14.0f, deformation_partLeftC),
PartPose.ZERO
);
CubeDeformation deformation_partLeftA = new CubeDeformation(0.0f);
modelPartData.addOrReplaceChild(
"partLeftA",
CubeListBuilder.create()
.texOffs(0, 0)
.addBox(0.0f, 0.0f, 0.0f, 15.0f, 5.0f, 14.0f, deformation_partLeftA),
PartPose.offset(0.0f, 9.0f, 1.0f)
);
CubeDeformation deformation_partLeftB = new CubeDeformation(0.0f);
modelPartData.addOrReplaceChild(
"partLeftB",
CubeListBuilder.create()
.texOffs(0, 0)
.addBox(0.0f, -1.0f, 15.0f, 1.0f, 4.0f, 1.0f, deformation_partLeftB),
PartPose.offset(0.0f, 8.0f, 0.0f)
);
return LayerDefinition.create(modelData, 64, 64);
}
public BaseChestBlockModel(ModelPart modelPart) {
super();
partC = modelPart.getChild("partC");
partA = modelPart.getChild("partA");
partB = modelPart.getChild("partB");
partRightC = modelPart.getChild("partRightC");
partRightA = modelPart.getChild("partRightA");
partRightB = modelPart.getChild("partRightB");
partLeftC = modelPart.getChild("partLeftC");
partLeftA = modelPart.getChild("partLeftA");
partLeftB = modelPart.getChild("partLeftB");
}
}

View file

@ -0,0 +1,62 @@
package org.betterx.bclib.client.models;
import net.minecraft.resources.ResourceLocation;
import org.betterx.bclib.BCLib;
public class BasePatterns {
//Block Models
public final static ResourceLocation BLOCK_EMPTY = BCLib.makeID("patterns/block/empty.json");
public final static ResourceLocation BLOCK_BASE = BCLib.makeID("patterns/block/block.json");
public final static ResourceLocation BLOCK_SIDED = BCLib.makeID("patterns/block/block_sided.json");
public final static ResourceLocation BLOCK_BOTTOM_TOP = BCLib.makeID("patterns/block/block_bottom_top.json");
public final static ResourceLocation BLOCK_SLAB = BCLib.makeID("patterns/block/slab.json");
public final static ResourceLocation BLOCK_STAIR = BCLib.makeID("patterns/block/stairs.json");
public final static ResourceLocation BLOCK_STAIR_INNER = BCLib.makeID("patterns/block/stairs_inner.json");
public final static ResourceLocation BLOCK_STAIR_OUTER = BCLib.makeID("patterns/block/stairs_outer.json");
public final static ResourceLocation BLOCK_WALL_POST = BCLib.makeID("patterns/block/wall_post.json");
public final static ResourceLocation BLOCK_WALL_SIDE = BCLib.makeID("patterns/block/wall_side.json");
public final static ResourceLocation BLOCK_WALL_SIDE_TALL = BCLib.makeID("patterns/block/wall_side_tall.json");
public final static ResourceLocation BLOCK_FENCE_POST = BCLib.makeID("patterns/block/fence_post.json");
public final static ResourceLocation BLOCK_FENCE_SIDE = BCLib.makeID("patterns/block/fence_side.json");
public final static ResourceLocation BLOCK_BUTTON = BCLib.makeID("patterns/block/button.json");
public final static ResourceLocation BLOCK_BUTTON_PRESSED = BCLib.makeID("patterns/block/button_pressed.json");
public final static ResourceLocation BLOCK_PILLAR = BCLib.makeID("patterns/block/pillar.json");
public final static ResourceLocation BLOCK_PLATE_UP = BCLib.makeID("patterns/block/pressure_plate_up.json");
public final static ResourceLocation BLOCK_PLATE_DOWN = BCLib.makeID("patterns/block/pressure_plate_down.json");
public final static ResourceLocation BLOCK_DOOR_TOP = BCLib.makeID("patterns/block/door_top.json");
public final static ResourceLocation BLOCK_DOOR_TOP_HINGE = BCLib.makeID("patterns/block/door_top_hinge.json");
public final static ResourceLocation BLOCK_DOOR_BOTTOM = BCLib.makeID("patterns/block/door_bottom.json");
public final static ResourceLocation BLOCK_DOOR_BOTTOM_HINGE = BCLib.makeID("patterns/block/door_bottom_hinge.json");
public final static ResourceLocation BLOCK_CROSS = BCLib.makeID("patterns/block/cross.json");
public final static ResourceLocation BLOCK_CROSS_SHADED = BCLib.makeID("patterns/block/cross_shaded.json");
public final static ResourceLocation BLOCK_GATE_CLOSED = BCLib.makeID("patterns/block/fence_gate_closed.json");
public final static ResourceLocation BLOCK_GATE_CLOSED_WALL = BCLib.makeID("patterns/block/wall_gate_closed.json");
public final static ResourceLocation BLOCK_GATE_OPEN = BCLib.makeID("patterns/block/fence_gate_open.json");
public final static ResourceLocation BLOCK_GATE_OPEN_WALL = BCLib.makeID("patterns/block/wall_gate_open.json");
public final static ResourceLocation BLOCK_TRAPDOOR = BCLib.makeID("patterns/block/trapdoor.json");
public final static ResourceLocation BLOCK_LADDER = BCLib.makeID("patterns/block/ladder.json");
public final static ResourceLocation BLOCK_BARREL_OPEN = BCLib.makeID("patterns/block/barrel_open.json");
public final static ResourceLocation BLOCK_BOOKSHELF = BCLib.makeID("patterns/block/bookshelf.json");
public final static ResourceLocation BLOCK_COMPOSTER = BCLib.makeID("patterns/block/composter.json");
public final static ResourceLocation BLOCK_COLORED = BCLib.makeID("patterns/block/block_colored.json");
public final static ResourceLocation BLOCK_BARS_POST = BCLib.makeID("patterns/block/bars_post.json");
public final static ResourceLocation BLOCK_BARS_SIDE = BCLib.makeID("patterns/block/bars_side.json");
public final static ResourceLocation BLOCK_ANVIL = BCLib.makeID("patterns/block/anvil.json");
public final static ResourceLocation BLOCK_CHAIN = BCLib.makeID("patterns/block/chain.json");
public final static ResourceLocation BLOCK_FURNACE = BCLib.makeID("patterns/block/furnace.json");
public final static ResourceLocation BLOCK_FURNACE_LIT = BCLib.makeID("patterns/block/furnace_glow.json");
public final static ResourceLocation BLOCK_TOP_SIDE_BOTTOM = BCLib.makeID("patterns/block/top_side_bottom.json");
public final static ResourceLocation BLOCK_PATH = BCLib.makeID("patterns/block/path.json");
//Item Models
public final static ResourceLocation ITEM_WALL = BCLib.makeID("patterns/item/pattern_wall.json");
public final static ResourceLocation ITEM_FENCE = BCLib.makeID("patterns/item/pattern_fence.json");
public final static ResourceLocation ITEM_BUTTON = BCLib.makeID("patterns/item/pattern_button.json");
public final static ResourceLocation ITEM_CHEST = BCLib.makeID("patterns/item/pattern_chest.json");
public final static ResourceLocation ITEM_BLOCK = BCLib.makeID("patterns/item/pattern_block_item.json");
public final static ResourceLocation ITEM_GENERATED = BCLib.makeID("patterns/item/pattern_item_generated.json");
public final static ResourceLocation ITEM_HANDHELD = BCLib.makeID("patterns/item/pattern_item_handheld.json");
public final static ResourceLocation ITEM_SPAWN_EGG = BCLib.makeID("patterns/item/pattern_item_spawn_egg.json");
}

View file

@ -0,0 +1,139 @@
package org.betterx.bclib.client.models;
import net.minecraft.client.renderer.block.BlockModelShaper;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.client.renderer.block.model.multipart.MultiPart;
import net.minecraft.client.resources.model.Material;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.mojang.datafixers.util.Pair;
import org.betterx.bclib.api.ModIntegrationAPI;
import org.betterx.bclib.client.render.EmissiveTextureInfo;
import org.betterx.bclib.interfaces.BlockModelProvider;
import org.betterx.bclib.interfaces.ItemModelProvider;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class CustomModelBakery {
private final Map<ResourceLocation, UnbakedModel> models = Maps.newConcurrentMap();
public UnbakedModel getBlockModel(ResourceLocation location) {
return models.get(location);
}
public UnbakedModel getItemModel(ResourceLocation location) {
ResourceLocation storageID = new ResourceLocation(location.getNamespace(),
"models/item/" + location.getPath() + ".json");
return models.get(location);
}
public void loadCustomModels(ResourceManager resourceManager) {
Registry.BLOCK.stream().parallel().filter(block -> block instanceof BlockModelProvider).forEach(block -> {
ResourceLocation blockID = Registry.BLOCK.getKey(block);
ResourceLocation storageID = new ResourceLocation(blockID.getNamespace(),
"blockstates/" + blockID.getPath() + ".json");
if (resourceManager.getResource(storageID).isEmpty()) {
addBlockModel(blockID, block);
}
storageID = new ResourceLocation(blockID.getNamespace(), "models/item/" + blockID.getPath() + ".json");
if (resourceManager.getResource(storageID).isEmpty()) {
addItemModel(blockID, (ItemModelProvider) block);
}
});
Registry.ITEM.stream().parallel().filter(item -> item instanceof ItemModelProvider).forEach(item -> {
ResourceLocation registryID = Registry.ITEM.getKey(item);
ResourceLocation storageID = new ResourceLocation(registryID.getNamespace(),
"models/item/" + registryID.getPath() + ".json");
if (resourceManager.getResource(storageID).isEmpty()) {
addItemModel(registryID, (ItemModelProvider) item);
}
});
}
private void addBlockModel(ResourceLocation blockID, Block block) {
BlockModelProvider provider = (BlockModelProvider) block;
ImmutableList<BlockState> states = block.getStateDefinition().getPossibleStates();
BlockState defaultState = block.defaultBlockState();
ResourceLocation defaultStateID = BlockModelShaper.stateToModelLocation(blockID, defaultState);
UnbakedModel defaultModel = provider.getModelVariant(defaultStateID, defaultState, models);
if (defaultModel instanceof MultiPart) {
states.forEach(blockState -> {
ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, blockState);
models.put(stateID, defaultModel);
});
} else {
states.forEach(blockState -> {
ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, blockState);
UnbakedModel model = stateID.equals(defaultStateID)
? defaultModel
: provider.getModelVariant(stateID, blockState, models);
models.put(stateID, model);
});
}
}
private void addItemModel(ResourceLocation itemID, ItemModelProvider provider) {
ModelResourceLocation modelLocation = new ModelResourceLocation(itemID.getNamespace(),
itemID.getPath(),
"inventory");
if (models.containsKey(modelLocation)) {
return;
}
BlockModel model = provider.getItemModel(modelLocation);
models.put(modelLocation, model);
}
public static void loadEmissiveModels(Map<ResourceLocation, UnbakedModel> unbakedCache) {
if (!ModIntegrationAPI.hasCanvas()) {
return;
}
Map<ResourceLocation, UnbakedModel> cacheCopy = new HashMap<>(unbakedCache);
Set<Pair<String, String>> strings = Sets.newConcurrentHashSet();
Registry.BLOCK.keySet().forEach(blockID -> {
Block block = Registry.BLOCK.get(blockID);
ImmutableList<BlockState> states = block.getStateDefinition().getPossibleStates();
boolean addBlock = false;
for (BlockState state : states) {
ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, state);
UnbakedModel model = cacheCopy.get(stateID);
if (model == null) {
continue;
}
Collection<Material> materials = model.getMaterials(cacheCopy::get, strings);
if (materials == null) {
continue;
}
for (Material material : materials) {
if (EmissiveTextureInfo.isEmissiveTexture(material.texture())) {
addBlock = true;
break;
}
}
if (addBlock) {
break;
}
}
if (addBlock) {
EmissiveTextureInfo.addBlock(blockID);
}
});
}
}

View file

@ -0,0 +1,24 @@
package org.betterx.bclib.client.models;
import net.minecraft.resources.ResourceLocation;
import com.google.common.collect.Sets;
import java.util.Set;
public class CustomModelData {
private static final Set<ResourceLocation> TRANSPARENT_EMISSION = Sets.newConcurrentHashSet();
public static void clear() {
TRANSPARENT_EMISSION.clear();
}
public static void addTransparent(ResourceLocation blockID) {
TRANSPARENT_EMISSION.add(blockID);
}
public static boolean isTransparentEmissive(ResourceLocation rawLocation) {
String name = rawLocation.getPath().replace("materialmaps/block/", "").replace(".json", "");
return TRANSPARENT_EMISSION.contains(new ResourceLocation(rawLocation.getNamespace(), name));
}
}

View file

@ -0,0 +1,162 @@
package org.betterx.bclib.client.models;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.client.renderer.block.model.MultiVariant;
import net.minecraft.client.renderer.block.model.Variant;
import net.minecraft.client.renderer.block.model.multipart.Condition;
import net.minecraft.client.renderer.block.model.multipart.MultiPart;
import net.minecraft.client.renderer.block.model.multipart.Selector;
import net.minecraft.client.resources.model.BlockModelRotation;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import com.google.common.collect.Lists;
import com.mojang.math.Transformation;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
@Environment(EnvType.CLIENT)
public class ModelsHelper {
public static BlockModel fromPattern(Optional<String> pattern) {
return pattern.map(BlockModel::fromString).orElse(null);
}
public static BlockModel createItemModel(ResourceLocation resourceLocation) {
return fromPattern(PatternsHelper.createItemGenerated(resourceLocation));
}
public static BlockModel createHandheldItem(ResourceLocation resourceLocation) {
return fromPattern(PatternsHelper.createItemHandheld(resourceLocation));
}
public static BlockModel createBlockItem(ResourceLocation resourceLocation) {
Optional<String> pattern = PatternsHelper.createJson(BasePatterns.ITEM_BLOCK, resourceLocation);
return fromPattern(pattern);
}
public static BlockModel createBlockEmpty(ResourceLocation resourceLocation) {
Optional<String> pattern = PatternsHelper.createJson(BasePatterns.BLOCK_EMPTY, resourceLocation);
return fromPattern(pattern);
}
public static MultiVariant createMultiVariant(ResourceLocation resourceLocation,
Transformation transform,
boolean uvLock) {
Variant variant = new Variant(resourceLocation, transform, uvLock, 1);
return new MultiVariant(Lists.newArrayList(variant));
}
public static MultiVariant createBlockSimple(ResourceLocation resourceLocation) {
return createMultiVariant(resourceLocation, Transformation.identity(), false);
}
public static MultiVariant createFacingModel(ResourceLocation resourceLocation,
Direction facing,
boolean uvLock,
boolean inverted) {
if (inverted) {
facing = facing.getOpposite();
}
BlockModelRotation rotation = BlockModelRotation.by(0, (int) facing.toYRot());
return createMultiVariant(resourceLocation, rotation.getRotation(), uvLock);
}
public static MultiVariant createRotatedModel(ResourceLocation resourceLocation, Direction.Axis axis) {
BlockModelRotation rotation = BlockModelRotation.X0_Y0;
switch (axis) {
case X:
rotation = BlockModelRotation.X90_Y90;
break;
case Z:
rotation = BlockModelRotation.X90_Y0;
break;
default:
break;
}
return createMultiVariant(resourceLocation, rotation.getRotation(), false);
}
public static MultiVariant createRandomTopModel(ResourceLocation resourceLocation) {
return new MultiVariant(Lists.newArrayList(
new Variant(resourceLocation, Transformation.identity(), false, 1),
new Variant(resourceLocation, BlockModelRotation.X0_Y90.getRotation(), false, 1),
new Variant(resourceLocation, BlockModelRotation.X0_Y180.getRotation(), false, 1),
new Variant(resourceLocation, BlockModelRotation.X0_Y270.getRotation(), false, 1)
));
}
public static class MultiPartBuilder {
//private final static MultiPartBuilder BUILDER = new MultiPartBuilder();
public static MultiPartBuilder create(StateDefinition<Block, BlockState> stateDefinition) {
// BUILDER.stateDefinition = stateDefinition;
//BUILDER.modelParts.clear();
// return BUILDER;
return new MultiPartBuilder(stateDefinition);
}
private final List<ModelPart> modelParts = Lists.newArrayList();
private final StateDefinition<Block, BlockState> stateDefinition;
private MultiPartBuilder(StateDefinition<Block, BlockState> stateDefinition) {
this.stateDefinition = stateDefinition;
}
public ModelPart part(ResourceLocation modelId) {
ModelPart part = new ModelPart(modelId);
return part;
}
public MultiPart build() {
if (modelParts.size() > 0) {
List<Selector> selectors = Lists.newArrayList();
modelParts.forEach(modelPart -> {
MultiVariant variant = createMultiVariant(modelPart.modelId, modelPart.transform, modelPart.uvLock);
selectors.add(new Selector(modelPart.condition, variant));
});
modelParts.clear();
return new MultiPart(stateDefinition, selectors);
}
throw new IllegalStateException("At least one model part need to be created.");
}
public class ModelPart {
private final ResourceLocation modelId;
private Transformation transform = Transformation.identity();
private Condition condition = Condition.TRUE;
private boolean uvLock = false;
private ModelPart(ResourceLocation modelId) {
this.modelId = modelId;
}
public ModelPart setCondition(Function<BlockState, Boolean> condition) {
this.condition = stateDefinition -> condition::apply;
return this;
}
public ModelPart setTransformation(Transformation transform) {
this.transform = transform;
return this;
}
public ModelPart setUVLock(boolean value) {
this.uvLock = value;
return this;
}
public void add() {
modelParts.add(this);
}
}
}
}

View file

@ -0,0 +1,294 @@
package org.betterx.bclib.client.models;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemOverrides;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.*;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mojang.datafixers.util.Pair;
import com.mojang.math.Vector3f;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.util.BlocksHelper;
import org.betterx.bclib.util.MHelper;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class OBJBlockModel implements UnbakedModel, BakedModel {
private static final Vector3f[] POSITIONS = new Vector3f[]{new Vector3f(), new Vector3f(), new Vector3f()};
protected final Map<Direction, List<UnbakedQuad>> quadsUnbakedMap = Maps.newEnumMap(Direction.class);
protected final Map<Direction, List<BakedQuad>> quadsBakedMap = Maps.newEnumMap(Direction.class);
protected final List<UnbakedQuad> quadsUnbaked = Lists.newArrayList();
protected final List<BakedQuad> quadsBaked = Lists.newArrayList();
protected TextureAtlasSprite[] sprites;
protected ItemTransforms transforms;
protected ItemOverrides overrides;
protected List<Material> materials;
protected boolean useCulling;
protected boolean useShading;
protected byte particleIndex;
public OBJBlockModel(ResourceLocation location,
Vector3f offset,
boolean useCulling,
boolean useShading,
byte particleIndex,
ResourceLocation... textureIDs) {
for (Direction dir : BlocksHelper.DIRECTIONS) {
quadsUnbakedMap.put(dir, Lists.newArrayList());
quadsBakedMap.put(dir, Lists.newArrayList());
}
transforms = ItemTransforms.NO_TRANSFORMS;
overrides = ItemOverrides.EMPTY;
materials = new ArrayList<>(textureIDs.length);
sprites = new TextureAtlasSprite[textureIDs.length];
this.particleIndex = particleIndex;
this.useCulling = useCulling;
this.useShading = useShading;
loadModel(location, offset, (byte) (textureIDs.length - 1));
for (int i = 0; i < textureIDs.length; i++) {
materials.add(new Material(TextureAtlas.LOCATION_BLOCKS, textureIDs[i]));
}
}
// UnbakedModel //
@Override
public Collection<ResourceLocation> getDependencies() {
return Collections.emptyList();
}
@Override
public Collection<Material> getMaterials(Function<ResourceLocation, UnbakedModel> function,
Set<Pair<String, String>> set) {
return materials;
}
@Nullable
@Override
public BakedModel bake(ModelBakery modelBakery,
Function<Material, TextureAtlasSprite> textureGetter,
ModelState modelState,
ResourceLocation resourceLocation) {
for (int i = 0; i < sprites.length; i++) {
sprites[i] = textureGetter.apply(materials.get(i));
}
quadsBaked.clear();
quadsUnbaked.forEach(quad -> quadsBaked.add(quad.bake(sprites, modelState)));
for (Direction dir : BlocksHelper.DIRECTIONS) {
List<UnbakedQuad> unbaked = quadsUnbakedMap.get(dir);
List<BakedQuad> baked = quadsBakedMap.get(dir);
baked.clear();
unbaked.forEach(quad -> baked.add(quad.bake(sprites, modelState)));
}
return this;
}
// Baked Model //
@Override
public List<BakedQuad> getQuads(@Nullable BlockState blockState,
@Nullable Direction direction,
RandomSource random) {
return direction == null ? quadsBaked : quadsBakedMap.get(direction);
}
@Override
public boolean useAmbientOcclusion() {
return true;
}
@Override
public boolean isGui3d() {
return true;
}
@Override
public boolean usesBlockLight() {
return true;
}
@Override
public boolean isCustomRenderer() {
return false;
}
@Override
public TextureAtlasSprite getParticleIcon() {
return sprites[particleIndex];
}
@Override
public ItemTransforms getTransforms() {
return transforms;
}
@Override
public ItemOverrides getOverrides() {
return overrides;
}
private Resource getResource(ResourceLocation location) {
return Minecraft.getInstance().getResourceManager().getResource(location).orElse(null);
}
private void loadModel(ResourceLocation location, Vector3f offset, byte maxIndex) {
Resource resource = getResource(location);
if (resource == null) {
return;
}
InputStream input = null;
try {
input = resource.open();
} catch (IOException e) {
BCLib.LOGGER.error("Unable to load Model", e);
throw new RuntimeException(e);
}
List<Float> vertecies = new ArrayList<>(12);
List<Float> uvs = new ArrayList<>(8);
List<Integer> vertexIndex = new ArrayList<>(4);
List<Integer> uvIndex = new ArrayList<>(4);
byte materialIndex = -1;
try {
InputStreamReader streamReader = new InputStreamReader(input);
BufferedReader reader = new BufferedReader(streamReader);
String string;
while ((string = reader.readLine()) != null) {
if (string.startsWith("usemtl")) {
materialIndex++;
if (materialIndex > maxIndex) {
materialIndex = maxIndex;
}
} else if (string.startsWith("vt")) {
String[] uv = string.split(" ");
uvs.add(Float.parseFloat(uv[1]));
uvs.add(Float.parseFloat(uv[2]));
} else if (string.startsWith("v")) {
String[] vert = string.split(" ");
for (int i = 1; i < 4; i++) {
vertecies.add(Float.parseFloat(vert[i]));
}
} else if (string.startsWith("f")) {
String[] members = string.split(" ");
if (members.length != 5) {
System.out.println("Only quads in OBJ are supported! Model [" + location + "] has n-gons or triangles!");
continue;
}
vertexIndex.clear();
uvIndex.clear();
for (int i = 1; i < members.length; i++) {
String member = members[i];
if (member.contains("/")) {
String[] sub = member.split("/");
vertexIndex.add(Integer.parseInt(sub[0]) - 1); // Vertex
uvIndex.add(Integer.parseInt(sub[1]) - 1); // UV
} else {
vertexIndex.add(Integer.parseInt(member) - 1); // Vertex
}
}
boolean hasUV = !uvIndex.isEmpty();
UnbakedQuad quad = new UnbakedQuad();
for (int i = 0; i < 4; i++) {
int index = vertexIndex.get(i) * 3;
int quadIndex = i * 5;
quad.addData(quadIndex++, vertecies.get(index++) + offset.x()); // X
quad.addData(quadIndex++, vertecies.get(index++) + offset.y()); // Y
quad.addData(quadIndex++, vertecies.get(index) + offset.z()); // Z
if (hasUV) {
index = uvIndex.get(i) * 2;
quad.addData(quadIndex++, uvs.get(index++) * 16F); // U
quad.addData(quadIndex, (1 - uvs.get(index)) * 16F); // V
}
}
quad.setSpriteIndex(materialIndex);
if (useShading) {
Direction dir = getNormalDirection(quad);
quad.setDirection(dir);
quad.setShading(true);
}
if (useCulling) {
Direction dir = getCullingDirection(quad);
if (dir == null) {
quadsUnbaked.add(quad);
} else {
quadsUnbakedMap.get(dir).add(quad);
}
} else {
quadsUnbaked.add(quad);
}
}
}
reader.close();
streamReader.close();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
if (materialIndex < 0) {
quadsUnbaked.forEach(quad -> quad.setSpriteIndex(0));
quadsUnbakedMap.values().forEach(list -> list.forEach(quad -> quad.setSpriteIndex(0)));
}
}
private Direction getNormalDirection(UnbakedQuad quad) {
Vector3f pos = quad.getPos(0, POSITIONS[0]);
Vector3f dirA = quad.getPos(1, POSITIONS[1]);
Vector3f dirB = quad.getPos(2, POSITIONS[2]);
dirA.sub(pos);
dirB.sub(pos);
pos = MHelper.cross(dirA, dirB);
return Direction.getNearest(pos.x(), pos.y(), pos.z());
}
@Nullable
private Direction getCullingDirection(UnbakedQuad quad) {
Direction dir = null;
for (int i = 0; i < 4; i++) {
Vector3f pos = quad.getPos(i, POSITIONS[0]);
if (pos.x() < 1 && pos.x() > 0 && pos.y() < 1 && pos.y() > 0 && pos.z() < 1 && pos.z() > 0) {
return null;
}
Direction newDir = Direction.getNearest(pos.x() - 0.5F, pos.y() - 0.5F, pos.z() - 0.5F);
if (dir == null) {
dir = newDir;
} else if (newDir != dir) {
return null;
}
}
return dir;
}
}

View file

@ -0,0 +1,112 @@
package org.betterx.bclib.client.models;
import net.minecraft.resources.ResourceLocation;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import com.google.common.collect.Lists;
import com.mojang.math.Vector3f;
import java.util.List;
@Environment(EnvType.CLIENT)
public class OBJModelBuilder {
private static final OBJModelBuilder INSTANCE = new OBJModelBuilder();
private final List<ResourceLocation> textures = Lists.newArrayList();
private final Vector3f offset = new Vector3f();
private ResourceLocation modelLocation;
private ResourceLocation particles;
private boolean useCulling;
private boolean useShading;
private OBJModelBuilder() {
}
/**
* Start a new bodel building process, clears data of previous builder.
*
* @return {@link OBJModelBuilder} instance.
*/
public static OBJModelBuilder start(ResourceLocation modelLocation) {
INSTANCE.modelLocation = modelLocation;
INSTANCE.offset.set(0, 0, 0);
INSTANCE.useCulling = true;
INSTANCE.useShading = true;
INSTANCE.particles = null;
INSTANCE.textures.clear();
return INSTANCE;
}
/**
* Add texture to the model. All textures have indexes with same order as in source OBJ model.
*
* @param texture {@link ResourceLocation} texture ID.
* @return this {@link OBJModelBuilder}.
*/
public OBJModelBuilder addTexture(ResourceLocation texture) {
textures.add(texture);
return this;
}
/**
* Culling used to remove block faces if they are on block faces or outside of the block to reduce faces count in rendering.
* Opaque blocks shoud have this as true to reduce geometry issues, block like plants should have this as false.
* Default value is {@code true}.
*
* @param useCulling {@link Boolean}.
* @return this {@link OBJModelBuilder}.
*/
public OBJModelBuilder useCulling(boolean useCulling) {
this.useCulling = useCulling;
return this;
}
/**
* Shading tints block faces in shades of gray to immitate volume in MC rendering.
* Blocks like plants don't have shading, most full opaque blocks - have.
* Default value is {@code true}.
*
* @param useShading {@link Boolean}.
* @return this {@link OBJModelBuilder}.
*/
public OBJModelBuilder useShading(boolean useShading) {
this.useShading = useShading;
return this;
}
/**
* Set particle texture for this model.
* Not required, if texture is not selected the first texture will be used instead of it.
*
* @param texture {@link ResourceLocation} texture ID.
* @return this {@link OBJModelBuilder}.
*/
public OBJModelBuilder setParticlesTexture(ResourceLocation texture) {
this.particles = texture;
return this;
}
public OBJModelBuilder setOffset(float x, float y, float z) {
this.offset.set(x, y, z);
return this;
}
/**
* Builds model from all required data.
*
* @return {@link OBJBlockModel}.
*/
public OBJBlockModel build() {
byte particleIndex = 0;
if (particles != null) {
particleIndex = (byte) textures.indexOf(particles);
if (particleIndex < 0) {
particleIndex = (byte) textures.size();
textures.add(particles);
}
}
ResourceLocation[] sprites = textures.toArray(new ResourceLocation[textures.size()]);
return new OBJBlockModel(modelLocation, offset, useCulling, useShading, particleIndex, sprites);
}
}

View file

@ -0,0 +1,76 @@
package org.betterx.bclib.client.models;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager;
import com.google.common.collect.Maps;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class PatternsHelper {
private static final Map<ResourceLocation, String> JSON_CACHE = Maps.newConcurrentMap();
public static Optional<String> createItemGenerated(ResourceLocation itemId) {
return createJson(BasePatterns.ITEM_GENERATED, itemId);
}
public static Optional<String> createItemHandheld(ResourceLocation itemId) {
return createJson(BasePatterns.ITEM_HANDHELD, itemId);
}
public static Optional<String> createBlockSimple(ResourceLocation blockId) {
return createJson(BasePatterns.BLOCK_BASE, blockId);
}
public static Optional<String> createBlockEmpty(ResourceLocation blockId) {
return createJson(BasePatterns.BLOCK_EMPTY, blockId);
}
public static Optional<String> createBlockPillar(ResourceLocation blockId) {
return createJson(BasePatterns.BLOCK_PILLAR, blockId);
}
public static Optional<String> createBlockBottomTop(ResourceLocation blockId) {
return createJson(BasePatterns.BLOCK_BOTTOM_TOP, blockId);
}
public static Optional<String> createBlockColored(ResourceLocation blockId) {
return createJson(BasePatterns.BLOCK_COLORED, blockId);
}
public static Optional<String> createJson(ResourceLocation patternId, ResourceLocation blockId) {
Map<String, String> textures = Maps.newHashMap();
textures.put("%modid%", blockId.getNamespace());
textures.put("%texture%", blockId.getPath());
return createJson(patternId, textures);
}
public static Optional<String> createJson(ResourceLocation patternId, Map<String, String> textures) {
ResourceManager resourceManager = Minecraft.getInstance().getResourceManager();
Optional<Resource> patternRes = resourceManager.getResource(patternId);
if (patternRes.isEmpty()) return Optional.empty();
try (InputStream input = patternRes.get().open()) {
String json = JSON_CACHE.get(patternId);
if (json == null) {
json = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining());
JSON_CACHE.put(patternId, json);
}
for (Map.Entry<String, String> texture : textures.entrySet()) {
json = json.replace(texture.getKey(), texture.getValue());
}
return Optional.of(json);
} catch (Exception ex) {
return Optional.empty();
}
}
}

View file

@ -0,0 +1,70 @@
package org.betterx.bclib.client.models;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.ModelState;
import net.minecraft.core.Direction;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import com.mojang.math.Matrix4f;
import com.mojang.math.Vector3f;
import com.mojang.math.Vector4f;
@Environment(EnvType.CLIENT)
public class UnbakedQuad {
private static final Vector4f POS = new Vector4f();
private final float[] data = new float[20]; // 4 points with 3 positions and 2 uvs, 4 * (3 + 2)
private Direction dir = Direction.UP;
private boolean useShading = false;
private int spriteIndex;
public void addData(int index, float value) {
data[index] = value;
}
public void setSpriteIndex(int index) {
spriteIndex = index;
}
public void setDirection(Direction dir) {
this.dir = dir;
}
public void setShading(boolean useShading) {
this.useShading = useShading;
}
public Vector3f getPos(int index, Vector3f result) {
int dataIndex = index * 5;
float x = data[dataIndex++];
float y = data[dataIndex++];
float z = data[dataIndex];
result.set(x, y, z);
return result;
}
public BakedQuad bake(TextureAtlasSprite[] sprites, ModelState modelState) {
Matrix4f matrix = modelState.getRotation().getMatrix();
TextureAtlasSprite sprite = sprites[spriteIndex];
int[] vertexData = new int[32];
for (int i = 0; i < 4; i++) {
int index = i << 3;
int dataIndex = i * 5;
float x = data[dataIndex++]; // X
float y = data[dataIndex++]; // Y
float z = data[dataIndex++]; // Z
POS.set(x, y, z, 0);
POS.transform(matrix);
vertexData[index] = Float.floatToIntBits(POS.x()); // X
vertexData[index | 1] = Float.floatToIntBits(POS.y()); // Y
vertexData[index | 2] = Float.floatToIntBits(POS.z()); // Z
vertexData[index | 3] = -1; // Unknown constant
vertexData[index | 4] = Float.floatToIntBits(sprite.getU(data[dataIndex++])); // U
vertexData[index | 5] = Float.floatToIntBits(sprite.getV(data[dataIndex])); // V
}
// vertices, tint index, direction, sprite, shade
return new BakedQuad(vertexData, 0, dir, sprites[spriteIndex], useShading);
}
}

View file

@ -0,0 +1,5 @@
package org.betterx.bclib.client.render;
public enum BCLRenderLayer {
CUTOUT, TRANSLUCENT
}

View file

@ -0,0 +1,186 @@
package org.betterx.bclib.client.render;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.blockentity.BrightnessCombiner;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.DoubleBlockCombiner.NeighborCombineResult;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.ChestType;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import com.google.common.collect.Maps;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Vector3f;
import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
import it.unimi.dsi.fastutil.ints.Int2IntFunction;
import org.betterx.bclib.blockentities.BaseChestBlockEntity;
import org.betterx.bclib.client.models.BaseChestBlockModel;
import java.util.HashMap;
@Environment(EnvType.CLIENT)
public class BaseChestBlockEntityRenderer implements BlockEntityRenderer<BaseChestBlockEntity> {
private static final HashMap<Block, RenderType[]> LAYERS = Maps.newHashMap();
private static final RenderType[] RENDER_TYPES;
private static final int ID_NORMAL = 0;
private static final int ID_LEFT = 1;
private static final int ID_RIGHT = 2;
private final BaseChestBlockModel chestModel;
public BaseChestBlockEntityRenderer(BlockEntityRendererProvider.Context ctx) {
super();
chestModel = new BaseChestBlockModel(BaseChestBlockModel.getTexturedModelData().bakeRoot());
}
public void render(BaseChestBlockEntity entity,
float tickDelta,
PoseStack matrices,
MultiBufferSource vertexConsumers,
int light,
int overlay) {
Level world = entity.getLevel();
boolean worldExists = world != null;
BlockState blockState = worldExists ? entity.getBlockState() : Blocks.CHEST.defaultBlockState()
.setValue(
ChestBlock.FACING,
Direction.SOUTH
);
ChestType chestType = blockState.hasProperty(ChestBlock.TYPE)
? blockState.getValue(ChestBlock.TYPE)
: ChestType.SINGLE;
Block block = blockState.getBlock();
if (block instanceof AbstractChestBlock) {
AbstractChestBlock<?> abstractChestBlock = (AbstractChestBlock<?>) block;
boolean isDouble = chestType != ChestType.SINGLE;
float f = blockState.getValue(ChestBlock.FACING).toYRot();
NeighborCombineResult<? extends ChestBlockEntity> propertySource;
matrices.pushPose();
matrices.translate(0.5D, 0.5D, 0.5D);
matrices.mulPose(Vector3f.YP.rotationDegrees(-f));
matrices.translate(-0.5D, -0.5D, -0.5D);
if (worldExists) {
propertySource = abstractChestBlock.combine(blockState, world, entity.getBlockPos(), true);
} else {
propertySource = DoubleBlockCombiner.Combiner::acceptNone;
}
float pitch = propertySource.apply(ChestBlock.opennessCombiner(entity)).get(
tickDelta);
pitch = 1.0F - pitch;
pitch = 1.0F - pitch * pitch * pitch;
@SuppressWarnings({
"unchecked",
"rawtypes"
}) int blockLight = ((Int2IntFunction) propertySource.apply(new BrightnessCombiner())).applyAsInt(light);
VertexConsumer vertexConsumer = getConsumer(vertexConsumers, block, chestType);
if (isDouble) {
if (chestType == ChestType.LEFT) {
renderParts(
matrices,
vertexConsumer,
chestModel.partLeftA,
chestModel.partLeftB,
chestModel.partLeftC,
pitch,
blockLight,
overlay
);
} else {
renderParts(
matrices,
vertexConsumer,
chestModel.partRightA,
chestModel.partRightB,
chestModel.partRightC,
pitch,
blockLight,
overlay
);
}
} else {
renderParts(
matrices,
vertexConsumer,
chestModel.partA,
chestModel.partB,
chestModel.partC,
pitch,
blockLight,
overlay
);
}
matrices.popPose();
}
}
private void renderParts(PoseStack matrices,
VertexConsumer vertices,
ModelPart modelPart,
ModelPart modelPart2,
ModelPart modelPart3,
float pitch,
int light,
int overlay) {
modelPart.xRot = -(pitch * 1.5707964F);
modelPart2.xRot = modelPart.xRot;
modelPart.render(matrices, vertices, light, overlay);
modelPart2.render(matrices, vertices, light, overlay);
modelPart3.render(matrices, vertices, light, overlay);
}
private static RenderType getChestTexture(ChestType type, RenderType[] layers) {
return switch (type) {
case LEFT -> layers[ID_LEFT];
case RIGHT -> layers[ID_RIGHT];
default -> layers[ID_NORMAL];
};
}
public static VertexConsumer getConsumer(MultiBufferSource provider, Block block, ChestType chestType) {
RenderType[] layers = LAYERS.getOrDefault(block, RENDER_TYPES);
return provider.getBuffer(getChestTexture(chestType, layers));
}
public static void registerRenderLayer(Block block) {
ResourceLocation blockId = Registry.BLOCK.getKey(block);
String modId = blockId.getNamespace();
String path = blockId.getPath();
LAYERS.put(
block,
new RenderType[]{
RenderType.entityCutout(new ResourceLocation(modId, "textures/entity/chest/" + path + ".png")),
RenderType.entityCutout(new ResourceLocation(modId,
"textures/entity/chest/" + path + "_left.png")),
RenderType.entityCutout(new ResourceLocation(modId,
"textures/entity/chest/" + path + "_right.png"))
}
);
}
static {
RENDER_TYPES = new RenderType[]{
RenderType.entityCutout(new ResourceLocation("textures/entity/chest/normal.png")),
RenderType.entityCutout(new ResourceLocation("textures/entity/chest/normal_left.png")),
RenderType.entityCutout(new ResourceLocation("textures/entity/chest/normal_right.png"))
};
}
}

View file

@ -0,0 +1,190 @@
package org.betterx.bclib.client.render;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.model.geom.ModelLayers;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.blockentity.SignRenderer;
import net.minecraft.client.resources.model.Material;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.FormattedCharSequence;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SignBlock;
import net.minecraft.world.level.block.StandingSignBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.WoodType;
import net.minecraft.world.phys.Vec3;
import com.google.common.collect.Maps;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Vector3f;
import org.betterx.bclib.blockentities.BaseSignBlockEntity;
import org.betterx.bclib.blocks.BaseSignBlock;
import java.util.HashMap;
import java.util.List;
public class BaseSignBlockEntityRenderer implements BlockEntityRenderer<BaseSignBlockEntity> {
private static final HashMap<Block, RenderType> RENDER_TYPES = Maps.newHashMap();
private static final int OUTLINE_RENDER_DISTANCE = Mth.square(16);
private static final RenderType RENDER_TYPE;
private final SignRenderer.SignModel model;
private final Font font;
public BaseSignBlockEntityRenderer(BlockEntityRendererProvider.Context ctx) {
super();
this.font = ctx.getFont();
model = new SignRenderer.SignModel(ctx.bakeLayer(ModelLayers.createSignModelName(WoodType.OAK)));
}
public void render(BaseSignBlockEntity signBlockEntity,
float tickDelta,
PoseStack matrixStack,
MultiBufferSource provider,
int light,
int overlay) {
BlockState state = signBlockEntity.getBlockState();
matrixStack.pushPose();
matrixStack.translate(0.5D, 0.5D, 0.5D);
float angle = -((float) (state.getValue(StandingSignBlock.ROTATION) * 360) / 16.0F);
BlockState blockState = signBlockEntity.getBlockState();
if (blockState.getValue(BaseSignBlock.FLOOR)) {
matrixStack.mulPose(Vector3f.YP.rotationDegrees(angle));
model.stick.visible = true;
} else {
matrixStack.mulPose(Vector3f.YP.rotationDegrees(angle + 180));
matrixStack.translate(0.0D, -0.3125D, -0.4375D);
model.stick.visible = false;
}
matrixStack.pushPose();
matrixStack.scale(0.6666667F, -0.6666667F, -0.6666667F);
VertexConsumer vertexConsumer = getConsumer(provider, state.getBlock());
model.root.render(matrixStack, vertexConsumer, light, overlay);
matrixStack.popPose();
matrixStack.translate(0.0D, 0.3333333432674408D, 0.046666666865348816D);
matrixStack.scale(0.010416667F, -0.010416667F, 0.010416667F);
int m = signBlockEntity.getColor().getTextColor();
int n = (int) (NativeImage.getR(m) * 0.4D);
int o = (int) (NativeImage.getG(m) * 0.4D);
int p = (int) (NativeImage.getB(m) * 0.4D);
int q = NativeImage.combine(0, p, o, n);
FormattedCharSequence[] formattedCharSequences = signBlockEntity.getRenderMessages(
Minecraft.getInstance()
.isTextFilteringEnabled(),
(component) -> {
List<FormattedCharSequence> list = this.font.split(component, 90);
return list.isEmpty() ? FormattedCharSequence.EMPTY : list.get(0);
}
);
int drawColor;
boolean drawOutlined;
int drawLight;
if (signBlockEntity.hasGlowingText()) {
drawColor = signBlockEntity.getColor().getTextColor();
drawOutlined = isOutlineVisible(signBlockEntity, drawColor);
drawLight = 15728880;
} else {
drawColor = m;
drawOutlined = false;
drawLight = light;
}
for (int s = 0; s < 4; ++s) {
FormattedCharSequence formattedCharSequence = formattedCharSequences[s];
float t = (float) (-this.font.width(formattedCharSequence) / 2);
if (drawOutlined) {
this.font.drawInBatch8xOutline(
formattedCharSequence,
t,
(float) (s * 10 - 20),
drawColor,
m,
matrixStack.last().pose(),
provider,
drawLight
);
} else {
this.font.drawInBatch(
formattedCharSequence,
t,
(float) (s * 10 - 20),
drawColor,
false,
matrixStack.last().pose(),
provider,
false,
0,
drawLight
);
}
}
matrixStack.popPose();
}
private static boolean isOutlineVisible(BaseSignBlockEntity signBlockEntity, int i) {
if (i == DyeColor.BLACK.getTextColor()) {
return true;
} else {
Minecraft minecraft = Minecraft.getInstance();
LocalPlayer localPlayer = minecraft.player;
if (localPlayer != null && minecraft.options.getCameraType().isFirstPerson() && localPlayer.isScoping()) {
return true;
} else {
Entity entity = minecraft.getCameraEntity();
return entity != null && entity.distanceToSqr(Vec3.atCenterOf(signBlockEntity.getBlockPos())) < (double) OUTLINE_RENDER_DISTANCE;
}
}
}
public static WoodType getSignType(Block block) {
WoodType signType2;
if (block instanceof SignBlock) {
signType2 = ((SignBlock) block).type();
} else {
signType2 = WoodType.OAK;
}
return signType2;
}
public static Material getModelTexture(Block block) {
return Sheets.getSignMaterial(getSignType(block));
}
public static VertexConsumer getConsumer(MultiBufferSource provider, Block block) {
return provider.getBuffer(RENDER_TYPES.getOrDefault(block, RENDER_TYPE));
}
public static void registerRenderLayer(Block block) {
ResourceLocation blockId = Registry.BLOCK.getKey(block);
RenderType layer = RenderType.entitySolid(new ResourceLocation(blockId.getNamespace(),
"textures/entity/sign/" + blockId.getPath() + ".png"));
RENDER_TYPES.put(block, layer);
}
static {
RENDER_TYPE = RenderType.entitySolid(new ResourceLocation("textures/entity/signs/oak.png"));
}
}

View file

@ -0,0 +1,155 @@
package org.betterx.bclib.client.render;
import net.minecraft.client.Camera;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.material.FogType;
import com.mojang.blaze3d.systems.RenderSystem;
import org.betterx.bclib.api.biomes.BiomeAPI;
import org.betterx.bclib.config.Configs;
import org.betterx.bclib.util.BackgroundInfo;
import org.betterx.bclib.util.MHelper;
import org.betterx.bclib.world.biomes.BCLBiome;
public class CustomFogRenderer {
private static final MutableBlockPos LAST_POS = new MutableBlockPos(0, -100, 0);
private static final MutableBlockPos MUT_POS = new MutableBlockPos();
private static final float[] FOG_DENSITY = new float[8];
private static final int GRID_SIZE = 32;
private static float fogStart = 0;
private static float fogEnd = 192;
public static boolean applyFogDensity(Camera camera, float viewDistance, boolean thickFog) {
if (!Configs.CLIENT_CONFIG.renderCustomFog()) {
return false;
}
FogType fogType = camera.getFluidInCamera();
if (fogType != FogType.NONE) {
BackgroundInfo.fogDensity = 1;
return false;
}
Entity entity = camera.getEntity();
if (!isForcedDimension(entity.level) && shouldIgnoreArea(entity.level,
(int) entity.getX(),
(int) entity.getEyeY(),
(int) entity.getZ())) {
BackgroundInfo.fogDensity = 1;
return false;
}
float fog = getFogDensity(entity.level, entity.getX(), entity.getEyeY(), entity.getZ());
BackgroundInfo.fogDensity = fog;
if (thickFog(thickFog, entity.level)) {
fogStart = viewDistance * 0.05F / fog;
fogEnd = Math.min(viewDistance, 192.0F) * 0.5F / fog;
} else {
fogStart = viewDistance * 0.25F / fog; // In vanilla - 0
fogEnd = viewDistance / fog;
}
if (entity instanceof LivingEntity) {
LivingEntity livingEntity = (LivingEntity) entity;
MobEffectInstance effect = livingEntity.getEffect(MobEffects.BLINDNESS);
if (effect != null) {
int duration = effect.getDuration();
if (duration > 20) {
fogStart = 0;
fogEnd *= 0.03F;
BackgroundInfo.blindness = 1;
} else {
float delta = (float) duration / 20F;
BackgroundInfo.blindness = delta;
fogStart = Mth.lerp(delta, fogStart, 0);
fogEnd = Mth.lerp(delta, fogEnd, fogEnd * 0.03F);
}
} else {
BackgroundInfo.blindness = 0;
}
}
RenderSystem.setShaderFogStart(fogStart);
RenderSystem.setShaderFogEnd(fogEnd);
return true;
}
private static boolean thickFog(boolean thickFog, Level level) {
if (!thickFog) {
return false;
}
if (level.dimension() == Level.NETHER) {
return Configs.CLIENT_CONFIG.netherThickFog();
}
return true;
}
private static boolean isForcedDimension(Level level) {
return level.dimension() == Level.END || level.dimension() == Level.NETHER;
}
private static boolean shouldIgnoreArea(Level level, int x, int y, int z) {
for (int i = -8; i <= 8; i += 8) {
for (int j = -8; j <= 8; j += 8) {
if (!shouldIgnore(level, x + i, y, z + j)) {
return false;
}
}
}
return true;
}
private static boolean shouldIgnore(Level level, int x, int y, int z) {
Biome biome = level.getBiome(MUT_POS.set(x, y, z)).value();
return BiomeAPI.getRenderBiome(biome) == BiomeAPI.EMPTY_BIOME;
}
private static float getFogDensityI(Level level, int x, int y, int z) {
Biome biome = level.getBiome(MUT_POS.set(x, y, z)).value();
BCLBiome renderBiome = BiomeAPI.getRenderBiome(biome);
return renderBiome.getFogDensity();
}
private static float getFogDensity(Level level, double x, double y, double z) {
int x1 = MHelper.floor(x / GRID_SIZE) * GRID_SIZE;
int y1 = MHelper.floor(y / GRID_SIZE) * GRID_SIZE;
int z1 = MHelper.floor(z / GRID_SIZE) * GRID_SIZE;
float dx = (float) (x - x1) / GRID_SIZE;
float dy = (float) (y - y1) / GRID_SIZE;
float dz = (float) (z - z1) / GRID_SIZE;
if (LAST_POS.getX() != x1 || LAST_POS.getY() != y1 || LAST_POS.getZ() != z1) {
int x2 = x1 + GRID_SIZE;
int y2 = y1 + GRID_SIZE;
int z2 = z1 + GRID_SIZE;
LAST_POS.set(x1, y1, z1);
FOG_DENSITY[0] = getFogDensityI(level, x1, y1, z1);
FOG_DENSITY[1] = getFogDensityI(level, x2, y1, z1);
FOG_DENSITY[2] = getFogDensityI(level, x1, y2, z1);
FOG_DENSITY[3] = getFogDensityI(level, x2, y2, z1);
FOG_DENSITY[4] = getFogDensityI(level, x1, y1, z2);
FOG_DENSITY[5] = getFogDensityI(level, x2, y1, z2);
FOG_DENSITY[6] = getFogDensityI(level, x1, y2, z2);
FOG_DENSITY[7] = getFogDensityI(level, x2, y2, z2);
}
float a = Mth.lerp(dx, FOG_DENSITY[0], FOG_DENSITY[1]);
float b = Mth.lerp(dx, FOG_DENSITY[2], FOG_DENSITY[3]);
float c = Mth.lerp(dx, FOG_DENSITY[4], FOG_DENSITY[5]);
float d = Mth.lerp(dx, FOG_DENSITY[6], FOG_DENSITY[7]);
a = Mth.lerp(dy, a, b);
b = Mth.lerp(dy, c, d);
return Mth.lerp(dz, a, b);
}
}

View file

@ -0,0 +1,33 @@
package org.betterx.bclib.client.render;
import net.minecraft.resources.ResourceLocation;
import com.google.common.collect.Sets;
import java.util.Set;
public class EmissiveTextureInfo {
private static final Set<ResourceLocation> EMISSIVE_TEXTURES = Sets.newHashSet();
private static final Set<ResourceLocation> EMISSIVE_BLOCKS = Sets.newHashSet();
public static void clear() {
EMISSIVE_TEXTURES.clear();
EMISSIVE_BLOCKS.clear();
}
public static void addTexture(ResourceLocation texture) {
EMISSIVE_TEXTURES.add(texture);
}
public static void addBlock(ResourceLocation blockID) {
EMISSIVE_BLOCKS.add(blockID);
}
public static boolean isEmissiveTexture(ResourceLocation texture) {
return EMISSIVE_TEXTURES.contains(texture);
}
public static boolean isEmissiveBlock(ResourceLocation blockID) {
return EMISSIVE_BLOCKS.contains(blockID);
}
}

View file

@ -0,0 +1,16 @@
package org.betterx.bclib.client.sound;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.level.block.SoundType;
public class BlockSounds {
public static final SoundType TERRAIN_SOUND = new SoundType(
1.0F,
1.0F,
SoundEvents.STONE_BREAK,
SoundEvents.WART_BLOCK_STEP,
SoundEvents.STONE_PLACE,
SoundEvents.STONE_HIT,
SoundEvents.STONE_FALL
);
}