Created
May 13, 2021 22:29
-
-
Save Haven-King/4f3f838e4e99abc875ac7e3f48149e54 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.aether.client.model; | |
import com.mojang.datafixers.util.Pair; | |
import net.fabricmc.fabric.api.renderer.v1.Renderer; | |
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.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.FabricBakedModel; | |
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; | |
import net.minecraft.block.BlockState; | |
import net.minecraft.client.render.model.*; | |
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.client.util.SpriteIdentifier; | |
import net.minecraft.item.ItemStack; | |
import net.minecraft.util.Identifier; | |
import net.minecraft.util.math.BlockPos; | |
import net.minecraft.util.math.Direction; | |
import net.minecraft.util.math.MathHelper; | |
import net.minecraft.util.math.Vec3i; | |
import net.minecraft.world.BlockRenderView; | |
import org.jetbrains.annotations.Nullable; | |
import java.util.*; | |
import java.util.function.Consumer; | |
import java.util.function.Function; | |
import java.util.function.Supplier; | |
public class TessellatingBlockModel implements BakedModel, FabricBakedModel, UnbakedModel { | |
private static final Direction[] DIRECTIONS = Direction.values(); | |
private final List<SpriteIdentifier> spriteIdentifiers; | |
private final Map<Direction, Mesh[]> meshes = new EnumMap<>(Direction.class); | |
private final int width, height; | |
private Sprite sprite; | |
private final SpriteIdentifier spriteId; | |
private final SpriteIdentifier overlay; | |
private Mesh overlayMesh; | |
private TessellatingBlockModel(SpriteIdentifier spriteId, int width, int height, List<SpriteIdentifier> spriteIdentifiers, @Nullable SpriteIdentifier overlay) { | |
this.width = width; | |
this.height = height; | |
this.spriteIdentifiers = spriteIdentifiers; | |
this.spriteId = spriteId; | |
this.overlay = overlay; | |
} | |
@Override | |
public boolean isVanillaAdapter() { | |
return false; | |
} | |
@Override | |
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) { | |
int x = pos.getX(), y = pos.getY(), z = pos.getZ(); | |
Consumer<Mesh> consumer = context.meshConsumer(); | |
for (Direction direction : DIRECTIONS) { | |
consumer.accept(this.meshes.get(direction)[this.indexOf(direction, x, y, z)]); | |
} | |
if (this.overlayMesh != null) { | |
consumer.accept(overlayMesh); | |
} | |
} | |
@Override | |
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) { | |
} | |
@Override | |
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction face, Random random) { | |
return Collections.emptyList(); | |
} | |
@Override | |
public boolean useAmbientOcclusion() { | |
return true; | |
} | |
@Override | |
public boolean hasDepth() { | |
return false; | |
} | |
@Override | |
public boolean isSideLit() { | |
return false; | |
} | |
@Override | |
public boolean isBuiltin() { | |
return false; | |
} | |
@Override | |
public Sprite getSprite() { | |
return this.sprite; | |
} | |
@Override | |
public ModelTransformation getTransformation() { | |
return ModelTransformation.NONE; | |
} | |
@Override | |
public ModelOverrideList getOverrides() { | |
return ModelOverrideList.EMPTY; | |
} | |
@Override | |
public Collection<Identifier> getModelDependencies() { | |
return Collections.emptyList(); | |
} | |
@Override | |
public Collection<SpriteIdentifier> getTextureDependencies(Function<Identifier, UnbakedModel> unbakedModelGetter, Set<Pair<String, String>> unresolvedTextureReferences) { | |
return this.spriteIdentifiers; | |
} | |
@Nullable | |
@Override | |
public BakedModel bake(ModelLoader loader, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) { | |
Renderer renderer = RendererAccess.INSTANCE.getRenderer(); | |
if (renderer != null) { | |
this.sprite = textureGetter.apply(this.spriteId); | |
MeshBuilder builder = renderer.meshBuilder(); | |
QuadEmitter emitter = builder.getEmitter(); | |
for (Direction direction : DIRECTIONS) { | |
Mesh[] meshes = new Mesh[this.width * this.height]; | |
for (int i = 0; i < this.width * this.height; ++i) { | |
emitter.square(direction, 0, 0, 1, 1, 0); | |
emitter.spriteBake(0, textureGetter.apply(this.spriteIdentifiers.get(i)), MutableQuadView.BAKE_LOCK_UV); | |
emitter.spriteColor(0, -1, -1, -1, -1); | |
emitter.emit(); | |
meshes[i] = builder.build(); | |
} | |
this.meshes.put(direction, meshes); | |
} | |
if (this.overlay != null) { | |
RenderMaterial material = renderer.materialFinder().blendMode(0, BlendMode.TRANSLUCENT).find(); | |
for (Direction direction : DIRECTIONS) { | |
emitter.square(direction, 0, 0, 1, 1, 0); | |
emitter.spriteBake(0, textureGetter.apply(this.overlay), MutableQuadView.BAKE_LOCK_UV); | |
emitter.spriteColor(0, -1, -1, -1, -1); | |
emitter.material(material); | |
emitter.emit(); | |
} | |
this.overlayMesh = builder.build(); | |
} | |
} | |
return this; | |
} | |
private int indexOf(Direction direction, int x, int y, int z) { | |
Direction.Axis axis = direction.getAxis(); | |
Vec3i vec3i = direction.getVector(); | |
int mod = axis.choose(vec3i.getX(), vec3i.getY(), vec3i.getZ()); | |
switch (axis) { | |
case X: | |
return MathHelper.floorMod(z * -mod + x, this.width) + (this.height - MathHelper.floorMod(y + x, this.height) - 1) * this.width; | |
case Y: | |
return MathHelper.floorMod(x + y, this.width) + (this.height - MathHelper.floorMod(z * -mod + y, this.height) - 1) * this.width; | |
case Z: | |
return MathHelper.floorMod(x * mod + z, this.width) + (this.height - MathHelper.floorMod(y + z, this.height) - 1) * this.width; | |
} | |
return 0; | |
} | |
public static final class Builder { | |
private final SpriteIdentifier spriteId; | |
private final List<SpriteIdentifier> spriteIdentifiers = new ArrayList<>(); | |
private int width, height; | |
private SpriteIdentifier overlay; | |
public Builder(SpriteIdentifier spriteId) { | |
this.spriteId = spriteId; | |
} | |
public Builder row(SpriteIdentifier... spriteIdentifiers) { | |
if (spriteIdentifiers.length > 0) { | |
if (this.width > 0 && spriteIdentifiers.length != this.width) { | |
throw new RuntimeException("Row is not of correct size! Expected " + this.width + " found " + spriteIdentifiers.length + "."); | |
} | |
if (this.width == 0) this.width = spriteIdentifiers.length; | |
++this.height; | |
this.spriteIdentifiers.addAll(Arrays.asList(spriteIdentifiers)); | |
} | |
return this; | |
} | |
public Builder of(SpriteIdentifier base, int startingIndex, int width, int height) { | |
this.width = width; | |
this.height = height; | |
for (int i = startingIndex; i < startingIndex + width * height; ++i) { | |
this.spriteIdentifiers.add(new SpriteIdentifier(base.getAtlasId(), new Identifier(base.getTextureId().toString() + i))); | |
} | |
return this; | |
} | |
public Builder overlay(SpriteIdentifier overlay) { | |
this.overlay = overlay; | |
return this; | |
} | |
public TessellatingBlockModel build() { | |
return new TessellatingBlockModel(this.spriteId, this.width, this.height, this.spriteIdentifiers, this.overlay); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment