Added RelRelation and RelFace; added discrete up vector to GravityModel
This commit is contained in:
parent
acef9d32df
commit
10d271059c
@ -38,6 +38,40 @@ public class VectorUtil {
|
||||
public static enum Axis {
|
||||
X, Y, Z, W;
|
||||
}
|
||||
|
||||
public static enum SignedAxis {
|
||||
POS_X(Axis.X, +1),
|
||||
NEG_X(Axis.X, -1),
|
||||
POS_Y(Axis.Y, +1),
|
||||
NEG_Y(Axis.Y, -1),
|
||||
POS_Z(Axis.Z, +1),
|
||||
NEG_Z(Axis.Z, -1),
|
||||
POS_W(Axis.W, +1),
|
||||
NEG_W(Axis.W, -1);
|
||||
|
||||
private final Axis axis;
|
||||
private final boolean isPositive;
|
||||
|
||||
private SignedAxis(Axis axis, int sign) {
|
||||
this.axis = axis;
|
||||
this.isPositive = (sign == +1 ? true : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the axis
|
||||
*/
|
||||
public Axis getAxis() {
|
||||
return axis;
|
||||
}
|
||||
|
||||
public boolean isPositive() {
|
||||
return isPositive;
|
||||
}
|
||||
|
||||
public int getSign() {
|
||||
return isPositive ? +1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static void iterateCuboid(
|
||||
int x0,
|
||||
|
@ -20,15 +20,33 @@ package ru.windcorp.progressia.common.world;
|
||||
import java.util.Objects;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||
import ru.windcorp.progressia.common.world.rels.AbsFace;
|
||||
|
||||
/**
|
||||
* Gravity model specifies the gravitational acceleration field. A gravity model
|
||||
* may be queried for the vector of gravitational acceleration that should
|
||||
* affect an object. This vector is, generally speaking, a function of space:
|
||||
* gravity in two different locations may vary. Gravity may also be a zero
|
||||
* vector.
|
||||
* Gravity model specifies the gravitational acceleration field, the up
|
||||
* direction field and the discrete up direction field.
|
||||
* <p>
|
||||
* A gravity model may be queried for the vector of gravitational acceleration
|
||||
* that should affect an object. This vector is, generally speaking, a function
|
||||
* of space: gravity in two different locations may vary. Gravity may also be a
|
||||
* zero vector.
|
||||
* <p>
|
||||
* The vector of gravitational acceleration defines the up direction. Up vector
|
||||
* is defined as the additive inverse of the normalized gravitational
|
||||
* acceleration vector or {@code (0; 0; 0)} if there is no gravity.
|
||||
* <p>
|
||||
* Separately from the gravitational acceleration and the up vectors, a
|
||||
* <em>discrete up</em> vector field is specified by a gravity model. This field
|
||||
* is defined for each chunk uniquely and may only take the value of one of the
|
||||
* six {@linkplain AbsFace absolute directions}. This vector specifies the
|
||||
* rotation of blocks, tiles and other objects that may not have a
|
||||
* non-axis-aligned direction. Discrete up vector must be specified even for
|
||||
* chunks that have a zero or an ambiguous up direction. Although discrete up
|
||||
* direction is not technically linked to the up direction, is it expected by
|
||||
* the players that they generally align.
|
||||
*
|
||||
* @author javapony
|
||||
*/
|
||||
@ -77,11 +95,54 @@ public abstract class GravityModel extends Namespaced {
|
||||
*/
|
||||
public Vec3 getUp(Vec3 pos, Vec3 output) {
|
||||
output = getGravity(pos, output);
|
||||
if (output.any())
|
||||
|
||||
if (output.any()) {
|
||||
output.normalize().negate();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the discrete up vector for the chunk at the specified
|
||||
* coordinates.
|
||||
*
|
||||
* @param chunkPos the coordinates of chunk to compute discrete up at
|
||||
* @return an {@link AbsFace} that corresponds to the up direction in the
|
||||
* specified chunk. Never {@code null}.
|
||||
*/
|
||||
public AbsFace getDiscreteUp(Vec3i chunkPos) {
|
||||
Objects.requireNonNull(chunkPos, "chunkPos");
|
||||
|
||||
final AbsFace result;
|
||||
|
||||
try {
|
||||
result = doGetDiscreteUp(chunkPos);
|
||||
} catch (Exception e) {
|
||||
throw CrashReports.report(
|
||||
e,
|
||||
"%s failed to compute discrete up at (%d; %d; %d)",
|
||||
this,
|
||||
chunkPos.x,
|
||||
chunkPos.y,
|
||||
chunkPos.z
|
||||
);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
throw CrashReports.report(
|
||||
null,
|
||||
"%s has computed null as the discrete up at (%d; %d; %d). This is forbidden.",
|
||||
this,
|
||||
chunkPos.x,
|
||||
chunkPos.y,
|
||||
chunkPos.z
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the gravitational acceleration vector at the provided location.
|
||||
* Actual computation of gravity is delegated to this method by the other
|
||||
@ -93,4 +154,16 @@ public abstract class GravityModel extends Namespaced {
|
||||
*/
|
||||
protected abstract void doGetGravity(Vec3 pos, Vec3 output);
|
||||
|
||||
/**
|
||||
* Computes the discrete up vector for the chunk at the specified
|
||||
* coordinates. A direction must be assigned under any circumstances. Actual
|
||||
* computation of discrete up is delegated to this method by the other
|
||||
* methods in this class.
|
||||
*
|
||||
* @param chunkPos the coordinates of chunk to compute discrete up at
|
||||
* @return an {@link AbsFace} that corresponds to the up direction in the
|
||||
* specified chunk. Never {@code null}.
|
||||
*/
|
||||
protected abstract AbsFace doGetDiscreteUp(Vec3i chunkPos);
|
||||
|
||||
}
|
||||
|
@ -18,9 +18,12 @@
|
||||
|
||||
package ru.windcorp.progressia.common.world.rels;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
|
||||
public final class AbsFace extends AbsRelation {
|
||||
@ -87,6 +90,124 @@ public final class AbsFace extends AbsRelation {
|
||||
.put(POS_Y, posY)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the provided vector to one of {@link AbsFace}s. The returned face
|
||||
* is pointing in the same general direction as the provided vector. The
|
||||
* result is undefined for arguments where two largest in absolute values
|
||||
* coordinates are equal (e.g. for {@code (5; -5; 2)}). For a zero vector
|
||||
* the result is {@code null}. Infinite vectors are handled correctly.
|
||||
*
|
||||
* @param vector the vector to round
|
||||
* @return the face most adequately describing the provided vector, or
|
||||
* {@code null} iff {@code vector.x = vector.y = vector.z = 0}
|
||||
* @throws IllegalArgumentException if one of the coordinates is a NaN
|
||||
*/
|
||||
public static AbsFace roundToFace(Vec3 vector) {
|
||||
Objects.requireNonNull(vector, "vector");
|
||||
return roundToFace(vector.x, vector.y, vector.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the provided vector to one of {@link AbsFace}s. The returned face
|
||||
* is pointing in the same general direction as the provided vector. The
|
||||
* result is undefined for arguments where two largest in absolute values
|
||||
* coordinates are equal (e.g. for {@code (5; -5; 2)}). For a zero vector
|
||||
* the result is {@code null}. Infinite arguments are handled correctly.
|
||||
*
|
||||
* @param x the X coordinate
|
||||
* @param y the Y coordinate
|
||||
* @param z the Z coordinate
|
||||
* @return the face most adequately describing the provided vector, or
|
||||
* {@code null} iff {@code x = y = z = 0}
|
||||
* @throws IllegalArgumentException if one of the coordinates is a NaN
|
||||
*/
|
||||
public static AbsFace roundToFace(float x, float y, float z) {
|
||||
if (x == 0 && y == 0 && z == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Float.isNaN(x) || Float.isNaN(y) || Float.isNaN(z)) {
|
||||
throw new IllegalArgumentException("Vector contains NaN: (" + x + "; " + y + "; " + z + ")");
|
||||
}
|
||||
|
||||
// The following code handles infinite x, y or z properly
|
||||
|
||||
float absX = Math.abs(x);
|
||||
float absY = Math.abs(y);
|
||||
float absZ = Math.abs(z);
|
||||
|
||||
if (absX > absY) {
|
||||
if (absX > absZ) {
|
||||
return x > 0 ? POS_X : NEG_X;
|
||||
} else {
|
||||
// Z is the answer; exit decision tree
|
||||
}
|
||||
} else {
|
||||
if (absY > absZ) {
|
||||
return y > 0 ? POS_Y : NEG_Y;
|
||||
} else {
|
||||
// Z is the answer; exit decision tree
|
||||
}
|
||||
}
|
||||
|
||||
return z > 0 ? POS_Z : NEG_Z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the provided vector to one of {@link AbsFace}s. The returned face
|
||||
* is pointing in the same general direction as the provided vector. The
|
||||
* result is undefined for arguments where two largest in absolute values
|
||||
* coordinates are equal (e.g. for {@code (5; -5; 2)}). For a zero vector
|
||||
* the result is {@code null}.
|
||||
*
|
||||
* @param vector the vector to round
|
||||
* @return the face most adequately describing the provided vector, or
|
||||
* {@code null} iff {@code vector.x = vector.y = vector.z = 0}
|
||||
*/
|
||||
public static AbsFace roundToFace(Vec3i vector) {
|
||||
Objects.requireNonNull(vector, "vector");
|
||||
return roundToFace(vector.x, vector.y, vector.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the provided vector to one of {@link AbsFace}s. The returned face
|
||||
* is pointing in the same general direction as the provided vector. The
|
||||
* result is undefined for arguments where two largest in absolute values
|
||||
* coordinates are equal (e.g. for {@code (5; -5; 2)}). For a zero vector
|
||||
* the result is {@code null}.
|
||||
*
|
||||
* @param x the X coordinate
|
||||
* @param y the Y coordinate
|
||||
* @param z the Z coordinate
|
||||
* @return the face most adequately describing the provided vector, or
|
||||
* {@code null} iff {@code x = y = z = 0}
|
||||
*/
|
||||
public static AbsFace roundToFace(int x, int y, int z) {
|
||||
if (x == 0 && y == 0 && z == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int absX = Math.abs(x);
|
||||
int absY = Math.abs(y);
|
||||
int absZ = Math.abs(z);
|
||||
|
||||
if (absX > absY) {
|
||||
if (absX > absZ) {
|
||||
return x > 0 ? POS_X : NEG_X;
|
||||
} else {
|
||||
// Z is the answer; exit decision tree
|
||||
}
|
||||
} else {
|
||||
if (absY > absZ) {
|
||||
return y > 0 ? POS_Y : NEG_Y;
|
||||
} else {
|
||||
// Z is the answer; exit decision tree
|
||||
}
|
||||
}
|
||||
|
||||
return z > 0 ? POS_Z : NEG_Z;
|
||||
}
|
||||
|
||||
private static int nextId = 0;
|
||||
|
||||
|
@ -39,6 +39,10 @@ public abstract class BlockRelation {
|
||||
return resolve(up).getNormalized();
|
||||
}
|
||||
|
||||
protected Vec3i getSample() {
|
||||
return getVector(AbsFace.POS_Z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance between the source and destination blocks, as
|
||||
* defined by the Euclidean space. Your everyday distance.
|
||||
@ -46,7 +50,7 @@ public abstract class BlockRelation {
|
||||
* @return square root of the sum of the squares of the coordinates
|
||||
*/
|
||||
public float getEuclideanDistance() {
|
||||
return getVector(AbsFace.POS_Z).length();
|
||||
return getSample().length();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,7 +63,7 @@ public abstract class BlockRelation {
|
||||
* @return the sum of the absolute values of the coordinates
|
||||
*/
|
||||
public int getManhattanDistance() {
|
||||
Vec3i vector = getVector(AbsFace.POS_Z);
|
||||
Vec3i vector = getSample();
|
||||
return abs(vector.x) + abs(vector.y) + abs(vector.z);
|
||||
}
|
||||
|
||||
@ -71,7 +75,7 @@ public abstract class BlockRelation {
|
||||
* @return the maximum of the absolute values of the coordinates
|
||||
*/
|
||||
public int getChebyshevDistance() {
|
||||
Vec3i vector = getVector(AbsFace.POS_Z);
|
||||
Vec3i vector = getSample();
|
||||
return max(abs(vector.x), max(abs(vector.y), abs(vector.z)));
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.common.world.rels;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
|
||||
public class RelFace extends RelRelation {
|
||||
|
||||
// @formatter:off
|
||||
public static final RelFace
|
||||
UP = new RelFace( 0, 0, +1, "UP"),
|
||||
DOWN = new RelFace( 0, 0, -1, "DOWN"),
|
||||
NORTH = new RelFace(+1, 0, 0, "NORTH"),
|
||||
SOUTH = new RelFace(-1, 0, 0, "SOUTH"),
|
||||
WEST = new RelFace( 0, +1, 0, "WEST"),
|
||||
EAST = new RelFace( 0, -1, 0, "EAST");
|
||||
// @formatter:on
|
||||
|
||||
private static final ImmutableList<RelFace> ALL_FACES = ImmutableList.of(UP, DOWN, NORTH, SOUTH, WEST, EAST);
|
||||
|
||||
static {
|
||||
link(UP, DOWN);
|
||||
link(NORTH, SOUTH);
|
||||
link(WEST, EAST);
|
||||
}
|
||||
|
||||
public static ImmutableList<RelFace> getFaces() {
|
||||
return ALL_FACES;
|
||||
}
|
||||
|
||||
private static void link(RelFace a, RelFace b) {
|
||||
a.counterFace = b;
|
||||
b.counterFace = a;
|
||||
}
|
||||
|
||||
public static <E> ImmutableMap<RelFace, E> mapToFaces(
|
||||
E up,
|
||||
E down,
|
||||
E north,
|
||||
E south,
|
||||
E west,
|
||||
E east
|
||||
) {
|
||||
return ImmutableMap.<RelFace, E>builderWithExpectedSize(6)
|
||||
.put(UP, up)
|
||||
.put(DOWN, down)
|
||||
.put(NORTH, north)
|
||||
.put(SOUTH, south)
|
||||
.put(WEST, west)
|
||||
.put(EAST, east)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static int nextId = 0;
|
||||
|
||||
private final int id;
|
||||
private final String name;
|
||||
private RelFace counterFace;
|
||||
|
||||
private RelFace(int x, int y, int z, String name) {
|
||||
super(x, y, z, true);
|
||||
this.id = nextId++;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the id
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public RelFace getCounter() {
|
||||
return counterFace;
|
||||
}
|
||||
|
||||
public RelFace getCounterAndMoveCursor(Vec3i cursor) {
|
||||
cursor.add(getVector());
|
||||
return counterFace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getEuclideanDistance() {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChebyshevDistance() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getManhattanDistance() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.common.world.rels;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||
import ru.windcorp.progressia.common.util.VectorUtil.SignedAxis;
|
||||
|
||||
import static ru.windcorp.progressia.common.util.VectorUtil.SignedAxis.*;
|
||||
|
||||
/**
|
||||
* Name stands for Relative Relation
|
||||
*/
|
||||
public class RelRelation extends BlockRelation {
|
||||
|
||||
private static class Rotation {
|
||||
private final SignedAxis northDestination;
|
||||
private final SignedAxis westDestination;
|
||||
private final SignedAxis upDestination;
|
||||
|
||||
public Rotation(SignedAxis northDestination, SignedAxis westDestination, SignedAxis upDestination) {
|
||||
this.northDestination = northDestination;
|
||||
this.westDestination = westDestination;
|
||||
this.upDestination = upDestination;
|
||||
}
|
||||
|
||||
public Vec3i apply(Vec3i output, Vec3i input) {
|
||||
if (output == null) {
|
||||
return output;
|
||||
}
|
||||
|
||||
set(output, input.x, northDestination);
|
||||
set(output, input.y, westDestination);
|
||||
set(output, input.z, upDestination);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private static void set(Vec3i output, int value, SignedAxis axis) {
|
||||
VectorUtil.set(output, axis.getAxis(), axis.isPositive() ? +value : -value);
|
||||
}
|
||||
}
|
||||
|
||||
private final static Map<AbsFace, Rotation> TRANSFORMATIONS = AbsFace.mapToFaces(
|
||||
new Rotation(POS_X, POS_Y, POS_Z),
|
||||
new Rotation(POS_X, NEG_Y, NEG_Z),
|
||||
new Rotation(POS_Z, NEG_Y, POS_X),
|
||||
new Rotation(POS_Z, POS_Y, NEG_X),
|
||||
new Rotation(POS_Z, NEG_X, NEG_Y),
|
||||
new Rotation(POS_Z, POS_X, POS_Y)
|
||||
);
|
||||
|
||||
private final Vec3i vector = new Vec3i();
|
||||
private AbsRelation[] resolved = null;
|
||||
|
||||
public RelRelation(int north, int west, int up) {
|
||||
this(north, west, up, false);
|
||||
}
|
||||
|
||||
protected RelRelation(int north, int west, int up, boolean precomputeAllResolutions) {
|
||||
vector.set(north, west, up);
|
||||
|
||||
if (precomputeAllResolutions) {
|
||||
for (AbsFace face : AbsFace.getFaces()) {
|
||||
resolve(face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the relative vector (northward, westward, upward)
|
||||
*/
|
||||
public Vec3i getVector() {
|
||||
return vector;
|
||||
}
|
||||
|
||||
public int getNorthward() {
|
||||
return vector.x;
|
||||
}
|
||||
|
||||
public int getWestward() {
|
||||
return vector.y;
|
||||
}
|
||||
|
||||
public int getUpward() {
|
||||
return vector.z;
|
||||
}
|
||||
|
||||
public int getSouthward() {
|
||||
return -getNorthward();
|
||||
}
|
||||
|
||||
public int getEastward() {
|
||||
return -getWestward();
|
||||
}
|
||||
|
||||
public int getDownward() {
|
||||
return -getUpward();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbsRelation resolve(AbsFace up) {
|
||||
if (resolved == null) {
|
||||
resolved = new AbsRelation[AbsFace.BLOCK_FACE_COUNT];
|
||||
}
|
||||
|
||||
if (resolved[up.getId()] == null) {
|
||||
resolved[up.getId()] = computeResolution(up);
|
||||
}
|
||||
|
||||
return resolved[up.getId()];
|
||||
}
|
||||
|
||||
private AbsRelation computeResolution(AbsFace up) {
|
||||
return new AbsRelation(TRANSFORMATIONS.get(up).apply(new Vec3i(), vector));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vec3i getSample() {
|
||||
return getVector();
|
||||
}
|
||||
|
||||
}
|
@ -18,7 +18,9 @@
|
||||
package ru.windcorp.progressia.test.gen;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.world.GravityModel;
|
||||
import ru.windcorp.progressia.common.world.rels.AbsFace;
|
||||
|
||||
public class TestGravityModel extends GravityModel {
|
||||
|
||||
@ -37,5 +39,11 @@ public class TestGravityModel extends GravityModel {
|
||||
|
||||
output.normalize().mul(-9.8f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbsFace doGetDiscreteUp(Vec3i chunkPos) {
|
||||
AbsFace rounded = AbsFace.roundToFace(chunkPos.x, chunkPos.y, chunkPos.z - 54);
|
||||
return rounded == null ? AbsFace.POS_Z : rounded;
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user