Implemented first version of collider

- Entities now can have an AABB collision model
- Collisions of pairs of entities are handled properly
- Added debug display of AABBs
This commit is contained in:
OLEGSHA 2020-11-08 18:26:33 +03:00
parent 1daec9a4df
commit 09af026137
16 changed files with 969 additions and 28 deletions

View File

@ -17,6 +17,8 @@
*******************************************************************************/
package ru.windcorp.progressia.client.graphics;
import glm.vec._3.Vec3;
public class Colors {
public static final int
@ -35,4 +37,16 @@ public class Colors {
DEBUG_MAGENTA = 0xFF00FF,
DEBUG_YELLOW = 0xFFFF00;
public static Vec3 toVector(int rgb) {
return toVector(rgb, new Vec3());
}
public static Vec3 toVector(int rgb, Vec3 output) {
output.x = ((rgb & 0xFF0000) >> 16) / 256f;
output.y = ((rgb & 0x00FF00) >> 8 ) / 256f;
output.z = ((rgb & 0x0000FF) ) / 256f;
return output;
}
}

View File

@ -227,7 +227,7 @@ public class RenderTarget {
return Faces.createRectangle(
FlatRenderProgram.getDefault(),
texture,
createVectorFromRGBInt(color),
Colors.toVector(color),
new Vec3(x, y, depth),
new Vec3(width, 0, 0),
new Vec3(0, height, 0),
@ -244,12 +244,4 @@ public class RenderTarget {
);
}
private static Vec3 createVectorFromRGBInt(int rgb) {
int r = (rgb & 0xFF0000) >> 16;
int g = (rgb & 0x00FF00) >> 8;
int b = (rgb & 0x0000FF);
return new Vec3(r / 256f, g / 256f, b / 256f);
}
}

View File

@ -17,6 +17,9 @@
*******************************************************************************/
package ru.windcorp.progressia.client.graphics.world;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.glfw.GLFW;
import glm.Glm;
@ -33,14 +36,18 @@ import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.bus.Input;
import ru.windcorp.progressia.common.collision.AABB;
import ru.windcorp.progressia.common.collision.Collideable;
import ru.windcorp.progressia.common.collision.CollisionClock;
import ru.windcorp.progressia.common.collision.CollisionModel;
import ru.windcorp.progressia.common.collision.colliders.Collider;
import ru.windcorp.progressia.common.util.FloatMathUtils;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.test.AABBRenderer;
public class LayerWorld extends Layer {
private final Vec3 velocity = new Vec3();
private final Mat3 angMat = new Mat3();
private int movementForward = 0;
@ -98,20 +105,11 @@ public class LayerWorld extends Layer {
angMat.mul_(movement); // bug in jglm, .mul() and mul_() are swapped
movement.z = movementUp;
movement.mul(movementSpeed);
movement.sub(velocity);
movement.sub(player.getVelocity());
movement.mul(controlAuthority);
velocity.add(movement);
player.getVelocity().add(movement);
Vectors.release(movement);
Vec3 velCopy = Vectors.grab3().set(velocity);
velCopy.mul((float) GraphicsInterface.getFrameLength());
player.move(velCopy);
player.getVelocity().set(velocity);
Vectors.release(velCopy);
}
private void renderWorld() {
@ -126,8 +124,51 @@ public class LayerWorld extends Layer {
helper.reset();
}
private final Collider.ColliderWorkspace tmp_colliderWorkspace = new Collider.ColliderWorkspace();
private final List<Collideable> tmp_collideableList = new ArrayList<>();
private static final boolean RENDER_AABBS = false;
private void tmp_doEveryFrame() {
// Stub for the future
try {
if (RENDER_AABBS) {
for (EntityData data : this.client.getWorld().getData().getEntities()) {
CollisionModel model = data.getCollisionModel();
if (model instanceof AABB) {
AABBRenderer.renderAABB((AABB) model, helper);
}
}
}
tmp_collideableList.clear();
tmp_collideableList.addAll(this.client.getWorld().getData().getEntities());
Collider.performCollisions(
tmp_collideableList,
new CollisionClock() {
private float t = 0;
@Override
public float getTime() {
return t;
}
@Override
public void advanceTime(float change) {
t += change;
}
},
(float) GraphicsInterface.getFrameLength(),
tmp_colliderWorkspace
);
final float frictionCoeff = 1 - 1e-2f;
for (EntityData data : this.client.getWorld().getData().getEntities()) {
data.getVelocity().mul(frictionCoeff);
}
} catch (Exception e) {
System.exit(31337);
}
}
@Override

View File

@ -0,0 +1,93 @@
package ru.windcorp.progressia.common.collision;
import java.util.Collection;
import java.util.Map;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.world.block.BlockFace;
public class AABB implements CollisionModel {
private final Map<BlockFace, CollisionWall> faces = BlockFace.mapToFaces(
new CollisionWall(-0.5f, -0.5f, -0.5f, +1, 0, 0, 0, 0, +1),
new CollisionWall(+0.5f, -0.5f, -0.5f, 0, +1, 0, 0, 0, +1),
new CollisionWall(+0.5f, +0.5f, -0.5f, -1, 0, 0, 0, 0, +1),
new CollisionWall(-0.5f, +0.5f, -0.5f, 0, -1, 0, 0, 0, +1),
new CollisionWall(-0.5f, -0.5f, +0.5f, +1, 0, 0, 0, +1, 0),
new CollisionWall(-0.5f, -0.5f, -0.5f, 0, +1, 0, +1, 0, 0)
);
private final Vec3 origin = new Vec3();
private final Vec3 size = new Vec3();
public AABB(Vec3 origin, Vec3 size) {
this.origin.set(origin);
this.size.set(size);
for (CollisionWall wall : getFaces()) {
wall.moveOrigin(origin);
wall.getWidth().mul(size);
wall.getHeight().mul(size);
}
}
public AABB(
float ox, float oy, float oz,
float xSize, float ySize, float zSize
) {
this.origin.set(ox, oy, oz);
this.size.set(xSize, ySize, zSize);
for (CollisionWall wall : getFaces()) {
wall.moveOrigin(ox, oy, oz);
wall.getWidth().mul(xSize, ySize, zSize);
wall.getHeight().mul(xSize, ySize, zSize);
}
}
public Collection<CollisionWall> getFaces() {
return faces.values();
}
public Vec3 getOrigin() {
return origin;
}
@Override
public void setOrigin(Vec3 origin) {
for (CollisionWall wall : getFaces()) {
wall.getOrigin().sub(this.origin).add(origin);
}
this.origin.set(origin);
}
@Override
public void moveOrigin(Vec3 displacement) {
for (CollisionWall wall : getFaces()) {
wall.getOrigin().add(displacement);
}
this.origin.add(displacement);
}
public Vec3 getSize() {
return size;
}
public void setSize(Vec3 size) {
setSize(size.x, size.y, size.z);
}
public void setSize(float xSize, float ySize, float zSize) {
for (CollisionWall wall : getFaces()) {
wall.getWidth().div(this.size).mul(xSize, ySize, zSize);
wall.getHeight().div(this.size).mul(xSize, ySize, zSize);
wall.getOrigin().sub(getOrigin()).div(this.size).mul(xSize, ySize, zSize).add(getOrigin());
}
this.size.set(xSize, ySize, zSize);
}
}

View File

@ -0,0 +1,30 @@
package ru.windcorp.progressia.common.collision;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.collision.colliders.Collider;
public interface Collideable {
CollisionModel getCollisionModel();
/**
* Invoked by {@link Collider} when two entities are about to collide.
* The world is at the moment of collision.
* @param other the colliding object
* @return {@code true} iff the collision should not be handled normally (e.g. this object has disappeared)
*/
boolean onCollision(Collideable other);
/**
* Returns the mass of this {@link Collideable} that should be used to calculate collisions.
* Collision mass must be a positive number. Positive infinity is allowed.
* @return this object's collision mass
*/
float getCollisionMass();
void moveAsCollideable(Vec3 displacement);
void getCollideableVelocity(Vec3 output);
void changeVelocityOnCollision(Vec3 velocityChange);
}

View File

@ -0,0 +1,8 @@
package ru.windcorp.progressia.common.collision;
public interface CollisionClock {
float getTime();
void advanceTime(float change);
}

View File

@ -0,0 +1,10 @@
package ru.windcorp.progressia.common.collision;
import glm.vec._3.Vec3;
public interface CollisionModel {
public void setOrigin(Vec3 origin);
public void moveOrigin(Vec3 displacement);
}

View File

@ -0,0 +1,55 @@
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);
}
}

