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:
OLEGSHA 2021-12-13 19:36:26 +03:00
parent 49d283e7a3
commit c8138faabe
Signed by: OLEGSHA
GPG Key ID: E57A4B08D64AFF7A
9 changed files with 277 additions and 67 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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