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'
|
implementation 'ru.windcorp.fork.io.github.java-graphics:glm:1.0.1'
|
||||||
|
|
||||||
// log4j
|
// log4j
|
||||||
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.3'
|
implementation 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-core', version: '2.13.3'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
|
|
||||||
|
// See also LWJGL dependencies below
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -77,3 +79,22 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LWJGL END
|
// 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.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.IntFunction;
|
import java.util.function.IntFunction;
|
||||||
|
|
||||||
public class StringUtil {
|
public class StringUtil {
|
||||||
@ -775,4 +776,35 @@ public class StringUtil {
|
|||||||
else return (char) ('A' - 0xA + value);
|
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.comms.localhost.LocalServerCommsChannel;
|
||||||
import ru.windcorp.progressia.client.graphics.GUI;
|
import ru.windcorp.progressia.client.graphics.GUI;
|
||||||
import ru.windcorp.progressia.client.graphics.flat.LayerTestUI;
|
import ru.windcorp.progressia.client.graphics.flat.LayerTestUI;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.LayerTestGUI;
|
|
||||||
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
|
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
import ru.windcorp.progressia.server.ServerState;
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
|
import ru.windcorp.progressia.test.LayerTestGUI;
|
||||||
|
|
||||||
public class ClientState {
|
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.primitive = primitive;
|
||||||
|
|
||||||
this.assumedWidth = abstractWidth
|
this.assumedWidth = abstractWidth
|
||||||
* primitive.getWidth() / (float) primitive.getBufferWidth();
|
/ (float) primitive.getWidth() * primitive.getBufferWidth();
|
||||||
|
|
||||||
this.assumedHeight = abstractHeight
|
this.assumedHeight = abstractHeight
|
||||||
* primitive.getHeight() / (float) primitive.getBufferHeight();
|
/ (float) primitive.getHeight() * primitive.getBufferHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Texture get(int x, int y, int width, int height) {
|
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() {
|
public float getLastAnchorYaw() {
|
||||||
return lastAnchorYaw;
|
return lastAnchorYaw;
|
||||||
}
|
}
|
||||||
|
@ -20,45 +20,27 @@ package ru.windcorp.progressia.client.graphics.world;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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.Client;
|
||||||
|
import ru.windcorp.progressia.client.ClientState;
|
||||||
import ru.windcorp.progressia.client.comms.controls.InputBasedControls;
|
import ru.windcorp.progressia.client.comms.controls.InputBasedControls;
|
||||||
import ru.windcorp.progressia.client.graphics.Layer;
|
import ru.windcorp.progressia.client.graphics.Layer;
|
||||||
import ru.windcorp.progressia.client.graphics.backend.FaceCulling;
|
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.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.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.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.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.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 {
|
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 WorldRenderHelper helper = new WorldRenderHelper();
|
||||||
|
|
||||||
private final Client client;
|
private final Client client;
|
||||||
private final InputBasedControls inputBasedControls;
|
private final InputBasedControls inputBasedControls;
|
||||||
|
private final TestPlayerControls tmp_testControls = TestPlayerControls.getInstance();
|
||||||
|
|
||||||
public LayerWorld(Client client) {
|
public LayerWorld(Client client) {
|
||||||
super("World");
|
super("World");
|
||||||
@ -79,40 +61,12 @@ public class LayerWorld extends Layer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRender() {
|
protected void doRender() {
|
||||||
if (client.getLocalPlayer() != null) {
|
|
||||||
tmp_handleControls();
|
|
||||||
}
|
|
||||||
|
|
||||||
Camera camera = client.getCamera();
|
Camera camera = client.getCamera();
|
||||||
if (camera.hasAnchor()) {
|
if (camera.hasAnchor()) {
|
||||||
renderWorld();
|
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() {
|
private void renderWorld() {
|
||||||
client.getCamera().apply(helper);
|
client.getCamera().apply(helper);
|
||||||
FaceCulling.push(true);
|
FaceCulling.push(true);
|
||||||
@ -128,150 +82,75 @@ public class LayerWorld extends Layer {
|
|||||||
private final Collider.ColliderWorkspace tmp_colliderWorkspace = new Collider.ColliderWorkspace();
|
private final Collider.ColliderWorkspace tmp_colliderWorkspace = new Collider.ColliderWorkspace();
|
||||||
private final List<Collideable> tmp_collideableList = new ArrayList<>();
|
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() {
|
private void tmp_doEveryFrame() {
|
||||||
|
float tickLength = (float) GraphicsInterface.getFrameLength();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (RENDER_AABBS) {
|
tmp_performCollisions(tickLength);
|
||||||
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_collideableList.clear();
|
tmp_testControls.applyPlayerControls();
|
||||||
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(),
|
|
||||||
tmp_colliderWorkspace
|
|
||||||
);
|
|
||||||
|
|
||||||
final float frictionCoeff = 1 - 1e-2f;
|
|
||||||
|
|
||||||
for (EntityData data : this.client.getWorld().getData().getEntities()) {
|
for (EntityData data : this.client.getWorld().getData().getEntities()) {
|
||||||
data.getVelocity().mul(frictionCoeff);
|
tmp_applyFriction(data);
|
||||||
|
tmp_applyGravity(data, tickLength);
|
||||||
|
tmp_renderCollisionModel(data);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.err.println("OLEGSHA is to blame. Tell him he vry stupiDD!!");
|
||||||
System.exit(31337);
|
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,
|
||||||
|
this.client.getWorld().getData(),
|
||||||
|
tickLength,
|
||||||
|
tmp_colliderWorkspace
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
protected void handleInput(Input input) {
|
protected void handleInput(Input input) {
|
||||||
if (input.isConsumed()) return;
|
if (input.isConsumed()) return;
|
||||||
|
|
||||||
InputEvent event = input.getEvent();
|
tmp_testControls.handleInput(input);
|
||||||
|
|
||||||
if (event instanceof KeyEvent) {
|
|
||||||
if (onKeyEvent((KeyEvent) event)) {
|
|
||||||
input.consume();
|
|
||||||
}
|
|
||||||
} else if (event instanceof CursorMoveEvent) {
|
|
||||||
onMouseMoved((CursorMoveEvent) event);
|
|
||||||
input.consume();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!input.isConsumed()) {
|
if (!input.isConsumed()) {
|
||||||
inputBasedControls.handleInput(input);
|
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 java.lang.Math.*;
|
||||||
import static ru.windcorp.progressia.common.util.FloatMathUtils.*;
|
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.mat._4.Mat4;
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
import glm.vec._4.Vec4;
|
|
||||||
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||||
import ru.windcorp.progressia.common.util.Matrices;
|
|
||||||
import ru.windcorp.progressia.common.util.Vectors;
|
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
|
||||||
public class QuadripedModel extends EntityRenderable {
|
public class QuadripedModel extends NPedModel {
|
||||||
|
|
||||||
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 static class Leg extends BodyPart {
|
public static class Leg extends BodyPart {
|
||||||
private final float animationOffset;
|
private final float animationOffset;
|
||||||
@ -95,34 +24,20 @@ public class QuadripedModel extends EntityRenderable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void applyTransform(Mat4 mat, QuadripedModel model) {
|
protected void applyTransform(Mat4 mat, NPedModel model) {
|
||||||
mat.rotateY(sin(model.walkingFrequency * model.walkingAnimationParameter + animationOffset) * model.walkingSwing * model.velocityCoeff);
|
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 leftForeLeg, rightForeLeg;
|
||||||
private final Leg leftHindLeg, rightHindLeg;
|
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 walkingSwing = (float) toRadians(30);
|
||||||
|
|
||||||
private float bodyYaw = Float.NaN;
|
|
||||||
private float headYaw;
|
|
||||||
private float headPitch;
|
|
||||||
|
|
||||||
public QuadripedModel(
|
public QuadripedModel(
|
||||||
EntityData entity,
|
EntityData entity,
|
||||||
|
|
||||||
@ -132,105 +47,30 @@ public class QuadripedModel extends EntityRenderable {
|
|||||||
|
|
||||||
float scale
|
float scale
|
||||||
) {
|
) {
|
||||||
super(entity);
|
super(entity, body, head, scale);
|
||||||
|
|
||||||
this.body = body;
|
|
||||||
this.head = head;
|
|
||||||
this.leftForeLeg = leftForeLeg;
|
this.leftForeLeg = leftForeLeg;
|
||||||
this.rightForeLeg = rightForeLeg;
|
this.rightForeLeg = rightForeLeg;
|
||||||
this.leftHindLeg = leftHindLeg;
|
this.leftHindLeg = leftHindLeg;
|
||||||
this.rightHindLeg = rightHindLeg;
|
this.rightHindLeg = rightHindLeg;
|
||||||
|
|
||||||
this.scale = scale;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(ShapeRenderHelper renderer) {
|
protected void renderBodyParts(ShapeRenderHelper renderer) {
|
||||||
renderer.pushTransform().scale(scale).rotateZ(bodyYaw);
|
super.renderBodyParts(renderer);
|
||||||
body.render(renderer, this);
|
this.leftForeLeg.render(renderer, this);
|
||||||
head.render(renderer, this);
|
this.rightForeLeg.render(renderer, this);
|
||||||
leftForeLeg.render(renderer, this);
|
this.leftHindLeg.render(renderer, this);
|
||||||
rightForeLeg.render(renderer, this);
|
this.rightHindLeg.render(renderer, this);
|
||||||
leftHindLeg.render(renderer, this);
|
|
||||||
rightHindLeg.render(renderer, this);
|
|
||||||
renderer.popTransform();
|
|
||||||
|
|
||||||
accountForVelocity();
|
|
||||||
evaluateAngles();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void evaluateAngles() {
|
public float getWalkingSwing() {
|
||||||
float globalYaw = normalizeAngle(getData().getYaw());
|
return walkingSwing;
|
||||||
|
|
||||||
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() {
|
public QuadripedModel setWalkingSwing(float walkingSwing) {
|
||||||
Vec3 horizontal = Vectors.grab3();
|
this.walkingSwing = walkingSwing;
|
||||||
horizontal.set(getData().getVelocity());
|
return this;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
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,53 +1,71 @@
|
|||||||
package ru.windcorp.progressia.common.collision;
|
package ru.windcorp.progressia.common.collision;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
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(
|
private class AABBWallImpl implements Wall {
|
||||||
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),
|
|
||||||
|
|
||||||
new CollisionWall(-0.5f, -0.5f, +0.5f, +1, 0, 0, 0, +1, 0),
|
private final Vec3 originOffset = new Vec3();
|
||||||
new CollisionWall(-0.5f, -0.5f, -0.5f, 0, +1, 0, +1, 0, 0)
|
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 origin = new Vec3();
|
||||||
private final Vec3 size = new Vec3();
|
private final Vec3 size = new Vec3();
|
||||||
|
|
||||||
public AABB(Vec3 origin, Vec3 size) {
|
public AABB(Vec3 origin, Vec3 size) {
|
||||||
this.origin.set(origin);
|
this(origin.x, origin.y, origin.z, size.x, size.y, size.z);
|
||||||
this.size.set(size);
|
|
||||||
|
|
||||||
for (CollisionWall wall : getFaces()) {
|
|
||||||
wall.moveOrigin(origin);
|
|
||||||
wall.getWidth().mul(size);
|
|
||||||
wall.getHeight().mul(size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AABB(
|
public AABB(
|
||||||
float ox, float oy, float oz,
|
float ox, float oy, float oz,
|
||||||
float xSize, float ySize, float zSize
|
float xSize, float ySize, float zSize
|
||||||
) {
|
) {
|
||||||
this.origin.set(ox, oy, oz);
|
this.origin.set(ox, oy, oz);
|
||||||
this.size.set(xSize, ySize, zSize);
|
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() {
|
public Vec3 getOrigin() {
|
||||||
@ -55,20 +73,17 @@ public class AABB implements CollisionModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOrigin(Vec3 origin) {
|
public void getOrigin(Vec3 output) {
|
||||||
for (CollisionWall wall : getFaces()) {
|
output.set(origin);
|
||||||
wall.getOrigin().sub(this.origin).add(origin);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOrigin(Vec3 origin) {
|
||||||
this.origin.set(origin);
|
this.origin.set(origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moveOrigin(Vec3 displacement) {
|
public void moveOrigin(Vec3 displacement) {
|
||||||
for (CollisionWall wall : getFaces()) {
|
|
||||||
wall.getOrigin().add(displacement);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.origin.add(displacement);
|
this.origin.add(displacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,18 +91,23 @@ public class AABB implements CollisionModel {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getSize(Vec3 output) {
|
||||||
|
output.set(size);
|
||||||
|
}
|
||||||
|
|
||||||
public void setSize(Vec3 size) {
|
public void setSize(Vec3 size) {
|
||||||
setSize(size.x, size.y, size.z);
|
setSize(size.x, size.y, size.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSize(float xSize, float ySize, float zSize) {
|
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);
|
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 {
|
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;
|
this.models = models;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ public class CompoundCollisionModel implements CollisionModel {
|
|||||||
this(ImmutableList.copyOf(models));
|
this(ImmutableList.copyOf(models));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<CollisionModel> getModels() {
|
public Collection<? extends CollisionModel> getModels() {
|
||||||
return models;
|
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.mat._3.Mat3;
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
import ru.windcorp.progressia.common.collision.AABB;
|
import ru.windcorp.progressia.common.collision.*;
|
||||||
import ru.windcorp.progressia.common.collision.Collideable;
|
|
||||||
import ru.windcorp.progressia.common.collision.CollisionWall;
|
|
||||||
import ru.windcorp.progressia.common.collision.colliders.Collider.ColliderWorkspace;
|
import ru.windcorp.progressia.common.collision.colliders.Collider.ColliderWorkspace;
|
||||||
import ru.windcorp.progressia.common.collision.colliders.Collider.Collision;
|
import ru.windcorp.progressia.common.collision.colliders.Collider.Collision;
|
||||||
import ru.windcorp.progressia.common.util.Matrices;
|
import ru.windcorp.progressia.common.util.Matrices;
|
||||||
import ru.windcorp.progressia.common.util.Vectors;
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
|
||||||
class AABBWithAABBCollider {
|
class AABBoidCollider {
|
||||||
|
|
||||||
static Collider.Collision computeModelCollision(
|
static Collider.Collision computeModelCollision(
|
||||||
Collideable aBody, Collideable bBody,
|
Collideable aBody, Collideable bBody,
|
||||||
AABB aModel, AABB bModel,
|
AABBoid aModel, AABBoid bModel,
|
||||||
float tickLength,
|
float tickLength,
|
||||||
ColliderWorkspace workspace
|
ColliderWorkspace workspace
|
||||||
) {
|
) {
|
||||||
Collideable obstacleBody = bBody;
|
Collideable obstacleBody = bBody;
|
||||||
Collideable colliderBody = aBody;
|
Collideable colliderBody = aBody;
|
||||||
AABB obstacleModel = bModel;
|
AABBoid obstacleModel = bModel;
|
||||||
AABB colliderModel = aModel;
|
AABBoid colliderModel = aModel;
|
||||||
|
|
||||||
Collision result = null;
|
Collision result = null;
|
||||||
|
|
||||||
@ -32,7 +31,8 @@ class AABBWithAABBCollider {
|
|||||||
computeCollisionVelocity(collisionVelocity, obstacleBody, colliderBody);
|
computeCollisionVelocity(collisionVelocity, obstacleBody, colliderBody);
|
||||||
|
|
||||||
// For every wall of collision space
|
// 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(
|
Collision collision = computeWallCollision(
|
||||||
wall, colliderModel,
|
wall, colliderModel,
|
||||||
@ -80,12 +80,21 @@ class AABBWithAABBCollider {
|
|||||||
Vectors.release(colliderVelocity);
|
Vectors.release(colliderVelocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AABB createOriginCollisionSpace(AABB obstacle, AABB collider, AABB output) {
|
private static AABB createOriginCollisionSpace(AABBoid obstacle, AABBoid collider, AABB output) {
|
||||||
output.setOrigin(obstacle.getOrigin());
|
Vec3 obstacleOrigin = Vectors.grab3();
|
||||||
|
Vec3 obstacleSize = Vectors.grab3();
|
||||||
|
Vec3 colliderSize = Vectors.grab3();
|
||||||
|
|
||||||
Vec3 size = Vectors.grab3().set(obstacle.getSize()).add(collider.getSize());
|
obstacle.getOrigin(obstacleOrigin);
|
||||||
output.setSize(size);
|
output.setOrigin(obstacleOrigin);
|
||||||
Vectors.release(size);
|
|
||||||
|
obstacle.getSize(obstacleSize);
|
||||||
|
collider.getSize(colliderSize);
|
||||||
|
output.setSize(obstacleSize.add(colliderSize));
|
||||||
|
|
||||||
|
Vectors.release(obstacleOrigin);
|
||||||
|
Vectors.release(obstacleSize);
|
||||||
|
Vectors.release(colliderSize);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -134,27 +143,34 @@ class AABBWithAABBCollider {
|
|||||||
* If all conditions are satisfied, then the moment of impact is t0 + t.
|
* If all conditions are satisfied, then the moment of impact is t0 + t.
|
||||||
*/
|
*/
|
||||||
private static Collision computeWallCollision(
|
private static Collision computeWallCollision(
|
||||||
CollisionWall obstacleWall,
|
Wall obstacleWall,
|
||||||
AABB colliderModel,
|
AABBoid colliderModel,
|
||||||
Vec3 collisionVelocity,
|
Vec3 collisionVelocity,
|
||||||
float tickLength, ColliderWorkspace workspace,
|
float tickLength, ColliderWorkspace workspace,
|
||||||
Collideable aBody, Collideable bBody
|
Collideable aBody, Collideable bBody
|
||||||
) {
|
) {
|
||||||
Vec3 w = obstacleWall.getWidth();
|
Vec3 w = Vectors.grab3();
|
||||||
Vec3 h = obstacleWall.getHeight();
|
Vec3 h = Vectors.grab3();
|
||||||
Vec3 v = Vectors.grab3();
|
Vec3 v = Vectors.grab3();
|
||||||
Mat3 m = Matrices.grab3(); // The matrix [w h -v]
|
Mat3 m = Matrices.grab3(); // The matrix [w h -v]
|
||||||
Vec3 r = Vectors.grab3();
|
Vec3 r = Vectors.grab3();
|
||||||
|
Vec3 r_line = Vectors.grab3();
|
||||||
|
Vec3 r_wall = Vectors.grab3();
|
||||||
Vec3 xyt = Vectors.grab3();
|
Vec3 xyt = Vectors.grab3();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
obstacleWall.getWidth(w);
|
||||||
|
obstacleWall.getHeight(h);
|
||||||
|
|
||||||
v.set(collisionVelocity);
|
v.set(collisionVelocity);
|
||||||
|
|
||||||
if (isExiting(v, w, h)) {
|
if (isExiting(v, w, h)) {
|
||||||
return null;
|
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());
|
m.c0(w).c1(h).c2(v.negate());
|
||||||
|
|
||||||
if (Math.abs(m.det()) < 1e-6) {
|
if (Math.abs(m.det()) < 1e-6) {
|
||||||
@ -179,9 +195,13 @@ class AABBWithAABBCollider {
|
|||||||
|
|
||||||
return workspace.grab().set(aBody, bBody, obstacleWall, t);
|
return workspace.grab().set(aBody, bBody, obstacleWall, t);
|
||||||
} finally {
|
} finally {
|
||||||
|
Vectors.release(w);
|
||||||
|
Vectors.release(h);
|
||||||
Vectors.release(v);
|
Vectors.release(v);
|
||||||
Vectors.release(r);
|
|
||||||
Matrices.release(m);
|
Matrices.release(m);
|
||||||
|
Vectors.release(r);
|
||||||
|
Vectors.release(r_line);
|
||||||
|
Vectors.release(r_wall);
|
||||||
Vectors.release(xyt);
|
Vectors.release(xyt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,6 +213,6 @@ class AABBWithAABBCollider {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AABBWithAABBCollider() {}
|
private AABBoidCollider() {}
|
||||||
|
|
||||||
}
|
}
|
@ -6,14 +6,10 @@ import java.util.List;
|
|||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
import ru.windcorp.progressia.common.collision.AABB;
|
import ru.windcorp.progressia.common.collision.*;
|
||||||
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.util.LowOverheadCache;
|
import ru.windcorp.progressia.common.util.LowOverheadCache;
|
||||||
import ru.windcorp.progressia.common.util.Vectors;
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
|
|
||||||
public class Collider {
|
public class Collider {
|
||||||
|
|
||||||
@ -21,7 +17,7 @@ public class Collider {
|
|||||||
|
|
||||||
public static void performCollisions(
|
public static void performCollisions(
|
||||||
List<? extends Collideable> colls,
|
List<? extends Collideable> colls,
|
||||||
CollisionClock clock,
|
WorldData world,
|
||||||
float tickLength,
|
float tickLength,
|
||||||
ColliderWorkspace workspace
|
ColliderWorkspace workspace
|
||||||
) {
|
) {
|
||||||
@ -37,12 +33,12 @@ public class Collider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Collision firstCollision = getFirstCollision(colls, tickLength, workspace);
|
Collision firstCollision = getFirstCollision(colls, tickLength, world, workspace);
|
||||||
|
|
||||||
if (firstCollision == null) {
|
if (firstCollision == null) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
collide(firstCollision, colls, clock, tickLength, workspace);
|
collide(firstCollision, colls, world, tickLength, workspace);
|
||||||
workspace.release(firstCollision);
|
workspace.release(firstCollision);
|
||||||
collisionCount++;
|
collisionCount++;
|
||||||
|
|
||||||
@ -50,45 +46,49 @@ public class Collider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
advanceTime(colls, clock, tickLength);
|
advanceTime(colls, world, tickLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Collision getFirstCollision(
|
private static Collision getFirstCollision(
|
||||||
List<? extends Collideable> colls,
|
List<? extends Collideable> colls,
|
||||||
float tickLength,
|
float tickLength,
|
||||||
|
WorldData world,
|
||||||
ColliderWorkspace workspace
|
ColliderWorkspace workspace
|
||||||
) {
|
) {
|
||||||
Collision result = null;
|
Collision result = null;
|
||||||
|
Collideable worldColl = workspace.worldCollisionHelper.getCollideable();
|
||||||
|
|
||||||
// For every pair of colls
|
// For every pair of colls
|
||||||
for (int i = 0; i < colls.size(); ++i) {
|
for (int i = 0; i < colls.size(); ++i) {
|
||||||
Collideable a = colls.get(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) {
|
for (int j = i + 1; j < colls.size(); ++j) {
|
||||||
Collideable b = colls.get(j);
|
Collideable b = colls.get(j);
|
||||||
|
|
||||||
Collision collision = getCollision(a, b, tickLength, workspace);
|
Collision collision = getCollision(a, b, tickLength, workspace);
|
||||||
|
result = workspace.updateLatestCollision(result, collision);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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(
|
static Collision getCollision(
|
||||||
Collideable a,
|
Collideable a,
|
||||||
Collideable b,
|
Collideable b,
|
||||||
@ -108,10 +108,10 @@ public class Collider {
|
|||||||
float tickLength,
|
float tickLength,
|
||||||
ColliderWorkspace workspace
|
ColliderWorkspace workspace
|
||||||
) {
|
) {
|
||||||
if (aModel instanceof AABB && bModel instanceof AABB) {
|
if (aModel instanceof AABBoid && bModel instanceof AABBoid) {
|
||||||
return AABBWithAABBCollider.computeModelCollision(
|
return AABBoidCollider.computeModelCollision(
|
||||||
aBody, bBody,
|
aBody, bBody,
|
||||||
(AABB) aModel, (AABB) bModel,
|
(AABBoid) aModel, (AABBoid) bModel,
|
||||||
tickLength,
|
tickLength,
|
||||||
workspace
|
workspace
|
||||||
);
|
);
|
||||||
@ -144,11 +144,11 @@ public class Collider {
|
|||||||
Collision collision,
|
Collision collision,
|
||||||
|
|
||||||
Collection<? extends Collideable> colls,
|
Collection<? extends Collideable> colls,
|
||||||
CollisionClock clock,
|
WorldData world,
|
||||||
float tickLength,
|
float tickLength,
|
||||||
ColliderWorkspace workspace
|
ColliderWorkspace workspace
|
||||||
) {
|
) {
|
||||||
advanceTime(colls, clock, collision.time);
|
advanceTime(colls, world, collision.time);
|
||||||
|
|
||||||
boolean doNotHandle = false;
|
boolean doNotHandle = false;
|
||||||
|
|
||||||
@ -237,7 +237,7 @@ public class Collider {
|
|||||||
Vec3 du_a = Vectors.grab3();
|
Vec3 du_a = Vectors.grab3();
|
||||||
Vec3 du_b = 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.a.getCollideableVelocity(v_a);
|
||||||
collision.b.getCollideableVelocity(v_b);
|
collision.b.getCollideableVelocity(v_b);
|
||||||
|
|
||||||
@ -306,10 +306,10 @@ public class Collider {
|
|||||||
|
|
||||||
private static void advanceTime(
|
private static void advanceTime(
|
||||||
Collection<? extends Collideable> colls,
|
Collection<? extends Collideable> colls,
|
||||||
CollisionClock clock,
|
WorldData world,
|
||||||
float step
|
float step
|
||||||
) {
|
) {
|
||||||
clock.advanceTime(step);
|
world.advanceTime(step);
|
||||||
|
|
||||||
Vec3 tmp = Vectors.grab3();
|
Vec3 tmp = Vectors.grab3();
|
||||||
|
|
||||||
@ -329,6 +329,8 @@ public class Collider {
|
|||||||
|
|
||||||
AABB dummyAABB = new AABB(0, 0, 0, 1, 1, 1);
|
AABB dummyAABB = new AABB(0, 0, 0, 1, 1, 1);
|
||||||
|
|
||||||
|
WorldCollisionHelper worldCollisionHelper = new WorldCollisionHelper();
|
||||||
|
|
||||||
Collision grab() {
|
Collision grab() {
|
||||||
return collisionCache.grab();
|
return collisionCache.grab();
|
||||||
}
|
}
|
||||||
@ -337,12 +339,35 @@ public class Collider {
|
|||||||
collisionCache.release(object);
|
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 {
|
static class Collision {
|
||||||
public Collideable a;
|
public Collideable a;
|
||||||
public Collideable b;
|
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.
|
* Time offset from the start of the tick.
|
||||||
@ -350,12 +375,15 @@ public class Collider {
|
|||||||
*/
|
*/
|
||||||
public float time;
|
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.a = a;
|
||||||
this.b = b;
|
this.b = b;
|
||||||
this.wall.getOrigin().set(wall.getOrigin());
|
wall.getWidth(wallWidth);
|
||||||
this.wall.getWidth().set(wall.getWidth());
|
wall.getHeight(wallHeight);
|
||||||
this.wall.getHeight().set(wall.getHeight());
|
|
||||||
this.time = time;
|
this.time = time;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
@ -30,6 +30,19 @@ import glm.vec._4.i.Vec4i;
|
|||||||
*/
|
*/
|
||||||
public class Vectors {
|
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 =
|
private static final LowOverheadCache<Vec3i> VEC3IS =
|
||||||
new LowOverheadCache<>(Vec3i::new);
|
new LowOverheadCache<>(Vec3i::new);
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ public class ChunkData {
|
|||||||
TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers");
|
TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers");
|
||||||
TileData sand = TileDataRegistry.getInstance().get("Test:Sand");
|
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();
|
Vec3i pos = new Vec3i();
|
||||||
|
|
||||||
for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) {
|
for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) {
|
||||||
@ -132,18 +132,28 @@ public class ChunkData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony");
|
if (!getPosition().any()) {
|
||||||
javapony.setEntityId(0x42);
|
// EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony");
|
||||||
javapony.setPosition(new Vec3(-6, -6, 20));
|
// javapony.setEntityId(0x42);
|
||||||
javapony.setDirection(new Vec2(
|
// javapony.setPosition(new Vec3(-6, -6, 20));
|
||||||
(float) Math.toRadians(40), (float) Math.toRadians(45)
|
// javapony.setDirection(new Vec2(
|
||||||
));
|
// (float) Math.toRadians(40), (float) Math.toRadians(45)
|
||||||
getEntities().add(javapony);
|
// ));
|
||||||
|
// getEntities().add(javapony);
|
||||||
|
|
||||||
EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie");
|
EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
|
||||||
statie.setEntityId(0xDEADBEEF);
|
player.setEntityId(0x42);
|
||||||
statie.setPosition(new Vec3(0, 15, 16));
|
player.setPosition(new Vec3(-6, -6, 20));
|
||||||
getEntities().add(statie);
|
player.setDirection(new Vec2(
|
||||||
|
(float) Math.toRadians(40), (float) Math.toRadians(45)
|
||||||
|
));
|
||||||
|
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) {
|
public BlockData getBlock(Vec3i posInChunk) {
|
||||||
|
@ -24,8 +24,10 @@ import glm.vec._3.i.Vec3i;
|
|||||||
import gnu.trove.impl.sync.TSynchronizedLongObjectMap;
|
import gnu.trove.impl.sync.TSynchronizedLongObjectMap;
|
||||||
import gnu.trove.map.TLongObjectMap;
|
import gnu.trove.map.TLongObjectMap;
|
||||||
import gnu.trove.map.hash.TLongObjectHashMap;
|
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.CoordinatePacker;
|
||||||
import ru.windcorp.progressia.common.util.Vectors;
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
|
||||||
public class WorldData {
|
public class WorldData {
|
||||||
@ -42,6 +44,8 @@ public class WorldData {
|
|||||||
private final Collection<EntityData> entities =
|
private final Collection<EntityData> entities =
|
||||||
Collections.unmodifiableCollection(entitiesById.valueCollection());
|
Collections.unmodifiableCollection(entitiesById.valueCollection());
|
||||||
|
|
||||||
|
private float time = 0;
|
||||||
|
|
||||||
public WorldData() {
|
public WorldData() {
|
||||||
final int size = 1;
|
final int size = 1;
|
||||||
|
|
||||||
@ -96,4 +100,25 @@ public class WorldData {
|
|||||||
return entities;
|
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;
|
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;
|
import ru.windcorp.progressia.common.util.Namespaced;
|
||||||
|
|
||||||
public class BlockData extends Namespaced {
|
public class BlockData extends Namespaced {
|
||||||
@ -25,4 +27,8 @@ public class BlockData extends Namespaced {
|
|||||||
super(namespace, name);
|
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.entity.*;
|
||||||
import ru.windcorp.progressia.client.world.tile.*;
|
import ru.windcorp.progressia.client.world.tile.*;
|
||||||
import ru.windcorp.progressia.common.collision.AABB;
|
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.comms.controls.*;
|
||||||
import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory;
|
import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
@ -41,7 +42,12 @@ public class TestContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void registerBlocks() {
|
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 BlockRenderNone("Test", "Air"));
|
||||||
register(new BlockLogic("Test", "Air"));
|
register(new BlockLogic("Test", "Air"));
|
||||||
|
|
||||||
@ -81,9 +87,14 @@ public class TestContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void registerEntities() {
|
private static void registerEntities() {
|
||||||
registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f)));
|
// registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f)));
|
||||||
register(new TestEntityRenderJavapony());
|
// register(new TestEntityRenderJavapony());
|
||||||
register(new EntityLogic("Test", "Javapony"));
|
// 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("Test", "Statie", TestEntityDataStatie::new);
|
||||||
register(new TestEntityRenderStatie());
|
register(new TestEntityRenderStatie());
|
||||||
@ -92,7 +103,7 @@ public class TestContent {
|
|||||||
|
|
||||||
private static void regsiterControls() {
|
private static void regsiterControls() {
|
||||||
ControlDataRegistry.getInstance().register(new ControlData("Test", "Switch000"));
|
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") {
|
ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "Switch000") {
|
||||||
@Override
|
@Override
|
||||||
public void apply(Server server, PacketControl packet, Client client) {
|
public void apply(Server server, PacketControl packet, Client client) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ru.windcorp.progressia.test;
|
package ru.windcorp.progressia.test;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.collision.AABB;
|
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.state.IntStateField;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
|
||||||
@ -12,10 +11,7 @@ public class TestEntityDataStatie extends EntityData {
|
|||||||
|
|
||||||
public TestEntityDataStatie() {
|
public TestEntityDataStatie() {
|
||||||
super("Test", "Statie");
|
super("Test", "Statie");
|
||||||
setCollisionModel(new CompoundCollisionModel(
|
setCollisionModel(new AABB(0, 0, 0, 1, 1, 1));
|
||||||
new AABB(0, 0, 0, 1, 1, 1 ),
|
|
||||||
new AABB(0, 0, 0.7f, 0.6f, 0.6f, 0.6f)
|
|
||||||
));
|
|
||||||
setSizeNow(16);
|
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