🦈 GET ROTATED Idiot 🦈

This commit is contained in:
quat1024 2023-06-30 04:59:10 -04:00
parent e1408c5a7a
commit 338e45d016
8 changed files with 64 additions and 150 deletions

View File

@ -16,14 +16,13 @@ Template blocks can be placed in the world, then right-clicked with a full-size
## Todo ## Todo
* Fix the item model lol, broke it with the new system (might need a ModelLoadingRegistry registerVariantProvider as a last resort)
* Re-generalize the model system (I removed a layer of indirection while rewriting it, so it's just slopes now) * 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 (see all the `paintXxx` stuff in `SlopeMeshTransformer`, it's fairly ugly)
* Upside-down slopes would be nice... * Upside-down slopes would be nice...
* (if i may): Packages-ish "retexturing" of json blockmodels * Pass UVs as part of the mesh and retexture them at runtime too
## Notes for addon developers ## Notes for addon developers
To create your block, instantiate or extend `TemplateBlock`. Pass your `Block.Settings` through `TemplateBlock.configureSettings` to wire up the "click for glowstone" feature. Create an `ItemBlock` as normal, and create a `BlockEntityType` by instantiating or extending `TemplateEntity`. To create your block, instantiate or extend `TemplateBlock`. Pass your `Block.Settings` through `TemplateBlock.configureSettings` to wire up the "click for glowstone" feature. Create an `ItemBlock` as normal, and create a `BlockEntityType` by instantiating or extending `TemplateEntity`.
Next, wire up the custom model. im going to refactor this in like 5 seconds so im not documenting it >:). Next, wire up the custom model todo document this, it's easy

View File

@ -1,6 +1,5 @@
package io.github.cottonmc.templates; package io.github.cottonmc.templates;
import io.github.cottonmc.templates.block.SlopeBlock;
import io.github.cottonmc.templates.model.SlopeUnbakedModel; 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;
@ -14,7 +13,6 @@ import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourceType; import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.ChunkSectionPos; import net.minecraft.util.math.ChunkSectionPos;
import net.minecraft.util.math.Direction;
public class TemplatesClient implements ClientModInitializer { public class TemplatesClient implements ClientModInitializer {
public static TemplateModelVariantProvider provider = new TemplateModelVariantProvider(); public static TemplateModelVariantProvider provider = new TemplateModelVariantProvider();
@ -43,17 +41,10 @@ public class TemplatesClient implements ClientModInitializer {
} }
}); });
ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> provider); ModelLoadingRegistry.INSTANCE.registerResourceProvider(rm -> provider);
BlockRenderLayerMap.INSTANCE.putBlock(Templates.SLOPE, RenderLayer.getCutout()); BlockRenderLayerMap.INSTANCE.putBlock(Templates.SLOPE, RenderLayer.getCutout());
//ADDON DEVELOEPRS: do this! provider.addTemplateModel(Templates.id("slope_special"), SlopeUnbakedModel::new);
provider.registerTemplateModels(
//block
Templates.SLOPE,
//the blockstate you'd like the item model to show
Templates.SLOPE.getDefaultState().with(SlopeBlock.FACING, Direction.EAST),
//Function<BlockState, UnbakedModel> that creates your model
SlopeUnbakedModel::new
);
} }
} }

View File

