Use platform-specific hooks for rendering BakedModels
This commit is contained in:
parent
42d5771e8e
commit
cebdfce06b
|
@ -4,8 +4,13 @@
|
|||
|
||||
package dan200.computercraft.client.platform;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface ClientPlatformHelper extends dan200.computercraft.impl.client.ClientPlatformHelper {
|
||||
static ClientPlatformHelper get() {
|
||||
|
@ -18,4 +23,16 @@ static ClientPlatformHelper get() {
|
|||
* @param message The message to send.
|
||||
*/
|
||||
void sendToServer(NetworkMessage<ServerNetworkContext> message);
|
||||
|
||||
/**
|
||||
* Render a {@link BakedModel}, using any loader-specific hooks.
|
||||
*
|
||||
* @param transform The current matrix transformation to apply.
|
||||
* @param buffers The current pool of render buffers.
|
||||
* @param model The model to draw.
|
||||
* @param lightmapCoord The current packed lightmap coordinate.
|
||||
* @param overlayLight The current overlay light.
|
||||
* @param tints Block colour tints to apply to the model.
|
||||
*/
|
||||
void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import dan200.computercraft.client.model.turtle.ModelTransformer;
|
||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utilities for rendering {@link BakedModel}s and {@link BakedQuad}s.
|
||||
*/
|
||||
public final class ModelRenderer {
|
||||
private ModelRenderer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a list of {@linkplain BakedQuad quads} to a buffer.
|
||||
* <p>
|
||||
* This is not intended to be used directly, but instead by {@link ClientPlatformHelper#renderBakedModel(PoseStack, MultiBufferSource, BakedModel, int, int, int[])}. The
|
||||
* implementation here is pretty similar to {@link ItemRenderer#renderQuadList(PoseStack, VertexConsumer, List, ItemStack, int, int)},
|
||||
* but supports inverted quads (i.e. those with a negative scale).
|
||||
*
|
||||
* @param transform The current matrix transformation to apply.
|
||||
* @param buffer The buffer to draw to.
|
||||
* @param quads The quads to draw.
|
||||
* @param lightmapCoord The current packed lightmap coordinate.
|
||||
* @param overlayLight The current overlay light.
|
||||
* @param tints Block colour tints to apply to the model.
|
||||
*/
|
||||
public static void renderQuads(PoseStack transform, VertexConsumer buffer, List<BakedQuad> quads, int lightmapCoord, int overlayLight, @Nullable int[] tints) {
|
||||
var matrix = transform.last();
|
||||
var inverted = matrix.pose().determinant() < 0;
|
||||
|
||||
for (var bakedquad : quads) {
|
||||
var tint = -1;
|
||||
if (tints != null && bakedquad.isTinted()) {
|
||||
var idx = bakedquad.getTintIndex();
|
||||
if (idx >= 0 && idx < tints.length) tint = tints[bakedquad.getTintIndex()];
|
||||
}
|
||||
|
||||
var r = (float) (tint >> 16 & 255) / 255.0F;
|
||||
var g = (float) (tint >> 8 & 255) / 255.0F;
|
||||
var b = (float) (tint & 255) / 255.0F;
|
||||
if (inverted) {
|
||||
putBulkQuadInvert(buffer, matrix, bakedquad, r, g, b, lightmapCoord, overlayLight);
|
||||
} else {
|
||||
buffer.putBulkData(matrix, bakedquad, r, g, b, lightmapCoord, overlayLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of {@link VertexConsumer#putBulkData(PoseStack.Pose, BakedQuad, float, float, float, int, int)} for
|
||||
* when the matrix is inverted.
|
||||
*
|
||||
* @param buffer The buffer to draw to.
|
||||
* @param pose The current matrix stack.
|
||||
* @param quad The quad to draw.
|
||||
* @param red The red tint of this quad.
|
||||
* @param green The green tint of this quad.
|
||||
* @param blue The blue tint of this quad.
|
||||
* @param lightmapCoord The lightmap coordinate
|
||||
* @param overlayLight The overlay light.
|
||||
*/
|
||||
private static void putBulkQuadInvert(VertexConsumer buffer, PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int lightmapCoord, int overlayLight) {
|
||||
var matrix = pose.pose();
|
||||
// It's a little dubious to transform using this matrix rather than the normal matrix. This mirrors the logic in
|
||||
// Direction.rotate (so not out of nowhere!), but is a little suspicious.
|
||||
var dirNormal = quad.getDirection().getNormal();
|
||||
var vector = new Vector4f();
|
||||
|
||||
matrix.transform(dirNormal.getX(), dirNormal.getY(), dirNormal.getZ(), 0.0f, vector).normalize();
|
||||
float normalX = vector.x(), normalY = vector.y(), normalZ = vector.z();
|
||||
|
||||
var vertices = quad.getVertices();
|
||||
for (var vertex : ModelTransformer.INVERSE_ORDER) {
|
||||
var i = vertex * ModelTransformer.STRIDE;
|
||||
|
||||
var x = Float.intBitsToFloat(vertices[i]);
|
||||
var y = Float.intBitsToFloat(vertices[i + 1]);
|
||||
var z = Float.intBitsToFloat(vertices[i + 2]);
|
||||
|
||||
matrix.transform(x, y, z, 1, vector);
|
||||
|
||||
var u = Float.intBitsToFloat(vertices[i + 4]);
|
||||
var v = Float.intBitsToFloat(vertices[i + 5]);
|
||||
buffer.vertex(
|
||||
vector.x(), vector.y(), vector.z(),
|
||||
red, green, blue, 1.0F, u, v, overlayLight, lightmapCoord,
|
||||
normalX, normalY, normalZ
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,23 +5,18 @@
|
|||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.math.Axis;
|
||||
import com.mojang.math.Transformation;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.client.model.turtle.ModelTransformer;
|
||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||
import dan200.computercraft.shared.util.DirectionUtil;
|
||||
import dan200.computercraft.shared.util.Holiday;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.Sheets;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
|
@ -31,10 +26,8 @@
|
|||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity> {
|
||||
private static final ModelResourceLocation NORMAL_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_normal", "inventory");
|
||||
|
@ -109,23 +102,22 @@ public void render(TurtleBlockEntity turtle, float partialTicks, PoseStack trans
|
|||
var family = turtle.getFamily();
|
||||
var overlay = turtle.getOverlay();
|
||||
|
||||
var buffer = buffers.getBuffer(Sheets.translucentCullBlockSheet());
|
||||
renderModel(transform, buffer, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour });
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour });
|
||||
|
||||
// Render the overlay
|
||||
var overlayModel = getTurtleOverlayModel(overlay, Holiday.getCurrent() == Holiday.CHRISTMAS);
|
||||
if (overlayModel != null) {
|
||||
renderModel(transform, buffer, lightmapCoord, overlayLight, overlayModel, null);
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, overlayModel, null);
|
||||
}
|
||||
|
||||
// Render the upgrades
|
||||
renderUpgrade(transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks);
|
||||
renderUpgrade(transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks);
|
||||
renderUpgrade(transform, buffers, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks);
|
||||
renderUpgrade(transform, buffers, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks);
|
||||
|
||||
transform.popPose();
|
||||
}
|
||||
|
||||
private void renderUpgrade(PoseStack transform, VertexConsumer renderer, int lightmapCoord, int overlayLight, TurtleBlockEntity turtle, TurtleSide side, float f) {
|
||||
private void renderUpgrade(PoseStack transform, MultiBufferSource buffers, int lightmapCoord, int overlayLight, TurtleBlockEntity turtle, TurtleSide side, float f) {
|
||||
var upgrade = turtle.getUpgrade(side);
|
||||
if (upgrade == null) return;
|
||||
transform.pushPose();
|
||||
|
@ -137,15 +129,15 @@ private void renderUpgrade(PoseStack transform, VertexConsumer renderer, int lig
|
|||
|
||||
var model = TurtleUpgradeModellers.getModel(upgrade, turtle.getAccess(), side);
|
||||
pushPoseFromTransformation(transform, model.getMatrix());
|
||||
renderModel(transform, renderer, lightmapCoord, overlayLight, model.getModel(), null);
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, model.getModel(), null);
|
||||
transform.popPose();
|
||||
|
||||
transform.popPose();
|
||||
}
|
||||
|
||||
private void renderModel(PoseStack transform, VertexConsumer renderer, int lightmapCoord, int overlayLight, ResourceLocation modelLocation, @Nullable int[] tints) {
|
||||
private void renderModel(PoseStack transform, MultiBufferSource buffers, int lightmapCoord, int overlayLight, ResourceLocation modelLocation, @Nullable int[] tints) {
|
||||
var modelManager = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getModelManager();
|
||||
renderModel(transform, renderer, lightmapCoord, overlayLight, ClientPlatformHelper.get().getModel(modelManager, modelLocation), tints);
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, ClientPlatformHelper.get().getModel(modelManager, modelLocation), tints);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,75 +151,8 @@ private void renderModel(PoseStack transform, VertexConsumer renderer, int light
|
|||
* @param tints Tints for the quads, as an array of RGB values.
|
||||
* @see net.minecraft.client.renderer.block.ModelBlockRenderer#renderModel
|
||||
*/
|
||||
private void renderModel(PoseStack transform, VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model, @Nullable int[] tints) {
|
||||
for (var facing : DirectionUtil.FACINGS) {
|
||||
random.setSeed(42);
|
||||
renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, facing, random), tints);
|
||||
}
|
||||
|
||||
random.setSeed(42);
|
||||
renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, null, random), tints);
|
||||
}
|
||||
|
||||
private static void renderQuads(PoseStack transform, VertexConsumer buffer, int lightmapCoord, int overlayLight, List<BakedQuad> quads, @Nullable int[] tints) {
|
||||
var matrix = transform.last();
|
||||
var inverted = matrix.pose().determinant() < 0;
|
||||
|
||||
for (var bakedquad : quads) {
|
||||
var tint = -1;
|
||||
if (tints != null && bakedquad.isTinted()) {
|
||||
var idx = bakedquad.getTintIndex();
|
||||
if (idx >= 0 && idx < tints.length) tint = tints[bakedquad.getTintIndex()];
|
||||
}
|
||||
|
||||
var r = (float) (tint >> 16 & 255) / 255.0F;
|
||||
var g = (float) (tint >> 8 & 255) / 255.0F;
|
||||
var b = (float) (tint & 255) / 255.0F;
|
||||
if (inverted) {
|
||||
putBulkQuadInvert(buffer, matrix, bakedquad, r, g, b, lightmapCoord, overlayLight);
|
||||
} else {
|
||||
buffer.putBulkData(matrix, bakedquad, r, g, b, lightmapCoord, overlayLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of {@link VertexConsumer#putBulkData(PoseStack.Pose, BakedQuad, float, float, float, int, int)} for
|
||||
* when the matrix is inverted.
|
||||
*
|
||||
* @param buffer The buffer to draw to.
|
||||
* @param pose The current matrix stack.
|
||||
* @param quad The quad to draw.
|
||||
* @param red The red tint of this quad.
|
||||
* @param green The green tint of this quad.
|
||||
* @param blue The blue tint of this quad.
|
||||
* @param lightmapCoord The lightmap coordinate
|
||||
* @param overlayLight The overlay light.
|
||||
*/
|
||||
private static void putBulkQuadInvert(VertexConsumer buffer, PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int lightmapCoord, int overlayLight) {
|
||||
var matrix = pose.pose();
|
||||
// It's a little dubious to transform using this matrix rather than the normal matrix. This mirrors the logic in
|
||||
// Direction.rotate (so not out of nowhere!), but is a little suspicious.
|
||||
var dirNormal = quad.getDirection().getNormal();
|
||||
var normal = matrix.transform(new Vector4f(dirNormal.getX(), dirNormal.getY(), dirNormal.getZ(), 0.0f)).normalize();
|
||||
|
||||
var vertices = quad.getVertices();
|
||||
for (var vertex : ModelTransformer.INVERSE_ORDER) {
|
||||
var i = vertex * ModelTransformer.STRIDE;
|
||||
|
||||
var x = Float.intBitsToFloat(vertices[i]);
|
||||
var y = Float.intBitsToFloat(vertices[i + 1]);
|
||||
var z = Float.intBitsToFloat(vertices[i + 2]);
|
||||
var transformed = matrix.transform(new Vector4f(x, y, z, 1));
|
||||
|
||||
var u = Float.intBitsToFloat(vertices[i + 4]);
|
||||
var v = Float.intBitsToFloat(vertices[i + 5]);
|
||||
buffer.vertex(
|
||||
transformed.x(), transformed.y(), transformed.z(),
|
||||
red, green, blue, 1.0F, u, v, overlayLight, lightmapCoord,
|
||||
normal.x(), normal.y(), normal.z()
|
||||
);
|
||||
}
|
||||
private void renderModel(PoseStack transform, MultiBufferSource renderer, int lightmapCoord, int overlayLight, BakedModel model, @Nullable int[] tints) {
|
||||
ClientPlatformHelper.get().renderBakedModel(transform, renderer, model, lightmapCoord, overlayLight, tints);
|
||||
}
|
||||
|
||||
private static void pushPoseFromTransformation(PoseStack stack, Transformation transformation) {
|
||||
|
|
|
@ -5,17 +5,28 @@
|
|||
package dan200.computercraft.client.platform;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.client.model.FoiledModel;
|
||||
import dan200.computercraft.client.render.ModelRenderer;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||
import dan200.computercraft.shared.platform.NetworkHandler;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.Sheets;
|
||||
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@AutoService(dan200.computercraft.impl.client.ClientPlatformHelper.class)
|
||||
public class ClientPlatformHelperImpl implements ClientPlatformHelper {
|
||||
private static final RandomSource random = RandomSource.create(0);
|
||||
|
||||
@Override
|
||||
public void sendToServer(NetworkMessage<ServerNetworkContext> message) {
|
||||
Minecraft.getInstance().player.connection.send(NetworkHandler.encodeServer(message));
|
||||
|
@ -31,4 +42,17 @@ public BakedModel getModel(ModelManager manager, ResourceLocation location) {
|
|||
public BakedModel createdFoiledModel(BakedModel model) {
|
||||
return new FoiledModel(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints) {
|
||||
// Unfortunately we can't call Fabric's emitItemQuads here, as there's no way to obtain a RenderContext via the
|
||||
// API. Instead, we special case our FoiledModel, and just render everything else normally.
|
||||
var buffer = ItemRenderer.getFoilBuffer(buffers, Sheets.translucentCullBlockSheet(), true, model instanceof FoiledModel);
|
||||
|
||||
for (var faceIdx = 0; faceIdx <= ModelHelper.NULL_FACE_ID; faceIdx++) {
|
||||
var face = ModelHelper.faceFromIndex(faceIdx);
|
||||
random.setSeed(42);
|
||||
ModelRenderer.renderQuads(transform, buffer, model.getQuads(null, face, random), lightmapCoord, overlayLight, tints);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,16 +5,29 @@
|
|||
package dan200.computercraft.client.platform;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.client.model.FoiledModel;
|
||||
import dan200.computercraft.client.render.ModelRenderer;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||
import dan200.computercraft.shared.platform.NetworkHandler;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.client.model.data.ModelData;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
|
||||
@AutoService(dan200.computercraft.impl.client.ClientPlatformHelper.class)
|
||||
public class ClientPlatformHelperImpl implements ClientPlatformHelper {
|
||||
private static final RandomSource random = RandomSource.create(0);
|
||||
private static final Direction[] directions = Arrays.copyOf(Direction.values(), 7);
|
||||
|
||||
@Override
|
||||
public BakedModel getModel(ModelManager manager, ResourceLocation location) {
|
||||
return manager.getModel(location);
|
||||
|
@ -29,4 +42,16 @@ public BakedModel createdFoiledModel(BakedModel model) {
|
|||
public void sendToServer(NetworkMessage<ServerNetworkContext> message) {
|
||||
NetworkHandler.sendToServer(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints) {
|
||||
for (var renderType : model.getRenderTypes(ItemStack.EMPTY, true)) {
|
||||
var buffer = buffers.getBuffer(renderType);
|
||||
for (var face : directions) {
|
||||
random.setSeed(42);
|
||||
var quads = model.getQuads(null, face, random, ModelData.EMPTY, renderType);
|
||||
ModelRenderer.renderQuads(transform, buffer, quads, lightmapCoord, overlayLight, tints);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue