RyanHub – file viewer
filename: common/src/main/java/rearth/oritech/client/renderers/LaserArmRenderer.java
branch: 1.21
back to repo
package rearth.oritech.client.renderers;

import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.*;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.state.property.Properties;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import rearth.oritech.block.entity.interaction.LaserArmBlockEntity;
import rearth.oritech.client.init.ParticleContent;
import rearth.oritech.util.Geometry;
import software.bernie.geckolib.animatable.GeoAnimatable;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoBlockRenderer;
import software.bernie.geckolib.renderer.layer.AutoGlowingGeoLayer;

import java.util.HashMap;

import static net.minecraft.client.render.RenderPhase.VIEW_OFFSET_Z_LAYERING;

public class LaserArmRenderer<T extends LaserArmBlockEntity & GeoAnimatable> extends GeoBlockRenderer<T> {
    public LaserArmRenderer(String modelPath) {
        super(new LaserArmModel<>(modelPath));
        addRenderLayer(new AutoGlowingGeoLayer<>(this));
    }
    
    // Modified RenderLayer.LINES
    public static final RenderLayer.MultiPhase CUSTOM_LINES = RenderLayer.of("lines", VertexFormats.LINES, VertexFormat.DrawMode.LINES, 1536, RenderLayer.MultiPhaseParameters.builder().program(RenderPhase.LINES_PROGRAM).layering(VIEW_OFFSET_Z_LAYERING).transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY).target(RenderPhase.ITEM_ENTITY_TARGET).writeMaskState(RenderPhase.ALL_MASK).cull(RenderPhase.DISABLE_CULLING).build(false));
    private static final HashMap<LaserArmBlockEntity, Vec3d> cachedOffsets = new HashMap<>();
    
    
    @Override
    public int getRenderDistance() {
        return 128;
    }
    @Override
    public boolean rendersOutsideBoundingBox(T blockEntity) {
        return true;
    }
    
    @Override
    public void postRender(MatrixStack matrices, T laserEntity, BakedGeoModel model, VertexConsumerProvider bufferSource, @Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int colour) {
        super.postRender(matrices, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, colour);
        
        if (laserEntity.getCurrentTarget() == null || !laserEntity.isFiring()) return;
        
        var startOffset = new Vector3f(0, 1.65f, 0);
        var startPos = Vec3d.of(laserEntity.getPos()).add(0.5, 1.55, 0.5);
        
        var targetPos = laserEntity.getVisualTarget();
        var targetBlock = laserEntity.getWorld().getBlockState(laserEntity.getCurrentTarget()).getBlock();
        if (laserEntity.isTargetingAtomicForge(targetBlock)) { // adjust so the beam end faces one of the corner pillars
            var moveX = 0.5;
            var moveZ = 0.5;
            if (startPos.x < targetPos.x) moveX = -0.5;
            if (startPos.z < targetPos.z) moveZ = -0.5;
            targetPos = targetPos.add(moveX, 0.5, moveZ);
        } else if (laserEntity.isTargetingDeepdrill(targetBlock)) {
            var offset = cachedOffsets.computeIfAbsent(laserEntity, id -> idToOffset(id.getPos(), 0.5f, laserEntity.getWorld(), laserEntity.getCurrentTarget()));
            targetPos = targetPos.add(offset);
        }
        
        if (laserEntity.lastRenderPosition == null) laserEntity.lastRenderPosition = targetPos;
        targetPos = lerp(laserEntity.lastRenderPosition, targetPos, 0.06f);
        laserEntity.lastRenderPosition = targetPos;
        
        var targetPosOffset = targetPos.subtract(Vec3d.of(laserEntity.getPos()));
        
        var forward = targetPos.subtract(startPos).normalize();
        if (!laserEntity.isTargetingEnergyContainer() && !laserEntity.isTargetingBuddingAmethyst())
            ParticleContent.LASER_BEAM_EFFECT.spawn(laserEntity.getWorld(), targetPos.add(0.5, 0, 0.5).subtract(forward.multiply(0.6)));
        
        var cross = forward.crossProduct(new Vec3d(0, 1, 0));
        
        matrices.push();
        var lineConsumer = bufferSource.getBuffer(CUSTOM_LINES);
        
        // to prevent line from becoming too big when further away, as the size seems to be in screen space
        var camPos = MinecraftClient.getInstance().cameraEntity.getPos();
        var camDist = camPos.subtract(startPos).length();
        var widthMultiplier = 1f;
        if (camDist > 20)
            widthMultiplier = (float) (camDist / 20f);
        RenderSystem.lineWidth((float) (Math.sin((laserEntity.getWorld().getTime() + partialTick) * 0.3) * 2 + 7) / widthMultiplier);
        
        lineConsumer.vertex(matrices.peek().getPositionMatrix(), startOffset.x, startOffset.y, startOffset.z)
          .color(138, 242, 223, 255)
          .light(packedLight)
          .overlay(packedOverlay)
          .normal(0, 1, 0);
        lineConsumer.vertex(matrices.peek().getPositionMatrix(), (float) targetPosOffset.x, (float) targetPosOffset.y, (float) targetPosOffset.z)
          .color(19, 91, 80, 255)
          .light(packedLight)
          .overlay(packedOverlay)
          .normal(1, 0, 0);
        
        // render a second one at right angle to first one
        lineConsumer.vertex(matrices.peek().getPositionMatrix(), startOffset.x, startOffset.y, startOffset.z)
          .color(138, 242, 223, 255)
          .light(packedLight)
          .overlay(packedOverlay)
          .normal((float) cross.x, (float) cross.y, (float) cross.z);
        lineConsumer.vertex(matrices.peek().getPositionMatrix(), (float) targetPosOffset.x, (float) targetPosOffset.y, (float) targetPosOffset.z)
          .color(19, 91, 80, 255)
          .light(packedLight)
          .overlay(packedOverlay)
          .normal((float) cross.x, (float) cross.y, (float) cross.z);
        
        matrices.pop();
    }
    
    public static Vec3d idToOffset(BlockPos source, float range, World world, BlockPos targetPos) {
        
        var drillFacing = world.getBlockState(targetPos).get(Properties.HORIZONTAL_FACING);
        var drillCenter = Geometry.rotatePosition(new Vec3d(1, 1.4, 0), drillFacing);
        
        var random = Random.create(source.asLong());
        return new Vec3d((random.nextFloat() * 2 - 1) * range, (random.nextFloat() * 2 - 1) * range, (random.nextFloat() * 2 - 1) * range).add(drillCenter);
    }
    
    public static Vec3d lerp(Vec3d a, Vec3d b, float f) {
        return new Vec3d(lerp(a.x, b.x, f), lerp(a.y, b.y, f), lerp(a.z, b.z, f));
    }
    
    public static double lerp(double a, double b, double f) {
        return a + f * (b - a);
    }
}