Reorganized Imports/Packages
This commit is contained in:
parent
cb9459f176
commit
3ee10482ab
721 changed files with 34873 additions and 33558 deletions
45
src/main/java/org/betterx/bclib/client/BCLibClient.java
Normal file
45
src/main/java/org/betterx/bclib/client/BCLibClient.java
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
162
src/main/java/org/betterx/bclib/client/models/ModelsHelper.java
Normal file
162
src/main/java/org/betterx/bclib/client/models/ModelsHelper.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
294
src/main/java/org/betterx/bclib/client/models/OBJBlockModel.java
Normal file
294
src/main/java/org/betterx/bclib/client/models/OBJBlockModel.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package org.betterx.bclib.client.render;
|
||||
|
||||
public enum BCLRenderLayer {
|
||||
CUTOUT, TRANSLUCENT
|
||||
}
|
|
@ -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"))
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue