Draw the rest of the fucking owl

This commit is contained in:
quat1024 2023-06-15 04:24:11 -04:00
parent a5c0c9c893
commit 7928a9de70
14 changed files with 480 additions and 621 deletions

View File

@ -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. 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. 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

View File

@ -1,10 +1,12 @@
package io.github.cottonmc.templates; package io.github.cottonmc.templates;
import io.github.cottonmc.templates.block.SlopeBlock; 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 io.github.cottonmc.templates.model.TemplateModelVariantProvider;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
public class TemplatesClient implements ClientModInitializer { public class TemplatesClient implements ClientModInitializer {
@ -13,6 +15,8 @@ public class TemplatesClient implements ClientModInitializer {
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> provider); 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());
} }
} }

View File

@ -3,7 +3,6 @@ package io.github.cottonmc.templates.block;
import io.github.cottonmc.templates.Templates; import io.github.cottonmc.templates.Templates;
import io.github.cottonmc.templates.block.entity.TemplateEntity; import io.github.cottonmc.templates.block.entity.TemplateEntity;
import io.github.cottonmc.templates.util.StateContainer; import io.github.cottonmc.templates.util.StateContainer;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider; import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -35,7 +34,9 @@ public abstract class TemplateBlock extends Block implements BlockEntityProvider
} }
public static Settings configureSettings(Settings s) { 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 @Override
@ -46,7 +47,7 @@ public abstract class TemplateBlock extends Block implements BlockEntityProvider
if(stack.getItem() instanceof BlockItem) { if(stack.getItem() instanceof BlockItem) {
Block block = ((BlockItem) stack.getItem()).getBlock(); Block block = ((BlockItem) stack.getItem()).getBlock();
if(block == Blocks.REDSTONE_TORCH) { if(block == Blocks.REDSTONE_TORCH) {
be.addRedstone(); be.setRedstone(true);
if(!player.isCreative()) stack.decrement(1); if(!player.isCreative()) stack.decrement(1);
} }
ItemPlacementContext ctx = new ItemPlacementContext(new ItemUsageContext(player, hand, hit)); 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); if(!player.isCreative()) stack.decrement(1);
} }
} else if(stack.getItem() == Items.GLOWSTONE_DUST) { } else if(stack.getItem() == Items.GLOWSTONE_DUST) {
be.addGlowstone(); be.setGlowstone(true);
if(!player.isCreative()) stack.decrement(1); if(!player.isCreative()) stack.decrement(1);
} }
return ActionResult.SUCCESS; return ActionResult.SUCCESS;

View File

@ -10,10 +10,14 @@ import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper; 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.registry.Registries;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.math.BlockPos; 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 { public abstract class TemplateEntity extends BlockEntity implements RenderAttachmentBlockEntity {
protected BlockState renderedState = Blocks.AIR.getDefaultState(); protected BlockState renderedState = Blocks.AIR.getDefaultState();
@ -26,15 +30,6 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach
this.baseBlock = baseBlock; this.baseBlock = baseBlock;
} }
public BlockState getRenderedState() {
return renderedState;
}
public void setRenderedState(BlockState state) {
this.renderedState = state;
markDirty();
}
@Override @Override
public void readNbt(NbtCompound tag) { public void readNbt(NbtCompound tag) {
super.readNbt(tag); super.readNbt(tag);
@ -57,17 +52,15 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach
tag.putBoolean("Redstone", redstone); tag.putBoolean("Redstone", redstone);
} }
@Nullable
@Override @Override
public void markDirty() { public Packet<ClientPlayPacketListener> toUpdatePacket() {
super.markDirty(); return BlockEntityUpdateS2CPacket.create(this);
if(world != null && !world.isClient) { }
for(ServerPlayerEntity player : PlayerLookup.tracking(this)) {
player.networkHandler.sendPacket(this.toUpdatePacket()); @Override
} public NbtCompound toInitialChunkDataNbt() {
world.updateNeighborsAlways(pos.offset(Direction.UP), baseBlock); return createNbt();
BlockState state = world.getBlockState(pos);
world.updateListeners(pos, state, state, 1);
}
} }
@Override @Override
@ -75,21 +68,44 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach
return renderedState; 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() { public boolean hasGlowstone() {
return glowstone; return glowstone;
} }
public void addGlowstone() { public void setGlowstone(boolean newGlowstone) {
glowstone = true; boolean lastGlowstone = glowstone;
markDirty(); glowstone = newGlowstone;
if(lastGlowstone != newGlowstone) change();
} }
public boolean hasRedstone() { public boolean hasRedstone() {
return redstone; return redstone;
} }
public void addRedstone() { public void setRedstone(boolean newRedstone) {
redstone = true; boolean lastRedstone = redstone;
markDirty(); redstone = newRedstone;
if(lastRedstone != newRedstone) change();
} }
} }

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}

