diff --git a/src/main/java/ru/windcorp/progressia/client/Client.java b/src/main/java/ru/windcorp/progressia/client/Client.java index de6fa54..cd8c5a4 100644 --- a/src/main/java/ru/windcorp/progressia/client/Client.java +++ b/src/main/java/ru/windcorp/progressia/client/Client.java @@ -3,6 +3,7 @@ package ru.windcorp.progressia.client; import ru.windcorp.progressia.client.comms.DefaultClientCommsListener; import ru.windcorp.progressia.client.comms.ServerCommsChannel; import ru.windcorp.progressia.client.graphics.world.Camera; +import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.client.world.WorldRender; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.entity.EntityData; @@ -10,7 +11,7 @@ import ru.windcorp.progressia.common.world.entity.EntityData; public class Client { private final WorldRender world; - private EntityData localPlayer; + private LocalPlayer localPlayer; private final Camera camera = new Camera((float) Math.toRadians(70)); @@ -27,12 +28,12 @@ public class Client { return world; } - public EntityData getLocalPlayer() { + public LocalPlayer getLocalPlayer() { return localPlayer; } public void setLocalPlayer(EntityData localPlayer) { - this.localPlayer = localPlayer; + this.localPlayer = new LocalPlayer(localPlayer); } public Camera getCamera() { diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java index fb7557e..2e4b6be 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java @@ -20,6 +20,7 @@ package ru.windcorp.progressia.client.graphics.world; import java.util.ArrayList; import java.util.List; +import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.Client; import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.comms.controls.InputBasedControls; @@ -65,6 +66,10 @@ public class LayerWorld extends Layer { if (camera.hasAnchor()) { renderWorld(); } + + if (client.getLocalPlayer() != null) { + client.getLocalPlayer().update(client.getWorld()); + } } private void renderWorld() { @@ -89,6 +94,7 @@ public class LayerWorld extends Layer { try { tmp_performCollisions(tickLength); + tmp_drawSelectionBox(); tmp_testControls.applyPlayerControls(); @@ -122,13 +128,25 @@ public class LayerWorld extends Layer { ); } + private void tmp_drawSelectionBox() { + LocalPlayer player = client.getLocalPlayer(); + if (player == null) return; + + Vec3i lookingAt = player.getLookingAt(); + if (lookingAt == null) return; + + helper.pushTransform().translate(lookingAt.x, lookingAt.y, lookingAt.z).scale(1.1f); + CollisionModelRenderer.renderCollisionModel(client.getWorld().getData().getCollisionModelOfBlock(lookingAt), helper); + helper.popTransform(); + } + private void tmp_applyFriction(EntityData entity) { final float frictionCoeff = 1 - 1e-5f; entity.getVelocity().mul(frictionCoeff); } private void tmp_applyGravity(EntityData entity, float tickLength) { - if (ClientState.getInstance().getLocalPlayer() == entity && tmp_testControls.isFlying()) { + if (ClientState.getInstance().getLocalPlayer().getEntity() == entity && tmp_testControls.isFlying()) { return; } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java new file mode 100644 index 0000000..5df2b84 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java @@ -0,0 +1,64 @@ +package ru.windcorp.progressia.client.graphics.world; + +import glm.vec._3.Vec3; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.world.WorldRender; +import ru.windcorp.progressia.client.world.entity.EntityRenderable; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.BlockRay; +import ru.windcorp.progressia.common.world.Player; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class LocalPlayer extends Player { + + private Vec3i lookingAt = new Vec3i(); + private boolean isLookingAtBlock = false; + + private BlockRay lookingAtRay = new BlockRay(); + + public LocalPlayer(EntityData entity) { + super(entity); + } + + public Vec3i getLookingAt() { + return isLookingAtBlock ? lookingAt : null; + } + + public void update(WorldRender world) { + updateLookingAt(world); + } + + private void updateLookingAt(WorldRender world) { + Vec3 direction = Vectors.grab3(); + Vec3 start = Vectors.grab3(); + + BlockRay ray = lookingAtRay; + EntityData player = getEntity(); + + player.getLookingAtVector(direction); + world.getEntityRenderable(player).getViewPoint(start); + start.add(player.getPosition()); + + isLookingAtBlock = false; + + for (ray.start(start, direction); ray.getDistance() < 6; ray.next()) { + Vec3i blockInWorld = ray.current(); + + if (world.getData().getCollisionModelOfBlock(blockInWorld) != null) { + isLookingAtBlock = true; + lookingAt.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); + break; + } + } + + ray.end(); + + Vectors.release(direction); + Vectors.release(start); + } + + public EntityRenderable getRenderable(WorldRender world) { + return world.getEntityRenderable(getEntity()); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java index 712a9cf..2b42481 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java +++ b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java @@ -3,12 +3,22 @@ package ru.windcorp.progressia.common.util; import java.util.function.Consumer; import glm.mat._4.Mat4; +import glm.vec._2.Vec2; +import glm.vec._2.d.Vec2d; +import glm.vec._2.i.Vec2i; import glm.vec._3.Vec3; +import glm.vec._3.d.Vec3d; import glm.vec._3.i.Vec3i; import glm.vec._4.Vec4; +import glm.vec._4.d.Vec4d; +import glm.vec._4.i.Vec4i; public class VectorUtil { + public static enum Axis { + X, Y, Z, W; + } + public static void forEachVectorInCuboid( int x0, int y0, int z0, int x1, int y1, int z1, @@ -47,6 +57,204 @@ public class VectorUtil { inOut.set(vec4.x, vec4.y, vec4.z); } + public static Vec3 linearCombination( + Vec3 va, float ka, + Vec3 vb, float kb, + Vec3 output + ) { + output.set( + va.x * ka + vb.x * kb, + va.y * ka + vb.y * kb, + va.z * ka + vb.z * kb + ); + return output; + } + + public static Vec3 linearCombination( + Vec3 va, float ka, + Vec3 vb, float kb, + Vec3 vc, float kc, + Vec3 output + ) { + output.set( + va.x * ka + vb.x * kb + vc.x * kc, + va.y * ka + vb.y * kb + vc.y * kc, + va.z * ka + vb.z * kb + vc.z * kc + ); + return output; + } + + public static float get(Vec2 v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + default: throw new IllegalArgumentException("Vec2 does not have axis " + a); + } + } + + public static Vec2 set(Vec2 v, Axis a, float value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + default: throw new IllegalArgumentException("Vec2 does not have axis " + a); + } + return v; + } + + public static int get(Vec2i v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + default: throw new IllegalArgumentException("Vec2i does not have axis " + a); + } + } + + public static Vec2i set(Vec2i v, Axis a, int value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + default: throw new IllegalArgumentException("Vec2i does not have axis " + a); + } + return v; + } + + public static double get(Vec2d v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + default: throw new IllegalArgumentException("Vec2d does not have axis " + a); + } + } + + public static Vec2d set(Vec2d v, Axis a, double value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + default: throw new IllegalArgumentException("Vec2d does not have axis " + a); + } + return v; + } + + public static float get(Vec3 v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + default: throw new IllegalArgumentException("Vec3 does not have axis " + a); + } + } + + public static Vec3 set(Vec3 v, Axis a, float value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + default: throw new IllegalArgumentException("Vec3 does not have axis " + a); + } + return v; + } + + public static int get(Vec3i v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + default: throw new IllegalArgumentException("Vec3i does not have axis " + a); + } + } + + public static Vec3i set(Vec3i v, Axis a, int value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + default: throw new IllegalArgumentException("Vec3i does not have axis " + a); + } + return v; + } + + public static double get(Vec3d v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + default: throw new IllegalArgumentException("Vec3d does not have axis " + a); + } + } + + public static Vec3d set(Vec3d v, Axis a, double value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + default: throw new IllegalArgumentException("Vec3d does not have axis " + a); + } + return v; + } + + public static float get(Vec4 v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + case W: return v.w; + default: throw new IllegalArgumentException("Vec4 does not have axis " + a); + } + } + + public static Vec4 set(Vec4 v, Axis a, float value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + case W: v.w = value; break; + default: throw new IllegalArgumentException("Vec4 does not have axis " + a); + } + return v; + } + + public static int get(Vec4i v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + case W: return v.w; + default: throw new IllegalArgumentException("Vec4i does not have axis " + a); + } + } + + public static Vec4i set(Vec4i v, Axis a, int value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + case W: v.w = value; break; + default: throw new IllegalArgumentException("Vec4i does not have axis " + a); + } + return v; + } + + public static double get(Vec4d v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + case W: return v.w; + default: throw new IllegalArgumentException("Vec4d does not have axis " + a); + } + } + + public static Vec4d set(Vec4d v, Axis a, double value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + case W: v.w = value; break; + default: throw new IllegalArgumentException("Vec4d does not have axis " + a); + } + return v; + } + private VectorUtil() {} } diff --git a/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java b/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java new file mode 100644 index 0000000..158a1fc --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java @@ -0,0 +1,123 @@ +package ru.windcorp.progressia.common.world; + +import glm.vec._3.Vec3; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.VectorUtil; +import ru.windcorp.progressia.common.util.VectorUtil.Axis; + +import static java.lang.Math.*; + +public class BlockRay { + + private final Vec3 position = new Vec3(); + private final Vec3 direction = new Vec3(); + + private float distance; + + private final Vec3i block = new Vec3i(); + + private boolean isValid = false; + + public void start(Vec3 position, Vec3 direction) { + if (!direction.any()) { + throw new IllegalArgumentException("Direction is a zero vector"); + } + + isValid = true; + this.position.set(position).sub(0.5f); // Make sure lattice points are block vertices, not centers + this.direction.set(direction).normalize(); + this.block.set(toBlock(position.x), toBlock(position.y), toBlock(position.z)); + this.distance = 0; + } + + public void end() { + isValid = false; + } + + public Vec3i next() { + checkState(); + + float tx = distanceToEdge(position.x, direction.x); + float ty = distanceToEdge(position.y, direction.y); + float tz = distanceToEdge(position.z, direction.z); + + float tMin; + Axis axis; + + if (tx < ty && tx < tz) { + tMin = tx; + axis = Axis.X; + } else if (ty < tx && ty < tz) { + tMin = ty; + axis = Axis.Y; + } else { + tMin = tz; + axis = Axis.Z; + } + + // block.(axis) += signum(direction.(axis)) + VectorUtil.set(block, axis, VectorUtil.get(block, axis) + (int) signum(VectorUtil.get(direction, axis))); + + // position += direction * tMin + VectorUtil.linearCombination(position, 1, direction, tMin, position); // position += direction * tMin + distance += tMin; + + // position.(axis) = round(position.(axis)) + VectorUtil.set(position, axis, round(VectorUtil.get(position, axis))); + + return block; + } + + public Vec3i current() { + checkState(); + return block; + } + + private static float distanceToEdge(float c, float dir) { + if (dir == 0) return Float.POSITIVE_INFINITY; + + float edge; + + if (dir > 0) { + edge = strictCeil(c); + } else { + edge = strictFloor(c); + } + + return (edge - c) / dir; + } + + public float getDistance() { + checkState(); + return distance; + } + + private void checkState() { + if (!isValid) { + throw new IllegalStateException("BlockRay not started"); + } + } + + private static int toBlock(float c) { + return (int) round(c); + } + + /** + * Returns a smallest integer a such that a > c. + * @param c the number to compute strict ceiling of + * @return the strict ceiling of c + */ + private static float strictCeil(float c) { + return (float) (floor(c) + 1); + } + + /** + * Returns a largest integer a such that a < c. + * @param c the number to compute strict ceiling of + * @return the strict ceiling of c + */ + private static float strictFloor(float c) { + return (float) (ceil(c) - 1); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index f25ac0a..031be35 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -133,14 +133,6 @@ public class ChunkData { } if (!getPosition().any()) { -// EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony"); -// javapony.setEntityId(0x42); -// javapony.setPosition(new Vec3(-6, -6, 20)); -// javapony.setDirection(new Vec2( -// (float) Math.toRadians(40), (float) Math.toRadians(45) -// )); -// getEntities().add(javapony); - EntityData player = EntityDataRegistry.getInstance().create("Test:Player"); player.setEntityId(0x42); player.setPosition(new Vec3(-6, -6, 20)); diff --git a/src/main/java/ru/windcorp/progressia/common/world/Player.java b/src/main/java/ru/windcorp/progressia/common/world/Player.java new file mode 100644 index 0000000..14d8c76 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/Player.java @@ -0,0 +1,17 @@ +package ru.windcorp.progressia.common.world; + +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class Player { + + private EntityData entity; + + public Player(EntityData entity) { + this.entity = entity; + } + + public EntityData getEntity() { + return entity; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java index 0b3b73c..5cd79fd 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java @@ -119,5 +119,15 @@ public class EntityData extends StatefulObject implements Collideable { public void changeVelocityOnCollision(Vec3 velocityChange) { getVelocity().add(velocityChange); } + + public Vec3 getLookingAtVector(Vec3 output) { + output.set( + Math.cos(getPitch()) * Math.cos(getYaw()), + Math.cos(getPitch()) * Math.sin(getYaw()), + -Math.sin(getPitch()) + ); + + return output; + } } diff --git a/src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java b/src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java index 15463d4..e2d226f 100644 --- a/src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java +++ b/src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java @@ -53,7 +53,7 @@ public class CollisionModelRenderer { } public static void renderBlock(Vec3i coords, ShapeRenderHelper helper) { - helper.pushTransform().translate(coords.x, coords.y, coords.z); + helper.pushTransform().translate(coords.x, coords.y, coords.z).scale(0.25f); CUBE_GRAY.render(helper); helper.popTransform(); } diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 1b2d16e..3074815 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -87,10 +87,6 @@ public class TestContent { } private static void registerEntities() { -// registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f))); -// register(new TestEntityRenderJavapony()); -// register(new EntityLogic("Test", "Javapony")); - float scale = 1.8f / 8; registerEntityData("Test", "Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f))); register(new TestEntityRenderHuman()); @@ -121,6 +117,26 @@ public class TestContent { server.getAdHocChanger().setBlock(z000, block); } }); + +// ControlDataRegistry.getInstance().register(new ControlData("Test", "BreakBlock")); +// ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "BreakBlock", new KeyMatcher(GLFW.GLFW_KEY_ENTER, new int[0], 0)::matches)); +// ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "BreakBlock") { +// @Override +// public void apply(Server server, PacketControl packet, Client client) { +// Vec3i z000 = new Vec3i(0, 0, 0); +// +// ChunkData data = server.getWorld().getChunk(z000).getData(); +// +// BlockData block; +// if (data.getBlock(z000).getId().equals("Test:Stone")) { +// block = BlockDataRegistry.getInstance().get("Test:Glass"); +// } else { +// block = BlockDataRegistry.getInstance().get("Test:Stone"); +// } +// +// server.getAdHocChanger().setBlock(z000, block); +// } +// }); } private static void register(BlockData x) { diff --git a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java index 5ad784b..6282145 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java +++ b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java @@ -13,6 +13,7 @@ import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent; import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.bus.Input; +import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.common.Units; import ru.windcorp.progressia.common.util.FloatMathUtils; import ru.windcorp.progressia.common.util.Matrices; @@ -65,7 +66,7 @@ public class TestPlayerControls { return; } - EntityData player = getPlayer(); + EntityData player = getEntity(); Mat3 angMat = Matrices.grab3(); angMat.identity().rotateZ(player.getYaw()); @@ -185,7 +186,7 @@ public class TestPlayerControls { } private void jump() { - getPlayer().getVelocity().add(0, 0, JUMP_VELOCITY * (useMinecraftGravity ? 2 : 1)); + getEntity().getVelocity().add(0, 0, JUMP_VELOCITY * (useMinecraftGravity ? 2 : 1)); } private void handleShift(int multiplier) { @@ -223,7 +224,7 @@ public class TestPlayerControls { final float yawScale = -0.002f; final float pitchScale = yawScale; - EntityData player = getPlayer(); + EntityData player = getEntity(); normalizeAngles(player.getDirection().add( (float) (event.getChangeX() * yawScale), @@ -241,7 +242,11 @@ public class TestPlayerControls { ); } - private EntityData getPlayer() { + private EntityData getEntity() { + return getPlayer().getEntity(); + } + + private LocalPlayer getPlayer() { return ClientState.getInstance().getLocalPlayer(); }