filename:
common/src/main/java/rearth/oritech/client/renderers/LaserArmModel.java
branch:
1.21
back to repo
package rearth.oritech.client.renderers;
import net.minecraft.client.MinecraftClient;
import net.minecraft.state.property.Properties;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import org.joml.Vector2f;
import rearth.oritech.Oritech;
import rearth.oritech.block.entity.interaction.LaserArmBlockEntity;
import rearth.oritech.util.Geometry;
import software.bernie.geckolib.animatable.GeoAnimatable;
import software.bernie.geckolib.animation.AnimationState;
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.model.DefaultedBlockGeoModel;
import java.util.HashMap;
public class LaserArmModel<T extends LaserArmBlockEntity & GeoAnimatable> extends DefaultedBlockGeoModel<T> {
private static final HashMap<Long, ModelRenderData> additionalData = new HashMap<>();
private static final HashMap<Long, Vec3d> drillOffsets = new HashMap<>();
private Vec3d lastActivePlayerPos = Vec3d.ZERO;
public LaserArmModel(String subpath) {
super(Oritech.id(subpath));
}
private ModelRenderData getById(long id) {
return additionalData.computeIfAbsent(id, s -> new ModelRenderData(0, 0, getAnimationProcessor().getBone("pivotX"), getAnimationProcessor().getBone("pivotY")));
}
private Vec3d getOffsetByDrillId(long id, T laserEntity) {
return drillOffsets.computeIfAbsent(id, s -> {
var drillFacing = laserEntity.getWorld().getBlockState(laserEntity.getCurrentTarget()).get(Properties.HORIZONTAL_FACING);
return Geometry.rotatePosition(new Vec3d(1, 1.4, 0), drillFacing);
});
}
@Override
public void setCustomAnimations(T laserEntity, long instanceId, AnimationState<T> animationState) {
Vec3d target;
var isIdle = false;
if (laserEntity.getCurrentTarget() == null || laserEntity.getCurrentTarget() == BlockPos.ORIGIN) {
target = getIdleTarget(laserEntity);
isIdle = true;
} else {
target = laserEntity.getVisualTarget();
}
if (target == null || target == Vec3d.ZERO) return;
if (!isIdle && laserEntity.isTargetingDeepdrill(laserEntity.getWorld().getBlockState(laserEntity.getCurrentTarget()).getBlock())) {
var drillId = laserEntity.getCurrentTarget().asLong();
var offset = getOffsetByDrillId(drillId, laserEntity);
target = target.add(offset);
}
var ownPos = Vec3d.of(laserEntity.getPos());
var offset = target.subtract(ownPos.add(0, 1.7 - 0.5, 0)); // add 1.55 to get to height of pivotX, minus block center offset
// thanks to: https://math.stackexchange.com/questions/878785/how-to-find-an-angle-in-range0-360-between-2-vectors
var offsetY = new Vector2f((float) offset.getX(), (float) offset.getZ());
var forwardY = new Vector2f(0, 1);
var detY = determinant(offsetY, forwardY);
var dotY = offsetY.dot(forwardY);
var angleY = Math.atan2(detY, dotY);
// to create a 2d vector in a plane based on normal angleY
var lengthY = offsetY.length();
var heightDiff = offset.getY();
var offsetX = new Vector2f(lengthY, (float) heightDiff);
var forwardX = new Vector2f(0, 1);
var detX = determinant(offsetX, forwardX);
var dotX = offsetX.dot(forwardX);
var angleX = Math.atan2(detX, dotX);
angleX -= 42.5 * Geometry.DEG_TO_RAD; //to offset for parent bone rotations
var data = getById(instanceId);
if (data.boneX != null) {
var newRotY = lerp(data.angleY, (float) angleY, 0.06f);
var newRotX = lerp(data.angleX, (float) angleX, 0.06f);
data.boneY.setRotY(newRotY);
data.boneX.setRotX(newRotX);
data.angleY = newRotY;
data.angleX = newRotX;
}
}
private Vec3d getIdleTarget(T entity) {
var intervalA = (entity.getWorld().getTime() / 20) % 2 == 0;
var intervalB = (entity.getWorld().getTime() / 60) % 2 == 0;
var intervalC = (entity.getWorld().getTime() / 50) % 3 == 0;
var intervalD = (entity.getWorld().getTime() / 80) % 2 == 0;
var offsetB = new Vec3d(0, intervalA ? 0.4f : -1.8f, 0);
var offsetC = new Vec3d(intervalB ? 0.4 : -0.4, 0, intervalC ? -0.7 : 0.4);
var offsetD = new Vec3d(0, intervalD ? 0.2f : -3f, intervalB ? 0.3 : -0.3);
if (entity.getWorld().getRandom().nextFloat() > 0.9f) {
lastActivePlayerPos = MinecraftClient.getInstance().player.getEyePos();
}
if (lastActivePlayerPos.equals(Vec3d.ZERO))
return Vec3d.ZERO;
return lastActivePlayerPos.add(offsetB).add(offsetC).add(offsetD);
}
public static float lerp(float a, float b, float f) {
return a + f * (b - a);
}
public static float determinant(Vector2f a, Vector2f b) {
return a.x * b.y - a.y * b.x;
}
private static class ModelRenderData {
protected float angleY;
protected float angleX;
protected GeoBone boneX;
protected GeoBone boneY;
public ModelRenderData(float angleX, float angleY, GeoBone boneX, GeoBone boneY) {
this.angleY = angleY;
this.angleX = angleX;
this.boneX = boneX;
this.boneY = boneY;
}
}
}