Refactored camera controls and added noclip mode
- Refactored camera controls into a separate class - Added noclip mode - Toggle with V - Simply uncouples camera from character; does not change interactions or load chunks! - Implemented sloppily, expect bugs in the future - EntityData's .equals() is now equivalent to == and .hashCode() hashes entity ID only - Collision can now be disabled for an object by setting its CollisionModel to null
This commit is contained in:
parent
49d283e7a3
commit
c8138faabe
@ -91,5 +91,14 @@ public class EntityAnchor implements Anchor {
|
||||
public Collection<Mode> getCameraModes() {
|
||||
return modes;
|
||||
}
|
||||
|
||||
public EntityData getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Anchor for entity " + entity;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -214,6 +214,9 @@ public class LayerWorld extends Layer {
|
||||
if (ClientState.getInstance().getLocalPlayer().getEntity() == entity && tmp_testControls.isFlying()) {
|
||||
return;
|
||||
}
|
||||
if (entity.getId().equals("Test:NoclipCamera")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vec3 gravitationalAcceleration = Vectors.grab3();
|
||||
gm.getGravity(entity.getPosition(), gravitationalAcceleration);
|
||||
|
@ -105,6 +105,9 @@ public class Collider {
|
||||
// For every pair of colls
|
||||
for (int i = 0; i < colls.size(); ++i) {
|
||||
Collideable a = colls.get(i);
|
||||
if (a.getCollisionModel() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tuneWorldCollisionHelper(a, tickLength, world, workspace);
|
||||
|
||||
@ -115,6 +118,10 @@ public class Collider {
|
||||
|
||||
for (int j = i + 1; j < colls.size(); ++j) {
|
||||
Collideable b = colls.get(j);
|
||||
if (b.getCollisionModel() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Collision collision = getCollision(a, b, tickLength, workspace);
|
||||
result = workspace.updateLatestCollision(result, collision);
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ public abstract class StatefulObject extends Namespaced implements Encodable {
|
||||
|
||||
StatefulObject statefulObj = (StatefulObject) obj;
|
||||
|
||||
if (statefulObj.getId().equals(this.getId()))
|
||||
if (!statefulObj.getId().equals(this.getId()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -379,5 +379,15 @@ public class EntityData extends StatefulObject implements Collideable, EntityGen
|
||||
|
||||
super.read(input, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return this == obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Long.hashCode(entityId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,26 +18,20 @@
|
||||
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import glm.Glm;
|
||||
import glm.mat._4.Mat4;
|
||||
import glm.vec._3.Vec3;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import ru.windcorp.progressia.client.ClientState;
|
||||
import ru.windcorp.progressia.client.graphics.GUI;
|
||||
import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend;
|
||||
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
||||
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.WheelScrollEvent;
|
||||
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
|
||||
import ru.windcorp.progressia.client.localization.Localizer;
|
||||
import ru.windcorp.progressia.common.util.Matrices;
|
||||
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||
import ru.windcorp.progressia.test.controls.CameraControls;
|
||||
import ru.windcorp.progressia.test.controls.MovementControls;
|
||||
|
||||
public class TestPlayerControls {
|
||||
@ -49,6 +43,7 @@ public class TestPlayerControls {
|
||||
}
|
||||
|
||||
private final MovementControls movementControls = new MovementControls();
|
||||
private final CameraControls cameraControls = new CameraControls();
|
||||
|
||||
private int selectedBlock = 0;
|
||||
private int selectedTile = 0;
|
||||
@ -66,7 +61,8 @@ public class TestPlayerControls {
|
||||
|
||||
private void reset() {
|
||||
movementControls.reset();
|
||||
|
||||
cameraControls.reset();
|
||||
|
||||
debugLayer = null;
|
||||
selectedBlock = 0;
|
||||
selectedTile = 0;
|
||||
@ -79,6 +75,7 @@ public class TestPlayerControls {
|
||||
|
||||
public void registerControls() {
|
||||
movementControls.registerControls();
|
||||
cameraControls.registerControls();
|
||||
}
|
||||
|
||||
public void handleInput(InputEvent event) {
|
||||
@ -86,9 +83,6 @@ public class TestPlayerControls {
|
||||
if (onKeyEvent((KeyEvent) event)) {
|
||||
event.consume();
|
||||
}
|
||||
} else if (event instanceof CursorMoveEvent) {
|
||||
onMouseMoved((CursorMoveEvent) event);
|
||||
event.consume();
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,10 +106,6 @@ public class TestPlayerControls {
|
||||
handleDebugLayerSwitch();
|
||||
break;
|
||||
|
||||
case GLFW.GLFW_KEY_F5:
|
||||
handleCameraMode();
|
||||
break;
|
||||
|
||||
case GLFW.GLFW_KEY_L:
|
||||
handleLanguageSwitch();
|
||||
break;
|
||||
@ -143,16 +133,6 @@ public class TestPlayerControls {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCameraMode() {
|
||||
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ClientState.getInstance().getCamera().hasAnchor()) {
|
||||
ClientState.getInstance().getCamera().selectNextMode();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLanguageSwitch() {
|
||||
Localizer localizer = Localizer.getInstance();
|
||||
if (localizer.getLanguage().equals("ru-RU")) {
|
||||
@ -162,43 +142,6 @@ public class TestPlayerControls {
|
||||
}
|
||||
}
|
||||
|
||||
private void onMouseMoved(CursorMoveEvent event) {
|
||||
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final double yawScale = -0.002f;
|
||||
final double pitchScale = -yawScale;
|
||||
final double pitchExtremum = Math.PI/2 * 0.95f;
|
||||
|
||||
double yawChange = event.getChangeX() * yawScale;
|
||||
double pitchChange = event.getChangeY() * pitchScale;
|
||||
|
||||
EntityData player = getEntity();
|
||||
|
||||
double startPitch = player.getPitch();
|
||||
double endPitch = startPitch + pitchChange;
|
||||
endPitch = Glm.clamp(endPitch, -pitchExtremum, +pitchExtremum);
|
||||
pitchChange = endPitch - startPitch;
|
||||
|
||||
Mat4 mat = Matrices.grab4();
|
||||
Vec3 lookingAt = Vectors.grab3();
|
||||
Vec3 rightVector = Vectors.grab3();
|
||||
|
||||
rightVector.set(player.getLookingAt()).cross(player.getUpVector()).normalize();
|
||||
|
||||
mat.identity()
|
||||
.rotate((float) yawChange, player.getUpVector())
|
||||
.rotate((float) pitchChange, rightVector);
|
||||
|
||||
VectorUtil.applyMat4(player.getLookingAt(), mat, lookingAt);
|
||||
player.setLookingAt(lookingAt);
|
||||
|
||||
Vectors.release(rightVector);
|
||||
Vectors.release(lookingAt);
|
||||
Matrices.release(mat);
|
||||
}
|
||||
|
||||
public void switchPlacingMode() {
|
||||
isBlockSelected = !isBlockSelected;
|
||||
}
|
||||
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.test.controls;
|
||||
|
||||
import glm.Glm;
|
||||
import glm.mat._4.Mat4;
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.client.ClientState;
|
||||
import ru.windcorp.progressia.client.comms.controls.ControlTriggerRegistry;
|
||||
import ru.windcorp.progressia.client.comms.controls.ControlTriggers;
|
||||
import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent;
|
||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||
import ru.windcorp.progressia.client.graphics.world.Camera;
|
||||
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
|
||||
import ru.windcorp.progressia.common.util.Matrices;
|
||||
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
|
||||
public class CameraControls {
|
||||
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
// No instance fields; futureproofing
|
||||
}
|
||||
|
||||
public void registerControls() {
|
||||
ControlTriggerRegistry triggers = ControlTriggerRegistry.getInstance();
|
||||
|
||||
triggers.register(
|
||||
ControlTriggers.localOf(
|
||||
"Test:TurnCameraWithCursor",
|
||||
CursorMoveEvent.class,
|
||||
this::turnCameraWithCursor
|
||||
)
|
||||
);
|
||||
|
||||
triggers.register(
|
||||
ControlTriggers.localOf(
|
||||
"Test:SwitchCameraMode",
|
||||
KeyEvent.class,
|
||||
this::switchCameraMode,
|
||||
new KeyMatcher("F5")::matches
|
||||
)
|
||||
);
|
||||
|
||||
NoclipCamera.register();
|
||||
|
||||
triggers.register(
|
||||
ControlTriggers.localOf(
|
||||
"Test:ToggleNoclip",
|
||||
KeyEvent.class,
|
||||
NoclipCamera::toggleNoclip,
|
||||
new KeyMatcher("V")::matches
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void turnCameraWithCursor(CursorMoveEvent event) {
|
||||
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Camera.Anchor cameraAnchor = ClientState.getInstance().getCamera().getAnchor();
|
||||
if (!(cameraAnchor instanceof EntityAnchor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityData player = ((EntityAnchor) cameraAnchor).getEntity();
|
||||
|
||||
final double yawScale = -0.002f;
|
||||
final double pitchScale = -yawScale;
|
||||
final double pitchExtremum = Math.PI / 2 * 0.995f;
|
||||
|
||||
double yawChange = event.getChangeX() * yawScale;
|
||||
double pitchChange = event.getChangeY() * pitchScale;
|
||||
|
||||
double startPitch = player.getPitch();
|
||||
double endPitch = startPitch + pitchChange;
|
||||
endPitch = Glm.clamp(endPitch, -pitchExtremum, +pitchExtremum);
|
||||
pitchChange = endPitch - startPitch;
|
||||
|
||||
Mat4 mat = Matrices.grab4();
|
||||
Vec3 lookingAt = Vectors.grab3();
|
||||
Vec3 rightVector = Vectors.grab3();
|
||||
|
||||
rightVector.set(player.getLookingAt()).cross(player.getUpVector()).normalize();
|
||||
|
||||
mat.identity()
|
||||
.rotate((float) yawChange, player.getUpVector())
|
||||
.rotate((float) pitchChange, rightVector);
|
||||
|
||||
VectorUtil.applyMat4(player.getLookingAt(), mat, lookingAt);
|
||||
player.setLookingAt(lookingAt);
|
||||
|
||||
Vectors.release(rightVector);
|
||||
Vectors.release(lookingAt);
|
||||
Matrices.release(mat);
|
||||
}
|
||||
|
||||
private void switchCameraMode() {
|
||||
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ClientState.getInstance().getCamera().hasAnchor()) {
|
||||
ClientState.getInstance().getCamera().selectNextMode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -28,6 +28,8 @@ import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
||||
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
|
||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||
import ru.windcorp.progressia.client.graphics.world.Camera;
|
||||
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
|
||||
import ru.windcorp.progressia.common.Units;
|
||||
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
@ -96,8 +98,15 @@ public class MovementControls {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityData player = ClientState.getInstance().getLocalPlayer().getEntity();
|
||||
assert player != null;
|
||||
Camera.Anchor cameraAnchor = ClientState.getInstance().getCamera().getAnchor();
|
||||
if (!(cameraAnchor instanceof EntityAnchor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityData player = ((EntityAnchor) cameraAnchor).getEntity();
|
||||
|
||||
boolean isFlying = this.isFlying || player.getId().equals("Test:NoclipCamera");
|
||||
boolean isSprinting = this.isSprinting || player.getId().equals("Test:NoclipCamera");
|
||||
|
||||
final float speed, authority;
|
||||
|
||||
@ -110,7 +119,7 @@ public class MovementControls {
|
||||
}
|
||||
|
||||
Mat3 movementTransform = getMovementTransform(player, null);
|
||||
Vec3 desiredVelocity = getDesiredVelocity(movementTransform, speed);
|
||||
Vec3 desiredVelocity = getDesiredVelocity(movementTransform, speed, isFlying);
|
||||
Vec3 newVelocity = getNewVelocity(desiredVelocity, player.getVelocity(), authority, player.getUpVector());
|
||||
player.getVelocity().set(newVelocity);
|
||||
|
||||
@ -144,7 +153,7 @@ public class MovementControls {
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
private Vec3 getDesiredVelocity(Mat3 movementTransform, float speed) {
|
||||
private Vec3 getDesiredVelocity(Mat3 movementTransform, float speed, boolean isFlying) {
|
||||
int forward = 0;
|
||||
int right = 0;
|
||||
int up = 0;
|
||||
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.test.controls;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.client.ClientState;
|
||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||
import ru.windcorp.progressia.client.graphics.world.Camera;
|
||||
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
|
||||
import ru.windcorp.progressia.client.world.WorldRender;
|
||||
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.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityDataRegistry;
|
||||
|
||||
public class NoclipCamera {
|
||||
|
||||
private static class NoclipEntityRender extends EntityRender {
|
||||
|
||||
public NoclipEntityRender() {
|
||||
super("Test:NoclipCamera");
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityRenderable createRenderable(EntityData entity) {
|
||||
return new NoclipEntityRenderable(entity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class NoclipEntityRenderable extends EntityRenderable {
|
||||
|
||||
public NoclipEntityRenderable(EntityData data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRender(ShapeRenderHelper renderer) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
EntityDataRegistry.getInstance().register("Test:NoclipCamera");
|
||||
EntityRenderRegistry.getInstance().register(new NoclipEntityRender());
|
||||
}
|
||||
|
||||
public static void toggleNoclip() {
|
||||
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Camera camera = ClientState.getInstance().getCamera();
|
||||
WorldRender world = ClientState.getInstance().getWorld();
|
||||
EntityData player = ClientState.getInstance().getLocalPlayer().getEntity();
|
||||
|
||||
List<EntityData> existingCameras = world.getData().getEntities().stream().filter(e -> e.getId().equals("Test:NoclipCamera")).collect(Collectors.toList());
|
||||
if (!existingCameras.isEmpty()) {
|
||||
existingCameras.forEach(world.getData()::removeEntity);
|
||||
camera.setAnchor(new EntityAnchor(world.getEntityRenderable(player)));
|
||||
return;
|
||||
}
|
||||
|
||||
EntityData noclipCamera = EntityDataRegistry.getInstance().create("Test:NoclipCamera");
|
||||
|
||||
noclipCamera.setLookingAt(player.getLookingAt());
|
||||
noclipCamera.setUpVector(player.getUpVector());
|
||||
noclipCamera.setPosition(player.getPosition());
|
||||
noclipCamera.setVelocity(player.getVelocity());
|
||||
noclipCamera.setEntityId(new Random().nextLong());
|
||||
|
||||
player.setVelocity(new Vec3(0));
|
||||
|
||||
world.getData().addEntity(noclipCamera);
|
||||
camera.setAnchor(new EntityAnchor(world.getEntityRenderable(noclipCamera)));
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user