Json model retexturing (kinda works)
This commit is contained in:
parent
e034fae532
commit
614be779b8
4
.gitignore
vendored
4
.gitignore
vendored
@ -27,3 +27,7 @@ Thumbs.db
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
local.properties
|
local.properties
|
||||||
.directory
|
.directory
|
||||||
|
|
||||||
|
# krita Moments
|
||||||
|
*.png~
|
||||||
|
*-autosave.kra
|
@ -30,7 +30,7 @@ You may create your block any way you like, just make sure it has a block entity
|
|||||||
|
|
||||||
## `Mesh`-based models
|
## `Mesh`-based models
|
||||||
|
|
||||||
We will construct a `RetexturedMeshTemplateUnbakedModel`. You need two things - the ID of a parent model, and a `Supplier<Mesh>` to retexture.
|
We will construct a `RetexturedMeshUnbakedModel`. You need two things - the ID of a parent model, and a `Supplier<Mesh>` to retexture.
|
||||||
|
|
||||||
Fill in the parent model field with the ID of any model. Ideally, this model should have a parent of `block/block`, or at least define *some* non-default rotations (smokey the bear voice *Only You Can Prevent Weirdly Rotated First-Person Models*), set `"gui_light": "front"` (lest the item model look weird), and define a particle texture.
|
Fill in the parent model field with the ID of any model. Ideally, this model should have a parent of `block/block`, or at least define *some* non-default rotations (smokey the bear voice *Only You Can Prevent Weirdly Rotated First-Person Models*), set `"gui_light": "front"` (lest the item model look weird), and define a particle texture.
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ When building the `Mesh`, if you want a face to be dynamically retextured, `.tag
|
|||||||
|
|
||||||
(TODO: implement a system for baking unchanging `Sprite`s onto the mesh, potentially by "registering" more tags; the problem is you don't have access to sprite uvs at mesh building time. Or just provide a way to get sprite UVs at mesh building time...?)
|
(TODO: implement a system for baking unchanging `Sprite`s onto the mesh, potentially by "registering" more tags; the problem is you don't have access to sprite uvs at mesh building time. Or just provide a way to get sprite UVs at mesh building time...?)
|
||||||
|
|
||||||
That's all you need in order to construct a `RetexturedMeshTemplateUnbakedModel`, so to finish things off:
|
That's all you need in order to construct a `RetexturedMeshUnbakedModel`, so to finish things off:
|
||||||
|
|
||||||
* Come up with an ID for it
|
* Come up with an ID for it
|
||||||
* Register it using `TemplatesClient.provider.addTemplateModel` (a thin wrapper around Fabric's `ModelResourceProvider`)
|
* Register it using `TemplatesClient.provider.addTemplateModel` (a thin wrapper around Fabric's `ModelResourceProvider`)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
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.block.TemplateSlabBlock;
|
||||||
import io.github.cottonmc.templates.block.entity.TemplateEntity;
|
import io.github.cottonmc.templates.block.entity.TemplateEntity;
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
|
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
|
||||||
@ -26,20 +27,31 @@ public class Templates implements ModInitializer {
|
|||||||
FabricBlockEntityTypeBuilder.create(Templates::makeSlopeEntity, SLOPE).build(null)
|
FabricBlockEntityTypeBuilder.create(Templates::makeSlopeEntity, SLOPE).build(null)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public static final Block SLAB = Registry.register(Registries.BLOCK, id("slab"), new TemplateSlabBlock());
|
||||||
|
public static final BlockEntityType<TemplateEntity> SLAB_ENTITY = Registry.register(
|
||||||
|
Registries.BLOCK_ENTITY_TYPE, id("slab"),
|
||||||
|
FabricBlockEntityTypeBuilder.create(Templates::makeSlabEntity, SLAB).build(null)
|
||||||
|
);
|
||||||
|
|
||||||
//Overridden in TemplatesClient
|
//Overridden in TemplatesClient
|
||||||
public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {};
|
public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
Registry.register(Registries.ITEM, id("slope"), (Item) new BlockItem(SLOPE, new Item.Settings()));
|
Registry.register(Registries.ITEM, id("slope"), new BlockItem(SLOPE, new Item.Settings()));
|
||||||
|
Registry.register(Registries.ITEM, id("slab"), new BlockItem(SLAB, new Item.Settings()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Identifier id(String path) {
|
public static Identifier id(String path) {
|
||||||
return new Identifier(MODID, path);
|
return new Identifier(MODID, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
//simply for breaking the circular reference in the SLOPE_ENTITY constructor call
|
//simply for breaking circular references in the registration calls
|
||||||
private static TemplateEntity makeSlopeEntity(BlockPos pos, BlockState state) {
|
private static TemplateEntity makeSlopeEntity(BlockPos pos, BlockState state) {
|
||||||
return new TemplateEntity(SLOPE_ENTITY, pos, state);
|
return new TemplateEntity(SLOPE_ENTITY, pos, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TemplateEntity makeSlabEntity(BlockPos pos, BlockState state) {
|
||||||
|
return new TemplateEntity(SLAB_ENTITY, pos, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.cottonmc.templates;
|
package io.github.cottonmc.templates;
|
||||||
|
|
||||||
import io.github.cottonmc.templates.model.RetexturedMeshTemplateUnbakedModel;
|
import io.github.cottonmc.templates.model.RetexturedJsonModelUnbakedModel;
|
||||||
|
import io.github.cottonmc.templates.model.RetexturedMeshUnbakedModel;
|
||||||
import io.github.cottonmc.templates.model.SlopeBaseMesh;
|
import io.github.cottonmc.templates.model.SlopeBaseMesh;
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
||||||
@ -53,9 +54,14 @@ public class TemplatesClient implements ClientModInitializer {
|
|||||||
ModelLoadingRegistry.INSTANCE.registerResourceProvider(rm -> provider); //block models
|
ModelLoadingRegistry.INSTANCE.registerResourceProvider(rm -> provider); //block models
|
||||||
ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> provider); //item models
|
ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> provider); //item models
|
||||||
|
|
||||||
BlockRenderLayerMap.INSTANCE.putBlock(Templates.SLOPE, RenderLayer.getCutout());
|
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), Templates.SLOPE, Templates.SLAB);
|
||||||
|
|
||||||
provider.addTemplateModel(Templates.id("slope_special"), () -> new RetexturedMeshTemplateUnbakedModel(Templates.id("block/slope_base"), SlopeBaseMesh::make));
|
provider.addTemplateModel(Templates.id("slope_special"), () -> new RetexturedMeshUnbakedModel(Templates.id("block/slope_base"), SlopeBaseMesh::make));
|
||||||
provider.assignItemModel(Templates.id("slope_special"), Templates.SLOPE);
|
provider.assignItemModel(Templates.id("slope_special"), Templates.SLOPE);
|
||||||
|
|
||||||
|
provider.addTemplateModel(Templates.id("cube_special"), () -> new RetexturedJsonModelUnbakedModel(Templates.id("block/cube")));
|
||||||
|
provider.addTemplateModel(Templates.id("slab_bottom_special"), () -> new RetexturedJsonModelUnbakedModel(Templates.id("block/slab_bottom")));
|
||||||
|
provider.addTemplateModel(Templates.id("slab_top_special"), () -> new RetexturedJsonModelUnbakedModel(Templates.id("block/slab_top")));
|
||||||
|
provider.assignItemModel(Templates.id("slab_bottom_special"), Templates.SLAB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package io.github.cottonmc.templates.block;
|
||||||
|
|
||||||
|
import io.github.cottonmc.templates.Templates;
|
||||||
|
import net.minecraft.block.BlockEntityProvider;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.SlabBlock;
|
||||||
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.sound.BlockSoundGroup;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class TemplateSlabBlock extends SlabBlock implements BlockEntityProvider {
|
||||||
|
public TemplateSlabBlock(Settings settings) {
|
||||||
|
super(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemplateSlabBlock() {
|
||||||
|
//super(TemplateBlock.configureSettings(Settings.create()) //TODO
|
||||||
|
super(Settings.create().nonOpaque()
|
||||||
|
.sounds(BlockSoundGroup.WOOD)
|
||||||
|
.hardness(0.2f)); //TODO: Material.WOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
return Templates.SLAB_ENTITY.instantiate(pos, state);
|
||||||
|
}
|
||||||
|
}
|
@ -6,20 +6,24 @@ 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.MeshBuilder;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||||
import net.minecraft.util.math.AffineTransformation;
|
import net.minecraft.util.math.AffineTransformation;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
import org.joml.Vector4f;
|
import org.joml.Vector4f;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms the position of each vertex in a `Mesh`.
|
* Transforms the position of each vertex in a `Mesh`.
|
||||||
* The transformation's origin is bumped to (0.5, 0.5, 0.5) just because it's more convenient for me lol.
|
* The transformation's origin is bumped to (0.5, 0.5, 0.5) just because it's more convenient for me lol.
|
||||||
*/
|
*/
|
||||||
public class MatrixMeshTransformer {
|
public class MatrixTransformer {
|
||||||
public static Mesh transformAroundCenter(AffineTransformation aff, Mesh oldMesh) {
|
public static Mesh meshAroundCenter(AffineTransformation aff, Mesh oldMesh) {
|
||||||
return transformAroundCenter(aff.getMatrix(), oldMesh);
|
return meshAroundCenter(aff.getMatrix(), oldMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Mesh transformAroundCenter(Matrix4f mat, Mesh oldMesh) {
|
public static Mesh meshAroundCenter(Matrix4f mat, Mesh oldMesh) {
|
||||||
Renderer r = TemplatesClient.getFabricRenderer();
|
Renderer r = TemplatesClient.getFabricRenderer();
|
||||||
MeshBuilder newMesh = r.meshBuilder();
|
MeshBuilder newMesh = r.meshBuilder();
|
||||||
QuadEmitter emitter = newMesh.getEmitter();
|
QuadEmitter emitter = newMesh.getEmitter();
|
||||||
@ -54,4 +58,22 @@ public class MatrixMeshTransformer {
|
|||||||
|
|
||||||
return newMesh.build();
|
return newMesh.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Hard to explain what this is for...
|
||||||
|
//Basically, the previous incarnation of this mod assembled the north/south/east/west faces all individually.
|
||||||
|
//This means it was easy to get the orientation of the block correct - to popular the north face of the slope, look at
|
||||||
|
//the north texture of the theme block. In this version, there is only *one* slope model that is dynamically rotated
|
||||||
|
//to form the other possible orientations. If I populate the north face of the model using the north face of the theme,
|
||||||
|
//that model will then be rotated so it's no longer facing the right way.
|
||||||
|
//
|
||||||
|
//This seems to work, but I'm kinda surprised I don't need to invert the transformation here, which is a clue that
|
||||||
|
//I don't really understand all the math, loool
|
||||||
|
public static Map<Direction, Direction> facePermutation(AffineTransformation aff) {
|
||||||
|
Map<Direction, Direction> facePermutation = new EnumMap<>(Direction.class);
|
||||||
|
for(Direction input : Direction.values()) {
|
||||||
|
Direction output = Direction.transform(aff.getMatrix(), input);
|
||||||
|
facePermutation.put(input, output);
|
||||||
|
}
|
||||||
|
return facePermutation;
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
package io.github.cottonmc.templates.model;
|
||||||
|
|
||||||
|
import io.github.cottonmc.templates.Templates;
|
||||||
|
import io.github.cottonmc.templates.TemplatesClient;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||||
|
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.mesh.QuadView;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||||
|
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.client.render.model.BakedModel;
|
||||||
|
import net.minecraft.client.render.model.BakedQuad;
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.client.util.SpriteIdentifier;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.screen.PlayerScreenHandler;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class RetexturedJsonModelBakedModel extends ForwardingBakedModel {
|
||||||
|
public RetexturedJsonModelBakedModel(BakedModel baseModel, TemplateAppearanceManager tam, Function<SpriteIdentifier, Sprite> spriteLookup) {
|
||||||
|
this.wrapped = baseModel;
|
||||||
|
this.tam = tam;
|
||||||
|
|
||||||
|
for(int i = 0; i < DIRECTIONS.length; i++) {
|
||||||
|
SpriteIdentifier id = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, Templates.id("templates_special/" + DIRECTIONS[i].getName()));
|
||||||
|
this.specialSprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record CacheKey(BlockState state, TemplateAppearance appearance) {}
|
||||||
|
|
||||||
|
private final TemplateAppearanceManager tam;
|
||||||
|
private final ConcurrentHashMap<CacheKey, Mesh> meshCache = new ConcurrentHashMap<>();
|
||||||
|
private final Sprite[] specialSprites = new Sprite[DIRECTIONS.length];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isVanillaAdapter() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
|
BlockState template = (((RenderAttachedBlockView) blockView).getBlockEntityRenderAttachment(pos) instanceof BlockState s) ? s : null;
|
||||||
|
TemplateAppearance ta = template == null || template.isAir() ? tam.getDefaultAppearance() : tam.getAppearance(template);
|
||||||
|
|
||||||
|
CacheKey key = new CacheKey(state, ta);
|
||||||
|
context.meshConsumer().accept(meshCache.computeIfAbsent(key, this::makeMesh));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
|
super.emitItemQuads(stack, randomSupplier, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Mesh makeMesh(CacheKey key) {
|
||||||
|
Renderer r = TemplatesClient.getFabricRenderer();
|
||||||
|
MeshBuilder builder = r.meshBuilder();
|
||||||
|
QuadEmitter emitter = builder.getEmitter();
|
||||||
|
RenderMaterial mat = key.appearance().getRenderMaterial();
|
||||||
|
|
||||||
|
Random rand = Random.create(42);
|
||||||
|
|
||||||
|
for(Direction cullFace : DIRECTIONS_AND_NULL) {
|
||||||
|
for(BakedQuad quad : wrapped.getQuads(key.state, cullFace, rand)) {
|
||||||
|
emitter.fromVanilla(quad, mat, cullFace);
|
||||||
|
|
||||||
|
QuadUvBounds bounds = QuadUvBounds.read(emitter);
|
||||||
|
for(int i = 0; i < specialSprites.length; i++) {
|
||||||
|
if(bounds.displaysSprite(specialSprites[i])) {
|
||||||
|
bounds.remap(emitter, specialSprites[i], key.appearance().getSprite(DIRECTIONS[i]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emitter.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
record QuadUvBounds(float minU, float maxU, float minV, float maxV) {
|
||||||
|
static QuadUvBounds read(QuadView quad) {
|
||||||
|
float u0 = quad.u(0); float u1 = quad.u(1); float u2 = quad.u(2); float u3 = quad.u(3);
|
||||||
|
float v0 = quad.v(0); float v1 = quad.v(1); float v2 = quad.v(2); float v3 = quad.v(3);
|
||||||
|
return new QuadUvBounds(
|
||||||
|
Math.min(Math.min(u0, u1), Math.min(u2, u3)),
|
||||||
|
Math.max(Math.max(u0, u1), Math.max(u2, u3)),
|
||||||
|
Math.min(Math.min(v0, v1), Math.min(v2, v3)),
|
||||||
|
Math.max(Math.max(v0, v1), Math.max(v2, v3))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean displaysSprite(Sprite sprite) {
|
||||||
|
return sprite.getMinU() <= minU && sprite.getMaxU() >= maxU && sprite.getMinV() <= minV && sprite.getMaxV() >= maxV;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remap(MutableQuadView quad, Sprite specialSprite, Sprite newSprite) {
|
||||||
|
float remappedMinU = rangeRemap(minU, specialSprite.getMinU(), specialSprite.getMaxU(), newSprite.getMinU(), newSprite.getMaxU());
|
||||||
|
float remappedMaxU = rangeRemap(maxU, specialSprite.getMinU(), specialSprite.getMaxU(), newSprite.getMinU(), newSprite.getMaxU());
|
||||||
|
float remappedMinV = rangeRemap(minV, specialSprite.getMinV(), specialSprite.getMaxV(), newSprite.getMinV(), newSprite.getMaxV());
|
||||||
|
float remappedMaxV = rangeRemap(maxV, specialSprite.getMinV(), specialSprite.getMaxV(), newSprite.getMinV(), newSprite.getMaxV());
|
||||||
|
|
||||||
|
quad.uv(0, MathHelper.approximatelyEquals(quad.u(0), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(0), minV) ? remappedMinV : remappedMaxV);
|
||||||
|
quad.uv(1, MathHelper.approximatelyEquals(quad.u(1), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(1), minV) ? remappedMinV : remappedMaxV);
|
||||||
|
quad.uv(2, MathHelper.approximatelyEquals(quad.u(2), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(2), minV) ? remappedMinV : remappedMaxV);
|
||||||
|
quad.uv(3, MathHelper.approximatelyEquals(quad.u(3), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(3), minV) ? remappedMinV : remappedMaxV);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float rangeRemap(float value, float low1, float high1, float low2, float high2) {
|
||||||
|
float value2 = MathHelper.clamp(value, low1, high1);
|
||||||
|
return low2 + (value2 - low1) * (high2 - low2) / (high1 - low1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Direction[] DIRECTIONS = Direction.values();
|
||||||
|
private static final Direction[] DIRECTIONS_AND_NULL = new Direction[DIRECTIONS.length + 1];
|
||||||
|
static {
|
||||||
|
System.arraycopy(DIRECTIONS, 0, DIRECTIONS_AND_NULL, 0, DIRECTIONS.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package io.github.cottonmc.templates.model;
|
||||||
|
|
||||||
|
import io.github.cottonmc.templates.TemplatesClient;
|
||||||
|
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 org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class RetexturedJsonModelUnbakedModel implements UnbakedModel {
|
||||||
|
public RetexturedJsonModelUnbakedModel(Identifier parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Identifier parent;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Identifier> getModelDependencies() {
|
||||||
|
return Collections.singletonList(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParents(Function<Identifier, UnbakedModel> function) {
|
||||||
|
function.apply(parent).setParents(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
|
||||||
|
return new RetexturedJsonModelBakedModel(
|
||||||
|
baker.bake(parent, modelBakeSettings),
|
||||||
|
TemplatesClient.provider.getOrCreateTemplateApperanceManager(spriteLookup),
|
||||||
|
spriteLookup
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -22,36 +22,20 @@ import net.minecraft.util.math.random.Random;
|
|||||||
import net.minecraft.world.BlockRenderView;
|
import net.minecraft.world.BlockRenderView;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public final class TemplateBakedModel extends ForwardingBakedModel {
|
public final class RetexturedMeshBakedModel extends ForwardingBakedModel {
|
||||||
public TemplateBakedModel(BakedModel baseModel, TemplateAppearanceManager tam, AffineTransformation aff, Mesh baseMesh) {
|
public RetexturedMeshBakedModel(BakedModel baseModel, TemplateAppearanceManager tam, AffineTransformation aff, Mesh baseMesh) {
|
||||||
this.wrapped = baseModel;
|
this.wrapped = baseModel;
|
||||||
|
|
||||||
this.tam = tam;
|
this.tam = tam;
|
||||||
this.baseMesh = MatrixMeshTransformer.transformAroundCenter(aff, baseMesh);
|
this.baseMesh = MatrixTransformer.meshAroundCenter(aff, baseMesh);
|
||||||
|
this.facePermutation = MatrixTransformer.facePermutation(aff);
|
||||||
//Hard to explain what this is for...
|
|
||||||
//Basically, the previous incarnation of this mod assembled the north/south/east/west faces all individually.
|
|
||||||
//This means it was easy to get the orientation of the block correct - to popular the north face of the slope, look at
|
|
||||||
//the north texture of the theme block. In this version, there is only *one* slope model that is dynamically rotated
|
|
||||||
//to form the other possible orientations. If I populate the north face of the model using the north face of the theme,
|
|
||||||
//that model will then be rotated so it's no longer facing the right way.
|
|
||||||
//
|
|
||||||
//This seems to work, but I'm kinda surprised I don't need to invert the transformation here, which is a clue that
|
|
||||||
//I don't really understand all the math, loool
|
|
||||||
for(Direction input : Direction.values()) {
|
|
||||||
Direction output = Direction.transform(aff.getMatrix(), input);
|
|
||||||
facePermutation.put(input, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final TemplateAppearanceManager tam;
|
private final TemplateAppearanceManager tam;
|
||||||
private final Mesh baseMesh;
|
private final Mesh baseMesh;
|
||||||
|
private final Map<Direction, Direction> facePermutation;
|
||||||
private final Map<Direction, Direction> facePermutation = new EnumMap<>(Direction.class);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isVanillaAdapter() {
|
public boolean isVanillaAdapter() {
|
@ -16,8 +16,8 @@ import java.util.function.Function;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@SuppressWarnings("ClassCanBeRecord")
|
@SuppressWarnings("ClassCanBeRecord")
|
||||||
public class RetexturedMeshTemplateUnbakedModel implements UnbakedModel {
|
public class RetexturedMeshUnbakedModel implements UnbakedModel {
|
||||||
public RetexturedMeshTemplateUnbakedModel(Identifier parent, Supplier<Mesh> baseMeshFactory) {
|
public RetexturedMeshUnbakedModel(Identifier parent, Supplier<Mesh> baseMeshFactory) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.baseMeshFactory = baseMeshFactory;
|
this.baseMeshFactory = baseMeshFactory;
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ public class RetexturedMeshTemplateUnbakedModel implements UnbakedModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
|
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
|
||||||
return new TemplateBakedModel(
|
return new RetexturedMeshBakedModel(
|
||||||
baker.bake(parent, modelBakeSettings),
|
baker.bake(parent, modelBakeSettings),
|
||||||
TemplatesClient.provider.getOrCreateTemplateApperanceManager(spriteLookup),
|
TemplatesClient.provider.getOrCreateTemplateApperanceManager(spriteLookup),
|
||||||
modelBakeSettings.getRotation(),
|
modelBakeSettings.getRotation(),
|
@ -9,7 +9,7 @@ import net.minecraft.util.math.Direction;
|
|||||||
|
|
||||||
public class SlopeBaseMesh {
|
public class SlopeBaseMesh {
|
||||||
/**
|
/**
|
||||||
* @see TemplateBakedModel.RetexturingTransformer for why these values were chosen
|
* @see RetexturedMeshBakedModel.RetexturingTransformer for why these values were chosen
|
||||||
*/
|
*/
|
||||||
public static final int TAG_SLOPE = Direction.UP.ordinal() + 1;
|
public static final int TAG_SLOPE = Direction.UP.ordinal() + 1;
|
||||||
public static final int TAG_LEFT = Direction.EAST.ordinal() + 1;
|
public static final int TAG_LEFT = Direction.EAST.ordinal() + 1;
|
||||||
|
9
src/main/resources/assets/minecraft/atlases/blocks.json
Normal file
9
src/main/resources/assets/minecraft/atlases/blocks.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "directory",
|
||||||
|
"source": "templates_special",
|
||||||
|
"prefix": "templates_special/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
13
src/main/resources/assets/templates/blockstates/slab.json
Normal file
13
src/main/resources/assets/templates/blockstates/slab.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"type=bottom": {
|
||||||
|
"model": "templates:slab_bottom_special"
|
||||||
|
},
|
||||||
|
"type=double": {
|
||||||
|
"model": "templates:cube_special"
|
||||||
|
},
|
||||||
|
"type=top": {
|
||||||
|
"model": "templates:slab_top_special"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"block.templates.slope": "Slope Template"
|
"block.templates.slope": "Slope Template",
|
||||||
|
"block.templates.slab": "Slab Template"
|
||||||
}
|
}
|
12
src/main/resources/assets/templates/models/block/cube.json
Normal file
12
src/main/resources/assets/templates/models/block/cube.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:block/cube",
|
||||||
|
"textures": {
|
||||||
|
"down": "templates:templates_special/down",
|
||||||
|
"up": "templates:templates_special/up",
|
||||||
|
"north": "templates:templates_special/north",
|
||||||
|
"south": "templates:templates_special/south",
|
||||||
|
"west": "templates:templates_special/west",
|
||||||
|
"east": "templates:templates_special/east",
|
||||||
|
"particle": "minecraft:block/scaffolding_top"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:block/block",
|
||||||
|
"textures": {
|
||||||
|
"down": "templates:templates_special/down",
|
||||||
|
"up": "templates:templates_special/up",
|
||||||
|
"north": "templates:templates_special/north",
|
||||||
|
"south": "templates:templates_special/south",
|
||||||
|
"west": "templates:templates_special/west",
|
||||||
|
"east": "templates:templates_special/east",
|
||||||
|
"particle": "minecraft:block/scaffolding_top"
|
||||||
|
},
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"from": [0, 0, 0],
|
||||||
|
"to": [16, 8, 16],
|
||||||
|
"faces": {
|
||||||
|
"down": {
|
||||||
|
"uv": [0, 0, 16, 16],
|
||||||
|
"texture": "#down",
|
||||||
|
"cullface": "down"
|
||||||
|
},
|
||||||
|
"up": {
|
||||||
|
"uv": [0, 0, 16, 16],
|
||||||
|
"texture": "#up"
|
||||||
|
},
|
||||||
|
"north": {
|
||||||
|
"uv": [0, 8, 16, 16],
|
||||||
|
"texture": "#north",
|
||||||
|
"cullface": "north"
|
||||||
|
},
|
||||||
|
"south": {
|
||||||
|
"uv": [0, 8, 16, 16],
|
||||||
|
"texture": "#south",
|
||||||
|
"cullface": "south"
|
||||||
|
},
|
||||||
|
"west": {
|
||||||
|
"uv": [0, 8, 16, 16],
|
||||||
|
"texture": "#west",
|
||||||
|
"cullface": "west"
|
||||||
|
},
|
||||||
|
"east": {
|
||||||
|
"uv": [0, 8, 16, 16],
|
||||||
|
"texture": "#east",
|
||||||
|
"cullface": "east"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:block/block",
|
||||||
|
"textures": {
|
||||||
|
"down": "templates:templates_special/down",
|
||||||
|
"up": "templates:templates_special/up",
|
||||||
|
"north": "templates:templates_special/north",
|
||||||
|
"south": "templates:templates_special/south",
|
||||||
|
"west": "templates:templates_special/west",
|
||||||
|
"east": "templates:templates_special/east",
|
||||||
|
"particle": "minecraft:block/scaffolding_top"
|
||||||
|
},
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"from": [0, 8, 0],
|
||||||
|
"to": [16, 16, 16],
|
||||||
|
"faces": {
|
||||||
|
"down": {
|
||||||
|
"uv": [0, 0, 16, 16],
|
||||||
|
"texture": "#down"
|
||||||
|
},
|
||||||
|
"up": {
|
||||||
|
"uv": [0, 0, 16, 16],
|
||||||
|
"texture": "#up",
|
||||||
|
"cullface": "up"
|
||||||
|
},
|
||||||
|
"north": {
|
||||||
|
"uv": [0, 0, 16, 8],
|
||||||
|
"texture": "#north",
|
||||||
|
"cullface": "north"
|
||||||
|
},
|
||||||
|
"south": {
|
||||||
|
"uv": [0, 0, 16, 8],
|
||||||
|
"texture": "#south",
|
||||||
|
"cullface": "south"
|
||||||
|
},
|
||||||
|
"west": {
|
||||||
|
"uv": [0, 0, 16, 8],
|
||||||
|
"texture": "#west",
|
||||||
|
"cullface": "west"
|
||||||
|
},
|
||||||
|
"east": {
|
||||||
|
"uv": [0, 0, 16, 8],
|
||||||
|
"texture": "#east",
|
||||||
|
"cullface": "east"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"parent": "block/block",
|
"parent": "minecraft:block/block",
|
||||||
"gui_light": "front",
|
"gui_light": "front",
|
||||||
"textures": {
|
"textures": {
|
||||||
"particle": "block/scaffolding_top"
|
"particle": "minecraft:block/scaffolding_top"
|
||||||
},
|
},
|
||||||
"display": {
|
"display": {
|
||||||
"firstperson_righthand": {
|
"firstperson_righthand": {
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 536 B |
Binary file not shown.
After Width: | Height: | Size: 528 B |
Binary file not shown.
After Width: | Height: | Size: 547 B |
Binary file not shown.
After Width: | Height: | Size: 564 B |
Binary file not shown.
After Width: | Height: | Size: 525 B |
Binary file not shown.
After Width: | Height: | Size: 545 B |
Loading…
Reference in New Issue
Block a user