https://docs.fabricmc.net/zh_cn/1.21/develop/rendering/basic-concepts
Render triangles in Hud
跟fabric文档一样使用 HudRenderCallback 事件:
HudRenderCallback.EVENT.register((drawContext, tickDeltaManager) -> {
// Get the transformation matrix from the matrix stack, alongside the tessellator instance and a new buffer builder.
MatrixStack matrixStack = drawContext.getMatrices();
Matrix4f transformationMatrix = matrixStack.peek().getPositionMatrix();
Tessellator tessellator = Tessellator.getInstance();
// Begin a triangle strip buffer using the POSITION_COLOR vertex format.
BufferBuilder buffer = tessellator.begin(VertexFormat.DrawMode.TRIANGLE_STRIP, VertexFormats.POSITION_COLOR);
// Write our vertices, Z doesn't really matter since it's on the HUD.
buffer.vertex(transformationMatrix, 20, 20, 5).color(0xFF414141);
buffer.vertex(transformationMatrix, 5, 40, 5).color(0xFF000000);
buffer.vertex(transformationMatrix, 35, 40, 5).color(0xFF000000);
buffer.vertex(transformationMatrix, 20, 60, 5).color(0xFF414141);
// We'll get to this bit in the next section.
RenderSystem.setShader(GameRenderer::getPositionColorProgram);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
// Draw the buffer onto the screen.
BufferRenderer.drawWithGlobalProgram(buffer.end());
});代码的结果同样查看文档。
Entity ESP
if(entity instanceof PlayerEntity || entity instanceof AnimalEntity){
IEntity e = (IEntity) entity; // 把 entity 转成 Mixin 后的IEntity
Box box = e.getDimensions().getBoxAt(new Vec3d(
interpolate(entity.lastRenderX, entity.getX(), mc.getRenderTickCounter().getTickDelta(true)),
interpolate(entity.lastRenderY, entity.getY(), mc.getRenderTickCounter().getTickDelta(true)),
interpolate(entity.lastRenderZ, entity.getZ(), mc.getRenderTickCounter().getTickDelta(true)))).expand(0,0.1,0);
draw3DBox(matrixStack, box,Color.CYAN);
}public static void draw3DBox(MatrixStack matrixStack, Box box, Color color){
// negate(): Creates a vector with the same length but with the opposite direction.
box = box.offset(mc.gameRenderer.getCamera().getPos().negate());
Matrix4f matrix = matrixStack.peek().getPositionMatrix();
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferBuilder = tessellator.begin(VertexFormat.DrawMode.DEBUG_LINES, VertexFormats.POSITION);
RenderSystem.setShaderColor(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 0.8f);
RenderSystem.setShader(ShaderProgramKeys.POSITION);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.minZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.minY, (float) box.minZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.minY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.minY, (float)box.maxZ);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.minZ);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.minZ);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.minZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.minY, (float) box.minZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.minZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.minY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.minZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.minZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.minZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.maxZ);
bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.minZ);
// Draw the buffer onto the screen.
BufferRenderer.drawWithGlobalProgram(bufferBuilder.end());
}// 最好在绘制前后包一层 `RenderSystem.enable/disableXXX` 确保深度测试 / blend / 矩阵恢复
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.disableDepthTest();
RenderSystem.depthMask(false);
// 渲染代码 ...
// 恢复渲染状态
RenderSystem.enableDepthTest();
RenderSystem.disableBlend();
RenderSystem.depthMask(true);另一种实现方式
override fun onRender3D(event: Render3DEvent) {
val target = mc.crosshairTarget
if (target is BlockHitResult) {
val shape = mc.world
?.getBlockState(target.blockPos)
?.getOutlineShape(mc.world, target.blockPos)
if (shape?.isEmpty == true) return
var box = shape?.boundingBox
box = box?.offset(target.blockPos)
if (box != null) {
RenderUtil.drawBox(event.matrix,box,Color.CYAN,1.0)
}
}
}fun drawBox(stack: MatrixStack, box: Box, c: Color, lineWidth: Double) {
val minX = (box.minX - mc.entityRenderDispatcher.camera.pos.getX())
val minY = (box.minY - mc.entityRenderDispatcher.camera.pos.getY())
val minZ = (box.minZ - mc.entityRenderDispatcher.camera.pos.getZ())
val maxX = (box.maxX - mc.entityRenderDispatcher.camera.pos.getX())
val maxY = (box.maxY - mc.entityRenderDispatcher.camera.pos.getY())
val maxZ = (box.maxZ - mc.entityRenderDispatcher.camera.pos.getZ())
val bufferBuilder = Tessellator.getInstance()
.begin(VertexFormat.DrawMode.LINES, VertexFormats.POSITION_COLOR_NORMAL)
VertexRendering.drawBox(
stack,
bufferBuilder,
minX,
minY,
minZ,
maxX,
maxY,
maxZ,
c.red / 255f,
c.green / 255f,
c.blue / 255f,
c.alpha / 255f
)
Layers.getGlobalLines(lineWidth).draw(bufferBuilder.end())
}object Layers {
private val GLOBAL_QUADS: RenderLayer
private var GLOBAL_LINES: Function<Double, RenderLayer>
fun getGlobalLines(width: Double): RenderLayer {
return GLOBAL_LINES.apply(width)
}
private fun builder(): RenderLayer.MultiPhaseParameters.Builder {
return RenderLayer.MultiPhaseParameters.builder()
}
private fun empty(): RenderLayer.MultiPhaseParameters {
return builder().build(false)
}
init {
GLOBAL_QUADS = RenderLayer.of("global_fill", 156, GLOBAL_QUADS_PIPELINE, empty())
GLOBAL_LINES = Util.memoize { l: Double ->
val width = LineWidth(OptionalDouble.of(l))
RenderLayer.of(
"global_lines",
156,
GLOBAL_LINES_PIPELINE,
builder().lineWidth(width).build(false)
)
}
}
}