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!
This commit is contained in:
OLEGSHA 2020-09-07 10:16:52 +03:00
parent c613bef8c5
commit ccc9d8f3a7
19 changed files with 484 additions and 114 deletions

View File

@ -1,6 +1,5 @@
package ru.windcorp.progressia.client; package ru.windcorp.progressia.client;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.comms.DefaultClientCommsListener; import ru.windcorp.progressia.client.comms.DefaultClientCommsListener;
import ru.windcorp.progressia.client.comms.ServerCommsChannel; import ru.windcorp.progressia.client.comms.ServerCommsChannel;
import ru.windcorp.progressia.client.graphics.world.Camera; import ru.windcorp.progressia.client.graphics.world.Camera;
@ -13,11 +12,7 @@ public class Client {
private final WorldRender world; private final WorldRender world;
private EntityData localPlayer; private EntityData localPlayer;
private final Camera camera = new Camera( private final Camera camera = new Camera((float) Math.toRadians(70));
new Vec3(-6, -6, 20),
(float) Math.toRadians(-40), (float) Math.toRadians(-45),
(float) Math.toRadians(70)
);
private final ServerCommsChannel comms; private final ServerCommsChannel comms;

View File

@ -1,6 +1,5 @@
package ru.windcorp.progressia.client; package ru.windcorp.progressia.client;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel; import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel;
import ru.windcorp.progressia.client.graphics.GUI; import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.flat.LayerTestUI; import ru.windcorp.progressia.client.graphics.flat.LayerTestUI;
@ -24,14 +23,15 @@ public class ClientState {
public static void connectToLocalServer() { public static void connectToLocalServer() {
WorldData world = new WorldData(); WorldData world = new WorldData();
Client client = new Client(world, new LocalServerCommsChannel(
ServerState.getInstance()
));
client.setLocalPlayer( LocalServerCommsChannel channel = new LocalServerCommsChannel(
world.getChunk(new Vec3i(0, 0, 0)).getEntities().get(0) ServerState.getInstance()
); );
Client client = new Client(world, channel);
channel.connect();
setInstance(client); setInstance(client);
GUI.addBottomLayer(new LayerWorld(client)); GUI.addBottomLayer(new LayerWorld(client));

View File

@ -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.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.world.entity.EntityRender; import ru.windcorp.progressia.client.world.entity.EntityRender;
import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; 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.client.world.entity.QuadripedModel;
import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
@ -313,13 +314,13 @@ public class TestEntityRenderJavapony extends EntityRender {
} }
@Override @Override
public Renderable createRenderable(EntityData entity) { public EntityRenderable createRenderable(EntityData entity) {
return new QuadripedModel( return new QuadripedModel(
entity, entity,
new QuadripedModel.Body(body), new QuadripedModel.Body(body),
new QuadripedModel.Head( 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( new QuadripedModel.Leg(
leftForeLeg, new Vec3( 6, +8.1f, -16), 0.0f leftForeLeg, new Vec3( 6, +8.1f, -16), 0.0f

View File

@ -1,12 +1,18 @@
package ru.windcorp.progressia.client.comms; package ru.windcorp.progressia.client.comms;
import java.io.IOException; import java.io.IOException;
import java.util.Collection;
import java.util.UUID;
import ru.windcorp.progressia.client.Client; 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.client.world.ChunkRender;
import ru.windcorp.progressia.common.comms.CommsListener; import ru.windcorp.progressia.common.comms.CommsListener;
import ru.windcorp.progressia.common.comms.packets.Packet; 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.comms.packets.PacketWorldChange;
import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.entity.EntityData;
public class DefaultClientCommsListener implements CommsListener { public class DefaultClientCommsListener implements CommsListener {
@ -24,9 +30,41 @@ public class DefaultClientCommsListener implements CommsListener {
); );
tmp_reassembleWorld(); tmp_reassembleWorld();
} else if (packet instanceof PacketSetLocalPlayer) {
setLocalPlayer((PacketSetLocalPlayer) packet);
} }
} }
private void setLocalPlayer(PacketSetLocalPlayer packet) {
UUID uuid = packet.getLocalPlayerEntityUUID();
Collection<ChunkData> 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() { private void tmp_reassembleWorld() {
getClient().getWorld().getChunks().forEach(ChunkRender::markForUpdate); getClient().getWorld().getChunks().forEach(ChunkRender::markForUpdate);
} }

View File

@ -6,10 +6,15 @@ import ru.windcorp.progressia.server.Server;
public class LocalServerCommsChannel extends ServerCommsChannel { public class LocalServerCommsChannel extends ServerCommsChannel {
private final LocalClient localClient; private LocalClient localClient;
private final Server server;
public LocalServerCommsChannel(Server server) { public LocalServerCommsChannel(Server server) {
super(Role.GAME, Role.CHAT); super(Role.GAME, Role.CHAT);
this.server = server;
}
public void connect() {
setState(State.CONNECTED); setState(State.CONNECTED);
this.localClient = new LocalClient( this.localClient = new LocalClient(

View File

@ -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.model.LambdaModel;
import ru.windcorp.progressia.client.graphics.texture.SimpleTextures; import ru.windcorp.progressia.client.graphics.texture.SimpleTextures;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.world.Camera;
public class LayerTestUI extends AssembledFlatLayer { public class LayerTestUI extends AssembledFlatLayer {
@ -73,7 +74,10 @@ public class LayerTestUI extends AssembledFlatLayer {
target.addCustomRenderer(new LambdaModel(LambdaModel.lambdaBuilder() target.addCustomRenderer(new LambdaModel(LambdaModel.lambdaBuilder()
.addDynamicPart( .addDynamicPart(
target.createRectagle(0, 0, texSize, texSize, 0xFFFFFF, compassFg), 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(); target.popTransform();
@ -81,6 +85,15 @@ public class LayerTestUI extends AssembledFlatLayer {
drawCross(target); 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) { private void drawCross(RenderTarget target) {
int cx = getWidth() / 2; int cx = getWidth() / 2;
int cy = getHeight() / 2; int cy = getHeight() / 2;

View File

@ -19,26 +19,62 @@ package ru.windcorp.progressia.client.graphics.world;
import static java.lang.Math.*; import static java.lang.Math.*;
import java.util.Collection;
import java.util.function.Consumer;
import glm.Glm; import glm.Glm;
import glm.mat._4.Mat4; import glm.mat._4.Mat4;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; 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 { 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<Vec3> offsetGetter,
Consumer<Mat4> 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<Mode> getCameraModes();
}
private float pitch; private Anchor anchor;
private float yaw;
private Anchor.Mode[] modes;
private int currentModeIndex;
private float fieldOfView; private float fieldOfView;
public boolean tmp_mode = false; public Camera(float fieldOfView) {
public Camera(Vec3 position, float pitch, float yaw, float fieldOfView) {
teleport(position);
setPitch(pitch);
setYaw(yaw);
setFieldOfView(fieldOfView); setFieldOfView(fieldOfView);
} }
@ -48,10 +84,7 @@ public class Camera {
applyPerspective(helper); applyPerspective(helper);
rotateCoordinateSystem(helper); rotateCoordinateSystem(helper);
// TODO debug applyMode(helper);
helper.pushViewTransform().translate(3.5f, 0, -0.5f);
if (tmp_mode) helper.pushViewTransform().rotateZ(PI);
applyDirection(helper); applyDirection(helper);
applyPosition(helper); applyPosition(helper);
} }
@ -70,14 +103,38 @@ public class Camera {
private void rotateCoordinateSystem(WorldRenderHelper helper) { private void rotateCoordinateSystem(WorldRenderHelper helper) {
helper.pushViewTransform().rotateX(-PI / 2).rotateZ(PI / 2); 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) { private void applyDirection(WorldRenderHelper helper) {
helper.pushViewTransform().rotateY(pitch).rotateZ(yaw); helper.pushViewTransform()
.rotateY(-anchor.getCameraPitch())
.rotateZ(-anchor.getCameraYaw());
} }
private void applyPosition(WorldRenderHelper helper) { private void applyPosition(WorldRenderHelper helper) {
helper.pushViewTransform().translate(position.negate()); Vec3 v = Vectors.grab3();
position.negate();
anchor.getCameraPosition(v);
v.negate();
helper.pushViewTransform().translate(v);
Vectors.release(v);
} }
private float computeFovY() { 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() { public float getFieldOfView() {
return fieldOfView; return fieldOfView;
@ -140,5 +158,47 @@ public class Camera {
public void setFieldOfView(float fieldOfView) { public void setFieldOfView(float fieldOfView) {
this.fieldOfView = 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<Mode> 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++;
}
}
} }

View File

@ -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<Mode> 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<Mode> getCameraModes() {
return modes;
}
}

View File

@ -19,7 +19,9 @@ package ru.windcorp.progressia.client.graphics.world;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import glm.Glm;
import glm.mat._3.Mat3; import glm.mat._3.Mat3;
import glm.vec._2.Vec2;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.Client; import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.comms.controls.InputBasedControls; 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.InputEvent;
import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.bus.Input; 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.util.Vectors;
import ru.windcorp.progressia.common.world.entity.EntityData;
public class LayerWorld extends Layer { public class LayerWorld extends Layer {
@ -67,19 +71,20 @@ public class LayerWorld extends Layer {
@Override @Override
protected void doRender() { protected void doRender() {
client.getLocalPlayer().setPosition(client.getCamera().getPosition()); if (client.getLocalPlayer() != null) {
client.getLocalPlayer().setVelocity(velocity); tmp_handleControls();
client.getLocalPlayer().getDirection().set( }
-client.getCamera().getYaw(),
-client.getCamera().getPitch()
);
client.getCamera().apply(helper); Camera camera = client.getCamera();
if (camera.hasAnchor()) {
renderWorld();
}
}
private void tmp_handleControls() {
EntityData player = client.getLocalPlayer();
renderWorld(); angMat.set().rotateZ(player.getYaw());
helper.reset();
angMat.set().rotateZ(-client.getCamera().getYaw());
Vec3 movement = Vectors.grab3(); Vec3 movement = Vectors.grab3();
@ -97,15 +102,21 @@ public class LayerWorld extends Layer {
Vec3 velCopy = Vectors.grab3().set(velocity); Vec3 velCopy = Vectors.grab3().set(velocity);
velCopy.mul((float) (GraphicsInterface.getFrameLength() * 60)); velCopy.mul((float) (GraphicsInterface.getFrameLength() * 60));
client.getCamera().move(velCopy);
player.getPosition().add(velCopy);
player.getVelocity().set(velocity);
Vectors.release(velCopy); Vectors.release(velCopy);
} }
private void renderWorld() { private void renderWorld() {
client.getCamera().apply(helper);
FaceCulling.push(true); FaceCulling.push(true);
this.client.getWorld().render(helper); this.client.getWorld().render(helper);
FaceCulling.pop(); FaceCulling.pop();
helper.reset();
} }
@Override @Override
@ -170,7 +181,9 @@ public class LayerWorld extends Layer {
case GLFW.GLFW_KEY_F5: case GLFW.GLFW_KEY_F5:
if (!event.isPress()) return false; if (!event.isPress()) return false;
client.getCamera().tmp_mode = !client.getCamera().tmp_mode; if (client.getCamera().hasAnchor()) {
client.getCamera().selectNextMode();
}
break; break;
default: default:
@ -183,12 +196,26 @@ public class LayerWorld extends Layer {
private void onMouseMoved(CursorMoveEvent event) { private void onMouseMoved(CursorMoveEvent event) {
if (!flag) return; if (!flag) return;
final float yawScale = 0.002f; final float yawScale = -0.002f;
final float pitchScale = yawScale; final float pitchScale = yawScale;
EntityData player = client.getLocalPlayer();
client.getCamera().turn( if (player != null) {
(float) (event.getChangeY() * pitchScale), normalizeAngles(player.getDirection().add(
(float) (event.getChangeX() * yawScale) (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
); );
} }

View File

@ -25,9 +25,9 @@ import java.util.WeakHashMap;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.graphics.backend.FaceCulling; 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.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; 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.ChunkData;
import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.WorldData;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
@ -37,7 +37,7 @@ public class WorldRender {
private final WorldData data; private final WorldData data;
private final Map<ChunkData, ChunkRender> chunks = new HashMap<>(); private final Map<ChunkData, ChunkRender> chunks = new HashMap<>();
private final Map<EntityData, Renderable> entityModels = private final Map<EntityData, EntityRenderable> entityModels =
Collections.synchronizedMap(new WeakHashMap<>()); Collections.synchronizedMap(new WeakHashMap<>());
public WorldRender(WorldData data) { public WorldRender(WorldData data) {
@ -86,14 +86,14 @@ public class WorldRender {
FaceCulling.pop(); FaceCulling.pop();
} }
public Renderable getEntityRenderable(EntityData entity) { public EntityRenderable getEntityRenderable(EntityData entity) {
return entityModels.computeIfAbsent( return entityModels.computeIfAbsent(
entity, entity,
WorldRender::createEntityRenderable WorldRender::createEntityRenderable
); );
} }
private static Renderable createEntityRenderable(EntityData entity) { private static EntityRenderable createEntityRenderable(EntityData entity) {
return EntityRenderRegistry.getInstance().get(entity.getId()) return EntityRenderRegistry.getInstance().get(entity.getId())
.createRenderable(entity); .createRenderable(entity);
} }

View File

@ -1,6 +1,5 @@
package ru.windcorp.progressia.client.world.entity; 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.util.Namespaced;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
@ -10,6 +9,6 @@ public abstract class EntityRender extends Namespaced {
super(namespace, name); super(namespace, name);
} }
public abstract Renderable createRenderable(EntityData entity); public abstract EntityRenderable createRenderable(EntityData entity);
} }

View File

@ -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);
}
}

View File

@ -1,17 +1,20 @@
package ru.windcorp.progressia.client.world.entity; package ru.windcorp.progressia.client.world.entity;
import static java.lang.Math.*; import static java.lang.Math.*;
import static ru.windcorp.progressia.common.util.FloatMathUtils.*;
import glm.Glm; import glm.Glm;
import glm.mat._4.Mat4; import glm.mat._4.Mat4;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; 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.util.Vectors;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
public class QuadripedModel implements Renderable { public class QuadripedModel extends EntityRenderable {
private static abstract class BodyPart { private static abstract class BodyPart {
private final Renderable renderable; private final Renderable renderable;
@ -20,7 +23,6 @@ public class QuadripedModel implements Renderable {
public BodyPart(Renderable renderable, Vec3 joint) { public BodyPart(Renderable renderable, Vec3 joint) {
this.renderable = renderable; this.renderable = renderable;
if (joint != null) { if (joint != null) {
// joint.negate(this.translation);
this.translation.set(joint); this.translation.set(joint);
} }
} }
@ -37,6 +39,10 @@ public class QuadripedModel implements Renderable {
} }
protected abstract void applyTransform(Mat4 mat, QuadripedModel model); protected abstract void applyTransform(Mat4 mat, QuadripedModel model);
public Vec3 getTranslation() {
return translation;
}
} }
public static class Body extends BodyPart { public static class Body extends BodyPart {
@ -54,19 +60,27 @@ public class QuadripedModel implements Renderable {
private final float maxYaw; private final float maxYaw;
private final float maxPitch; private final float maxPitch;
private final Vec3 viewPoint;
public Head( public Head(
Renderable renderable, Vec3 joint, Renderable renderable, Vec3 joint,
double maxYawDegrees, double maxPitchDegrees double maxYawDegrees, double maxPitchDegrees,
Vec3 viewPoint
) { ) {
super(renderable, joint); super(renderable, joint);
this.maxYaw = (float) toRadians(maxYawDegrees); this.maxYaw = (float) toRadians(maxYawDegrees);
this.maxPitch = (float) toRadians(maxPitchDegrees); this.maxPitch = (float) toRadians(maxPitchDegrees);
this.viewPoint = viewPoint;
} }
@Override @Override
protected void applyTransform(Mat4 mat, QuadripedModel model) { protected void applyTransform(Mat4 mat, QuadripedModel model) {
mat.rotateZ(model.headYaw).rotateY(model.headPitch); mat.rotateZ(model.headYaw).rotateY(model.headPitch);
} }
public Vec3 getViewPoint() {
return viewPoint;
}
} }
public static class Leg extends BodyPart { 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 Body body;
private final Head head; private final Head head;
private final Leg leftForeLeg, rightForeLeg; private final Leg leftForeLeg, rightForeLeg;
@ -120,7 +132,7 @@ public class QuadripedModel implements Renderable {
float scale float scale
) { ) {
this.entity = entity; super(entity);
this.body = body; this.body = body;
this.head = head; this.head = head;
@ -134,23 +146,21 @@ public class QuadripedModel implements Renderable {
@Override @Override
public void render(ShapeRenderHelper renderer) { public void render(ShapeRenderHelper renderer) {
accountForVelocity();
evaluateAngles();
renderer.pushTransform().scale(scale).rotateZ(bodyYaw); renderer.pushTransform().scale(scale).rotateZ(bodyYaw);
body.render(renderer, this); body.render(renderer, this);
head.render(renderer, this); head.render(renderer, this);
leftForeLeg.render(renderer, this); leftForeLeg.render(renderer, this);
rightForeLeg.render(renderer, this); rightForeLeg.render(renderer, this);
leftHindLeg.render(renderer, this); leftHindLeg.render(renderer, this);
rightHindLeg.render(renderer, this); rightHindLeg.render(renderer, this);
renderer.popTransform(); renderer.popTransform();
accountForVelocity();
evaluateAngles();
} }
private void evaluateAngles() { private void evaluateAngles() {
float globalYaw = normalizeAngle(entity.getYaw()); float globalYaw = normalizeAngle(getData().getYaw());
if (Float.isNaN(bodyYaw)) { if (Float.isNaN(bodyYaw)) {
bodyYaw = globalYaw; bodyYaw = globalYaw;
@ -170,14 +180,14 @@ public class QuadripedModel implements Renderable {
bodyYaw = normalizeAngle(bodyYaw); bodyYaw = normalizeAngle(bodyYaw);
headPitch = Glm.clamp( headPitch = Glm.clamp(
entity.getPitch(), getData().getPitch(),
-head.maxPitch, head.maxPitch -head.maxPitch, head.maxPitch
); );
} }
private void accountForVelocity() { private void accountForVelocity() {
Vec3 horizontal = Vectors.grab3(); Vec3 horizontal = Vectors.grab3();
horizontal.set(entity.getVelocity()); horizontal.set(getData().getVelocity());
horizontal.z = 0; horizontal.z = 0;
velocity = horizontal.length(); velocity = horizontal.length();
@ -201,11 +211,26 @@ public class QuadripedModel implements Renderable {
velocityCoeff *= velocityCoeff; velocityCoeff *= velocityCoeff;
} }
} }
private static float normalizeAngle(float x) { @Override
final float half = (float) (PI); public void getViewPoint(Vec3 output) {
final float full = (float) (2 * PI); Mat4 m = Matrices.grab4();
return ((x + half) % full + full) % full - half; 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);
} }
} }

View File

@ -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;
}
}

View File

@ -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() {}
}

View File

@ -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.
* <p>
* All {@code grab}bed objects must be {@code release}d as soon as possible.
* Ideally, user code should be:
* <pre>
* Mat4 myMatrix = Vectors.grab4();
* try {
* // use myMatrix
* } finally {
* Matrices.release(myMatrix);
* }
* </pre>
* Provided objects may be reused after {@code release} has been invoked;
* do not store them.
* <p>
* This class is thread- and recursion-safe.
*
* @see Vectors
*/
public class Matrices {
private static final LowOverheadCache<Mat3> 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<Mat4> 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<Mat4d> MAT4DS =
new LowOverheadCache<>(Mat4d::new);
public static Mat4d grab4d() {
return MAT4DS.grab();
}
public static void release(Mat4d m) {
MAT4DS.release(m);
}
private Matrices() {}
}

View File

@ -25,6 +25,8 @@ import glm.vec._4.i.Vec4i;
* do not store them. * do not store them.
* <p> * <p>
* This class is thread- and recursion-safe. * This class is thread- and recursion-safe.
*
* @see Matrices
*/ */
public class Vectors { public class Vectors {

View File

@ -22,11 +22,13 @@ import static ru.windcorp.progressia.common.world.block.BlockFace.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import glm.vec._2.Vec2;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.world.tile.TileLocation; import ru.windcorp.progressia.client.world.tile.TileLocation;
@ -131,7 +133,11 @@ public class ChunkData {
} }
EntityData javapony = new EntityData("Test", "Javapony"); 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); getEntities().add(javapony);
} }

View File

@ -4,12 +4,14 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import glm.vec._3.i.Vec3i;
import gnu.trove.TCollections; import gnu.trove.TCollections;
import gnu.trove.map.TIntObjectMap; import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap; import gnu.trove.map.hash.TIntObjectHashMap;
import ru.windcorp.progressia.common.comms.CommsChannel.Role; import ru.windcorp.progressia.common.comms.CommsChannel.Role;
import ru.windcorp.progressia.common.comms.CommsChannel.State; import ru.windcorp.progressia.common.comms.CommsChannel.State;
import ru.windcorp.progressia.common.comms.packets.Packet; import ru.windcorp.progressia.common.comms.packets.Packet;
import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.Server;
public class ClientManager { public class ClientManager {
@ -36,6 +38,11 @@ public class ClientManager {
clientsById.put(client.getId(), client); clientsById.put(client.getId(), client);
client.addListener(new DefaultServerCommsListener(this, 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) { public void disconnectClient(Client client) {