View File

@ -0,0 +1,53 @@
package ru.windcorp.progressia.common.collision;
import java.util.function.Consumer;
import glm.mat._4.Mat4;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.util.Vectors;
public class TransformedCollisionModel implements CollisionModel {
private final CollisionModel parent;
private final Mat4 transform = new Mat4();
private final Mat4 inverseTransform = new Mat4();
public TransformedCollisionModel(CollisionModel parent, Mat4 transform) {
this.parent = parent;
setTransform(transform);
}
public TransformedCollisionModel(CollisionModel parent, Consumer<Mat4> transform) {
this.parent = parent;
transform.accept(this.transform);
this.inverseTransform.set(this.transform).inverse();
}
public Mat4 getTransform() {
return transform;
}
public void setTransform(Mat4 newTransform) {
this.transform.set(newTransform);
this.inverseTransform.set(newTransform).inverse();
}
@Override
public void setOrigin(Vec3 origin) {
Vec3 vec3 = Vectors.grab3();
VectorUtil.applyMat4(origin, inverseTransform, vec3);
this.parent.setOrigin(vec3);
Vectors.release(vec3);
}
@Override
public void moveOrigin(Vec3 displacement) {
Vec3 vec3 = Vectors.grab3();
VectorUtil.applyMat4(displacement, inverseTransform, vec3);
this.parent.moveOrigin(vec3);
Vectors.release(vec3);
}
}

