diff --git a/.idea/dictionaries/kevint.xml b/.idea/dictionaries/kevint.xml
index 0044df94131..ec1a15440b6 100644
--- a/.idea/dictionaries/kevint.xml
+++ b/.idea/dictionaries/kevint.xml
@@ -4,6 +4,8 @@
SPDX-License-Identifier
artifactory
classpaths
+ deserialized
+ deserializing
gameplay
minecraft
reddit
diff --git a/.idea/inspectionProfiles/Terasology.xml b/.idea/inspectionProfiles/Terasology.xml
index 48970b27fe6..e975a5a7887 100644
--- a/.idea/inspectionProfiles/Terasology.xml
+++ b/.idea/inspectionProfiles/Terasology.xml
@@ -12,5 +12,6 @@
+
\ No newline at end of file
diff --git a/Jenkinsfile b/Jenkinsfile
index ed6d1179074..4c08cfffbf9 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -34,7 +34,7 @@ properties([
pipeline {
agent {
- label 'ts-engine && heavy && java8'
+ label 'ts-engine && heavy && java11'
}
stages {
// declarative pipeline does `checkout scm` automatically when hitting first stage
@@ -42,6 +42,7 @@ pipeline {
steps {
echo 'Automatically checked out the things!'
sh 'chmod +x gradlew'
+ sh './gradlew --version'
}
}
diff --git a/README.markdown b/README.markdown
index 8b19b92630b..626d9ef2a0c 100644
--- a/README.markdown
+++ b/README.markdown
@@ -68,9 +68,9 @@ For more information about playing, like hot keys or server hosting, see the [de
#### Alternative Installation Methods
-If you already have Java Runtime Environment (JRE) installed, you may use a direct download release as an alternative to using the [launcher](https://github.com/MovingBlocks/TerasologyLauncher/releases). Java versions 8 and 11 are supported.
+If you already have Java Runtime Environment (JRE) installed, you may use a direct download release as an alternative to using the [launcher](https://github.com/MovingBlocks/TerasologyLauncher/releases). Java version 11 is supported.
-Direct download stable builds are uploaded to [our release section here on GitHub](https://github.com/MovingBlocks/Terasology/releases) while the cutting-edge develop version can be downloaded direct [here from our Jenkins](https://jenkins.terasology.io/teraorg/job/Terasology/job/Omega/job/master/lastSuccessfulBuild/artifact/distros/omega/build/distributions/TerasologyOmega.zip)
+Direct download stable builds are uploaded to [our release section here on GitHub](https://github.com/MovingBlocks/Terasology/releases) while the cutting-edge develop version can be downloaded direct [here from our Jenkins](https://jenkins.terasology.io/teraorg/job/Terasology/job/Omega/job/master/lastSuccessfulBuild/artifact/distros/omega/build/distributions/TerasologyOmega.zip).
## Developing
diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts
index 8d179d2654f..42c90695dba 100644
--- a/build-logic/build.gradle.kts
+++ b/build-logic/build.gradle.kts
@@ -17,6 +17,15 @@ repositories {
@Suppress("UnstableApiUsage")
isAllowInsecureProtocol = true // 😱
}
+
+ // TODO MYSTERY: As of November 7th 2011 virtual-repo-live could no longer be relied on for latest snapshots - Pro feature?
+ // We've been using it that way for *years* and nothing likewise changed in the area for years as well. This seems to work ....
+ maven {
+ name = "Terasology snapshot locals"
+ url = URI("http://artifactory.terasology.org/artifactory/terasology-snapshot-local")
+ @Suppress("UnstableApiUsage")
+ isAllowInsecureProtocol = true // 😱
+ }
}
dependencies {
diff --git a/build-logic/src/main/kotlin/facade.gradle.kts b/build-logic/src/main/kotlin/facade.gradle.kts
index e5014974c4c..906f29e200d 100644
--- a/build-logic/src/main/kotlin/facade.gradle.kts
+++ b/build-logic/src/main/kotlin/facade.gradle.kts
@@ -34,3 +34,13 @@ dependencies {
// Make sure all module dependencies are available to the game in cacheModules.
"modules"(project(":modules"))
}
+
+tasks.register("unitTest") {
+ group = "Verification"
+ description = "Runs unit tests (fast)"
+
+ useJUnitPlatform {
+ excludeTags("MteTest", "TteTest")
+ }
+ systemProperty("junit.jupiter.execution.timeout.default", "1m")
+}
diff --git a/build-logic/src/main/kotlin/terasology-repositories.gradle.kts b/build-logic/src/main/kotlin/terasology-repositories.gradle.kts
index 729bb6c170b..4c68a6a8863 100644
--- a/build-logic/src/main/kotlin/terasology-repositories.gradle.kts
+++ b/build-logic/src/main/kotlin/terasology-repositories.gradle.kts
@@ -48,4 +48,12 @@ repositories {
isAllowInsecureProtocol = true // 😱
}
}
+
+ // TODO MYSTERY: As of November 7th 2011 virtual-repo-live could no longer be relied on for latest snapshots - Pro feature?
+ // We've been using it that way for *years* and nothing likewise changed in the area for years as well. This seems to work ....
+ maven {
+ name = "Terasology snapshot locals"
+ url = URI("http://artifactory.terasology.org/artifactory/terasology-snapshot-local")
+ isAllowInsecureProtocol = true // 😱
+ }
}
diff --git a/build.gradle b/build.gradle
index 18bd24b6b7d..d2814728c38 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,6 +14,14 @@ buildscript {
url = "http://artifactory.terasology.org/artifactory/virtual-repo-live"
allowInsecureProtocol = true // 😱
}
+
+ // TODO MYSTERY: As of November 7th 2011 virtual-repo-live could no longer be relied on for latest snapshots - Pro feature?
+ // We've been using it that way for *years* and nothing likewise changed in the area for years as well. This seems to work ....
+ maven {
+ name = "Terasology snapshot locals"
+ url = "http://artifactory.terasology.org/artifactory/terasology-snapshot-local"
+ allowInsecureProtocol = true // 😱
+ }
}
dependencies {
@@ -54,11 +62,18 @@ import org.jetbrains.gradle.ext.ActionDelegationConfig
import static org.gradle.internal.logging.text.StyledTextOutput.Style
// Test for right version of Java in use for running this script
-assert org.gradle.api.JavaVersion.current().isJava8Compatible()
-// Check for Java 8
-if(!(JavaVersion.current() == JavaVersion.VERSION_1_8 || JavaVersion.current() == JavaVersion.VERSION_11)) {
+assert org.gradle.api.JavaVersion.current().isJava11Compatible()
+if(!(JavaVersion.current() == JavaVersion.VERSION_11)) {
def out = services.get(StyledTextOutputFactory).create("an-ouput")
- out.withStyle(Style.FailureHeader).println("WARNING: Compiling with a JDK not 8 nor 11. While some other Javas may be safe to use any newer than 11 may cause issues. If you encounter oddities try Java 8 or 11. See https://github.com/MovingBlocks/Terasology/issues/3976. Current detected Java version is ${JavaVersion.current()} from vendor ${System.getProperty("java.vendor")} located at ${System.getProperty("java.home")}")
+ out.withStyle(Style.FailureHeader).println("""
+WARNING: Compiling with a JDK of not version 11. While some other Javas may be
+safe to use, any newer than 11 may cause issues.
+If you encounter oddities try Java 11.
+See https://github.com/MovingBlocks/Terasology/issues/3976.
+Current detected Java version is ${JavaVersion.current()}
+ from vendor ${System.getProperty("java.vendor")}
+located at ${System.getProperty("java.home")}
+""")
}
// Declare "extra properties" (variables) for the project (and subs) - a Gradle thing that makes them special.
diff --git a/config/gradle/common.gradle b/config/gradle/common.gradle
index d896181b67d..784637f119d 100644
--- a/config/gradle/common.gradle
+++ b/config/gradle/common.gradle
@@ -18,8 +18,9 @@ apply plugin: 'org.sonarqube'
apply plugin: 'terasology-repositories'
-sourceCompatibility = 1.8
-targetCompatibility = 1.8
+java {
+ sourceCompatibility = JavaVersion.VERSION_11
+}
javadoc.options.encoding = 'UTF-8'
diff --git a/engine-tests/src/test/java/org/terasology/i18n/TranslationFormatTests.java b/engine-tests/src/test/java/org/terasology/i18n/TranslationFormatTests.java
index 49eb8c853e8..06ce58f0d61 100644
--- a/engine-tests/src/test/java/org/terasology/i18n/TranslationFormatTests.java
+++ b/engine-tests/src/test/java/org/terasology/i18n/TranslationFormatTests.java
@@ -6,7 +6,6 @@
import com.google.common.collect.ImmutableList;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.terasology.engine.core.SimpleUri;
import org.terasology.engine.i18n.assets.TranslationData;
import org.terasology.engine.i18n.assets.TranslationFormat;
import org.terasology.gestalt.assets.ResourceUrn;
@@ -67,7 +66,7 @@ public void testEmptyDataGenRoot() throws IOException, InvalidAssetFilenameExcep
ResourceUrn urn = createUrnFromFile(format, assetDataFile);
TranslationData data = format.load(urn, Collections.singletonList(assetDataFile));
- assertEquals(new SimpleUri("engine:menu"), data.getProjectUri());
+ assertEquals(new ResourceUrn("engine:menu"), data.getProjectUrn());
assertEquals(Locale.ROOT, data.getLocale());
}
diff --git a/engine/build.gradle b/engine/build.gradle
index eb2d4b68e0e..8b1ea6d6a90 100644
--- a/engine/build.gradle
+++ b/engine/build.gradle
@@ -99,8 +99,9 @@ dependencies {
implementation "org.lwjgl:lwjgl-openal"
api "org.lwjgl:lwjgl-opengl"
implementation "org.lwjgl:lwjgl-stb"
-
- api group: 'io.projectreactor', name: 'reactor-core', version: '3.4.7'
+
+ api group: 'io.projectreactor', name: 'reactor-core', version: '3.4.11'
+ api group: 'io.projectreactor.addons', name: 'reactor-extra', version: '3.4.5'
api group: 'org.joml', name: 'joml', version: '1.10.0'
api group: 'org.terasology.joml-ext', name: 'joml-geometry', version: '0.1.0'
diff --git a/engine/src/main/java/org/terasology/engine/config/SystemConfig.java b/engine/src/main/java/org/terasology/engine/config/SystemConfig.java
index b990e7d7b7c..14d3ec99a93 100644
--- a/engine/src/main/java/org/terasology/engine/config/SystemConfig.java
+++ b/engine/src/main/java/org/terasology/engine/config/SystemConfig.java
@@ -12,6 +12,7 @@
import java.util.Locale.Category;
import java.util.Optional;
+import static java.lang.Math.max;
import static org.terasology.engine.config.flexible.SettingArgument.constraint;
import static org.terasology.engine.config.flexible.SettingArgument.defaultValue;
import static org.terasology.engine.config.flexible.SettingArgument.name;
@@ -31,7 +32,7 @@ public class SystemConfig extends AutoConfig {
public final Setting maxThreads = setting(
type(Integer.class),
- defaultValue(Runtime.getRuntime().availableProcessors() - 1),
+ defaultValue(max(1, Runtime.getRuntime().availableProcessors() - 1)),
name("Max threads(not yet)"),
constraint(new NumberRangeConstraint<>(0, Integer.MAX_VALUE, false, false))
);
diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/ui/LocaleConstraintWidgetFactory.java b/engine/src/main/java/org/terasology/engine/config/flexible/ui/LocaleConstraintWidgetFactory.java
index d66eac75c56..d7134e85afa 100644
--- a/engine/src/main/java/org/terasology/engine/config/flexible/ui/LocaleConstraintWidgetFactory.java
+++ b/engine/src/main/java/org/terasology/engine/config/flexible/ui/LocaleConstraintWidgetFactory.java
@@ -6,10 +6,10 @@
import com.google.common.collect.Lists;
import org.terasology.engine.config.flexible.Setting;
import org.terasology.engine.config.flexible.constraints.LocaleConstraint;
-import org.terasology.engine.core.SimpleUri;
import org.terasology.engine.i18n.TranslationProject;
import org.terasology.engine.i18n.TranslationSystem;
import org.terasology.engine.rendering.nui.layers.mainMenu.settings.LocaleRenderer;
+import org.terasology.gestalt.assets.ResourceUrn;
import org.terasology.nui.UIWidget;
import org.terasology.nui.databinding.Binding;
import org.terasology.nui.widgets.UIDropdownScrollable;
@@ -58,8 +58,8 @@ public void set(Locale value) {
};
UIDropdownScrollable dropdownScrollable = new UIDropdownScrollable<>();
- SimpleUri menuUri = new SimpleUri("engine:menu");
- TranslationProject menuProject = translationSystem.getProject(menuUri);
+ ResourceUrn menuUrn = new ResourceUrn("engine:menu");
+ TranslationProject menuProject = translationSystem.getProject(menuUrn);
List locales = new ArrayList<>(menuProject.getAvailableLocales());
for (Locale languageExcluded : languagesExcluded) {
locales.remove(languageExcluded);
diff --git a/engine/src/main/java/org/terasology/engine/core/subsystem/headless/renderer/HeadlessWorldRenderer.java b/engine/src/main/java/org/terasology/engine/core/subsystem/headless/renderer/HeadlessWorldRenderer.java
index 74b903c665b..6702411ed1c 100644
--- a/engine/src/main/java/org/terasology/engine/core/subsystem/headless/renderer/HeadlessWorldRenderer.java
+++ b/engine/src/main/java/org/terasology/engine/core/subsystem/headless/renderer/HeadlessWorldRenderer.java
@@ -11,7 +11,6 @@
import org.terasology.engine.monitoring.PerformanceMonitor;
import org.terasology.engine.rendering.assets.material.Material;
import org.terasology.engine.rendering.cameras.Camera;
-import org.terasology.engine.rendering.cameras.SubmersibleCamera;
import org.terasology.engine.rendering.dag.RenderGraph;
import org.terasology.engine.rendering.world.WorldRenderer;
import org.terasology.engine.rendering.world.viewDistance.ViewDistance;
@@ -35,7 +34,7 @@ public class HeadlessWorldRenderer implements WorldRenderer {
private WorldProvider worldProvider;
private ChunkProvider chunkProvider;
- private Camera noCamera = new NullCamera(null, null);
+ private Camera noCamera = new NullCamera();
/* CHUNKS */
private boolean pendingChunks;
@@ -76,8 +75,8 @@ public void onChunkUnloaded(Vector3ic pos) {
}
@Override
- public SubmersibleCamera getActiveCamera() {
- return (SubmersibleCamera) noCamera;
+ public Camera getActiveCamera() {
+ return noCamera;
}
@Override
diff --git a/engine/src/main/java/org/terasology/engine/core/subsystem/headless/renderer/NullCamera.java b/engine/src/main/java/org/terasology/engine/core/subsystem/headless/renderer/NullCamera.java
index 1d2b50fbea3..5579ef4bf12 100644
--- a/engine/src/main/java/org/terasology/engine/core/subsystem/headless/renderer/NullCamera.java
+++ b/engine/src/main/java/org/terasology/engine/core/subsystem/headless/renderer/NullCamera.java
@@ -2,13 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
package org.terasology.engine.core.subsystem.headless.renderer;
-import org.terasology.engine.config.RenderingConfig;
-import org.terasology.engine.rendering.cameras.SubmersibleCamera;
-import org.terasology.engine.world.WorldProvider;
+import org.terasology.engine.rendering.cameras.Camera;
-public final class NullCamera extends SubmersibleCamera {
- public NullCamera(WorldProvider worldProvider, RenderingConfig renderingConfig) {
- super(worldProvider, renderingConfig);
+public final class NullCamera extends Camera {
+ public NullCamera() {
}
@Override
diff --git a/engine/src/main/java/org/terasology/engine/i18n/TranslationSystem.java b/engine/src/main/java/org/terasology/engine/i18n/TranslationSystem.java
index f9815818b9a..9797a0cc12d 100644
--- a/engine/src/main/java/org/terasology/engine/i18n/TranslationSystem.java
+++ b/engine/src/main/java/org/terasology/engine/i18n/TranslationSystem.java
@@ -3,7 +3,7 @@
package org.terasology.engine.i18n;
-import org.terasology.engine.core.Uri;
+import org.terasology.gestalt.assets.ResourceUrn;
import org.terasology.gestalt.module.sandbox.API;
import org.terasology.nui.translate.Translator;
@@ -22,7 +22,7 @@ public interface TranslationSystem extends Translator {
* @param name
* @return the project or null
.
*/
- TranslationProject getProject(Uri name);
+ TranslationProject getProject(ResourceUrn name);
/**
* If no perfect match is found for the default locale, fallback strategies will attempt to find the closest match.
diff --git a/engine/src/main/java/org/terasology/engine/i18n/TranslationSystemImpl.java b/engine/src/main/java/org/terasology/engine/i18n/TranslationSystemImpl.java
index be271fc23c2..690ec2f18d4 100644
--- a/engine/src/main/java/org/terasology/engine/i18n/TranslationSystemImpl.java
+++ b/engine/src/main/java/org/terasology/engine/i18n/TranslationSystemImpl.java
@@ -7,8 +7,6 @@
import org.slf4j.LoggerFactory;
import org.terasology.engine.config.SystemConfig;
import org.terasology.engine.context.Context;
-import org.terasology.engine.core.SimpleUri;
-import org.terasology.engine.core.Uri;
import org.terasology.engine.i18n.assets.Translation;
import org.terasology.engine.persistence.TemplateEngine;
import org.terasology.engine.persistence.TemplateEngineImpl;
@@ -32,7 +30,7 @@ public class TranslationSystemImpl implements TranslationSystem {
private static final Logger logger = LoggerFactory.getLogger(TranslationSystemImpl.class);
private final List> changeListeners = new CopyOnWriteArrayList<>();
- private final Map projects = new HashMap<>();
+ private final Map projects = new HashMap<>();
private final SystemConfig systemConfig;
@@ -55,21 +53,21 @@ public void refresh() {
Optional asset = assetManager.getAsset(urn, Translation.class);
if (asset.isPresent()) {
Translation trans = asset.get();
- Uri uri = trans.getProjectUri();
- if (uri.isValid()) {
- TranslationProject proj = projects.computeIfAbsent(uri, e -> new StandardTranslationProject());
+ ResourceUrn projectUrn = trans.getProjectUrn();
+ if (!projectUrn.getModuleName().isEmpty() && !projectUrn.getResourceName().isEmpty()) {
+ TranslationProject proj = projects.computeIfAbsent(projectUrn, e -> new StandardTranslationProject());
proj.add(trans);
trans.subscribe(this::onAssetChanged);
logger.info("Discovered " + trans);
} else {
- logger.warn("Ignoring invalid project uri: {}", uri);
+ logger.warn("Ignoring invalid project projectUrn: {}", projectUrn);
}
}
}
}
@Override
- public TranslationProject getProject(Uri name) {
+ public TranslationProject getProject(ResourceUrn name) {
return projects.get(name);
}
@@ -82,7 +80,7 @@ public String translate(String id) {
public String translate(String text, Locale otherLocale) {
TemplateEngine templateEngine = new TemplateEngineImpl(id -> {
ResourceUrn uri = new ResourceUrn(id);
- SimpleUri projectUri = new SimpleUri(uri.getModuleName(), uri.getResourceName());
+ ResourceUrn projectUri = new ResourceUrn(uri.getModuleName(), uri.getResourceName());
TranslationProject project = getProject(projectUri);
if (project != null) {
Optional opt = project.translate(uri.getFragmentName(), otherLocale);
@@ -112,7 +110,7 @@ public void unsubscribe(Consumer reloadListener) {
}
private void onAssetChanged(Translation trans) {
- Uri uri = trans.getProjectUri();
+ ResourceUrn uri = trans.getProjectUrn();
TranslationProject project = projects.get(uri);
if (trans.isDisposed()) {
project.remove(trans);
diff --git a/engine/src/main/java/org/terasology/engine/i18n/assets/Translation.java b/engine/src/main/java/org/terasology/engine/i18n/assets/Translation.java
index 6a9d7cbe274..b5ff5f46154 100644
--- a/engine/src/main/java/org/terasology/engine/i18n/assets/Translation.java
+++ b/engine/src/main/java/org/terasology/engine/i18n/assets/Translation.java
@@ -5,7 +5,6 @@
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
-import org.terasology.engine.core.Uri;
import org.terasology.gestalt.assets.Asset;
import org.terasology.gestalt.assets.AssetType;
import org.terasology.gestalt.assets.DisposableResource;
@@ -27,7 +26,7 @@ public class Translation extends Asset {
private Map dictionary = new HashMap<>();
private Locale locale;
- private Uri projectUri;
+ private ResourceUrn projectUrn;
private final DisposalAction disposalAction;
@@ -57,8 +56,8 @@ public static Translation create(ResourceUrn urn, AssetType, TranslationData>
/**
* @return the uri of the project this instance is part of
*/
- public Uri getProjectUri() {
- return projectUri;
+ public ResourceUrn getProjectUrn() {
+ return projectUrn;
}
/**
* @return the locale of the translation data
@@ -101,14 +100,14 @@ public String toString() {
protected void doReload(TranslationData data) {
Preconditions.checkArgument(data != null);
- boolean isEqual = Objects.equal(data.getProjectUri(), projectUri)
+ boolean isEqual = Objects.equal(data.getProjectUrn(), projectUrn)
&& Objects.equal(data.getLocale(), locale)
&& Objects.equal(data.getTranslations(), dictionary);
if (!isEqual) {
this.dictionary.clear();
this.dictionary.putAll(data.getTranslations());
- this.projectUri = data.getProjectUri();
+ this.projectUrn = data.getProjectUrn();
this.locale = data.getLocale();
for (Consumer listener : disposalAction.changeListeners) {
diff --git a/engine/src/main/java/org/terasology/engine/i18n/assets/TranslationData.java b/engine/src/main/java/org/terasology/engine/i18n/assets/TranslationData.java
index 0282838a732..466011efb09 100644
--- a/engine/src/main/java/org/terasology/engine/i18n/assets/TranslationData.java
+++ b/engine/src/main/java/org/terasology/engine/i18n/assets/TranslationData.java
@@ -3,8 +3,8 @@
package org.terasology.engine.i18n.assets;
import com.google.common.base.Preconditions;
-import org.terasology.engine.core.Uri;
import org.terasology.gestalt.assets.AssetData;
+import org.terasology.gestalt.assets.ResourceUrn;
import java.util.Collections;
import java.util.HashMap;
@@ -19,17 +19,17 @@ public class TranslationData implements AssetData {
private final Map map = new HashMap<>();
private final Locale locale;
- private final Uri uri;
+ private final ResourceUrn urn;
/**
- * @param uri the id of the data set, never null
.
+ * @param urn the id of the data set, never null
.
* @param locale the locale of the data set, never null
.
*/
- public TranslationData(Uri uri, Locale locale) {
- Preconditions.checkArgument(uri != null);
+ public TranslationData(ResourceUrn urn, Locale locale) {
+ Preconditions.checkArgument(urn != null);
Preconditions.checkArgument(locale != null);
- this.uri = uri;
+ this.urn = urn;
this.locale = locale;
}
@@ -58,7 +58,7 @@ public Locale getLocale() {
/**
* @return the project uri this data set belongs to.
*/
- public Uri getProjectUri() {
- return uri;
+ public ResourceUrn getProjectUrn() {
+ return urn;
}
}
diff --git a/engine/src/main/java/org/terasology/engine/i18n/assets/TranslationFormat.java b/engine/src/main/java/org/terasology/engine/i18n/assets/TranslationFormat.java
index 152af4d9f85..ec3ec7fdbec 100644
--- a/engine/src/main/java/org/terasology/engine/i18n/assets/TranslationFormat.java
+++ b/engine/src/main/java/org/terasology/engine/i18n/assets/TranslationFormat.java
@@ -9,7 +9,6 @@
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonParseException;
-import org.terasology.engine.core.SimpleUri;
import org.terasology.gestalt.assets.ResourceUrn;
import org.terasology.gestalt.assets.exceptions.InvalidAssetFilenameException;
import org.terasology.gestalt.assets.format.AssetDataFile;
@@ -91,8 +90,8 @@ public TranslationData load(ResourceUrn urn, List inputs) throws
Locale locale = localeFromFilename(file.getFilename());
Name projName = basenameFromFilename(file.getFilename());
- SimpleUri projUri = new SimpleUri(urn.getModuleName(), projName);
- TranslationData data = new TranslationData(projUri, locale);
+ ResourceUrn projUrn = new ResourceUrn(urn.getModuleName(), projName);
+ TranslationData data = new TranslationData(projUrn, locale);
try (InputStreamReader isr = new InputStreamReader(file.openStream(), Charsets.UTF_8)) {
Map entry = gson.fromJson(isr, MAP_TOKEN.getType());
diff --git a/engine/src/main/java/org/terasology/engine/logic/console/commands/CoreCommands.java b/engine/src/main/java/org/terasology/engine/logic/console/commands/CoreCommands.java
index 9c1fda59b9b..eb3c0621bb6 100644
--- a/engine/src/main/java/org/terasology/engine/logic/console/commands/CoreCommands.java
+++ b/engine/src/main/java/org/terasology/engine/logic/console/commands/CoreCommands.java
@@ -8,7 +8,6 @@
import org.terasology.engine.config.SystemConfig;
import org.terasology.engine.core.GameEngine;
import org.terasology.engine.core.PathManager;
-import org.terasology.engine.core.SimpleUri;
import org.terasology.engine.core.TerasologyConstants;
import org.terasology.engine.core.Time;
import org.terasology.engine.core.modes.StateLoading;
@@ -281,7 +280,7 @@ public void setTimeDilation(@CommandParam("dilation") float rate) {
@Command(shortDescription = "Changes the UI language")
public String setLanguage(@CommandParam("language-tag") String langTag) {
Locale locale = Locale.forLanguageTag(langTag);
- TranslationProject proj = translationSystem.getProject(new SimpleUri("engine:menu"));
+ TranslationProject proj = translationSystem.getProject(new ResourceUrn("engine:menu"));
// Try if language exists
if (proj.getAvailableLocales().contains(locale)) {
diff --git a/engine/src/main/java/org/terasology/engine/persistence/typeHandling/extensionTypes/ChunkMeshTypeHandler.java b/engine/src/main/java/org/terasology/engine/persistence/typeHandling/extensionTypes/ChunkMeshTypeHandler.java
index cd73dd356a0..4fbb3053f1a 100644
--- a/engine/src/main/java/org/terasology/engine/persistence/typeHandling/extensionTypes/ChunkMeshTypeHandler.java
+++ b/engine/src/main/java/org/terasology/engine/persistence/typeHandling/extensionTypes/ChunkMeshTypeHandler.java
@@ -5,12 +5,12 @@
import org.lwjgl.BufferUtils;
import org.terasology.engine.rendering.primitives.ChunkMesh;
+import org.terasology.engine.rendering.primitives.ChunkMeshImpl;
import org.terasology.persistence.typeHandling.PersistedData;
import org.terasology.persistence.typeHandling.PersistedDataSerializer;
import org.terasology.persistence.typeHandling.TypeHandler;
import java.nio.ByteBuffer;
-import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -44,19 +44,13 @@ public Optional deserialize(PersistedData data) {
directBuffer.rewind();
asBuffers.add(directBuffer);
}
- ChunkMesh result = new ChunkMesh();
+ ChunkMesh result = new ChunkMeshImpl();
for (ChunkMesh.RenderType renderType : ChunkMesh.RenderType.values()) {
result.getVertexElements(renderType).buffer.replace(asBuffers.remove(0));
result.getVertexElements(renderType).indices.replace(asBuffers.remove(0));
}
- result.generateVBOs();
+ result.updateMesh();
return Optional.of(result);
}
- private ByteBuffer asByteBuffer(IntBuffer in) {
- ByteBuffer result = ByteBuffer.allocate(in.limit() * 4);
- in.rewind();
- result.asIntBuffer().put(in);
- return result;
- }
}
diff --git a/engine/src/main/java/org/terasology/engine/rendering/cameras/OpenVRStereoCamera.java b/engine/src/main/java/org/terasology/engine/rendering/cameras/OpenVRStereoCamera.java
index c63b35e34b9..8542f113172 100644
--- a/engine/src/main/java/org/terasology/engine/rendering/cameras/OpenVRStereoCamera.java
+++ b/engine/src/main/java/org/terasology/engine/rendering/cameras/OpenVRStereoCamera.java
@@ -6,18 +6,16 @@
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector4f;
-import org.terasology.engine.config.RenderingConfig;
import org.terasology.engine.registry.CoreRegistry;
import org.terasology.engine.rendering.openvrprovider.OpenVRProvider;
import org.terasology.engine.rendering.openvrprovider.OpenVRUtil;
import org.terasology.engine.rendering.world.WorldRenderer;
import org.terasology.engine.rendering.world.WorldRenderer.RenderingStage;
-import org.terasology.engine.world.WorldProvider;
/**
* Camera which can be used to render stereoscopic images of the scene for VR.
*/
-public class OpenVRStereoCamera extends SubmersibleCamera {
+public class OpenVRStereoCamera extends Camera {
private final Matrix4f projectionMatrixLeftEye = new Matrix4f();
private final Matrix4f projectionMatrixRightEye = new Matrix4f();
@@ -46,8 +44,7 @@ public class OpenVRStereoCamera extends SubmersibleCamera {
private final Matrix4f viewTranslationRightEye = new Matrix4f();
private final OpenVRProvider vrProvider;
- public OpenVRStereoCamera(OpenVRProvider provider, WorldProvider worldProvider, RenderingConfig renderingConfig) {
- super(worldProvider, renderingConfig);
+ public OpenVRStereoCamera(OpenVRProvider provider) {
vrProvider = provider;
// OpenVR's projection matrix is such that this is approximately true.
zFar = 400.0f;
diff --git a/engine/src/main/java/org/terasology/engine/rendering/cameras/PerspectiveCamera.java b/engine/src/main/java/org/terasology/engine/rendering/cameras/PerspectiveCamera.java
index a3f31a07f38..73f3eb04dcc 100644
--- a/engine/src/main/java/org/terasology/engine/rendering/cameras/PerspectiveCamera.java
+++ b/engine/src/main/java/org/terasology/engine/rendering/cameras/PerspectiveCamera.java
@@ -7,7 +7,6 @@
import org.terasology.engine.config.RenderingConfig;
import org.terasology.engine.core.subsystem.DisplayDevice;
import org.terasology.engine.rendering.nui.layers.mainMenu.videoSettings.CameraSetting;
-import org.terasology.engine.world.WorldProvider;
import org.terasology.engine.world.chunks.Chunks;
import org.terasology.math.TeraMath;
@@ -21,7 +20,7 @@
/**
* Simple default camera.
*/
-public class PerspectiveCamera extends SubmersibleCamera implements PropertyChangeListener {
+public class PerspectiveCamera extends Camera implements PropertyChangeListener {
// Values used for smoothing
private Deque previousPositions = new LinkedList<>();
private Deque previousViewingDirections = new LinkedList<>();
@@ -35,13 +34,13 @@ public class PerspectiveCamera extends SubmersibleCamera implements PropertyChan
private float cachedBobbingRotationOffsetFactor;
private float cachedBobbingVerticalOffsetFactor;
private final DisplayDevice displayDevice;
+ private final RenderingConfig renderingConfig;
private Vector3f tempRightVector = new Vector3f();
- public PerspectiveCamera(WorldProvider worldProvider, RenderingConfig renderingConfig,
- DisplayDevice displayDevice) {
- super(worldProvider, renderingConfig);
+ public PerspectiveCamera(RenderingConfig renderingConfig, DisplayDevice displayDevice) {
this.displayDevice = displayDevice;
+ this.renderingConfig = renderingConfig;
this.cameraSettings = renderingConfig.getCameraSettings();
displayDevice.subscribe(DISPLAY_RESOLUTION_CHANGE, this);
diff --git a/engine/src/main/java/org/terasology/engine/rendering/cameras/SubmersibleCamera.java b/engine/src/main/java/org/terasology/engine/rendering/cameras/SubmersibleCamera.java
deleted file mode 100644
index c1ae32cb7d5..00000000000
--- a/engine/src/main/java/org/terasology/engine/rendering/cameras/SubmersibleCamera.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2021 The Terasology Foundation
-// SPDX-License-Identifier: Apache-2.0
-package org.terasology.engine.rendering.cameras;
-
-import org.joml.Vector3f;
-import org.terasology.engine.config.RenderingConfig;
-import org.terasology.engine.rendering.RenderHelper;
-import org.terasology.engine.world.WorldProvider;
-
-public abstract class SubmersibleCamera extends Camera {
-
- /* Used for Underwater Checks */
- RenderingConfig renderingConfig;
- private WorldProvider worldProvider;
-
- public SubmersibleCamera(WorldProvider worldProvider, RenderingConfig renderingConfig) {
- this.worldProvider = worldProvider;
- this.renderingConfig = renderingConfig;
- }
-
- /**
- * Returns True if the head of the player is underwater. False otherwise.
- *
- * Takes in account waves if present.
- *
- * @return True if the head of the player is underwater. False otherwise.
- */
- public boolean isUnderWater() {
- // TODO: Making this as a subscribable value especially for node "ChunksRefractiveReflectiveNode",
- // TODO: glDisable and glEnable state changes on that node will be dynamically added/removed based on this value.
- Vector3f cameraPosition = new Vector3f(this.getPosition());
-
- // Compensate for waves
- if (renderingConfig.isAnimateWater()) {
- cameraPosition.y -= RenderHelper.evaluateOceanHeightAtPosition(cameraPosition, worldProvider.getTime().getDays());
- }
-
- if (worldProvider.isBlockRelevant(cameraPosition)) {
- return worldProvider.getBlock(cameraPosition).isLiquid();
- }
- return false;
- }
-}
diff --git a/engine/src/main/java/org/terasology/engine/rendering/iconmesh/IconMeshFactory.java b/engine/src/main/java/org/terasology/engine/rendering/iconmesh/IconMeshFactory.java
index c231435a84b..bd019ff70ea 100644
--- a/engine/src/main/java/org/terasology/engine/rendering/iconmesh/IconMeshFactory.java
+++ b/engine/src/main/java/org/terasology/engine/rendering/iconmesh/IconMeshFactory.java
@@ -2,18 +2,21 @@
// SPDX-License-Identifier: Apache-2.0
package org.terasology.engine.rendering.iconmesh;
+import org.joml.Vector2f;
+import org.joml.Vector2fc;
+import org.joml.Vector3f;
import org.joml.Vector4f;
-import org.terasology.gestalt.assets.Asset;
-import org.terasology.gestalt.assets.ResourceUrn;
-import org.terasology.engine.rendering.primitives.Tessellator;
-import org.terasology.engine.rendering.primitives.TessellatorHelper;
-import org.terasology.joml.geom.Rectanglei;
-import org.terasology.gestalt.module.sandbox.API;
-import org.terasology.gestalt.naming.Name;
import org.terasology.engine.rendering.assets.mesh.Mesh;
import org.terasology.engine.rendering.assets.mesh.MeshData;
+import org.terasology.engine.rendering.assets.mesh.StandardMeshData;
import org.terasology.engine.rendering.assets.texture.TextureRegion;
import org.terasology.engine.utilities.Assets;
+import org.terasology.gestalt.assets.Asset;
+import org.terasology.gestalt.assets.ResourceUrn;
+import org.terasology.gestalt.module.sandbox.API;
+import org.terasology.gestalt.naming.Name;
+import org.terasology.joml.geom.Rectanglei;
+import org.terasology.nui.Color;
import java.nio.ByteBuffer;
@@ -30,7 +33,8 @@ public static Mesh getIconMesh(TextureRegion region) {
return Assets.get(new ResourceUrn(urn.getModuleName(), IconMeshDataProducer.ICON_DISCRIMINATOR,
urn.getResourceName()), Mesh.class).get();
} else {
- Name fragName = new Name(urn.getResourceName().toString() + ResourceUrn.FRAGMENT_SEPARATOR + urn.getFragmentName().toString());
+ Name fragName =
+ new Name(urn.getResourceName().toString() + ResourceUrn.FRAGMENT_SEPARATOR + urn.getFragmentName().toString());
return Assets.get(new ResourceUrn(urn.getModuleName(), IconMeshDataProducer.ICON_DISCRIMINATOR,
fragName), Mesh.class).get();
}
@@ -59,6 +63,88 @@ public static Mesh generateIconMesh(ResourceUrn urn, TextureRegion tex, int alph
}
}
+ private static void addPixel(StandardMeshData mesh, Vector2fc position, float size, Color c) {
+ Vector3f pos = new Vector3f();
+ Vector3f norm = new Vector3f();
+ final float sizeHalf = size / 2;
+
+ int firstIndex = mesh.position.getPosition();
+
+ // top
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, sizeHalf, sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, sizeHalf, sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, sizeHalf, -sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, sizeHalf, -sizeHalf));
+ for (int i = 0; i < 4; i++) {
+ mesh.normal.put(norm.set(0, 1.0f, 0));
+ mesh.color0.put(c);
+ }
+
+ // left
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, -sizeHalf, -sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, -sizeHalf, sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, sizeHalf, sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, sizeHalf, -sizeHalf));
+ for (int i = 0; i < 4; i++) {
+ mesh.normal.put(norm.set(-1.0f, 0, 0));
+ mesh.color0.put(c);
+ }
+
+ // right
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, sizeHalf, -sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, sizeHalf, sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, -sizeHalf, sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, -sizeHalf, -sizeHalf));
+ for (int i = 0; i < 4; i++) {
+ mesh.normal.put(norm.set(1.0f, 0, 0));
+ mesh.color0.put(c);
+ }
+
+ // darkern for sides facing left, right and bottom
+ Color cd = new Color(c.rf() * 0.6f, c.gf() * 0.6f, c.bf() * 0.6f, c.af());
+ // back
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, sizeHalf, -sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, sizeHalf, -sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, -sizeHalf, -sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, -sizeHalf, -sizeHalf));
+ for (int i = 0; i < 4; i++) {
+ mesh.normal.put(norm.set(0, 0, -1.0f));
+ mesh.color0.put(cd);
+ }
+
+ // front
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, -sizeHalf, sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, -sizeHalf, sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, sizeHalf, sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, sizeHalf, sizeHalf));
+ for (int i = 0; i < 4; i++) {
+ mesh.normal.put(norm.set(0, 0, 1.0f));
+ mesh.color0.put(cd);
+ }
+
+ // bottom
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, -sizeHalf, -sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, -sizeHalf, -sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(sizeHalf, -sizeHalf, sizeHalf));
+ mesh.position.put(pos.set(position, 0.0f).add(-sizeHalf, -sizeHalf, sizeHalf));
+ for (int i = 0; i < 4; i++) {
+ mesh.normal.put(norm.set(0, -1, 0f));
+ mesh.color0.put(cd);
+ }
+
+
+ int lastIndex = mesh.position.getPosition();
+ for (int i = firstIndex; i < lastIndex - 2; i += 4) {
+ mesh.indices.put(i);
+ mesh.indices.put(i + 1);
+ mesh.indices.put(i + 2);
+
+ mesh.indices.put(i + 2);
+ mesh.indices.put(i + 3);
+ mesh.indices.put(i);
+ }
+ }
+
public static MeshData generateIconMeshData(TextureRegion tex, int alphaLimit, boolean withContour, Vector4f colorContour) {
ByteBuffer buffer = tex.getTexture().getData().getBuffers()[0];
@@ -70,8 +156,9 @@ public static MeshData generateIconMeshData(TextureRegion tex, int alphaLimit, b
float textureSize = Math.max(tex.getWidth(), tex.getHeight());
- Tessellator tessellator = new Tessellator();
-
+ StandardMeshData mesh = new StandardMeshData();
+ Vector2f pos = new Vector2f();
+ Color color = new Color();
for (int y = 0; y < tex.getHeight(); y++) {
for (int x = 0; x < tex.getWidth(); x++) {
int r = buffer.get((posY + y) * stride + (posX + x) * 4) & 255;
@@ -80,9 +167,12 @@ public static MeshData generateIconMeshData(TextureRegion tex, int alphaLimit, b
int a = buffer.get((posY + y) * stride + (posX + x) * 4 + 3) & 255;
if (a > alphaLimit) {
- Vector4f color = new Vector4f(r / 255f, g / 255f, b / 255f, a / 255f);
- TessellatorHelper.addBlockMesh(tessellator, color, 2f / textureSize, 1.0f, 0.5f,
- 2f / textureSize * x - 1f, 2f / textureSize * (tex.getHeight() - y - 1) - 1f, 0f);
+ color.setRed(r)
+ .setGreen(g)
+ .setBlue(b)
+ .setAlpha(a);
+ pos.set(2f / textureSize * x - 1f, 2f / textureSize * (tex.getHeight() - y - 1) - 1f);
+ addPixel(mesh, pos, 2f / textureSize, color);
if (withContour) {
int newX = 0;
@@ -129,18 +219,18 @@ public static MeshData generateIconMeshData(TextureRegion tex, int alphaLimit, b
}
if (newA < alphaLimit) {
- Vector4f cColor = new Vector4f(colorContour.x / 255f,
- colorContour.y / 255f,
- colorContour.z / 255f, colorContour.w);
- TessellatorHelper.addBlockMesh(tessellator, cColor, 0.125f, 1.0f, 0.5f,
- 2f * 0.0625f * newX - 0.5f, 0.125f * (15 - newY) - 1f, 0f);
+ color.setRed(colorContour.x)
+ .setGreen(colorContour.y)
+ .setBlue(colorContour.z)
+ .setAlpha(colorContour.w);
+ addPixel(mesh, pos.set(2f * 0.0625f * newX - 0.5f, 0.125f * (15 - newY) - 1f), 0.125f, color);
}
}
}
}
}
}
- return tessellator.buildMeshData();
+ return mesh;
}
}
diff --git a/engine/src/main/java/org/terasology/engine/rendering/logic/ChunkMeshRenderer.java b/engine/src/main/java/org/terasology/engine/rendering/logic/ChunkMeshRenderer.java
index a78788a64ef..0dbfd8e1b8d 100644
--- a/engine/src/main/java/org/terasology/engine/rendering/logic/ChunkMeshRenderer.java
+++ b/engine/src/main/java/org/terasology/engine/rendering/logic/ChunkMeshRenderer.java
@@ -3,7 +3,8 @@
package org.terasology.engine.rendering.logic;
-import org.lwjgl.opengl.GL15;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.terasology.engine.entitySystem.entity.EntityRef;
import org.terasology.engine.entitySystem.entity.lifecycleEvents.BeforeDeactivateComponent;
import org.terasology.engine.entitySystem.entity.lifecycleEvents.OnActivatedComponent;
@@ -11,20 +12,14 @@
import org.terasology.engine.entitySystem.systems.BaseComponentSystem;
import org.terasology.engine.entitySystem.systems.RegisterMode;
import org.terasology.engine.entitySystem.systems.RegisterSystem;
-import org.terasology.engine.entitySystem.systems.UpdateSubscriberSystem;
import org.terasology.engine.logic.location.LocationComponent;
import org.terasology.engine.registry.In;
-import org.terasology.engine.rendering.primitives.ChunkMesh;
import org.terasology.engine.rendering.world.RenderableWorld;
import org.terasology.engine.world.chunks.RenderableChunk;
-import java.lang.ref.PhantomReference;
-import java.lang.ref.ReferenceQueue;
import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
-import java.util.Set;
/**
* Handles entities with ChunkMeshComponents, and passes them to the rendering engine.
@@ -32,13 +27,13 @@
* Also handles disposing of the mesh data, assuming the mesh is never changed after being added.
*/
@RegisterSystem(RegisterMode.CLIENT)
-public class ChunkMeshRenderer extends BaseComponentSystem implements UpdateSubscriberSystem {
+public class ChunkMeshRenderer extends BaseComponentSystem {
+ private static final Logger logger = LoggerFactory.getLogger(ChunkMeshRenderer.class);
+
@In
private RenderableWorld renderableWorld;
private Map pseudoChunks = new HashMap<>();
- private ReferenceQueue disposalQueue = new ReferenceQueue<>();
- private Set disposalSet = new HashSet<>();
@Override
public void initialise() {
@@ -48,43 +43,24 @@ public void initialise() {
@ReceiveEvent(components = {ChunkMeshComponent.class, LocationComponent.class})
public void onNewMesh(OnActivatedComponent event, EntityRef entity, ChunkMeshComponent mesh) {
pseudoChunks.put(entity, new EntityBasedRenderableChunk(entity));
- disposalSet.add(new BuffersReference(mesh.mesh, disposalQueue));
}
- @ReceiveEvent(components = {MeshComponent.class, LocationComponent.class})
+ @ReceiveEvent(components = ChunkMeshComponent.class)
public void onDestroyMesh(BeforeDeactivateComponent event, EntityRef entity) {
- pseudoChunks.remove(entity);
+ EntityBasedRenderableChunk renderableChunk = pseudoChunks.remove(entity);
+ if (renderableChunk != null) {
+ renderableChunk.disposeMesh();
+ }
}
public Collection extends RenderableChunk> getRenderableChunks() {
return pseudoChunks.values();
}
- @Override
- public void update(float delta) {
- BuffersReference reference = (BuffersReference) disposalQueue.poll();
- while (reference != null) {
- GL15.glDeleteBuffers(reference.bufferIds);
- disposalSet.remove(reference);
- reference = (BuffersReference) disposalQueue.poll();
- }
- }
-
@Override
public void shutdown() {
- for (BuffersReference reference : disposalSet) {
- GL15.glDeleteBuffers(reference.bufferIds);
- }
- }
-
- private static class BuffersReference extends PhantomReference {
- int[] bufferIds;
-
- BuffersReference(ChunkMesh mesh, ReferenceQueue queue) {
- super(mesh, queue);
- bufferIds = new int[mesh.vertexBuffers.length + mesh.idxBuffers.length];
- System.arraycopy(mesh.vertexBuffers, 0, bufferIds, 0, mesh.vertexBuffers.length);
- System.arraycopy(mesh.idxBuffers, 0, bufferIds, mesh.vertexBuffers.length, mesh.idxBuffers.length);
+ for (EntityBasedRenderableChunk reference : pseudoChunks.values()) {
+ reference.disposeMesh();
}
}
}
diff --git a/engine/src/main/java/org/terasology/engine/rendering/nui/editor/layers/NUIEditorSettingsScreen.java b/engine/src/main/java/org/terasology/engine/rendering/nui/editor/layers/NUIEditorSettingsScreen.java
index 1a691230c2a..231b0bc3198 100644
--- a/engine/src/main/java/org/terasology/engine/rendering/nui/editor/layers/NUIEditorSettingsScreen.java
+++ b/engine/src/main/java/org/terasology/engine/rendering/nui/editor/layers/NUIEditorSettingsScreen.java
@@ -3,18 +3,17 @@
package org.terasology.engine.rendering.nui.editor.layers;
import com.google.common.collect.Lists;
-import org.terasology.gestalt.assets.ResourceUrn;
import org.terasology.engine.config.Config;
import org.terasology.engine.config.SystemConfig;
-import org.terasology.engine.core.SimpleUri;
import org.terasology.engine.i18n.TranslationProject;
import org.terasology.engine.i18n.TranslationSystem;
+import org.terasology.engine.registry.In;
+import org.terasology.engine.rendering.nui.CoreScreenLayer;
import org.terasology.engine.rendering.nui.layers.mainMenu.settings.LocaleRenderer;
+import org.terasology.gestalt.assets.ResourceUrn;
import org.terasology.nui.WidgetUtil;
import org.terasology.nui.databinding.BindHelper;
import org.terasology.nui.widgets.UIDropdownScrollable;
-import org.terasology.engine.registry.In;
-import org.terasology.engine.rendering.nui.CoreScreenLayer;
import java.util.ArrayList;
import java.util.Collections;
@@ -48,7 +47,7 @@ public void initialise() {
alternativeLocale = find("alternativeLocale", UIDropdownScrollable.class);
if (alternativeLocale != null) {
// Build the list of available locales and set the dropdown's options to them.
- TranslationProject menuProject = translationSystem.getProject(new SimpleUri("engine:menu"));
+ TranslationProject menuProject = translationSystem.getProject(new ResourceUrn("engine:menu"));
List locales = new ArrayList<>(menuProject.getAvailableLocales());
Collections.sort(locales, ((Object o1, Object o2) -> (o1.toString().compareTo(o2.toString()))));
alternativeLocale.setOptions(Lists.newArrayList(locales));
diff --git a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/ingame/metrics/DebugOverlay.java b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/ingame/metrics/DebugOverlay.java
index 17c4c84978c..40acfd5ee2b 100644
--- a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/ingame/metrics/DebugOverlay.java
+++ b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/ingame/metrics/DebugOverlay.java
@@ -15,7 +15,6 @@
import org.terasology.engine.persistence.StorageManager;
import org.terasology.engine.registry.In;
import org.terasology.engine.rendering.nui.CoreScreenLayer;
-import org.terasology.engine.rendering.primitives.ChunkTessellator;
import org.terasology.engine.world.WorldProvider;
import org.terasology.engine.world.chunks.Chunks;
import org.terasology.nui.databinding.ReadOnlyBinding;
@@ -147,8 +146,7 @@ public String get() {
debugLine4.bindText(new ReadOnlyBinding() {
@Override
public String get() {
- return String.format("Total VUs: %s, World Time: %.3f, Time Dilation: %.1f",
- ChunkTessellator.getVertexArrayUpdateCount(),
+ return String.format("World Time: %.3f, Time Dilation: %.1f",
worldProvider.getTime().getDays() - 0.0005f, // use floor instead of rounding up
time.getGameTimeDilation());
}
diff --git a/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMesh.java b/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMesh.java
index 6ce759633e7..0ed52c6dd78 100644
--- a/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMesh.java
+++ b/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMesh.java
@@ -6,7 +6,6 @@
import org.joml.Vector2fc;
import org.joml.Vector3f;
import org.joml.Vector3fc;
-import org.lwjgl.opengl.GL30;
import org.terasology.engine.rendering.assets.material.Material;
import org.terasology.engine.rendering.assets.mesh.resource.GLAttributes;
import org.terasology.engine.rendering.assets.mesh.resource.IndexResource;
@@ -19,18 +18,48 @@
import org.terasology.nui.Color;
import org.terasology.nui.Colorc;
-import java.util.concurrent.locks.ReentrantLock;
-
/**
* Chunk meshes store, manipulate and render the vertex data of tessellated chunks.
*/
-public class ChunkMesh {
+public interface ChunkMesh {
+
+ VertexElements getVertexElements(ChunkMesh.RenderType renderType);
+
+ /**
+ * has any vertex elements for is cleared from {@link #discardData()}
+ *
+ * @return if the vertex data is cleared
+ */
+ boolean hasVertexElements();
+
+ /**
+ * update the mesh data
+ *
+ * @return true if the data has been updated
+ */
+ boolean updateMesh();
+
+ void discardData();
+
+ void updateMaterial(Material chunkMaterial, Vector3fc chunkPosition, boolean chunkIsAnimated);
+
+ int triangleCount(ChunkMesh.RenderPhase phase);
+
+ int getTimeToGenerateBlockVertices();
+
+ int getTimeToGenerateOptimizedBuffers();
+
+ void dispose();
+
+ int render(ChunkMesh.RenderPhase type);
+
+
/**
* Possible rendering types.
*/
@API
- public enum RenderType {
+ enum RenderType {
OPAQUE(0),
TRANSLUCENT(1),
BILLBOARD(2),
@@ -47,257 +76,14 @@ public int getIndex() {
}
}
- public enum RenderPhase {
+ enum RenderPhase {
OPAQUE,
ALPHA_REJECT,
REFRACTIVE,
Z_PRE_PASS
}
- /* VERTEX DATA */
- public final int[] vertexBuffers = new int[4];
- public final int[] idxBuffers = new int[4];
- public final int[] vertexCount = new int[4];
- public final int[] vaoCount = new int[4];
-
- /* STATS */
- private int triangleCount = -1;
-
- /* TEMPORARY DATA */
- private VertexElements[] vertexElements = new VertexElements[RenderType.values().length];
-
- private boolean disposed;
-
- /* CONCURRENCY */
- private final ReentrantLock lock = new ReentrantLock();
-
- /* MEASUREMENTS */
- private int timeToGenerateBlockVertices;
- private int timeToGenerateOptimizedBuffers;
-
- public ChunkMesh() {
- for (RenderType type : RenderType.values()) {
- vertexElements[type.ordinal()] = new VertexElements();
- }
- }
-
- public VertexElements getVertexElements(RenderType renderType) {
- return vertexElements[renderType.ordinal()];
- }
-
- public boolean hasVertexElements() {
- return vertexElements != null;
- }
-
- /**
- * Generates the VBOs from the pre calculated arrays.
- *
- * @return True if something was generated
- */
- public boolean generateVBOs() {
- if (lock.tryLock()) {
- try {
- // IMPORTANT: A mesh can only be generated once.
- if (vertexElements == null || disposed) {
- return false;
- }
-
- // Make sure that if it has already been generated, the previous buffers are freed
- dispose();
- disposed = false;
-
- for (RenderType type : RenderType.values()) {
- generateVBO(type);
- }
-
- // Calculate the final amount of triangles
- triangleCount = (vertexCount[0] + vertexCount[1] + vertexCount[2] + vertexCount[3]) / 3;
- } finally {
- lock.unlock();
- }
-
- return true;
- }
-
- return false;
- }
-
- private void generateVBO(RenderType type) {
- VertexElements elements = vertexElements[type.ordinal()];
- int id = type.getIndex();
- if (!disposed && elements.buffer.elements() > 0) {
- vertexBuffers[id] = GL30.glGenBuffers();
- idxBuffers[id] = GL30.glGenBuffers();
- vaoCount[id] = GL30.glGenVertexArrays();
-
- GL30.glBindVertexArray(vaoCount[id]);
-
- GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vertexBuffers[id]);
- elements.buffer.writeBuffer(buffer -> GL30.glBufferData(GL30.GL_ARRAY_BUFFER, buffer, GL30.GL_STATIC_DRAW));
-
- for (VertexResource.VertexDefinition definition : elements.buffer.definitions()) {
- GL30.glEnableVertexAttribArray(definition.location);
- if (definition.location == VertexElements.FLAGS_INDEX) {
- GL30.glVertexAttribIPointer(definition.location, definition.attribute.count,
- definition.attribute.mapping.glType, elements.buffer.inStride(), definition.offset);
- } else {
- GL30.glVertexAttribPointer(definition.location, definition.attribute.count,
- definition.attribute.mapping.glType, false, elements.buffer.inStride(), definition.offset);
- }
- }
-
- GL30.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, idxBuffers[id]);
- elements.indices.writeBuffer((buffer) -> GL30.glBufferData(GL30.GL_ELEMENT_ARRAY_BUFFER, buffer, GL30.GL_STATIC_DRAW));
- vertexCount[id] = elements.indices.indices();
-
- GL30.glBindVertexArray(0);
-
- } else {
- vertexBuffers[id] = 0;
- idxBuffers[id] = 0;
- vertexCount[id] = 0;
- }
- }
-
- /**
- * Save space by removing the data that was used to construct the mesh, but
- * after discardData is called, the mesh can't be serialized, so it shouldn't
- * be used in contexts where that might be necessary.
- */
- public void discardData() {
- vertexElements = null;
- }
-
- private void renderVbo(int id) {
- if (lock.tryLock()) {
- try {
- if (vertexBuffers[id] <= 0 || disposed) {
- return;
- }
-
- GL30.glBindVertexArray(vaoCount[id]);
- GL30.glDrawElements(GL30.GL_TRIANGLES, vertexCount[id], GL30.GL_UNSIGNED_INT, 0);
- GL30.glBindVertexArray(0);
- } finally {
- lock.unlock();
- }
- }
- }
-
- /**
- * Updates a given material with information such as the World position of a chunk and whether it is animated.
- *
- * @param chunkMaterial a Material instance to be updated
- * @param chunkPosition a Vector3f instance holding the world coordinates of a chunk
- * @param chunkIsAnimated a boolean: true if the chunk is animated, false otherwise
- */
- public void updateMaterial(Material chunkMaterial, Vector3fc chunkPosition, boolean chunkIsAnimated) {
- chunkMaterial.setFloat3("chunkPositionWorld", chunkPosition, true);
- chunkMaterial.setFloat("animated", chunkIsAnimated ? 1.0f : 0.0f, true);
- }
-
- public int render(RenderPhase type) {
- switch (type) {
- case OPAQUE:
- renderVbo(0);
- break;
- case ALPHA_REJECT:
- renderVbo(1);
- renderVbo(2);
- break;
- case REFRACTIVE:
- renderVbo(3);
- break;
- default:
- break;
- }
- return triangleCount();
- }
-
- /**
- * Disposes of all the data stored in an instance of this class and
- * the associated data stored in the GLBufferPool instance provided on construction.
- *
- * ChunkMesh instances cannot be un-disposed.
- */
- public void dispose() {
- lock.lock();
- try {
- if (!disposed) {
- for (int i = 0; i < vertexBuffers.length; i++) {
- int id = vertexBuffers[i];
- if (id != 0) {
- GL30.glDeleteBuffers(id);
- vertexBuffers[i] = 0;
- }
-
- id = idxBuffers[i];
- if (id != 0) {
- GL30.glDeleteBuffers(id);
- idxBuffers[i] = 0;
- }
-
- id = vaoCount[i];
- if (id != 0) {
- GL30.glDeleteVertexArrays(id);
- vaoCount[i] = 0;
- }
- }
-
- disposed = true;
- }
- } finally {
- lock.unlock();
- }
- }
-
- public boolean isDisposed() {
- return disposed;
- }
-
- public int triangleCount(RenderPhase phase) {
- if (phase == RenderPhase.OPAQUE) {
- return vertexCount[0] / 3;
- } else if (phase == RenderPhase.ALPHA_REJECT) {
- return (vertexCount[1] + vertexCount[2]) / 3;
- } else {
- return vertexCount[3] / 3;
- }
- }
-
- private int triangleCount() {
- return triangleCount;
- }
-
- /**
- * Returns true if an instance of this class stores no triangles.
- *
- * @return True if no triangles are stored in the instance, false otherwise.
- */
- public boolean isEmpty() {
- return triangleCount == 0;
- }
-
- void setTimeToGenerateBlockVertices(int timeToGenerateBlockVertices) {
- this.timeToGenerateBlockVertices = timeToGenerateBlockVertices;
- }
-
- public int getTimeToGenerateBlockVertices() {
- return timeToGenerateBlockVertices;
- }
-
- void setTimeToGenerateOptimizedBuffers(int timeToGenerateOptimizedBuffers) {
- this.timeToGenerateOptimizedBuffers = timeToGenerateOptimizedBuffers;
- }
-
- public int getTimeToGenerateOptimizedBuffers() {
- return timeToGenerateOptimizedBuffers;
- }
-
- /**
- * Data structure for storing vertex data. Abused like a "struct" in C/C++. Just sad.
- */
- public static class VertexElements {
+ class VertexElements {
public static final int VERTEX_INDEX = 0; // vec3
public static final int NORMAL_INDEX = 1; // vec3
@@ -327,7 +113,7 @@ public static class VertexElements {
public final VertexFloatAttributeBinding sunlight; // this could be changed to a single byte
public final VertexFloatAttributeBinding blockLight; // this could be changed to a single byte
public final VertexFloatAttributeBinding ambientOcclusion; // this could be changed to a single byte
- public int vertexCount;
+ public int vertexCount;
VertexElements() {
diff --git a/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMeshImpl.java b/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMeshImpl.java
new file mode 100644
index 00000000000..45777bf0a6e
--- /dev/null
+++ b/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMeshImpl.java
@@ -0,0 +1,230 @@
+// Copyright 2021 The Terasology Foundation
+// SPDX-License-Identifier: Apache-2.0
+
+package org.terasology.engine.rendering.primitives;
+
+import org.joml.Vector3fc;
+import org.lwjgl.opengl.GL30;
+import org.terasology.engine.rendering.assets.material.Material;
+import org.terasology.engine.rendering.assets.mesh.resource.VertexResource;
+
+public class ChunkMeshImpl implements ChunkMesh {
+
+ /* VERTEX DATA */
+ private final int[] vertexBuffers = new int[4];
+ private final int[] idxBuffers = new int[4];
+ private final int[] vertexCount = new int[4];
+ private final int[] vaoCount = new int[4];
+
+ /* STATS */
+ private int triangleCount = -1;
+
+ /* TEMPORARY DATA */
+ private VertexElements[] vertexElements = new VertexElements[ChunkMesh.RenderType.values().length];
+
+ private boolean disposed;
+
+ /* MEASUREMENTS */
+ private int timeToGenerateBlockVertices;
+ private int timeToGenerateOptimizedBuffers;
+
+ public ChunkMeshImpl() {
+ for (ChunkMesh.RenderType type : ChunkMesh.RenderType.values()) {
+ vertexElements[type.ordinal()] = new VertexElements();
+ }
+ }
+
+ @Override
+ public VertexElements getVertexElements(ChunkMesh.RenderType renderType) {
+ return vertexElements[renderType.ordinal()];
+ }
+
+ @Override
+ public boolean hasVertexElements() {
+ return vertexElements != null;
+ }
+
+ @Override
+ public boolean updateMesh() {
+
+ if (vertexElements == null || disposed) {
+ return false;
+ }
+
+ // Make sure that if it has already been generated, the previous buffers are freed
+ dispose();
+ disposed = false;
+
+ for (ChunkMesh.RenderType type : ChunkMesh.RenderType.values()) {
+ generateVBO(type);
+ }
+
+ // Calculate the final amount of triangles
+ triangleCount = (vertexCount[0] + vertexCount[1] + vertexCount[2] + vertexCount[3]) / 3;
+
+ return true;
+ }
+
+ private void generateVBO(ChunkMesh.RenderType type) {
+ VertexElements elements = vertexElements[type.ordinal()];
+ int id = type.getIndex();
+ if (!disposed && elements.buffer.elements() > 0) {
+ vertexBuffers[id] = GL30.glGenBuffers();
+ idxBuffers[id] = GL30.glGenBuffers();
+ vaoCount[id] = GL30.glGenVertexArrays();
+
+ GL30.glBindVertexArray(vaoCount[id]);
+
+ GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vertexBuffers[id]);
+ elements.buffer.writeBuffer(buffer -> GL30.glBufferData(GL30.GL_ARRAY_BUFFER, buffer, GL30.GL_STATIC_DRAW));
+
+ for (VertexResource.VertexDefinition definition : elements.buffer.definitions()) {
+ GL30.glEnableVertexAttribArray(definition.location);
+ if (definition.location == VertexElements.FLAGS_INDEX) {
+ GL30.glVertexAttribIPointer(definition.location, definition.attribute.count,
+ definition.attribute.mapping.glType, elements.buffer.inStride(), definition.offset);
+ } else {
+ GL30.glVertexAttribPointer(definition.location, definition.attribute.count,
+ definition.attribute.mapping.glType, false, elements.buffer.inStride(), definition.offset);
+ }
+ }
+
+ GL30.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, idxBuffers[id]);
+ elements.indices.writeBuffer((buffer) -> GL30.glBufferData(GL30.GL_ELEMENT_ARRAY_BUFFER, buffer, GL30.GL_STATIC_DRAW));
+ vertexCount[id] = elements.indices.indices();
+
+ GL30.glBindVertexArray(0);
+
+ } else {
+ vertexBuffers[id] = 0;
+ idxBuffers[id] = 0;
+ vertexCount[id] = 0;
+ }
+ }
+
+ /**
+ * Save space by removing the data that was used to construct the mesh, but after discardData is called, the mesh can't be serialized,
+ * so it shouldn't be used in contexts where that might be necessary.
+ */
+ @Override
+ public void discardData() {
+ vertexElements = null;
+ }
+
+ private void renderVbo(int id) {
+ if (vertexBuffers[id] <= 0 || disposed) {
+ return;
+ }
+
+ GL30.glBindVertexArray(vaoCount[id]);
+ GL30.glDrawElements(GL30.GL_TRIANGLES, vertexCount[id], GL30.GL_UNSIGNED_INT, 0);
+ GL30.glBindVertexArray(0);
+ }
+
+ /**
+ * Updates a given material with information such as the World position of a chunk and whether it is animated.
+ *
+ * @param chunkMaterial a Material instance to be updated
+ * @param chunkPosition a Vector3f instance holding the world coordinates of a chunk
+ * @param chunkIsAnimated a boolean: true if the chunk is animated, false otherwise
+ */
+ @Override
+ public void updateMaterial(Material chunkMaterial, Vector3fc chunkPosition, boolean chunkIsAnimated) {
+ chunkMaterial.setFloat3("chunkPositionWorld", chunkPosition, true);
+ chunkMaterial.setFloat("animated", chunkIsAnimated ? 1.0f : 0.0f, true);
+ }
+
+ public int render(ChunkMesh.RenderPhase type) {
+ switch (type) {
+ case OPAQUE:
+ renderVbo(0);
+ break;
+ case ALPHA_REJECT:
+ renderVbo(1);
+ renderVbo(2);
+ break;
+ case REFRACTIVE:
+ renderVbo(3);
+ break;
+ default:
+ break;
+ }
+ return triangleCount();
+ }
+
+ /**
+ * Disposes of all the data stored in an instance of this class and the associated data stored in the GLBufferPool instance provided on
+ * construction.
+ */
+ @Override
+ public void dispose() {
+ if (!disposed) {
+ for (int i = 0; i < vertexBuffers.length; i++) {
+ int id = vertexBuffers[i];
+ if (id != 0) {
+ GL30.glDeleteBuffers(id);
+ vertexBuffers[i] = 0;
+ }
+
+ id = idxBuffers[i];
+ if (id != 0) {
+ GL30.glDeleteBuffers(id);
+ idxBuffers[i] = 0;
+ }
+
+ id = vaoCount[i];
+ if (id != 0) {
+ GL30.glDeleteVertexArrays(id);
+ vaoCount[i] = 0;
+ }
+ }
+
+ disposed = true;
+ }
+ }
+
+ public boolean isDisposed() {
+ return disposed;
+ }
+
+ @Override
+ public int triangleCount(ChunkMesh.RenderPhase phase) {
+ if (phase == ChunkMesh.RenderPhase.OPAQUE) {
+ return vertexCount[0] / 3;
+ } else if (phase == ChunkMesh.RenderPhase.ALPHA_REJECT) {
+ return (vertexCount[1] + vertexCount[2]) / 3;
+ } else {
+ return vertexCount[3] / 3;
+ }
+ }
+
+ private int triangleCount() {
+ return triangleCount;
+ }
+
+ /**
+ * Returns true if an instance of this class stores no triangles.
+ *
+ * @return True if no triangles are stored in the instance, false otherwise.
+ */
+ public boolean isEmpty() {
+ return triangleCount == 0;
+ }
+
+ void setTimeToGenerateBlockVertices(int timeToGenerateBlockVertices) {
+ this.timeToGenerateBlockVertices = timeToGenerateBlockVertices;
+ }
+
+ public int getTimeToGenerateBlockVertices() {
+ return timeToGenerateBlockVertices;
+ }
+
+ void setTimeToGenerateOptimizedBuffers(int timeToGenerateOptimizedBuffers) {
+ this.timeToGenerateOptimizedBuffers = timeToGenerateOptimizedBuffers;
+ }
+
+ public int getTimeToGenerateOptimizedBuffers() {
+ return timeToGenerateOptimizedBuffers;
+ }
+
+}
diff --git a/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkTessellator.java b/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkTessellator.java
index 15730c5c950..b920d3134cb 100644
--- a/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkTessellator.java
+++ b/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkTessellator.java
@@ -29,7 +29,7 @@ public ChunkMesh generateMesh(ChunkView chunkView) {
public ChunkMesh generateMesh(ChunkView chunkView, float scale, int border) {
PerformanceMonitor.startActivity("GenerateMesh");
- ChunkMesh mesh = new ChunkMesh();
+ ChunkMeshImpl mesh = new ChunkMeshImpl();
final Stopwatch watch = Stopwatch.createStarted();
diff --git a/engine/src/main/java/org/terasology/engine/rendering/world/RenderableWorldImpl.java b/engine/src/main/java/org/terasology/engine/rendering/world/RenderableWorldImpl.java
index b678c47485e..446eaa74847 100644
--- a/engine/src/main/java/org/terasology/engine/rendering/world/RenderableWorldImpl.java
+++ b/engine/src/main/java/org/terasology/engine/rendering/world/RenderableWorldImpl.java
@@ -166,7 +166,7 @@ public boolean pregenerateChunks() {
chunk.setDirty(false);
newMesh = chunkTessellator.generateMesh(localView);
- newMesh.generateVBOs();
+ newMesh.updateMesh();
newMesh.discardData();
if (chunk.hasMesh()) {
@@ -289,7 +289,7 @@ public void generateVBOs() {
if (chunk.hasPendingMesh() && chunksInProximityOfCamera.contains(chunk)) {
pendingMesh = chunk.getPendingMesh();
- pendingMesh.generateVBOs();
+ pendingMesh.updateMesh();
pendingMesh.discardData();
if (chunk.hasMesh()) {
chunk.getMesh().dispose();
diff --git a/engine/src/main/java/org/terasology/engine/rendering/world/WorldRenderer.java b/engine/src/main/java/org/terasology/engine/rendering/world/WorldRenderer.java
index 5b8a126ee84..d9bbe75af62 100644
--- a/engine/src/main/java/org/terasology/engine/rendering/world/WorldRenderer.java
+++ b/engine/src/main/java/org/terasology/engine/rendering/world/WorldRenderer.java
@@ -5,7 +5,7 @@
import org.joml.Vector3f;
import org.joml.Vector3ic;
import org.terasology.engine.rendering.assets.material.Material;
-import org.terasology.engine.rendering.cameras.SubmersibleCamera;
+import org.terasology.engine.rendering.cameras.Camera;
import org.terasology.engine.rendering.world.viewDistance.ViewDistance;
import org.terasology.engine.rendering.dag.RenderGraph;
import org.terasology.gestalt.module.sandbox.API;
@@ -89,7 +89,7 @@ enum RenderingStage {
*
* @return a SubmersibleCamera object
*/
- SubmersibleCamera getActiveCamera();
+ Camera getActiveCamera();
/**
* Called potentially multiple times per frame, this method allows the renderer to trigger the update
diff --git a/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java b/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java
index e4c541a446c..b9ea118c7d0 100644
--- a/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java
+++ b/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java
@@ -25,9 +25,9 @@
import org.terasology.engine.rendering.ShaderManager;
import org.terasology.engine.rendering.assets.material.Material;
import org.terasology.engine.rendering.backdrop.BackdropProvider;
+import org.terasology.engine.rendering.cameras.Camera;
import org.terasology.engine.rendering.cameras.OpenVRStereoCamera;
import org.terasology.engine.rendering.cameras.PerspectiveCamera;
-import org.terasology.engine.rendering.cameras.SubmersibleCamera;
import org.terasology.engine.rendering.dag.ModuleRendering;
import org.terasology.engine.rendering.dag.Node;
import org.terasology.engine.rendering.dag.RenderGraph;
@@ -79,7 +79,7 @@ public final class WorldRendererImpl implements WorldRenderer {
private final WorldProvider worldProvider;
private final RenderableWorld renderableWorld;
private final ShaderManager shaderManager;
- private final SubmersibleCamera playerCamera;
+ private final Camera playerCamera;
private final OpenVRProvider vrProvider;
@@ -133,7 +133,7 @@ public WorldRendererImpl(Context context) {
// vrSupport, we fall back on rendering to the main display. The reason for init failure can be read from
// the log.
if (vrProvider.init()) {
- playerCamera = new OpenVRStereoCamera(vrProvider, worldProvider, renderingConfig);
+ playerCamera = new OpenVRStereoCamera(vrProvider);
/*
* The origin of OpenVR's coordinate system lies on the ground of the user. We have to move this origin
* such that the ground plane of the rendering system and the ground plane of the room the VR user is
@@ -143,11 +143,11 @@ public WorldRendererImpl(Context context) {
GROUND_PLANE_HEIGHT_DISPARITY - context.get(PlayerConfig.class).eyeHeight.get());
currentRenderingStage = RenderingStage.LEFT_EYE;
} else {
- playerCamera = new PerspectiveCamera(worldProvider, renderingConfig, context.get(DisplayDevice.class));
+ playerCamera = new PerspectiveCamera(renderingConfig, context.get(DisplayDevice.class));
currentRenderingStage = RenderingStage.MONO;
}
} else {
- playerCamera = new PerspectiveCamera(worldProvider, renderingConfig, context.get(DisplayDevice.class));
+ playerCamera = new PerspectiveCamera(renderingConfig, context.get(DisplayDevice.class));
currentRenderingStage = RenderingStage.MONO;
}
// TODO: won't need localPlayerSystem here once camera is in the ES proper
@@ -437,7 +437,7 @@ public float getMillisecondsSinceRenderingStart() {
}
@Override
- public SubmersibleCamera getActiveCamera() {
+ public Camera getActiveCamera() {
return playerCamera;
}
diff --git a/engine/src/main/java/org/terasology/engine/world/chunks/LodChunkProvider.java b/engine/src/main/java/org/terasology/engine/world/chunks/LodChunkProvider.java
index b3a32447af3..60cf0ee0007 100644
--- a/engine/src/main/java/org/terasology/engine/world/chunks/LodChunkProvider.java
+++ b/engine/src/main/java/org/terasology/engine/world/chunks/LodChunkProvider.java
@@ -113,7 +113,7 @@ private void processReadyChunks() {
int scale = chunk.scale;
if (requiredScale != null && requiredScale <= scale) { // The relevant region may have been updated since
// this chunk was requested.
- chunk.getMesh().generateVBOs();
+ chunk.getMesh().updateMesh();
chunk.getMesh().discardData();
Vector3i subPos = new Vector3i();
if (scale > 0) {
diff --git a/engine/src/main/resources/org/terasology/engine/assets/i18n/menu_uk.lang b/engine/src/main/resources/org/terasology/engine/assets/i18n/menu_uk.lang
index 1d73e310a60..4a9c85f77d5 100644
--- a/engine/src/main/resources/org/terasology/engine/assets/i18n/menu_uk.lang
+++ b/engine/src/main/resources/org/terasology/engine/assets/i18n/menu_uk.lang
@@ -12,6 +12,7 @@
"animate-water": "Анімована вода",
"audio-settings": "Звук",
"audio-settings-title": "Налаштування звуку",
+ "system-settings-title": "Налаштування системи",
"back": "Назад",
"behavior-editor-assign": "присвоїти",
"behavior-editor-copy": "копіювати",
@@ -19,6 +20,7 @@
"behavior-editor-new": "новий",
"behavior-editor-paste": "вставити",
"behavior-editor-remove": "видалити",
+ "billboard-limit": "Відстань прозорих об'єктів",
"binding-attack": "Атака",
"binding-autoMove-mode": "Автоматичний рух",
"binding-backwards": "Назад",
@@ -81,6 +83,7 @@
"category-nui": "NUI",
"change-keybind-popup-message": "вже зайнята іншою дією. Все одно присвоїти?",
"change-keybind-popup-title": "Клавіша зайнята",
+ "chunk-lods": "Рівень деталізації",
"clamp-lighting": "Обмежене освітлення",
"cloud-shadows": "Тіні від хмар",
"config": "Конфігурація",
@@ -113,14 +116,17 @@
"dialog-yes": "Так",
"disable-all-modules": "Відключити все",
"disable-launch-popup": "Запам'ятати і більше не показувати",
+ "disable-rendering-class": "Відключити",
"discord-presence": "Discord Rich Presence",
+ "discord-settings-title": "Налаштування Discord",
"download-module": "Завантажити",
"downloading-server-list": "Завантаження списку серверів ..",
"drop-item": "Кинути предмет",
"edit-server": "Редагувати",
"edit-server-title": "Додати/редагувати сервер",
- "enable-error-reporting":"Надсилати повідомлення про помилку",
+ "enable-error-reporting": "Надсилати повідомлення про помилку",
"enable-telemetry": "Надсилати метрики",
+ "enable-rendering-class": "Включити",
"enter-username-message": "Вітаємо - ласкаво просимо до онлайн-режиму Terasology!\nБажаєте оновити ім'я користувача?",
"enum-editor-prompt": "Виберіть значення для вершини:",
"enum-editor-title": "Редагування enum-вершин",
@@ -296,6 +302,7 @@
"remove-confirmation-popup-message": "Ви впевнені, що хочете видалити це?",
"remove-binding": "Скинути",
"remove-server": "Видал.",
+ "rendering-settings": "Налаштування модулів рендерингу",
"reset-default": "Скинути",
"re-roll": "Перегенерувати",
"reset-fov-amount": "Скинути поле зору",
@@ -308,6 +315,7 @@
"return-main-menu": "Вийти до головного меню",
"return-pregeneration": "Вийти до попереднього генерування",
"return-universe-setup": "Вийти до налаштувань світу",
+ "return": "Вийти",
"rotation-dead-zone": "Мертва зона осі повертання",
"save-game-path": "Шлях до файлу збереження:",
"save-game-details": "Деталі",
diff --git a/subsystems/TypeHandlerLibrary/build.gradle.kts b/subsystems/TypeHandlerLibrary/build.gradle.kts
index 52572e2bbbd..2ee43ea2ac8 100644
--- a/subsystems/TypeHandlerLibrary/build.gradle.kts
+++ b/subsystems/TypeHandlerLibrary/build.gradle.kts
@@ -12,7 +12,7 @@ group = "org.terasology.subsystems"
version = project(":engine").version
dependencies {
- implementation("org.slf4j:slf4j-api:1.7.21")
+ implementation("org.slf4j:slf4j-api:1.7.32")
implementation("net.sf.trove4j:trove4j:3.0.3")
implementation("org.terasology:reflections:0.9.12-MB")
@@ -20,10 +20,28 @@ dependencies {
implementation("org.terasology.gestalt:gestalt-module:7.1.0")
implementation("org.terasology.gestalt:gestalt-asset-core:7.1.0")
- testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.2")
- testImplementation("org.junit.jupiter:junit-jupiter-params:5.5.2")
- testImplementation("org.mockito:mockito-junit-jupiter:3.11.2")
+ testRuntimeOnly("org.slf4j:slf4j-simple:1.7.32") {
+ because("log output during tests")
+ }
+ testImplementation(platform("org.junit:junit-bom:5.8.1")) {
+ // junit-bom will set version numbers for the other org.junit dependencies.
+ }
+ testImplementation("org.junit.jupiter:junit-jupiter-api")
+ testImplementation("org.junit.jupiter:junit-jupiter-params")
+ testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
+ testImplementation("org.mockito:mockito-inline:3.12.4")
+
testImplementation("com.google.guava:guava:31.0-jre")
- testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.5.2")
+ testImplementation("org.mockito:mockito-junit-jupiter:3.12.4")
+}
+
+tasks.register("unitTest") {
+ group = "Verification"
+ description = "Runs unit tests (fast)"
+
+ useJUnitPlatform {
+ excludeTags("MteTest", "TteTest")
+ }
+ systemProperty("junit.jupiter.execution.timeout.default", "1m")
}
diff --git a/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/coreTypes/RuntimeDelegatingTypeHandlerTest.java b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/coreTypes/RuntimeDelegatingTypeHandlerTest.java
index 457b6c1703b..3fc9caddd06 100644
--- a/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/coreTypes/RuntimeDelegatingTypeHandlerTest.java
+++ b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/coreTypes/RuntimeDelegatingTypeHandlerTest.java
@@ -4,8 +4,10 @@
import com.google.common.collect.ImmutableMap;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatcher;
-import org.reflections.Reflections;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.terasology.persistence.typeHandling.PersistedData;
import org.terasology.persistence.typeHandling.PersistedDataSerializer;
import org.terasology.persistence.typeHandling.TypeHandler;
@@ -14,7 +16,7 @@
import org.terasology.persistence.typeHandling.inMemory.AbstractPersistedData;
import org.terasology.persistence.typeHandling.inMemory.PersistedMap;
import org.terasology.persistence.typeHandling.inMemory.PersistedString;
-import org.terasology.persistence.typeHandling.reflection.ReflectionsSandbox;
+import org.terasology.persistence.typeHandling.reflection.SerializationSandbox;
import org.terasology.reflection.TypeInfo;
import java.lang.reflect.Type;
@@ -30,81 +32,62 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-class RuntimeDelegatingTypeHandlerTest {
- private final TypeHandlerLibrary typeHandlerLibrary = mock(TypeHandlerLibrary.class);
-
- private final TypeHandlerContext context =
- new TypeHandlerContext(typeHandlerLibrary, new ReflectionsSandbox(new Reflections(getClass().getClassLoader())));
-
- private TypeHandler baseTypeHandler;
- private TypeHandler subTypeHandler;
- private Class subType;
- private Type baseType;
- private RuntimeDelegatingTypeHandler runtimeDelegatingTypeHandler;
-
- private static class Base {
- int x;
+@ExtendWith(MockitoExtension.class)
+public class RuntimeDelegatingTypeHandlerTest {
+ private final TypeHandlerLibrary typeHandlerLibrary;
+
+ private final TypeHandler baseTypeHandler;
+ private final TypeHandler subTypeHandler;
+ private final Class subType = Sub.class;
+ private final Class baseType = Base.class;
+ private final RuntimeDelegatingTypeHandler runtimeDelegatingTypeHandler;
+
+
+ RuntimeDelegatingTypeHandlerTest(@Mock TypeHandler baseTypeHandler, @Mock SubHandler subTypeHandler,
+ @Mock SerializationSandbox sandbox, @Mock TypeHandlerLibrary typeHandlerLibrary) {
+ this.baseTypeHandler = baseTypeHandler;
+ this.subTypeHandler = subTypeHandler;
+ configureMockSerializer(baseTypeHandler);
+ configureMockSerializer(subTypeHandler);
+
+ this.typeHandlerLibrary = typeHandlerLibrary;
+ // We must mock `getTypeHandler(Type)`, not only `getTypeHandler(Class<>)`
+ when(typeHandlerLibrary.getTypeHandler((Type) baseType))
+ .thenReturn(Optional.of(baseTypeHandler));
+ when(typeHandlerLibrary.getTypeHandler((Type) subType))
+ .thenReturn(Optional.of(subTypeHandler));
+
+ when(sandbox.findSubTypeOf(subType.getName(), baseType))
+ .thenReturn(Optional.of(subType));
+ when(sandbox.getSubTypeIdentifier(subType, baseType))
+ .thenReturn(subType.getName());
+ TypeHandlerContext context = new TypeHandlerContext(typeHandlerLibrary, sandbox);
+
+ runtimeDelegatingTypeHandler = new RuntimeDelegatingTypeHandler<>(
+ baseTypeHandler, TypeInfo.of(Base.class), context);
}
- private static class Sub extends Base {
- float y;
- }
-
- private void setupHandlers() {
- subType = Sub.class;
- baseType = TypeInfo.of(Base.class).getType();
-
- abstract class SubHandler extends TypeHandler { }
-
- baseTypeHandler = mockTypeHandler();
- subTypeHandler = mockTypeHandler(SubHandler.class);
-
- when(typeHandlerLibrary.getTypeHandler(eq(baseType)))
- .thenReturn(Optional.of(baseTypeHandler));
-
- when(typeHandlerLibrary.getTypeHandler(eq((Type) subType)))
- .thenReturn(Optional.of(subTypeHandler));
-
- runtimeDelegatingTypeHandler = new RuntimeDelegatingTypeHandler(baseTypeHandler, TypeInfo.of(Base.class), context);
- }
-
- private static TypeHandler> mockTypeHandler(Class extends TypeHandler> subHandlerClass) {
- TypeHandler> mocked = mock(subHandlerClass);
-
+ private static void configureMockSerializer(TypeHandler> mocked) {
when(mocked.serialize(any(), any())).thenReturn(new AbstractPersistedData() {
@Override
public boolean isNull() {
return true;
}
});
-
- return mocked;
- }
-
- private static TypeHandler> mockTypeHandler() {
- return mockTypeHandler(TypeHandler.class);
}
@Test
void testSerializeBase() {
PersistedDataSerializer serializer = mock(PersistedDataSerializer.class);
- when(serializer.serialize(any(String.class)))
- .then(invocation -> new PersistedString((String) invocation.getArguments()[0]));
-
- setupHandlers();
Base base = new Base();
runtimeDelegatingTypeHandler.serialize(base, serializer);
- verify(typeHandlerLibrary, never()).getTypeHandler(eq((Type) subType));
-
verify(baseTypeHandler).serialize(any(), any());
verify(subTypeHandler, never()).serialize(any(), any());
verify(serializer, never()).serialize(
- argThat((ArgumentMatcher