Draw the rest of the fucking owl
This commit is contained in:
parent
a5c0c9c893
commit
7928a9de70
@ -11,3 +11,12 @@
|
||||
Templates is an API for Carpenter's Blocks-like templated blocks. Currently, plain slopes are the only built-in template blocks.
|
||||
|
||||
Template blocks can be placed in the world, then right-clicked with a full-size block to set the textures for the template. Template blocks will inherit light and redstone values from the blocks they're given, or they can have light or redstone output added to any given block by right-clicking the template with glowstone dust or a redstone torch, respectively.
|
||||
|
||||
# quat was here - TODO
|
||||
|
||||
* Schedule chunk rerenders when a player edits a template block :sweat_smile:
|
||||
* Re-generalize the model system (I removed a layer of indirection while rewriting it, so it's just slopes now)
|
||||
* See what I can do about using the vanilla rotation system (`ModelBakeSettings.getRotation`) instead of manually rotating the `Mesh`
|
||||
* A simplification of the mesh system would *definitely* reduce the friction of adding new meshes
|
||||
* Upside-down slopes would be nice...
|
||||
* (if i may): Packages-ish "retexturing" of json blockmodels
|
@ -1,10 +1,12 @@
|
||||
package io.github.cottonmc.templates;
|
||||
|
||||
import io.github.cottonmc.templates.block.SlopeBlock;
|
||||
import io.github.cottonmc.templates.model.SlopeModel;
|
||||
import io.github.cottonmc.templates.model.SlopeUnbakedModel;
|
||||
import io.github.cottonmc.templates.model.TemplateModelVariantProvider;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
||||
import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
public class TemplatesClient implements ClientModInitializer {
|
||||
@ -13,6 +15,8 @@ public class TemplatesClient implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> provider);
|
||||
provider.registerTemplateModels(Templates.SLOPE, Templates.SLOPE.getDefaultState().with(SlopeBlock.FACING, Direction.SOUTH), SlopeModel::new);
|
||||
provider.registerTemplateModels2(Templates.SLOPE, Templates.SLOPE.getDefaultState().with(SlopeBlock.FACING, Direction.SOUTH), SlopeUnbakedModel::new);
|
||||
|
||||
BlockRenderLayerMap.INSTANCE.putBlock(Templates.SLOPE, RenderLayer.getCutout());
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package io.github.cottonmc.templates.block;
|
||||
import io.github.cottonmc.templates.Templates;
|
||||
import io.github.cottonmc.templates.block.entity.TemplateEntity;
|
||||
import io.github.cottonmc.templates.util.StateContainer;
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
@ -35,7 +34,9 @@ public abstract class TemplateBlock extends Block implements BlockEntityProvider
|
||||
}
|
||||
|
||||
public static Settings configureSettings(Settings s) {
|
||||
return s.luminance(state -> ((TemplateBlock) state.getBlock()).luminance(state));
|
||||
return s
|
||||
.luminance(state -> ((TemplateBlock) state.getBlock()).luminance(state))
|
||||
.nonOpaque();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -46,7 +47,7 @@ public abstract class TemplateBlock extends Block implements BlockEntityProvider
|
||||
if(stack.getItem() instanceof BlockItem) {
|
||||
Block block = ((BlockItem) stack.getItem()).getBlock();
|
||||
if(block == Blocks.REDSTONE_TORCH) {
|
||||
be.addRedstone();
|
||||
be.setRedstone(true);
|
||||
if(!player.isCreative()) stack.decrement(1);
|
||||
}
|
||||
ItemPlacementContext ctx = new ItemPlacementContext(new ItemUsageContext(player, hand, hit));
|
||||
@ -60,7 +61,7 @@ public abstract class TemplateBlock extends Block implements BlockEntityProvider
|
||||
if(!player.isCreative()) stack.decrement(1);
|
||||
}
|
||||
} else if(stack.getItem() == Items.GLOWSTONE_DUST) {
|
||||
be.addGlowstone();
|
||||
be.setGlowstone(true);
|
||||
if(!player.isCreative()) stack.decrement(1);
|
||||
}
|
||||
return ActionResult.SUCCESS;
|
||||
|
@ -10,10 +10,14 @@ import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtHelper;
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class TemplateEntity extends BlockEntity implements RenderAttachmentBlockEntity {
|
||||
protected BlockState renderedState = Blocks.AIR.getDefaultState();
|
||||
@ -26,15 +30,6 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach
|
||||
this.baseBlock = baseBlock;
|
||||
}
|
||||
|
||||
public BlockState getRenderedState() {
|
||||
return renderedState;
|
||||
}
|
||||
|
||||
public void setRenderedState(BlockState state) {
|
||||
this.renderedState = state;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNbt(NbtCompound tag) {
|
||||
super.readNbt(tag);
|
||||
@ -57,17 +52,15 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach
|
||||
tag.putBoolean("Redstone", redstone);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public void markDirty() {
|
||||
super.markDirty();
|
||||
if(world != null && !world.isClient) {
|
||||
for(ServerPlayerEntity player : PlayerLookup.tracking(this)) {
|
||||
player.networkHandler.sendPacket(this.toUpdatePacket());
|
||||
}
|
||||
world.updateNeighborsAlways(pos.offset(Direction.UP), baseBlock);
|
||||
BlockState state = world.getBlockState(pos);
|
||||
world.updateListeners(pos, state, state, 1);
|
||||
}
|
||||
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
||||
return BlockEntityUpdateS2CPacket.create(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtCompound toInitialChunkDataNbt() {
|
||||
return createNbt();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -75,21 +68,44 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach
|
||||
return renderedState;
|
||||
}
|
||||
|
||||
public void change() {
|
||||
markDirty();
|
||||
if(world != null && !world.isClient) {
|
||||
//for(ServerPlayerEntity player : PlayerLookup.tracking(this)) player.networkHandler.sendPacket(this.toUpdatePacket());
|
||||
|
||||
//TODO is this needed
|
||||
//world.updateNeighborsAlways(pos.offset(Direction.UP), baseBlock);
|
||||
world.updateListeners(pos, getCachedState(), getCachedState(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockState getRenderedState() {
|
||||
return renderedState;
|
||||
}
|
||||
|
||||
public void setRenderedState(BlockState newState) {
|
||||
BlockState lastState = renderedState;
|
||||
renderedState = newState;
|
||||
if(!Objects.equals(lastState, newState)) change();
|
||||
}
|
||||
|
||||
public boolean hasGlowstone() {
|
||||
return glowstone;
|
||||
}
|
||||
|
||||
public void addGlowstone() {
|
||||
glowstone = true;
|
||||
markDirty();
|
||||
public void setGlowstone(boolean newGlowstone) {
|
||||
boolean lastGlowstone = glowstone;
|
||||
glowstone = newGlowstone;
|
||||
if(lastGlowstone != newGlowstone) change();
|
||||
}
|
||||
|
||||
public boolean hasRedstone() {
|
||||
return redstone;
|
||||
}
|
||||
|
||||
public void addRedstone() {
|
||||
redstone = true;
|
||||
markDirty();
|
||||
public void setRedstone(boolean newRedstone) {
|
||||
boolean lastRedstone = redstone;
|
||||
redstone = newRedstone;
|
||||
if(lastRedstone != newRedstone) change();
|
||||
}
|
||||
}
|
||||
|
@ -1,58 +0,0 @@
|
||||
package io.github.cottonmc.templates.model;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.json.ModelTransformation;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
|
||||
public abstract class AbstractModel implements BakedModel, FabricBakedModel {
|
||||
protected static final Renderer RENDERER = RendererAccess.INSTANCE.getRenderer();
|
||||
|
||||
static {
|
||||
if(RENDERER == null) {
|
||||
throw new ExceptionInInitializerError("RenderAccess.INSTANCE must be populated");
|
||||
}
|
||||
}
|
||||
|
||||
protected final Sprite modelSprite;
|
||||
protected final ModelTransformation transformation;
|
||||
|
||||
protected AbstractModel(Sprite sprite, ModelTransformation transformation) {
|
||||
this.modelSprite = sprite;
|
||||
this.transformation = transformation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAmbientOcclusion() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDepth() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBuiltin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sprite getParticleSprite() {
|
||||
//TODO
|
||||
return MinecraftClient.getInstance().getBakedModelManager().getMissingModel().getParticleSprite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSideLit() {
|
||||
return false; //?
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelTransformation getTransformation() {
|
||||
return transformation;
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
package io.github.cottonmc.templates.model;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.render.model.json.ModelOverrideList;
|
||||
import net.minecraft.client.render.model.json.ModelTransformation;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Simple baked model supporting the Fabric Render API features.<p>
|
||||
*/
|
||||
public class SimpleModel extends AbstractModel {
|
||||
protected final Mesh mesh;
|
||||
protected final Supplier<MeshTransformer> transformerFactory;
|
||||
protected WeakReference<List<BakedQuad>[]> quadLists = null;
|
||||
//protected final ItemProxy itemProxy = new ItemProxy();
|
||||
|
||||
public SimpleModel(Mesh mesh, Supplier<MeshTransformer> transformerFactory, Sprite sprite, ModelTransformation transformation) {
|
||||
super(sprite, transformation);
|
||||
this.mesh = mesh;
|
||||
this.transformerFactory = transformerFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanillaAdapter() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(BlockState state, Direction face, Random rand) {
|
||||
List<BakedQuad>[] lists = quadLists == null ? null : quadLists.get();
|
||||
if(lists == null) {
|
||||
lists = ModelHelper.toQuadLists(this.mesh);
|
||||
quadLists = new WeakReference<>(lists);
|
||||
}
|
||||
List<BakedQuad> result = lists[face == null ? 6 : face.getId()];
|
||||
return result == null ? ImmutableList.of() : result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
final MeshTransformer transform = transformerFactory == null ? null : transformerFactory.get().prepare(blockView, state, pos, randomSupplier);
|
||||
if(transform != null) {
|
||||
context.pushTransform(transform);
|
||||
}
|
||||
if(mesh != null) {
|
||||
context.meshConsumer().accept(mesh);
|
||||
}
|
||||
if(transform != null) {
|
||||
context.popTransform();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelOverrideList getOverrides() {
|
||||
return ModelOverrideList.EMPTY;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public ModelItemPropertyOverrideList getItemPropertyOverrides() {
|
||||
// return itemProxy;
|
||||
// }
|
||||
//
|
||||
// protected class ItemProxy extends ModelOverrideList {
|
||||
// @Override
|
||||
// public BakedModel apply(BakedModel bakedModel_1, ItemStack itemStack_1, World world_1, LivingEntity livingEntity_1) {
|
||||
// return SimpleModel.this;
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
final MeshTransformer transform = transformerFactory == null ? null : transformerFactory.get().prepare(stack, randomSupplier);
|
||||
if(transform != null) {
|
||||
context.pushTransform(transform);
|
||||
}
|
||||
if(mesh != null) {
|
||||
context.meshConsumer().accept(mesh);
|
||||
}
|
||||
if(transform != null) {
|
||||
context.popTransform();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package io.github.cottonmc.templates.model;
|
||||
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SimpleUnbakedModel extends UnbakedModel {
|
||||
BakedModel bake();
|
||||
|
||||
@Override
|
||||
default Collection<Identifier> getModelDependencies() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setParents(Function<Identifier, UnbakedModel> function) {
|
||||
// nope
|
||||
}
|
||||
|
||||
@Override
|
||||
default @Nullable BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> function, ModelBakeSettings modelBakeSettings, Identifier identifier) {
|
||||
return bake();
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package io.github.cottonmc.templates.model;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class SlopeBakedModel extends ForwardingBakedModel {
|
||||
public SlopeBakedModel(BakedModel baseModel, BlockState slopeState, Function<SpriteIdentifier, Sprite> spriteLookup) {
|
||||
this.wrapped = baseModel;
|
||||
this.slopeState = slopeState;
|
||||
this.spriteLookup = spriteLookup;
|
||||
|
||||
this.xform = new SlopeMeshTransformer(spriteLookup);
|
||||
this.baseMesh = SlopeBaseMesh.make(slopeState);
|
||||
}
|
||||
|
||||
public final BlockState slopeState;
|
||||
public final Function<SpriteIdentifier, Sprite> spriteLookup;
|
||||
|
||||
private final MeshTransformer xform;
|
||||
private final Mesh baseMesh;
|
||||
|
||||
@Override
|
||||
public boolean isVanillaAdapter() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
MeshTransformer xform2 = xform.prepare(blockView, state, pos, randomSupplier);
|
||||
|
||||
if(xform2 != null) {
|
||||
context.pushTransform(xform2);
|
||||
context.meshConsumer().accept(baseMesh);
|
||||
context.popTransform();
|
||||
} else {
|
||||
context.meshConsumer().accept(baseMesh);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
MeshTransformer xform2 = xform.prepare(stack, randomSupplier);
|
||||
|
||||
if(xform2 != null) {
|
||||
context.pushTransform(xform2);
|
||||
context.meshConsumer().accept(baseMesh);
|
||||
context.popTransform();
|
||||
} else {
|
||||
context.meshConsumer().accept(baseMesh);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package io.github.cottonmc.templates.model;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
public class SlopeBaseMesh {
|
||||
public static Mesh make(BlockState state) {
|
||||
Renderer renderer = RendererAccess.INSTANCE.getRenderer();
|
||||
if(renderer == null) throw new IllegalStateException("RenderAccess.INSTANCE not populated - no Fabric Renderer API?");
|
||||
|
||||
final MeshBuilder builder = renderer.meshBuilder();
|
||||
final QuadEmitter quad = builder.getEmitter();
|
||||
final Direction dir = state.get(Properties.HORIZONTAL_FACING);
|
||||
drawSlope(quad.color(-1, -1, -1, -1), dir);
|
||||
drawLeftSide(quad.color(-1, -1, -1, -1), dir);
|
||||
drawRightSide(quad.color(-1, -1, -1, -1), dir);
|
||||
drawBack(quad.color(-1, -1, -1, -1), dir);
|
||||
drawBottom(quad.color(-1, -1, -1, -1));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static final int TAG_SLOPE = 0;
|
||||
public static final int TAG_LEFT = 1;
|
||||
public static final int TAG_RIGHT = 2;
|
||||
public static final int TAG_BACK = 3;
|
||||
public static final int TAG_BOTTOM = 4;
|
||||
|
||||
private static void drawSlope(QuadEmitter quad, Direction dir) {
|
||||
quad.tag(TAG_SLOPE);
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.pos(0, 0f, 1f, 0f).pos(1, 0f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 1f, 0f).emit();
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 1f).pos(2, 1f, 1f, 1f).pos(3, 1f, 0f, 0f).emit();
|
||||
break;
|
||||
case EAST:
|
||||
quad.pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 1f, 1f, 1f).pos(3, 1f, 1f, 0f).emit();
|
||||
break;
|
||||
case WEST:
|
||||
quad.pos(0, 0f, 1f, 0f).pos(1, 0f, 1f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 0f).emit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawLeftSide(QuadEmitter quad, Direction dir) {
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.tag(TAG_LEFT).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 0f, 0f, 1f).pos(3, 0f, 1f, 0f).emit();
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.tag(TAG_LEFT).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 1f).emit();
|
||||
break;
|
||||
case EAST:
|
||||
quad.tag(TAG_LEFT).pos(0, 1f, 0f, 0f).pos(1, 0f, 0f, 0f).pos(2, 0f, 0f, 0f).pos(3, 1f, 1f, 0f).emit();
|
||||
break;
|
||||
case WEST:
|
||||
quad.tag(TAG_LEFT).pos(0, 0f, 0f, 1f).pos(1, 1f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 0f, 1f, 1f).emit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawRightSide(QuadEmitter quad, Direction dir) {
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.tag(TAG_RIGHT).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 0f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 1f).emit();
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 0f).pos(2, 0f, 0f, 1f).pos(3, 0f, 1f, 1f).emit();
|
||||
break;
|
||||
case EAST:
|
||||
quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 1f).pos(1, 0f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 1f, 1f).emit();
|
||||
break;
|
||||
case WEST:
|
||||
quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 0f).pos(2, 1f, 0f, 0f).pos(3, 1f, 0f, 0f).emit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawBack(QuadEmitter quad, Direction dir) {
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.tag(TAG_BACK).pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 0f).pos(2, 1f, 1f, 0f).pos(3, 1f, 0f, 0f).emit();
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.tag(TAG_BACK).pos(0, 0f, 0f, 1f).pos(1, 1f, 0f, 1f).pos(2, 1f, 1f, 1f).pos(3, 0f, 1f, 1f).emit();
|
||||
break;
|
||||
case EAST:
|
||||
quad.tag(TAG_BACK).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 0f).pos(2, 1f, 1f, 1f).pos(3, 1f, 0f, 1f).emit();
|
||||
break;
|
||||
case WEST:
|
||||
quad.tag(TAG_BACK).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 0f, 1f, 1f).pos(3, 0f, 1f, 0f).emit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawBottom(QuadEmitter quad) {
|
||||
quad.tag(TAG_BOTTOM).pos(0, 0f, 0f, 0f).pos(1, 1f, 0f, 0f).pos(2, 1f, 0f, 1f).pos(3, 0f, 0f, 1f).emit();
|
||||
}
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
package io.github.cottonmc.templates.model;
|
||||
|
||||
import io.github.cottonmc.templates.util.SpriteSet;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
|
||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
|
||||
import net.fabricmc.fabric.api.util.TriState;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.color.block.BlockColorProvider;
|
||||
import net.minecraft.client.render.RenderLayers;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SlopeMeshTransformer implements MeshTransformer {
|
||||
public SlopeMeshTransformer(Function<SpriteIdentifier, Sprite> spriteLookup) {
|
||||
this.sprites = new SpriteSet(spriteLookup);
|
||||
}
|
||||
|
||||
private final MinecraftClient minecraft = MinecraftClient.getInstance();
|
||||
private final SpriteSet sprites;
|
||||
private final MaterialFinder finder = RendererAccess.INSTANCE.getRenderer().materialFinder();
|
||||
|
||||
private int color;
|
||||
private Direction dir;
|
||||
private RenderMaterial material;
|
||||
|
||||
@Override
|
||||
public MeshTransformer prepare(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier) {
|
||||
dir = state.get(Properties.HORIZONTAL_FACING);
|
||||
color = 0xffffff;
|
||||
|
||||
Object renderAttach = ((RenderAttachedBlockView) blockView).getBlockEntityRenderAttachment(pos);
|
||||
BlockState template = (renderAttach instanceof BlockState s) ? s : Blocks.AIR.getDefaultState();
|
||||
final Block block = template.getBlock();
|
||||
|
||||
if(block == Blocks.AIR) {
|
||||
sprites.clear();
|
||||
material = finder.clear().blendMode(BlendMode.CUTOUT).find();
|
||||
} else {
|
||||
material = finder.clear()
|
||||
.disableDiffuse(false)
|
||||
.ambientOcclusion(TriState.FALSE)
|
||||
.blendMode(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state)))
|
||||
.find();
|
||||
|
||||
BakedModel model = minecraft.getBlockRenderManager().getModel(template);
|
||||
sprites.prepare(model, randomSupplier.get());
|
||||
BlockColorProvider blockColor = ColorProviderRegistry.BLOCK.get(block);
|
||||
if(blockColor != null) color = 0xff000000 | blockColor.getColor(template, blockView, pos, 1);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MeshTransformer prepare(ItemStack stack, Supplier<Random> randomSupplier) {
|
||||
dir = Direction.NORTH;
|
||||
color = 0xffffff;
|
||||
sprites.clear();
|
||||
material = finder.clear().find();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transform(MutableQuadView quad) {
|
||||
quad.material(material);
|
||||
|
||||
final SpriteSet sprites = this.sprites;
|
||||
switch(quad.tag()) {
|
||||
case SlopeBaseMesh.TAG_SLOPE -> {
|
||||
if(sprites.hasColor(Direction.UP)) quad.color(color, color, color, color);
|
||||
paintSlope(quad, dir, sprites.getSprite(Direction.UP));
|
||||
}
|
||||
case SlopeBaseMesh.TAG_LEFT -> {
|
||||
final Direction leftDir = this.dir.rotateYCounterclockwise();
|
||||
if(sprites.hasColor(leftDir)) quad.color(color, color, color, color);
|
||||
paintLeftSide(quad, dir, sprites.getSprite(leftDir));
|
||||
}
|
||||
case SlopeBaseMesh.TAG_RIGHT -> {
|
||||
final Direction rightDir = this.dir.rotateYClockwise();
|
||||
if(sprites.hasColor(rightDir)) quad.color(color, color, color, color);
|
||||
paintRightSide(quad, dir, sprites.getSprite(rightDir));
|
||||
}
|
||||
case SlopeBaseMesh.TAG_BACK -> {
|
||||
if(sprites.hasColor(dir)) quad.color(color, color, color, color);
|
||||
paintBack(quad, dir, sprites.getSprite(dir));
|
||||
}
|
||||
case SlopeBaseMesh.TAG_BOTTOM -> {
|
||||
if(sprites.hasColor(Direction.DOWN)) quad.color(color, color, color, color);
|
||||
paintBottom(quad, sprites.getSprite(Direction.DOWN));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void paintSlope(MutableQuadView quad, Direction dir, Sprite sprite) {
|
||||
switch(dir) {
|
||||
case NORTH -> quad.uv(0, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(1, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(3, sprite.getMaxU(), sprite.getMinV());
|
||||
case SOUTH -> quad.uv(0, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMaxV());
|
||||
case EAST -> quad.uv(0, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMinV());
|
||||
case WEST -> quad.uv(0, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(1, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(3, sprite.getMaxU(), sprite.getMaxV());
|
||||
}
|
||||
}
|
||||
|
||||
private static void paintLeftSide(MutableQuadView quad, Direction dir, Sprite sprite) {
|
||||
switch(dir) {
|
||||
case NORTH, EAST, WEST -> quad.uv(0, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMinV());
|
||||
case SOUTH -> quad.uv(0, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(1, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(3, sprite.getMaxU(), sprite.getMaxV());
|
||||
}
|
||||
}
|
||||
|
||||
private static void paintRightSide(MutableQuadView quad, Direction dir, Sprite sprite) {
|
||||
switch(dir) {
|
||||
case NORTH, WEST -> quad.uv(0, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMaxV());
|
||||
case SOUTH, EAST -> quad.uv(0, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(1, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(3, sprite.getMaxU(), sprite.getMinV());
|
||||
}
|
||||
}
|
||||
|
||||
private static void paintBack(MutableQuadView quad, Direction dir, Sprite sprite) {
|
||||
switch(dir) {
|
||||
case NORTH, EAST -> quad.uv(0, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMaxV());
|
||||
case SOUTH, WEST -> quad.uv(0, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMinV());
|
||||
}
|
||||
}
|
||||
|
||||
private static void paintBottom(MutableQuadView quad, Sprite sprite) {
|
||||
quad.uv(0, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMinV());
|
||||
}
|
||||
}
|
@ -1,370 +0,0 @@
|
||||
package io.github.cottonmc.templates.model;
|
||||
|
||||
import io.github.cottonmc.templates.util.SpriteSet;
|
||||
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
|
||||
import net.fabricmc.fabric.api.util.TriState;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.color.block.BlockColorProvider;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.RenderLayers;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.texture.MissingSprite;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SlopeModel extends SimpleModel {
|
||||
|
||||
private static final ThreadLocal<Transformer> TRANSFORMERS = ThreadLocal.withInitial(Transformer::new);
|
||||
|
||||
public SlopeModel(BlockState blockState) {
|
||||
super(
|
||||
baseMesh(blockState),
|
||||
TRANSFORMERS::get,
|
||||
SpriteSet.FALLBACK,
|
||||
ModelHelper.MODEL_TRANSFORM_BLOCK
|
||||
);
|
||||
}
|
||||
|
||||
private static Mesh baseMesh(BlockState state) {
|
||||
final MeshBuilder builder = RENDERER.meshBuilder();
|
||||
final QuadEmitter quad = builder.getEmitter();
|
||||
final Direction dir = state.get(Properties.HORIZONTAL_FACING);
|
||||
drawSlope(quad.color(-1, -1, -1, -1), dir);
|
||||
drawLeftSide(quad.color(-1, -1, -1, -1), dir);
|
||||
drawRightSide(quad.color(-1, -1, -1, -1), dir);
|
||||
drawBack(quad.color(-1, -1, -1, -1), dir);
|
||||
drawBottom(quad.color(-1, -1, -1, -1));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static final int TAG_SLOPE = 0;
|
||||
private static final int TAG_LEFT = 1;
|
||||
private static final int TAG_RIGHT = 2;
|
||||
private static final int TAG_BACK = 3;
|
||||
private static final int TAG_BOTTOM = 4;
|
||||
|
||||
private static void drawSlope(QuadEmitter quad, Direction dir) {
|
||||
quad.tag(TAG_SLOPE);
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.pos(0, 0f, 1f, 0f).pos(1, 0f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 1f, 0f).emit();
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 1f).pos(2, 1f, 1f, 1f).pos(3, 1f, 0f, 0f).emit();
|
||||
break;
|
||||
case EAST:
|
||||
quad.pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 1f, 1f, 1f).pos(3, 1f, 1f, 0f).emit();
|
||||
break;
|
||||
case WEST:
|
||||
quad.pos(0, 0f, 1f, 0f).pos(1, 0f, 1f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 0f).emit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawLeftSide(QuadEmitter quad, Direction dir) {
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.tag(TAG_LEFT).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 0f, 0f, 1f).pos(3, 0f, 1f, 0f).emit();
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.tag(TAG_LEFT).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 1f).emit();
|
||||
break;
|
||||
case EAST:
|
||||
quad.tag(TAG_LEFT).pos(0, 1f, 0f, 0f).pos(1, 0f, 0f, 0f).pos(2, 0f, 0f, 0f).pos(3, 1f, 1f, 0f).emit();
|
||||
break;
|
||||
case WEST:
|
||||
quad.tag(TAG_LEFT).pos(0, 0f, 0f, 1f).pos(1, 1f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 0f, 1f, 1f).emit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawRightSide(QuadEmitter quad, Direction dir) {
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.tag(TAG_RIGHT).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 0f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 1f).emit();
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 0f).pos(2, 0f, 0f, 1f).pos(3, 0f, 1f, 1f).emit();
|
||||
break;
|
||||
case EAST:
|
||||
quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 1f).pos(1, 0f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 1f, 1f).emit();
|
||||
break;
|
||||
case WEST:
|
||||
quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 0f).pos(2, 1f, 0f, 0f).pos(3, 1f, 0f, 0f).emit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawBack(QuadEmitter quad, Direction dir) {
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.tag(TAG_BACK).pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 0f).pos(2, 1f, 1f, 0f).pos(3, 1f, 0f, 0f).emit();
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.tag(TAG_BACK).pos(0, 0f, 0f, 1f).pos(1, 1f, 0f, 1f).pos(2, 1f, 1f, 1f).pos(3, 0f, 1f, 1f).emit();
|
||||
break;
|
||||
case EAST:
|
||||
quad.tag(TAG_BACK).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 0f).pos(2, 1f, 1f, 1f).pos(3, 1f, 0f, 1f).emit();
|
||||
break;
|
||||
case WEST:
|
||||
quad.tag(TAG_BACK).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 0f, 1f, 1f).pos(3, 0f, 1f, 0f).emit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawBottom(QuadEmitter quad) {
|
||||
quad.tag(TAG_BOTTOM).pos(0, 0f, 0f, 0f).pos(1, 1f, 0f, 0f).pos(2, 1f, 0f, 1f).pos(3, 0f, 0f, 1f).emit();
|
||||
}
|
||||
|
||||
private static class Transformer implements MeshTransformer {
|
||||
private final MinecraftClient minecraft = MinecraftClient.getInstance();
|
||||
private final SpriteSet sprites = new SpriteSet();
|
||||
private final MaterialFinder finder = RENDERER.materialFinder();
|
||||
|
||||
private int color;
|
||||
private Direction dir;
|
||||
private RenderMaterial material;
|
||||
|
||||
@Override
|
||||
public MeshTransformer prepare(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier) {
|
||||
dir = state.get(Properties.HORIZONTAL_FACING);
|
||||
color = 0xffffff;
|
||||
final BlockState template = ObjectUtils.defaultIfNull((BlockState) ((RenderAttachedBlockView) blockView).getBlockEntityRenderAttachment(pos), Blocks.AIR.getDefaultState());
|
||||
final Block block = template.getBlock();
|
||||
|
||||
if(block == Blocks.AIR) {
|
||||
sprites.clear();
|
||||
material = finder.clear().blendMode(BlendMode.CUTOUT).find();
|
||||
} else {
|
||||
material = finder.clear()
|
||||
.disableDiffuse(false)
|
||||
.ambientOcclusion(TriState.FALSE)
|
||||
.blendMode(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state)))
|
||||
.find();
|
||||
|
||||
BakedModel model = minecraft.getBlockRenderManager().getModel(template);
|
||||
sprites.prepare(model, randomSupplier.get());
|
||||
BlockColorProvider blockColor = ColorProviderRegistry.BLOCK.get(block);
|
||||
if(blockColor != null) {
|
||||
color = 0xff000000 | blockColor.getColor(template, blockView, pos, 1);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MeshTransformer prepare(ItemStack stack, Supplier<Random> randomSupplier) {
|
||||
dir = Direction.NORTH;
|
||||
color = 0xffffff;
|
||||
sprites.clear();
|
||||
material = finder.clear().find();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transform(MutableQuadView quad) {
|
||||
quad.material(material);
|
||||
|
||||
final SpriteSet sprites = this.sprites;
|
||||
switch(quad.tag()) {
|
||||
|
||||
case TAG_SLOPE:
|
||||
if(sprites.hasColor(Direction.UP)) {
|
||||
quad.color(color, color, color, color);
|
||||
}
|
||||
paintSlope(quad, dir, sprites.getSprite(Direction.UP));
|
||||
break;
|
||||
|
||||
|
||||
case TAG_LEFT:
|
||||
final Direction leftDir = this.dir.rotateYCounterclockwise();
|
||||
if(sprites.hasColor(leftDir)) {
|
||||
quad.color(color, color, color, color);
|
||||
}
|
||||
paintLeftSide(quad, dir, sprites.getSprite(leftDir));
|
||||
break;
|
||||
|
||||
|
||||
case TAG_RIGHT: {
|
||||
final Direction rightDir = this.dir.rotateYClockwise();
|
||||
if(sprites.hasColor(rightDir)) {
|
||||
quad.color(color, color, color, color);
|
||||
}
|
||||
paintRightSide(quad, dir, sprites.getSprite(rightDir));
|
||||
break;
|
||||
}
|
||||
|
||||
case TAG_BACK: {
|
||||
if(sprites.hasColor(dir)) {
|
||||
quad.color(color, color, color, color);
|
||||
}
|
||||
paintBack(quad, dir, sprites.getSprite(dir));
|
||||
break;
|
||||
}
|
||||
|
||||
case TAG_BOTTOM: {
|
||||
if(sprites.hasColor(Direction.DOWN)) {
|
||||
quad.color(color, color, color, color);
|
||||
}
|
||||
paintBottom(quad, sprites.getSprite(Direction.DOWN));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void paintSlope(MutableQuadView quad, Direction dir, Sprite sprite) {
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.uv(0, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(1, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(3, sprite.getMaxU(), sprite.getMinV());
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.uv(0, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMaxV());
|
||||
break;
|
||||
case EAST:
|
||||
quad.uv(0, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMinV());
|
||||
break;
|
||||
case WEST:
|
||||
quad.uv(0, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(1, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(3, sprite.getMaxU(), sprite.getMaxV());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void paintLeftSide(MutableQuadView quad, Direction dir, Sprite sprite) {
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.uv(0, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMinV());
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.uv(0, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(1, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(3, sprite.getMaxU(), sprite.getMaxV());
|
||||
break;
|
||||
case EAST:
|
||||
quad.uv(0, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMinV());
|
||||
break;
|
||||
case WEST:
|
||||
quad.uv(0, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMinV());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void paintRightSide(MutableQuadView quad, Direction dir, Sprite sprite) {
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.uv(0, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMaxV());
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.uv(0, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(1, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(3, sprite.getMaxU(), sprite.getMinV());
|
||||
break;
|
||||
case EAST:
|
||||
quad.uv(0, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(1, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(3, sprite.getMaxU(), sprite.getMinV());
|
||||
break;
|
||||
case WEST:
|
||||
quad.uv(0, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMaxV());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void paintBack(MutableQuadView quad, Direction dir, Sprite sprite) {
|
||||
switch(dir) {
|
||||
case NORTH:
|
||||
quad.uv(0, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMaxV());
|
||||
break;
|
||||
case SOUTH:
|
||||
quad.uv(0, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMinV());
|
||||
break;
|
||||
case EAST:
|
||||
quad.uv(0, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(2, sprite.getMinU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMaxV());
|
||||
break;
|
||||
case WEST:
|
||||
quad.uv(0, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMinV());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void paintBottom(MutableQuadView quad, Sprite sprite) {
|
||||
quad.uv(0, sprite.getMinU(), sprite.getMaxV())
|
||||
.uv(1, sprite.getMaxU(), sprite.getMaxV())
|
||||
.uv(2, sprite.getMaxU(), sprite.getMinV())
|
||||
.uv(3, sprite.getMinU(), sprite.getMinV());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package io.github.cottonmc.templates.model;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.render.block.BlockModels;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
|
||||
public record SlopeUnbakedModel(BlockState slopeState) implements UnbakedModel {
|
||||
@Override
|
||||
public Collection<Identifier> getModelDependencies() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParents(Function<Identifier, UnbakedModel> function) {
|
||||
//nothing to see here
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> function, ModelBakeSettings modelBakeSettings, Identifier identifier) {
|
||||
//TODO: weird, should use my own model instead of STONE
|
||||
BakedModel baseModel = baker.bake(BlockModels.getModelId(Blocks.SANDSTONE_STAIRS.getDefaultState()), modelBakeSettings);
|
||||
|
||||
return new SlopeBakedModel(baseModel, slopeState, function);
|
||||
}
|
||||
}
|
@ -24,10 +24,10 @@ public class TemplateModelVariantProvider implements ModelVariantProvider {
|
||||
return variants.get(modelId);
|
||||
}
|
||||
|
||||
public void registerTemplateModels(Block block, BlockState itemState, Function<BlockState, AbstractModel> model) {
|
||||
public void registerTemplateModels2(Block block, BlockState itemState, Function<BlockState, UnbakedModel> model) {
|
||||
for(BlockState state : block.getStateManager().getStates()) {
|
||||
variants.put(BlockModels.getModelId(state), (SimpleUnbakedModel) () -> model.apply(state));
|
||||
variants.put(BlockModels.getModelId(state), model.apply(state));
|
||||
}
|
||||
variants.put(new ModelIdentifier(Registries.ITEM.getId(block.asItem()), "inventory"), (SimpleUnbakedModel) () -> model.apply(itemState));
|
||||
variants.put(new ModelIdentifier(Registries.ITEM.getId(block.asItem()), "inventory"), model.apply(itemState));
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,41 @@
|
||||
package io.github.cottonmc.templates.util;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.texture.MissingSprite;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.texture.SpriteAtlasTexture;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class SpriteSet {
|
||||
private Object2ObjectOpenHashMap<Direction, BakedQuad> quads = new Object2ObjectOpenHashMap<>();
|
||||
private boolean isDefault = true;
|
||||
public static final Sprite DEFAULT = findSprite(new Identifier("minecraft:block/scaffolding_top"));
|
||||
public static final Sprite FALLBACK = findSprite(MissingSprite.getMissingSpriteId());
|
||||
|
||||
private static Sprite findSprite(Identifier id) {
|
||||
Sprite s = MinecraftClient.getInstance().getBakedModelManager().getAtlas(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE).getSprite(id);
|
||||
if(false) throw new IllegalStateException("null sprite " + id);
|
||||
else return s;
|
||||
}
|
||||
|
||||
public SpriteSet() {
|
||||
public SpriteSet(Function<SpriteIdentifier, Sprite> spriteLookup) {
|
||||
//TODO: I can probably find these from a public static location
|
||||
// I think I tried that, though, and they were null. But I might have been doing it too early
|
||||
// Regardless, they should not be stored in static fields (resource-reload could invalidate them)
|
||||
this.defaultSprite = spriteLookup.apply(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:block/scaffolding_top")));
|
||||
this.missingSprite = spriteLookup.apply(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, MissingSprite.getMissingSpriteId()));
|
||||
|
||||
if(defaultSprite == null) throw new IllegalStateException("defaultSprite == null!");
|
||||
if(missingSprite == null) throw new IllegalStateException("missingSprite == null!");
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
private static final Direction[] DIRECTIONS = Direction.values();
|
||||
|
||||
private final Sprite defaultSprite;
|
||||
private final Sprite missingSprite;
|
||||
|
||||
private final EnumMap<Direction, BakedQuad> quads = new EnumMap<>(Direction.class);
|
||||
private boolean isDefault = true;
|
||||
|
||||
/** Allow re-use of instances to avoid allocation in render loop */
|
||||
public void clear() {
|
||||
isDefault = true;
|
||||
@ -40,26 +46,21 @@ public class SpriteSet {
|
||||
public void prepare(BakedModel model, Random rand) {
|
||||
this.quads.clear();
|
||||
isDefault = false;
|
||||
// avoid Direction.values() in hot loop - for thread safety may generate new array instances
|
||||
//for (Direction dir : Direction.values()) {
|
||||
for(int i = 0; i < 6; i++) {
|
||||
final Direction dir = ModelHelper.faceFromIndex(i);
|
||||
|
||||
for(Direction dir : DIRECTIONS) {
|
||||
List<BakedQuad> quads = model.getQuads(null, dir, rand);
|
||||
if(!quads.isEmpty()) this.quads.put(dir, quads.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
public Sprite getSprite(Direction dir) {
|
||||
//TODO
|
||||
if(true) return MinecraftClient.getInstance().getBakedModelManager().getMissingModel().getParticleSprite();
|
||||
|
||||
if(isDefault) return DEFAULT;
|
||||
if(isDefault) return defaultSprite;
|
||||
|
||||
BakedQuad quad = quads.get(dir);
|
||||
if(quad == null) return FALLBACK;
|
||||
if(quad == null) return missingSprite;
|
||||
|
||||
Sprite sprite = quad.getSprite();
|
||||
if(sprite == null) return FALLBACK;
|
||||
if(sprite == null) return missingSprite;
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user