View File

@ -0,0 +1,198 @@
package ru.windcorp.progressia.common.collision.colliders;
import glm.mat._3.Mat3;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.collision.AABB;
import ru.windcorp.progressia.common.collision.Collideable;
import ru.windcorp.progressia.common.collision.CollisionWall;
import ru.windcorp.progressia.common.collision.colliders.Collider.ColliderWorkspace;
import ru.windcorp.progressia.common.collision.colliders.Collider.Collision;
import ru.windcorp.progressia.common.util.Matrices;
import ru.windcorp.progressia.common.util.Vectors;
class AABBWithAABBCollider {
static Collider.Collision computeModelCollision(
Collideable aBody, Collideable bBody,
AABB aModel, AABB bModel,
float tickLength,
ColliderWorkspace workspace
) {
Collideable obstacleBody = bBody;
Collideable colliderBody = aBody;
AABB obstacleModel = bModel;
AABB colliderModel = aModel;
Collision result = null;
AABB originCollisionSpace =
createOriginCollisionSpace(obstacleModel, colliderModel, workspace.dummyAABB);
Vec3 collisionVelocity = Vectors.grab3();
computeCollisionVelocity(collisionVelocity, obstacleBody, colliderBody);
// For every wall of collision space
for (CollisionWall wall : originCollisionSpace.getFaces()) {
Collision collision = computeWallCollision(
wall, colliderModel,
collisionVelocity,
tickLength, workspace,
aBody, bBody
);
// 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);
}
}
Vectors.release(collisionVelocity);
return result;
}
private static void computeCollisionVelocity(
Vec3 output,
Collideable obstacleBody,
Collideable colliderBody
) {
Vec3 obstacleVelocity = Vectors.grab3();
Vec3 colliderVelocity = Vectors.grab3();
obstacleBody.getCollideableVelocity(obstacleVelocity);
colliderBody.getCollideableVelocity(colliderVelocity);
output.set(colliderVelocity).sub(obstacleVelocity);
Vectors.release(obstacleVelocity);
Vectors.release(colliderVelocity);
}
private static AABB createOriginCollisionSpace(AABB obstacle, AABB collider, AABB output) {
output.setOrigin(obstacle.getOrigin());
Vec3 size = Vectors.grab3().set(obstacle.getSize()).add(collider.getSize());
output.setSize(size);
Vectors.release(size);
return output;
}
/*
* Here we determine whether a collision has actually happened, and if it did, at what moment.
*
* The basic idea is to compute the moment of collision and impact coordinates in wall coordinate space.
* Then, we can check impact coordinates to determine if we actually hit the wall or flew by and then
* check time to make sure the collision is not too far in the future and not in the past.
*
* DETAILED EXPLANATION:
*
* Consider a surface defined by an origin r_wall and two noncollinear nonzero vectors w and h.
* Consider a line defined by an origin r_line and a nonzero vector v.
*
* Then, a collision occurs if there exist x, y and t such that
* ______ _
* r_line + v * t
*
* and
* ______ _ _
* r_wall + w * x + h * y
*
* describe the same location (indeed, this corresponds to a collision at moment t0 + t
* with a point on the wall with coordinates (x; y) in (w; h) coordinate system).
*
* Therefore,
* ______ _ ______ _ _
* r_line + v*t = r_wall + w*x + h*y;
*
* _ w_x h_x -v_x x _ ______ ______
* r = w_y h_y -v_y * y, where r = r_line - r_wall;
* w_z h_z -v_z t
*
* x w_x h_x -v_x -1 _
* y = w_y h_y -v_y * r, if the matrix is invertible.
* t w_z h_z -v_z
*
* Then, one only needs to ensure that:
* 0 < x < 1,
* 0 < y < 1, and
* 0 < t < T, where T is remaining tick time.
*
* If the matrix is not invertible or any of the conditions are not met, no collision happened.
* If all conditions are satisfied, then the moment of impact is t0 + t.
*/
private static Collision computeWallCollision(
CollisionWall obstacleWall,
AABB colliderModel,
Vec3 collisionVelocity,
float tickLength, ColliderWorkspace workspace,
Collideable aBody, Collideable bBody
) {
Vec3 w = obstacleWall.getWidth();
Vec3 h = obstacleWall.getHeight();
Vec3 v = Vectors.grab3();
Mat3 m = Matrices.grab3(); // The matrix [w h -v]
Vec3 r = Vectors.grab3();
Vec3 xyt = Vectors.grab3();
try {
v.set(collisionVelocity);
if (isExiting(v, w, h)) {
return null;
}
r.set(colliderModel.getOrigin()).sub(obstacleWall.getOrigin());
m.c0(w).c1(h).c2(v.negate());
if (Math.abs(m.det()) < 1e-6) {
return null;
}
m.inverse().mul(r, xyt);
float x = xyt.x;
float y = xyt.y;
float t = xyt.z;
if (x <= 0 || x >= 1 || y <= 0 || y >= 1) {
// We're missing the wall
return null;
}
if (t < 0 || t > tickLength) {
// We're colliding at the wrong moment
return null;
}
return workspace.grab().set(aBody, bBody, obstacleWall, t);
} finally {
Vectors.release(v);
Vectors.release(r);
Matrices.release(m);
Vectors.release(xyt);
}
}
private static boolean isExiting(Vec3 v, Vec3 w, Vec3 h) {
Vec3 tmp = Vectors.grab3().set(w).cross(h);
boolean result = Vec3.dot(tmp, v) >= 0;
Vectors.release(tmp);
return result;
}
private AABBWithAABBCollider() {}
}

