Merge branch 'master' into audio
This commit is contained in:
commit
104d64ff9a
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)
|
||||||
|
@ -66,6 +66,18 @@ public class ArrayUtil {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isSorted(byte[] array, boolean ascending) {
|
||||||
|
for (int i = 0; i < array.length - 1; ++i) {
|
||||||
|
if (array[i] == array[i + 1]) continue;
|
||||||
|
|
||||||
|
if ((array[i] < array[i + 1]) != ascending) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static int firstIndexOf(short[] array, short element) {
|
public static int firstIndexOf(short[] array, short element) {
|
||||||
for (int i = 0; i < array.length; ++i) {
|
for (int i = 0; i < array.length; ++i) {
|
||||||
if (array[i] == element) {
|
if (array[i] == element) {
|
||||||
@ -107,6 +119,18 @@ public class ArrayUtil {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isSorted(short[] array, boolean ascending) {
|
||||||
|
for (int i = 0; i < array.length - 1; ++i) {
|
||||||
|
if (array[i] == array[i + 1]) continue;
|
||||||
|
|
||||||
|
if ((array[i] < array[i + 1]) != ascending) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static int firstIndexOf(int[] array, int element) {
|
public static int firstIndexOf(int[] array, int element) {
|
||||||
for (int i = 0; i < array.length; ++i) {
|
for (int i = 0; i < array.length; ++i) {
|
||||||
if (array[i] == element) {
|
if (array[i] == element) {
|
||||||
@ -148,6 +172,18 @@ public class ArrayUtil {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isSorted(int[] array, boolean ascending) {
|
||||||
|
for (int i = 0; i < array.length - 1; ++i) {
|
||||||
|
if (array[i] == array[i + 1]) continue;
|
||||||
|
|
||||||
|
if ((array[i] < array[i + 1]) != ascending) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static int firstIndexOf(long[] array, long element) {
|
public static int firstIndexOf(long[] array, long element) {
|
||||||
for (int i = 0; i < array.length; ++i) {
|
for (int i = 0; i < array.length; ++i) {
|
||||||
if (array[i] == element) {
|
if (array[i] == element) {
|
||||||
@ -189,6 +225,18 @@ public class ArrayUtil {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isSorted(long[] array, boolean ascending) {
|
||||||
|
for (int i = 0; i < array.length - 1; ++i) {
|
||||||
|
if (array[i] == array[i + 1]) continue;
|
||||||
|
|
||||||
|
if ((array[i] < array[i + 1]) != ascending) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static int firstIndexOf(float[] array, float element) {
|
public static int firstIndexOf(float[] array, float element) {
|
||||||
for (int i = 0; i < array.length; ++i) {
|
for (int i = 0; i < array.length; ++i) {
|
||||||
if (array[i] == element) {
|
if (array[i] == element) {
|
||||||
@ -230,6 +278,18 @@ public class ArrayUtil {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isSorted(float[] array, boolean ascending) {
|
||||||
|
for (int i = 0; i < array.length - 1; ++i) {
|
||||||
|
if (array[i] == array[i + 1]) continue;
|
||||||
|
|
||||||
|
if ((array[i] < array[i + 1]) != ascending) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static int firstIndexOf(double[] array, double element) {
|
public static int firstIndexOf(double[] array, double element) {
|
||||||
for (int i = 0; i < array.length; ++i) {
|
for (int i = 0; i < array.length; ++i) {
|
||||||
if (array[i] == element) {
|
if (array[i] == element) {
|
||||||
@ -271,6 +331,18 @@ public class ArrayUtil {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isSorted(double[] array, boolean ascending) {
|
||||||
|
for (int i = 0; i < array.length - 1; ++i) {
|
||||||
|
if (array[i] == array[i + 1]) continue;
|
||||||
|
|
||||||
|
if ((array[i] < array[i + 1]) != ascending) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static int firstIndexOf(boolean[] array, boolean element) {
|
public static int firstIndexOf(boolean[] array, boolean element) {
|
||||||
for (int i = 0; i < array.length; ++i) {
|
for (int i = 0; i < array.length; ++i) {
|
||||||
if (array[i] == element) {
|
if (array[i] == element) {
|
||||||
@ -340,6 +412,18 @@ public class ArrayUtil {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isSorted(char[] array, boolean ascending) {
|
||||||
|
for (int i = 0; i < array.length - 1; ++i) {
|
||||||
|
if (array[i] == array[i + 1]) continue;
|
||||||
|
|
||||||
|
if ((array[i] < array[i + 1]) != ascending) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static int firstIndexOf(Object[] array, Object element) {
|
public static int firstIndexOf(Object[] array, Object element) {
|
||||||
for (int i = 0; i < array.length; ++i) {
|
for (int i = 0; i < array.length; ++i) {
|
||||||
if (array[i] == element) {
|
if (array[i] == element) {
|
||||||
@ -422,6 +506,20 @@ public class ArrayUtil {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T extends Comparable<T>> boolean isSorted(T[] array, boolean ascending) {
|
||||||
|
for (int i = 0; i < array.length - 1; ++i) {
|
||||||
|
if (array[i] == array[i + 1]) continue;
|
||||||
|
|
||||||
|
int order = array[i].compareTo(array[i + 1]);
|
||||||
|
|
||||||
|
if ((order < 0) != ascending) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static long sum(byte[] array, int start, int length) {
|
public static long sum(byte[] array, int start, int length) {
|
||||||
long s = 0;
|
long s = 0;
|
||||||
length += start;
|
length += start;
|
||||||
|
@ -26,8 +26,11 @@ 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;
|
||||||
|
|
||||||
|
import ru.windcorp.jputil.ArrayUtil;
|
||||||
|
|
||||||
public class StringUtil {
|
public class StringUtil {
|
||||||
|
|
||||||
private StringUtil() {}
|
private StringUtil() {}
|
||||||
@ -368,6 +371,106 @@ public class StringUtil {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits {@code src} at index {@code at} discarding the character at that index.
|
||||||
|
* <p>
|
||||||
|
* Indices {@code 0} and {@code src.length() - 1} produce {@code str} excluding
|
||||||
|
* the specified character and {@code ""}.
|
||||||
|
* <p>
|
||||||
|
* @param src the String to split
|
||||||
|
* @param at index to split at
|
||||||
|
* @throws IllegalArgumentException if the index is out of bounds for {@code src}
|
||||||
|
* @return an array containing the substrings, in order of encounter in {@code src}.
|
||||||
|
* Its length is always 2.
|
||||||
|
*/
|
||||||
|
public static String[] splitAt(String src, int at) {
|
||||||
|
Objects.requireNonNull(src, "src");
|
||||||
|
|
||||||
|
if (at < 0) {
|
||||||
|
throw new StringIndexOutOfBoundsException(at);
|
||||||
|
} else if (at >= src.length()) {
|
||||||
|
throw new StringIndexOutOfBoundsException(at);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (at == 0) {
|
||||||
|
return new String[] {"", src.substring(1)};
|
||||||
|
} else if (at == src.length()) {
|
||||||
|
return new String[] {src.substring(0, src.length() - 1), ""};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String[] {
|
||||||
|
src.substring(0, at),
|
||||||
|
src.substring(at + 1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits {@code src} at indices {@code at} discarding characters at those indices.
|
||||||
|
* <p>
|
||||||
|
* Indices {@code 0} and {@code src.length() - 1} produce extra zero-length outputs.
|
||||||
|
* Duplicate indices produce extra zero-length outputs.
|
||||||
|
* <p>
|
||||||
|
* Examples:
|
||||||
|
* <pre>
|
||||||
|
* splitAt("a.b.c", new int[] {1, 3}) -> {"a", "b", "c"}
|
||||||
|
* splitAt("a..b", new int[] {1, 2}) -> {"a", "", "b"}
|
||||||
|
* splitAt(".b.", new int[] {0, 2}) -> {"", "b", ""}
|
||||||
|
* splitAt("a.b", new int[] {1, 1, 1}) -> {"a", "", "", "b"}
|
||||||
|
* </pre>
|
||||||
|
* @param src the String to split
|
||||||
|
* @param at indices to split at, in any order
|
||||||
|
* @throws IllegalArgumentException if some index is out of bounds for {@code src}
|
||||||
|
* @return an array containing the substrings, in order of encounter in {@code src}.
|
||||||
|
* Its length is always {@code at.length + 1}.
|
||||||
|
*/
|
||||||
|
public static String[] splitAt(String src, int... at) {
|
||||||
|
Objects.requireNonNull(src, "src");
|
||||||
|
Objects.requireNonNull(at, "at");
|
||||||
|
|
||||||
|
if (at.length == 0) return new String[] {src};
|
||||||
|
if (at.length == 1) return splitAt(src, at[0]);
|
||||||
|
|
||||||
|
int[] indices; // Always sorted
|
||||||
|
|
||||||
|
if (ArrayUtil.isSorted(at, true)) {
|
||||||
|
indices = at;
|
||||||
|
} else {
|
||||||
|
indices = at.clone();
|
||||||
|
Arrays.sort(indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indices[0] < 0) {
|
||||||
|
throw new StringIndexOutOfBoundsException(indices[0]);
|
||||||
|
} else if (indices[indices.length - 1] >= src.length()) {
|
||||||
|
throw new StringIndexOutOfBoundsException(indices[indices.length - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] result = new String[at.length + 1];
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
int resultIndex = 0;
|
||||||
|
for (int index : indices) {
|
||||||
|
int end = index;
|
||||||
|
|
||||||
|
String substring;
|
||||||
|
|
||||||
|
if (end <= start) {
|
||||||
|
// Duplicate or successive index
|
||||||
|
substring = "";
|
||||||
|
} else {
|
||||||
|
substring = src.substring(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
result[resultIndex] = substring;
|
||||||
|
resultIndex++;
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[resultIndex] = src.substring(start);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static IllegalArgumentException illegalArrayLength(int length) {
|
private static IllegalArgumentException illegalArrayLength(int length) {
|
||||||
return new IllegalArgumentException("arrayLength must be non-negative (" + length + ")");
|
return new IllegalArgumentException("arrayLength must be non-negative (" + length + ")");
|
||||||
}
|
}
|
||||||
@ -775,4 +878,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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,23 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package ru.windcorp.progressia;
|
package ru.windcorp.progressia;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.providers.OSContextProvider;
|
||||||
|
|
||||||
public class ProgressiaLauncher {
|
public class ProgressiaLauncher {
|
||||||
|
|
||||||
public static void launch(String[] args, Proxy proxy) {
|
public static void launch(String[] args, Proxy proxy) {
|
||||||
|
setupCrashReports();
|
||||||
proxy.initialize();
|
proxy.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void setupCrashReports() {
|
||||||
|
CrashReports.registerProvider(new OSContextProvider());
|
||||||
|
CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer());
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler((Thread thread, Throwable t)-> {
|
||||||
|
CrashReports.report(t,"Uncaught exception in thread %s", thread.getName());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package ru.windcorp.progressia.client;
|
|||||||
import ru.windcorp.progressia.client.comms.DefaultClientCommsListener;
|
import ru.windcorp.progressia.client.comms.DefaultClientCommsListener;
|
||||||
import ru.windcorp.progressia.client.comms.ServerCommsChannel;
|
import ru.windcorp.progressia.client.comms.ServerCommsChannel;
|
||||||
import ru.windcorp.progressia.client.graphics.world.Camera;
|
import ru.windcorp.progressia.client.graphics.world.Camera;
|
||||||
|
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
|
||||||
import ru.windcorp.progressia.client.world.WorldRender;
|
import ru.windcorp.progressia.client.world.WorldRender;
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
@ -10,7 +11,7 @@ import ru.windcorp.progressia.common.world.entity.EntityData;
|
|||||||
public class Client {
|
public class Client {
|
||||||
|
|
||||||
private final WorldRender world;
|
private final WorldRender world;
|
||||||
private EntityData localPlayer;
|
private LocalPlayer localPlayer;
|
||||||
|
|
||||||
private final Camera camera = new Camera((float) Math.toRadians(70));
|
private final Camera camera = new Camera((float) Math.toRadians(70));
|
||||||
|
|
||||||
@ -27,12 +28,12 @@ public class Client {
|
|||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityData getLocalPlayer() {
|
public LocalPlayer getLocalPlayer() {
|
||||||
return localPlayer;
|
return localPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocalPlayer(EntityData localPlayer) {
|
public void setLocalPlayer(EntityData localPlayer) {
|
||||||
this.localPlayer = localPlayer;
|
this.localPlayer = new LocalPlayer(localPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Camera getCamera() {
|
public Camera getCamera() {
|
||||||
|
@ -26,6 +26,7 @@ import ru.windcorp.progressia.client.graphics.font.Typefaces;
|
|||||||
import ru.windcorp.progressia.client.graphics.texture.Atlases;
|
import ru.windcorp.progressia.client.graphics.texture.Atlases;
|
||||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
import ru.windcorp.progressia.server.ServerState;
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
import ru.windcorp.progressia.test.TestContent;
|
import ru.windcorp.progressia.test.TestContent;
|
||||||
|
|
||||||
@ -39,8 +40,7 @@ public class ClientProxy implements Proxy {
|
|||||||
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
||||||
RenderTaskQueue.waitAndInvoke(() -> Typefaces.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz"))));
|
RenderTaskQueue.waitAndInvoke(() -> Typefaces.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz"))));
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// TODO Auto-generated catch block
|
CrashReports.report(e, "ClientProxy failed");
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TestContent.registerContent();
|
TestContent.registerContent();
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
@ -30,6 +30,8 @@ public class ClientState {
|
|||||||
|
|
||||||
Client client = new Client(world, channel);
|
Client client = new Client(world, channel);
|
||||||
|
|
||||||
|
world.tmp_generate();
|
||||||
|
|
||||||
channel.connect();
|
channel.connect();
|
||||||
|
|
||||||
setInstance(client);
|
setInstance(client);
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
package ru.windcorp.progressia.client.comms;
|
package ru.windcorp.progressia.client.comms;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ru.windcorp.jputil.chars.StringUtil;
|
||||||
import ru.windcorp.progressia.client.Client;
|
import ru.windcorp.progressia.client.Client;
|
||||||
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
|
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
|
||||||
import ru.windcorp.progressia.client.world.ChunkRender;
|
|
||||||
import ru.windcorp.progressia.common.comms.CommsListener;
|
import ru.windcorp.progressia.common.comms.CommsListener;
|
||||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||||
import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer;
|
import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer;
|
||||||
import ru.windcorp.progressia.common.comms.packets.PacketWorldChange;
|
import ru.windcorp.progressia.common.comms.packets.PacketWorldChange;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
import ru.windcorp.progressia.common.world.entity.PacketEntityChange;
|
|
||||||
|
|
||||||
// TODO refactor with no mercy
|
// TODO refactor with no mercy
|
||||||
public class DefaultClientCommsListener implements CommsListener {
|
public class DefaultClientCommsListener implements CommsListener {
|
||||||
@ -26,10 +27,6 @@ public class DefaultClientCommsListener implements CommsListener {
|
|||||||
((PacketWorldChange) packet).apply(
|
((PacketWorldChange) packet).apply(
|
||||||
getClient().getWorld().getData()
|
getClient().getWorld().getData()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!(packet instanceof PacketEntityChange)) {
|
|
||||||
tmp_reassembleWorld();
|
|
||||||
}
|
|
||||||
} else if (packet instanceof PacketSetLocalPlayer) {
|
} else if (packet instanceof PacketSetLocalPlayer) {
|
||||||
setLocalPlayer((PacketSetLocalPlayer) packet);
|
setLocalPlayer((PacketSetLocalPlayer) packet);
|
||||||
}
|
}
|
||||||
@ -41,7 +38,11 @@ public class DefaultClientCommsListener implements CommsListener {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
throw new RuntimeException("");
|
CrashReports.report(
|
||||||
|
null,
|
||||||
|
"Player entity with ID %s not found",
|
||||||
|
new String(StringUtil.toFullHex(packet.getLocalPlayerEntityId()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getClient().setLocalPlayer(entity);
|
getClient().setLocalPlayer(entity);
|
||||||
@ -50,10 +51,6 @@ public class DefaultClientCommsListener implements CommsListener {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tmp_reassembleWorld() {
|
|
||||||
getClient().getWorld().getChunks().forEach(ChunkRender::markForUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIOError(IOException reason) {
|
public void onIOError(IOException reason) {
|
||||||
// TODO implement
|
// TODO implement
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package ru.windcorp.progressia.client.comms.controls;
|
package ru.windcorp.progressia.client.comms.controls;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
|
|
||||||
public abstract class ControlTrigger extends Namespaced {
|
public abstract class ControlTrigger extends Namespaced {
|
||||||
|
|
||||||
public ControlTrigger(String namespace, String name) {
|
public ControlTrigger(String id) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,8 @@ import ru.windcorp.progressia.common.comms.controls.PacketControl;
|
|||||||
|
|
||||||
public abstract class ControlTriggerInputBased extends ControlTrigger {
|
public abstract class ControlTriggerInputBased extends ControlTrigger {
|
||||||
|
|
||||||
public ControlTriggerInputBased(String namespace, String name) {
|
public ControlTriggerInputBased(String id) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract PacketControl onInputEvent(InputEvent event);
|
public abstract PacketControl onInputEvent(InputEvent event);
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package ru.windcorp.progressia.client.comms.controls;
|
||||||
|
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
|
import ru.windcorp.progressia.common.comms.controls.ControlData;
|
||||||
|
import ru.windcorp.progressia.common.comms.controls.ControlDataRegistry;
|
||||||
|
import ru.windcorp.progressia.common.comms.controls.PacketControl;
|
||||||
|
import ru.windcorp.progressia.common.util.namespaces.NamespacedUtil;
|
||||||
|
|
||||||
|
public class ControlTriggerLambda extends ControlTriggerInputBased {
|
||||||
|
|
||||||
|
private final String packetId;
|
||||||
|
private final Predicate<InputEvent> predicate;
|
||||||
|
private final BiConsumer<InputEvent, ControlData> dataWriter;
|
||||||
|
|
||||||
|
public ControlTriggerLambda(
|
||||||
|
String id,
|
||||||
|
Predicate<InputEvent> predicate,
|
||||||
|
BiConsumer<InputEvent, ControlData> dataWriter
|
||||||
|
) {
|
||||||
|
super(id);
|
||||||
|
|
||||||
|
this.packetId = NamespacedUtil.getId(
|
||||||
|
NamespacedUtil.getNamespace(id),
|
||||||
|
"ControlKeyPress" + NamespacedUtil.getName(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.predicate = predicate;
|
||||||
|
this.dataWriter = dataWriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketControl onInputEvent(InputEvent event) {
|
||||||
|
if (!predicate.test(event)) return null;
|
||||||
|
|
||||||
|
PacketControl packet = new PacketControl(
|
||||||
|
packetId,
|
||||||
|
ControlDataRegistry.getInstance().create(getId())
|
||||||
|
);
|
||||||
|
|
||||||
|
dataWriter.accept(event, packet.getControl());
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,39 +0,0 @@
|
|||||||
package ru.windcorp.progressia.client.comms.controls;
|
|
||||||
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
|
||||||
import ru.windcorp.progressia.common.comms.controls.ControlDataRegistry;
|
|
||||||
import ru.windcorp.progressia.common.comms.controls.PacketControl;
|
|
||||||
|
|
||||||
public class ControlTriggerOnKeyPress extends ControlTriggerInputBased {
|
|
||||||
|
|
||||||
private final Predicate<KeyEvent> predicate;
|
|
||||||
private final PacketControl packet;
|
|
||||||
|
|
||||||
public ControlTriggerOnKeyPress(
|
|
||||||
String namespace, String name,
|
|
||||||
Predicate<KeyEvent> predicate
|
|
||||||
) {
|
|
||||||
super(namespace, name);
|
|
||||||
this.predicate = predicate;
|
|
||||||
this.packet = new PacketControl(
|
|
||||||
getNamespace(), "ControlKeyPress" + getName(),
|
|
||||||
ControlDataRegistry.getInstance().get(getId())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketControl onInputEvent(InputEvent event) {
|
|
||||||
if (!(event instanceof KeyEvent)) return null;
|
|
||||||
|
|
||||||
KeyEvent keyEvent = (KeyEvent) event;
|
|
||||||
|
|
||||||
if (!keyEvent.isPress()) return null;
|
|
||||||
if (!predicate.test(keyEvent)) return null;
|
|
||||||
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
package ru.windcorp.progressia.client.comms.controls;
|
package ru.windcorp.progressia.client.comms.controls;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.NamespacedRegistry;
|
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
|
||||||
|
|
||||||
public class ControlTriggerRegistry extends NamespacedRegistry<ControlTrigger> {
|
public class ControlTriggerRegistry extends NamespacedInstanceRegistry<ControlTrigger> {
|
||||||
|
|
||||||
private static final ControlTriggerRegistry INSTANCE =
|
private static final ControlTriggerRegistry INSTANCE =
|
||||||
new ControlTriggerRegistry();
|
new ControlTriggerRegistry();
|
||||||
|
@ -0,0 +1,176 @@
|
|||||||
|
package ru.windcorp.progressia.client.comms.controls;
|
||||||
|
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
|
import ru.windcorp.progressia.common.comms.controls.ControlData;
|
||||||
|
|
||||||
|
public class ControlTriggers {
|
||||||
|
|
||||||
|
public static ControlTriggerInputBased of(
|
||||||
|
String id,
|
||||||
|
BiConsumer<InputEvent, ControlData> dataWriter,
|
||||||
|
Predicate<InputEvent> predicate
|
||||||
|
) {
|
||||||
|
return new ControlTriggerLambda(id, predicate, dataWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ControlTriggerInputBased of(
|
||||||
|
String id,
|
||||||
|
Consumer<ControlData> dataWriter,
|
||||||
|
Predicate<InputEvent> predicate
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
(input, control) -> dataWriter.accept(control),
|
||||||
|
predicate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ControlTriggerInputBased of(
|
||||||
|
String id,
|
||||||
|
Predicate<InputEvent> predicate
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
(input, control) -> {},
|
||||||
|
predicate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <I extends InputEvent> ControlTriggerInputBased of(
|
||||||
|
String id,
|
||||||
|
Class<I> inputType,
|
||||||
|
BiConsumer<I, ControlData> dataWriter,
|
||||||
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
createCheckedDataWriter(inputType, dataWriter),
|
||||||
|
createCheckedCompoundPredicate(inputType, predicates)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <I extends InputEvent> ControlTriggerInputBased of(
|
||||||
|
String id,
|
||||||
|
Class<I> inputType,
|
||||||
|
Consumer<ControlData> dataWriter,
|
||||||
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
inputType,
|
||||||
|
(input, control) -> dataWriter.accept(control),
|
||||||
|
predicates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <I extends InputEvent> ControlTriggerInputBased of(
|
||||||
|
String id,
|
||||||
|
Class<I> inputType,
|
||||||
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
(input, control) -> {},
|
||||||
|
createCheckedCompoundPredicate(inputType, predicates)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static ControlTriggerInputBased of(
|
||||||
|
String id,
|
||||||
|
BiConsumer<InputEvent, ControlData> dataWriter,
|
||||||
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
InputEvent.class,
|
||||||
|
dataWriter,
|
||||||
|
predicates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <I extends InputEvent> ControlTriggerInputBased of(
|
||||||
|
String id,
|
||||||
|
Consumer<ControlData> dataWriter,
|
||||||
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
(input, control) -> dataWriter.accept(control),
|
||||||
|
predicates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static ControlTriggerInputBased of(
|
||||||
|
String id,
|
||||||
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
InputEvent.class,
|
||||||
|
(input, control) -> {},
|
||||||
|
predicates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static
|
||||||
|
<I extends InputEvent>
|
||||||
|
BiConsumer<InputEvent, ControlData>
|
||||||
|
createCheckedDataWriter(
|
||||||
|
Class<I> inputType,
|
||||||
|
BiConsumer<I, ControlData> dataWriter
|
||||||
|
) {
|
||||||
|
return (inputEvent, control) -> dataWriter.accept(inputType.cast(inputEvent), control);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static
|
||||||
|
<I extends InputEvent>
|
||||||
|
Predicate<InputEvent>
|
||||||
|
createCheckedCompoundPredicate(
|
||||||
|
Class<I> inputType,
|
||||||
|
Predicate<I>[] predicates
|
||||||
|
) {
|
||||||
|
return new CompoundCastPredicate<>(inputType, predicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CompoundCastPredicate<I extends InputEvent> implements Predicate<InputEvent> {
|
||||||
|
|
||||||
|
private final Class<I> inputType;
|
||||||
|
private final Predicate<I>[] predicates;
|
||||||
|
|
||||||
|
public CompoundCastPredicate(Class<I> inputType, Predicate<I>[] predicates) {
|
||||||
|
this.inputType = inputType;
|
||||||
|
this.predicates = predicates;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(InputEvent inputEvent) {
|
||||||
|
if (!inputType.isInstance(inputEvent)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
I castEvent = inputType.cast(inputEvent);
|
||||||
|
|
||||||
|
for (Predicate<I> predicate : predicates) {
|
||||||
|
if (!predicate.test(castEvent)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private ControlTriggers() {}
|
||||||
|
|
||||||
|
}
|
@ -24,6 +24,7 @@ import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker;
|
|||||||
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
|
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
|
||||||
import ru.windcorp.progressia.client.graphics.backend.shaders.attributes.Attribute;
|
import ru.windcorp.progressia.client.graphics.backend.shaders.attributes.Attribute;
|
||||||
import ru.windcorp.progressia.client.graphics.backend.shaders.uniforms.Uniform;
|
import ru.windcorp.progressia.client.graphics.backend.shaders.uniforms.Uniform;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class Program implements OpenGLDeletable {
|
public class Program implements OpenGLDeletable {
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ public class Program implements OpenGLDeletable {
|
|||||||
glLinkProgram(handle);
|
glLinkProgram(handle);
|
||||||
|
|
||||||
if (glGetProgrami(handle, GL_LINK_STATUS) == GL_FALSE) {
|
if (glGetProgrami(handle, GL_LINK_STATUS) == GL_FALSE) {
|
||||||
throw new RuntimeException("Bad program:\n" + glGetProgramInfoLog(handle));
|
CrashReports.report(null, "Bad program:\n%s", glGetProgramInfoLog(handle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,12 +26,12 @@ import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker;
|
|||||||
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
|
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
|
||||||
import ru.windcorp.progressia.common.resource.Resource;
|
import ru.windcorp.progressia.common.resource.Resource;
|
||||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class Shader implements OpenGLDeletable {
|
public class Shader implements OpenGLDeletable {
|
||||||
|
|
||||||
public static enum ShaderType {
|
public static enum ShaderType {
|
||||||
VERTEX(GL_VERTEX_SHADER),
|
VERTEX(GL_VERTEX_SHADER), FRAGMENT(GL_FRAGMENT_SHADER);
|
||||||
FRAGMENT(GL_FRAGMENT_SHADER);
|
|
||||||
|
|
||||||
private final int glCode;
|
private final int glCode;
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ public class Shader implements OpenGLDeletable {
|
|||||||
if (glGetShaderi(handle, GL_COMPILE_STATUS) == GL_FALSE) {
|
if (glGetShaderi(handle, GL_COMPILE_STATUS) == GL_FALSE) {
|
||||||
System.out.println("***************** ERROR ******************");
|
System.out.println("***************** ERROR ******************");
|
||||||
System.out.println(source);
|
System.out.println(source);
|
||||||
throw new RuntimeException("Bad shader:\n" + glGetShaderInfoLog(handle));
|
CrashReports.report(null, "Bad shader:\n %s", glGetShaderInfoLog(handle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
package ru.windcorp.progressia.client.graphics.backend.shaders.attributes;
|
package ru.windcorp.progressia.client.graphics.backend.shaders.attributes;
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.backend.shaders.Program;
|
import ru.windcorp.progressia.client.graphics.backend.shaders.Program;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class Attribute {
|
public class Attribute {
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ public class Attribute {
|
|||||||
|
|
||||||
public Attribute(int handle, Program program) {
|
public Attribute(int handle, Program program) {
|
||||||
if (handle < 0) {
|
if (handle < 0) {
|
||||||
throw new RuntimeException("Bad handle: " + handle);
|
CrashReports.report(null, "Bad handle: %d", handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
package ru.windcorp.progressia.client.graphics.backend.shaders.uniforms;
|
package ru.windcorp.progressia.client.graphics.backend.shaders.uniforms;
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.backend.shaders.Program;
|
import ru.windcorp.progressia.client.graphics.backend.shaders.Program;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class Uniform {
|
public class Uniform {
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ public class Uniform {
|
|||||||
|
|
||||||
public Uniform(int handle, Program program) {
|
public Uniform(int handle, Program program) {
|
||||||
if (handle < 0) {
|
if (handle < 0) {
|
||||||
throw new RuntimeException("Bad handle: " + handle);
|
CrashReports.report(null, "Bad handle: %d", handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
|
@ -21,14 +21,13 @@ import ru.windcorp.progressia.client.graphics.texture.Texture;
|
|||||||
import ru.windcorp.progressia.client.graphics.texture.TextureDataEditor;
|
import ru.windcorp.progressia.client.graphics.texture.TextureDataEditor;
|
||||||
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
|
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
|
||||||
import ru.windcorp.progressia.common.resource.Resource;
|
import ru.windcorp.progressia.common.resource.Resource;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class GNUUnifontLoader {
|
public class GNUUnifontLoader {
|
||||||
|
|
||||||
private static final AtlasGroup ATLAS_GROUP_GNU_UNIFONT =
|
private static final AtlasGroup ATLAS_GROUP_GNU_UNIFONT = new AtlasGroup("GNUUnifont", 1 << 12);
|
||||||
new AtlasGroup("GNUUnifont", 1 << 12);
|
|
||||||
|
|
||||||
private static final TextureSettings TEXTURE_SETTINGS =
|
private static final TextureSettings TEXTURE_SETTINGS = new TextureSettings(false);
|
||||||
new TextureSettings(false);
|
|
||||||
|
|
||||||
private static final int BITS_PER_HEX_DIGIT = 4;
|
private static final int BITS_PER_HEX_DIGIT = 4;
|
||||||
private static final int PREFIX_LENGTH = "0000:".length();
|
private static final int PREFIX_LENGTH = "0000:".length();
|
||||||
@ -55,29 +54,17 @@ public class GNUUnifontLoader {
|
|||||||
|
|
||||||
public static GNUUnifont load(Resource resource) {
|
public static GNUUnifont load(Resource resource) {
|
||||||
try (BufferedReader reader = createReader(resource)) {
|
try (BufferedReader reader = createReader(resource)) {
|
||||||
return createStream(reader)
|
return createStream(reader).map(GNUUnifontLoader::parse).map(GNUUnifontLoader::addToAtlas)
|
||||||
.map(GNUUnifontLoader::parse)
|
.collect(Collectors.collectingAndThen(createMapper(), GNUUnifont::new));
|
||||||
.map(GNUUnifontLoader::addToAtlas)
|
|
||||||
.collect(Collectors.collectingAndThen(
|
|
||||||
createMapper(),
|
|
||||||
GNUUnifont::new
|
|
||||||
));
|
|
||||||
} catch (IOException | UncheckedIOException e) {
|
} catch (IOException | UncheckedIOException e) {
|
||||||
throw new RuntimeException(e);
|
CrashReports.report(e, "Could not load GNUUnifont");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BufferedReader createReader(Resource resource)
|
private static BufferedReader createReader(Resource resource) throws IOException {
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
return new BufferedReader(
|
return new BufferedReader(
|
||||||
new InputStreamReader(
|
new InputStreamReader(new GZIPInputStream(resource.getInputStream()), StandardCharsets.UTF_8));
|
||||||
new GZIPInputStream(
|
|
||||||
resource.getInputStream()
|
|
||||||
),
|
|
||||||
StandardCharsets.UTF_8
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream<String> createStream(BufferedReader reader) {
|
private static Stream<String> createStream(BufferedReader reader) {
|
||||||
@ -85,38 +72,37 @@ public class GNUUnifontLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ParsedGlyph parse(String declar) {
|
private static ParsedGlyph parse(String declar) {
|
||||||
|
try {
|
||||||
|
|
||||||
int width = getWidth(declar);
|
int width = getWidth(declar);
|
||||||
checkDeclaration(declar, width);
|
checkDeclaration(declar, width);
|
||||||
|
|
||||||
char c = getChar(declar);
|
char c = getChar(declar);
|
||||||
|
|
||||||
TextureDataEditor editor = new TextureDataEditor(
|
TextureDataEditor editor = new TextureDataEditor(width, GNUUnifont.HEIGHT, width, GNUUnifont.HEIGHT,
|
||||||
width, GNUUnifont.HEIGHT,
|
TEXTURE_SETTINGS);
|
||||||
width, GNUUnifont.HEIGHT,
|
|
||||||
TEXTURE_SETTINGS
|
|
||||||
);
|
|
||||||
|
|
||||||
for (int y = 0; y < GNUUnifont.HEIGHT; ++y) {
|
for (int y = 0; y < GNUUnifont.HEIGHT; ++y) {
|
||||||
for (int x = 0; x < width; ++x) {
|
for (int x = 0; x < width; ++x) {
|
||||||
int bit = x + y * width;
|
int bit = x + y * width;
|
||||||
|
|
||||||
editor.setPixel(
|
editor.setPixel(x, GNUUnifont.HEIGHT - y - 1, getBit(declar, bit) ? 0xFFFFFFFF : 0x00000000);
|
||||||
x, GNUUnifont.HEIGHT - y - 1,
|
|
||||||
getBit(declar, bit) ? 0xFFFFFFFF : 0x00000000
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ParsedGlyph(c, editor);
|
return new ParsedGlyph(c, editor);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
CrashReports.report(e, "Could not load GNUUnifont: could not load character \"%s\"", declar);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static char getChar(String declar) {
|
private static char getChar(String declar) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
result =
|
result = (result << BITS_PER_HEX_DIGIT) | getHexValue(declar.charAt(i));
|
||||||
(result << BITS_PER_HEX_DIGIT) |
|
|
||||||
getHexValue(declar.charAt(i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (char) result;
|
return (char) result;
|
||||||
@ -138,35 +124,39 @@ public class GNUUnifontLoader {
|
|||||||
return meaningfulChars / charsPerColumn;
|
return meaningfulChars / charsPerColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkDeclaration(String declar, int width) {
|
private static void checkDeclaration(String declar, int width) throws IOException {
|
||||||
if (!GNUUnifont.WIDTHS.contains(width)) {
|
if (!GNUUnifont.WIDTHS.contains(width)) {
|
||||||
throw new RuntimeException("Width " + width + " is not supported (in declar \"" + declar + "\")");
|
throw new IOException("Width " + width + " is not supported (in declar \"" + declar + "\")");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((declar.length() - PREFIX_LENGTH) % width != 0) {
|
if ((declar.length() - PREFIX_LENGTH) % width != 0) {
|
||||||
throw new RuntimeException("Declar \"" + declar + "\" has invalid length");
|
throw new IOException("Declar \"" + declar + "\" has invalid length");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < declar.length(); ++i) {
|
for (int i = 0; i < declar.length(); ++i) {
|
||||||
if (i == BITS_PER_HEX_DIGIT) {
|
if (i == BITS_PER_HEX_DIGIT) {
|
||||||
if (declar.charAt(i) != ':') {
|
if (declar.charAt(i) != ':') {
|
||||||
throw new RuntimeException("No colon ':' found in declar \"" + declar + "\" at index 4");
|
throw new IOException("No colon ':' found in declar \"" + declar + "\" at index 4");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
char c = declar.charAt(i);
|
char c = declar.charAt(i);
|
||||||
|
|
||||||
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))) {
|
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))) {
|
||||||
throw new RuntimeException("Illegal char in declar \"" + declar + "\" at index " + i + "; expected 0-9A-F");
|
throw new IOException("Illegal char in declar \"" + declar + "\" at index " + i + "; expected 0-9A-F");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getHexValue(char digit) {
|
private static int getHexValue(char digit) {
|
||||||
if (digit < '0') throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
if (digit < '0')
|
||||||
if (digit <= '9') return digit - '0';
|
throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
||||||
if (digit < 'A') throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
if (digit <= '9')
|
||||||
if (digit <= 'F') return digit - 'A' + 0xA;
|
return digit - '0';
|
||||||
|
if (digit < 'A')
|
||||||
|
throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
||||||
|
if (digit <= 'F')
|
||||||
|
return digit - 'A' + 0xA;
|
||||||
throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,11 +165,8 @@ public class GNUUnifontLoader {
|
|||||||
return new AtlasGlyph(glyph.c, new SimpleTexture(sprite));
|
return new AtlasGlyph(glyph.c, new SimpleTexture(sprite));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static
|
private static Collector<AtlasGlyph, ?, TCharObjectMap<Texture>> createMapper() {
|
||||||
Collector<AtlasGlyph, ?, TCharObjectMap<Texture>>
|
return Collector.of(TCharObjectHashMap<Texture>::new,
|
||||||
createMapper() {
|
|
||||||
return Collector.of(
|
|
||||||
TCharObjectHashMap<Texture>::new,
|
|
||||||
|
|
||||||
(map, glyph) -> map.put(glyph.c, glyph.texture),
|
(map, glyph) -> map.put(glyph.c, glyph.texture),
|
||||||
|
|
||||||
@ -188,10 +175,10 @@ public class GNUUnifontLoader {
|
|||||||
return a;
|
return a;
|
||||||
},
|
},
|
||||||
|
|
||||||
Characteristics.UNORDERED
|
Characteristics.UNORDERED);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private GNUUnifontLoader() {}
|
private GNUUnifontLoader() {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,11 @@ import ru.windcorp.progressia.client.graphics.input.bus.Input;
|
|||||||
import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
|
import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
|
||||||
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
|
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
|
||||||
import ru.windcorp.progressia.common.util.Named;
|
import ru.windcorp.progressia.common.util.Named;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class Component extends Named {
|
public class Component extends Named {
|
||||||
|
|
||||||
private final List<Component> children =
|
private final List<Component> children = Collections.synchronizedList(new CopyOnWriteArrayList<>());
|
||||||
Collections.synchronizedList(new CopyOnWriteArrayList<>());
|
|
||||||
|
|
||||||
private Component parent = null;
|
private Component parent = null;
|
||||||
|
|
||||||
@ -88,7 +88,8 @@ public class Component extends Named {
|
|||||||
|
|
||||||
public Component getChild(int index) {
|
public Component getChild(int index) {
|
||||||
synchronized (getChildren()) {
|
synchronized (getChildren()) {
|
||||||
if (index < 0 || index >= getChildren().size()) return null;
|
if (index < 0 || index >= getChildren().size())
|
||||||
|
return null;
|
||||||
return getChildren().get(index);
|
return getChildren().get(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,7 +108,8 @@ public class Component extends Named {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void moveChild(Component child, int newIndex) {
|
public void moveChild(Component child, int newIndex) {
|
||||||
if (newIndex == -1) newIndex = getChildren().size() - 1;
|
if (newIndex == -1)
|
||||||
|
newIndex = getChildren().size() - 1;
|
||||||
|
|
||||||
if (getChildren().remove(child)) {
|
if (getChildren().remove(child)) {
|
||||||
getChildren().add(newIndex, child);
|
getChildren().add(newIndex, child);
|
||||||
@ -123,7 +125,8 @@ public class Component extends Named {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Component addChild(Component child, int index) {
|
public Component addChild(Component child, int index) {
|
||||||
if (index == -1) index = getChildren().size();
|
if (index == -1)
|
||||||
|
index = getChildren().size();
|
||||||
|
|
||||||
invalidate();
|
invalidate();
|
||||||
getChildren().add(index, child);
|
getChildren().add(index, child);
|
||||||
@ -232,7 +235,7 @@ public class Component extends Named {
|
|||||||
|
|
||||||
valid = true;
|
valid = true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
CrashReports.report(e, "Could not layout Component %s", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,7 +248,7 @@ public class Component extends Named {
|
|||||||
try {
|
try {
|
||||||
return getLayout().calculatePreferredSize(this);
|
return getLayout().calculatePreferredSize(this);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
CrashReports.report(e, "Could not calculate preferred size for Component %s", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +345,8 @@ public class Component extends Named {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Component getNextFocusCandidate(boolean canUseChildren) {
|
private Component getNextFocusCandidate(boolean canUseChildren) {
|
||||||
if (canUseChildren) synchronized (getChildren()) {
|
if (canUseChildren)
|
||||||
|
synchronized (getChildren()) {
|
||||||
if (!getChildren().isEmpty()) {
|
if (!getChildren().isEmpty()) {
|
||||||
return getChild(0);
|
return getChild(0);
|
||||||
}
|
}
|
||||||
@ -458,20 +462,19 @@ public class Component extends Named {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void removeListener(Object listener) {
|
public void removeListener(Object listener) {
|
||||||
if (eventBus == null) return;
|
if (eventBus == null)
|
||||||
|
return;
|
||||||
eventBus.unregister(listener);
|
eventBus.unregister(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispatchEvent(Object event) {
|
public void dispatchEvent(Object event) {
|
||||||
if (eventBus == null) return;
|
if (eventBus == null)
|
||||||
|
return;
|
||||||
eventBus.post(event);
|
eventBus.post(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends InputEvent> void addListener(
|
public <T extends InputEvent> void addListener(Class<? extends T> type, boolean handlesConsumed,
|
||||||
Class<? extends T> type,
|
InputListener<T> listener) {
|
||||||
boolean handlesConsumed,
|
|
||||||
InputListener<T> listener
|
|
||||||
) {
|
|
||||||
if (inputBus == null) {
|
if (inputBus == null) {
|
||||||
inputBus = new InputBus();
|
inputBus = new InputBus();
|
||||||
}
|
}
|
||||||
@ -479,10 +482,7 @@ public class Component extends Named {
|
|||||||
inputBus.register(type, handlesConsumed, listener);
|
inputBus.register(type, handlesConsumed, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends InputEvent> void addListener(
|
public <T extends InputEvent> void addListener(Class<? extends T> type, InputListener<T> listener) {
|
||||||
Class<? extends T> type,
|
|
||||||
InputListener<T> listener
|
|
||||||
) {
|
|
||||||
if (inputBus == null) {
|
if (inputBus == null) {
|
||||||
inputBus = new InputBus();
|
inputBus = new InputBus();
|
||||||
}
|
}
|
||||||
@ -517,15 +517,17 @@ public class Component extends Named {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
CrashReports.report(e, "Could not dispatch input to Component %s", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispatchInputToFocused(Input input) {
|
private void dispatchInputToFocused(Input input) {
|
||||||
Component c = findFocused();
|
Component c = findFocused();
|
||||||
|
|
||||||
if (c == null) return;
|
if (c == null)
|
||||||
if (attemptFocusTransfer(input, c)) return;
|
return;
|
||||||
|
if (attemptFocusTransfer(input, c))
|
||||||
|
return;
|
||||||
|
|
||||||
while (c != null) {
|
while (c != null) {
|
||||||
c.handleInput(input);
|
c.handleInput(input);
|
||||||
@ -555,8 +557,10 @@ public class Component extends Named {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean attemptFocusTransfer(Input input, Component focused) {
|
private boolean attemptFocusTransfer(Input input, Component focused) {
|
||||||
if (input.isConsumed()) return false;
|
if (input.isConsumed())
|
||||||
if (!(input.getEvent() instanceof KeyEvent)) return false;
|
return false;
|
||||||
|
if (!(input.getEvent() instanceof KeyEvent))
|
||||||
|
return false;
|
||||||
|
|
||||||
KeyEvent keyInput = (KeyEvent) input.getEvent();
|
KeyEvent keyInput = (KeyEvent) input.getEvent();
|
||||||
|
|
||||||
@ -574,16 +578,11 @@ public class Component extends Named {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean contains(int x, int y) {
|
public synchronized boolean contains(int x, int y) {
|
||||||
return
|
return x >= getX() && x < getX() + getWidth() && y >= getY() && y < getY() + getHeight();
|
||||||
x >= getX() && x < getX() + getWidth() &&
|
|
||||||
y >= getY() && y < getY() + getHeight();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsCursor() {
|
public boolean containsCursor() {
|
||||||
return contains(
|
return contains((int) InputTracker.getCursorX(), (int) InputTracker.getCursorY());
|
||||||
(int) InputTracker.getCursorX(),
|
|
||||||
(int) InputTracker.getCursorY()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestReassembly() {
|
public void requestReassembly() {
|
||||||
@ -610,7 +609,7 @@ public class Component extends Named {
|
|||||||
try {
|
try {
|
||||||
assembleSelf(target);
|
assembleSelf(target);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
CrashReports.report(e, "Could not assemble Component %s", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
assembleChildren(target);
|
assembleChildren(target);
|
||||||
@ -618,7 +617,7 @@ public class Component extends Named {
|
|||||||
try {
|
try {
|
||||||
postAssembleSelf(target);
|
postAssembleSelf(target);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
CrashReports.report(e, "Post-assembly failed for Component %s", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -17,46 +17,24 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package ru.windcorp.progressia.client.graphics.input;
|
package ru.windcorp.progressia.client.graphics.input;
|
||||||
|
|
||||||
import gnu.trove.set.TIntSet;
|
import java.util.function.Predicate;
|
||||||
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
public class KeyMatcher {
|
public class KeyMatcher {
|
||||||
|
|
||||||
private final int key;
|
private final int key;
|
||||||
private final int[] additionalKeys;
|
|
||||||
private final int mods;
|
private final int mods;
|
||||||
|
|
||||||
public KeyMatcher(int key, int[] additionalKeys, int mods) {
|
protected KeyMatcher(int key, int mods) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.additionalKeys = additionalKeys;
|
|
||||||
this.mods = mods;
|
this.mods = mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyMatcher(KeyEvent template, int... additionalKeys) {
|
|
||||||
this(template.getKey(), additionalKeys, template.getMods());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeyMatcher createKeyMatcher(KeyEvent template) {
|
|
||||||
return new KeyMatcher(
|
|
||||||
template,
|
|
||||||
InputTracker.getPressedKeys().toArray()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean matches(KeyEvent event) {
|
public boolean matches(KeyEvent event) {
|
||||||
if (!event.isPress()) return false;
|
if (!event.isPress()) return false;
|
||||||
if (event.getKey() != getKey()) return false;
|
if (event.getKey() != getKey()) return false;
|
||||||
if (event.getMods() != getMods()) return false;
|
if ((event.getMods() & getMods()) != getMods()) return false;
|
||||||
|
|
||||||
TIntSet pressedKeys = InputTracker.getPressedKeys();
|
|
||||||
|
|
||||||
if (pressedKeys.size() != additionalKeys.length) return false;
|
|
||||||
|
|
||||||
for (int additionalKey : additionalKeys) {
|
|
||||||
if (!pressedKeys.contains(additionalKey)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -65,12 +43,52 @@ public class KeyMatcher {
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getAdditionalKeys() {
|
|
||||||
return additionalKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMods() {
|
public int getMods() {
|
||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static KeyMatcher.Builder of(int key) {
|
||||||
|
return new KeyMatcher.Builder(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private final int key;
|
||||||
|
private int mods = 0;
|
||||||
|
|
||||||
|
public Builder(int key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder with(int modifier) {
|
||||||
|
this.mods += modifier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withShift() {
|
||||||
|
return with(GLFW.GLFW_MOD_SHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withCtrl() {
|
||||||
|
return with(GLFW.GLFW_MOD_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withAlt() {
|
||||||
|
return with(GLFW.GLFW_MOD_ALT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withSuper() {
|
||||||
|
return with(GLFW.GLFW_MOD_SUPER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyMatcher build() {
|
||||||
|
return new KeyMatcher(key, mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Predicate<KeyEvent> matcher() {
|
||||||
|
return build()::matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,26 +31,23 @@ import gnu.trove.map.hash.TIntObjectHashMap;
|
|||||||
import gnu.trove.map.hash.TObjectIntHashMap;
|
import gnu.trove.map.hash.TObjectIntHashMap;
|
||||||
import gnu.trove.set.TIntSet;
|
import gnu.trove.set.TIntSet;
|
||||||
import gnu.trove.set.hash.TIntHashSet;
|
import gnu.trove.set.hash.TIntHashSet;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class Keys {
|
public class Keys {
|
||||||
|
|
||||||
private static final TIntObjectMap<String> CODES_TO_NAMES =
|
private static final TIntObjectMap<String> CODES_TO_NAMES = new TIntObjectHashMap<>();
|
||||||
new TIntObjectHashMap<>();
|
|
||||||
|
|
||||||
private static final TObjectIntMap<String> NAMES_TO_CODES =
|
private static final TObjectIntMap<String> NAMES_TO_CODES = new TObjectIntHashMap<>();
|
||||||
new TObjectIntHashMap<>();
|
|
||||||
|
|
||||||
private static final TIntSet MOUSE_BUTTONS = new TIntHashSet();
|
private static final TIntSet MOUSE_BUTTONS = new TIntHashSet();
|
||||||
|
|
||||||
private static final String KEY_PREFIX = "GLFW_KEY_";
|
private static final String KEY_PREFIX = "GLFW_KEY_";
|
||||||
private static final String MOUSE_BUTTON_PREFIX = "GLFW_MOUSE_BUTTON_";
|
private static final String MOUSE_BUTTON_PREFIX = "GLFW_MOUSE_BUTTON_";
|
||||||
|
|
||||||
private static final Set<String> IGNORE_FIELDS =
|
private static final Set<String> IGNORE_FIELDS = new HashSet<>(
|
||||||
new HashSet<>(Arrays.asList(
|
Arrays.asList("GLFW_KEY_UNKNOWN", "GLFW_KEY_LAST", "GLFW_MOUSE_BUTTON_LAST", "GLFW_MOUSE_BUTTON_1", // Alias
|
||||||
"GLFW_KEY_UNKNOWN",
|
// for
|
||||||
"GLFW_KEY_LAST",
|
// LEFT
|
||||||
"GLFW_MOUSE_BUTTON_LAST",
|
|
||||||
"GLFW_MOUSE_BUTTON_1", // Alias for LEFT
|
|
||||||
"GLFW_MOUSE_BUTTON_2", // Alias for RIGHT
|
"GLFW_MOUSE_BUTTON_2", // Alias for RIGHT
|
||||||
"GLFW_MOUSE_BUTTON_3" // Alias for MIDDLE
|
"GLFW_MOUSE_BUTTON_3" // Alias for MIDDLE
|
||||||
));
|
));
|
||||||
@ -63,28 +60,28 @@ public class Keys {
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
for (Field field : GLFW.class.getFields()) {
|
for (Field field : GLFW.class.getFields()) {
|
||||||
if (!Modifier.isStatic(field.getModifiers())) continue;
|
if (!Modifier.isStatic(field.getModifiers()))
|
||||||
if (!Modifier.isFinal(field.getModifiers())) continue;
|
continue;
|
||||||
|
if (!Modifier.isFinal(field.getModifiers()))
|
||||||
|
continue;
|
||||||
|
|
||||||
String name = field.getName();
|
String name = field.getName();
|
||||||
|
|
||||||
if (
|
if (!name.startsWith(KEY_PREFIX) && !name.startsWith(MOUSE_BUTTON_PREFIX))
|
||||||
!name.startsWith(KEY_PREFIX) &&
|
continue;
|
||||||
!name.startsWith(MOUSE_BUTTON_PREFIX)
|
|
||||||
) continue;
|
|
||||||
|
|
||||||
if (IGNORE_FIELDS.contains(name)) continue;
|
if (IGNORE_FIELDS.contains(name))
|
||||||
|
continue;
|
||||||
|
|
||||||
addToDictionary(field);
|
addToDictionary(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new RuntimeException(e);
|
CrashReports.report(e, "Cannot access GLFW constants");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addToDictionary(Field field)
|
private static void addToDictionary(Field field) throws IllegalAccessException {
|
||||||
throws IllegalAccessException {
|
|
||||||
|
|
||||||
int value = field.getInt(null);
|
int value = field.getInt(null);
|
||||||
String name = field.getName();
|
String name = field.getName();
|
||||||
@ -97,11 +94,8 @@ public class Keys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (CODES_TO_NAMES.containsKey(value)) {
|
if (CODES_TO_NAMES.containsKey(value)) {
|
||||||
throw new RuntimeException(
|
CrashReports.report(null, "Duplicate keys: %s and %s both map to %d(0x%s)",
|
||||||
"Duplicate keys: " + CODES_TO_NAMES.get(value) +
|
CODES_TO_NAMES.get(value), name, value, Integer.toHexString(value));
|
||||||
" and " + name + " both map to " + value +
|
|
||||||
"(0x" + Integer.toHexString(value) + ")"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CODES_TO_NAMES.put(value, name);
|
CODES_TO_NAMES.put(value, name);
|
||||||
@ -125,8 +119,7 @@ public class Keys {
|
|||||||
name = "KEYPAD_" + name.substring("KP_".length());
|
name = "KEYPAD_" + name.substring("KP_".length());
|
||||||
}
|
}
|
||||||
|
|
||||||
name = Character.toTitleCase(name.charAt(0)) +
|
name = Character.toTitleCase(name.charAt(0)) + name.substring(1).toLowerCase();
|
||||||
name.substring(1).toLowerCase();
|
|
||||||
|
|
||||||
name = name.replace('_', ' ');
|
name = name.replace('_', ' ');
|
||||||
|
|
||||||
|
@ -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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import ru.windcorp.progressia.client.graphics.backend.RenderTaskQueue;
|
|||||||
import ru.windcorp.progressia.common.resource.Resource;
|
import ru.windcorp.progressia.common.resource.Resource;
|
||||||
import ru.windcorp.progressia.common.util.BinUtil;
|
import ru.windcorp.progressia.common.util.BinUtil;
|
||||||
import ru.windcorp.progressia.common.util.Named;
|
import ru.windcorp.progressia.common.util.Named;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class Atlases {
|
public class Atlases {
|
||||||
|
|
||||||
@ -24,10 +25,7 @@ public class Atlases {
|
|||||||
this.atlasSize = atlasSize;
|
this.atlasSize = atlasSize;
|
||||||
|
|
||||||
if (!BinUtil.isPowerOf2(atlasSize)) {
|
if (!BinUtil.isPowerOf2(atlasSize)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException("Atlas size " + atlasSize + " is not a power of 2");
|
||||||
"Atlas size " + atlasSize
|
|
||||||
+ " is not a power of 2"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,11 +69,8 @@ public class Atlases {
|
|||||||
|
|
||||||
editor.draw(data, nextX, nextY);
|
editor.draw(data, nextX, nextY);
|
||||||
|
|
||||||
Sprite result = new Sprite(
|
Sprite result = new Sprite(getPrimitive(), toPrimitiveCoords(nextX, nextY),
|
||||||
getPrimitive(),
|
toPrimitiveCoords(width, height));
|
||||||
toPrimitiveCoords(nextX, nextY),
|
|
||||||
toPrimitiveCoords(width, height)
|
|
||||||
);
|
|
||||||
|
|
||||||
nextX += width;
|
nextX += width;
|
||||||
|
|
||||||
@ -99,10 +94,7 @@ public class Atlases {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Vec2 toPrimitiveCoords(int x, int y) {
|
private Vec2 toPrimitiveCoords(int x, int y) {
|
||||||
return new Vec2(
|
return new Vec2(toPrimitiveCoord(x), toPrimitiveCoord(y));
|
||||||
toPrimitiveCoord(x),
|
|
||||||
toPrimitiveCoord(y)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private float toPrimitiveCoord(int c) {
|
private float toPrimitiveCoord(int c) {
|
||||||
@ -152,10 +144,8 @@ public class Atlases {
|
|||||||
|
|
||||||
private static final TextureSettings SETTINGS = new TextureSettings(false);
|
private static final TextureSettings SETTINGS = new TextureSettings(false);
|
||||||
|
|
||||||
private static final Map<Resource, Sprite> LOADED =
|
private static final Map<Resource, Sprite> LOADED = new HashMap<>();
|
||||||
new HashMap<>();
|
private static final Multimap<AtlasGroup, Atlas> ATLASES = MultimapBuilder.hashKeys().arrayListValues().build();
|
||||||
private static final Multimap<AtlasGroup, Atlas> ATLASES =
|
|
||||||
MultimapBuilder.hashKeys().arrayListValues().build();
|
|
||||||
|
|
||||||
public static Sprite getSprite(Resource resource, AtlasGroup group) {
|
public static Sprite getSprite(Resource resource, AtlasGroup group) {
|
||||||
return LOADED.computeIfAbsent(resource, k -> loadSprite(k, group));
|
return LOADED.computeIfAbsent(resource, k -> loadSprite(k, group));
|
||||||
@ -163,12 +153,12 @@ public class Atlases {
|
|||||||
|
|
||||||
private static Sprite loadSprite(Resource resource, AtlasGroup group) {
|
private static Sprite loadSprite(Resource resource, AtlasGroup group) {
|
||||||
try {
|
try {
|
||||||
TextureDataEditor data =
|
TextureDataEditor data = TextureLoader.loadPixels(resource, SETTINGS);
|
||||||
TextureLoader.loadPixels(resource, SETTINGS);
|
|
||||||
|
|
||||||
return loadSprite(data.getData(), group);
|
return loadSprite(data.getData(), group);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
CrashReports.report(e, "Could not load sprite %s into atlas group %s", resource, group);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,14 +170,11 @@ public class Atlases {
|
|||||||
private static Atlas getReadyAtlas(AtlasGroup group, TextureData data) {
|
private static Atlas getReadyAtlas(AtlasGroup group, TextureData data) {
|
||||||
List<Atlas> atlases = (List<Atlas>) ATLASES.get(group);
|
List<Atlas> atlases = (List<Atlas>) ATLASES.get(group);
|
||||||
|
|
||||||
if (
|
if (atlases.isEmpty() || !(atlases.get(atlases.size() - 1).canAddSprite(data))) {
|
||||||
atlases.isEmpty() ||
|
|
||||||
!(atlases.get(atlases.size() - 1).canAddSprite(data))
|
|
||||||
) {
|
|
||||||
Atlas newAtlas = new Atlas(group);
|
Atlas newAtlas = new Atlas(group);
|
||||||
|
|
||||||
if (!newAtlas.canAddSprite(data)) {
|
if (!newAtlas.canAddSprite(data)) {
|
||||||
throw new RuntimeException("Could not fit tex into atlas of size " + newAtlas.getSize());
|
CrashReports.report(null, "Could not fit texture into atlas of size %d", newAtlas.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
atlases.add(newAtlas);
|
atlases.add(newAtlas);
|
||||||
@ -202,6 +189,7 @@ public class Atlases {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Atlases() {}
|
private Atlases() {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -6,6 +6,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
import ru.windcorp.progressia.common.resource.Resource;
|
import ru.windcorp.progressia.common.resource.Resource;
|
||||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class SimpleTextures {
|
public class SimpleTextures {
|
||||||
|
|
||||||
@ -30,7 +31,8 @@ public class SimpleTextures {
|
|||||||
new Sprite(new TexturePrimitive(data.getData()))
|
new Sprite(new TexturePrimitive(data.getData()))
|
||||||
);
|
);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
CrashReports.report(e, "Could not load texture %s", resource);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker;
|
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker;
|
||||||
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
|
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class TexturePrimitive implements OpenGLDeletable {
|
public class TexturePrimitive implements OpenGLDeletable {
|
||||||
|
|
||||||
@ -89,7 +90,7 @@ public class TexturePrimitive implements OpenGLDeletable {
|
|||||||
OpenGLObjectTracker.register(this);
|
OpenGLObjectTracker.register(this);
|
||||||
|
|
||||||
if (handle < 0) {
|
if (handle < 0) {
|
||||||
throw new RuntimeException("oops");
|
CrashReports.report(null, "Failed to allocate texture");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,36 @@ 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.mat._4.Mat4;
|
||||||
|
|
||||||
import glm.Glm;
|
|
||||||
import glm.mat._3.Mat3;
|
|
||||||
import glm.vec._2.Vec2;
|
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
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.client.graphics.model.Renderable;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.StaticModel;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
|
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.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,38 +70,14 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (client.getLocalPlayer() != null) {
|
||||||
|
client.getLocalPlayer().update(client.getWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
||||||
@ -128,150 +95,117 @@ 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);
|
||||||
|
tmp_drawSelectionBox();
|
||||||
|
|
||||||
|
tmp_testControls.applyPlayerControls();
|
||||||
|
|
||||||
for (EntityData data : this.client.getWorld().getData().getEntities()) {
|
for (EntityData data : this.client.getWorld().getData().getEntities()) {
|
||||||
CollisionModel model = data.getCollisionModel();
|
tmp_applyFriction(data);
|
||||||
if (model instanceof AABB) {
|
tmp_applyGravity(data, tickLength);
|
||||||
AABBRenderer.renderAABB((AABB) model, helper);
|
tmp_renderCollisionModel(data);
|
||||||
} else if (model instanceof CompoundCollisionModel) {
|
|
||||||
AABBRenderer.renderAABBsInCompound((CompoundCollisionModel) model, helper);
|
|
||||||
}
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.err.println("OLEGSHA is to blame. Tell him he vry stupiDD!!");
|
||||||
|
System.exit(31337);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void tmp_renderCollisionModel(EntityData entity) {
|
||||||
|
if (RENDER_COLLISION_MODELS) {
|
||||||
|
CollisionModelRenderer.renderCollisionModel(entity.getCollisionModel(), helper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tmp_performCollisions(float tickLength) {
|
||||||
tmp_collideableList.clear();
|
tmp_collideableList.clear();
|
||||||
tmp_collideableList.addAll(this.client.getWorld().getData().getEntities());
|
tmp_collideableList.addAll(this.client.getWorld().getData().getEntities());
|
||||||
|
|
||||||
Collider.performCollisions(
|
Collider.performCollisions(
|
||||||
tmp_collideableList,
|
tmp_collideableList,
|
||||||
new CollisionClock() {
|
this.client.getWorld().getData(),
|
||||||
private float t = 0;
|
tickLength,
|
||||||
@Override
|
|
||||||
public float getTime() {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void advanceTime(float change) {
|
|
||||||
t += change;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(float) GraphicsInterface.getFrameLength(),
|
|
||||||
tmp_colliderWorkspace
|
tmp_colliderWorkspace
|
||||||
);
|
);
|
||||||
|
|
||||||
final float frictionCoeff = 1 - 1e-2f;
|
|
||||||
|
|
||||||
for (EntityData data : this.client.getWorld().getData().getEntities()) {
|
|
||||||
data.getVelocity().mul(frictionCoeff);
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
System.exit(31337);
|
private static final Renderable SELECTION_BOX = tmp_createSelectionBox();
|
||||||
|
|
||||||
|
private void tmp_drawSelectionBox() {
|
||||||
|
LocalPlayer player = client.getLocalPlayer();
|
||||||
|
if (player == null) return;
|
||||||
|
|
||||||
|
Vec3i selection = player.getSelection().getBlock();
|
||||||
|
if (selection == null) return;
|
||||||
|
|
||||||
|
helper.pushTransform().translate(selection.x, selection.y, selection.z);
|
||||||
|
SELECTION_BOX.render(helper);
|
||||||
|
helper.popTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Renderable tmp_createSelectionBox() {
|
||||||
|
StaticModel.Builder b = StaticModel.builder();
|
||||||
|
ShapeRenderProgram p = WorldRenderProgram.getDefault();
|
||||||
|
|
||||||
|
final float f = 1e-2f;
|
||||||
|
final float scale = 1 - f/2;
|
||||||
|
final Vec3 color = new Vec3(1, 1, 1).mul(0);
|
||||||
|
|
||||||
|
for (float phi = 0; phi < 2*FloatMathUtils.PI_F; phi += FloatMathUtils.PI_F/2) {
|
||||||
|
Mat4 rot = new Mat4().identity().rotateZ(phi).scale(scale);
|
||||||
|
|
||||||
|
b.addPart(new PppBuilder(p, (Texture) null).setOrigin(
|
||||||
|
new Vec3(-f - 0.5f, -f - 0.5f, -f - 0.5f)
|
||||||
|
).setSize(f, f, 2*f + 1).setColorMultiplier(color).create(), rot);
|
||||||
|
|
||||||
|
b.addPart(new PppBuilder(p, (Texture) null).setOrigin(
|
||||||
|
new Vec3(-f - 0.5f, - 0.5f, -f - 0.5f)
|
||||||
|
).setSize(f, 1, f).setColorMultiplier(color).create(), rot);
|
||||||
|
|
||||||
|
b.addPart(new PppBuilder(p, (Texture) null).setOrigin(
|
||||||
|
new Vec3(-f - 0.5f, - 0.5f, + 0.5f)
|
||||||
|
).setSize(f, 1, f).setColorMultiplier(color).create(), rot);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StaticModel(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
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().getEntity() == 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,28 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.world;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.world.WorldRender;
|
||||||
|
import ru.windcorp.progressia.client.world.entity.EntityRenderable;
|
||||||
|
import ru.windcorp.progressia.common.world.Player;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
|
||||||
|
public class LocalPlayer extends Player {
|
||||||
|
|
||||||
|
private final Selection selection = new Selection();
|
||||||
|
|
||||||
|
public LocalPlayer(EntityData entity) {
|
||||||
|
super(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Selection getSelection() {
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(WorldRender world) {
|
||||||
|
getSelection().update(world, getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityRenderable getRenderable(WorldRender world) {
|
||||||
|
return world.getEntityRenderable(getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.world;
|
||||||
|
|
||||||
|
import glm.vec._2.Vec2;
|
||||||
|
import glm.vec._3.Vec3;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.client.world.WorldRender;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.BlockRay;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
|
||||||
|
public class Selection {
|
||||||
|
|
||||||
|
private final Vec3i block = new Vec3i();
|
||||||
|
private BlockFace surface = null;
|
||||||
|
private final Vec2 pointOnSurface = new Vec2(0.5f, 0.5f);
|
||||||
|
private final Vec3 point = new Vec3();
|
||||||
|
|
||||||
|
private boolean exists = false;
|
||||||
|
|
||||||
|
private BlockRay ray = new BlockRay();
|
||||||
|
|
||||||
|
public void update(WorldRender world, EntityData player) {
|
||||||
|
Vec3 direction = Vectors.grab3();
|
||||||
|
Vec3 start = Vectors.grab3();
|
||||||
|
|
||||||
|
player.getLookingAtVector(direction);
|
||||||
|
world.getEntityRenderable(player).getViewPoint(start);
|
||||||
|
start.add(player.getPosition());
|
||||||
|
|
||||||
|
exists = false;
|
||||||
|
|
||||||
|
for (ray.start(start, direction); ray.getDistance() < 6; ray.next()) {
|
||||||
|
Vec3i blockInWorld = ray.current();
|
||||||
|
|
||||||
|
if (world.getData().getCollisionModelOfBlock(blockInWorld) != null) {
|
||||||
|
exists = true;
|
||||||
|
block.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
|
||||||
|
ray.getPoint(point);
|
||||||
|
surface = ray.getCurrentFace();
|
||||||
|
// TODO selectedPointOnSurface
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ray.end();
|
||||||
|
|
||||||
|
Vectors.release(direction);
|
||||||
|
Vectors.release(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3i getBlock() {
|
||||||
|
return exists ? block : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3 getPoint() {
|
||||||
|
return exists ? point : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockFace getSurface() {
|
||||||
|
return exists ? surface : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec2 getPointOnSurface() {
|
||||||
|
return exists ? pointOnSurface : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean exists() {
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,6 +3,8 @@ package ru.windcorp.progressia.client.localization;
|
|||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class Localizer {
|
public class Localizer {
|
||||||
private static final Localizer INSTANCE = new Localizer("assets/languages/", "en-US");
|
private static final Localizer INSTANCE = new Localizer("assets/languages/", "en-US");
|
||||||
|
|
||||||
@ -41,7 +43,7 @@ public class Localizer {
|
|||||||
data = new Parser(langFolder + this.language + ".lang").parse();
|
data = new Parser(langFolder + this.language + ".lang").parse();
|
||||||
pokeListeners(language);
|
pokeListeners(language);
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("Language not found: " + language);
|
CrashReports.report(null, "Language not found: %s", language);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package ru.windcorp.progressia.client.localization;
|
|||||||
import ru.windcorp.jputil.chars.EscapeException;
|
import ru.windcorp.jputil.chars.EscapeException;
|
||||||
import ru.windcorp.jputil.chars.Escaper;
|
import ru.windcorp.jputil.chars.Escaper;
|
||||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
@ -80,7 +81,8 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException | EscapeException e) {
|
} catch (IOException | EscapeException e) {
|
||||||
throw new RuntimeException(e);
|
CrashReports.report(e, "Could not parse language file %s", filePath);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return parsedData;
|
return parsedData;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package ru.windcorp.progressia.client.world;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkDataListener;
|
||||||
|
|
||||||
|
class ChunkUpdateListener implements ChunkDataListener {
|
||||||
|
|
||||||
|
private final WorldRender world;
|
||||||
|
|
||||||
|
public ChunkUpdateListener(WorldRender world) {
|
||||||
|
this.world = world;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChunkChanged(ChunkData chunk) {
|
||||||
|
world.getChunk(chunk).markForUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -29,7 +29,9 @@ import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
|||||||
import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry;
|
import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry;
|
||||||
import ru.windcorp.progressia.client.world.entity.EntityRenderable;
|
import ru.windcorp.progressia.client.world.entity.EntityRenderable;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkDataListeners;
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
|
import ru.windcorp.progressia.common.world.WorldDataListener;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
|
||||||
public class WorldRender {
|
public class WorldRender {
|
||||||
@ -43,9 +45,18 @@ public class WorldRender {
|
|||||||
public WorldRender(WorldData data) {
|
public WorldRender(WorldData data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
||||||
for (ChunkData chunkData : data.getChunks()) {
|
data.addListener(ChunkDataListeners.createAdder(new ChunkUpdateListener(this)));
|
||||||
chunks.put(chunkData, new ChunkRender(this, chunkData));
|
data.addListener(new WorldDataListener() {
|
||||||
|
@Override
|
||||||
|
public void onChunkLoaded(WorldData world, ChunkData chunk) {
|
||||||
|
chunks.put(chunk, new ChunkRender(WorldRender.this, chunk));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeChunkUnloaded(WorldData world, ChunkData chunk) {
|
||||||
|
chunks.remove(chunk);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldData getData() {
|
public WorldData getData() {
|
||||||
|
@ -18,13 +18,13 @@
|
|||||||
package ru.windcorp.progressia.client.world.block;
|
package ru.windcorp.progressia.client.world.block;
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||||
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
import ru.windcorp.progressia.common.util.Namespaced;
|
|
||||||
|
|
||||||
public abstract class BlockRender extends Namespaced {
|
public abstract class BlockRender extends Namespaced {
|
||||||
|
|
||||||
public BlockRender(String namespace, String name) {
|
public BlockRender(String id) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(ShapeRenderHelper renderer) {
|
public void render(ShapeRenderHelper renderer) {
|
||||||
|
@ -22,8 +22,8 @@ import ru.windcorp.progressia.client.graphics.model.Renderable;
|
|||||||
|
|
||||||
public class BlockRenderNone extends BlockRender {
|
public class BlockRenderNone extends BlockRender {
|
||||||
|
|
||||||
public BlockRenderNone(String namespace, String name) {
|
public BlockRenderNone(String id) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,25 +23,22 @@ import ru.windcorp.progressia.common.world.block.BlockFace;
|
|||||||
public class BlockRenderOpaqueCube extends BlockRenderTexturedCube {
|
public class BlockRenderOpaqueCube extends BlockRenderTexturedCube {
|
||||||
|
|
||||||
public BlockRenderOpaqueCube(
|
public BlockRenderOpaqueCube(
|
||||||
String namespace, String name,
|
String id,
|
||||||
Texture topTexture, Texture bottomTexture,
|
Texture topTexture, Texture bottomTexture,
|
||||||
Texture northTexture, Texture southTexture,
|
Texture northTexture, Texture southTexture,
|
||||||
Texture eastTexture, Texture westTexture
|
Texture eastTexture, Texture westTexture
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
namespace, name,
|
id,
|
||||||
topTexture, bottomTexture,
|
topTexture, bottomTexture,
|
||||||
northTexture, southTexture,
|
northTexture, southTexture,
|
||||||
eastTexture, westTexture
|
eastTexture, westTexture
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockRenderOpaqueCube(
|
public BlockRenderOpaqueCube(String id, Texture texture) {
|
||||||
String namespace, String name,
|
|
||||||
Texture texture
|
|
||||||
) {
|
|
||||||
this(
|
this(
|
||||||
namespace, name,
|
id,
|
||||||
texture, texture,
|
texture, texture,
|
||||||
texture, texture,
|
texture, texture,
|
||||||
texture, texture
|
texture, texture
|
||||||
|
@ -22,9 +22,9 @@ import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup;
|
|||||||
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
|
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
|
||||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
import ru.windcorp.progressia.common.util.NamespacedRegistry;
|
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
|
||||||
|
|
||||||
public class BlockRenderRegistry extends NamespacedRegistry<BlockRender> {
|
public class BlockRenderRegistry extends NamespacedInstanceRegistry<BlockRender> {
|
||||||
|
|
||||||
private static final BlockRenderRegistry INSTANCE =
|
private static final BlockRenderRegistry INSTANCE =
|
||||||
new BlockRenderRegistry();
|
new BlockRenderRegistry();
|
||||||
|
@ -36,12 +36,12 @@ implements OpaqueCube {
|
|||||||
private final Map<BlockFace, Texture> textures = new HashMap<>();
|
private final Map<BlockFace, Texture> textures = new HashMap<>();
|
||||||
|
|
||||||
public BlockRenderTexturedCube(
|
public BlockRenderTexturedCube(
|
||||||
String namespace, String name,
|
String id,
|
||||||
Texture topTexture, Texture bottomTexture,
|
Texture topTexture, Texture bottomTexture,
|
||||||
Texture northTexture, Texture southTexture,
|
Texture northTexture, Texture southTexture,
|
||||||
Texture eastTexture, Texture westTexture
|
Texture eastTexture, Texture westTexture
|
||||||
) {
|
) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
|
|
||||||
textures.put(TOP, topTexture);
|
textures.put(TOP, topTexture);
|
||||||
textures.put(BOTTOM, bottomTexture);
|
textures.put(BOTTOM, bottomTexture);
|
||||||
|
@ -23,25 +23,22 @@ import ru.windcorp.progressia.common.world.block.BlockFace;
|
|||||||
public class BlockRenderTransparentCube extends BlockRenderTexturedCube {
|
public class BlockRenderTransparentCube extends BlockRenderTexturedCube {
|
||||||
|
|
||||||
public BlockRenderTransparentCube(
|
public BlockRenderTransparentCube(
|
||||||
String namespace, String name,
|
String id,
|
||||||
Texture topTexture, Texture bottomTexture,
|
Texture topTexture, Texture bottomTexture,
|
||||||
Texture northTexture, Texture southTexture,
|
Texture northTexture, Texture southTexture,
|
||||||
Texture eastTexture, Texture westTexture
|
Texture eastTexture, Texture westTexture
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
namespace, name,
|
id,
|
||||||
topTexture, bottomTexture,
|
topTexture, bottomTexture,
|
||||||
northTexture, southTexture,
|
northTexture, southTexture,
|
||||||
eastTexture, westTexture
|
eastTexture, westTexture
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockRenderTransparentCube(
|
public BlockRenderTransparentCube(String id, Texture texture) {
|
||||||
String namespace, String name,
|
|
||||||
Texture texture
|
|
||||||
) {
|
|
||||||
this(
|
this(
|
||||||
namespace, name,
|
id,
|
||||||
texture, texture,
|
texture, texture,
|
||||||
texture, texture,
|
texture, texture,
|
||||||
texture, texture
|
texture, texture
|
||||||
|
@ -19,21 +19,21 @@ package ru.windcorp.progressia.client.world.cro;
|
|||||||
|
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
|
|
||||||
public abstract class ChunkRenderOptimizerSupplier extends Namespaced {
|
public abstract class ChunkRenderOptimizerSupplier extends Namespaced {
|
||||||
|
|
||||||
public ChunkRenderOptimizerSupplier(String namespace, String name) {
|
public ChunkRenderOptimizerSupplier(String id) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract ChunkRenderOptimizer createOptimizer();
|
public abstract ChunkRenderOptimizer createOptimizer();
|
||||||
|
|
||||||
public static ChunkRenderOptimizerSupplier of(
|
public static ChunkRenderOptimizerSupplier of(
|
||||||
String namespace, String name,
|
String id,
|
||||||
Supplier<ChunkRenderOptimizer> supplier
|
Supplier<ChunkRenderOptimizer> supplier
|
||||||
) {
|
) {
|
||||||
return new ChunkRenderOptimizerSupplier(namespace, name) {
|
return new ChunkRenderOptimizerSupplier(id) {
|
||||||
@Override
|
@Override
|
||||||
public ChunkRenderOptimizer createOptimizer() {
|
public ChunkRenderOptimizer createOptimizer() {
|
||||||
return supplier.get();
|
return supplier.get();
|
||||||
|
@ -30,7 +30,7 @@ public class ChunkRenderOptimizers {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
register(ChunkRenderOptimizerSupplier.of(
|
register(ChunkRenderOptimizerSupplier.of(
|
||||||
"Default", "OpaqueCube",
|
"Default:OpaqueCube",
|
||||||
ChunkRenderOptimizerCube::new
|
ChunkRenderOptimizerCube::new
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package ru.windcorp.progressia.client.world.entity;
|
package ru.windcorp.progressia.client.world.entity;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
|
||||||
public abstract class EntityRender extends Namespaced {
|
public abstract class EntityRender extends Namespaced {
|
||||||
|
|
||||||
public EntityRender(String namespace, String name) {
|
public EntityRender(String id) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract EntityRenderable createRenderable(EntityData entity);
|
public abstract EntityRenderable createRenderable(EntityData entity);
|
||||||
|
@ -6,9 +6,10 @@ import ru.windcorp.progressia.client.graphics.texture.TextureLoader;
|
|||||||
import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive;
|
import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive;
|
||||||
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
|
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
|
||||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
import ru.windcorp.progressia.common.util.NamespacedRegistry;
|
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class EntityRenderRegistry extends NamespacedRegistry<EntityRender> {
|
public class EntityRenderRegistry extends NamespacedInstanceRegistry<EntityRender> {
|
||||||
|
|
||||||
private static final EntityRenderRegistry INSTANCE =
|
private static final EntityRenderRegistry INSTANCE =
|
||||||
new EntityRenderRegistry();
|
new EntityRenderRegistry();
|
||||||
@ -28,7 +29,8 @@ public class EntityRenderRegistry extends NamespacedRegistry<EntityRender> {
|
|||||||
).getData()
|
).getData()
|
||||||
);
|
);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
CrashReports.report(e, "Could not load entity texture %s", name);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
public QuadripedModel setWalkingSwing(float walkingSwing) {
|
||||||
|
this.walkingSwing = walkingSwing;
|
||||||
headPitch = Glm.clamp(
|
return this;
|
||||||
getData().getPitch(),
|
|
||||||
-head.maxPitch, head.maxPitch
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void accountForVelocity() {
|
|
||||||
Vec3 horizontal = Vectors.grab3();
|
|
||||||
horizontal.set(getData().getVelocity());
|
|
||||||
horizontal.z = 0;
|
|
||||||
|
|
||||||
velocity = horizontal.length();
|
|
||||||
|
|
||||||
evaluateVelocityCoeff();
|
|
||||||
|
|
||||||
// TODO switch to world time
|
|
||||||
walkingAnimationParameter += velocity * GraphicsInterface.getFrameLength() * 1000;
|
|
||||||
|
|
||||||
bodyYaw += velocityCoeff * normalizeAngle(
|
|
||||||
(float) (atan2(horizontal.y, horizontal.x) - bodyYaw)
|
|
||||||
) * min(1, GraphicsInterface.getFrameLength() * 10);
|
|
||||||
Vectors.release(horizontal);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void evaluateVelocityCoeff() {
|
|
||||||
if (velocity * velocityCutoff > 1) {
|
|
||||||
velocityCoeff = 1;
|
|
||||||
} else {
|
|
||||||
velocityCoeff = velocity * velocityCutoff;
|
|
||||||
velocityCoeff *= velocityCoeff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void getViewPoint(Vec3 output) {
|
|
||||||
Mat4 m = Matrices.grab4();
|
|
||||||
Vec4 v = Vectors.grab4();
|
|
||||||
|
|
||||||
m.identity()
|
|
||||||
.scale(scale)
|
|
||||||
.rotateZ(bodyYaw)
|
|
||||||
.translate(head.getTranslation())
|
|
||||||
.rotateZ(headYaw)
|
|
||||||
.rotateY(headPitch);
|
|
||||||
|
|
||||||
v.set(head.getViewPoint(), 1);
|
|
||||||
m.mul(v);
|
|
||||||
|
|
||||||
output.set(v.x, v.y, v.z);
|
|
||||||
|
|
||||||
Vectors.release(v);
|
|
||||||
Matrices.release(m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@ package ru.windcorp.progressia.client.world.tile;
|
|||||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer;
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer;
|
||||||
import ru.windcorp.progressia.common.util.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
|
||||||
public class TileRender extends Namespaced {
|
public class TileRender extends Namespaced {
|
||||||
|
|
||||||
public TileRender(String namespace, String name) {
|
public TileRender(String id) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(ShapeRenderHelper renderer, BlockFace face) {
|
public void render(ShapeRenderHelper renderer, BlockFace face) {
|
||||||
|
@ -18,10 +18,10 @@ public class TileRenderGrass extends TileRender implements OpaqueTile {
|
|||||||
private final Texture sideTexture;
|
private final Texture sideTexture;
|
||||||
|
|
||||||
public TileRenderGrass(
|
public TileRenderGrass(
|
||||||
String namespace, String name,
|
String id,
|
||||||
Texture top, Texture side
|
Texture top, Texture side
|
||||||
) {
|
) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
this.topTexture = top;
|
this.topTexture = top;
|
||||||
this.sideTexture = side;
|
this.sideTexture = side;
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,9 @@ import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup;
|
|||||||
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
|
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
|
||||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
import ru.windcorp.progressia.common.util.NamespacedRegistry;
|
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
|
||||||
|
|
||||||
public class TileRenderRegistry extends NamespacedRegistry<TileRender> {
|
public class TileRenderRegistry extends NamespacedInstanceRegistry<TileRender> {
|
||||||
|
|
||||||
private static final TileRenderRegistry INSTANCE = new TileRenderRegistry();
|
private static final TileRenderRegistry INSTANCE = new TileRenderRegistry();
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@ public class TileRenderSimple extends TileRender implements OpaqueTile {
|
|||||||
|
|
||||||
private final Texture texture;
|
private final Texture texture;
|
||||||
|
|
||||||
public TileRenderSimple(String namespace, String name, Texture texture) {
|
public TileRenderSimple(String id, Texture texture) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
this.texture = texture;
|
this.texture = texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
56
src/main/java/ru/windcorp/progressia/common/Units.java
Normal file
56
src/main/java/ru/windcorp/progressia/common/Units.java
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package ru.windcorp.progressia.common;
|
||||||
|
|
||||||
|
public class Units {
|
||||||
|
|
||||||
|
// Base units
|
||||||
|
// We're SI.
|
||||||
|
public static final float METERS = 1;
|
||||||
|
public static final float KILOGRAMS = 1;
|
||||||
|
public static final float SECONDS = 1;
|
||||||
|
|
||||||
|
// Length
|
||||||
|
public static final float CENTIMETERS = METERS / 100;
|
||||||
|
public static final float MILLIMETERS = METERS / 1000;
|
||||||
|
public static final float KILOMETERS = METERS * 1000;
|
||||||
|
|
||||||
|
// Surface
|
||||||
|
public static final float SQUARE_CENTIMETERS = CENTIMETERS * CENTIMETERS;
|
||||||
|
public static final float SQUARE_METERS = METERS * METERS;
|
||||||
|
public static final float SQUARE_MILLIMETERS = MILLIMETERS * MILLIMETERS;
|
||||||
|
public static final float SQUARE_KILOMETERS = KILOMETERS * KILOMETERS;
|
||||||
|
|
||||||
|
// Volume
|
||||||
|
public static final float CUBIC_CENTIMETERS = CENTIMETERS * CENTIMETERS * CENTIMETERS;
|
||||||
|
public static final float CUBIC_METERS = METERS * METERS * METERS;
|
||||||
|
public static final float CUBIC_MILLIMETERS = MILLIMETERS * MILLIMETERS * MILLIMETERS;
|
||||||
|
public static final float CUBIC_KILOMETERS = KILOMETERS * KILOMETERS * KILOMETERS;
|
||||||
|
|
||||||
|
// Mass
|
||||||
|
public static final float GRAMS = KILOGRAMS / 1000;
|
||||||
|
public static final float TONNES = KILOGRAMS * 1000;
|
||||||
|
|
||||||
|
// Density
|
||||||
|
public static final float KILOGRAMS_PER_CUBIC_METER = KILOGRAMS / CUBIC_METERS;
|
||||||
|
public static final float GRAMS_PER_CUBIC_CENTIMETER = GRAMS / CUBIC_CENTIMETERS;
|
||||||
|
|
||||||
|
// Time
|
||||||
|
public static final float MILLISECONDS = SECONDS / 1000;
|
||||||
|
public static final float MINUTES = SECONDS * 60;
|
||||||
|
public static final float HOURS = MINUTES * 60;
|
||||||
|
public static final float DAYS = HOURS * 24;
|
||||||
|
|
||||||
|
// Frequency
|
||||||
|
public static final float HERTZ = 1 / SECONDS;
|
||||||
|
public static final float KILOHERTZ = HERTZ * 1000;
|
||||||
|
|
||||||
|
// Velocity
|
||||||
|
public static final float METERS_PER_SECOND = METERS / SECONDS;
|
||||||
|
public static final float KILOMETERS_PER_HOUR = KILOMETERS / HOURS;
|
||||||
|
|
||||||
|
// Acceleration
|
||||||
|
public static final float METERS_PER_SECOND_SQUARED = METERS_PER_SECOND / SECONDS;
|
||||||
|
|
||||||
|
// Force
|
||||||
|
public static final float NEWTONS = METERS_PER_SECOND_SQUARED * KILOGRAMS;
|
||||||
|
|
||||||
|
}
|
@ -1,35 +1,63 @@
|
|||||||
package ru.windcorp.progressia.common.collision;
|
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(
|
||||||
@ -38,16 +66,6 @@ public class AABB implements CollisionModel {
|
|||||||
) {
|
) {
|
||||||
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,81 @@
|
|||||||
|
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 {
|
||||||
|
|
||||||
|
private static final float PADDING = 0.5f;
|
||||||
|
|
||||||
|
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) - PADDING);
|
||||||
|
pos.x <= (int) ceil(origin.x + max(0, size.x) + max(0, displacement.x) + PADDING);
|
||||||
|
pos.x += 1
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
pos.y = (int) floor(origin.y + min(0, size.y) + min(0, displacement.y) - PADDING);
|
||||||
|
pos.y <= (int) ceil(origin.y + max(0, size.y) + max(0, displacement.y) + PADDING);
|
||||||
|
pos.y += 1
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
pos.z = (int) floor(origin.z + min(0, size.z) + min(0, displacement.z) - PADDING);
|
||||||
|
pos.z <= (int) ceil(origin.z + max(0, size.z) + max(0, displacement.z) + PADDING);
|
||||||
|
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,22 +6,35 @@ 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 {
|
||||||
|
|
||||||
private static final int MAX_COLLISIONS_PER_ENTITY = 64;
|
private static final int MAX_COLLISIONS_PER_ENTITY = 64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dear Princess Celestia,
|
||||||
|
* <p>
|
||||||
|
* When {@linkplain #advanceTime(Collection, Collision, WorldData, float) advancing time},
|
||||||
|
* time step for all entities <em>except</em> currently colliding bodies is the current
|
||||||
|
* collisions's timestamp relative to now. However, currently colliding bodies
|
||||||
|
* (Collision.a and Collision.b) have a smaller time step. This is done to make sure
|
||||||
|
* they don't intersect due to rounding errors.
|
||||||
|
* <p>
|
||||||
|
* Today I learned that bad code has nothing to do with friendship, although lemme tell ya:
|
||||||
|
* it's got some dank magic.
|
||||||
|
* <p>
|
||||||
|
* Your faithful student,<br />
|
||||||
|
* Kostyl.
|
||||||
|
*/
|
||||||
|
private static final float TIME_STEP_COEFFICIENT_FOR_CURRENTLY_COLLIDING_BODIES = 1e-1f;
|
||||||
|
|
||||||
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 +50,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 +63,49 @@ public class Collider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
advanceTime(colls, clock, tickLength);
|
advanceTime(colls, null, 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 +125,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 +161,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, collision, world, collision.time);
|
||||||
|
|
||||||
boolean doNotHandle = false;
|
boolean doNotHandle = false;
|
||||||
|
|
||||||
@ -237,7 +254,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);
|
||||||
|
|
||||||
@ -274,8 +291,6 @@ public class Collider {
|
|||||||
collision.a.changeVelocityOnCollision(du_a);
|
collision.a.changeVelocityOnCollision(du_a);
|
||||||
collision.b.changeVelocityOnCollision(du_b);
|
collision.b.changeVelocityOnCollision(du_b);
|
||||||
|
|
||||||
separate(collision, n, m_a, m_b);
|
|
||||||
|
|
||||||
// JGML is still to fuck
|
// JGML is still to fuck
|
||||||
Vectors.release(n);
|
Vectors.release(n);
|
||||||
Vectors.release(v_a);
|
Vectors.release(v_a);
|
||||||
@ -287,35 +302,26 @@ public class Collider {
|
|||||||
Vectors.release(du_b);
|
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(
|
private static void advanceTime(
|
||||||
Collection<? extends Collideable> colls,
|
Collection<? extends Collideable> colls,
|
||||||
CollisionClock clock,
|
Collision exceptions,
|
||||||
|
WorldData world,
|
||||||
float step
|
float step
|
||||||
) {
|
) {
|
||||||
clock.advanceTime(step);
|
world.advanceTime(step);
|
||||||
|
|
||||||
Vec3 tmp = Vectors.grab3();
|
Vec3 tmp = Vectors.grab3();
|
||||||
|
|
||||||
for (Collideable coll : colls) {
|
for (Collideable coll : colls) {
|
||||||
coll.getCollideableVelocity(tmp);
|
coll.getCollideableVelocity(tmp);
|
||||||
tmp.mul(step);
|
|
||||||
|
float currentStep = step;
|
||||||
|
|
||||||
|
if (exceptions != null && (exceptions.a == coll || exceptions.b == coll)) {
|
||||||
|
currentStep *= TIME_STEP_COEFFICIENT_FOR_CURRENTLY_COLLIDING_BODIES;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp.mul(currentStep);
|
||||||
coll.moveAsCollideable(tmp);
|
coll.moveAsCollideable(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,6 +335,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 +345,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 +381,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;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package ru.windcorp.progressia.common.comms.controls;
|
package ru.windcorp.progressia.common.comms.controls;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
|
|
||||||
public class ControlData extends Namespaced {
|
public class ControlData extends Namespaced {
|
||||||
|
|
||||||
public ControlData(String namespace, String name) {
|
public ControlData(String id) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package ru.windcorp.progressia.common.comms.controls;
|
package ru.windcorp.progressia.common.comms.controls;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.NamespacedRegistry;
|
import ru.windcorp.progressia.common.util.namespaces.NamespacedFactoryRegistry;
|
||||||
|
|
||||||
public class ControlDataRegistry extends NamespacedRegistry<ControlData> {
|
public class ControlDataRegistry extends NamespacedFactoryRegistry<ControlData> {
|
||||||
|
|
||||||
private static final ControlDataRegistry INSTANCE = new ControlDataRegistry();
|
private static final ControlDataRegistry INSTANCE = new ControlDataRegistry();
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ public class PacketControl extends Packet {
|
|||||||
|
|
||||||
private final ControlData control;
|
private final ControlData control;
|
||||||
|
|
||||||
public PacketControl(String namespace, String name, ControlData control) {
|
public PacketControl(String id, ControlData control) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
this.control = control;
|
this.control = control;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package ru.windcorp.progressia.common.comms.packets;
|
package ru.windcorp.progressia.common.comms.packets;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
|
|
||||||
public class Packet extends Namespaced {
|
public class Packet extends Namespaced {
|
||||||
|
|
||||||
public Packet(String namespace, String name) {
|
public Packet(String id) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,11 @@ public class PacketSetLocalPlayer extends Packet {
|
|||||||
private long localPlayerEntityId;
|
private long localPlayerEntityId;
|
||||||
|
|
||||||
public PacketSetLocalPlayer(long entityId) {
|
public PacketSetLocalPlayer(long entityId) {
|
||||||
super("Core", "SetLocalPlayer");
|
this("Core:SetLocalPlayer", entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PacketSetLocalPlayer(String id, long entityId) {
|
||||||
|
super(id);
|
||||||
this.localPlayerEntityId = entityId;
|
this.localPlayerEntityId = entityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ import ru.windcorp.progressia.common.world.WorldData;
|
|||||||
|
|
||||||
public abstract class PacketWorldChange extends Packet {
|
public abstract class PacketWorldChange extends Packet {
|
||||||
|
|
||||||
public PacketWorldChange(String namespace, String name) {
|
public PacketWorldChange(String id) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void apply(WorldData world);
|
public abstract void apply(WorldData world);
|
||||||
|
@ -30,6 +30,7 @@ import com.google.common.io.CharStreams;
|
|||||||
|
|
||||||
import ru.windcorp.progressia.Progressia;
|
import ru.windcorp.progressia.Progressia;
|
||||||
import ru.windcorp.progressia.common.util.Named;
|
import ru.windcorp.progressia.common.util.Named;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class Resource extends Named {
|
public class Resource extends Named {
|
||||||
|
|
||||||
@ -50,7 +51,8 @@ public class Resource extends Named {
|
|||||||
try (Reader reader = getReader()) {
|
try (Reader reader = getReader()) {
|
||||||
return CharStreams.toString(reader);
|
return CharStreams.toString(reader);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e); // TODO handle gracefully
|
CrashReports.report(e, "Could not read resource %s as text", this);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +61,8 @@ public class Resource extends Named {
|
|||||||
try (InputStream stream = getInputStream()) {
|
try (InputStream stream = getInputStream()) {
|
||||||
byteArray = ByteStreams.toByteArray(stream);
|
byteArray = ByteStreams.toByteArray(stream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e); // TODO handle gracefully
|
CrashReports.report(e, "Could not read resource %s as bytes", this);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output == null || output.remaining() < byteArray.length) {
|
if (output == null || output.remaining() < byteArray.length) {
|
||||||
|
@ -43,10 +43,8 @@ extends AbstractStatefulObjectLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StateFieldBuilder getBuilder(String namespace, String name) {
|
public StateFieldBuilder getBuilder(String id) {
|
||||||
return new InspectingStateFieldBuilder(
|
return new InspectingStateFieldBuilder(id);
|
||||||
namespace, name
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InspectingStateFieldBuilder implements StateFieldBuilder {
|
private class InspectingStateFieldBuilder implements StateFieldBuilder {
|
||||||
@ -56,7 +54,7 @@ extends AbstractStatefulObjectLayout {
|
|||||||
@Override
|
@Override
|
||||||
public IntStateField build() {
|
public IntStateField build() {
|
||||||
return registerField(new IntStateField(
|
return registerField(new IntStateField(
|
||||||
namespace, name,
|
id,
|
||||||
isLocal,
|
isLocal,
|
||||||
fieldIndexCounters.getIntsThenIncrement()
|
fieldIndexCounters.getIntsThenIncrement()
|
||||||
));
|
));
|
||||||
@ -64,16 +62,12 @@ extends AbstractStatefulObjectLayout {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String namespace;
|
private final String id;
|
||||||
private final String name;
|
|
||||||
|
|
||||||
private boolean isLocal = true;
|
private boolean isLocal = true;
|
||||||
|
|
||||||
public InspectingStateFieldBuilder(
|
public InspectingStateFieldBuilder(String id) {
|
||||||
String namespace, String name
|
this.id = id;
|
||||||
) {
|
|
||||||
this.namespace = namespace;
|
|
||||||
this.name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -7,11 +7,11 @@ import java.io.IOException;
|
|||||||
public class IntStateField extends StateField {
|
public class IntStateField extends StateField {
|
||||||
|
|
||||||
public IntStateField(
|
public IntStateField(
|
||||||
String namespace, String name,
|
String id,
|
||||||
boolean isLocal,
|
boolean isLocal,
|
||||||
int index
|
int index
|
||||||
) {
|
) {
|
||||||
super(namespace, name, isLocal, index);
|
super(id, isLocal, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int get(StatefulObject object) {
|
public int get(StatefulObject object) {
|
||||||
|
@ -35,7 +35,7 @@ extends AbstractStatefulObjectLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StateFieldBuilder getBuilder(String namespace, String name) {
|
public StateFieldBuilder getBuilder(String id) {
|
||||||
return new RetrieverStateFieldBuilder();
|
return new RetrieverStateFieldBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import java.io.DataInput;
|
|||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
|
|
||||||
public abstract class StateField extends Namespaced {
|
public abstract class StateField extends Namespaced {
|
||||||
|
|
||||||
@ -12,11 +12,11 @@ public abstract class StateField extends Namespaced {
|
|||||||
private final int index;
|
private final int index;
|
||||||
|
|
||||||
public StateField(
|
public StateField(
|
||||||
String namespace, String name,
|
String id,
|
||||||
boolean isLocal,
|
boolean isLocal,
|
||||||
int index
|
int index
|
||||||
) {
|
) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
this.isLocal = isLocal;
|
this.isLocal = isLocal;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import java.io.DataOutput;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract class describing objects that have trackable state,
|
* An abstract class describing objects that have trackable state,
|
||||||
@ -44,10 +44,9 @@ public abstract class StatefulObject extends Namespaced {
|
|||||||
|
|
||||||
public StatefulObject(
|
public StatefulObject(
|
||||||
StatefulObjectRegistry<?> type,
|
StatefulObjectRegistry<?> type,
|
||||||
String namespace,
|
String id
|
||||||
String name
|
|
||||||
) {
|
) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
this.layout = type.getLayout(getId());
|
this.layout = type.getLayout(getId());
|
||||||
this.storage = getLayout().createStorage();
|
this.storage = getLayout().createStorage();
|
||||||
}
|
}
|
||||||
@ -96,8 +95,8 @@ public abstract class StatefulObject extends Namespaced {
|
|||||||
*
|
*
|
||||||
* @return a configured builder
|
* @return a configured builder
|
||||||
*/
|
*/
|
||||||
protected StateFieldBuilder field(String namespace, String name) {
|
protected StateFieldBuilder field(String id) {
|
||||||
StateFieldBuilder builder = getLayout().getBuilder(namespace, name);
|
StateFieldBuilder builder = getLayout().getBuilder(id);
|
||||||
|
|
||||||
builder.setOrdinal(fieldOrdinal);
|
builder.setOrdinal(fieldOrdinal);
|
||||||
fieldOrdinal++;
|
fieldOrdinal++;
|
||||||
|
@ -43,6 +43,6 @@ public abstract class StatefulObjectLayout {
|
|||||||
public abstract int computeHashCode(StatefulObject object);
|
public abstract int computeHashCode(StatefulObject object);
|
||||||
public abstract boolean areEqual(StatefulObject a, StatefulObject b);
|
public abstract boolean areEqual(StatefulObject a, StatefulObject b);
|
||||||
|
|
||||||
public abstract StateFieldBuilder getBuilder(String namespace, String name);
|
public abstract StateFieldBuilder getBuilder(String id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,8 @@ import java.util.Map;
|
|||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
import ru.windcorp.progressia.common.util.NamespacedRegistry;
|
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry-like object for identification of various {@link StatefulObject}
|
* Registry-like object for identification of various {@link StatefulObject}
|
||||||
@ -30,8 +30,8 @@ public class StatefulObjectRegistry<T extends StatefulObject> {
|
|||||||
|
|
||||||
private final AtomicBoolean isRegistered = new AtomicBoolean(false);
|
private final AtomicBoolean isRegistered = new AtomicBoolean(false);
|
||||||
|
|
||||||
public Type(String namespace, String name, Factory<T> factory) {
|
public Type(String id, Factory<T> factory) {
|
||||||
super(namespace, name);
|
super(id);
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,8 +45,8 @@ public class StatefulObjectRegistry<T extends StatefulObject> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final NamespacedRegistry<Type<T>> registry =
|
private final NamespacedInstanceRegistry<Type<T>> registry =
|
||||||
new NamespacedRegistry<Type<T>>() {
|
new NamespacedInstanceRegistry<Type<T>>() {
|
||||||
@Override
|
@Override
|
||||||
public void register(Type<T> element) {
|
public void register(Type<T> element) {
|
||||||
super.register(element);
|
super.register(element);
|
||||||
@ -91,8 +91,8 @@ public class StatefulObjectRegistry<T extends StatefulObject> {
|
|||||||
return registry.get(id).build();
|
return registry.get(id).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void register(String namespace, String name, Factory<T> factory) {
|
public void register(String id, Factory<T> factory) {
|
||||||
registry.register(new Type<>(namespace, name, factory));
|
registry.register(new Type<>(id, factory));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package ru.windcorp.progressia.common.util;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class MultiLOC {
|
||||||
|
|
||||||
|
private final Map<Class<?>, LowOverheadCache<?>> caches = new HashMap<>();
|
||||||
|
|
||||||
|
public <T> MultiLOC addClass(Class<T> clazz, Supplier<T> generator) {
|
||||||
|
caches.put(clazz, new LowOverheadCache<>(generator));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T grab(Class<T> clazz) {
|
||||||
|
return clazz.cast(caches.get(clazz).grab());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void release(Object obj) {
|
||||||
|
((LowOverheadCache<Object>) caches.get(obj.getClass())).release(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,46 +0,0 @@
|
|||||||
package ru.windcorp.progressia.common.util;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class NamespacedUtil {
|
|
||||||
|
|
||||||
public static final char SEPARATOR = ':';
|
|
||||||
public static final int MAX_PART_LENGTH = 127;
|
|
||||||
public static final int MAX_TOTAL_LENGTH = MAX_PART_LENGTH * 2 + 1;
|
|
||||||
|
|
||||||
private static final String PART_REGEX = "^[A-Z][a-zA-Z0-9]{2,}$";
|
|
||||||
|
|
||||||
private static final Predicate<String> PART_CHECKER =
|
|
||||||
Pattern.compile(PART_REGEX).asPredicate();
|
|
||||||
|
|
||||||
public static String getId(String namespace, String name) {
|
|
||||||
checkPart(namespace, "Namespace");
|
|
||||||
checkPart(name, "Name");
|
|
||||||
|
|
||||||
return namespace + SEPARATOR + name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkPart(String data, String name) {
|
|
||||||
Objects.requireNonNull(data, name);
|
|
||||||
|
|
||||||
if (data.length() > MAX_PART_LENGTH) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
name + " \"" + data + "\" is too long. "
|
|
||||||
+ "Expected at most " + MAX_PART_LENGTH
|
|
||||||
+ " characters"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PART_CHECKER.test(name)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
name + " \"" + data + "\" is invalid. "
|
|
||||||
+ "Allowed is: " + PART_REGEX
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private NamespacedUtil() {}
|
|
||||||
|
|
||||||
}
|
|
@ -3,12 +3,22 @@ package ru.windcorp.progressia.common.util;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import glm.mat._4.Mat4;
|
import glm.mat._4.Mat4;
|
||||||
|
import glm.vec._2.Vec2;
|
||||||
|
import glm.vec._2.d.Vec2d;
|
||||||
|
import glm.vec._2.i.Vec2i;
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
|
import glm.vec._3.d.Vec3d;
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import glm.vec._4.Vec4;
|
import glm.vec._4.Vec4;
|
||||||
|
import glm.vec._4.d.Vec4d;
|
||||||
|
import glm.vec._4.i.Vec4i;
|
||||||
|
|
||||||
public class VectorUtil {
|
public class VectorUtil {
|
||||||
|
|
||||||
|
public static enum Axis {
|
||||||
|
X, Y, Z, W;
|
||||||
|
}
|
||||||
|
|
||||||
public static void forEachVectorInCuboid(
|
public static void forEachVectorInCuboid(
|
||||||
int x0, int y0, int z0,
|
int x0, int y0, int z0,
|
||||||
int x1, int y1, int z1,
|
int x1, int y1, int z1,
|
||||||
@ -47,6 +57,204 @@ public class VectorUtil {
|
|||||||
inOut.set(vec4.x, vec4.y, vec4.z);
|
inOut.set(vec4.x, vec4.y, vec4.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Vec3 linearCombination(
|
||||||
|
Vec3 va, float ka,
|
||||||
|
Vec3 vb, float kb,
|
||||||
|
Vec3 output
|
||||||
|
) {
|
||||||
|
output.set(
|
||||||
|
va.x * ka + vb.x * kb,
|
||||||
|
va.y * ka + vb.y * kb,
|
||||||
|
va.z * ka + vb.z * kb
|
||||||
|
);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec3 linearCombination(
|
||||||
|
Vec3 va, float ka,
|
||||||
|
Vec3 vb, float kb,
|
||||||
|
Vec3 vc, float kc,
|
||||||
|
Vec3 output
|
||||||
|
) {
|
||||||
|
output.set(
|
||||||
|
va.x * ka + vb.x * kb + vc.x * kc,
|
||||||
|
va.y * ka + vb.y * kb + vc.y * kc,
|
||||||
|
va.z * ka + vb.z * kb + vc.z * kc
|
||||||
|
);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float get(Vec2 v, Axis a) {
|
||||||
|
switch (a) {
|
||||||
|
case X: return v.x;
|
||||||
|
case Y: return v.y;
|
||||||
|
default: throw new IllegalArgumentException("Vec2 does not have axis " + a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec2 set(Vec2 v, Axis a, float value) {
|
||||||
|
switch (a) {
|
||||||
|
case X: v.x = value; break;
|
||||||
|
case Y: v.y = value; break;
|
||||||
|
default: throw new IllegalArgumentException("Vec2 does not have axis " + a);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int get(Vec2i v, Axis a) {
|
||||||
|
switch (a) {
|
||||||
|
case X: return v.x;
|
||||||
|
case Y: return v.y;
|
||||||
|
default: throw new IllegalArgumentException("Vec2i does not have axis " + a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec2i set(Vec2i v, Axis a, int value) {
|
||||||
|
switch (a) {
|
||||||
|
case X: v.x = value; break;
|
||||||
|
case Y: v.y = value; break;
|
||||||
|
default: throw new IllegalArgumentException("Vec2i does not have axis " + a);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double get(Vec2d v, Axis a) {
|
||||||
|
switch (a) {
|
||||||
|
case X: return v.x;
|
||||||
|
case Y: return v.y;
|
||||||
|
default: throw new IllegalArgumentException("Vec2d does not have axis " + a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec2d set(Vec2d v, Axis a, double value) {
|
||||||
|
switch (a) {
|
||||||
|
case X: v.x = value; break;
|
||||||
|
case Y: v.y = value; break;
|
||||||
|
default: throw new IllegalArgumentException("Vec2d does not have axis " + a);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float get(Vec3 v, Axis a) {
|
||||||
|
switch (a) {
|
||||||
|
case X: return v.x;
|
||||||
|
case Y: return v.y;
|
||||||
|
case Z: return v.z;
|
||||||
|
default: throw new IllegalArgumentException("Vec3 does not have axis " + a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec3 set(Vec3 v, Axis a, float value) {
|
||||||
|
switch (a) {
|
||||||
|
case X: v.x = value; break;
|
||||||
|
case Y: v.y = value; break;
|
||||||
|
case Z: v.z = value; break;
|
||||||
|
default: throw new IllegalArgumentException("Vec3 does not have axis " + a);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int get(Vec3i v, Axis a) {
|
||||||
|
switch (a) {
|
||||||
|
case X: return v.x;
|
||||||
|
case Y: return v.y;
|
||||||
|
case Z: return v.z;
|
||||||
|
default: throw new IllegalArgumentException("Vec3i does not have axis " + a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec3i set(Vec3i v, Axis a, int value) {
|
||||||
|
switch (a) {
|
||||||
|
case X: v.x = value; break;
|
||||||
|
case Y: v.y = value; break;
|
||||||
|
case Z: v.z = value; break;
|
||||||
|
default: throw new IllegalArgumentException("Vec3i does not have axis " + a);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double get(Vec3d v, Axis a) {
|
||||||
|
switch (a) {
|
||||||
|
case X: return v.x;
|
||||||
|
case Y: return v.y;
|
||||||
|
case Z: return v.z;
|
||||||
|
default: throw new IllegalArgumentException("Vec3d does not have axis " + a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec3d set(Vec3d v, Axis a, double value) {
|
||||||
|
switch (a) {
|
||||||
|
case X: v.x = value; break;
|
||||||
|
case Y: v.y = value; break;
|
||||||
|
case Z: v.z = value; break;
|
||||||
|
default: throw new IllegalArgumentException("Vec3d does not have axis " + a);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float get(Vec4 v, Axis a) {
|
||||||
|
switch (a) {
|
||||||
|
case X: return v.x;
|
||||||
|
case Y: return v.y;
|
||||||
|
case Z: return v.z;
|
||||||
|
case W: return v.w;
|
||||||
|
default: throw new IllegalArgumentException("Vec4 does not have axis " + a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec4 set(Vec4 v, Axis a, float value) {
|
||||||
|
switch (a) {
|
||||||
|
case X: v.x = value; break;
|
||||||
|
case Y: v.y = value; break;
|
||||||
|
case Z: v.z = value; break;
|
||||||
|
case W: v.w = value; break;
|
||||||
|
default: throw new IllegalArgumentException("Vec4 does not have axis " + a);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int get(Vec4i v, Axis a) {
|
||||||
|
switch (a) {
|
||||||
|
case X: return v.x;
|
||||||
|
case Y: return v.y;
|
||||||
|
case Z: return v.z;
|
||||||
|
case W: return v.w;
|
||||||
|
default: throw new IllegalArgumentException("Vec4i does not have axis " + a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec4i set(Vec4i v, Axis a, int value) {
|
||||||
|
switch (a) {
|
||||||
|
case X: v.x = value; break;
|
||||||
|
case Y: v.y = value; break;
|
||||||
|
case Z: v.z = value; break;
|
||||||
|
case W: v.w = value; break;
|
||||||
|
default: throw new IllegalArgumentException("Vec4i does not have axis " + a);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double get(Vec4d v, Axis a) {
|
||||||
|
switch (a) {
|
||||||
|
case X: return v.x;
|
||||||
|
case Y: return v.y;
|
||||||
|
case Z: return v.z;
|
||||||
|
case W: return v.w;
|
||||||
|
default: throw new IllegalArgumentException("Vec4d does not have axis " + a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec4d set(Vec4d v, Axis a, double value) {
|
||||||
|
switch (a) {
|
||||||
|
case X: v.x = value; break;
|
||||||
|
case Y: v.y = value; break;
|
||||||
|
case Z: v.z = value; break;
|
||||||
|
case W: v.w = value; break;
|
||||||
|
default: throw new IllegalArgumentException("Vec4d does not have axis " + a);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
private VectorUtil() {}
|
private VectorUtil() {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package ru.windcorp.progressia.common.util.crash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A crash report utility that performs analysis of a problem during crash
|
||||||
|
* report generation and presents its conclusion to the user via the crash report.
|
||||||
|
* Unlike {@link ContextProvider}s, Analyzers are provided with the reported problem
|
||||||
|
* details.
|
||||||
|
* @see ContextProvider
|
||||||
|
* @author serega404
|
||||||
|
*/
|
||||||
|
public interface Analyzer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a human-readable string describing this analyzer's conclusion
|
||||||
|
* on the presented problem, or returns {@code null} if no conclusion
|
||||||
|
* could be made.
|
||||||
|
* @param throwable The reported throwable (may be {@code null})
|
||||||
|
* @param messageFormat A {@linkplain java.util.Formatter#syntax format string} of a
|
||||||
|
* human-readable description of the problem
|
||||||
|
* @param args The arguments for the format string
|
||||||
|
* @return a conclusion or {@code null}
|
||||||
|
*/
|
||||||
|
String analyze(Throwable throwable, String messageFormat, Object... args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this analyzer's human-readable name.
|
||||||
|
* It should be A String In Title Case With Spaces.
|
||||||
|
* @return this analyzer's name
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package ru.windcorp.progressia.common.util.crash;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A crash report utility that gathers information about game and system state
|
||||||
|
* when a crash occurs and presents it to the user via the crash report.
|
||||||
|
* ContextProviders are not aware of the nature of the problem, unlike {@link Analyzer}s.
|
||||||
|
* @see Analyzer
|
||||||
|
* @author serega404
|
||||||
|
*/
|
||||||
|
public interface ContextProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides human-readable description of the state of the game and the system.
|
||||||
|
* This information is {@link Map#put(Object, Object) put} into the provided map
|
||||||
|
* as key-value pairs. Keys are the characteristic being described, such as "OS Name",
|
||||||
|
* and should be Strings In Title Case With Spaces.
|
||||||
|
* If this provider cannot provide any information at this moment, the map is not
|
||||||
|
* modified.
|
||||||
|
* @param output the map to append output to
|
||||||
|
*/
|
||||||
|
void provideContext(Map<String, String> output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this provider's human-readable name.
|
||||||
|
* It should be A String In Title Case With Spaces.
|
||||||
|
* @return this provider's name
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
}
|
@ -0,0 +1,232 @@
|
|||||||
|
package ru.windcorp.progressia.common.util.crash;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.logging.log4j.core.util.StringBuilderWriter;
|
||||||
|
|
||||||
|
import ru.windcorp.jputil.chars.StringUtil;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class CrashReports {
|
||||||
|
|
||||||
|
private CrashReports() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Path CRASH_REPORTS_PATH = Paths.get("crash-reports");
|
||||||
|
|
||||||
|
private static final Collection<ContextProvider> PROVIDERS = Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
|
private static final Collection<Analyzer> ANALYZERS = Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger("crash");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <em>This method never returns.</em>
|
||||||
|
* <p>
|
||||||
|
* TODO document
|
||||||
|
*
|
||||||
|
* @param throwable
|
||||||
|
* @param messageFormat
|
||||||
|
* @param args
|
||||||
|
*/
|
||||||
|
public static void report(Throwable throwable, String messageFormat, Object... args) {
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
|
||||||
|
try {
|
||||||
|
String.format(messageFormat, args);
|
||||||
|
} catch (IllegalFormatException e) {
|
||||||
|
messageFormat = StringUtil.replaceAll(messageFormat, "%", "%%");
|
||||||
|
|
||||||
|
if (args.length != 0) {
|
||||||
|
messageFormat += "\nArgs:";
|
||||||
|
for (Object arg : args) {
|
||||||
|
try {
|
||||||
|
messageFormat += " \"" + arg.toString() + "\"";
|
||||||
|
} catch (Throwable t) {
|
||||||
|
messageFormat += " exc: \"" + t.getClass().toString() + "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args = new Object[0]; // clear args
|
||||||
|
}
|
||||||
|
|
||||||
|
messageFormat += "\nCould not format provided description";
|
||||||
|
}
|
||||||
|
|
||||||
|
appendContextProviders(output);
|
||||||
|
addSeparator(output);
|
||||||
|
if (appendAnalyzers(output, throwable, messageFormat, args)) {
|
||||||
|
addSeparator(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendMessageFormat(output, messageFormat, args);
|
||||||
|
|
||||||
|
appendStackTrace(output, throwable);
|
||||||
|
|
||||||
|
export(output.toString());
|
||||||
|
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendContextProviders(StringBuilder output) {
|
||||||
|
|
||||||
|
// Do a local copy to avoid deadlocks -OLEGSHA
|
||||||
|
ContextProvider[] localProvidersCopy = PROVIDERS.toArray(new ContextProvider[PROVIDERS.size()]);
|
||||||
|
|
||||||
|
for (ContextProvider provider : localProvidersCopy) {
|
||||||
|
if (provider == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
addSeparator(output);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Map<String, String> buf = new HashMap<>();
|
||||||
|
provider.provideContext(buf);
|
||||||
|
|
||||||
|
if (!buf.isEmpty()) {
|
||||||
|
output.append("Provider name: ").append(provider.getName()).append("\n");
|
||||||
|
for (Map.Entry<String, String> entry : buf.entrySet()) {
|
||||||
|
output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
output.append("\n");
|
||||||
|
|
||||||
|
String providerName;
|
||||||
|
|
||||||
|
try {
|
||||||
|
providerName = provider.getName();
|
||||||
|
} catch (Throwable t1) {
|
||||||
|
providerName = provider.getClass().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
output.append(providerName).append(" is broken").append("\n");
|
||||||
|
// ContextProvider is broken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean appendAnalyzers(StringBuilder output, Throwable throwable, String messageFormat,
|
||||||
|
Object[] args) {
|
||||||
|
boolean analyzerResponsesExist = false;
|
||||||
|
|
||||||
|
// Do a local copy to avoid deadlocks -OLEGSHA
|
||||||
|
Analyzer[] localAnalyzersCopy = ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]);
|
||||||
|
|
||||||
|
for (Analyzer analyzer : localAnalyzersCopy) {
|
||||||
|
if (analyzer == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String answer;
|
||||||
|
try {
|
||||||
|
answer = analyzer.analyze(throwable, messageFormat, args);
|
||||||
|
|
||||||
|
if (answer != null && !answer.isEmpty()) {
|
||||||
|
analyzerResponsesExist = true;
|
||||||
|
output.append(analyzer.getName()).append(": ").append(answer).append("\n");
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
analyzerResponsesExist = true;
|
||||||
|
|
||||||
|
output.append("\n");
|
||||||
|
|
||||||
|
String analyzerName;
|
||||||
|
|
||||||
|
try {
|
||||||
|
analyzerName = analyzer.getName();
|
||||||
|
} catch (Throwable t1) {
|
||||||
|
analyzerName = analyzer.getClass().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
output.append(analyzerName).append(" is broken").append("\n");
|
||||||
|
// Analyzer is broken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return analyzerResponsesExist;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendMessageFormat(StringBuilder output, String messageFormat, Object... arg) {
|
||||||
|
output.append("Provided description: \n").append(String.format(messageFormat, arg)).append("\n");
|
||||||
|
|
||||||
|
addSeparator(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendStackTrace(StringBuilder output, Throwable throwable) {
|
||||||
|
output.append("Stacktrace: \n");
|
||||||
|
|
||||||
|
if (throwable == null) {
|
||||||
|
output.append("no Throwable provided").append("\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatting to a human-readable string
|
||||||
|
Writer sink = new StringBuilderWriter(output);
|
||||||
|
try {
|
||||||
|
throwable.printStackTrace(new PrintWriter(sink));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// PLAK
|
||||||
|
}
|
||||||
|
output.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void export(String report) {
|
||||||
|
try {
|
||||||
|
LOGGER.fatal("/n" + report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// PLAK
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println(report);
|
||||||
|
|
||||||
|
generateCrashReportFiles(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void generateCrashReportFiles(String output) {
|
||||||
|
Date date = new Date();
|
||||||
|
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!Files.exists(CRASH_REPORTS_PATH))
|
||||||
|
Files.createDirectory(CRASH_REPORTS_PATH);
|
||||||
|
|
||||||
|
createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/latest.log");
|
||||||
|
createFileForCrashReport(output,
|
||||||
|
CRASH_REPORTS_PATH.toString() + "/crash-" + dateFormat.format(date) + ".log");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Crash Report not created
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createFileForCrashReport(String buffer, String filename) {
|
||||||
|
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename), StandardCharsets.UTF_8)) {
|
||||||
|
writer.write(buffer);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
// Crash Report not created
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerProvider(ContextProvider provider) {
|
||||||
|
PROVIDERS.add(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerAnalyzer(Analyzer analyzer) {
|
||||||
|
ANALYZERS.add(analyzer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addSeparator(StringBuilder sb) {
|
||||||
|
sb.append(
|
||||||
|
// 80 chars
|
||||||
|
"--------------------------------------------------------------------------------").append("\n");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package ru.windcorp.progressia.common.util.crash.analyzers;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.util.crash.Analyzer;
|
||||||
|
|
||||||
|
public class OutOfMemoryAnalyzer implements Analyzer {
|
||||||
|
@Override
|
||||||
|
public String analyze(Throwable throwable, String messageFormat, Object... args) {
|
||||||
|
if (throwable instanceof OutOfMemoryError)
|
||||||
|
return "Try to add memory to the JVM";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Out Of Memory Analyzer";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package ru.windcorp.progressia.common.util.crash.providers;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.util.crash.ContextProvider;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class OSContextProvider implements ContextProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void provideContext(Map<String, String> output) {
|
||||||
|
output.put("OS Name", System.getProperty("os.name"));
|
||||||
|
output.put("OS Version", System.getProperty("os.version"));
|
||||||
|
output.put("OS Architecture", System.getProperty("os.arch"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "OS Context Provider";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package ru.windcorp.progressia.common.util.namespaces;
|
||||||
|
|
||||||
|
public class IllegalIdException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -1572240191058305981L;
|
||||||
|
|
||||||
|
public IllegalIdException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IllegalIdException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IllegalIdException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IllegalIdException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IllegalIdException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -15,50 +15,27 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package ru.windcorp.progressia.common.util;
|
package ru.windcorp.progressia.common.util.namespaces;
|
||||||
|
|
||||||
import java.util.Objects;
|
public abstract class Namespaced {
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public abstract class Namespaced extends Named {
|
|
||||||
|
|
||||||
private static final char SEPARATOR = ':';
|
|
||||||
|
|
||||||
private static final String PART_REGEX = "^[A-Z][a-zA-Z0-9]{2,}$";
|
|
||||||
|
|
||||||
private static final Predicate<String> PART_CHECKER =
|
|
||||||
Pattern.compile(PART_REGEX).asPredicate();
|
|
||||||
|
|
||||||
private final String namespace;
|
|
||||||
private final String id;
|
private final String id;
|
||||||
|
|
||||||
public Namespaced(String namespace, String name) {
|
public Namespaced(String id) {
|
||||||
super(name);
|
NamespacedUtil.checkId(id);
|
||||||
this.namespace = Objects.requireNonNull(namespace, "namespace");
|
this.id = id;
|
||||||
this.id = namespace + SEPARATOR + name;
|
|
||||||
|
|
||||||
if (!PART_CHECKER.test(name)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Name \"" + name + "\" is invalid. "
|
|
||||||
+ "Allowed is: " + PART_REGEX
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PART_CHECKER.test(namespace)) {
|
public final String getId() {
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Namespace \"" + namespace + "\" is invalid. "
|
|
||||||
+ "Allowed is: " + PART_REGEX
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getNamespace() {
|
public String getNamespace() {
|
||||||
return namespace;
|
return NamespacedUtil.getNamespace(getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return NamespacedUtil.getName(getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -75,15 +52,10 @@ public abstract class Namespaced extends Named {
|
|||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj)
|
||||||
return true;
|
return true;
|
||||||
if (!super.equals(obj))
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
if (getClass() != obj.getClass())
|
||||||
return false;
|
return false;
|
||||||
Namespaced other = (Namespaced) obj;
|
Namespaced other = (Namespaced) obj;
|
||||||
if (id == null) {
|
if (!id.equals(other.id))
|
||||||
if (other.id != null)
|
|
||||||
return false;
|
|
||||||
} else if (!id.equals(other.id))
|
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
package ru.windcorp.progressia.common.util.namespaces;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
public class NamespacedFactoryRegistry<E extends Namespaced>
|
||||||
|
implements Map<String, NamespacedFactoryRegistry.Factory<E>> {
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public static interface Factory<E> {
|
||||||
|
E build(String id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<String, Factory<E>> backingMap =
|
||||||
|
Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
|
private final Logger logger = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
|
public void register(String id, Factory<E> element) {
|
||||||
|
if (get(id) != null) {
|
||||||
|
throw new IllegalArgumentException("ID " + id + " is already registered in " + getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Registering {} in {}", id, getClass().getSimpleName());
|
||||||
|
backingMap.put(id, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return backingMap.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return backingMap.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return backingMap.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean has(String id) {
|
||||||
|
return backingMap.containsKey(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
return backingMap.containsValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRegistered(E element) {
|
||||||
|
return has(element.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Factory<E> get(Object key) {
|
||||||
|
return backingMap.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public E create(String id) {
|
||||||
|
Factory<E> factory = get(id);
|
||||||
|
E result = factory.build(id);
|
||||||
|
if (!result.getId().equals(id)) {
|
||||||
|
throw new IllegalStateException("Requested ID " + id + " but factory " + factory + " returned an object with ID " + result.getId());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Factory<E> put(String id, Factory<E> factory) {
|
||||||
|
register(id, factory);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends String, ? extends Factory<E>> m) {
|
||||||
|
synchronized (backingMap) {
|
||||||
|
m.entrySet().forEach(e -> register(e.getKey(), e.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Factory<E> remove(Object key) {
|
||||||
|
return backingMap.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
backingMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> keySet() {
|
||||||
|
return backingMap.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Factory<E>> values() {
|
||||||
|
return backingMap.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entry<String, Factory<E>>> entrySet() {
|
||||||
|
return backingMap.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package ru.windcorp.progressia.common.util;
|
package ru.windcorp.progressia.common.util.namespaces;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -7,17 +7,20 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import com.google.errorprone.annotations.DoNotCall;
|
import com.google.errorprone.annotations.DoNotCall;
|
||||||
|
|
||||||
public class NamespacedRegistry<E extends Namespaced>
|
public class NamespacedInstanceRegistry<E extends Namespaced>
|
||||||
implements Map<String, E> {
|
implements Map<String, E> {
|
||||||
|
|
||||||
private final Map<String, E> backingMap =
|
private final Map<String, E> backingMap =
|
||||||
Collections.synchronizedMap(new HashMap<>());
|
Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
|
private final Logger logger = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
public void register(E element) {
|
public void register(E element) {
|
||||||
LogManager.getLogger(getClass()).debug("Registering " + element.getId());
|
logger.debug("Registering {} in {}", element.getId(), getClass().getSimpleName());
|
||||||
backingMap.put(element.getId(), element);
|
backingMap.put(element.getId(), element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +70,7 @@ implements Map<String, E> {
|
|||||||
@DoNotCall @Deprecated
|
@DoNotCall @Deprecated
|
||||||
public E put(String key, E value) {
|
public E put(String key, E value) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"Use NamespacedRegistry.register(E)"
|
"Use NamespacedInstanceRegistry.register(E)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +86,7 @@ implements Map<String, E> {
|
|||||||
@DoNotCall @Deprecated
|
@DoNotCall @Deprecated
|
||||||
public void putAll(Map<? extends String, ? extends E> m) {
|
public void putAll(Map<? extends String, ? extends E> m) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"Use NamespacedRegistry.registerAll(Collection<? extends E>)"
|
"Use NamespacedInstanceRegistry.registerAll(Collection<? extends E>)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,106 @@
|
|||||||
|
package ru.windcorp.progressia.common.util.namespaces;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import ru.windcorp.jputil.chars.StringUtil;
|
||||||
|
|
||||||
|
public class NamespacedUtil {
|
||||||
|
|
||||||
|
public static final char SEPARATOR = ':';
|
||||||
|
|
||||||
|
public static final int MAX_ID_LENGTH = 255;
|
||||||
|
private static final int MAX_PART_LENGTH = (MAX_ID_LENGTH - 1) / 2;
|
||||||
|
|
||||||
|
public static final int MAX_NAMESPACE_LENGTH = MAX_PART_LENGTH;
|
||||||
|
public static final int MAX_NAME_LENGTH = MAX_PART_LENGTH;
|
||||||
|
|
||||||
|
private static final int MIN_PART_LENGTH = 3;
|
||||||
|
|
||||||
|
public static final int MIN_NAMESPACE_LENGTH = MIN_PART_LENGTH;
|
||||||
|
public static final int MIN_NAME_LENGTH = MIN_PART_LENGTH;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the definition of the accepted pattern, but the value of
|
||||||
|
* these constants is not actually consulted in the check* methods.
|
||||||
|
*/
|
||||||
|
private static final String PART_CORE_REGEX = "[A-Z][a-zA-Z0-9]{2," + (MAX_PART_LENGTH - 1) + "}";
|
||||||
|
private static final String PART_REGEX = "^" + PART_CORE_REGEX + "$";
|
||||||
|
|
||||||
|
public static final String NAMESPACE_REGEX = PART_REGEX;
|
||||||
|
public static final String NAME_REGEX = PART_REGEX;
|
||||||
|
public static final String ID_REGEX = "^" + PART_CORE_REGEX + ":" + PART_CORE_REGEX + "$";
|
||||||
|
|
||||||
|
public static String getName(String id) {
|
||||||
|
checkId(id);
|
||||||
|
return id.substring(id.indexOf(':') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getNamespace(String id) {
|
||||||
|
checkId(id);
|
||||||
|
return id.substring(0, id.indexOf(':'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getId(String namespace, String name) {
|
||||||
|
checkPart(namespace, 0, namespace.length(), "Namespace");
|
||||||
|
checkPart(name, 0, name.length(), "Name");
|
||||||
|
|
||||||
|
return namespace + SEPARATOR + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkId(String id) {
|
||||||
|
Objects.requireNonNull(id, "id");
|
||||||
|
|
||||||
|
int firstSeparator = id.indexOf(SEPARATOR);
|
||||||
|
|
||||||
|
boolean areSeparatorsInvalid = (firstSeparator < 0) || (id.indexOf(SEPARATOR, firstSeparator + 1) >= 0);
|
||||||
|
|
||||||
|
if (areSeparatorsInvalid) {
|
||||||
|
int separators = StringUtil.count(id, SEPARATOR);
|
||||||
|
throw new IllegalIdException(
|
||||||
|
"ID \"" + id + "\" is invalid. "
|
||||||
|
+ (separators == 0 ? "No " : "Too many (" + separators + ") ")
|
||||||
|
+ "separators '" + SEPARATOR + "' found, exactly one required"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkPart(id, 0, firstSeparator, "namespace");
|
||||||
|
checkPart(id, firstSeparator + 1, id.length() - firstSeparator - 1, "name");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkPart(String data, int offset, int length, String nameForErrors) {
|
||||||
|
Objects.requireNonNull(data, nameForErrors);
|
||||||
|
|
||||||
|
if (length > MAX_PART_LENGTH) {
|
||||||
|
throw new IllegalIdException(
|
||||||
|
nameForErrors + " \"" + data.substring(offset, offset + length) + "\" is too long. "
|
||||||
|
+ "Expected at most " + MAX_PART_LENGTH
|
||||||
|
+ " characters"
|
||||||
|
);
|
||||||
|
} else if (length < MIN_PART_LENGTH) {
|
||||||
|
throw new IllegalIdException(
|
||||||
|
nameForErrors + " \"" + data.substring(offset, offset + length) + "\" is too short. "
|
||||||
|
+ "Expected at lest " + MIN_PART_LENGTH
|
||||||
|
+ " characters"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't actually use *_REGEX for speed
|
||||||
|
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
char c = data.charAt(i + offset);
|
||||||
|
if (!(
|
||||||
|
( c >= 'A' && c <= 'Z') ||
|
||||||
|
(i != 0 && c >= 'a' && c <= 'z') ||
|
||||||
|
(i != 0 && c >= '0' && c <= '9')
|
||||||
|
)) {
|
||||||
|
throw new IllegalIdException(
|
||||||
|
nameForErrors + " \"" + data.substring(offset, offset + length) + "\" is invalid. "
|
||||||
|
+ "Allowed is: " + PART_REGEX
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private NamespacedUtil() {}
|
||||||
|
|
||||||
|
}
|
148
src/main/java/ru/windcorp/progressia/common/world/BlockRay.java
Normal file
148
src/main/java/ru/windcorp/progressia/common/world/BlockRay.java
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package ru.windcorp.progressia.common.world;
|
||||||
|
|
||||||
|
import glm.vec._3.Vec3;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||||
|
import ru.windcorp.progressia.common.util.VectorUtil.Axis;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
|
||||||
|
import static java.lang.Math.*;
|
||||||
|
|
||||||
|
public class BlockRay {
|
||||||
|
|
||||||
|
private final Vec3 position = new Vec3();
|
||||||
|
private final Vec3 direction = new Vec3();
|
||||||
|
|
||||||
|
private float distance;
|
||||||
|
|
||||||
|
private final Vec3i block = new Vec3i();
|
||||||
|
private BlockFace currentFace = null;
|
||||||
|
|
||||||
|
private boolean isValid = false;
|
||||||
|
|
||||||
|
public void start(Vec3 position, Vec3 direction) {
|
||||||
|
if (!direction.any()) {
|
||||||
|
throw new IllegalArgumentException("Direction is a zero vector");
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid = true;
|
||||||
|
this.position.set(position).sub(0.5f); // Make sure lattice points are block vertices, not centers
|
||||||
|
this.direction.set(direction).normalize();
|
||||||
|
this.block.set(toBlock(position.x), toBlock(position.y), toBlock(position.z));
|
||||||
|
this.distance = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void end() {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3i next() {
|
||||||
|
checkState();
|
||||||
|
|
||||||
|
float tx = distanceToEdge(position.x, direction.x);
|
||||||
|
float ty = distanceToEdge(position.y, direction.y);
|
||||||
|
float tz = distanceToEdge(position.z, direction.z);
|
||||||
|
|
||||||
|
float tMin;
|
||||||
|
Axis axis;
|
||||||
|
|
||||||
|
if (tx < ty && tx < tz) {
|
||||||
|
tMin = tx;
|
||||||
|
axis = Axis.X;
|
||||||
|
} else if (ty < tx && ty < tz) {
|
||||||
|
tMin = ty;
|
||||||
|
axis = Axis.Y;
|
||||||
|
} else {
|
||||||
|
tMin = tz;
|
||||||
|
axis = Axis.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// block.(axis) += signum(direction.(axis))
|
||||||
|
VectorUtil.set(block, axis, VectorUtil.get(block, axis) + (int) signum(VectorUtil.get(direction, axis)));
|
||||||
|
|
||||||
|
// position += direction * tMin
|
||||||
|
VectorUtil.linearCombination(position, 1, direction, tMin, position); // position += direction * tMin
|
||||||
|
distance += tMin;
|
||||||
|
|
||||||
|
// position.(axis) = round(position.(axis))
|
||||||
|
VectorUtil.set(position, axis, round(VectorUtil.get(position, axis)));
|
||||||
|
|
||||||
|
this.currentFace = computeCurrentFace(axis, (int) signum(VectorUtil.get(direction, axis)));
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float distanceToEdge(float c, float dir) {
|
||||||
|
if (dir == 0) return Float.POSITIVE_INFINITY;
|
||||||
|
|
||||||
|
float edge;
|
||||||
|
|
||||||
|
if (dir > 0) {
|
||||||
|
edge = strictCeil(c);
|
||||||
|
} else {
|
||||||
|
edge = strictFloor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (edge - c) / dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockFace computeCurrentFace(Axis axis, int sign) {
|
||||||
|
if (sign == 0) throw new IllegalStateException("sign is zero");
|
||||||
|
|
||||||
|
switch (axis) {
|
||||||
|
case X: return sign > 0 ? BlockFace.SOUTH : BlockFace.NORTH;
|
||||||
|
case Y: return sign > 0 ? BlockFace.EAST : BlockFace.WEST;
|
||||||
|
default:
|
||||||
|
case Z: return sign > 0 ? BlockFace.BOTTOM : BlockFace.TOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3i current() {
|
||||||
|
checkState();
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3 getPoint(Vec3 output) {
|
||||||
|
output.set(position);
|
||||||
|
output.add(0.5f); // Make sure we're in the block-center coordinate system
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockFace getCurrentFace() {
|
||||||
|
return currentFace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDistance() {
|
||||||
|
checkState();
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkState() {
|
||||||
|
if (!isValid) {
|
||||||
|
throw new IllegalStateException("BlockRay not started");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int toBlock(float c) {
|
||||||
|
return (int) round(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a smallest integer <i>a</i> such that <i>a</i> > <i>c</i>.
|
||||||
|
* @param c the number to compute strict ceiling of
|
||||||
|
* @return the strict ceiling of <i>c</i>
|
||||||
|
*/
|
||||||
|
private static float strictCeil(float c) {
|
||||||
|
return (float) (floor(c) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a largest integer <i>a</i> such that <i>a</i> < <i>c</i>.
|
||||||
|
* @param c the number to compute strict ceiling of
|
||||||
|
* @return the strict ceiling of <i>c</i>
|
||||||
|
*/
|
||||||
|
private static float strictFloor(float c) {
|
||||||
|
return (float) (ceil(c) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -20,6 +20,7 @@ package ru.windcorp.progressia.common.world;
|
|||||||
import static ru.windcorp.progressia.common.world.block.BlockFace.*;
|
import static ru.windcorp.progressia.common.world.block.BlockFace.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
@ -63,8 +64,11 @@ public class ChunkData {
|
|||||||
private final List<EntityData> entities =
|
private final List<EntityData> entities =
|
||||||
Collections.synchronizedList(new ArrayList<>());
|
Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
public ChunkData(int x, int y, int z, WorldData world) {
|
private final Collection<ChunkDataListener> listeners =
|
||||||
this.position.set(x, y, z);
|
Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
|
public ChunkData(Vec3i position, WorldData world) {
|
||||||
|
this.position.set(position.x, position.y, position.z);
|
||||||
this.world = world;
|
this.world = world;
|
||||||
|
|
||||||
tmp_generate();
|
tmp_generate();
|
||||||
@ -80,7 +84,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) {
|
||||||
@ -92,11 +96,11 @@ public class ChunkData {
|
|||||||
pos.set(x, y, z);
|
pos.set(x, y, z);
|
||||||
|
|
||||||
if (f > 17) {
|
if (f > 17) {
|
||||||
setBlock(pos, stone);
|
setBlock(pos, stone, false);
|
||||||
} else if (f > 14) {
|
} else if (f > 14) {
|
||||||
setBlock(pos, dirt);
|
setBlock(pos, dirt, false);
|
||||||
} else {
|
} else {
|
||||||
setBlock(pos, air);
|
setBlock(pos, air, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -132,26 +136,36 @@ public class ChunkData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony");
|
if (!getPosition().any()) {
|
||||||
javapony.setEntityId(0x42);
|
EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
|
||||||
javapony.setPosition(new Vec3(-6, -6, 20));
|
player.setEntityId(0x42);
|
||||||
javapony.setDirection(new Vec2(
|
player.setPosition(new Vec3(-6, -6, 20));
|
||||||
|
player.setDirection(new Vec2(
|
||||||
(float) Math.toRadians(40), (float) Math.toRadians(45)
|
(float) Math.toRadians(40), (float) Math.toRadians(45)
|
||||||
));
|
));
|
||||||
getEntities().add(javapony);
|
getEntities().add(player);
|
||||||
|
|
||||||
EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie");
|
EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie");
|
||||||
statie.setEntityId(0xDEADBEEF);
|
statie.setEntityId(0xDEADBEEF);
|
||||||
statie.setPosition(new Vec3(0, 15, 16));
|
statie.setPosition(new Vec3(0, 15, 16));
|
||||||
getEntities().add(statie);
|
getEntities().add(statie);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public BlockData getBlock(Vec3i posInChunk) {
|
public BlockData getBlock(Vec3i posInChunk) {
|
||||||
return blocks[getBlockIndex(posInChunk)];
|
return blocks[getBlockIndex(posInChunk)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBlock(Vec3i posInChunk, BlockData block) {
|
public void setBlock(Vec3i posInChunk, BlockData block, boolean notify) {
|
||||||
|
BlockData previous = blocks[getBlockIndex(posInChunk)];
|
||||||
blocks[getBlockIndex(posInChunk)] = block;
|
blocks[getBlockIndex(posInChunk)] = block;
|
||||||
|
|
||||||
|
if (notify) {
|
||||||
|
getListeners().forEach(l -> {
|
||||||
|
l.onChunkBlockChanged(this, posInChunk, previous, block);
|
||||||
|
l.onChunkChanged(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<TileData> getTilesOrNull(Vec3i blockInChunk, BlockFace face) {
|
public List<TileData> getTilesOrNull(Vec3i blockInChunk, BlockFace face) {
|
||||||
@ -243,7 +257,7 @@ public class ChunkData {
|
|||||||
|
|
||||||
private static void checkLocalCoordinates(Vec3i posInChunk) {
|
private static void checkLocalCoordinates(Vec3i posInChunk) {
|
||||||
if (!isInBounds(posInChunk)) {
|
if (!isInBounds(posInChunk)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalCoordinatesException(
|
||||||
"Coordinates " + str(posInChunk) + " "
|
"Coordinates " + str(posInChunk) + " "
|
||||||
+ "are not legal chunk coordinates"
|
+ "are not legal chunk coordinates"
|
||||||
);
|
);
|
||||||
@ -329,8 +343,28 @@ public class ChunkData {
|
|||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<ChunkDataListener> getListeners() {
|
||||||
|
return listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(ChunkDataListener listener) {
|
||||||
|
this.listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeListener(ChunkDataListener listener) {
|
||||||
|
this.listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
private static String str(Vec3i v) {
|
private static String str(Vec3i v) {
|
||||||
return "(" + v.x + "; " + v.y + "; " + v.z + ")";
|
return "(" + v.x + "; " + v.y + "; " + v.z + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onLoaded() {
|
||||||
|
getListeners().forEach(l -> l.onChunkLoaded(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void beforeUnloaded() {
|
||||||
|
getListeners().forEach(l -> l.beforeChunkUnloaded(this));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
package ru.windcorp.progressia.common.world;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
|
|
||||||
|
public interface ChunkDataListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked after a block has changed in a chunk.
|
||||||
|
* This is not triggered when a change is caused by chunk loading or unloading.
|
||||||
|
* @param chunk the chunk that has changed
|
||||||
|
* @param blockInChunk the {@linkplain Coordinates#blockInChunk chunk coordinates} of the change
|
||||||
|
* @param previous the previous occupant of {@code blockInChunk}
|
||||||
|
* @param current the current (new) occupant of {@code blockInChunk}
|
||||||
|
*/
|
||||||
|
default void onChunkBlockChanged(ChunkData chunk, Vec3i blockInChunk, BlockData previous, BlockData current) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked after a tile has been added or removed from a chunk.
|
||||||
|
* This is not triggered when a change is caused by chunk loading or unloading.
|
||||||
|
* @param chunk the chunk that has changed
|
||||||
|
* @param blockInChunk the {@linkplain Coordinates#blockInChunk chunk coordinates} of the change
|
||||||
|
* @param face the face that the changed tile belongs or belonged to
|
||||||
|
* @param tile the tile that has been added or removed
|
||||||
|
* @param wasAdded {@code true} iff the tile has been added, {@code false} iff the tile has been removed
|
||||||
|
*/
|
||||||
|
default void onChunkTilesChanged(ChunkData chunk, Vec3i blockInChunk, BlockFace face, TileData tile, boolean wasAdded) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked whenever a chunk changes, loads or unloads. If some other method in this
|
||||||
|
* {@code ChunkDataListener} are to be invoked, e.g. is the change was caused by a
|
||||||
|
* block being removed, this method is called last.
|
||||||
|
* @param chunk the chunk that has changed
|
||||||
|
*/
|
||||||
|
default void onChunkChanged(ChunkData chunk) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked whenever a chunk has been loaded.
|
||||||
|
* @param chunk the chunk that has loaded
|
||||||
|
*/
|
||||||
|
default void onChunkLoaded(ChunkData chunk) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked whenever a chunk is about to be unloaded.
|
||||||
|
* @param chunk the chunk that is going to be loaded
|
||||||
|
*/
|
||||||
|
default void beforeChunkUnloaded(ChunkData chunk) {}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package ru.windcorp.progressia.common.world;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
|
||||||
|
public class ChunkDataListeners {
|
||||||
|
|
||||||
|
public static WorldDataListener createAdder(Supplier<ChunkDataListener> listenerSupplier) {
|
||||||
|
return new WorldDataListener() {
|
||||||
|
@Override
|
||||||
|
public void getChunkListeners(WorldData world, Vec3i chunk, Consumer<ChunkDataListener> chunkListenerSink) {
|
||||||
|
chunkListenerSink.accept(listenerSupplier.get());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorldDataListener createAdder(ChunkDataListener listener) {
|
||||||
|
return createAdder(() -> listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChunkDataListeners() {}
|
||||||
|
|
||||||
|
}
|
@ -11,18 +11,17 @@ import glm.vec._3.i.Vec3i;
|
|||||||
* Three types of coordinates are used in Progressia:
|
* Three types of coordinates are used in Progressia:
|
||||||
* <ul>
|
* <ul>
|
||||||
*
|
*
|
||||||
* <li><em>World coordinates</em>, in code referred to as {@code blockInWorld} -
|
* <li id="blockInWorld"><em>World coordinates</em>, in code referred to as {@code blockInWorld} -
|
||||||
* coordinates relative to world origin. Every block in the world has unique
|
* coordinates relative to world origin. Every block in the world has unique
|
||||||
* world coordinates.</li>
|
* world coordinates.</li>
|
||||||
*
|
*
|
||||||
* <li><em>Chunk coordinates</em>, in code referred to as {@code blockInChunk} -
|
* <li id="blockInChunk"><em>Chunk coordinates</em>, in code referred to as {@code blockInChunk} -
|
||||||
* coordinates relative some chunk's origin. Every block in the chunk has unique
|
* coordinates relative some chunk's origin. Every block in the chunk has unique
|
||||||
* chunk coordinates, but blocks in different chunks may have identical chunk
|
* chunk coordinates, but blocks in different chunks may have identical chunk
|
||||||
* coordinates. These coordinates are only useful in combination with a chunk
|
* coordinates. These coordinates are only useful in combination with a chunk
|
||||||
* reference. Chunk coordinates are always <tt>[0; {@link #BLOCKS_PER_CHUNK})
|
* reference. Chunk coordinates are always <tt>[0; {@link #BLOCKS_PER_CHUNK})</tt>.</li>
|
||||||
* </tt>.</li>
|
|
||||||
*
|
*
|
||||||
* <li><em>Coordinates of chunk</em>, in code referred to as {@code chunk} -
|
* <li id="chunk"><em>Coordinates of chunk</em>, in code referred to as {@code chunk} -
|
||||||
* chunk coordinates relative to world origin. Every chunk in the world has
|
* chunk coordinates relative to world origin. Every chunk in the world has
|
||||||
* unique coordinates of chunk.</li>
|
* unique coordinates of chunk.</li>
|
||||||
*
|
*
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package ru.windcorp.progressia.common.world;
|
||||||
|
|
||||||
|
public class IllegalCoordinatesException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1362481281554206710L;
|
||||||
|
|
||||||
|
public IllegalCoordinatesException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IllegalCoordinatesException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IllegalCoordinatesException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IllegalCoordinatesException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IllegalCoordinatesException(String message, Throwable cause, boolean enableSuppression,
|
||||||
|
boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user