@ -3,8 +3,6 @@ package io.github.cottonmc.templates.model;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.util.math.AffineTransformation; import net.minecraft.util.math.AffineTransformation;
import net.minecraft.util.math.AffineTransformations;
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;
@ -16,40 +14,36 @@ public record AffineQuadTransformer(Matrix4f affineMatrix) implements RenderCont
@Override @Override
public boolean transform(MutableQuadView quad) { public boolean transform(MutableQuadView quad) {
Matrix4f mat = affineMatrix;
//TODO: Hard Coded for DEBUGGING
mat = (
AffineTransformations.DIRECTION_ROTATIONS.get(Direction.NORTH)
).getMatrix();
Vector3f pos3 = new Vector3f(); Vector3f pos3 = new Vector3f();
//Vector3f norm3 = new Vector3f(); Vector3f norm3 = new Vector3f();
//ugh //ugh
Vector4f pos4 = new Vector4f(); Vector4f pos4 = new Vector4f();
//Vector4f norm4 = new Vector4f(); Vector4f norm4 = new Vector4f();
for(int i = 0; i < 4; i++) { for(int i = 0; i < 4; i++) {
//Copy quad data into vec3's //Copy quad data into vec3's
quad.copyPos(i, pos3); quad.copyPos(i, pos3);
//quad.copyNormal(i, norm3); quad.copyNormal(i, norm3);
pos3.add(-0.5f, -0.5f, -0.5f); //TODO HACK pos3.add(-0.5f, -0.5f, -0.5f); //TODO, kinda a hack to center the affine transformation not at 0,0,0
//Initialize the x/y/z components of vec4s using that data //Initialize the x/y/z components of vec4s using that data
//W component of normal vector is set to 1, normal vectors transform differently from points :) //W component of normal vector is set to 1, normal vectors transform differently from points :)
pos4.set(pos3, 0); pos4.set(pos3, 0);
//norm4.set(norm3, 1); norm4.set(norm3, 1);
//Compute the matrix-vector product. This function mutates the vec4 in-place. //Compute the matrix-vector product. This function mutates the vec4 in-place.
//Note that `transformAffine` has the same purpose as `transform`; the difference is it //Note that `transformAffine` has the same purpose as `transform`; the difference is it
//assumes (without checking) that the last row of the matrix is 0,0,0,1, as an optimization //assumes (without checking) that the last row of the matrix is 0,0,0,1, as an optimization
mat.transformAffine(pos4); affineMatrix.transformAffine(pos4);
//mat.transformAffine(norm4); affineMatrix.transformAffine(norm4);
//Manually copy the data back onto the vertex //Manually copy the data back onto the vertex
quad.pos(i, pos4.x + 0.5f, pos4.y + 0.5f, pos4.z + 0.5f); quad.pos(i, pos4.x + 0.5f, pos4.y + 0.5f, pos4.z + 0.5f);
//quad.normal(i, norm4.x, norm4.y + 1, norm4.z);
//TODO: makes everything turn black for some reason (norm vec is (0, 0, 0))
//quad.normal(i, norm4.x, norm4.y, norm4.z);
} }
return true; return true;

View File

@ -17,12 +17,12 @@ import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
public final class SlopeBakedModel extends ForwardingBakedModel { public final class SlopeBakedModel extends ForwardingBakedModel {
public SlopeBakedModel(BakedModel baseModel, BlockState slopeState, AffineTransformation aff, Function<SpriteIdentifier, Sprite> spriteLookup) { public SlopeBakedModel(BakedModel baseModel, Function<SpriteIdentifier, Sprite> spriteLookup, AffineTransformation aff) {
this.wrapped = baseModel; this.wrapped = baseModel;
this.preparer = new SlopeMeshTransformPreparer(spriteLookup); this.preparer = new SlopeMeshTransformPreparer(spriteLookup);
this.affineTransformer = new AffineQuadTransformer(aff); this.affineTransformer = new AffineQuadTransformer(aff);
this.baseMesh = SlopeBaseMesh.make(slopeState.getBlock().getDefaultState()); //TODO this.baseMesh = SlopeBaseMesh.make();
} }
private final TemplateQuadTransformPreparer preparer; private final TemplateQuadTransformPreparer preparer;

View File

@ -5,106 +5,25 @@ 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.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.block.BlockState;
import net.minecraft.state.property.Properties;
import net.minecraft.util.math.Direction;
public class SlopeBaseMesh { 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_SLOPE = 0;
public static final int TAG_LEFT = 1; public static final int TAG_LEFT = 1;
public static final int TAG_RIGHT = 2; public static final int TAG_RIGHT = 2;
public static final int TAG_BACK = 3; public static final int TAG_BACK = 3;
public static final int TAG_BOTTOM = 4; public static final int TAG_BOTTOM = 4;
private static void drawSlope(QuadEmitter quad, Direction dir) { public static Mesh make() {
quad.tag(TAG_SLOPE); Renderer renderer = RendererAccess.INSTANCE.getRenderer();
switch(dir) { if(renderer == null) throw new IllegalStateException("RenderAccess.INSTANCE not populated - no Fabric Renderer API?");
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) { MeshBuilder builder = renderer.meshBuilder();
switch(dir) { QuadEmitter qu = builder.getEmitter();
case NORTH: qu.color(-1, -1, -1, -1).tag(TAG_SLOPE) .pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 1f).pos(2, 1f, 1f, 1f).pos(3, 1f, 0f, 0f).emit()
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(); .color(-1, -1, -1, -1).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; .color(-1, -1, -1, -1).tag(TAG_RIGHT) .pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 0f).pos(2, 0f, 0f, 1f).pos(3, 0f, 1f, 1f).emit()
case SOUTH: .color(-1, -1, -1, -1).tag(TAG_BACK) .pos(0, 0f, 0f, 1f).pos(1, 1f, 0f, 1f).pos(2, 1f, 1f, 1f).pos(3, 0f, 1f, 1f).emit()
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(); .color(-1, -1, -1, -1).tag(TAG_BOTTOM).pos(0, 0f, 0f, 0f).pos(1, 1f, 0f, 0f).pos(2, 1f, 0f, 1f).pos(3, 0f, 0f, 1f).emit();
break; return builder.build();
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

@ -1,6 +1,5 @@
package io.github.cottonmc.templates.model; package io.github.cottonmc.templates.model;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.client.render.block.BlockModels; import net.minecraft.client.render.block.BlockModels;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
@ -15,7 +14,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.function.Function; import java.util.function.Function;
public record SlopeUnbakedModel(BlockState slopeState) implements UnbakedModel { public class SlopeUnbakedModel implements UnbakedModel {
@Override @Override
public Collection<Identifier> getModelDependencies() { public Collection<Identifier> getModelDependencies() {
return Collections.emptyList(); return Collections.emptyList();
@ -28,10 +27,9 @@ public record SlopeUnbakedModel(BlockState slopeState) implements UnbakedModel {
@Override @Override
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> function, ModelBakeSettings modelBakeSettings, Identifier identifier) { public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> function, ModelBakeSettings modelBakeSettings, Identifier identifier) {
//TODO: weird, should use my own model instead //TODO: this is weird, should use my own model instead
BakedModel baseModel = baker.bake(BlockModels.getModelId(Blocks.SANDSTONE_STAIRS.getDefaultState()), modelBakeSettings); BakedModel baseModel = baker.bake(BlockModels.getModelId(Blocks.SANDSTONE_STAIRS.getDefaultState()), modelBakeSettings);
//TODO: ModelBakeSettings.getRotation is always the identity transform atm return new SlopeBakedModel(baseModel, function, modelBakeSettings.getRotation());
return new SlopeBakedModel(baseModel, slopeState, modelBakeSettings.getRotation(), function);
} }
} }

View File

@ -2,34 +2,29 @@ package io.github.cottonmc.templates.model;
import net.fabricmc.fabric.api.client.model.ModelProviderContext; import net.fabricmc.fabric.api.client.model.ModelProviderContext;
import net.fabricmc.fabric.api.client.model.ModelProviderException; import net.fabricmc.fabric.api.client.model.ModelProviderException;
import net.fabricmc.fabric.api.client.model.ModelVariantProvider; import net.fabricmc.fabric.api.client.model.ModelResourceProvider;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.block.BlockModels;
import net.minecraft.client.render.model.UnbakedModel; import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.client.util.ModelIdentifier; import net.minecraft.util.Identifier;
import net.minecraft.registry.Registries;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
public class TemplateModelVariantProvider implements ModelVariantProvider { public class TemplateModelVariantProvider implements ModelResourceProvider {
private final Map<ModelIdentifier, Supplier<UnbakedModel>> factories = new HashMap<>(); private final Map<Identifier, Supplier<UnbakedModel>> factories = new HashMap<>();
private final Map<ModelIdentifier, UnbakedModel> cache = new HashMap<>(); private final Map<Identifier, UnbakedModel> cache = new HashMap<>();
@Override @Override
public @Nullable UnbakedModel loadModelVariant(ModelIdentifier modelId, ModelProviderContext context) throws ModelProviderException { public @Nullable UnbakedModel loadModelResource(Identifier resourceId, ModelProviderContext context) throws ModelProviderException {
UnbakedModel cacheResult = cache.get(modelId); UnbakedModel cacheResult = cache.get(resourceId);
if(cacheResult != null) return cacheResult; if(cacheResult != null) return cacheResult;
//Either we have a factory for this model (just haven't cached its output yet), //Either we have a factory for this model (just haven't cached its output yet),
Supplier<UnbakedModel> factory = factories.get(modelId); Supplier<UnbakedModel> factory = factories.get(resourceId);
if(factory != null) { if(factory != null) {
UnbakedModel freshModel = factory.get(); UnbakedModel freshModel = factory.get();
cache.put(modelId, freshModel); cache.put(resourceId, freshModel);
return freshModel; return freshModel;
} }
@ -37,9 +32,8 @@ public class TemplateModelVariantProvider implements ModelVariantProvider {
return null; return null;
} }
public void registerTemplateModels(Block block, BlockState itemState, Function<BlockState, UnbakedModel> model) { public void addTemplateModel(Identifier id, Supplier<UnbakedModel> modelFactory) {
for(BlockState state : block.getStateManager().getStates()) factories.put(BlockModels.getModelId(state), () -> model.apply(state)); factories.put(id, modelFactory);
factories.put(new ModelIdentifier(Registries.ITEM.getId(block.asItem()), "inventory"), () -> model.apply(itemState));
} }
public void dumpCache() { public void dumpCache() {

View File

@ -0,0 +1,19 @@
{
"variants": {
"facing=east": {
"model": "templates:slope_special",
"y": 270
},
"facing=north": {
"model": "templates:slope_special",
"y": 180
},
"facing=south": {
"model": "templates:slope_special"
},
"facing=west": {
"model": "templates:slope_special",
"y": 90
}
}
}