View File

@ -0,0 +1,338 @@
package ru.windcorp.progressia.common.collision.colliders;
import java.util.Collection;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.collision.AABB;
import ru.windcorp.progressia.common.collision.Collideable;
import ru.windcorp.progressia.common.collision.CollisionClock;
import ru.windcorp.progressia.common.collision.CollisionModel;
import ru.windcorp.progressia.common.collision.CollisionWall;
import ru.windcorp.progressia.common.util.LowOverheadCache;
import ru.windcorp.progressia.common.util.Vectors;
public class Collider {
private static final int MAX_COLLISIONS_PER_ENTITY = 64;
public static void performCollisions(
List<? extends Collideable> colls,
CollisionClock clock,
float tickLength,
ColliderWorkspace workspace
) {
int collisionCount = 0;
int maxCollisions = colls.size() * MAX_COLLISIONS_PER_ENTITY;
while (true) {
if (collisionCount > maxCollisions) {
LogManager.getLogger().warn(
"Attempted to handle more than {} collisions",
maxCollisions
);
return;
}
Collision firstCollision = getFirstCollision(colls, tickLength, workspace);
if (firstCollision == null) {
break;
} else {
collide(firstCollision, colls, clock, tickLength, workspace);
workspace.release(firstCollision);
collisionCount++;
tickLength -= firstCollision.time;
}
}
advanceTime(colls, clock, tickLength);
}
private static Collision getFirstCollision(
List<? extends Collideable> colls,
float tickLength,
ColliderWorkspace workspace
) {
Collision result = null;
// For every pair of colls
for (int i = 0; i < colls.size(); ++i) {
Collideable a = colls.get(i);
for (int j = i + 1; j < colls.size(); ++j) {
Collideable b = colls.get(j);
Collision collision = getCollision(a, b, tickLength, workspace);
// Update result
if (collision != null) {
Collision second;
if (result == null || collision.time < result.time) {
second = result;
result = collision;
} else {
second = collision;
}
// Release Collision that is no longer used
if (second != null) workspace.release(second);
}
}
}
return result;
}
private static Collision getCollision(
Collideable a,
Collideable b,
float tickLength,
ColliderWorkspace workspace
) {
CollisionModel aModel = a.getCollisionModel();
CollisionModel bModel = b.getCollisionModel();
if (aModel instanceof AABB && bModel instanceof AABB) {
return AABBWithAABBCollider.computeModelCollision(
a, b,
(AABB) aModel, (AABB) bModel,
tickLength,
workspace
);
}
throw new UnsupportedOperationException(
"Collisions between " + aModel + " and " + bModel + " are not yet implemented"
);
}
private static void collide(
Collision collision,
Collection<? extends Collideable> colls,
CollisionClock clock,
float tickLength,
ColliderWorkspace workspace
) {
advanceTime(colls, clock, collision.time);
boolean doNotHandle = false;
doNotHandle |= collision.a.onCollision(collision.b);
doNotHandle |= collision.b.onCollision(collision.a);
if (doNotHandle) {
return;
}
handlePhysics(collision);
}
/*
* Here we compute the change in body velocities due to a collision.
*
* We make the following simplifications:
* 1) The bodies are perfectly rigid;
* 2) The collision is perfectly inelastic
* (no bouncing);
* 3) The bodies are spherical;
* 4) No tangential friction exists
* (bodies do not experience friction when sliding against each other);
* 5) Velocities are not relativistic.
*
* Angular momentum is ignored per 3) and 4),
* e.g. when something pushes an end of a long stick, the stick does not rotate.
*
* DETAILED EXPLANATION:
*
* Two spherical (sic) bodies, a and b, experience a perfectly inelastic collision
* along a unit vector
* _ _ _ _ _
* n = (w h) / (|w h|),
* _ _
* where w and h are two noncollinear nonzero vectors on the dividing plane.
* ___ ___
* Body masses and velocities are M_a, M_b and v_a, v_b, respectively.
* ___ ___
* After the collision desired velocities are u_a and u_b, respectively.
* _
* (Notation convention: suffix 'n' denotes a vector projection onto vector n,
* and suffix 't' denotes a vector projection onto the dividing plane.)
*
* Consider the law of conservation of momentum for axis n and the dividing plane:
* ____________ ____________ ________________
* n: p_a_before_n + p_b_before_n = p_common_after_n;
* ___________ ____________
* t: p_i_after_t = p_i_before_t for any i in {a, b}.
*
* Expressing all p_* in given terms:
* ___ _ ___ _ ___ ___ ____ ____
* n: M_a * (v_a n) + M_b * (v_b n) = (M_a + M_b) * u_n, where u_n u_an = u_bn;
* ____ ___ _ ___ _
* t: u_it = v_i - n * (v_i n) for any i in {a, b}.
*
* Therefore:
* ___ _ ___ _ ___ _
* u_n = n * ( M_a/(M_a + M_b) * v_a n + M_b/(M_a + M_b) * v_b n );
*
* or, equivalently,
* ___ _ ___ _ ___ _
* u_n = n * ( m_a * v_a n + m_b * v_b n ),
*
* where m_a and m_b are relative masses (see below).
*
* Finally,
* ___ ____ ___
* u_i = u_it + u_n for any i in {a, b}.
*
* The usage of relative masses m_i permits a convenient generalization of the algorithm
* for infinite masses, signifying masses "significantly greater" than finite masses:
*
* 1) If both M_a and M_b are finite, let m_i = M_i / (M_a + M_b) for any i in {a, b}.
* 2) If M_i is finite but M_j is infinite, let m_i = 0 and m_j = 1.
* 3) If both M_a and M_b are infinite, let m_i = 1/2 for any i in {a, b}.
*/
private static void handlePhysics(Collision collision) {
// Fuck JGLM
Vec3 n = Vectors.grab3();
Vec3 v_a = Vectors.grab3();
Vec3 v_b = Vectors.grab3();
Vec3 u_n = Vectors.grab3();
Vec3 u_at = Vectors.grab3();
Vec3 u_bt = Vectors.grab3();
Vec3 du_a = Vectors.grab3();
Vec3 du_b = Vectors.grab3();
n.set(collision.wall.getWidth()).cross(collision.wall.getHeight()).normalize();
collision.a.getCollideableVelocity(v_a);
collision.b.getCollideableVelocity(v_b);
float M_a = collision.a.getCollisionMass();
float M_b = collision.b.getCollisionMass();
float m_a, m_b;
if (Float.isFinite(M_a)) {
if (Float.isFinite(M_b)) {
float sum = M_a + M_b;
m_a = M_a / sum;
m_b = M_b / sum;
} else {
m_a = 0;
m_b = 1;
}
} else {
if (Float.isFinite(M_b)) {
m_a = 1;
m_b = 0;
} else {
m_a = 0.5f;
m_b = 0.5f;
}
}
u_n.set(n).mul(m_a * Vec3.dot(v_a, n) + m_b * Vec3.dot(v_b, n));
u_at.set(n).mul(Vec3.dot(v_a, n)).negate().add(v_a);
u_bt.set(n).mul(Vec3.dot(v_b, n)).negate().add(v_b);
du_a.set(u_n).add(u_at).sub(v_a);
du_b.set(u_n).add(u_bt).sub(v_b);
collision.a.changeVelocityOnCollision(du_a);
collision.b.changeVelocityOnCollision(du_b);
separate(collision, n, m_a, m_b);
// JGML is still to fuck
Vectors.release(n);
Vectors.release(v_a);
Vectors.release(v_b);
Vectors.release(u_n);
Vectors.release(u_at);
Vectors.release(u_bt);
Vectors.release(du_a);
Vectors.release(du_b);
}
private static void separate(
Collision collision,
Vec3 normal, float aRelativeMass, float bRelativeMass
) {
final float margin = 1e-4f;
Vec3 displacement = Vectors.grab3();
displacement.set(normal).mul(margin).mul(bRelativeMass);
collision.a.moveAsCollideable(displacement);
displacement.set(normal).mul(margin).mul(aRelativeMass).negate();
collision.b.moveAsCollideable(displacement);
Vectors.release(displacement);
}
private static void advanceTime(
Collection<? extends Collideable> colls,
CollisionClock clock,
float step
) {
clock.advanceTime(step);
Vec3 tmp = Vectors.grab3();
for (Collideable coll : colls) {
coll.getCollideableVelocity(tmp);
tmp.mul(step);
coll.moveAsCollideable(tmp);
}
Vectors.release(tmp);
}
public static class ColliderWorkspace {
private final LowOverheadCache<Collision> collisionCache =
new LowOverheadCache<>(Collision::new);
AABB dummyAABB = new AABB(0, 0, 0, 1, 1, 1);
Collision grab() {
return collisionCache.grab();
}
void release(Collision object) {
collisionCache.release(object);
}
}
static class Collision {
public Collideable a;
public Collideable b;
public final CollisionWall wall = new CollisionWall(0, 0, 0, 0, 0, 0, 0, 0, 0);
/**
* Time offset from the start of the tick.
* 0 means right now, tickLength means at the end of the tick.
*/
public float time;
public Collision set(Collideable a, Collideable b, CollisionWall wall, float time) {
this.a = a;
this.b = b;
this.wall.getOrigin().set(wall.getOrigin());
this.wall.getWidth().set(wall.getWidth());
this.wall.getHeight().set(wall.getHeight());
this.time = time;
return this;
}
}
private Collider() {}
}

