1.21.8
This commit is contained in:
@@ -10,8 +10,9 @@ public class KeplerClient implements ClientModInitializer {
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
var renderer = new TrajectoryRenderer();
|
||||
ClientEntityEvents.ENTITY_LOAD.register((entity, _a) -> {
|
||||
if (entity instanceof PlayerEntity) TrajectoryRenderer.setupRenderLine((PlayerEntity) entity);
|
||||
if (entity instanceof PlayerEntity) renderer.setupRenderLine((PlayerEntity) entity);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.walruslab.kepler.render;
|
||||
|
||||
import net.minecraft.entity.projectile.ArrowEntity;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.walruslab.kepler.trajectory.Trajectory;
|
||||
import org.walruslab.kepler.trajectory.TrajectoryHit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ParallelTrajectoryManager {
|
||||
private boolean isDataAvailable = false;
|
||||
private List<TrajectoryHit> lastHits;
|
||||
|
||||
public void calculateTrajectories(List<ArrowEntity> arrows) {
|
||||
if (!isDataAvailable) {
|
||||
AtomicInteger i = new AtomicInteger();
|
||||
lastHits = new ArrayList<>();
|
||||
for (var arrow : arrows) {
|
||||
var t = new Thread(() -> {
|
||||
var hit = calculateArrow(arrow);
|
||||
if (hit != null) {
|
||||
lastHits.add(hit);
|
||||
}
|
||||
i.addAndGet(1);
|
||||
|
||||
if (arrows.size() == i.get()) {
|
||||
isDataAvailable = true;
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<TrajectoryHit> getLastHits() {
|
||||
if (isDataAvailable) {
|
||||
isDataAvailable = false;
|
||||
return lastHits;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
private @Nullable TrajectoryHit calculateArrow(ArrowEntity arrow) {
|
||||
if (arrow.groundCollision) return null;
|
||||
|
||||
var pos = arrow.getPos();
|
||||
var velocity = arrow.getVelocity();
|
||||
|
||||
var tr = new Trajectory(pos.toVector3f(), velocity.toVector3f());
|
||||
var hit = tr.getTrajectoryPoints(arrow.getWorld(), 0.1f, arrow.getOwner());
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,149 +1,125 @@
|
||||
package org.walruslab.kepler.render;
|
||||
|
||||
import com.mojang.blaze3d.opengl.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
|
||||
import net.jpountz.util.Utils;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.Tessellator;
|
||||
import net.minecraft.client.render.VertexFormats;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.projectile.ArrowEntity;
|
||||
import net.minecraft.item.BowItem;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.predicate.entity.EntityPredicate;
|
||||
import net.minecraft.predicate.entity.EntityPredicates;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.TypeFilter;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.Box;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.Vec3i;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.walruslab.kepler.trajectory.Trajectory;
|
||||
import org.walruslab.kepler.trajectory.TrajectoryHit;
|
||||
import org.walruslab.kepler.trajectory.item.ShootableItemFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class TrajectoryRenderer {
|
||||
public static void setupRenderLine(PlayerEntity player) {
|
||||
private final ParallelTrajectoryManager trajectoryManager = new ParallelTrajectoryManager();
|
||||
|
||||
public void setupRenderLine(PlayerEntity player) {
|
||||
player.sendMessage(Text.of("Kepler: TrajectoryRenderer setupRenderLine"), false);
|
||||
WorldRenderEvents.END.register((drawContext) -> {
|
||||
// get all error entity in player world
|
||||
var world = player.getWorld();
|
||||
|
||||
var entities = world.getEntitiesByClass(LivingEntity.class, new Box(player.getPos().subtract(Vec3d.of(new Vec3i(100, 100, 100))), player.getPos().add(Vec3d.of(new Vec3i(100, 100, 100)))), EntityPredicates.VALID_ENTITY);
|
||||
var arrows = world.getEntitiesByType(TypeFilter.instanceOf(ArrowEntity.class), new Box(player.getPos().subtract(Vec3d.of(new Vec3i(100, 100, 100))), player.getPos().add(Vec3d.of(new Vec3i(100, 100, 100)))), EntityPredicates.VALID_ENTITY);
|
||||
|
||||
for (ArrowEntity arrow : arrows) {
|
||||
if (arrow.groundCollision) return;
|
||||
trajectoryManager.calculateTrajectories(arrows);
|
||||
|
||||
var pos = arrow.getPos();
|
||||
var velocity = arrow.getVelocity();
|
||||
|
||||
var tr = new Trajectory(pos.toVector3f(), velocity.toVector3f());
|
||||
var hit = tr.getTrajectoryPoints(world, 0.1f, player);
|
||||
var points = hit.points();
|
||||
|
||||
if (points.size() < 2) {
|
||||
continue; // Skip if not enough points
|
||||
}
|
||||
|
||||
// Render the trajectory line
|
||||
if (hit.hitEntity()) {
|
||||
renderLine(drawContext, points, 0.0f, 1.0f, 0.0f); // Red for hit
|
||||
} else {
|
||||
renderLine(drawContext, points, 1.0f, 0.0f, 1.0f); // Green for no hit
|
||||
var hits = trajectoryManager.getLastHits();
|
||||
if (hits != null) {
|
||||
for (var hit : hits) {
|
||||
if (hit != null)
|
||||
renderForArrow(hit, drawContext);
|
||||
}
|
||||
}
|
||||
|
||||
var livingEntities = world.getOtherEntities(player, new Box(player.getPos().subtract(Vec3d.of(new Vec3i(100, 100, 100))), player.getPos().add(Vec3d.of(new Vec3i(100, 100, 100)))), EntityPredicates.VALID_ENTITY);
|
||||
for (var entity : livingEntities) {
|
||||
if (entity instanceof ArrowEntity) continue;
|
||||
|
||||
var pos = entity.getPos();
|
||||
var velocity = entity.getVelocity();
|
||||
|
||||
if (velocity.length() < 0.1) continue; // Skip if velocity is too low
|
||||
|
||||
var predictedPos = new Vector3f(
|
||||
(float) (entity.getX() + velocity.x * 20),
|
||||
(float) (entity.getY() + velocity.y * 20),
|
||||
(float) (entity.getZ() + velocity.z * 20)
|
||||
);
|
||||
|
||||
var points = List.of(
|
||||
new Vector3f((float) pos.x, (float) pos.y, (float) pos.z),
|
||||
predictedPos
|
||||
);
|
||||
|
||||
// Render the trajectory line
|
||||
renderLine(drawContext, points, 0.0f, 0.0f, 1.0f); // Blue for entity
|
||||
}
|
||||
|
||||
// check player is shooting bow
|
||||
var activeItem = player.getMainHandStack();
|
||||
if (activeItem != null && (activeItem.isOf(Items.BOW) || activeItem.isOf(Items.CROSSBOW))) {
|
||||
float speed;
|
||||
if (activeItem.isOf(Items.BOW)) {
|
||||
var item = (BowItem) activeItem.getItem();
|
||||
var f = BowItem.getPullProgress(player.getItemUseTime());
|
||||
speed = f * 3.0f;
|
||||
} else {
|
||||
speed = 3.15f;
|
||||
}
|
||||
|
||||
var dirDouble = player.getRotationVec(1.0f);
|
||||
|
||||
var dir = new Vector3f(
|
||||
(float) dirDouble.x,
|
||||
(float) dirDouble.y,
|
||||
(float) dirDouble.z
|
||||
).normalize();
|
||||
|
||||
var pos = player.getEyePos();
|
||||
var playerVelocity = player.getVelocity();
|
||||
|
||||
var velocity = new Vector3f(
|
||||
(float) (dir.x * speed + playerVelocity.x),
|
||||
(float) (dir.y * speed + playerVelocity.y),
|
||||
(float) (dir.z * speed + playerVelocity.z)
|
||||
);
|
||||
|
||||
var tr = new Trajectory(pos.toVector3f(), velocity);
|
||||
var hit = tr.getTrajectoryPoints(world, 0.1f, player);
|
||||
|
||||
var points = hit.points();
|
||||
|
||||
if (points.size() >= 2) {
|
||||
var playerRight = dir.cross(new Vector3f(0, 1, 0)).normalize();
|
||||
// move points to the right, closest point -> move 1, farthest point -> move 0, linear
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
var point = points.get(i);
|
||||
var rightOffset = 0.05;
|
||||
var dx = (float) (point.x + playerRight.x * rightOffset * (1 - ((float) i / points.size())));
|
||||
var dy = (float) (point.y + playerRight.y * rightOffset * (1 - ((float) i / points.size())));
|
||||
var dz = (float) (point.z + playerRight.z * rightOffset * (1 - ((float) i / points.size())));
|
||||
|
||||
points.set(i, new Vector3f(dx, dy, dz));
|
||||
}
|
||||
|
||||
// Render the trajectory line
|
||||
if (hit.hitEntity()) {
|
||||
renderLine(drawContext, points, 0.0f, 1.0f, 0.0f); // Red for hit
|
||||
} else {
|
||||
renderLine(drawContext, points, 1.0f, 0.0f, 1.0f); // Green for no hit
|
||||
}
|
||||
for (LivingEntity entity : entities) {
|
||||
var activeItem = entity.getMainHandStack();
|
||||
if (activeItem != null && (activeItem.isOf(Items.BOW) || activeItem.isOf(Items.CROSSBOW))) {
|
||||
renderForPlayer(entity, drawContext);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void renderLine(WorldRenderContext drawContext, List<Vector3f> points, float r, float g, float b) {
|
||||
private void renderForArrow(TrajectoryHit hit, WorldRenderContext drawContext) {
|
||||
var points = hit.points();
|
||||
|
||||
if (points.size() >= 2) {
|
||||
if (hit.hitEntity() != null) {
|
||||
renderLine(drawContext, points, 0.0f, 1.0f, 0.0f); // Red for hit
|
||||
} else {
|
||||
renderLine(drawContext, points, 1.0f, 0.0f, 1.0f); // Green for no hit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderForPlayer(LivingEntity player, WorldRenderContext drawContext) {
|
||||
// get all error entity in player world
|
||||
var world = player.getWorld();
|
||||
|
||||
var activeItem = player.getMainHandStack();
|
||||
float progress = 1.0f;
|
||||
if (activeItem.isOf(Items.BOW)) {
|
||||
progress = BowItem.getPullProgress(player.getItemUseTime());
|
||||
}
|
||||
|
||||
var dirDouble = player.getRotationVec(1.0f);
|
||||
|
||||
var dir = new Vector3f(
|
||||
(float) dirDouble.x,
|
||||
(float) dirDouble.y,
|
||||
(float) dirDouble.z
|
||||
).normalize();
|
||||
|
||||
var pos = player.getEyePos();
|
||||
var playerVelocity = player.getVelocity();
|
||||
|
||||
var item = ShootableItemFactory.create(activeItem);
|
||||
if (item != null) {
|
||||
var tr = item.makeTrajectory(pos.toVector3f(), dir, playerVelocity.toVector3f(), progress);
|
||||
var hit = tr.getTrajectoryPoints(world, 0.1f, player);
|
||||
|
||||
var points = hit.points();
|
||||
|
||||
if (points.size() >= 2) {
|
||||
var playerRight = dir.cross(new Vector3f(0, 1, 0)).normalize();
|
||||
// move points to the right, closest point -> move 1, farthest point -> move 0, linear
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
var point = points.get(i);
|
||||
var rightOffset = 0.05;
|
||||
var dx = (float) (point.x + playerRight.x * rightOffset * (1 - ((float) i / points.size())));
|
||||
var dy = (float) (point.y + playerRight.y * rightOffset * (1 - ((float) i / points.size())));
|
||||
var dz = (float) (point.z + playerRight.z * rightOffset * (1 - ((float) i / points.size())));
|
||||
|
||||
points.set(i, new Vector3f(dx, dy, dz));
|
||||
}
|
||||
|
||||
// Render the trajectory line
|
||||
if (hit.hitEntity() != null) {
|
||||
renderLine(drawContext, points, 0.0f, 1.0f, 0.0f); // Red for hit
|
||||
} else {
|
||||
renderLine(drawContext, points, 1.0f, 0.0f, 1.0f); // Green for no hit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderLine(WorldRenderContext drawContext, List<Vector3f> points, float r, float g, float b) {
|
||||
// enable z-buffer
|
||||
GlStateManager._enableDepthTest();
|
||||
GlStateManager._depthFunc(GL11.GL_LEQUAL);
|
||||
@@ -155,7 +131,7 @@ public class TrajectoryRenderer {
|
||||
|
||||
var playerPos = MinecraftClient.getInstance().getCameraEntity().getCameraPosVec(0);
|
||||
|
||||
for (var point: points) {
|
||||
for (var point : points) {
|
||||
float dx = (float) (point.x - playerPos.x);
|
||||
float dy = (float) (point.y - playerPos.y);
|
||||
float dz = (float) (point.z - playerPos.z);
|
||||
|
||||
@@ -53,21 +53,11 @@ public class Trajectory {
|
||||
var bb = entity.getBoundingBox();
|
||||
var entityVelocity = entity.getVelocity();
|
||||
// predict entity position by adding its velocity * seconds(by tick)
|
||||
var curEntityPos = new Vector3f(
|
||||
(float) (entity.getX()),
|
||||
(float) (entity.getY()),
|
||||
(float) (entity.getZ())
|
||||
);
|
||||
var predictEntityPos = new Vector3f(
|
||||
(float) (entity.getX() + entityVelocity.x * currentTick),
|
||||
(float) (entity.getY() + entityVelocity.y * currentTick),
|
||||
(float) (entity.getZ() + entityVelocity.z * currentTick)
|
||||
);
|
||||
var entityStart = new Vec3d(bb.minX, bb.minY, bb.minZ).add(entityVelocity.x * currentTick, entityVelocity.y * currentTick, entityVelocity.z * currentTick);
|
||||
var entityEnd = new Vec3d(bb.maxX, bb.maxY, bb.maxZ).add(entityVelocity.x * currentTick, entityVelocity.y * currentTick, entityVelocity.z * currentTick);
|
||||
var entityBox = new Box(entityStart, entityEnd);
|
||||
if (this.boxIntersectsLine(start, end, entityBox)) {
|
||||
return new TrajectoryHit(points, true); // Hit an entity
|
||||
return new TrajectoryHit(points, entity, currentTick); // Hit an entity
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +73,7 @@ public class Trajectory {
|
||||
if (hitResult.getType() != HitResult.Type.MISS) {
|
||||
// Check if the hit result is a block
|
||||
if (hitResult.getType() == HitResult.Type.BLOCK) {
|
||||
return new TrajectoryHit(points, false); // Hit a block
|
||||
return new TrajectoryHit(points, null, currentTick); // Hit a block
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +89,7 @@ public class Trajectory {
|
||||
break; // Prevent infinite loop
|
||||
}
|
||||
}
|
||||
return new TrajectoryHit(points, false); // No hit
|
||||
return new TrajectoryHit(points, null, currentTick); // No hit
|
||||
}
|
||||
|
||||
private boolean boxIntersectsLine(Vec3d start, Vec3d end, Box box) {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package org.walruslab.kepler.trajectory;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.Pair;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record TrajectoryHit(List<Vector3f> points, boolean hitEntity) {
|
||||
public record TrajectoryHit(List<Vector3f> points, Entity hitEntity, int ticks) {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.walruslab.kepler.trajectory.item;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
import org.walruslab.kepler.trajectory.Trajectory;
|
||||
|
||||
public class ShootableBow implements ShootableItem {
|
||||
public final float speed = 3.0f;
|
||||
|
||||
@Override
|
||||
public Trajectory makeTrajectory(Vector3f pos, Vector3f dir, Vector3f bias, float mul) {
|
||||
var velocity = dir.mul(speed * mul).add(bias);
|
||||
return new Trajectory(pos, velocity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.walruslab.kepler.trajectory.item;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
import org.walruslab.kepler.trajectory.Trajectory;
|
||||
|
||||
public class ShootableCrossbow implements ShootableItem {
|
||||
public final float speed = 3.15f;
|
||||
|
||||
@Override
|
||||
public Trajectory makeTrajectory(Vector3f pos, Vector3f dir, Vector3f bias, float mul) {
|
||||
var velocity = dir.mul(speed * mul).add(bias);
|
||||
return new Trajectory(pos, velocity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.walruslab.kepler.trajectory.item;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
import org.walruslab.kepler.trajectory.Trajectory;
|
||||
|
||||
public interface ShootableItem {
|
||||
public Trajectory makeTrajectory(Vector3f pos, Vector3f dir, Vector3f bias, float mul);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.walruslab.kepler.trajectory.item;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ShootableItemFactory {
|
||||
public static @Nullable ShootableItem create(ItemStack itemStack) {
|
||||
if (itemStack.isOf(Items.BOW))
|
||||
return new ShootableBow();
|
||||
else if (itemStack.isOf(Items.CROSSBOW))
|
||||
return new ShootableCrossbow();
|
||||
else return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user