Merge remote-tracking branch 'origin/master' into crashreports
This commit is contained in:
commit
ccda1eff74
25
build.gradle
25
build.gradle
@ -18,10 +18,12 @@ dependencies {
|
||||
implementation 'ru.windcorp.fork.io.github.java-graphics:glm:1.0.1'
|
||||
|
||||
// log4j
|
||||
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.3'
|
||||
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.3'
|
||||
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.3'
|
||||
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.3'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
|
||||
// See also LWJGL dependencies below
|
||||
}
|
||||
|
||||
/*
|
||||
@ -77,3 +79,22 @@ dependencies {
|
||||
}
|
||||
|
||||
// LWJGL END
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
"Main-Class": "ru.windcorp.progressia.client.ProgressiaClientMain",
|
||||
"Class-Path": configurations.runtimeClasspath.collect { "lib/" + it.getName() }.join(' ')
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies runtime dependencies to a prespecified location so they can be packaged properly.
|
||||
*/
|
||||
task copyLibs(type: Copy) {
|
||||
into "${libsDir}/lib"
|
||||
from configurations.runtimeClasspath
|
||||
}
|
||||
|
||||
build.dependsOn(copyLibs)
|
||||
|
@ -26,6 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class StringUtil {
|
||||
@ -775,4 +776,35 @@ public class StringUtil {
|
||||
else return (char) ('A' - 0xA + value);
|
||||
}
|
||||
|
||||
public static String replaceAll(String source, String substring, String replacement) {
|
||||
Objects.requireNonNull(source, "source");
|
||||
Objects.requireNonNull(substring, "substring");
|
||||
|
||||
if (substring.isEmpty()) {
|
||||
throw new IllegalArgumentException("substring is empty");
|
||||
}
|
||||
|
||||
if (!source.contains(substring)) { // also passes if source is empty
|
||||
return source;
|
||||
}
|
||||
|
||||
if (substring.equals(replacement)) { // null-safe
|
||||
return source;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(2 * source.length());
|
||||
|
||||
for (int i = 0; i < source.length() - substring.length() + 1; ++i) {
|
||||
if (source.startsWith(substring, i)) {
|
||||
if (replacement != null) {
|
||||
sb.append(replacement);
|
||||
}
|
||||
} else {
|
||||
sb.append(source.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ package ru.windcorp.progressia.client;
|
||||
import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel;
|
||||
import ru.windcorp.progressia.client.graphics.GUI;
|
||||
import ru.windcorp.progressia.client.graphics.flat.LayerTestUI;
|
||||
import ru.windcorp.progressia.client.graphics.gui.LayerTestGUI;
|
||||
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
import ru.windcorp.progressia.server.ServerState;
|
||||
import ru.windcorp.progressia.test.LayerTestGUI;
|
||||
|
||||
public class ClientState {
|
||||
|
||||
|
@ -1,125 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Progressia
|
||||
* Copyright (C) 2020 Wind Corporation
|
||||
*
|
||||
* 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.client.graphics.gui;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import glm.vec._2.i.Vec2i;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import ru.windcorp.progressia.client.graphics.Colors;
|
||||
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
|
||||
import ru.windcorp.progressia.client.graphics.font.Font;
|
||||
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
|
||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||
import ru.windcorp.progressia.client.localization.Localizer;
|
||||
import ru.windcorp.progressia.client.localization.MutableString;
|
||||
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
|
||||
|
||||
public class LayerTestGUI extends GUILayer {
|
||||
|
||||
private static class DebugComponent extends Component {
|
||||
private final int color;
|
||||
|
||||
public DebugComponent(String name, Vec2i size, int color) {
|
||||
super(name);
|
||||
this.color = color;
|
||||
|
||||
setPreferredSize(size);
|
||||
|
||||
addListener(new Object() {
|
||||
@Subscribe
|
||||
public void onHoverChanged(HoverEvent e) {
|
||||
requestReassembly();
|
||||
}
|
||||
});
|
||||
|
||||
addListener(KeyEvent.class, this::onClicked);
|
||||
}
|
||||
|
||||
private boolean onClicked(KeyEvent event) {
|
||||
if (!event.isMouse()) {
|
||||
return false;
|
||||
} else if (event.isPress() && event.isLeftMouseButton()) {
|
||||
System.out.println("You pressed a Component!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assembleSelf(RenderTarget target) {
|
||||
target.fill(getX(), getY(), getWidth(), getHeight(), Colors.BLACK);
|
||||
|
||||
target.fill(
|
||||
getX() + 2, getY() + 2,
|
||||
getWidth() - 4, getHeight() - 4,
|
||||
isHovered() ? Colors.DEBUG_YELLOW : color
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public LayerTestGUI() {
|
||||
super("LayerTestGui", new LayoutAlign(1, 0.75, 5));
|
||||
|
||||
Panel panel = new Panel("Alex", new LayoutVertical(5));
|
||||
|
||||
panel.addChild(new DebugComponent("Bravo", new Vec2i(200, 100), 0x44FF44));
|
||||
|
||||
Component charlie = new DebugComponent("Charlie", null, 0x222222);
|
||||
charlie.setLayout(new LayoutVertical(5));
|
||||
|
||||
//Debug
|
||||
Localizer.getInstance().setLanguage("ru-RU");
|
||||
MutableString epsilon = new MutableStringLocalized("Epsilon")
|
||||
.addListener(() -> ((Label)charlie.getChild(0)).update()).format(34, "thirty-four");
|
||||
// These two are swapped in code due to a bug in layouts, fixing ATM
|
||||
charlie.addChild(
|
||||
new Label(
|
||||
"Delta",
|
||||
new Font().withColor(0xCCBB44).deriveShadow().deriveBold(),
|
||||
"Пре-альфа!"
|
||||
)
|
||||
);
|
||||
charlie.addChild(
|
||||
new Label(
|
||||
"Epsilon",
|
||||
new Font().withColor(0x4444BB).deriveItalic(),
|
||||
() -> epsilon.get().concat("\u269b")
|
||||
)
|
||||
);
|
||||
panel.addChild(charlie);
|
||||
|
||||
|
||||
charlie.addListener(KeyEvent.class, e -> {
|
||||
if(e.isPress() && e.getKey() == GLFW.GLFW_KEY_L) {
|
||||
Localizer localizer = Localizer.getInstance();
|
||||
if (localizer.getLanguage().equals("ru-RU")) {
|
||||
localizer.setLanguage("en-US");
|
||||
} else {
|
||||
localizer.setLanguage("ru-RU");
|
||||
}
|
||||
return true;
|
||||
} return false;
|
||||
});
|
||||
charlie.setFocusable(true);
|
||||
charlie.takeFocus();
|
||||
|
||||
getRoot().addChild(panel);
|
||||
}
|
||||
|
||||
}
|
@ -122,4 +122,13 @@ public class LambdaModel extends DynamicModel {
|
||||
|
||||
}
|
||||
|
||||
public static LambdaModel animate(Renderable model, TransformGetter transform) {
|
||||
return new LambdaModel(
|
||||
new Renderable[] { model },
|
||||
new Mat4[] { new Mat4() },
|
||||
new boolean[] { true },
|
||||
new TransformGetter[] { transform }
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,10 +19,10 @@ public class ComplexTexture {
|
||||
this.primitive = primitive;
|
||||
|
||||
this.assumedWidth = abstractWidth
|
||||
* primitive.getWidth() / (float) primitive.getBufferWidth();
|
||||
/ (float) primitive.getWidth() * primitive.getBufferWidth();
|
||||
|
||||
this.assumedHeight = abstractHeight
|
||||
* primitive.getHeight() / (float) primitive.getBufferHeight();
|
||||
/ (float) primitive.getHeight() * primitive.getBufferHeight();
|
||||
}
|
||||
|
||||
public Texture get(int x, int y, int width, int height) {
|
||||
|
@ -272,6 +272,10 @@ public class Camera {
|
||||
}
|
||||
}
|
||||
|
||||
public int getCurrentModeIndex() {
|
||||
return currentModeIndex;
|
||||
}
|
||||
|
||||
public float getLastAnchorYaw() {
|
||||
return lastAnchorYaw;
|
||||
}
|
||||
|
@ -20,45 +20,27 @@ package ru.windcorp.progressia.client.graphics.world;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.ClientState;
|
||||
import ru.windcorp.progressia.client.comms.controls.InputBasedControls;
|
||||
import ru.windcorp.progressia.client.graphics.Layer;
|
||||
import ru.windcorp.progressia.client.graphics.backend.FaceCulling;
|
||||
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.bus.Input;
|
||||
import ru.windcorp.progressia.common.collision.AABB;
|
||||
import ru.windcorp.progressia.common.Units;
|
||||
import ru.windcorp.progressia.common.collision.Collideable;
|
||||
import ru.windcorp.progressia.common.collision.CollisionClock;
|
||||
import ru.windcorp.progressia.common.collision.CollisionModel;
|
||||
import ru.windcorp.progressia.common.collision.CompoundCollisionModel;
|
||||
import ru.windcorp.progressia.common.collision.colliders.Collider;
|
||||
import ru.windcorp.progressia.common.util.FloatMathUtils;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.test.AABBRenderer;
|
||||
import ru.windcorp.progressia.test.CollisionModelRenderer;
|
||||
import ru.windcorp.progressia.test.TestPlayerControls;
|
||||
|
||||
public class LayerWorld extends Layer {
|
||||
|
||||
private final Mat3 angMat = new Mat3();
|
||||
|
||||
private int movementForward = 0;
|
||||
private int movementRight = 0;
|
||||
private int movementUp = 0;
|
||||
|
||||
private final WorldRenderHelper helper = new WorldRenderHelper();
|
||||
|
||||
private final Client client;
|
||||
private final InputBasedControls inputBasedControls;
|
||||
private final TestPlayerControls tmp_testControls = TestPlayerControls.getInstance();
|
||||
|
||||
public LayerWorld(Client client) {
|
||||
super("World");
|
||||
@ -79,40 +61,12 @@ public class LayerWorld extends Layer {
|
||||
|
||||
@Override
|
||||
protected void doRender() {
|
||||
if (client.getLocalPlayer() != null) {
|
||||
tmp_handleControls();
|
||||
}
|
||||
|
||||
Camera camera = client.getCamera();
|
||||
if (camera.hasAnchor()) {
|
||||
renderWorld();
|
||||
}
|
||||
}
|
||||
|
||||
private void tmp_handleControls() {
|
||||
EntityData player = client.getLocalPlayer();
|
||||
|
||||
angMat.identity().rotateZ(player.getYaw());
|
||||
|
||||
Vec3 movement = Vectors.grab3();
|
||||
|
||||
// Horizontal and vertical max control speed
|
||||
final float movementSpeed = 0.1f * 60.0f;
|
||||
// (0; 1], 1 is instant change, 0 is no control authority
|
||||
final float controlAuthority = 0.1f;
|
||||
|
||||
movement.set(movementForward, -movementRight, 0);
|
||||
if (movementForward != 0 && movementRight != 0) movement.normalize();
|
||||
angMat.mul_(movement); // bug in jglm, .mul() and mul_() are swapped
|
||||
movement.z = movementUp;
|
||||
movement.mul(movementSpeed);
|
||||
movement.sub(player.getVelocity());
|
||||
movement.mul(controlAuthority);
|
||||
player.getVelocity().add(movement);
|
||||
|
||||
Vectors.release(movement);
|
||||
}
|
||||
|
||||
private void renderWorld() {
|
||||
client.getCamera().apply(helper);
|
||||
FaceCulling.push(true);
|
||||
@ -128,150 +82,75 @@ public class LayerWorld extends Layer {
|
||||
private final Collider.ColliderWorkspace tmp_colliderWorkspace = new Collider.ColliderWorkspace();
|
||||
private final List<Collideable> tmp_collideableList = new ArrayList<>();
|
||||
|
||||
private static final boolean RENDER_AABBS = true;
|
||||
private static final boolean RENDER_COLLISION_MODELS = false;
|
||||
|
||||
private void tmp_doEveryFrame() {
|
||||
float tickLength = (float) GraphicsInterface.getFrameLength();
|
||||
|
||||
try {
|
||||
if (RENDER_AABBS) {
|
||||
tmp_performCollisions(tickLength);
|
||||
|
||||
tmp_testControls.applyPlayerControls();
|
||||
|
||||
for (EntityData data : this.client.getWorld().getData().getEntities()) {
|
||||
CollisionModel model = data.getCollisionModel();
|
||||
if (model instanceof AABB) {
|
||||
AABBRenderer.renderAABB((AABB) model, helper);
|
||||
} else if (model instanceof CompoundCollisionModel) {
|
||||
AABBRenderer.renderAABBsInCompound((CompoundCollisionModel) model, helper);
|
||||
tmp_applyFriction(data);
|
||||
tmp_applyGravity(data, tickLength);
|
||||
tmp_renderCollisionModel(data);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("OLEGSHA is to blame. Tell him he vry stupiDD!!");
|
||||
System.exit(31337);
|
||||
}
|
||||
}
|
||||
|
||||
private void tmp_renderCollisionModel(EntityData entity) {
|
||||
if (RENDER_COLLISION_MODELS) {
|
||||
CollisionModelRenderer.renderCollisionModel(entity.getCollisionModel(), helper);
|
||||
}
|
||||
}
|
||||
|
||||
private void tmp_performCollisions(float tickLength) {
|
||||
tmp_collideableList.clear();
|
||||
tmp_collideableList.addAll(this.client.getWorld().getData().getEntities());
|
||||
|
||||
Collider.performCollisions(
|
||||
tmp_collideableList,
|
||||
new CollisionClock() {
|
||||
private float t = 0;
|
||||
@Override
|
||||
public float getTime() {
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advanceTime(float change) {
|
||||
t += change;
|
||||
}
|
||||
},
|
||||
(float) GraphicsInterface.getFrameLength(),
|
||||
this.client.getWorld().getData(),
|
||||
tickLength,
|
||||
tmp_colliderWorkspace
|
||||
);
|
||||
|
||||
final float frictionCoeff = 1 - 1e-2f;
|
||||
|
||||
for (EntityData data : this.client.getWorld().getData().getEntities()) {
|
||||
data.getVelocity().mul(frictionCoeff);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.exit(31337);
|
||||
|
||||
private void tmp_applyFriction(EntityData entity) {
|
||||
final float frictionCoeff = 1 - 1e-5f;
|
||||
entity.getVelocity().mul(frictionCoeff);
|
||||
}
|
||||
|
||||
private void tmp_applyGravity(EntityData entity, float tickLength) {
|
||||
if (ClientState.getInstance().getLocalPlayer() == entity && tmp_testControls.isFlying()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final float gravitationalAcceleration;
|
||||
|
||||
if (tmp_testControls.useMinecraftGravity()) {
|
||||
gravitationalAcceleration = 32f * Units.METERS_PER_SECOND_SQUARED; // plz dont sue me M$
|
||||
} else {
|
||||
gravitationalAcceleration = 9.81f * Units.METERS_PER_SECOND_SQUARED;
|
||||
}
|
||||
entity.getVelocity().add(0, 0, -gravitationalAcceleration * tickLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleInput(Input input) {
|
||||
if (input.isConsumed()) return;
|
||||
|
||||
InputEvent event = input.getEvent();
|
||||
|
||||
if (event instanceof KeyEvent) {
|
||||
if (onKeyEvent((KeyEvent) event)) {
|
||||
input.consume();
|
||||
}
|
||||
} else if (event instanceof CursorMoveEvent) {
|
||||
onMouseMoved((CursorMoveEvent) event);
|
||||
input.consume();
|
||||
}
|
||||
tmp_testControls.handleInput(input);
|
||||
|
||||
if (!input.isConsumed()) {
|
||||
inputBasedControls.handleInput(input);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean flag = true;
|
||||
|
||||
private boolean onKeyEvent(KeyEvent event) {
|
||||
if (event.isRepeat()) return false;
|
||||
|
||||
int multiplier = event.isPress() ? 1 : -1;
|
||||
|
||||
switch (event.getKey()) {
|
||||
case GLFW.GLFW_KEY_W:
|
||||
movementForward += +1 * multiplier;
|
||||
break;
|
||||
case GLFW.GLFW_KEY_S:
|
||||
movementForward += -1 * multiplier;
|
||||
break;
|
||||
case GLFW.GLFW_KEY_A:
|
||||
movementRight += -1 * multiplier;
|
||||
break;
|
||||
case GLFW.GLFW_KEY_D:
|
||||
movementRight += +1 * multiplier;
|
||||
break;
|
||||
case GLFW.GLFW_KEY_SPACE:
|
||||
movementUp += +1 * multiplier;
|
||||
break;
|
||||
case GLFW.GLFW_KEY_LEFT_SHIFT:
|
||||
movementUp += -1 * multiplier;
|
||||
break;
|
||||
|
||||
case GLFW.GLFW_KEY_ESCAPE:
|
||||
if (!event.isPress()) return false;
|
||||
|
||||
if (flag) {
|
||||
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL);
|
||||
} else {
|
||||
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
|
||||
}
|
||||
|
||||
flag = !flag;
|
||||
break;
|
||||
|
||||
case GLFW.GLFW_KEY_F5:
|
||||
if (!event.isPress()) return false;
|
||||
|
||||
if (client.getCamera().hasAnchor()) {
|
||||
client.getCamera().selectNextMode();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onMouseMoved(CursorMoveEvent event) {
|
||||
if (!flag) return;
|
||||
|
||||
final float yawScale = -0.002f;
|
||||
final float pitchScale = yawScale;
|
||||
|
||||
EntityData player = client.getLocalPlayer();
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,116 @@
|
||||
package ru.windcorp.progressia.client.world.entity;
|
||||
|
||||
import static java.lang.Math.*;
|
||||
import static ru.windcorp.progressia.common.util.FloatMathUtils.*;
|
||||
|
||||
import glm.mat._4.Mat4;
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
|
||||
public class HumanoidModel extends NPedModel {
|
||||
|
||||
protected static abstract class Limb extends BodyPart {
|
||||
private final float animationOffset;
|
||||
|
||||
public Limb(
|
||||
Renderable renderable, Vec3 joint,
|
||||
float animationOffset
|
||||
) {
|
||||
super(renderable, joint);
|
||||
this.animationOffset = animationOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyTransform(Mat4 mat, NPedModel model) {
|
||||
float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset;
|
||||
float value = sin(phase);
|
||||
float amplitude = getSwingAmplitude((HumanoidModel) model) * model.getVelocityParameter();
|
||||
mat.rotateY(value * amplitude);
|
||||
}
|
||||
|
||||
protected abstract float getSwingAmplitude(HumanoidModel model);
|
||||
|
||||
}
|
||||
|
||||
public static class Leg extends Limb {
|
||||
public Leg(
|
||||
Renderable renderable, Vec3 joint,
|
||||
float animationOffset
|
||||
) {
|
||||
super(renderable, joint, animationOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getSwingAmplitude(HumanoidModel model) {
|
||||
return model.walkingLegSwing;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Arm extends Limb {
|
||||
public Arm(
|
||||
Renderable renderable, Vec3 joint,
|
||||
float animationOffset
|
||||
) {
|
||||
super(renderable, joint, animationOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getSwingAmplitude(HumanoidModel model) {
|
||||
return model.walkingArmSwing;
|
||||
}
|
||||
}
|
||||
|
||||
private final Arm leftArm;
|
||||
private final Arm rightArm;
|
||||
private final Leg leftLeg;
|
||||
private final Leg rightLeg;
|
||||
|
||||
private float walkingLegSwing;
|
||||
private float walkingArmSwing;
|
||||
|
||||
public HumanoidModel(
|
||||
EntityData entity,
|
||||
|
||||
Body body, Head head,
|
||||
Arm leftArm, Arm rightArm,
|
||||
Leg leftLeg, Leg rightLeg,
|
||||
|
||||
float scale
|
||||
) {
|
||||
super(entity, body, head, scale);
|
||||
this.leftArm = leftArm;
|
||||
this.rightArm = rightArm;
|
||||
this.leftLeg = leftLeg;
|
||||
this.rightLeg = rightLeg;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBodyParts(ShapeRenderHelper renderer) {
|
||||
super.renderBodyParts(renderer);
|
||||
leftArm.render(renderer, this);
|
||||
rightArm.render(renderer, this);
|
||||
leftLeg.render(renderer, this);
|
||||
rightLeg.render(renderer, this);
|
||||
}
|
||||
|
||||
public float getWalkingArmSwing() {
|
||||
return walkingArmSwing;
|
||||
}
|
||||
|
||||
public float getWalkingLegSwing() {
|
||||
return walkingLegSwing;
|
||||
}
|
||||
|
||||
public HumanoidModel setWalkingLegSwing(float walkingLegSwing) {
|
||||
this.walkingLegSwing = walkingLegSwing;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HumanoidModel setWalkingArmSwing(float walkingArmSwing) {
|
||||
this.walkingArmSwing = walkingArmSwing;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,284 @@
|
||||
package ru.windcorp.progressia.client.world.entity;
|
||||
|
||||
import static java.lang.Math.atan2;
|
||||
import static java.lang.Math.min;
|
||||
import static java.lang.Math.pow;
|
||||
import static java.lang.Math.toRadians;
|
||||
import static ru.windcorp.progressia.common.util.FloatMathUtils.normalizeAngle;
|
||||
|
||||
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.Units;
|
||||
import ru.windcorp.progressia.common.util.Matrices;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
|
||||
public abstract class NPedModel extends EntityRenderable {
|
||||
|
||||
protected static abstract class BodyPart {
|
||||
private final Renderable renderable;
|
||||
private final Vec3 translation = new Vec3();
|
||||
|
||||
public BodyPart(Renderable renderable, Vec3 joint) {
|
||||
this.renderable = renderable;
|
||||
if (joint != null) {
|
||||
this.translation.set(joint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void render(
|
||||
ShapeRenderHelper renderer, NPedModel model
|
||||
) {
|
||||
renderer.pushTransform().translate(translation);
|
||||
applyTransform(renderer.pushTransform(), model);
|
||||
renderable.render(renderer);
|
||||
renderer.popTransform();
|
||||
renderer.popTransform();
|
||||
}
|
||||
|
||||
protected abstract void applyTransform(Mat4 mat, NPedModel model);
|
||||
|
||||
public Vec3 getTranslation() {
|
||||
return translation;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Body extends BodyPart {
|
||||
public Body(Renderable renderable) {
|
||||
super(renderable, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyTransform(Mat4 mat, NPedModel model) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
public static class Head extends BodyPart {
|
||||
private final float maxYaw;
|
||||
private final float maxPitch;
|
||||
|
||||
private final Vec3 viewPoint;
|
||||
|
||||
public Head(
|
||||
Renderable renderable, Vec3 joint,
|
||||
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, NPedModel model) {
|
||||
mat.rotateZ(model.getHeadYaw()).rotateY(model.getHeadPitch());
|
||||
}
|
||||
|
||||
public Vec3 getViewPoint() {
|
||||
return viewPoint;
|
||||
}
|
||||
}
|
||||
|
||||
protected final Body body;
|
||||
protected final Head head;
|
||||
|
||||
private float walkingParameter = 0;
|
||||
private float velocityParameter = 0;
|
||||
private float velocity = 0;
|
||||
|
||||
/**
|
||||
* If {@link #velocity} is greater than this value, {@link #velocityParameter} is 1.0.
|
||||
*/
|
||||
private float maxEffectiveVelocity = 5 * Units.METERS_PER_SECOND;
|
||||
|
||||
/**
|
||||
* If {@link #velocity} is less than {@link #maxEffectiveVelocity}, then
|
||||
* {@code velocityCoeff = exp(velocity / maxEffectiveVelocity, velocityCoeffPower)}.
|
||||
*/
|
||||
private float velocityCoeffPower = 1;
|
||||
|
||||
private final float scale;
|
||||
|
||||
private float walkingFrequency;
|
||||
|
||||
private float bodyYaw = Float.NaN;
|
||||
private float headYaw;
|
||||
private float headPitch;
|
||||
|
||||
public NPedModel(EntityData data, Body body, Head head, float scale) {
|
||||
super(data);
|
||||
this.body = body;
|
||||
this.head = head;
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(ShapeRenderHelper renderer) {
|
||||
renderer.pushTransform().scale(scale).rotateZ(bodyYaw);
|
||||
renderBodyParts(renderer);
|
||||
renderer.popTransform();
|
||||
|
||||
accountForVelocity();
|
||||
evaluateAngles();
|
||||
}
|
||||
|
||||
protected void renderBodyParts(ShapeRenderHelper renderer) {
|
||||
body.render(renderer, this);
|
||||
head.render(renderer, this);
|
||||
}
|
||||
|
||||
private void evaluateAngles() {
|
||||
float globalYaw = normalizeAngle(getData().getYaw());
|
||||
|
||||
if (Float.isNaN(bodyYaw)) {
|
||||
bodyYaw = globalYaw;
|
||||
headYaw = 0;
|
||||
} else {
|
||||
headYaw = normalizeAngle(globalYaw - bodyYaw);
|
||||
|
||||
if (headYaw > +head.maxYaw) {
|
||||
bodyYaw += headYaw - +head.maxYaw;
|
||||
headYaw = +head.maxYaw;
|
||||
} else if (headYaw < -head.maxYaw) {
|
||||
bodyYaw += headYaw - -head.maxYaw;
|
||||
headYaw = -head.maxYaw;
|
||||
}
|
||||
}
|
||||
|
||||
bodyYaw = normalizeAngle(bodyYaw);
|
||||
|
||||
headPitch = Glm.clamp(
|
||||
getData().getPitch(),
|
||||
-head.maxPitch, head.maxPitch
|
||||
);
|
||||
}
|
||||
|
||||
private void accountForVelocity() {
|
||||
Vec3 horizontal = Vectors.grab3();
|
||||
horizontal.set(getData().getVelocity());
|
||||
horizontal.z = 0;
|
||||
|
||||
velocity = horizontal.length();
|
||||
|
||||
evaluateVelocityCoeff();
|
||||
|
||||
// TODO switch to world time
|
||||
walkingParameter += velocity * GraphicsInterface.getFrameLength() * 1000;
|
||||
|
||||
bodyYaw += velocityParameter * normalizeAngle(
|
||||
(float) (atan2(horizontal.y, horizontal.x) - bodyYaw)
|
||||
) * min(1, GraphicsInterface.getFrameLength() * 10);
|
||||
Vectors.release(horizontal);
|
||||
}
|
||||
|
||||
private void evaluateVelocityCoeff() {
|
||||
if (velocity > maxEffectiveVelocity) {
|
||||
velocityParameter = 1;
|
||||
} else {
|
||||
velocityParameter = (float) pow(velocity / maxEffectiveVelocity, velocityCoeffPower);
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
public Body getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public Head getHead() {
|
||||
return head;
|
||||
}
|
||||
|
||||
public float getBodyYaw() {
|
||||
return bodyYaw;
|
||||
}
|
||||
|
||||
public float getHeadYaw() {
|
||||
return headYaw;
|
||||
}
|
||||
|
||||
public float getHeadPitch() {
|
||||
return headPitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a number in the range [0; 1] that can be used to scale animation effects that depend on speed.
|
||||
* This parameter is 0 when the entity is not moving and 1 when it's moving "fast".
|
||||
* @return velocity parameter
|
||||
*/
|
||||
protected float getVelocityParameter() {
|
||||
return velocityParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a number that can be used to parameterize animation effects that depend on walking.
|
||||
* This parameter increases when the entity moves (e.g. this can be total traveled distance).
|
||||
* @return walking parameter
|
||||
*/
|
||||
protected float getWalkingParameter() {
|
||||
return walkingParameter;
|
||||
}
|
||||
|
||||
protected float getVelocity() {
|
||||
return velocity;
|
||||
}
|
||||
|
||||
public float getScale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
protected float getWalkingFrequency() {
|
||||
return walkingFrequency;
|
||||
}
|
||||
|
||||
public NPedModel setWalkingFrequency(float walkingFrequency) {
|
||||
this.walkingFrequency = walkingFrequency;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getMaxEffectiveVelocity() {
|
||||
return maxEffectiveVelocity;
|
||||
}
|
||||
|
||||
public float getVelocityCoeffPower() {
|
||||
return velocityCoeffPower;
|
||||
}
|
||||
|
||||
public NPedModel setMaxEffectiveVelocity(float maxEffectiveVelocity) {
|
||||
this.maxEffectiveVelocity = maxEffectiveVelocity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NPedModel setVelocityCoeffPower(float velocityCoeffPower) {
|
||||
this.velocityCoeffPower = velocityCoeffPower;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -2,86 +2,15 @@ package ru.windcorp.progressia.client.world.entity;
|
||||
|
||||
import static java.lang.Math.*;
|
||||
import static ru.windcorp.progressia.common.util.FloatMathUtils.*;
|
||||
import static ru.windcorp.progressia.common.util.FloatMathUtils.sin;
|
||||
|
||||
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 extends EntityRenderable {
|
||||
|
||||
private static abstract class BodyPart {
|
||||
private final Renderable renderable;
|
||||
private final Vec3 translation = new Vec3();
|
||||
|
||||
public BodyPart(Renderable renderable, Vec3 joint) {
|
||||
this.renderable = renderable;
|
||||
if (joint != null) {
|
||||
this.translation.set(joint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void render(
|
||||
ShapeRenderHelper renderer, QuadripedModel model
|
||||
) {
|
||||
renderer.pushTransform().translate(translation);
|
||||
applyTransform(renderer.pushTransform(), model);
|
||||
renderable.render(renderer);
|
||||
renderer.popTransform();
|
||||
renderer.popTransform();
|
||||
}
|
||||
|
||||
protected abstract void applyTransform(Mat4 mat, QuadripedModel model);
|
||||
|
||||
public Vec3 getTranslation() {
|
||||
return translation;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Body extends BodyPart {
|
||||
public Body(Renderable renderable) {
|
||||
super(renderable, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyTransform(Mat4 mat, QuadripedModel model) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
public static class Head extends BodyPart {
|
||||
private final float maxYaw;
|
||||
private final float maxPitch;
|
||||
|
||||
private final Vec3 viewPoint;
|
||||
|
||||
public Head(
|
||||
Renderable renderable, Vec3 joint,
|
||||
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 class QuadripedModel extends NPedModel {
|
||||
|
||||
public static class Leg extends BodyPart {
|
||||
private final float animationOffset;
|
||||
@ -95,34 +24,20 @@ public class QuadripedModel extends EntityRenderable {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyTransform(Mat4 mat, QuadripedModel model) {
|
||||
mat.rotateY(sin(model.walkingFrequency * model.walkingAnimationParameter + animationOffset) * model.walkingSwing * model.velocityCoeff);
|
||||
}
|
||||
protected void applyTransform(Mat4 mat, NPedModel model) {
|
||||
float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset;
|
||||
float value = sin(phase);
|
||||
float amplitude = ((QuadripedModel) model).getWalkingSwing() * model.getVelocityParameter();
|
||||
mat.rotateY(value * amplitude);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Body body;
|
||||
private final Head head;
|
||||
private final Leg leftForeLeg, rightForeLeg;
|
||||
private final Leg leftHindLeg, rightHindLeg;
|
||||
|
||||
private final float scale;
|
||||
|
||||
private float walkingAnimationParameter = 0;
|
||||
private float velocityCoeff = 0;
|
||||
private float velocity = 0;
|
||||
|
||||
/**
|
||||
* Controls how quickly velocityCoeff approaches 1
|
||||
*/
|
||||
private float velocityCutoff = 10;
|
||||
|
||||
private float walkingFrequency = 0.15f / 60.0f;
|
||||
private float walkingSwing = (float) toRadians(30);
|
||||
|
||||
private float bodyYaw = Float.NaN;
|
||||
private float headYaw;
|
||||
private float headPitch;
|
||||
|
||||
public QuadripedModel(
|
||||
EntityData entity,
|
||||
|
||||
@ -132,105 +47,30 @@ public class QuadripedModel extends EntityRenderable {
|
||||
|
||||
float scale
|
||||
) {
|
||||
super(entity);
|
||||
super(entity, body, head, scale);
|
||||
|
||||
this.body = body;
|
||||
this.head = head;
|
||||
this.leftForeLeg = leftForeLeg;
|
||||
this.rightForeLeg = rightForeLeg;
|
||||
this.leftHindLeg = leftHindLeg;
|
||||
this.rightHindLeg = rightHindLeg;
|
||||
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(ShapeRenderHelper renderer) {
|
||||
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();
|
||||
protected void renderBodyParts(ShapeRenderHelper renderer) {
|
||||
super.renderBodyParts(renderer);
|
||||
this.leftForeLeg.render(renderer, this);
|
||||
this.rightForeLeg.render(renderer, this);
|
||||
this.leftHindLeg.render(renderer, this);
|
||||
this.rightHindLeg.render(renderer, this);
|
||||
}
|
||||
|
||||
private void evaluateAngles() {
|
||||
float globalYaw = normalizeAngle(getData().getYaw());
|
||||
|
||||
if (Float.isNaN(bodyYaw)) {
|
||||
bodyYaw = globalYaw;
|
||||
headYaw = 0;
|
||||
} else {
|
||||
headYaw = normalizeAngle(globalYaw - bodyYaw);
|
||||
|
||||
if (headYaw > +head.maxYaw) {
|
||||
bodyYaw += headYaw - +head.maxYaw;
|
||||
headYaw = +head.maxYaw;
|
||||
} else if (headYaw < -head.maxYaw) {
|
||||
bodyYaw += headYaw - -head.maxYaw;
|
||||
headYaw = -head.maxYaw;
|
||||
}
|
||||
public float getWalkingSwing() {
|
||||
return walkingSwing;
|
||||
}
|
||||
|
||||
bodyYaw = normalizeAngle(bodyYaw);
|
||||
|
||||
headPitch = Glm.clamp(
|
||||
getData().getPitch(),
|
||||
-head.maxPitch, head.maxPitch
|
||||
);
|
||||
}
|
||||
|
||||
private void accountForVelocity() {
|
||||
Vec3 horizontal = Vectors.grab3();
|
||||
horizontal.set(getData().getVelocity());
|
||||
horizontal.z = 0;
|
||||
|
||||
velocity = horizontal.length();
|
||||
|
||||
evaluateVelocityCoeff();
|
||||
|
||||
// TODO switch to world time
|
||||
walkingAnimationParameter += velocity * GraphicsInterface.getFrameLength() * 1000;
|
||||
|
||||
bodyYaw += velocityCoeff * normalizeAngle(
|
||||
(float) (atan2(horizontal.y, horizontal.x) - bodyYaw)
|
||||
) * min(1, GraphicsInterface.getFrameLength() * 10);
|
||||
Vectors.release(horizontal);
|
||||
}
|
||||
|
||||
private void evaluateVelocityCoeff() {
|
||||
if (velocity * velocityCutoff > 1) {
|
||||
velocityCoeff = 1;
|
||||
} else {
|
||||
velocityCoeff = velocity * velocityCutoff;
|
||||
velocityCoeff *= velocityCoeff;
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
public QuadripedModel setWalkingSwing(float walkingSwing) {
|
||||
this.walkingSwing = walkingSwing;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
56
src/main/java/ru/windcorp/progressia/common/Units.java
Normal file
56
src/main/java/ru/windcorp/progressia/common/Units.java
Normal file
@ -0,0 +1,56 @@
|
||||
package ru.windcorp.progressia.common;
|
||||
|
||||
public class Units {
|
||||
|
||||
// Base units
|
||||
// We're SI.
|
||||
public static final float METERS = 1;
|
||||
public static final float KILOGRAMS = 1;
|
||||
public static final float SECONDS = 1;
|
||||
|
||||
// Length
|
||||
public static final float CENTIMETERS = METERS / 100;
|
||||
public static final float MILLIMETERS = METERS / 1000;
|
||||
public static final float KILOMETERS = METERS * 1000;
|
||||
|
||||
// Surface
|
||||
public static final float SQUARE_CENTIMETERS = CENTIMETERS * CENTIMETERS;
|
||||
public static final float SQUARE_METERS = METERS * METERS;
|
||||
public static final float SQUARE_MILLIMETERS = MILLIMETERS * MILLIMETERS;
|
||||
public static final float SQUARE_KILOMETERS = KILOMETERS * KILOMETERS;
|
||||
|
||||
// Volume
|
||||
public static final float CUBIC_CENTIMETERS = CENTIMETERS * CENTIMETERS * CENTIMETERS;
|
||||
public static final float CUBIC_METERS = METERS * METERS * METERS;
|
||||
public static final float CUBIC_MILLIMETERS = MILLIMETERS * MILLIMETERS * MILLIMETERS;
|
||||
public static final float CUBIC_KILOMETERS = KILOMETERS * KILOMETERS * KILOMETERS;
|
||||
|
||||
// Mass
|
||||
public static final float GRAMS = KILOGRAMS / 1000;
|
||||
public static final float TONNES = KILOGRAMS * 1000;
|
||||
|
||||
// Density
|
||||
public static final float KILOGRAMS_PER_CUBIC_METER = KILOGRAMS / CUBIC_METERS;
|
||||
public static final float GRAMS_PER_CUBIC_CENTIMETER = GRAMS / CUBIC_CENTIMETERS;
|
||||
|
||||
// Time
|
||||
public static final float MILLISECONDS = SECONDS / 1000;
|
||||
public static final float MINUTES = SECONDS * 60;
|
||||
public static final float HOURS = MINUTES * 60;
|
||||
public static final float DAYS = HOURS * 24;
|
||||
|
||||
// Frequency
|
||||
public static final float HERTZ = 1 / SECONDS;
|
||||
public static final float KILOHERTZ = HERTZ * 1000;
|
||||
|
||||
// Velocity
|
||||
public static final float METERS_PER_SECOND = METERS / SECONDS;
|
||||
public static final float KILOMETERS_PER_HOUR = KILOMETERS / HOURS;
|
||||
|
||||
// Acceleration
|
||||
public static final float METERS_PER_SECOND_SQUARED = METERS_PER_SECOND / SECONDS;
|
||||
|
||||
// Force
|
||||
public static final float NEWTONS = METERS_PER_SECOND_SQUARED * KILOGRAMS;
|
||||
|
||||
}
|
@ -1,35 +1,63 @@
|
||||
package ru.windcorp.progressia.common.collision;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
public class AABB implements CollisionModel {
|
||||
/**
|
||||
* An implementation of an
|
||||
* <a href="https://en.wikipedia.org/wiki/Minimum_bounding_box#Axis-aligned_minimum_bounding_box">Axis-Aligned Bounding Box</a>.
|
||||
* @author javapony
|
||||
*/
|
||||
public class AABB implements AABBoid {
|
||||
|
||||
private final Map<BlockFace, CollisionWall> faces = BlockFace.mapToFaces(
|
||||
new CollisionWall(-0.5f, -0.5f, -0.5f, +1, 0, 0, 0, 0, +1),
|
||||
new CollisionWall(+0.5f, -0.5f, -0.5f, 0, +1, 0, 0, 0, +1),
|
||||
new CollisionWall(+0.5f, +0.5f, -0.5f, -1, 0, 0, 0, 0, +1),
|
||||
new CollisionWall(-0.5f, +0.5f, -0.5f, 0, -1, 0, 0, 0, +1),
|
||||
private class AABBWallImpl implements Wall {
|
||||
|
||||
new CollisionWall(-0.5f, -0.5f, +0.5f, +1, 0, 0, 0, +1, 0),
|
||||
new CollisionWall(-0.5f, -0.5f, -0.5f, 0, +1, 0, +1, 0, 0)
|
||||
);
|
||||
private final Vec3 originOffset = new Vec3();
|
||||
private final Vec3 widthSelector = new Vec3();
|
||||
private final Vec3 heightSelector = new Vec3();
|
||||
|
||||
public AABBWallImpl(
|
||||
float ox, float oy, float oz,
|
||||
float wx, float wy, float wz,
|
||||
float hx, float hy, float hz
|
||||
) {
|
||||
this.originOffset.set(ox, oy, oz);
|
||||
this.widthSelector.set(wx, wy, wz);
|
||||
this.heightSelector.set(hx, hy, hz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOrigin(Vec3 output) {
|
||||
output.set(originOffset).mul(AABB.this.getSize()).add(AABB.this.getOrigin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getWidth(Vec3 output) {
|
||||
output.set(AABB.this.getSize()).mul(widthSelector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getHeight(Vec3 output) {
|
||||
output.set(AABB.this.getSize()).mul(heightSelector);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final AABB UNIT_CUBE = new AABB(0, 0, 0, 1, 1, 1);
|
||||
|
||||
private final Wall[] walls = new Wall[] {
|
||||
new AABBWallImpl(-0.5f, -0.5f, +0.5f, +1, 0, 0, 0, +1, 0), // Top
|
||||
new AABBWallImpl(-0.5f, -0.5f, -0.5f, 0, +1, 0, +1, 0, 0), // Bottom
|
||||
new AABBWallImpl(+0.5f, -0.5f, -0.5f, 0, +1, 0, 0, 0, +1), // North
|
||||
new AABBWallImpl(-0.5f, +0.5f, -0.5f, 0, -1, 0, 0, 0, +1), // South
|
||||
new AABBWallImpl(+0.5f, +0.5f, -0.5f, -1, 0, 0, 0, 0, +1), // West
|
||||
new AABBWallImpl(-0.5f, -0.5f, -0.5f, +1, 0, 0, 0, 0, +1) // East
|
||||
};
|
||||
|
||||
private final Vec3 origin = new Vec3();
|
||||
private final Vec3 size = new Vec3();
|
||||
|
||||
public AABB(Vec3 origin, Vec3 size) {
|
||||
this.origin.set(origin);
|
||||
this.size.set(size);
|
||||
|
||||
for (CollisionWall wall : getFaces()) {
|
||||
wall.moveOrigin(origin);
|
||||
wall.getWidth().mul(size);
|
||||
wall.getHeight().mul(size);
|
||||
}
|
||||
this(origin.x, origin.y, origin.z, size.x, size.y, size.z);
|
||||
}
|
||||
|
||||
public AABB(
|
||||
@ -38,16 +66,6 @@ public class AABB implements CollisionModel {
|
||||
) {
|
||||
this.origin.set(ox, oy, oz);
|
||||
this.size.set(xSize, ySize, zSize);
|
||||
|
||||
for (CollisionWall wall : getFaces()) {
|
||||
wall.moveOrigin(ox, oy, oz);
|
||||
wall.getWidth().mul(xSize, ySize, zSize);
|
||||
wall.getHeight().mul(xSize, ySize, zSize);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<CollisionWall> getFaces() {
|
||||
return faces.values();
|
||||
}
|
||||
|
||||
public Vec3 getOrigin() {
|
||||
@ -55,20 +73,17 @@ public class AABB implements CollisionModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrigin(Vec3 origin) {
|
||||
for (CollisionWall wall : getFaces()) {
|
||||
wall.getOrigin().sub(this.origin).add(origin);
|
||||
public void getOrigin(Vec3 output) {
|
||||
output.set(origin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrigin(Vec3 origin) {
|
||||
this.origin.set(origin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveOrigin(Vec3 displacement) {
|
||||
for (CollisionWall wall : getFaces()) {
|
||||
wall.getOrigin().add(displacement);
|
||||
}
|
||||
|
||||
this.origin.add(displacement);
|
||||
}
|
||||
|
||||
@ -76,18 +91,23 @@ public class AABB implements CollisionModel {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSize(Vec3 output) {
|
||||
output.set(size);
|
||||
}
|
||||
|
||||
public void setSize(Vec3 size) {
|
||||
setSize(size.x, size.y, size.z);
|
||||
}
|
||||
|
||||
public void setSize(float xSize, float ySize, float zSize) {
|
||||
for (CollisionWall wall : getFaces()) {
|
||||
wall.getWidth().div(this.size).mul(xSize, ySize, zSize);
|
||||
wall.getHeight().div(this.size).mul(xSize, ySize, zSize);
|
||||
wall.getOrigin().sub(getOrigin()).div(this.size).mul(xSize, ySize, zSize).add(getOrigin());
|
||||
}
|
||||
|
||||
this.size.set(xSize, ySize, zSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wall getWall(int faceId) {
|
||||
// No, we don't support Apple.
|
||||
return walls[faceId];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
package ru.windcorp.progressia.common.collision;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
public interface AABBoid extends CollisionModel {
|
||||
|
||||
void getOrigin(Vec3 output);
|
||||
void getSize(Vec3 output);
|
||||
|
||||
default Wall getWall(BlockFace face) {
|
||||
return getWall(face.getId());
|
||||
}
|
||||
|
||||
Wall getWall(int faceId);
|
||||
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package ru.windcorp.progressia.common.collision;
|
||||
|
||||
public interface CollisionClock {
|
||||
|
||||
float getTime();
|
||||
void advanceTime(float change);
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package ru.windcorp.progressia.common.collision;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
|
||||
import static java.lang.Math.*;
|
||||
|
||||
public class CollisionPathComputer {
|
||||
|
||||
public static void forEveryBlockInCollisionPath(
|
||||
Collideable coll,
|
||||
float maxTime,
|
||||
Consumer<Vec3i> action
|
||||
) {
|
||||
Vec3 displacement = Vectors.grab3();
|
||||
coll.getCollideableVelocity(displacement);
|
||||
displacement.mul(maxTime);
|
||||
|
||||
handleModel(coll.getCollisionModel(), displacement, action);
|
||||
|
||||
Vectors.release(displacement);
|
||||
}
|
||||
|
||||
private static void handleModel(
|
||||
CollisionModel model,
|
||||
Vec3 displacement,
|
||||
Consumer<Vec3i> action
|
||||
) {
|
||||
if (model instanceof CompoundCollisionModel) {
|
||||
for (CollisionModel subModel : ((CompoundCollisionModel) model).getModels()) {
|
||||
handleModel(subModel, displacement, action);
|
||||
}
|
||||
} else if (model instanceof AABBoid) {
|
||||
handleAABBoid((AABBoid) model, displacement, action);
|
||||
} else {
|
||||
throw new RuntimeException("not supported");
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleAABBoid(AABBoid model, Vec3 displacement, Consumer<Vec3i> action) {
|
||||
Vec3 size = Vectors.grab3();
|
||||
Vec3 origin = Vectors.grab3();
|
||||
|
||||
model.getOrigin(origin);
|
||||
model.getSize(size);
|
||||
|
||||
origin.mul(2).sub(size).div(2); // Subtract 0.5*size
|
||||
|
||||
Vec3i pos = Vectors.grab3i();
|
||||
|
||||
for (
|
||||
pos.x = (int) floor(origin.x + min(0, size.x) + min(0, displacement.x));
|
||||
pos.x <= (int) ceil(origin.x + max(0, size.x) + max(0, displacement.x));
|
||||
pos.x += 1
|
||||
) {
|
||||
for (
|
||||
pos.y = (int) floor(origin.y + min(0, size.y) + min(0, displacement.y));
|
||||
pos.y <= (int) ceil(origin.y + max(0, size.y) + max(0, displacement.y));
|
||||
pos.y += 1
|
||||
) {
|
||||
for (
|
||||
pos.z = (int) floor(origin.z + min(0, size.z) + min(0, displacement.z));
|
||||
pos.z <= (int) ceil(origin.z + max(0, size.z) + max(0, displacement.z));
|
||||
pos.z += 1
|
||||
) {
|
||||
action.accept(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vectors.release(origin);
|
||||
Vectors.release(size);
|
||||
Vectors.release(pos);
|
||||
}
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
package ru.windcorp.progressia.common.collision;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
|
||||
public class CollisionWall {
|
||||
|
||||
private final Vec3 origin = new Vec3();
|
||||
private final Vec3 width = new Vec3();
|
||||
private final Vec3 height = new Vec3();
|
||||
|
||||
public CollisionWall(Vec3 origin, Vec3 width, Vec3 height) {
|
||||
this.origin.set(origin);
|
||||
this.width.set(width);
|
||||
this.height.set(height);
|
||||
}
|
||||
|
||||
public CollisionWall(
|
||||
float ox, float oy, float oz,
|
||||
float wx, float wy, float wz,
|
||||
float hx, float hy, float hz
|
||||
) {
|
||||
this.origin.set(ox, oy, oz);
|
||||
this.width.set(wx, wy, wz);
|
||||
this.height.set(hx, hy, hz);
|
||||
}
|
||||
|
||||
public Vec3 getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public Vec3 getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public Vec3 getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setOrigin(Vec3 origin) {
|
||||
setOrigin(origin.x, origin.y, origin.z);
|
||||
}
|
||||
|
||||
public void setOrigin(float x, float y, float z) {
|
||||
this.origin.set(x, y, z);
|
||||
}
|
||||
|
||||
public void moveOrigin(Vec3 displacement) {
|
||||
moveOrigin(displacement.x, displacement.y, displacement.z);
|
||||
}
|
||||
|
||||
public void moveOrigin(float dx, float dy, float dz) {
|
||||
this.origin.add(dx, dy, dz);
|
||||
}
|
||||
|
||||
}
|
@ -8,9 +8,9 @@ import glm.vec._3.Vec3;
|
||||
|
||||
public class CompoundCollisionModel implements CollisionModel {
|
||||
|
||||
private final Collection<CollisionModel> models;
|
||||
private final Collection<? extends CollisionModel> models;
|
||||
|
||||
public CompoundCollisionModel(Collection<CollisionModel> models) {
|
||||
public CompoundCollisionModel(Collection<? extends CollisionModel> models) {
|
||||
this.models = models;
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ public class CompoundCollisionModel implements CollisionModel {
|
||||
this(ImmutableList.copyOf(models));
|
||||
}
|
||||
|
||||
public Collection<CollisionModel> getModels() {
|
||||
public Collection<? extends CollisionModel> getModels() {
|
||||
return models;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,105 @@
|
||||
package ru.windcorp.progressia.common.collision;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
public class TranslatedAABB implements AABBoid {
|
||||
|
||||
private class TranslatedAABBWall implements Wall {
|
||||
private final int id;
|
||||
|
||||
public TranslatedAABBWall(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOrigin(Vec3 output) {
|
||||
parent.getWall(id).getOrigin(output);
|
||||
output.add(translation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getWidth(Vec3 output) {
|
||||
parent.getWall(id).getWidth(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getHeight(Vec3 output) {
|
||||
parent.getWall(id).getHeight(output);
|
||||
}
|
||||
}
|
||||
|
||||
private AABBoid parent;
|
||||
private final Vec3 translation = new Vec3();
|
||||
|
||||
private final TranslatedAABBWall[] walls = new TranslatedAABBWall[BlockFace.BLOCK_FACE_COUNT];
|
||||
|
||||
{
|
||||
for (int id = 0; id < walls.length; ++id) {
|
||||
walls[id] = new TranslatedAABBWall(id);
|
||||
}
|
||||
}
|
||||
|
||||
public TranslatedAABB(AABBoid parent, float tx, float ty, float tz) {
|
||||
setParent(parent);
|
||||
setTranslation(tx, ty, tz);
|
||||
}
|
||||
|
||||
public TranslatedAABB(AABBoid parent, Vec3 translation) {
|
||||
this(parent, translation.x, translation.y, translation.z);
|
||||
}
|
||||
|
||||
public TranslatedAABB() {
|
||||
this(null, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrigin(Vec3 origin) {
|
||||
Vec3 v = Vectors.grab3().set(origin).sub(translation);
|
||||
parent.setOrigin(v);
|
||||
Vectors.release(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveOrigin(Vec3 displacement) {
|
||||
parent.moveOrigin(displacement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOrigin(Vec3 output) {
|
||||
parent.getOrigin(output);
|
||||
output.add(translation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSize(Vec3 output) {
|
||||
parent.getSize(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wall getWall(int faceId) {
|
||||
return walls[faceId];
|
||||
}
|
||||
|
||||
public AABBoid getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(AABBoid parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Vec3 getTranslation() {
|
||||
return translation;
|
||||
}
|
||||
|
||||
public void setTranslation(Vec3 translation) {
|
||||
setTranslation(translation.x, translation.y, translation.z);
|
||||
}
|
||||
|
||||
public void setTranslation(float tx, float ty, float tz) {
|
||||
this.translation.set(tx, ty, tz);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package ru.windcorp.progressia.common.collision;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
|
||||
public interface Wall {
|
||||
|
||||
void getOrigin(Vec3 output);
|
||||
|
||||
void getWidth(Vec3 output);
|
||||
void getHeight(Vec3 output);
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package ru.windcorp.progressia.common.collision;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.util.LowOverheadCache;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
|
||||
public class WorldCollisionHelper {
|
||||
|
||||
private final Collideable collideable = new Collideable() {
|
||||
@Override
|
||||
public boolean onCollision(Collideable other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAsCollideable(Vec3 displacement) {
|
||||
// Ignore
|
||||
assert displacement.length() < 1e-3f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollisionModel getCollisionModel() {
|
||||
return WorldCollisionHelper.this.model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCollisionMass() {
|
||||
return Float.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getCollideableVelocity(Vec3 output) {
|
||||
output.set(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeVelocityOnCollision(Vec3 velocityChange) {
|
||||
// Ignore
|
||||
assert velocityChange.length() < 1e-3f;
|
||||
}
|
||||
};
|
||||
|
||||
private final Collection<TranslatedAABB> activeBlockModels = new ArrayList<>();
|
||||
private final CollisionModel model = new CompoundCollisionModel(activeBlockModels);
|
||||
private final LowOverheadCache<TranslatedAABB> blockModelCache = new LowOverheadCache<>(TranslatedAABB::new);
|
||||
|
||||
/**
|
||||
* Changes the state of this helper's {@link #getCollideable()} so it is ready to adequately handle
|
||||
* collisions with the {@code collideable} that might happen in the next {@code maxTime} seconds.
|
||||
* This helper is only valid for checking collisions with the given Collideable and only within
|
||||
* the given time limit.
|
||||
* @param collideable the {@link Collideable} that collisions will be checked against
|
||||
* @param maxTime maximum collision time
|
||||
*/
|
||||
public void tuneToCollideable(WorldData world, Collideable collideable, float maxTime) {
|
||||
activeBlockModels.forEach(blockModelCache::release);
|
||||
activeBlockModels.clear();
|
||||
CollisionPathComputer.forEveryBlockInCollisionPath(
|
||||
collideable,
|
||||
maxTime,
|
||||
v -> addModel(world.getCollisionModelOfBlock(v), v)
|
||||
);
|
||||
}
|
||||
|
||||
private void addModel(CollisionModel model, Vec3i pos) {
|
||||
if (model == null) {
|
||||
// Ignore
|
||||
} else if (model instanceof AABBoid) {
|
||||
addAABBoidModel((AABBoid) model, pos);
|
||||
} else if (model instanceof CompoundCollisionModel) {
|
||||
for (CollisionModel subModel : ((CompoundCollisionModel) model).getModels()) {
|
||||
addModel(subModel, pos);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("not supported");
|
||||
}
|
||||
}
|
||||
|
||||
private void addAABBoidModel(AABBoid model, Vec3i pos) {
|
||||
TranslatedAABB translator = blockModelCache.grab();
|
||||
translator.setParent(model);
|
||||
translator.setTranslation(pos.x, pos.y, pos.z);
|
||||
activeBlockModels.add(translator);
|
||||
}
|
||||
|
||||
public Collideable getCollideable() {
|
||||
return collideable;
|
||||
}
|
||||
|
||||
}
|
@ -2,26 +2,25 @@ package ru.windcorp.progressia.common.collision.colliders;
|
||||
|
||||
import glm.mat._3.Mat3;
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.common.collision.AABB;
|
||||
import ru.windcorp.progressia.common.collision.Collideable;
|
||||
import ru.windcorp.progressia.common.collision.CollisionWall;
|
||||
import ru.windcorp.progressia.common.collision.*;
|
||||
import ru.windcorp.progressia.common.collision.colliders.Collider.ColliderWorkspace;
|
||||
import ru.windcorp.progressia.common.collision.colliders.Collider.Collision;
|
||||
import ru.windcorp.progressia.common.util.Matrices;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
|
||||
class AABBWithAABBCollider {
|
||||
class AABBoidCollider {
|
||||
|
||||
static Collider.Collision computeModelCollision(
|
||||
Collideable aBody, Collideable bBody,
|
||||
AABB aModel, AABB bModel,
|
||||
AABBoid aModel, AABBoid bModel,
|
||||
float tickLength,
|
||||
ColliderWorkspace workspace
|
||||
) {
|
||||
Collideable obstacleBody = bBody;
|
||||
Collideable colliderBody = aBody;
|
||||
AABB obstacleModel = bModel;
|
||||
AABB colliderModel = aModel;
|
||||
AABBoid obstacleModel = bModel;
|
||||
AABBoid colliderModel = aModel;
|
||||
|
||||
Collision result = null;
|
||||
|
||||
@ -32,7 +31,8 @@ class AABBWithAABBCollider {
|
||||
computeCollisionVelocity(collisionVelocity, obstacleBody, colliderBody);
|
||||
|
||||
// For every wall of collision space
|
||||
for (CollisionWall wall : originCollisionSpace.getFaces()) {
|
||||
for (int i = 0; i < BlockFace.BLOCK_FACE_COUNT; ++i) {
|
||||
Wall wall = originCollisionSpace.getWall(i);
|
||||
|
||||
Collision collision = computeWallCollision(
|
||||
wall, colliderModel,
|
||||
@ -80,12 +80,21 @@ class AABBWithAABBCollider {
|
||||
Vectors.release(colliderVelocity);
|
||||
}
|
||||
|
||||
private static AABB createOriginCollisionSpace(AABB obstacle, AABB collider, AABB output) {
|
||||
output.setOrigin(obstacle.getOrigin());
|
||||
private static AABB createOriginCollisionSpace(AABBoid obstacle, AABBoid collider, AABB output) {
|
||||
Vec3 obstacleOrigin = Vectors.grab3();
|
||||
Vec3 obstacleSize = Vectors.grab3();
|
||||
Vec3 colliderSize = Vectors.grab3();
|
||||
|
||||
Vec3 size = Vectors.grab3().set(obstacle.getSize()).add(collider.getSize());
|
||||
output.setSize(size);
|
||||
Vectors.release(size);
|
||||
obstacle.getOrigin(obstacleOrigin);
|
||||
output.setOrigin(obstacleOrigin);
|
||||
|
||||
obstacle.getSize(obstacleSize);
|
||||
collider.getSize(colliderSize);
|
||||
output.setSize(obstacleSize.add(colliderSize));
|
||||
|
||||
Vectors.release(obstacleOrigin);
|
||||
Vectors.release(obstacleSize);
|
||||
Vectors.release(colliderSize);
|
||||
|
||||
return output;
|
||||
}
|
||||
@ -134,27 +143,34 @@ class AABBWithAABBCollider {
|
||||
* If all conditions are satisfied, then the moment of impact is t0 + t.
|
||||
*/
|
||||
private static Collision computeWallCollision(
|
||||
CollisionWall obstacleWall,
|
||||
AABB colliderModel,
|
||||
Wall obstacleWall,
|
||||
AABBoid colliderModel,
|
||||
Vec3 collisionVelocity,
|
||||
float tickLength, ColliderWorkspace workspace,
|
||||
Collideable aBody, Collideable bBody
|
||||
) {
|
||||
Vec3 w = obstacleWall.getWidth();
|
||||
Vec3 h = obstacleWall.getHeight();
|
||||
Vec3 w = Vectors.grab3();
|
||||
Vec3 h = Vectors.grab3();
|
||||
Vec3 v = Vectors.grab3();
|
||||
Mat3 m = Matrices.grab3(); // The matrix [w h -v]
|
||||
Vec3 r = Vectors.grab3();
|
||||
Vec3 r_line = Vectors.grab3();
|
||||
Vec3 r_wall = Vectors.grab3();
|
||||
Vec3 xyt = Vectors.grab3();
|
||||
|
||||
try {
|
||||
obstacleWall.getWidth(w);
|
||||
obstacleWall.getHeight(h);
|
||||
|
||||
v.set(collisionVelocity);
|
||||
|
||||
if (isExiting(v, w, h)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
r.set(colliderModel.getOrigin()).sub(obstacleWall.getOrigin());
|
||||
obstacleWall.getOrigin(r_wall);
|
||||
colliderModel.getOrigin(r_line);
|
||||
r.set(r_line).sub(r_wall);
|
||||
m.c0(w).c1(h).c2(v.negate());
|
||||
|
||||
if (Math.abs(m.det()) < 1e-6) {
|
||||
@ -179,9 +195,13 @@ class AABBWithAABBCollider {
|
||||
|
||||
return workspace.grab().set(aBody, bBody, obstacleWall, t);
|
||||
} finally {
|
||||
Vectors.release(w);
|
||||
Vectors.release(h);
|
||||
Vectors.release(v);
|
||||
Vectors.release(r);
|
||||
Matrices.release(m);
|
||||
Vectors.release(r);
|
||||
Vectors.release(r_line);
|
||||
Vectors.release(r_wall);
|
||||
Vectors.release(xyt);
|
||||
}
|
||||
}
|
||||
@ -193,6 +213,6 @@ class AABBWithAABBCollider {
|
||||
return result;
|
||||
}
|
||||
|
||||
private AABBWithAABBCollider() {}
|
||||
private AABBoidCollider() {}
|
||||
|
||||
}
|
@ -6,14 +6,10 @@ import java.util.List;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.common.collision.AABB;
|
||||
import ru.windcorp.progressia.common.collision.Collideable;
|
||||
import ru.windcorp.progressia.common.collision.CollisionClock;
|
||||
import ru.windcorp.progressia.common.collision.CollisionModel;
|
||||
import ru.windcorp.progressia.common.collision.CollisionWall;
|
||||
import ru.windcorp.progressia.common.collision.CompoundCollisionModel;
|
||||
import ru.windcorp.progressia.common.collision.*;
|
||||
import ru.windcorp.progressia.common.util.LowOverheadCache;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
|
||||
public class Collider {
|
||||
|
||||
@ -21,7 +17,7 @@ public class Collider {
|
||||
|
||||
public static void performCollisions(
|
||||
List<? extends Collideable> colls,
|
||||
CollisionClock clock,
|
||||
WorldData world,
|
||||
float tickLength,
|
||||
ColliderWorkspace workspace
|
||||
) {
|
||||
@ -37,12 +33,12 @@ public class Collider {
|
||||
return;
|
||||
}
|
||||
|
||||
Collision firstCollision = getFirstCollision(colls, tickLength, workspace);
|
||||
Collision firstCollision = getFirstCollision(colls, tickLength, world, workspace);
|
||||
|
||||
if (firstCollision == null) {
|
||||
break;
|
||||
} else {
|
||||
collide(firstCollision, colls, clock, tickLength, workspace);
|
||||
collide(firstCollision, colls, world, tickLength, workspace);
|
||||
workspace.release(firstCollision);
|
||||
collisionCount++;
|
||||
|
||||
@ -50,45 +46,49 @@ public class Collider {
|
||||
}
|
||||
}
|
||||
|
||||
advanceTime(colls, clock, tickLength);
|
||||
advanceTime(colls, world, tickLength);
|
||||
}
|
||||
|
||||
private static Collision getFirstCollision(
|
||||
List<? extends Collideable> colls,
|
||||
float tickLength,
|
||||
WorldData world,
|
||||
ColliderWorkspace workspace
|
||||
) {
|
||||
Collision result = null;
|
||||
Collideable worldColl = workspace.worldCollisionHelper.getCollideable();
|
||||
|
||||
// For every pair of colls
|
||||
for (int i = 0; i < colls.size(); ++i) {
|
||||
Collideable a = colls.get(i);
|
||||
|
||||
tuneWorldCollisionHelper(a, tickLength, world, workspace);
|
||||
|
||||
result = workspace.updateLatestCollision(
|
||||
result,
|
||||
getCollision(a, worldColl, tickLength, workspace)
|
||||
);
|
||||
|
||||
for (int j = i + 1; j < colls.size(); ++j) {
|
||||
Collideable b = colls.get(j);
|
||||
|
||||
Collision collision = getCollision(a, b, tickLength, workspace);
|
||||
|
||||
// Update result
|
||||
if (collision != null) {
|
||||
Collision second;
|
||||
|
||||
if (result == null || collision.time < result.time) {
|
||||
second = result;
|
||||
result = collision;
|
||||
} else {
|
||||
second = collision;
|
||||
}
|
||||
|
||||
// Release Collision that is no longer used
|
||||
if (second != null) workspace.release(second);
|
||||
}
|
||||
result = workspace.updateLatestCollision(result, collision);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void tuneWorldCollisionHelper(
|
||||
Collideable coll,
|
||||
float tickLength,
|
||||
WorldData world,
|
||||
ColliderWorkspace workspace
|
||||
) {
|
||||
WorldCollisionHelper wch = workspace.worldCollisionHelper;
|
||||
wch.tuneToCollideable(world, coll, tickLength);
|
||||
}
|
||||
|
||||
static Collision getCollision(
|
||||
Collideable a,
|
||||
Collideable b,
|
||||
@ -108,10 +108,10 @@ public class Collider {
|
||||
float tickLength,
|
||||
ColliderWorkspace workspace
|
||||
) {
|
||||
if (aModel instanceof AABB && bModel instanceof AABB) {
|
||||
return AABBWithAABBCollider.computeModelCollision(
|
||||
if (aModel instanceof AABBoid && bModel instanceof AABBoid) {
|
||||
return AABBoidCollider.computeModelCollision(
|
||||
aBody, bBody,
|
||||
(AABB) aModel, (AABB) bModel,
|
||||
(AABBoid) aModel, (AABBoid) bModel,
|
||||
tickLength,
|
||||
workspace
|
||||
);
|
||||
@ -144,11 +144,11 @@ public class Collider {
|
||||
Collision collision,
|
||||
|
||||
Collection<? extends Collideable> colls,
|
||||
CollisionClock clock,
|
||||
WorldData world,
|
||||
float tickLength,
|
||||
ColliderWorkspace workspace
|
||||
) {
|
||||
advanceTime(colls, clock, collision.time);
|
||||
advanceTime(colls, world, collision.time);
|
||||
|
||||
boolean doNotHandle = false;
|
||||
|
||||
@ -237,7 +237,7 @@ public class Collider {
|
||||
Vec3 du_a = Vectors.grab3();
|
||||
Vec3 du_b = Vectors.grab3();
|
||||
|
||||
n.set(collision.wall.getWidth()).cross(collision.wall.getHeight()).normalize();
|
||||
n.set(collision.wallWidth).cross(collision.wallHeight).normalize();
|
||||
collision.a.getCollideableVelocity(v_a);
|
||||
collision.b.getCollideableVelocity(v_b);
|
||||
|
||||
@ -306,10 +306,10 @@ public class Collider {
|
||||
|
||||
private static void advanceTime(
|
||||
Collection<? extends Collideable> colls,
|
||||
CollisionClock clock,
|
||||
WorldData world,
|
||||
float step
|
||||
) {
|
||||
clock.advanceTime(step);
|
||||
world.advanceTime(step);
|
||||
|
||||
Vec3 tmp = Vectors.grab3();
|
||||
|
||||
@ -329,6 +329,8 @@ public class Collider {
|
||||
|
||||
AABB dummyAABB = new AABB(0, 0, 0, 1, 1, 1);
|
||||
|
||||
WorldCollisionHelper worldCollisionHelper = new WorldCollisionHelper();
|
||||
|
||||
Collision grab() {
|
||||
return collisionCache.grab();
|
||||
}
|
||||
@ -337,12 +339,35 @@ public class Collider {
|
||||
collisionCache.release(object);
|
||||
}
|
||||
|
||||
Collision updateLatestCollision(Collision a, Collision b) {
|
||||
if (a == null) {
|
||||
return b; // may be null
|
||||
} else if (b == null) {
|
||||
return a;
|
||||
}
|
||||
|
||||
Collision first, second;
|
||||
|
||||
if (a.time > b.time) {
|
||||
first = b;
|
||||
second = a;
|
||||
} else {
|
||||
first = a;
|
||||
second = b;
|
||||
}
|
||||
|
||||
release(second);
|
||||
return first;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Collision {
|
||||
public Collideable a;
|
||||
public Collideable b;
|
||||
public final CollisionWall wall = new CollisionWall(0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
public final Vec3 wallWidth = new Vec3();
|
||||
public final Vec3 wallHeight = new Vec3();
|
||||
|
||||
/**
|
||||
* Time offset from the start of the tick.
|
||||
@ -350,12 +375,15 @@ public class Collider {
|
||||
*/
|
||||
public float time;
|
||||
|
||||
public Collision set(Collideable a, Collideable b, CollisionWall wall, float time) {
|
||||
public Collision set(
|
||||
Collideable a, Collideable b,
|
||||
Wall wall,
|
||||
float time
|
||||
) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.wall.getOrigin().set(wall.getOrigin());
|
||||
this.wall.getWidth().set(wall.getWidth());
|
||||
this.wall.getHeight().set(wall.getHeight());
|
||||
wall.getWidth(wallWidth);
|
||||
wall.getHeight(wallHeight);
|
||||
this.time = time;
|
||||
|
||||
return this;
|
||||
|
@ -30,6 +30,19 @@ import glm.vec._4.i.Vec4i;
|
||||
*/
|
||||
public class Vectors {
|
||||
|
||||
public static final Vec2 ZERO_2 = new Vec2 (0, 0);
|
||||
public static final Vec2i ZERO_2i = new Vec2i(0, 0);
|
||||
public static final Vec3 ZERO_3 = new Vec3 (0, 0, 0);
|
||||
public static final Vec3i ZERO_3i = new Vec3i(0, 0, 0);
|
||||
public static final Vec4 ZERO_4 = new Vec4 (0, 0, 0, 0);
|
||||
public static final Vec4i ZERO_4i = new Vec4i(0, 0, 0, 0);
|
||||
public static final Vec2 UNIT_2 = new Vec2 (1, 1);
|
||||
public static final Vec2i UNIT_2i = new Vec2i(1, 1);
|
||||
public static final Vec3 UNIT_3 = new Vec3 (1, 1, 1);
|
||||
public static final Vec3i UNIT_3i = new Vec3i(1, 1, 1);
|
||||
public static final Vec4 UNIT_4 = new Vec4 (1, 1, 1, 1);
|
||||
public static final Vec4i UNIT_4i = new Vec4i(1, 1, 1, 1);
|
||||
|
||||
private static final LowOverheadCache<Vec3i> VEC3IS =
|
||||
new LowOverheadCache<>(Vec3i::new);
|
||||
|
||||
|
@ -80,7 +80,7 @@ public class ChunkData {
|
||||
TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers");
|
||||
TileData sand = TileDataRegistry.getInstance().get("Test:Sand");
|
||||
|
||||
Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2);
|
||||
Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2).sub(getPosition());
|
||||
Vec3i pos = new Vec3i();
|
||||
|
||||
for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) {
|
||||
@ -132,19 +132,29 @@ public class ChunkData {
|
||||
}
|
||||
}
|
||||
|
||||
EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony");
|
||||
javapony.setEntityId(0x42);
|
||||
javapony.setPosition(new Vec3(-6, -6, 20));
|
||||
javapony.setDirection(new Vec2(
|
||||
if (!getPosition().any()) {
|
||||
// EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony");
|
||||
// javapony.setEntityId(0x42);
|
||||
// javapony.setPosition(new Vec3(-6, -6, 20));
|
||||
// javapony.setDirection(new Vec2(
|
||||
// (float) Math.toRadians(40), (float) Math.toRadians(45)
|
||||
// ));
|
||||
// getEntities().add(javapony);
|
||||
|
||||
EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
|
||||
player.setEntityId(0x42);
|
||||
player.setPosition(new Vec3(-6, -6, 20));
|
||||
player.setDirection(new Vec2(
|
||||
(float) Math.toRadians(40), (float) Math.toRadians(45)
|
||||
));
|
||||
getEntities().add(javapony);
|
||||
getEntities().add(player);
|
||||
|
||||
EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie");
|
||||
statie.setEntityId(0xDEADBEEF);
|
||||
statie.setPosition(new Vec3(0, 15, 16));
|
||||
getEntities().add(statie);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockData getBlock(Vec3i posInChunk) {
|
||||
return blocks[getBlockIndex(posInChunk)];
|
||||
|
@ -24,8 +24,10 @@ import glm.vec._3.i.Vec3i;
|
||||
import gnu.trove.impl.sync.TSynchronizedLongObjectMap;
|
||||
import gnu.trove.map.TLongObjectMap;
|
||||
import gnu.trove.map.hash.TLongObjectHashMap;
|
||||
import ru.windcorp.progressia.common.collision.CollisionModel;
|
||||
import ru.windcorp.progressia.common.util.CoordinatePacker;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
|
||||
public class WorldData {
|
||||
@ -42,6 +44,8 @@ public class WorldData {
|
||||
private final Collection<EntityData> entities =
|
||||
Collections.unmodifiableCollection(entitiesById.valueCollection());
|
||||
|
||||
private float time = 0;
|
||||
|
||||
public WorldData() {
|
||||
final int size = 1;
|
||||
|
||||
@ -96,4 +100,25 @@ public class WorldData {
|
||||
return entities;
|
||||
}
|
||||
|
||||
public float getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void advanceTime(float change) {
|
||||
this.time += change;
|
||||
}
|
||||
|
||||
public CollisionModel getCollisionModelOfBlock(Vec3i blockInWorld) {
|
||||
ChunkData chunk = getChunkByBlock(blockInWorld);
|
||||
if (chunk == null) return null;
|
||||
|
||||
Vec3i blockInChunk = Vectors.grab3i();
|
||||
Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk);
|
||||
BlockData block = chunk.getBlock(blockInChunk);
|
||||
Vectors.release(blockInChunk);
|
||||
|
||||
if (block == null) return null;
|
||||
return block.getCollisionModel();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.progressia.common.world.block;
|
||||
|
||||
import ru.windcorp.progressia.common.collision.AABB;
|
||||
import ru.windcorp.progressia.common.collision.CollisionModel;
|
||||
import ru.windcorp.progressia.common.util.Namespaced;
|
||||
|
||||
public class BlockData extends Namespaced {
|
||||
@ -25,4 +27,8 @@ public class BlockData extends Namespaced {
|
||||
super(namespace, name);
|
||||
}
|
||||
|
||||
public CollisionModel getCollisionModel() {
|
||||
return AABB.UNIT_CUBE;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,35 +0,0 @@
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||
import ru.windcorp.progressia.client.graphics.model.Shapes;
|
||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.common.collision.AABB;
|
||||
import ru.windcorp.progressia.common.collision.CollisionModel;
|
||||
import ru.windcorp.progressia.common.collision.CompoundCollisionModel;
|
||||
|
||||
public class AABBRenderer {
|
||||
|
||||
private static final Shape CUBE = new Shapes.PppBuilder(WorldRenderProgram.getDefault(), (Texture) null).setColorMultiplier(1.0f, 0.7f, 0.2f).create();
|
||||
|
||||
public static void renderAABB(AABB aabb, ShapeRenderHelper helper) {
|
||||
helper.pushTransform().translate(aabb.getOrigin()).scale(aabb.getSize());
|
||||
CUBE.render(helper);
|
||||
helper.popTransform();
|
||||
}
|
||||
|
||||
public static void renderAABBsInCompound(
|
||||
CompoundCollisionModel model,
|
||||
ShapeRenderHelper helper
|
||||
) {
|
||||
for (CollisionModel part : model.getModels()) {
|
||||
if (part instanceof CompoundCollisionModel) {
|
||||
renderAABBsInCompound((CompoundCollisionModel) part, helper);
|
||||
} else if (part instanceof AABB) {
|
||||
renderAABB((AABB) part, helper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import glm.mat._4.Mat4;
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||
import ru.windcorp.progressia.client.graphics.model.Shapes;
|
||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.common.collision.AABBoid;
|
||||
import ru.windcorp.progressia.common.collision.CollisionModel;
|
||||
import ru.windcorp.progressia.common.collision.CompoundCollisionModel;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
|
||||
public class CollisionModelRenderer {
|
||||
|
||||
private static final Shape CUBE = new Shapes.PppBuilder(WorldRenderProgram.getDefault(), (Texture) null).setColorMultiplier(1.0f, 0.7f, 0.2f).create();
|
||||
private static final Shape CUBE_GRAY = new Shapes.PppBuilder(WorldRenderProgram.getDefault(), (Texture) null).setColorMultiplier(0.5f, 0.5f, 0.5f).create();
|
||||
|
||||
public static void renderCollisionModel(CollisionModel model, ShapeRenderHelper helper) {
|
||||
if (model instanceof AABBoid) {
|
||||
renderAABBoid((AABBoid) model, helper);
|
||||
} else if (model instanceof CompoundCollisionModel) {
|
||||
renderCompound((CompoundCollisionModel) model, helper);
|
||||
} else {
|
||||
// Ignore silently
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderAABBoid(AABBoid aabb, ShapeRenderHelper helper) {
|
||||
Mat4 mat = helper.pushTransform();
|
||||
Vec3 tmp = Vectors.grab3();
|
||||
|
||||
aabb.getOrigin(tmp);
|
||||
mat.translate(tmp);
|
||||
aabb.getSize(tmp);
|
||||
mat.scale(tmp);
|
||||
|
||||
Vectors.release(tmp);
|
||||
|
||||
CUBE.render(helper);
|
||||
helper.popTransform();
|
||||
}
|
||||
|
||||
private static void renderCompound(
|
||||
CompoundCollisionModel model,
|
||||
ShapeRenderHelper helper
|
||||
) {
|
||||
for (CollisionModel part : model.getModels()) {
|
||||
renderCollisionModel(part, helper);
|
||||
}
|
||||
}
|
||||
|
||||
public static void renderBlock(Vec3i coords, ShapeRenderHelper helper) {
|
||||
helper.pushTransform().translate(coords.x, coords.y, coords.z);
|
||||
CUBE_GRAY.render(helper);
|
||||
helper.popTransform();
|
||||
}
|
||||
|
||||
}
|
155
src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java
Executable file
155
src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java
Executable file
@ -0,0 +1,155 @@
|
||||
/*******************************************************************************
|
||||
* Progressia
|
||||
* Copyright (C) 2020 Wind Corporation
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import ru.windcorp.progressia.client.ClientState;
|
||||
import ru.windcorp.progressia.client.graphics.font.Font;
|
||||
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
|
||||
import ru.windcorp.progressia.client.graphics.gui.Label;
|
||||
import ru.windcorp.progressia.client.graphics.gui.Panel;
|
||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
||||
|
||||
public class LayerTestGUI extends GUILayer {
|
||||
|
||||
public LayerTestGUI() {
|
||||
super("LayerTestGui", new LayoutAlign(0, 1, 5));
|
||||
|
||||
Panel panel = new Panel("ControlDisplays", new LayoutVertical(5));
|
||||
|
||||
Collection<Label> labels = new ArrayList<>();
|
||||
|
||||
panel.addChild(new Label(
|
||||
"IsFlyingDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
|
||||
() -> String.format("Flying: %5s (Space bar x2)", TestPlayerControls.getInstance().isFlying())
|
||||
));
|
||||
|
||||
panel.addChild(new Label(
|
||||
"IsMouseCapturedDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
|
||||
() -> String.format("Mouse captured: %5s (esc)", TestPlayerControls.getInstance().isMouseCaptured())
|
||||
));
|
||||
|
||||
panel.addChild(new Label(
|
||||
"CameraModeDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
|
||||
() -> String.format("Camera mode: %5d (F5)", ClientState.getInstance().getCamera().getCurrentModeIndex())
|
||||
));
|
||||
|
||||
panel.addChild(new Label(
|
||||
"GravityModeDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
|
||||
() -> String.format("Gravity: %9s (G)", TestPlayerControls.getInstance().useMinecraftGravity() ? "Minecraft" : "Realistic")
|
||||
));
|
||||
|
||||
panel.getChildren().forEach(c -> labels.add((Label) c));
|
||||
TestPlayerControls.getInstance().setUpdateCallback(() -> labels.forEach(Label::update));
|
||||
|
||||
getRoot().addChild(panel);
|
||||
}
|
||||
|
||||
// private static class DebugComponent extends Component {
|
||||
// private final int color;
|
||||
//
|
||||
// public DebugComponent(String name, Vec2i size, int color) {
|
||||
// super(name);
|
||||
// this.color = color;
|
||||
//
|
||||
// setPreferredSize(size);
|
||||
//
|
||||
// addListener(new Object() {
|
||||
// @Subscribe
|
||||
// public void onHoverChanged(HoverEvent e) {
|
||||
// requestReassembly();
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// addListener(KeyEvent.class, this::onClicked);
|
||||
// }
|
||||
//
|
||||
// private boolean onClicked(KeyEvent event) {
|
||||
// if (!event.isMouse()) {
|
||||
// return false;
|
||||
// } else if (event.isPress() && event.isLeftMouseButton()) {
|
||||
// System.out.println("You pressed a Component!");
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void assembleSelf(RenderTarget target) {
|
||||
// target.fill(getX(), getY(), getWidth(), getHeight(), Colors.BLACK);
|
||||
//
|
||||
// target.fill(
|
||||
// getX() + 2, getY() + 2,
|
||||
// getWidth() - 4, getHeight() - 4,
|
||||
// isHovered() ? Colors.DEBUG_YELLOW : color
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public LayerTestGUI() {
|
||||
// super("LayerTestGui", new LayoutAlign(1, 0.75, 5));
|
||||
//
|
||||
// Panel panel = new Panel("Alex", new LayoutVertical(5));
|
||||
//
|
||||
// panel.addChild(new DebugComponent("Bravo", new Vec2i(200, 100), 0x44FF44));
|
||||
//
|
||||
// Component charlie = new DebugComponent("Charlie", null, 0x222222);
|
||||
// charlie.setLayout(new LayoutVertical(5));
|
||||
//
|
||||
// //Debug
|
||||
// Localizer.getInstance().setLanguage("ru-RU");
|
||||
// MutableString epsilon = new MutableStringLocalized("Epsilon")
|
||||
// .addListener(() -> ((Label)charlie.getChild(0)).update()).format(34, "thirty-four");
|
||||
// // These two are swapped in code due to a bug in layouts, fixing ATM
|
||||
// charlie.addChild(
|
||||
// new Label(
|
||||
// "Delta",
|
||||
// new Font().withColor(0xCCBB44).deriveShadow().deriveBold(),
|
||||
// "Пре-альфа!"
|
||||
// )
|
||||
// );
|
||||
// charlie.addChild(
|
||||
// new Label(
|
||||
// "Epsilon",
|
||||
// new Font().withColor(0x4444BB).deriveItalic(),
|
||||
// () -> epsilon.get().concat("\u269b")
|
||||
// )
|
||||
// );
|
||||
// panel.addChild(charlie);
|
||||
//
|
||||
//
|
||||
// charlie.addListener(KeyEvent.class, e -> {
|
||||
// if(e.isPress() && e.getKey() == GLFW.GLFW_KEY_L) {
|
||||
// Localizer localizer = Localizer.getInstance();
|
||||
// if (localizer.getLanguage().equals("ru-RU")) {
|
||||
// localizer.setLanguage("en-US");
|
||||
// } else {
|
||||
// localizer.setLanguage("ru-RU");
|
||||
// }
|
||||
// return true;
|
||||
// } return false;
|
||||
// });
|
||||
// charlie.setFocusable(true);
|
||||
// charlie.takeFocus();
|
||||
//
|
||||
// getRoot().addChild(panel);
|
||||
// }
|
||||
|
||||
}
|
@ -14,6 +14,7 @@ import ru.windcorp.progressia.client.world.block.*;
|
||||
import ru.windcorp.progressia.client.world.entity.*;
|
||||
import ru.windcorp.progressia.client.world.tile.*;
|
||||
import ru.windcorp.progressia.common.collision.AABB;
|
||||
import ru.windcorp.progressia.common.collision.CollisionModel;
|
||||
import ru.windcorp.progressia.common.comms.controls.*;
|
||||
import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
@ -41,7 +42,12 @@ public class TestContent {
|
||||
}
|
||||
|
||||
private static void registerBlocks() {
|
||||
register(new BlockData("Test", "Air"));
|
||||
register(new BlockData("Test", "Air") {
|
||||
@Override
|
||||
public CollisionModel getCollisionModel() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
register(new BlockRenderNone("Test", "Air"));
|
||||
register(new BlockLogic("Test", "Air"));
|
||||
|
||||
@ -81,9 +87,14 @@ public class TestContent {
|
||||
}
|
||||
|
||||
private static void registerEntities() {
|
||||
registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f)));
|
||||
register(new TestEntityRenderJavapony());
|
||||
register(new EntityLogic("Test", "Javapony"));
|
||||
// registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f)));
|
||||
// register(new TestEntityRenderJavapony());
|
||||
// register(new EntityLogic("Test", "Javapony"));
|
||||
|
||||
float scale = 1.8f / 8;
|
||||
registerEntityData("Test", "Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f)));
|
||||
register(new TestEntityRenderHuman());
|
||||
register(new EntityLogic("Test", "Player"));
|
||||
|
||||
register("Test", "Statie", TestEntityDataStatie::new);
|
||||
register(new TestEntityRenderStatie());
|
||||
@ -92,7 +103,7 @@ public class TestContent {
|
||||
|
||||
private static void regsiterControls() {
|
||||
ControlDataRegistry.getInstance().register(new ControlData("Test", "Switch000"));
|
||||
ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "Switch000", new KeyMatcher(GLFW.GLFW_KEY_G, new int[0], 0)::matches));
|
||||
ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "Switch000", new KeyMatcher(GLFW.GLFW_KEY_H, new int[0], 0)::matches));
|
||||
ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "Switch000") {
|
||||
@Override
|
||||
public void apply(Server server, PacketControl packet, Client client) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import ru.windcorp.progressia.common.collision.AABB;
|
||||
import ru.windcorp.progressia.common.collision.CompoundCollisionModel;
|
||||
import ru.windcorp.progressia.common.state.IntStateField;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
|
||||
@ -12,10 +11,7 @@ public class TestEntityDataStatie extends EntityData {
|
||||
|
||||
public TestEntityDataStatie() {
|
||||
super("Test", "Statie");
|
||||
setCollisionModel(new CompoundCollisionModel(
|
||||
new AABB(0, 0, 0, 1, 1, 1 ),
|
||||
new AABB(0, 0, 0.7f, 0.6f, 0.6f, 0.6f)
|
||||
));
|
||||
setCollisionModel(new AABB(0, 0, 0, 1, 1, 1));
|
||||
setSizeNow(16);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,164 @@
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import static java.lang.Math.toRadians;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
||||
import ru.windcorp.progressia.client.graphics.model.LambdaModel;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder;
|
||||
import ru.windcorp.progressia.client.graphics.model.StaticModel;
|
||||
import ru.windcorp.progressia.client.graphics.texture.ComplexTexture;
|
||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.client.world.entity.HumanoidModel;
|
||||
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.util.FloatMathUtils;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
|
||||
import static java.lang.Math.*;
|
||||
|
||||
public class TestEntityRenderHuman extends EntityRender {
|
||||
|
||||
private static final float SECOND_LAYER_OFFSET = 1 / 12f;
|
||||
|
||||
private final Renderable body;
|
||||
private final Renderable head;
|
||||
private final Renderable leftArm;
|
||||
private final Renderable rightArm;
|
||||
private final Renderable leftLeg;
|
||||
private final Renderable rightLeg;
|
||||
|
||||
public TestEntityRenderHuman() {
|
||||
super("Test", "Player");
|
||||
|
||||
ComplexTexture texture = new ComplexTexture(
|
||||
EntityRenderRegistry.getEntityTexture("pyotr"),
|
||||
16, 16
|
||||
);
|
||||
|
||||
this.body = createBody(texture);
|
||||
this.head = createHead(texture);
|
||||
|
||||
this.leftArm = createLimb(texture, 8, 0, 12, 0, true, true);
|
||||
this.rightArm = createLimb(texture, 10, 8, 10, 4, true, false);
|
||||
this.leftLeg = createLimb(texture, 4, 0, 0, 0, false, true);
|
||||
this.rightLeg = createLimb(texture, 0, 8, 0, 4, false, false);
|
||||
}
|
||||
|
||||
private Renderable createBody(ComplexTexture texture) {
|
||||
return createLayeredCuboid(
|
||||
texture,
|
||||
4, 8,
|
||||
4, 4,
|
||||
2, 3, 1,
|
||||
-0.5f, -1, 3,
|
||||
1, 2, 3
|
||||
);
|
||||
}
|
||||
|
||||
private Renderable createHead(ComplexTexture texture) {
|
||||
return createLayeredCuboid(
|
||||
texture,
|
||||
0, 12,
|
||||
8, 12,
|
||||
2, 2, 2,
|
||||
-1, -1, 0,
|
||||
2, 2, 2
|
||||
);
|
||||
}
|
||||
|
||||
private Renderable createLimb(
|
||||
ComplexTexture texture,
|
||||
int tx, int ty,
|
||||
int tx2, int ty2,
|
||||
boolean isArm, boolean isLeft
|
||||
) {
|
||||
Renderable model = createLayeredCuboid(
|
||||
texture,
|
||||
tx, ty,
|
||||
tx2, ty2,
|
||||
1, 3, 1,
|
||||
-0.5f, -0.5f, isArm ? -2.5f : -3f,
|
||||
1, 1, 3
|
||||
);
|
||||
|
||||
if (isArm) {
|
||||
return LambdaModel.animate(
|
||||
model,
|
||||
mat -> {
|
||||
double phase = GraphicsInterface.getTime() + (isLeft ? 0 : Math.PI / 3);
|
||||
mat.rotateX((isLeft ? +1 : -1) * 1/40f * (sin(phase) + 1));
|
||||
mat.rotateY(1/20f * sin(Math.PI / 3 * phase));
|
||||
}
|
||||
);
|
||||
} else {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
private Renderable createLayeredCuboid(
|
||||
ComplexTexture texture,
|
||||
int tx, int ty,
|
||||
int tx2, int ty2,
|
||||
int tw, int th, int td,
|
||||
float ox, float oy, float oz,
|
||||
float sx, float sy, float sz
|
||||
) {
|
||||
WorldRenderProgram program = WorldRenderProgram.getDefault();
|
||||
StaticModel.Builder b = StaticModel.builder();
|
||||
|
||||
// First layer
|
||||
b.addPart(new PppBuilder(
|
||||
program,
|
||||
texture.getCuboidTextures(tx, ty, tw, th, td)
|
||||
).setOrigin(ox, oy, oz).setSize(sx, sy, sz).create());
|
||||
|
||||
ox -= SECOND_LAYER_OFFSET;
|
||||
oy -= SECOND_LAYER_OFFSET;
|
||||
oz -= SECOND_LAYER_OFFSET;
|
||||
|
||||
sx += SECOND_LAYER_OFFSET * 2;
|
||||
sy += SECOND_LAYER_OFFSET * 2;
|
||||
sz += SECOND_LAYER_OFFSET * 2;
|
||||
|
||||
// Second layer
|
||||
b.addPart(new PppBuilder(
|
||||
program,
|
||||
texture.getCuboidTextures(tx2, ty2, tw, th, td)
|
||||
).setOrigin(ox, oy, oz).setSize(sx, sy, sz).create());
|
||||
|
||||
return new StaticModel(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityRenderable createRenderable(EntityData entity) {
|
||||
return new HumanoidModel(
|
||||
entity,
|
||||
|
||||
new HumanoidModel.Body(body),
|
||||
new HumanoidModel.Head(
|
||||
head, new Vec3(0, 0, 6), 70, 25, new Vec3(1.2f, 0, 1.5f)
|
||||
),
|
||||
new HumanoidModel.Arm(
|
||||
leftArm, new Vec3(0, +1.5f, 3 + 3 - 0.5f), 0.0f
|
||||
),
|
||||
new HumanoidModel.Arm(
|
||||
rightArm, new Vec3(0, -1.5f, 3 + 3 - 0.5f), FloatMathUtils.PI_F
|
||||
),
|
||||
new HumanoidModel.Leg(
|
||||
leftLeg, new Vec3(0, +0.5f, 3), FloatMathUtils.PI_F
|
||||
),
|
||||
new HumanoidModel.Leg(
|
||||
rightLeg, new Vec3(0, -0.5f, 3), 0.0f
|
||||
),
|
||||
|
||||
1.8f / (3 + 3 + 2)
|
||||
)
|
||||
.setWalkingArmSwing((float) toRadians(30))
|
||||
.setWalkingLegSwing((float) toRadians(50))
|
||||
.setWalkingFrequency(0.15f / 60.0f);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,270 @@
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
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.ClientState;
|
||||
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.bus.Input;
|
||||
import ru.windcorp.progressia.common.Units;
|
||||
import ru.windcorp.progressia.common.util.FloatMathUtils;
|
||||
import ru.windcorp.progressia.common.util.Matrices;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
|
||||
public class TestPlayerControls {
|
||||
|
||||
private static final TestPlayerControls INSTANCE = new TestPlayerControls();
|
||||
|
||||
public static TestPlayerControls getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private TestPlayerControls() {}
|
||||
|
||||
private static final double MODE_SWITCH_MAX_DELAY = 100 * Units.MILLISECONDS;
|
||||
private static final double MIN_JUMP_DELAY = 200 * Units.MILLISECONDS;
|
||||
|
||||
// Horizontal and vertical max control speed when flying
|
||||
private static final float FLYING_SPEED = 6.0f * Units.METERS_PER_SECOND;
|
||||
|
||||
// (0; 1], 1 is instant change, 0 is no control authority
|
||||
private static final float FLYING_CONTROL_AUTHORITY = 0.05f;
|
||||
|
||||
// Horizontal and vertical max control speed when walking
|
||||
private static final float WALKING_SPEED = 4.0f * Units.METERS_PER_SECOND;
|
||||
|
||||
// (0; 1], 1 is instant change, 0 is no control authority
|
||||
private static final float WALKING_CONTROL_AUTHORITY = 0.1f;
|
||||
|
||||
// Vertical velocity instantly add to player when they jump
|
||||
private static final float JUMP_VELOCITY = 5f * Units.METERS_PER_SECOND;
|
||||
|
||||
private boolean isFlying = true;
|
||||
|
||||
private int movementForward = 0;
|
||||
private int movementRight = 0;
|
||||
private int movementUp = 0;
|
||||
|
||||
private double lastSpacePress = Double.NEGATIVE_INFINITY;
|
||||
|
||||
private boolean captureMouse = true;
|
||||
private boolean useMinecraftGravity = false;
|
||||
|
||||
private Runnable updateCallback = null;
|
||||
|
||||
public void applyPlayerControls() {
|
||||
if (ClientState.getInstance() == null || ClientState.getInstance().getLocalPlayer() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityData player = getPlayer();
|
||||
Mat3 angMat = Matrices.grab3();
|
||||
|
||||
angMat.identity().rotateZ(player.getYaw());
|
||||
|
||||
Vec3 movement = Vectors.grab3();
|
||||
|
||||
movement.set(movementForward, -movementRight, 0);
|
||||
|
||||
if (movementForward != 0 && movementRight != 0) {
|
||||
movement.normalize();
|
||||
}
|
||||
|
||||
angMat.mul_(movement); // bug in jglm, .mul() and mul_() are swapped
|
||||
|
||||
if (isFlying) {
|
||||
movement.z = movementUp;
|
||||
movement.mul(FLYING_SPEED);
|
||||
movement.sub(player.getVelocity());
|
||||
movement.mul(FLYING_CONTROL_AUTHORITY);
|
||||
} else {
|
||||
movement.mul(WALKING_SPEED);
|
||||
movement.sub(player.getVelocity());
|
||||
movement.mul(WALKING_CONTROL_AUTHORITY);
|
||||
movement.z = 0;
|
||||
}
|
||||
|
||||
player.getVelocity().add(movement);
|
||||
|
||||
Matrices.release(angMat);
|
||||
Vectors.release(movement);
|
||||
}
|
||||
|
||||
public void handleInput(Input input) {
|
||||
if (ClientState.getInstance() == null || ClientState.getInstance().getLocalPlayer() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
InputEvent event = input.getEvent();
|
||||
|
||||
if (event instanceof KeyEvent) {
|
||||
if (onKeyEvent((KeyEvent) event)) {
|
||||
input.consume();
|
||||
}
|
||||
} else if (event instanceof CursorMoveEvent) {
|
||||
onMouseMoved((CursorMoveEvent) event);
|
||||
input.consume();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onKeyEvent(KeyEvent event) {
|
||||
if (event.isRepeat()) return false;
|
||||
|
||||
int multiplier = event.isPress() ? 1 : -1;
|
||||
|
||||
switch (event.getKey()) {
|
||||
case GLFW.GLFW_KEY_W:
|
||||
movementForward += +1 * multiplier;
|
||||
break;
|
||||
case GLFW.GLFW_KEY_S:
|
||||
movementForward += -1 * multiplier;
|
||||
break;
|
||||
case GLFW.GLFW_KEY_A:
|
||||
movementRight += -1 * multiplier;
|
||||
break;
|
||||
case GLFW.GLFW_KEY_D:
|
||||
movementRight += +1 * multiplier;
|
||||
break;
|
||||
case GLFW.GLFW_KEY_SPACE:
|
||||
handleSpace(multiplier);
|
||||
break;
|
||||
case GLFW.GLFW_KEY_LEFT_SHIFT:
|
||||
handleShift(multiplier);
|
||||
break;
|
||||
|
||||
case GLFW.GLFW_KEY_ESCAPE:
|
||||
if (!event.isPress()) return false;
|
||||
handleEscape();
|
||||
break;
|
||||
|
||||
case GLFW.GLFW_KEY_F5:
|
||||
if (!event.isPress()) return false;
|
||||
handleCameraMode();
|
||||
break;
|
||||
|
||||
case GLFW.GLFW_KEY_G:
|
||||
if (!event.isPress()) return false;
|
||||
handleGravitySwitch();
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleSpace(int multiplier) {
|
||||
boolean isPressed = multiplier > 0;
|
||||
|
||||
double timeSinceLastSpacePress = GraphicsInterface.getTime() - lastSpacePress;
|
||||
|
||||
if (isPressed && timeSinceLastSpacePress < MODE_SWITCH_MAX_DELAY) {
|
||||
isFlying = !isFlying;
|
||||
updateGUI();
|
||||
movementUp = +1;
|
||||
} else {
|
||||
if (isFlying) {
|
||||
movementUp += +1 * multiplier;
|
||||
} else {
|
||||
if (isPressed && timeSinceLastSpacePress > MIN_JUMP_DELAY) {
|
||||
jump();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastSpacePress = GraphicsInterface.getTime();
|
||||
}
|
||||
|
||||
private void jump() {
|
||||
getPlayer().getVelocity().add(0, 0, JUMP_VELOCITY * (useMinecraftGravity ? 2 : 1));
|
||||
}
|
||||
|
||||
private void handleShift(int multiplier) {
|
||||
if (isFlying) {
|
||||
movementUp += -1 * multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEscape() {
|
||||
if (captureMouse) {
|
||||
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL);
|
||||
} else {
|
||||
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
|
||||
}
|
||||
|
||||
captureMouse = !captureMouse;
|
||||
updateGUI();
|
||||
}
|
||||
|
||||
private void handleCameraMode() {
|
||||
if (ClientState.getInstance().getCamera().hasAnchor()) {
|
||||
ClientState.getInstance().getCamera().selectNextMode();
|
||||
updateGUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGravitySwitch() {
|
||||
useMinecraftGravity = !useMinecraftGravity;
|
||||
updateGUI();
|
||||
}
|
||||
|
||||
private void onMouseMoved(CursorMoveEvent event) {
|
||||
if (!captureMouse) return;
|
||||
|
||||
final float yawScale = -0.002f;
|
||||
final float pitchScale = yawScale;
|
||||
|
||||
EntityData player = getPlayer();
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
private EntityData getPlayer() {
|
||||
return ClientState.getInstance().getLocalPlayer();
|
||||
}
|
||||
|
||||
public void setUpdateCallback(Runnable updateCallback) {
|
||||
this.updateCallback = updateCallback;
|
||||
}
|
||||
|
||||
private void updateGUI() {
|
||||
if (this.updateCallback != null) {
|
||||
this.updateCallback.run();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFlying() {
|
||||
return isFlying;
|
||||
}
|
||||
|
||||
public boolean isMouseCaptured() {
|
||||
return captureMouse;
|
||||
}
|
||||
|
||||
public boolean useMinecraftGravity() {
|
||||
return useMinecraftGravity;
|
||||
}
|
||||
|
||||
}
|
BIN
src/main/resources/assets/textures/entities/pyotr.png
Normal file
BIN
src/main/resources/assets/textures/entities/pyotr.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.8 KiB |
BIN
src/main/resources/assets/textures/entities/test_skin.png
Normal file
BIN
src/main/resources/assets/textures/entities/test_skin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
src/main/resources/assets/textures/entities/test_skin_layout.png
Normal file
BIN
src/main/resources/assets/textures/entities/test_skin_layout.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
Reference in New Issue
Block a user