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,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);
}
}