From ccc9d8f3a7d4c7fb39a252793dad3745832ffbb1 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Mon, 7 Sep 2020 10:16:52 +0300 Subject: [PATCH] Refactored Camera to use anchors - Camera now uses anchor objects to obtain position and direction - Replaceable at runtime - Anchor supplies info about camera modes (switch with F5) - Only Entity anchor implemented ATM - Added PacketSetLocalPlayer - EntityRenders now produce EntityRenderable - Holds reference to EntityData - Has getViewPoint() to use with camera anchors - Added Matrices (like Vectors) - Added FloatMathUtils for all those pesky math functions that are only implemented for double. Curse you double! --- .../ru/windcorp/progressia/client/Client.java | 7 +- .../progressia/client/ClientState.java | 12 +- .../client/TestEntityRenderJavapony.java | 5 +- .../comms/DefaultClientCommsListener.java | 38 ++++ .../localhost/LocalServerCommsChannel.java | 7 +- .../client/graphics/flat/LayerTestUI.java | 15 +- .../client/graphics/world/Camera.java | 170 ++++++++++++------ .../client/graphics/world/EntityAnchor.java | 67 +++++++ .../client/graphics/world/LayerWorld.java | 61 +++++-- .../progressia/client/world/WorldRender.java | 8 +- .../client/world/entity/EntityRender.java | 3 +- .../client/world/entity/EntityRenderable.java | 23 +++ .../client/world/entity/QuadripedModel.java | 63 +++++-- .../comms/packets/PacketSetLocalPlayer.java | 18 ++ .../common/util/FloatMathUtils.java | 19 ++ .../progressia/common/util/Matrices.java | 65 +++++++ .../progressia/common/util/Vectors.java | 2 + .../progressia/common/world/ChunkData.java | 8 +- .../server/comms/ClientManager.java | 7 + 19 files changed, 484 insertions(+), 114 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/client/graphics/world/EntityAnchor.java create mode 100644 src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderable.java create mode 100644 src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java create mode 100644 src/main/java/ru/windcorp/progressia/common/util/FloatMathUtils.java create mode 100644 src/main/java/ru/windcorp/progressia/common/util/Matrices.java diff --git a/src/main/java/ru/windcorp/progressia/client/Client.java b/src/main/java/ru/windcorp/progressia/client/Client.java index 5115cc5..de6fa54 100644 --- a/src/main/java/ru/windcorp/progressia/client/Client.java +++ b/src/main/java/ru/windcorp/progressia/client/Client.java @@ -1,6 +1,5 @@ package ru.windcorp.progressia.client; -import glm.vec._3.Vec3; import ru.windcorp.progressia.client.comms.DefaultClientCommsListener; import ru.windcorp.progressia.client.comms.ServerCommsChannel; import ru.windcorp.progressia.client.graphics.world.Camera; @@ -13,11 +12,7 @@ public class Client { private final WorldRender world; private EntityData localPlayer; - private final Camera camera = new Camera( - new Vec3(-6, -6, 20), - (float) Math.toRadians(-40), (float) Math.toRadians(-45), - (float) Math.toRadians(70) - ); + private final Camera camera = new Camera((float) Math.toRadians(70)); private final ServerCommsChannel comms; diff --git a/src/main/java/ru/windcorp/progressia/client/ClientState.java b/src/main/java/ru/windcorp/progressia/client/ClientState.java index eab5cfc..84c26a2 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientState.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientState.java @@ -1,6 +1,5 @@ package ru.windcorp.progressia.client; -import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel; import ru.windcorp.progressia.client.graphics.GUI; import ru.windcorp.progressia.client.graphics.flat.LayerTestUI; @@ -24,14 +23,15 @@ public class ClientState { public static void connectToLocalServer() { WorldData world = new WorldData(); - Client client = new Client(world, new LocalServerCommsChannel( - ServerState.getInstance() - )); - client.setLocalPlayer( - world.getChunk(new Vec3i(0, 0, 0)).getEntities().get(0) + LocalServerCommsChannel channel = new LocalServerCommsChannel( + ServerState.getInstance() ); + Client client = new Client(world, channel); + + channel.connect(); + setInstance(client); GUI.addBottomLayer(new LayerWorld(client)); diff --git a/src/main/java/ru/windcorp/progressia/client/TestEntityRenderJavapony.java b/src/main/java/ru/windcorp/progressia/client/TestEntityRenderJavapony.java index 1dbf6ec..b1e8a92 100644 --- a/src/main/java/ru/windcorp/progressia/client/TestEntityRenderJavapony.java +++ b/src/main/java/ru/windcorp/progressia/client/TestEntityRenderJavapony.java @@ -18,6 +18,7 @@ import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; import ru.windcorp.progressia.client.world.entity.EntityRender; import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; +import ru.windcorp.progressia.client.world.entity.EntityRenderable; import ru.windcorp.progressia.client.world.entity.QuadripedModel; import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.common.world.entity.EntityData; @@ -313,13 +314,13 @@ public class TestEntityRenderJavapony extends EntityRender { } @Override - public Renderable createRenderable(EntityData entity) { + public EntityRenderable createRenderable(EntityData entity) { return new QuadripedModel( entity, new QuadripedModel.Body(body), new QuadripedModel.Head( - head, new Vec3(12, 0, 20), 60, 45 + head, new Vec3(12, 0, 20), 60, 45, new Vec3(16, 0, 20) ), new QuadripedModel.Leg( leftForeLeg, new Vec3( 6, +8.1f, -16), 0.0f diff --git a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java index 134bfb1..299c50a 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java @@ -1,12 +1,18 @@ package ru.windcorp.progressia.client.comms; import java.io.IOException; +import java.util.Collection; +import java.util.UUID; import ru.windcorp.progressia.client.Client; +import ru.windcorp.progressia.client.graphics.world.EntityAnchor; import ru.windcorp.progressia.client.world.ChunkRender; import ru.windcorp.progressia.common.comms.CommsListener; import ru.windcorp.progressia.common.comms.packets.Packet; +import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer; import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.entity.EntityData; public class DefaultClientCommsListener implements CommsListener { @@ -24,9 +30,41 @@ public class DefaultClientCommsListener implements CommsListener { ); tmp_reassembleWorld(); + } else if (packet instanceof PacketSetLocalPlayer) { + setLocalPlayer((PacketSetLocalPlayer) packet); } } + private void setLocalPlayer(PacketSetLocalPlayer packet) { + UUID uuid = packet.getLocalPlayerEntityUUID(); + + Collection chunks = + getClient().getWorld().getData().getChunks(); + + EntityData entity = null; + + synchronized (chunks) { + chunkLoop: + for (ChunkData chunk : chunks) { + for (EntityData anEntity : chunk.getEntities()) { + if (anEntity.getUUID().equals(uuid)) { + entity = anEntity; + break chunkLoop; + } + } + } + } + + if (entity == null) { + throw new RuntimeException(""); + } + + getClient().setLocalPlayer(entity); + getClient().getCamera().setAnchor(new EntityAnchor( + getClient().getWorld().getEntityRenderable(entity) + )); + } + private void tmp_reassembleWorld() { getClient().getWorld().getChunks().forEach(ChunkRender::markForUpdate); } diff --git a/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java b/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java index 478f6f4..8c7b6d7 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java @@ -6,10 +6,15 @@ import ru.windcorp.progressia.server.Server; public class LocalServerCommsChannel extends ServerCommsChannel { - private final LocalClient localClient; + private LocalClient localClient; + private final Server server; public LocalServerCommsChannel(Server server) { super(Role.GAME, Role.CHAT); + this.server = server; + } + + public void connect() { setState(State.CONNECTED); this.localClient = new LocalClient( diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/flat/LayerTestUI.java b/src/main/java/ru/windcorp/progressia/client/graphics/flat/LayerTestUI.java index cf2cb90..f5da33e 100755 --- a/src/main/java/ru/windcorp/progressia/client/graphics/flat/LayerTestUI.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/flat/LayerTestUI.java @@ -30,6 +30,7 @@ import ru.windcorp.progressia.client.graphics.input.bus.Input; import ru.windcorp.progressia.client.graphics.model.LambdaModel; import ru.windcorp.progressia.client.graphics.texture.SimpleTextures; import ru.windcorp.progressia.client.graphics.texture.Texture; +import ru.windcorp.progressia.client.graphics.world.Camera; public class LayerTestUI extends AssembledFlatLayer { @@ -73,7 +74,10 @@ public class LayerTestUI extends AssembledFlatLayer { target.addCustomRenderer(new LambdaModel(LambdaModel.lambdaBuilder() .addDynamicPart( target.createRectagle(0, 0, texSize, texSize, 0xFFFFFF, compassFg), - mat -> mat.translate(texSize/2, texSize/2, 0).rotateZ(ClientState.getInstance().getCamera().getYaw()).translate(-texSize/2, -texSize/2, 0) + mat -> + mat.translate(texSize/2, texSize/2, 0) + .rotateZ(getCompassRotation()) + .translate(-texSize/2, -texSize/2, 0) ) )); target.popTransform(); @@ -81,6 +85,15 @@ public class LayerTestUI extends AssembledFlatLayer { drawCross(target); } + private double getCompassRotation() { + Camera.Anchor anchor = + ClientState.getInstance().getCamera().getAnchor(); + + if (anchor == null) return 0; + + return -anchor.getCameraYaw(); + } + private void drawCross(RenderTarget target) { int cx = getWidth() / 2; int cy = getHeight() / 2; diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java index 23af1e4..7e8a59d 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java @@ -19,26 +19,62 @@ package ru.windcorp.progressia.client.graphics.world; import static java.lang.Math.*; +import java.util.Collection; +import java.util.function.Consumer; + import glm.Glm; import glm.mat._4.Mat4; import glm.vec._3.Vec3; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; +import ru.windcorp.progressia.client.graphics.world.Camera.Anchor.Mode; +import ru.windcorp.progressia.common.util.Vectors; public class Camera { - private final Vec3 position = new Vec3(); + public static interface Anchor { + + /** + * Offset is applied after the rotation. + */ + public static interface Mode { + void getCameraOffset(Vec3 output); + void applyCameraRotation(Mat4 output); + + public static Mode of( + Consumer offsetGetter, + Consumer rotator + ) { + return new Mode() { + @Override + public void getCameraOffset(Vec3 output) { + offsetGetter.accept(output); + } + + @Override + public void applyCameraRotation(Mat4 output) { + rotator.accept(output); + } + }; + } + } + + void getCameraPosition(Vec3 output); + void getCameraVelocity(Vec3 output); + float getCameraYaw(); + float getCameraPitch(); + + Collection getCameraModes(); + + } - private float pitch; - private float yaw; + private Anchor anchor; + + private Anchor.Mode[] modes; + private int currentModeIndex; private float fieldOfView; - public boolean tmp_mode = false; - - public Camera(Vec3 position, float pitch, float yaw, float fieldOfView) { - teleport(position); - setPitch(pitch); - setYaw(yaw); + public Camera(float fieldOfView) { setFieldOfView(fieldOfView); } @@ -48,10 +84,7 @@ public class Camera { applyPerspective(helper); rotateCoordinateSystem(helper); - // TODO debug - helper.pushViewTransform().translate(3.5f, 0, -0.5f); - if (tmp_mode) helper.pushViewTransform().rotateZ(PI); - + applyMode(helper); applyDirection(helper); applyPosition(helper); } @@ -70,14 +103,38 @@ public class Camera { private void rotateCoordinateSystem(WorldRenderHelper helper) { helper.pushViewTransform().rotateX(-PI / 2).rotateZ(PI / 2); } + + private void applyMode(WorldRenderHelper helper) { + Mode mode = getMode(); + + Mat4 matrix = helper.pushViewTransform(); + + Vec3 offset = Vectors.grab3(); + mode.getCameraOffset(offset); + + offset.negate(); + matrix.translate(offset); + + Vectors.release(offset); + + mode.applyCameraRotation(matrix); + } private void applyDirection(WorldRenderHelper helper) { - helper.pushViewTransform().rotateY(pitch).rotateZ(yaw); + helper.pushViewTransform() + .rotateY(-anchor.getCameraPitch()) + .rotateZ(-anchor.getCameraYaw()); } private void applyPosition(WorldRenderHelper helper) { - helper.pushViewTransform().translate(position.negate()); - position.negate(); + Vec3 v = Vectors.grab3(); + + anchor.getCameraPosition(v); + v.negate(); + + helper.pushViewTransform().translate(v); + + Vectors.release(v); } private float computeFovY() { @@ -93,45 +150,6 @@ public class Camera { )); } } - - public Vec3 getPosition() { - return position; - } - - public void teleport(Vec3 pos) { - position.set(pos); - } - - public void move(Vec3 pos) { - position.add(pos); - } - - public float getPitch() { - return pitch; - } - - public void setPitch(float pitch) { - final float maxPitch = (float) (Math.PI / 2); - this.pitch = Glm.clamp(pitch, -maxPitch, +maxPitch); - } - - public float getYaw() { - return yaw; - } - - public void setYaw(float yaw) { - this.yaw = Glm.mod(yaw, 2 * (float) PI); - } - - public void setDirection(float pitch, float yaw) { - setPitch(pitch); - setYaw(yaw); - } - - public void turn(float pitchChange, float yawChange) { - setPitch(getPitch() + pitchChange); - setYaw(getYaw() + yawChange); - } public float getFieldOfView() { return fieldOfView; @@ -140,5 +158,47 @@ public class Camera { public void setFieldOfView(float fieldOfView) { this.fieldOfView = fieldOfView; } + + public Anchor getAnchor() { + return anchor; + } + + public boolean hasAnchor() { + return anchor != null; + } + + public void setAnchor(Anchor anchor) { + if (anchor == null) { + this.anchor = null; + this.modes = null; + return; + } + + Collection modesCollection = anchor.getCameraModes(); + + if (modesCollection.isEmpty()) { + throw new IllegalArgumentException( + "Anchor " + anchor + " returned no camera modes," + + " at least one required" + ); + } + + this.anchor = anchor; + + this.modes = modesCollection.toArray(new Mode[modesCollection.size()]); + this.currentModeIndex = 0; + } + + public Anchor.Mode getMode() { + return modes[currentModeIndex]; + } + + public void selectNextMode() { + if (currentModeIndex == modes.length - 1) { + currentModeIndex = 0; + } else { + currentModeIndex++; + } + } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/EntityAnchor.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/EntityAnchor.java new file mode 100644 index 0000000..86f859f --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/EntityAnchor.java @@ -0,0 +1,67 @@ +package ru.windcorp.progressia.client.graphics.world; + +import java.util.Collection; + +import com.google.common.collect.ImmutableList; + +import glm.vec._3.Vec3; +import ru.windcorp.progressia.client.graphics.world.Camera.Anchor; +import ru.windcorp.progressia.client.world.entity.EntityRenderable; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class EntityAnchor implements Anchor { + + private final EntityData entity; + private final EntityRenderable model; + + private final Collection modes; + + public EntityAnchor(EntityRenderable model) { + this.entity = model.getData(); + this.model = model; + + this.modes = ImmutableList.of( + // From viewpoint / first person + Mode.of(v -> v.set(0), m -> {}), + + // Third person, looking forward + Mode.of( + v -> v.set(-3.5f, +0.5f, 0), + m -> {} + ), + + // Third person, looking back + Mode.of( + v -> v.set(-3.5f, 0, 0), + m -> m.rotateZ((float) Math.PI) + ) + ); + } + + @Override + public void getCameraPosition(Vec3 output) { + model.getViewPoint(output); + output.add(entity.getPosition()); + } + + @Override + public void getCameraVelocity(Vec3 output) { + output.set(entity.getVelocity()); + } + + @Override + public float getCameraYaw() { + return entity.getYaw(); + } + + @Override + public float getCameraPitch() { + return entity.getPitch(); + } + + @Override + public Collection getCameraModes() { + return modes; + } + +} 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 6aa5a87..2cc11b0 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 @@ -19,7 +19,9 @@ package ru.windcorp.progressia.client.graphics.world; import org.lwjgl.glfw.GLFW; +import glm.Glm; import glm.mat._3.Mat3; +import glm.vec._2.Vec2; import glm.vec._3.Vec3; import ru.windcorp.progressia.client.Client; import ru.windcorp.progressia.client.comms.controls.InputBasedControls; @@ -31,7 +33,9 @@ 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.common.util.FloatMathUtils; import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.entity.EntityData; public class LayerWorld extends Layer { @@ -67,19 +71,20 @@ public class LayerWorld extends Layer { @Override protected void doRender() { - client.getLocalPlayer().setPosition(client.getCamera().getPosition()); - client.getLocalPlayer().setVelocity(velocity); - client.getLocalPlayer().getDirection().set( - -client.getCamera().getYaw(), - -client.getCamera().getPitch() - ); + if (client.getLocalPlayer() != null) { + tmp_handleControls(); + } - client.getCamera().apply(helper); + Camera camera = client.getCamera(); + if (camera.hasAnchor()) { + renderWorld(); + } + } + + private void tmp_handleControls() { + EntityData player = client.getLocalPlayer(); - renderWorld(); - helper.reset(); - - angMat.set().rotateZ(-client.getCamera().getYaw()); + angMat.set().rotateZ(player.getYaw()); Vec3 movement = Vectors.grab3(); @@ -97,15 +102,21 @@ public class LayerWorld extends Layer { Vec3 velCopy = Vectors.grab3().set(velocity); velCopy.mul((float) (GraphicsInterface.getFrameLength() * 60)); - client.getCamera().move(velCopy); + + player.getPosition().add(velCopy); + player.getVelocity().set(velocity); Vectors.release(velCopy); } private void renderWorld() { + client.getCamera().apply(helper); FaceCulling.push(true); + this.client.getWorld().render(helper); + FaceCulling.pop(); + helper.reset(); } @Override @@ -170,7 +181,9 @@ public class LayerWorld extends Layer { case GLFW.GLFW_KEY_F5: if (!event.isPress()) return false; - client.getCamera().tmp_mode = !client.getCamera().tmp_mode; + if (client.getCamera().hasAnchor()) { + client.getCamera().selectNextMode(); + } break; default: @@ -183,12 +196,26 @@ public class LayerWorld extends Layer { private void onMouseMoved(CursorMoveEvent event) { if (!flag) return; - final float yawScale = 0.002f; + final float yawScale = -0.002f; final float pitchScale = yawScale; + + EntityData player = client.getLocalPlayer(); - client.getCamera().turn( - (float) (event.getChangeY() * pitchScale), - (float) (event.getChangeX() * yawScale) + if (player != null) { + normalizeAngles(player.getDirection().add( + (float) (event.getChangeX() * yawScale), + (float) (event.getChangeY() * pitchScale) + )); + } + } + + private void normalizeAngles(Vec2 dir) { + // Normalize yaw + dir.x = FloatMathUtils.normalizeAngle(dir.x); + + // Clamp pitch + dir.y = Glm.clamp( + dir.y, -FloatMathUtils.PI_F/2, +FloatMathUtils.PI_F/2 ); } diff --git a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java index 26dd653..fc219f4 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java @@ -25,9 +25,9 @@ import java.util.WeakHashMap; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.graphics.backend.FaceCulling; -import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; +import ru.windcorp.progressia.client.world.entity.EntityRenderable; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.entity.EntityData; @@ -37,7 +37,7 @@ public class WorldRender { private final WorldData data; private final Map chunks = new HashMap<>(); - private final Map entityModels = + private final Map entityModels = Collections.synchronizedMap(new WeakHashMap<>()); public WorldRender(WorldData data) { @@ -86,14 +86,14 @@ public class WorldRender { FaceCulling.pop(); } - public Renderable getEntityRenderable(EntityData entity) { + public EntityRenderable getEntityRenderable(EntityData entity) { return entityModels.computeIfAbsent( entity, WorldRender::createEntityRenderable ); } - private static Renderable createEntityRenderable(EntityData entity) { + private static EntityRenderable createEntityRenderable(EntityData entity) { return EntityRenderRegistry.getInstance().get(entity.getId()) .createRenderable(entity); } diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java index 98e1f10..32c9be1 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java @@ -1,6 +1,5 @@ package ru.windcorp.progressia.client.world.entity; -import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.common.util.Namespaced; import ru.windcorp.progressia.common.world.entity.EntityData; @@ -10,6 +9,6 @@ public abstract class EntityRender extends Namespaced { super(namespace, name); } - public abstract Renderable createRenderable(EntityData entity); + public abstract EntityRenderable createRenderable(EntityData entity); } diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderable.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderable.java new file mode 100644 index 0000000..e7e8466 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderable.java @@ -0,0 +1,23 @@ +package ru.windcorp.progressia.client.world.entity; + +import glm.vec._3.Vec3; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public abstract class EntityRenderable implements Renderable { + + private final EntityData data; + + public EntityRenderable(EntityData data) { + this.data = data; + } + + public EntityData getData() { + return data; + } + + public void getViewPoint(Vec3 output) { + output.set(0, 0, 0); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java b/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java index 5b55665..a7bdc97 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java @@ -1,17 +1,20 @@ package ru.windcorp.progressia.client.world.entity; import static java.lang.Math.*; +import static ru.windcorp.progressia.common.util.FloatMathUtils.*; import glm.Glm; import glm.mat._4.Mat4; import glm.vec._3.Vec3; +import glm.vec._4.Vec4; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; +import ru.windcorp.progressia.common.util.Matrices; import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.entity.EntityData; -public class QuadripedModel implements Renderable { +public class QuadripedModel extends EntityRenderable { private static abstract class BodyPart { private final Renderable renderable; @@ -20,7 +23,6 @@ public class QuadripedModel implements Renderable { public BodyPart(Renderable renderable, Vec3 joint) { this.renderable = renderable; if (joint != null) { -// joint.negate(this.translation); this.translation.set(joint); } } @@ -37,6 +39,10 @@ public class QuadripedModel implements Renderable { } protected abstract void applyTransform(Mat4 mat, QuadripedModel model); + + public Vec3 getTranslation() { + return translation; + } } public static class Body extends BodyPart { @@ -54,19 +60,27 @@ public class QuadripedModel implements Renderable { private final float maxYaw; private final float maxPitch; + private final Vec3 viewPoint; + public Head( Renderable renderable, Vec3 joint, - double maxYawDegrees, double maxPitchDegrees + double maxYawDegrees, double maxPitchDegrees, + Vec3 viewPoint ) { super(renderable, joint); this.maxYaw = (float) toRadians(maxYawDegrees); this.maxPitch = (float) toRadians(maxPitchDegrees); + this.viewPoint = viewPoint; } @Override protected void applyTransform(Mat4 mat, QuadripedModel model) { mat.rotateZ(model.headYaw).rotateY(model.headPitch); } + + public Vec3 getViewPoint() { + return viewPoint; + } } public static class Leg extends BodyPart { @@ -86,8 +100,6 @@ public class QuadripedModel implements Renderable { } } - private final EntityData entity; - private final Body body; private final Head head; private final Leg leftForeLeg, rightForeLeg; @@ -120,7 +132,7 @@ public class QuadripedModel implements Renderable { float scale ) { - this.entity = entity; + super(entity); this.body = body; this.head = head; @@ -134,23 +146,21 @@ public class QuadripedModel implements Renderable { @Override public void render(ShapeRenderHelper renderer) { - accountForVelocity(); - evaluateAngles(); - renderer.pushTransform().scale(scale).rotateZ(bodyYaw); body.render(renderer, this); - head.render(renderer, this); - leftForeLeg.render(renderer, this); rightForeLeg.render(renderer, this); leftHindLeg.render(renderer, this); rightHindLeg.render(renderer, this); renderer.popTransform(); + + accountForVelocity(); + evaluateAngles(); } private void evaluateAngles() { - float globalYaw = normalizeAngle(entity.getYaw()); + float globalYaw = normalizeAngle(getData().getYaw()); if (Float.isNaN(bodyYaw)) { bodyYaw = globalYaw; @@ -170,14 +180,14 @@ public class QuadripedModel implements Renderable { bodyYaw = normalizeAngle(bodyYaw); headPitch = Glm.clamp( - entity.getPitch(), + getData().getPitch(), -head.maxPitch, head.maxPitch ); } private void accountForVelocity() { Vec3 horizontal = Vectors.grab3(); - horizontal.set(entity.getVelocity()); + horizontal.set(getData().getVelocity()); horizontal.z = 0; velocity = horizontal.length(); @@ -201,11 +211,26 @@ public class QuadripedModel implements Renderable { velocityCoeff *= velocityCoeff; } } - - private static float normalizeAngle(float x) { - final float half = (float) (PI); - final float full = (float) (2 * PI); - return ((x + half) % full + full) % full - half; + + @Override + public void getViewPoint(Vec3 output) { + Mat4 m = Matrices.grab4(); + Vec4 v = Vectors.grab4(); + + m.identity() + .scale(scale) + .rotateZ(bodyYaw) + .translate(head.getTranslation()) + .rotateZ(headYaw) + .rotateY(headPitch); + + v.set(head.getViewPoint(), 1); + m.mul(v); + + output.set(v.x, v.y, v.z); + + Vectors.release(v); + Matrices.release(m); } } diff --git a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java new file mode 100644 index 0000000..f20843a --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java @@ -0,0 +1,18 @@ +package ru.windcorp.progressia.common.comms.packets; + +import java.util.UUID; + +public class PacketSetLocalPlayer extends Packet { + + private final UUID localPlayerEntityUUID; + + public PacketSetLocalPlayer(UUID uuid) { + super("Core", "SetLocalPlayer"); + this.localPlayerEntityUUID = uuid; + } + + public UUID getLocalPlayerEntityUUID() { + return localPlayerEntityUUID; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/FloatMathUtils.java b/src/main/java/ru/windcorp/progressia/common/util/FloatMathUtils.java new file mode 100644 index 0000000..bdf3dec --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/FloatMathUtils.java @@ -0,0 +1,19 @@ +package ru.windcorp.progressia.common.util; + +import org.apache.commons.math3.util.FastMath; + +public class FloatMathUtils { + + public static final float PI_F = (float) Math.PI; + + public static float floor(float x) { + return (float) FastMath.floor(x); + } + + public static float normalizeAngle(float a) { + return a - 2*PI_F * floor((a + PI_F) / (2*PI_F)); + } + + private FloatMathUtils() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/Matrices.java b/src/main/java/ru/windcorp/progressia/common/util/Matrices.java new file mode 100644 index 0000000..bc60c22 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/Matrices.java @@ -0,0 +1,65 @@ +package ru.windcorp.progressia.common.util; + +import glm.mat._3.Mat3; +import glm.mat._4.Mat4; +import glm.mat._4.d.Mat4d; + +/** + * A set of caches for GLM matrix objects. Use this instead of allocating new + * matrices when the objects are effectively local. + *

+ * All {@code grab}bed objects must be {@code release}d as soon as possible. + * Ideally, user code should be: + *

+ * Mat4 myMatrix = Vectors.grab4();
+ * try {
+ *     // use myMatrix
+ * } finally {
+ *     Matrices.release(myMatrix);
+ * }
+ * 
+ * Provided objects may be reused after {@code release} has been invoked; + * do not store them. + *

+ * This class is thread- and recursion-safe. + * + * @see Vectors + */ +public class Matrices { + + private static final LowOverheadCache MAT3S = + new LowOverheadCache<>(Mat3::new); + + public static Mat3 grab3() { + return MAT3S.grab(); + } + + public static void release(Mat3 m) { + MAT3S.release(m); + } + + private static final LowOverheadCache MAT4S = + new LowOverheadCache<>(Mat4::new); + + public static Mat4 grab4() { + return MAT4S.grab(); + } + + public static void release(Mat4 m) { + MAT4S.release(m); + } + + private static final LowOverheadCache MAT4DS = + new LowOverheadCache<>(Mat4d::new); + + public static Mat4d grab4d() { + return MAT4DS.grab(); + } + + public static void release(Mat4d m) { + MAT4DS.release(m); + } + + private Matrices() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/Vectors.java b/src/main/java/ru/windcorp/progressia/common/util/Vectors.java index 2468707..489bf13 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/Vectors.java +++ b/src/main/java/ru/windcorp/progressia/common/util/Vectors.java @@ -25,6 +25,8 @@ import glm.vec._4.i.Vec4i; * do not store them. *

* This class is thread- and recursion-safe. + * + * @see Matrices */ public class Vectors { 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 b67032d..681b779 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -22,11 +22,13 @@ import static ru.windcorp.progressia.common.world.block.BlockFace.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.UUID; import java.util.function.BiConsumer; import java.util.function.Consumer; import com.google.common.collect.Lists; +import glm.vec._2.Vec2; import glm.vec._3.Vec3; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.world.tile.TileLocation; @@ -131,7 +133,11 @@ public class ChunkData { } EntityData javapony = new EntityData("Test", "Javapony"); - javapony.setPosition(new Vec3(8, 12, 16.2f)); + javapony.setUUID(UUID.nameUUIDFromBytes(new byte[] {42})); + javapony.setPosition(new Vec3(-6, -6, 20)); + javapony.setDirection(new Vec2( + (float) Math.toRadians(40), (float) Math.toRadians(45) + )); getEntities().add(javapony); } diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java index cfff2b0..6f9381b 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java @@ -4,12 +4,14 @@ import java.util.Collection; import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; +import glm.vec._3.i.Vec3i; import gnu.trove.TCollections; import gnu.trove.map.TIntObjectMap; import gnu.trove.map.hash.TIntObjectHashMap; import ru.windcorp.progressia.common.comms.CommsChannel.Role; import ru.windcorp.progressia.common.comms.CommsChannel.State; import ru.windcorp.progressia.common.comms.packets.Packet; +import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer; import ru.windcorp.progressia.server.Server; public class ClientManager { @@ -36,6 +38,11 @@ public class ClientManager { clientsById.put(client.getId(), client); client.addListener(new DefaultServerCommsListener(this, client)); + + client.sendPacket(new PacketSetLocalPlayer( + server.getWorld().getData().getChunk(new Vec3i(0, 0, 0)) + .getEntities().get(0).getUUID() + )); } public void disconnectClient(Client client) {