View File

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

View File

@ -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();
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}
}

View File

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

View File

@ -24,10 +24,10 @@ public class TemplateModelVariantProvider implements ModelVariantProvider {
return variants.get(modelId); 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()) { 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));
} }
} }

View File

@ -1,35 +1,41 @@
package io.github.cottonmc.templates.util; 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.BakedModel;
import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.texture.MissingSprite; import net.minecraft.client.texture.MissingSprite;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture; import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
import java.util.EnumMap;
import java.util.List; import java.util.List;
import java.util.function.Function;
public class SpriteSet { public class SpriteSet {
private Object2ObjectOpenHashMap<Direction, BakedQuad> quads = new Object2ObjectOpenHashMap<>(); public SpriteSet(Function<SpriteIdentifier, Sprite> spriteLookup) {
private boolean isDefault = true; //TODO: I can probably find these from a public static location
public static final Sprite DEFAULT = findSprite(new Identifier("minecraft:block/scaffolding_top")); // I think I tried that, though, and they were null. But I might have been doing it too early
public static final Sprite FALLBACK = findSprite(MissingSprite.getMissingSpriteId()); // 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()));
private static Sprite findSprite(Identifier id) { if(defaultSprite == null) throw new IllegalStateException("defaultSprite == null!");
Sprite s = MinecraftClient.getInstance().getBakedModelManager().getAtlas(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE).getSprite(id); if(missingSprite == null) throw new IllegalStateException("missingSprite == null!");
if(false) throw new IllegalStateException("null sprite " + id);
else return s;
}
public SpriteSet() {
clear(); 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 */ /** Allow re-use of instances to avoid allocation in render loop */
public void clear() { public void clear() {
isDefault = true; isDefault = true;
@ -40,26 +46,21 @@ public class SpriteSet {
public void prepare(BakedModel model, Random rand) { public void prepare(BakedModel model, Random rand) {
this.quads.clear(); this.quads.clear();
isDefault = false; isDefault = false;
// avoid Direction.values() in hot loop - for thread safety may generate new array instances
//for (Direction dir : Direction.values()) { for(Direction dir : DIRECTIONS) {
for(int i = 0; i < 6; i++) {
final Direction dir = ModelHelper.faceFromIndex(i);
List<BakedQuad> quads = model.getQuads(null, dir, rand); List<BakedQuad> quads = model.getQuads(null, dir, rand);
if(!quads.isEmpty()) this.quads.put(dir, quads.get(0)); if(!quads.isEmpty()) this.quads.put(dir, quads.get(0));
} }
} }
public Sprite getSprite(Direction dir) { public Sprite getSprite(Direction dir) {
//TODO if(isDefault) return defaultSprite;
if(true) return MinecraftClient.getInstance().getBakedModelManager().getMissingModel().getParticleSprite();
if(isDefault) return DEFAULT;
BakedQuad quad = quads.get(dir); BakedQuad quad = quads.get(dir);
if(quad == null) return FALLBACK; if(quad == null) return missingSprite;
Sprite sprite = quad.getSprite(); Sprite sprite = quad.getSprite();
if(sprite == null) return FALLBACK; if(sprite == null) return missingSprite;
return sprite; return sprite;
} }