View File

@ -2,7 +2,10 @@ package ru.windcorp.progressia.common.util;
import java.util.function.Consumer;
import glm.mat._4.Mat4;
import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i;
import glm.vec._4.Vec4;
public class VectorUtil {
@ -25,6 +28,25 @@ public class VectorUtil {
Vectors.release(cursor);
}
public static void applyMat4(Vec3 in, Mat4 mat, Vec3 out) {
Vec4 vec4 = Vectors.grab4();
vec4.set(in, 1f);
mat.mul(vec4);
out.set(vec4.x, vec4.y, vec4.z);
Vectors.release(vec4);
}
public static void applyMat4(Vec3 inOut, Mat4 mat) {
Vec4 vec4 = Vectors.grab4();
vec4.set(inOut, 1f);
mat.mul(vec4);
inOut.set(vec4.x, vec4.y, vec4.z);
}
private VectorUtil() {}
}

View File

@ -2,9 +2,12 @@ package ru.windcorp.progressia.common.world.entity;
import glm.vec._2.Vec2;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.collision.Collideable;
import ru.windcorp.progressia.common.collision.CollisionModel;
import ru.windcorp.progressia.common.state.StatefulObject;
import ru.windcorp.progressia.common.util.Vectors;
public class EntityData extends StatefulObject {
public class EntityData extends StatefulObject implements Collideable {
private final Vec3 position = new Vec3();
private final Vec3 velocity = new Vec3();
@ -13,6 +16,8 @@ public class EntityData extends StatefulObject {
private long entityId;
private CollisionModel collisionModel = null;
private double age = 0;
public EntityData(String namespace, String name) {
@ -24,7 +29,17 @@ public class EntityData extends StatefulObject {
}
public void setPosition(Vec3 position) {
this.position.set(position);
Vec3 displacement = Vectors.grab3();
displacement.set(position).sub(getPosition());
move(displacement);
Vectors.release(displacement);
}
public void move(Vec3 displacement) {
this.position.add(displacement);
if (getCollisionModel() != null) {
getCollisionModel().moveOrigin(displacement);
}
}
public Vec3 getVelocity() {
@ -71,4 +86,38 @@ public class EntityData extends StatefulObject {
this.age += increment;
}
@Override
public CollisionModel getCollisionModel() {
return collisionModel;
}
public void setCollisionModel(CollisionModel collisionModel) {
this.collisionModel = collisionModel;
}
@Override
public boolean onCollision(Collideable other) {
return false;
}
@Override
public float getCollisionMass() {
return 1.0f;
}
@Override
public void moveAsCollideable(Vec3 displacement) {
move(displacement);
}
@Override
public void getCollideableVelocity(Vec3 output) {
output.set(getVelocity());
}
@Override
public void changeVelocityOnCollision(Vec3 velocityChange) {
getVelocity().add(velocityChange);
}
}

View File

@ -0,0 +1,20 @@
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;
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();
}
}

View File

@ -3,6 +3,8 @@ package ru.windcorp.progressia.test;
import static ru.windcorp.progressia.client.world.block.BlockRenderRegistry.getBlockTexture;
import static ru.windcorp.progressia.client.world.tile.TileRenderRegistry.getTileTexture;
import java.util.function.Consumer;
import org.lwjgl.glfw.GLFW;
import glm.vec._3.i.Vec3i;
@ -11,6 +13,7 @@ import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
import ru.windcorp.progressia.client.world.block.*;
import ru.windcorp.progressia.client.world.entity.*;
import ru.windcorp.progressia.client.world.tile.*;
import ru.windcorp.progressia.common.collision.AABB;
import ru.windcorp.progressia.common.comms.controls.*;
import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory;
import ru.windcorp.progressia.common.world.ChunkData;
@ -78,7 +81,7 @@ public class TestContent {
}
private static void registerEntities() {
registerEntityData("Test", "Javapony");
registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f)));
register(new TestEntityRenderJavapony());
register(new EntityLogic("Test", "Javapony"));
@ -125,9 +128,17 @@ public class TestContent {
}
private static void registerEntityData(
String namespace, String name
String namespace, String name,
Consumer<EntityData> transform
) {
EntityDataRegistry.getInstance().register(namespace, name);
EntityDataRegistry.getInstance().register(namespace, name, new Factory<EntityData>() {
@Override
public EntityData build() {
EntityData entity = new EntityData(namespace, name);
transform.accept(entity);
return entity;
}
});
}
private static void register(BlockRender x) {

View File

@ -1,5 +1,6 @@
package ru.windcorp.progressia.test;
import ru.windcorp.progressia.common.collision.AABB;
import ru.windcorp.progressia.common.state.IntStateField;
import ru.windcorp.progressia.common.world.entity.EntityData;
@ -10,6 +11,7 @@ public class TestEntityDataStatie extends EntityData {
public TestEntityDataStatie() {
super("Test", "Statie");
setCollisionModel(new AABB(0, 0, 0, 16f/24, 16f/24, 16f/24));
setSizeNow(16);
}
@ -21,4 +23,9 @@ public class TestEntityDataStatie extends EntityData {
this.size.setNow(this, size);
}
@Override
public float getCollisionMass() {
return 50f;
}
}