Initual upload to branch
This commit is contained in:
@@ -1,5 +0,0 @@
|
|||||||
# Disable autocrlf on generated files, they always generate with LF
|
|
||||||
# Add any extra files or paths here to make git stop saying they
|
|
||||||
# are changed when only line endings change.
|
|
||||||
src/generated/**/.cache/cache text eol=lf
|
|
||||||
src/generated/**/*.json text eol=lf
|
|
||||||
+21
-37
@@ -1,49 +1,33 @@
|
|||||||
|
# gradle
|
||||||
|
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
out/
|
||||||
|
classes/
|
||||||
|
|
||||||
# eclipse
|
# eclipse
|
||||||
bin
|
|
||||||
*.launch
|
*.launch
|
||||||
.settings
|
|
||||||
.metadata
|
|
||||||
.classpath
|
|
||||||
.project
|
|
||||||
|
|
||||||
# idea
|
# idea
|
||||||
out
|
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
*.iml
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# gradle
|
# vscode
|
||||||
build
|
|
||||||
.gradle
|
|
||||||
|
|
||||||
# other
|
.settings/
|
||||||
eclipse
|
.vscode/
|
||||||
run
|
|
||||||
|
|
||||||
# Files from Forge MDK
|
|
||||||
logs
|
|
||||||
forge*changelog.txt
|
|
||||||
|
|
||||||
.architectury-transformer/
|
|
||||||
build/
|
|
||||||
*.ipr
|
|
||||||
run/
|
|
||||||
*.iws
|
|
||||||
out/
|
|
||||||
*.iml
|
|
||||||
.gradle/
|
|
||||||
output/
|
|
||||||
bin/
|
bin/
|
||||||
libs/
|
|
||||||
|
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
.idea/
|
|
||||||
classes/
|
|
||||||
.metadata
|
|
||||||
.vscode
|
|
||||||
.settings
|
|
||||||
*.launch
|
|
||||||
|
|
||||||
**/src/generated/
|
# macos
|
||||||
|
|
||||||
|
*.DS_Store
|
||||||
|
|
||||||
|
# fabric
|
||||||
|
|
||||||
|
run/
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
[submodule "core"]
|
|
||||||
path = core
|
|
||||||
url = https://gitlab.com/jeseibel/distant-horizons-core.git
|
|
||||||
+119
-46
@@ -1,65 +1,138 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "architectury-plugin" version "3.4-SNAPSHOT"
|
id 'fabric-loom' version '0.10-SNAPSHOT'
|
||||||
id "dev.architectury.loom" version "0.10.0.195" apply false
|
id 'maven-publish'
|
||||||
|
id "com.github.johnrengelman.shadow" version "7.1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
architectury {
|
version = '1.0'
|
||||||
minecraft = rootProject.minecraft_version
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
|
||||||
|
println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch'))
|
||||||
|
|
||||||
|
// Include resources generated by data generators.
|
||||||
|
sourceSets.main.resources { srcDir 'src/generated/resources' }
|
||||||
|
|
||||||
|
loom {
|
||||||
|
accessWidenerPath = file("src/main/resources/lod.accesswidener")
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects { p ->
|
// this is required so that we can use
|
||||||
apply plugin: "dev.architectury.loom"
|
// jitpack in the dependencies section below
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
// used to download and compile dependencies from git repos
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
|
|
||||||
loom {
|
// Required for ModMenu
|
||||||
silentMojangMappingsLicense()
|
maven { url "https://maven.terraformersmc.com/" }
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
shadowMe
|
||||||
|
compileOnly.extendsFrom(embed)
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||||
|
mappings loom.officialMojangMappings()
|
||||||
|
modImplementation "net.fabricmc:fabric-loader:${project.fabric_loader_version}"
|
||||||
|
|
||||||
|
// Fabric API
|
||||||
|
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
|
||||||
|
|
||||||
|
// Mod Menu
|
||||||
|
modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}") {
|
||||||
|
exclude(group: "net.fabricmc.fabric-api")
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
implementation 'org.tukaani:xz:1.9'
|
||||||
common
|
shadowMe 'org.tukaani:xz:1.9'
|
||||||
shadowMe
|
implementation 'org.apache.commons:commons-compress:1.21'
|
||||||
implementation.extendsFrom shadowMe
|
shadowMe 'org.apache.commons:commons-compress:1.21'
|
||||||
|
}
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||||
|
configurations = [project.configurations.getByName("shadowMe")]
|
||||||
|
relocate 'org.tukaani', 'shaded.tukaani'
|
||||||
|
relocate 'org.apache.commons.compress', 'shaded.apache.commons.compress'
|
||||||
|
archiveClassifier = 'unmapped'
|
||||||
|
|
||||||
|
from("LICENSE") {
|
||||||
|
rename { "${it}_${project.archivesBaseName}"}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
jar {
|
||||||
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
|
archiveClassifier = 'unmapped'
|
||||||
// The following line declares the mojmap mappings
|
}
|
||||||
mappings loom.officialMojangMappings()
|
|
||||||
|
|
||||||
if (p != project(":forge")) {
|
remapJar {
|
||||||
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
|
dependsOn(shadowJar)
|
||||||
// Do NOT use other classes from fabric loader unless working with fabric
|
mustRunAfter(shadowJar)
|
||||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
input = shadowJar.archiveFile
|
||||||
}
|
|
||||||
|
|
||||||
|
archiveBaseName = "${project.name}"
|
||||||
|
archiveVersion = "${project.version}"
|
||||||
|
archiveClassifier = ''
|
||||||
|
}
|
||||||
|
|
||||||
if (p != project(":core")) {
|
processResources {
|
||||||
common(project(":core")) { transitive false }
|
inputs.property "version", project.version
|
||||||
shadowMe(project(":core")) { transitive false }
|
|
||||||
|
filesMatching("fabric.mod.json") {
|
||||||
|
expand "version": project.version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
// ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||||
|
// this fixes some edge cases with special characters not displaying correctly
|
||||||
|
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
|
||||||
|
// If Javadoc is generated, this must be specified in that task too.
|
||||||
|
it.options.encoding = "UTF-8"
|
||||||
|
|
||||||
|
// Minecraft 1.18 (1.18-pre2) upwards uses Java 17.
|
||||||
|
it.options.release = 17
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
|
||||||
|
// if it is present.
|
||||||
|
// If you remove this line, sources will not be generated.
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure the maven publication
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
mavenJava(MavenPublication) {
|
||||||
|
// add all the jars that should be included when publishing to maven
|
||||||
|
artifact(remapJar) {
|
||||||
|
builtBy remapJar
|
||||||
|
}
|
||||||
|
artifact(sourcesJar) {
|
||||||
|
builtBy remapSourcesJar
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
apply plugin: "java"
|
|
||||||
apply plugin: "architectury-plugin"
|
|
||||||
apply plugin: "maven-publish"
|
|
||||||
|
|
||||||
archivesBaseName = rootProject.archives_base_name
|
|
||||||
version = rootProject.mod_version
|
|
||||||
group = rootProject.maven_group
|
|
||||||
|
|
||||||
|
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
// Add repositories to publish to here.
|
||||||
// used to download and compile dependencies from git repos
|
// Notice: This block does NOT have the same function as the block in the top level.
|
||||||
maven { url 'https://jitpack.io' }
|
// The repositories here will be used for publishing your artifact, not for
|
||||||
}
|
// retrieving dependencies.
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
|
||||||
options.encoding = "UTF-8"
|
|
||||||
options.release = 16
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//// add Distant Horizon's Core source folders
|
||||||
|
//sourceSets {
|
||||||
|
// main {
|
||||||
|
// java {
|
||||||
|
// srcDirs("core/src/main/java");
|
||||||
|
// srcDirs("core/src/main/resources")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
loom {
|
|
||||||
accessWidenerPath.set(file("src/main/resources/lod.accesswidener"))
|
|
||||||
}
|
|
||||||
|
|
||||||
architectury {
|
|
||||||
common()
|
|
||||||
}
|
|
||||||
|
|
||||||
afterEvaluate {
|
|
||||||
tasks {
|
|
||||||
remapJar {
|
|
||||||
remapAccessWidener.set(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
mavenCommon(MavenPublication) {
|
|
||||||
artifactId = rootProject.archives_base_name
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
|
||||||
repositories {
|
|
||||||
// Add repositories to publish to here.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 172 KiB |
-1
Submodule core deleted from f3b6b15bcb
@@ -1,124 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id "com.github.johnrengelman.shadow" version "7.0.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
loom {
|
|
||||||
accessWidenerPath.set(project(":common").file("src/main/resources/lod.accesswidener"))
|
|
||||||
}
|
|
||||||
|
|
||||||
architectury {
|
|
||||||
platformSetupLoomIde()
|
|
||||||
fabric()
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
compileClasspath.extendsFrom common
|
|
||||||
runtimeClasspath.extendsFrom common
|
|
||||||
developmentFabric.extendsFrom common
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
// Required for ModMenu
|
|
||||||
maven { url "https://maven.terraformersmc.com/" }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
|
||||||
|
|
||||||
// TODO: This is only for LodMain, try to find a way to remove it
|
|
||||||
// Fabric API
|
|
||||||
modApi "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}"
|
|
||||||
|
|
||||||
// Mod Menu
|
|
||||||
modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") {
|
|
||||||
exclude(group: "net.fabricmc.fabric-api")
|
|
||||||
}
|
|
||||||
|
|
||||||
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
|
||||||
shadowMe(project(path: ":common", configuration: "transformProductionFabric")) { transitive false }
|
|
||||||
|
|
||||||
common 'org.tukaani:xz:1.9'
|
|
||||||
common 'org.apache.commons:commons-compress:1.21'
|
|
||||||
shadowMe 'org.tukaani:xz:1.9'
|
|
||||||
shadowMe 'org.apache.commons:commons-compress:1.21'
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method copies the access wideners from the common project to the fabric project. And it was generated by Github Copilot
|
|
||||||
task copyAccessWidener(type: Copy) {
|
|
||||||
from project(":common").file("src/main/resources/lod.accesswidener")
|
|
||||||
into file("src/generated/resources")
|
|
||||||
}
|
|
||||||
|
|
||||||
task copyCoreResources(type: Copy) {
|
|
||||||
from fileTree(project(":core").file("src/main/resources"))
|
|
||||||
into file("build/resources/main")
|
|
||||||
}
|
|
||||||
|
|
||||||
task deleteResources(type: Delete) {
|
|
||||||
delete file("build/resources/main")
|
|
||||||
}
|
|
||||||
|
|
||||||
task copyCommonResources(type: Copy) {
|
|
||||||
from fileTree(project(":common").file("src/main/resources"))
|
|
||||||
into file("build/resources/main")
|
|
||||||
}
|
|
||||||
|
|
||||||
runClient {
|
|
||||||
dependsOn(copyCoreResources)
|
|
||||||
dependsOn(copyCommonResources)
|
|
||||||
finalizedBy(deleteResources)
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
dependsOn(copyAccessWidener)
|
|
||||||
inputs.property "version", project.version
|
|
||||||
|
|
||||||
filesMatching("fabric.mod.json") {
|
|
||||||
expand "version": project.version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
configurations = [project.configurations.shadowMe]
|
|
||||||
relocate 'org.tukaani', 'shaded.tukaani'
|
|
||||||
relocate 'org.apache.commons.compress', 'shaded.apache.commons.compress'
|
|
||||||
|
|
||||||
classifier "dev-shadow"
|
|
||||||
}
|
|
||||||
|
|
||||||
remapJar {
|
|
||||||
input.set shadowJar.archiveFile
|
|
||||||
dependsOn shadowJar
|
|
||||||
classifier null
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
classifier "dev"
|
|
||||||
}
|
|
||||||
|
|
||||||
sourcesJar {
|
|
||||||
def commonSources = project(":common").sourcesJar
|
|
||||||
dependsOn commonSources
|
|
||||||
from commonSources.archiveFile.map { zipTree(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
components.java {
|
|
||||||
withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
|
|
||||||
skip()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
mavenFabric(MavenPublication) {
|
|
||||||
artifactId = rootProject.archives_base_name + "-" + project.name
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
|
||||||
repositories {
|
|
||||||
// Add repositories to publish to here.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id "com.github.johnrengelman.shadow" version "7.0.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
loom {
|
|
||||||
accessWidenerPath.set(project(":common").file("src/main/resources/lod.accesswidener"))
|
|
||||||
|
|
||||||
forge {
|
|
||||||
convertAccessWideners.set(true)
|
|
||||||
extraAccessWideners.add("lod.accesswidener")
|
|
||||||
mixinConfigs("lod.mixins.json")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
architectury {
|
|
||||||
platformSetupLoomIde()
|
|
||||||
forge()
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
compileClasspath.extendsFrom common
|
|
||||||
runtimeClasspath.extendsFrom common
|
|
||||||
developmentForge.extendsFrom common
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
forge "net.minecraftforge:forge:${rootProject.minecraft_version}-${rootProject.forge_version}"
|
|
||||||
|
|
||||||
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
|
||||||
shadowMe(project(path: ":common", configuration: "transformProductionForge")) { transitive = false }
|
|
||||||
|
|
||||||
// forgeDependencies(project(":core")) { transitive false }
|
|
||||||
|
|
||||||
forgeDependencies('org.tukaani:xz:1.9')
|
|
||||||
forgeDependencies('org.apache.commons:commons-compress:1.21')
|
|
||||||
shadowMe 'org.tukaani:xz:1.9'
|
|
||||||
shadowMe 'org.apache.commons:commons-compress:1.21'
|
|
||||||
}
|
|
||||||
|
|
||||||
task copyCoreResources(type: Copy) {
|
|
||||||
from fileTree(project(":core").file("src/main/resources"))
|
|
||||||
into file("build/resources/main")
|
|
||||||
}
|
|
||||||
|
|
||||||
task copyCommonResources(type: Copy) {
|
|
||||||
from fileTree(project(":common").file("src/main/resources"))
|
|
||||||
into file("build/resources/main")
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
dependsOn(copyCoreResources)
|
|
||||||
dependsOn(copyCommonResources)
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
dependencies {
|
|
||||||
exclude(dependency("net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"))
|
|
||||||
}
|
|
||||||
exclude "fabric.mod.json"
|
|
||||||
configurations = [project.configurations.shadowMe]
|
|
||||||
relocate 'org.tukaani', 'shaded.tukaani'
|
|
||||||
relocate 'org.apache.commons.compress', 'shaded.apache.commons.compress'
|
|
||||||
|
|
||||||
classifier "dev-shadow"
|
|
||||||
}
|
|
||||||
|
|
||||||
remapJar {
|
|
||||||
input.set shadowJar.archiveFile
|
|
||||||
dependsOn shadowJar
|
|
||||||
classifier null
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
classifier "dev"
|
|
||||||
}
|
|
||||||
|
|
||||||
sourcesJar {
|
|
||||||
def commonSources = project(":common").sourcesJar
|
|
||||||
dependsOn commonSources
|
|
||||||
from commonSources.archiveFile.map { zipTree(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
components.java {
|
|
||||||
withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
|
|
||||||
skip()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
mavenForge(MavenPublication) {
|
|
||||||
artifactId = rootProject.archives_base_name + "-" + project.name
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
|
||||||
repositories {
|
|
||||||
// Add repositories to publish to here.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
loom.platform=forge
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
|
||||||
* licensed under the GNU GPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* 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, version 3.
|
|
||||||
*
|
|
||||||
* 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 com.seibel.lod.forge;
|
|
||||||
|
|
||||||
import com.seibel.lod.core.api.EventApi;
|
|
||||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
|
||||||
import com.seibel.lod.common.wrappers.chunk.ChunkWrapper;
|
|
||||||
import com.seibel.lod.common.wrappers.world.DimensionTypeWrapper;
|
|
||||||
import com.seibel.lod.common.wrappers.world.WorldWrapper;
|
|
||||||
|
|
||||||
import net.minecraftforge.client.event.InputEvent;
|
|
||||||
import net.minecraftforge.event.TickEvent;
|
|
||||||
import net.minecraftforge.event.world.BlockEvent;
|
|
||||||
import net.minecraftforge.event.world.ChunkEvent;
|
|
||||||
import net.minecraftforge.event.world.WorldEvent;
|
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This handles all events sent to the client,
|
|
||||||
* and is the starting point for most of the mod.
|
|
||||||
*
|
|
||||||
* @author James_Seibel
|
|
||||||
* @version 11-12-2021
|
|
||||||
*/
|
|
||||||
public class ForgeClientProxy
|
|
||||||
{
|
|
||||||
private final EventApi eventApi = EventApi.INSTANCE;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public void serverTickEvent(TickEvent.ServerTickEvent event)
|
|
||||||
{
|
|
||||||
eventApi.serverTickEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public void chunkLoadEvent(ChunkEvent.Load event)
|
|
||||||
{
|
|
||||||
eventApi.chunkLoadEvent(new ChunkWrapper(event.getChunk()), DimensionTypeWrapper.getDimensionTypeWrapper(event.getWorld().dimensionType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public void worldSaveEvent(WorldEvent.Save event)
|
|
||||||
{
|
|
||||||
eventApi.worldSaveEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This is also called when a new dimension loads */
|
|
||||||
@SubscribeEvent
|
|
||||||
public void worldLoadEvent(WorldEvent.Load event)
|
|
||||||
{
|
|
||||||
if (event.getWorld() != null) {
|
|
||||||
eventApi.worldLoadEvent(WorldWrapper.getWorldWrapper(event.getWorld()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public void worldUnloadEvent(WorldEvent.Unload event)
|
|
||||||
{
|
|
||||||
eventApi.worldUnloadEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public void blockChangeEvent(BlockEvent event)
|
|
||||||
{
|
|
||||||
// we only care about certain block events
|
|
||||||
if (event.getClass() == BlockEvent.BreakEvent.class ||
|
|
||||||
event.getClass() == BlockEvent.EntityPlaceEvent.class ||
|
|
||||||
event.getClass() == BlockEvent.EntityMultiPlaceEvent.class ||
|
|
||||||
event.getClass() == BlockEvent.FluidPlaceBlockEvent.class ||
|
|
||||||
event.getClass() == BlockEvent.PortalSpawnEvent.class)
|
|
||||||
{
|
|
||||||
IChunkWrapper chunk = new ChunkWrapper(event.getWorld().getChunk(event.getPos()));
|
|
||||||
DimensionTypeWrapper dimType = DimensionTypeWrapper.getDimensionTypeWrapper(event.getWorld().dimensionType());
|
|
||||||
|
|
||||||
// recreate the LOD where the blocks were changed
|
|
||||||
eventApi.blockChangeEvent(chunk, dimType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public void onKeyInput(InputEvent.KeyInputEvent event)
|
|
||||||
{
|
|
||||||
eventApi.onKeyInput(event.getKey(), event.getAction());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
|
||||||
* licensed under the GNU GPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* 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, version 3.
|
|
||||||
*
|
|
||||||
* 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 com.seibel.lod.forge;
|
|
||||||
|
|
||||||
import com.seibel.lod.common.Config;
|
|
||||||
import com.seibel.lod.common.LodCommonMain;
|
|
||||||
import com.seibel.lod.common.forge.LodForgeMethodCaller;
|
|
||||||
import com.seibel.lod.common.wrappers.config.ConfigGui;
|
|
||||||
import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper;
|
|
||||||
import com.seibel.lod.core.ModInfo;
|
|
||||||
import com.seibel.lod.forge.wrappers.ForgeDependencySetup;
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraftforge.client.model.data.ModelDataMap;
|
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
||||||
import net.minecraftforge.fml.ModLoadingContext;
|
|
||||||
import net.minecraftforge.fml.common.Mod;
|
|
||||||
import net.minecraftforge.fml.config.ModConfig;
|
|
||||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
|
||||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
|
||||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
|
||||||
import net.minecraftforge.fmlserverevents.FMLServerStartedEvent;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize and setup the Mod. <br>
|
|
||||||
* If you are looking for the real start of the mod
|
|
||||||
* check out the ClientProxy.
|
|
||||||
*
|
|
||||||
* @author James Seibel
|
|
||||||
* @version 11-21-2021
|
|
||||||
*/
|
|
||||||
@Mod(ModInfo.ID)
|
|
||||||
public class ForgeMain implements LodForgeMethodCaller
|
|
||||||
{
|
|
||||||
public static ForgeClientProxy forgeClientProxy;
|
|
||||||
|
|
||||||
private void init(final FMLCommonSetupEvent event)
|
|
||||||
{
|
|
||||||
// make sure the dependencies are set up before the mod needs them
|
|
||||||
LodCommonMain.initConfig();
|
|
||||||
LodCommonMain.startup(this);
|
|
||||||
ForgeDependencySetup.createInitialBindings();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public ForgeMain()
|
|
||||||
{
|
|
||||||
// Register the methods
|
|
||||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::init);
|
|
||||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientStart);
|
|
||||||
|
|
||||||
// Register ourselves for server and other game events we are interested in
|
|
||||||
MinecraftForge.EVENT_BUS.register(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onClientStart(final FMLClientSetupEvent event)
|
|
||||||
{
|
|
||||||
forgeClientProxy = new ForgeClientProxy();
|
|
||||||
MinecraftForge.EVENT_BUS.register(forgeClientProxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public void onServerStarting(FMLServerStartedEvent event)
|
|
||||||
{
|
|
||||||
// this is called when the server starts
|
|
||||||
}
|
|
||||||
|
|
||||||
private ModelDataMap dataMap = new ModelDataMap.Builder().build();
|
|
||||||
@Override
|
|
||||||
public List<BakedQuad> getQuads(MinecraftWrapper mc, Block block, BlockState blockState, Direction direction, Random random) {
|
|
||||||
return mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package com.seibel.lod.forge.wrappers;
|
|
||||||
|
|
||||||
import com.seibel.lod.common.wrappers.config.LodConfigWrapperSingleton;
|
|
||||||
import com.seibel.lod.core.util.SingletonHandler;
|
|
||||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds all necessary dependencies so we
|
|
||||||
* can access them in Core. <br>
|
|
||||||
* This needs to be called before any Core classes
|
|
||||||
* are loaded.
|
|
||||||
*
|
|
||||||
* @author James Seibel
|
|
||||||
* @author Ran
|
|
||||||
* @version 12-1-2021
|
|
||||||
*/
|
|
||||||
public class ForgeDependencySetup
|
|
||||||
{
|
|
||||||
public static void createInitialBindings()
|
|
||||||
{
|
|
||||||
SingletonHandler.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#// This is an example mods.toml file. It contains the data relating to the loading mods.
|
|
||||||
#// There are several mandatory fields (#mandatory), and many more that are optional (#optional).
|
|
||||||
#// The overall format is standard TOML format, v0.5.0.
|
|
||||||
#// Note that there are a couple of TOML lists in this file.
|
|
||||||
#// Find more information on toml format here: https://github.com/toml-lang/toml
|
|
||||||
#// The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
|
|
||||||
modLoader="javafml" #mandatory
|
|
||||||
|
|
||||||
#// A version range to match for said mod loader - for regular FML @Mod it will be the forge version
|
|
||||||
loaderVersion="[36,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions.
|
|
||||||
|
|
||||||
#// The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties.
|
|
||||||
#// Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here.
|
|
||||||
license="GNU GPLv3"
|
|
||||||
|
|
||||||
#// A URL to refer people to when problems occur with this mod
|
|
||||||
issueTrackerURL="https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues" #optional
|
|
||||||
#// A list of mods - how many allowed here is determined by the individual mod loader
|
|
||||||
[[mods]] #mandatory
|
|
||||||
|
|
||||||
#// The modid of the mod
|
|
||||||
modId="lod" #mandatory
|
|
||||||
|
|
||||||
#// The version number of the mod - there's a few well known ${} variables useable here or just hardcode it
|
|
||||||
#//${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata
|
|
||||||
#// see the associated build.gradle script for how to populate this completely automatically during a build
|
|
||||||
version="1.5.4a" #mandatory
|
|
||||||
|
|
||||||
#// A display name for the mod
|
|
||||||
displayName="Distant Horizons" #mandatory
|
|
||||||
|
|
||||||
#// A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/
|
|
||||||
#//updateJSONURL="https://change.me.example.invalid/updates.json" #optional
|
|
||||||
|
|
||||||
#// A URL for the "homepage" for this mod, displayed in the mod UI
|
|
||||||
displayURL="https://www.curseforge.com/minecraft/mc-mods/lod-level-of-detail" #optional
|
|
||||||
|
|
||||||
#// A file name (in the root of the mod JAR) containing a logo for display
|
|
||||||
logoFile="logo.png" #optional
|
|
||||||
|
|
||||||
#// A file name (in the root of the mod JAR) containing a icon for display by catalogue
|
|
||||||
catalogueImageIcon="icon.png"
|
|
||||||
|
|
||||||
#// A text field displayed in the mod UI
|
|
||||||
credits="TechnoVision, Vike, and Darkhax for their modding tutorials." #optional
|
|
||||||
|
|
||||||
#// A text field displayed in the mod UI
|
|
||||||
authors="James Seibel, Leonardo Amato, and Cola" #optional
|
|
||||||
|
|
||||||
#// The description text for the mod (multi line!) (#mandatory)
|
|
||||||
description='''This mod generates and renders simplified terrain beyond the normal view distance, at a low performance cost.'''
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"required": true,
|
|
||||||
"package": "com.seibel.lod.common.mixins",
|
|
||||||
"compatibilityLevel": "JAVA_16",
|
|
||||||
"refmap": "lod.refmap.json",
|
|
||||||
"mixins": [
|
|
||||||
"MixinWorldRenderer",
|
|
||||||
"MixinOptionsScreen"
|
|
||||||
],
|
|
||||||
"minVersion": "0.8"
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"pack": {
|
|
||||||
"description": "",
|
|
||||||
"pack_format": 7
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+10
-12
@@ -1,14 +1,12 @@
|
|||||||
org.gradle.jvmargs=-Xmx2048M
|
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
|
||||||
|
# This is required to provide enough memory for the Minecraft decompilation process.
|
||||||
|
org.gradle.jvmargs=-Xmx3G
|
||||||
|
org.gradle.daemon=false
|
||||||
|
|
||||||
# TODO: HEY REMEMBER TO UPDATE THE lod.accesswidener WHEN UPDATING TO 1.18
|
# Minecraft
|
||||||
minecraft_version=1.17.1
|
minecraft_version=1.18
|
||||||
|
fabric_loader_version=0.12.6
|
||||||
|
|
||||||
archives_base_name=DistantHorizons
|
# Mod dependencies
|
||||||
mod_version=1.5.4a
|
fabric_api_version=0.43.1+1.18
|
||||||
maven_group=com.seibel.lod
|
modmenu_version=3.0.0
|
||||||
|
|
||||||
fabric_loader_version=0.11.6
|
|
||||||
fabric_api_version=0.37.1+1.17
|
|
||||||
modmenu_version=2.0.14
|
|
||||||
|
|
||||||
forge_version=37.1.0
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2015 the original author or authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -17,67 +17,101 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
# Need this for daisy-chained symlinks.
|
||||||
ls=`ls -ld "$PRG"`
|
while
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
[ -h "$app_path" ]
|
||||||
PRG="$link"
|
do
|
||||||
else
|
ls=$( ls -ld "$app_path" )
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
link=${ls#*' -> '}
|
||||||
fi
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MSYS* | MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
@@ -106,80 +140,95 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
warn "Could not query maximum file descriptor limit"
|
||||||
fi
|
esac
|
||||||
ulimit -n $MAX_FD
|
case $MAX_FD in #(
|
||||||
if [ $? -ne 0 ] ; then
|
'' | soft) :;; #(
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
*)
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
else
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=`expr $i + 1`
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
0) set -- ;;
|
|
||||||
1) set -- "$args0" ;;
|
|
||||||
2) set -- "$args0" "$args1" ;;
|
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=`save "$@"`
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
package com.seibel.lod.common.wrappers;
|
package com.seibel.lod.common.wrappers;
|
||||||
|
|
||||||
import com.seibel.lod.common.wrappers.block.BlockColorSingletonWrapper;
|
import com.seibel.lod.common.wrappers.block.BlockColorSingletonWrapper;
|
||||||
import com.seibel.lod.common.wrappers.minecraft.MinecraftRenderWrapper;
|
import com.seibel.lod.fabric.wrappers.minecraft.MinecraftRenderWrapper;
|
||||||
import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper;
|
import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper;
|
||||||
import com.seibel.lod.core.handlers.IReflectionHandler;
|
import com.seibel.lod.core.handlers.IReflectionHandler;
|
||||||
import com.seibel.lod.core.handlers.ReflectionHandler;
|
import com.seibel.lod.core.handlers.ReflectionHandler;
|
||||||
+1
-1
@@ -28,7 +28,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
|||||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
||||||
import com.seibel.lod.common.wrappers.block.BlockPosWrapper;
|
import com.seibel.lod.common.wrappers.block.BlockPosWrapper;
|
||||||
import com.seibel.lod.common.wrappers.chunk.ChunkPosWrapper;
|
import com.seibel.lod.common.wrappers.chunk.ChunkPosWrapper;
|
||||||
import com.seibel.lod.common.wrappers.worldGeneration.WorldGeneratorWrapper;
|
import com.seibel.lod.fabric.wrappers.worldGeneration.WorldGeneratorWrapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This handles creating abstract wrapper objects.
|
* This handles creating abstract wrapper objects.
|
||||||
+1
-1
@@ -58,7 +58,7 @@ public class ChunkWrapper implements IChunkWrapper
|
|||||||
@Override
|
@Override
|
||||||
public IBiomeWrapper getBiome(int xRel, int yAbs, int zRel)
|
public IBiomeWrapper getBiome(int xRel, int yAbs, int zRel)
|
||||||
{
|
{
|
||||||
return BiomeWrapper.getBiomeWrapper(chunk.getBiomes().getNoiseBiome(xRel >> 2, yAbs >> 2, zRel >> 2));
|
return BiomeWrapper.getBiomeWrapper(chunk.getNoiseBiome(xRel >> 2, yAbs >> 2, zRel >> 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
+1
-1
@@ -31,7 +31,7 @@ public class TexturedButtonWidget extends ImageButton {
|
|||||||
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
||||||
RenderSystem.setShaderTexture(0, WIDGETS_LOCATION);
|
RenderSystem.setShaderTexture(0, WIDGETS_LOCATION);
|
||||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
|
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
|
||||||
int i = this.getYImage(this.isHovered());
|
int i = this.getYImage(this.isHovered);
|
||||||
RenderSystem.enableBlend();
|
RenderSystem.enableBlend();
|
||||||
RenderSystem.defaultBlendFunc();
|
RenderSystem.defaultBlendFunc();
|
||||||
RenderSystem.enableDepthTest();
|
RenderSystem.enableDepthTest();
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is similar to mcmod.info
|
||||||
|
* <br>
|
||||||
|
* If you are looking at this mod's source code and don't
|
||||||
|
* know where to start.
|
||||||
|
* Go to the api/lod package (folder) and take a look at the ClientApi.java file,
|
||||||
|
* Pretty much all of the mod stems from there.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-29-2021
|
||||||
|
*/
|
||||||
|
public final class ModInfo
|
||||||
|
{
|
||||||
|
public static final String ID = "lod";
|
||||||
|
/** The internal mod name */
|
||||||
|
public static final String NAME = "DistantHorizons";
|
||||||
|
/** Human readable version of NAME */
|
||||||
|
public static final String READABLE_NAME = "Distant Horizons";
|
||||||
|
public static final String API = "LodAPI";
|
||||||
|
public static final String VERSION = "a1.5.4";
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.api;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory;
|
||||||
|
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||||
|
import com.seibel.lod.core.objects.lod.LodWorld;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This stores objects and variables that
|
||||||
|
* are shared between the different Core api classes.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-12-2021
|
||||||
|
*/
|
||||||
|
public class ApiShared
|
||||||
|
{
|
||||||
|
public ApiShared INSTANCE = new ApiShared();
|
||||||
|
|
||||||
|
public static final LodBufferBuilderFactory lodBufferBuilderFactory = new LodBufferBuilderFactory();
|
||||||
|
public static final LodWorld lodWorld = new LodWorld();
|
||||||
|
public static final LodBuilder lodBuilder = new LodBuilder();
|
||||||
|
|
||||||
|
/** Used to determine if the LODs should be regenerated */
|
||||||
|
public static int previousChunkRenderDistance = 0;
|
||||||
|
/** Used to determine if the LODs should be regenerated */
|
||||||
|
public static int previousLodRenderDistance = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private ApiShared()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.api;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.ModInfo;
|
||||||
|
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||||
|
import com.seibel.lod.core.objects.math.Mat4f;
|
||||||
|
import com.seibel.lod.core.render.GLProxy;
|
||||||
|
import com.seibel.lod.core.render.LodRenderer;
|
||||||
|
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||||
|
import com.seibel.lod.core.util.SingletonHandler;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This holds the methods that should be called
|
||||||
|
* by the host mod loader (Fabric, Forge, etc.).
|
||||||
|
* Specifically for the client.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-12-2021
|
||||||
|
*/
|
||||||
|
public class ClientApi
|
||||||
|
{
|
||||||
|
public static final ClientApi INSTANCE = new ClientApi();
|
||||||
|
public static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME);
|
||||||
|
|
||||||
|
public static LodRenderer renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory);
|
||||||
|
|
||||||
|
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||||
|
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
|
||||||
|
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||||
|
private static final EventApi EVENT_API = EventApi.INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* there is some setup that should only happen once,
|
||||||
|
* once this is true that setup has completed
|
||||||
|
*/
|
||||||
|
private boolean firstTimeSetupComplete = false;
|
||||||
|
private boolean configOverrideReminderPrinted = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private ClientApi()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void renderLods(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks)
|
||||||
|
{
|
||||||
|
// comment out when creating a release
|
||||||
|
applyConfigOverrides();
|
||||||
|
|
||||||
|
// clear any out of date objects
|
||||||
|
MC.clearFrameObjectCache();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// only run the first time setup once
|
||||||
|
if (!firstTimeSetupComplete)
|
||||||
|
firstFrameSetup();
|
||||||
|
|
||||||
|
|
||||||
|
if (!MC.playerExists() || ApiShared.lodWorld.getIsWorldNotLoaded())
|
||||||
|
return;
|
||||||
|
|
||||||
|
LodDimension lodDim = ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension());
|
||||||
|
if (lodDim == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DetailDistanceUtil.updateSettings();
|
||||||
|
EVENT_API.viewDistanceChangedEvent();
|
||||||
|
EVENT_API.playerMoveEvent(lodDim);
|
||||||
|
|
||||||
|
lodDim.cutRegionNodesAsync(MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getZ());
|
||||||
|
lodDim.expandOrLoadRegionsAsync(MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getZ());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (CONFIG.client().advanced().debugging().getDrawLods())
|
||||||
|
{
|
||||||
|
// Note to self:
|
||||||
|
// if "unspecified" shows up in the pie chart, it is
|
||||||
|
// possibly because the amount of time between sections
|
||||||
|
// is too small for the profiler to measure
|
||||||
|
IProfilerWrapper profiler = MC.getProfiler();
|
||||||
|
profiler.pop(); // get out of "terrain"
|
||||||
|
profiler.push("LOD");
|
||||||
|
|
||||||
|
|
||||||
|
ClientApi.renderer.drawLODs(lodDim, mcModelViewMatrix, mcProjectionMatrix, partialTicks, MC.getProfiler());
|
||||||
|
|
||||||
|
profiler.pop(); // end LOD
|
||||||
|
profiler.push("terrain"); // go back into "terrain"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// these can't be set until after the buffers are built (in renderer.drawLODs)
|
||||||
|
// otherwise the buffers may be set to the wrong size, or not changed at all
|
||||||
|
ApiShared.previousChunkRenderDistance = MC_RENDER.getRenderDistance();
|
||||||
|
ApiShared.previousLodRenderDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ClientApi.LOGGER.error("client proxy: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** used in a development environment to change settings on the fly */
|
||||||
|
private void applyConfigOverrides()
|
||||||
|
{
|
||||||
|
// remind the developer(s) that the config override is active
|
||||||
|
if (!configOverrideReminderPrinted)
|
||||||
|
{
|
||||||
|
MC.sendChatMessage(ModInfo.READABLE_NAME + " experimental build " + ModInfo.VERSION);
|
||||||
|
MC.sendChatMessage("You are running a unsupported version of the mod!");
|
||||||
|
MC.sendChatMessage("Here be dragons!");
|
||||||
|
|
||||||
|
configOverrideReminderPrinted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONFIG.client().worldGenerator().setDistanceGenerationMode(DistanceGenerationMode.FULL);
|
||||||
|
|
||||||
|
// CONFIG.client().worldGenerator().setGenerationPriority(GenerationPriority.AUTO);
|
||||||
|
|
||||||
|
// CONFIG.client().graphics().advancedGraphics().setGpuUploadMethod(GpuUploadMethod.BUFFER_STORAGE);
|
||||||
|
// CONFIG.client().graphics().quality().setLodChunkRenderDistance(128);
|
||||||
|
|
||||||
|
// CONFIG.client().graphics().fogQuality().setFogDrawMode(FogDrawMode.FOG_ENABLED);
|
||||||
|
// CONFIG.client().graphics().fogQuality().setFogDistance(FogDistance.FAR);
|
||||||
|
// CONFIG.client().graphics().fogQuality().setDisableVanillaFog(true);
|
||||||
|
|
||||||
|
// CONFIG.client().advanced().buffers().setRebuildTimes(BufferRebuildTimes.FREQUENT);
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG.client().advanced().debugging().setDebugKeybindingsEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================//
|
||||||
|
// Lod maintenance //
|
||||||
|
//=================//
|
||||||
|
|
||||||
|
/** This event is called once during the first frame Minecraft renders in the world. */
|
||||||
|
public void firstFrameSetup()
|
||||||
|
{
|
||||||
|
// make sure the GLProxy is created before the LodBufferBuilder needs it
|
||||||
|
GLProxy.getInstance();
|
||||||
|
|
||||||
|
firstTimeSetupComplete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.api;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.builders.worldGeneration.LodGenWorker;
|
||||||
|
import com.seibel.lod.core.builders.worldGeneration.LodWorldGenerator;
|
||||||
|
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||||
|
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||||
|
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||||
|
import com.seibel.lod.core.render.LodRenderer;
|
||||||
|
import com.seibel.lod.core.util.DataPointUtil;
|
||||||
|
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
import com.seibel.lod.core.util.SingletonHandler;
|
||||||
|
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This holds the methods that should be called
|
||||||
|
* by the host mod loader (Fabric, Forge, etc.).
|
||||||
|
* Specifically server and client events.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-12-2021
|
||||||
|
*/
|
||||||
|
public class EventApi
|
||||||
|
{
|
||||||
|
public static final EventApi INSTANCE = new EventApi();
|
||||||
|
|
||||||
|
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||||
|
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* can be set if we want to recalculate variables related
|
||||||
|
* to the LOD view distance
|
||||||
|
*/
|
||||||
|
private boolean recalculateWidths = false;
|
||||||
|
|
||||||
|
|
||||||
|
private EventApi()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// tick events //
|
||||||
|
//=============//
|
||||||
|
|
||||||
|
public void serverTickEvent()
|
||||||
|
{
|
||||||
|
if (!MC.playerExists() || ApiShared.lodWorld.getIsWorldNotLoaded())
|
||||||
|
return;
|
||||||
|
|
||||||
|
LodDimension lodDim = ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension());
|
||||||
|
if (lodDim == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ClientApi.renderer, ApiShared.lodBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==============//
|
||||||
|
// world events //
|
||||||
|
//==============//
|
||||||
|
|
||||||
|
public void chunkLoadEvent(IChunkWrapper chunk, IDimensionTypeWrapper dimType)
|
||||||
|
{
|
||||||
|
ApiShared.lodBuilder.generateLodNodeAsync(chunk, ApiShared.lodWorld, dimType, DistanceGenerationMode.FULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void worldSaveEvent()
|
||||||
|
{
|
||||||
|
ApiShared.lodWorld.saveAllDimensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This is also called when a new dimension loads */
|
||||||
|
public void worldLoadEvent(IWorldWrapper world)
|
||||||
|
{
|
||||||
|
DataPointUtil.WORLD_HEIGHT = world.getHeight();
|
||||||
|
//LodNodeGenWorker.restartExecutorService();
|
||||||
|
//ThreadMapUtil.clearMaps();
|
||||||
|
|
||||||
|
// the player just loaded a new world/dimension
|
||||||
|
ApiShared.lodWorld.selectWorld(LodUtil.getWorldID(world));
|
||||||
|
|
||||||
|
// make sure the correct LODs are being rendered
|
||||||
|
// (if this isn't done the previous world's LODs may be drawn)
|
||||||
|
ClientApi.renderer.regenerateLODsNextFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This is also called when the user disconnects from a server+ */
|
||||||
|
public void worldUnloadEvent()
|
||||||
|
{
|
||||||
|
// the player just unloaded a world/dimension
|
||||||
|
ThreadMapUtil.clearMaps();
|
||||||
|
|
||||||
|
new Thread(() -> checkIfDisconnectedFromServer()).start();
|
||||||
|
}
|
||||||
|
private void checkIfDisconnectedFromServer()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// world unloading events are called before disconnecting from the server,
|
||||||
|
// so we need to wait a second for MC to disconnect
|
||||||
|
Thread.sleep(1000);
|
||||||
|
}
|
||||||
|
catch (InterruptedException e)
|
||||||
|
{
|
||||||
|
// this should never happen, but just in case
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (MC.getWrappedClientWorld() == null || (!MC.connectedToServer() && !MC.hasSinglePlayerServer()))
|
||||||
|
{
|
||||||
|
// the player just left the server
|
||||||
|
|
||||||
|
// TODO should "resetMod()" be called here? -James
|
||||||
|
|
||||||
|
// if this isn't done unfinished tasks may be left in the queue
|
||||||
|
// preventing new LodChunks form being generated
|
||||||
|
LodGenWorker.restartExecutorService();
|
||||||
|
|
||||||
|
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
|
||||||
|
ApiShared.lodWorld.deselectWorld();
|
||||||
|
|
||||||
|
|
||||||
|
// prevent issues related to the buffer builder
|
||||||
|
// breaking or retaining previous data when changing worlds.
|
||||||
|
ClientApi.renderer.destroyBuffers();
|
||||||
|
recalculateWidths = true;
|
||||||
|
ClientApi.renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory);
|
||||||
|
|
||||||
|
|
||||||
|
// make sure the nulled objects are freed.
|
||||||
|
// (this prevents an out of memory error when
|
||||||
|
// changing worlds)
|
||||||
|
System.gc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void blockChangeEvent(IChunkWrapper chunk, IDimensionTypeWrapper dimType)
|
||||||
|
{
|
||||||
|
// recreate the LOD where the blocks were changed
|
||||||
|
ApiShared.lodBuilder.generateLodNodeAsync(chunk, ApiShared.lodWorld, dimType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// Misc Events //
|
||||||
|
//=============//
|
||||||
|
|
||||||
|
public void onKeyInput(int key, int keyAction)
|
||||||
|
{
|
||||||
|
if (CONFIG.client().advanced().debugging().getDebugKeybindingsEnabled())
|
||||||
|
{
|
||||||
|
if (key == GLFW.GLFW_KEY_F4 && keyAction == GLFW.GLFW_PRESS)
|
||||||
|
{
|
||||||
|
CONFIG.client().advanced().debugging().setDebugMode(CONFIG.client().advanced().debugging().getDebugMode().getNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == GLFW.GLFW_KEY_F6 && keyAction == GLFW.GLFW_PRESS)
|
||||||
|
{
|
||||||
|
CONFIG.client().advanced().debugging().setDrawLods(!CONFIG.client().advanced().debugging().getDrawLods());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Re-centers the given LodDimension if it needs to be. */
|
||||||
|
public void playerMoveEvent(LodDimension lodDim)
|
||||||
|
{
|
||||||
|
// make sure the dimension is centered
|
||||||
|
RegionPos playerRegionPos = new RegionPos(MC.getPlayerBlockPos());
|
||||||
|
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterRegionPosX(), playerRegionPos.z - lodDim.getCenterRegionPosZ());
|
||||||
|
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
|
||||||
|
{
|
||||||
|
ApiShared.lodWorld.saveAllDimensions();
|
||||||
|
lodDim.move(worldRegionOffset);
|
||||||
|
//LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Re-sizes all LodDimensions if they need to be. */
|
||||||
|
public void viewDistanceChangedEvent()
|
||||||
|
{
|
||||||
|
// calculate how wide the dimension(s) should be in regions
|
||||||
|
int chunksWide;
|
||||||
|
if (MC.getWrappedClientWorld().getDimensionType().hasCeiling())
|
||||||
|
chunksWide = Math.min(CONFIG.client().graphics().quality().getLodChunkRenderDistance(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * 2 + 1;
|
||||||
|
else
|
||||||
|
chunksWide = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * 2 + 1;
|
||||||
|
|
||||||
|
int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS);
|
||||||
|
// make sure we have an odd number of regions
|
||||||
|
newWidth += (newWidth & 1) == 0 ? 1 : 2;
|
||||||
|
|
||||||
|
// do the dimensions need to change in size?
|
||||||
|
if (ApiShared.lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths)
|
||||||
|
{
|
||||||
|
ApiShared.lodWorld.saveAllDimensions();
|
||||||
|
|
||||||
|
// update the dimensions to fit the new width
|
||||||
|
ApiShared.lodWorld.resizeDimensionRegionWidth(newWidth);
|
||||||
|
ApiShared.lodBuilder.defaultDimensionWidthInRegions = newWidth;
|
||||||
|
ClientApi.renderer.setupBuffers(ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension()));
|
||||||
|
|
||||||
|
recalculateWidths = false;
|
||||||
|
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
|
||||||
|
}
|
||||||
|
DetailDistanceUtil.updateSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+1062
File diff suppressed because it is too large
Load Diff
+52
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.builders.bufferBuilding.lodTemplates;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.enums.LodDirection;
|
||||||
|
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||||
|
import com.seibel.lod.core.objects.VertexOptimizer;
|
||||||
|
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||||
|
import com.seibel.lod.core.util.ColorUtil;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the abstract class used to create different
|
||||||
|
* BufferBuilders.
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-13-2021
|
||||||
|
*/
|
||||||
|
public abstract class AbstractLodTemplate
|
||||||
|
{
|
||||||
|
/** Uploads the given LOD to the buffer. */
|
||||||
|
public abstract void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||||
|
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled);
|
||||||
|
|
||||||
|
/** add the given position and color to the buffer */
|
||||||
|
protected void addPosAndColor(LodBufferBuilder buffer,
|
||||||
|
float x, float y, float z,
|
||||||
|
int color)
|
||||||
|
{
|
||||||
|
// TODO re-add transparency by replacing the 255 with "ColorUtil.getAlpha(color)"
|
||||||
|
buffer.vertex(x, y, z).color(ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color), 255).endVertex();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.builders.bufferBuilding.lodTemplates;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.enums.LodDirection;
|
||||||
|
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||||
|
import com.seibel.lod.core.objects.VertexOptimizer;
|
||||||
|
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||||
|
import com.seibel.lod.core.util.ColorUtil;
|
||||||
|
import com.seibel.lod.core.util.DataPointUtil;
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds LODs as rectangular prisms.
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-8-2021
|
||||||
|
*/
|
||||||
|
public class CubicLodTemplate extends AbstractLodTemplate
|
||||||
|
{
|
||||||
|
|
||||||
|
public CubicLodTemplate()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||||
|
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||||
|
{
|
||||||
|
if (vertexOptimizer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// equivalent to 2^detailLevel
|
||||||
|
int blockWidth = 1 << detailLevel;
|
||||||
|
|
||||||
|
int color;
|
||||||
|
if (debugging != DebugMode.OFF)
|
||||||
|
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB();
|
||||||
|
else
|
||||||
|
color = DataPointUtil.getColor(data);
|
||||||
|
|
||||||
|
|
||||||
|
generateBoundingBox(
|
||||||
|
vertexOptimizer,
|
||||||
|
DataPointUtil.getHeight(data),
|
||||||
|
DataPointUtil.getDepth(data),
|
||||||
|
blockWidth,
|
||||||
|
posX * blockWidth, 0, posZ * blockWidth, // x, y, z offset
|
||||||
|
bufferCenterBlockPos,
|
||||||
|
adjData,
|
||||||
|
color,
|
||||||
|
DataPointUtil.getLightSkyAlt(data),
|
||||||
|
DataPointUtil.getLightBlock(data),
|
||||||
|
adjShadeDisabled);
|
||||||
|
|
||||||
|
addBoundingBoxToBuffer(buffer, vertexOptimizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateBoundingBox(VertexOptimizer vertexOptimizer,
|
||||||
|
int height, int depth, int width,
|
||||||
|
double xOffset, double yOffset, double zOffset,
|
||||||
|
AbstractBlockPosWrapper bufferCenterBlockPos,
|
||||||
|
Map<LodDirection, long[]> adjData,
|
||||||
|
int color,
|
||||||
|
int skyLight,
|
||||||
|
int blockLight,
|
||||||
|
boolean[] adjShadeDisabled)
|
||||||
|
{
|
||||||
|
// don't add an LOD if it is empty
|
||||||
|
if (height == -1 && depth == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (depth == height)
|
||||||
|
// if the top and bottom points are at the same height
|
||||||
|
// render this LOD as 1 block thick
|
||||||
|
height++;
|
||||||
|
|
||||||
|
// offset the AABB by its x/z position in the world since
|
||||||
|
// it uses doubles to specify its location, unlike the model view matrix
|
||||||
|
// which only uses floats
|
||||||
|
double x = -bufferCenterBlockPos.getX();
|
||||||
|
double z = -bufferCenterBlockPos.getZ();
|
||||||
|
vertexOptimizer.reset();
|
||||||
|
vertexOptimizer.setColor(color, adjShadeDisabled);
|
||||||
|
vertexOptimizer.setLights(skyLight, blockLight);
|
||||||
|
vertexOptimizer.setWidth(width, height - depth, width);
|
||||||
|
vertexOptimizer.setOffset((int) (xOffset + x), (int) (depth + yOffset), (int) (zOffset + z));
|
||||||
|
vertexOptimizer.setUpCulling(32, bufferCenterBlockPos);
|
||||||
|
vertexOptimizer.setAdjData(adjData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBoundingBoxToBuffer(LodBufferBuilder buffer, VertexOptimizer vertexOptimizer)
|
||||||
|
{
|
||||||
|
int color;
|
||||||
|
int skyLight;
|
||||||
|
int blockLight;
|
||||||
|
for (LodDirection lodDirection : VertexOptimizer.DIRECTIONS)
|
||||||
|
{
|
||||||
|
if(vertexOptimizer.isCulled(lodDirection))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int verticalFaceIndex = 0;
|
||||||
|
while (vertexOptimizer.shouldRenderFace(lodDirection, verticalFaceIndex))
|
||||||
|
{
|
||||||
|
for (int vertexIndex = 0; vertexIndex < 6; vertexIndex++)
|
||||||
|
{
|
||||||
|
color = vertexOptimizer.getColor(lodDirection);
|
||||||
|
skyLight = vertexOptimizer.getSkyLight(lodDirection, verticalFaceIndex);
|
||||||
|
blockLight = vertexOptimizer.getBlockLight();
|
||||||
|
color = ColorUtil.applyLightValue(color, skyLight, blockLight);
|
||||||
|
addPosAndColor(buffer,
|
||||||
|
vertexOptimizer.getX(lodDirection, vertexIndex),
|
||||||
|
vertexOptimizer.getY(lodDirection, vertexIndex, verticalFaceIndex) + DataPointUtil.VERTICAL_OFFSET,
|
||||||
|
vertexOptimizer.getZ(lodDirection, vertexIndex),
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
verticalFaceIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.builders.bufferBuilding.lodTemplates;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.api.ClientApi;
|
||||||
|
import com.seibel.lod.core.enums.LodDirection;
|
||||||
|
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||||
|
import com.seibel.lod.core.objects.VertexOptimizer;
|
||||||
|
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO DynamicLodTemplate
|
||||||
|
* Chunks smoothly transition between
|
||||||
|
* each other, unless a neighboring chunk
|
||||||
|
* is at a significantly different height.
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 06-16-2021
|
||||||
|
*/
|
||||||
|
public class DynamicLodTemplate extends AbstractLodTemplate
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||||
|
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||||
|
{
|
||||||
|
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.builders.bufferBuilding.lodTemplates;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.api.ClientApi;
|
||||||
|
import com.seibel.lod.core.enums.LodDirection;
|
||||||
|
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||||
|
import com.seibel.lod.core.objects.VertexOptimizer;
|
||||||
|
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO #21 TriangularLodTemplate
|
||||||
|
* Builds each LOD chunk as a singular rectangular prism.
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 06-16-2021
|
||||||
|
*/
|
||||||
|
public class TriangularLodTemplate extends AbstractLodTemplate
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||||
|
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||||
|
{
|
||||||
|
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,538 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.builders.lodBuilding;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||||
|
import com.seibel.lod.core.enums.config.HorizontalResolution;
|
||||||
|
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||||
|
import com.seibel.lod.core.objects.lod.LodRegion;
|
||||||
|
import com.seibel.lod.core.objects.lod.LodWorld;
|
||||||
|
import com.seibel.lod.core.util.ColorUtil;
|
||||||
|
import com.seibel.lod.core.util.DataPointUtil;
|
||||||
|
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||||
|
import com.seibel.lod.core.util.LevelPosUtil;
|
||||||
|
import com.seibel.lod.core.util.LodThreadFactory;
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
import com.seibel.lod.core.util.SingletonHandler;
|
||||||
|
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is in charge of creating Lod related objects.
|
||||||
|
*
|
||||||
|
* @author Cola
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 10-22-2021
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("GrazieInspection") public class LodBuilder
|
||||||
|
{
|
||||||
|
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||||
|
private static final IBlockColorSingletonWrapper BLOCK_COLOR = SingletonHandler.get(IBlockColorSingletonWrapper.class);
|
||||||
|
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||||
|
|
||||||
|
/** If no blocks are found in the area in determineBottomPointForArea return this */
|
||||||
|
public static final short DEFAULT_DEPTH = 0;
|
||||||
|
/** If no blocks are found in the area in determineHeightPointForArea return this */
|
||||||
|
public static final short DEFAULT_HEIGHT = 0;
|
||||||
|
/** Minecraft's max light value */
|
||||||
|
public static final short DEFAULT_MAX_LIGHT = 15;
|
||||||
|
|
||||||
|
|
||||||
|
private final ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
|
||||||
|
private final ILodConfigWrapperSingleton config = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How wide LodDimensions should be in regions <br>
|
||||||
|
* Is automatically set before the first frame in ClientProxy.
|
||||||
|
*/
|
||||||
|
public int defaultDimensionWidthInRegions = 0;
|
||||||
|
|
||||||
|
//public static final boolean useExperimentalLighting = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public LodBuilder()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateLodNodeAsync(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim)
|
||||||
|
{
|
||||||
|
generateLodNodeAsync(chunk, lodWorld, dim, DistanceGenerationMode.FULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateLodNodeAsync(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim, DistanceGenerationMode generationMode)
|
||||||
|
{
|
||||||
|
if (lodWorld == null || lodWorld.getIsWorldNotLoaded())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// don't try to create an LOD object
|
||||||
|
// if for some reason we aren't
|
||||||
|
// given a valid chunk object
|
||||||
|
if (chunk == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Thread thread = new Thread(() ->
|
||||||
|
{
|
||||||
|
//noinspection GrazieInspection
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// we need a loaded client world in order to
|
||||||
|
// get the textures for blocks
|
||||||
|
if (MC.getWrappedClientWorld() == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// don't try to generate LODs if the user isn't in the world anymore
|
||||||
|
// (this happens a lot when the user leaves a world/server)
|
||||||
|
if (!MC.hasSinglePlayerServer() && !MC.connectedToServer())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// make sure the dimension exists
|
||||||
|
LodDimension lodDim;
|
||||||
|
if (lodWorld.getLodDimension(dim) == null)
|
||||||
|
{
|
||||||
|
lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions);
|
||||||
|
lodWorld.addLodDimension(lodDim);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lodDim = lodWorld.getLodDimension(dim);
|
||||||
|
}
|
||||||
|
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode));
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException | NullPointerException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
// if the world changes while LODs are being generated
|
||||||
|
// they will throw errors as they try to access things that no longer
|
||||||
|
// exist.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lodGenThreadPool.execute(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a LodNode for a chunk in the given world.
|
||||||
|
* @throws IllegalArgumentException thrown if either the chunk or world is null.
|
||||||
|
*/
|
||||||
|
public void generateLodNodeFromChunk(LodDimension lodDim, IChunkWrapper chunk) throws IllegalArgumentException
|
||||||
|
{
|
||||||
|
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a LodNode for a chunk in the given world.
|
||||||
|
* @throws IllegalArgumentException thrown if either the chunk or world is null.
|
||||||
|
*/
|
||||||
|
public void generateLodNodeFromChunk(LodDimension lodDim, IChunkWrapper chunk, LodBuilderConfig config)
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
|
if (chunk == null)
|
||||||
|
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||||
|
|
||||||
|
int startX;
|
||||||
|
int startZ;
|
||||||
|
|
||||||
|
|
||||||
|
LodRegion region = lodDim.getRegion(chunk.getPos().getRegionX(), chunk.getPos().getRegionZ());
|
||||||
|
if (region == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// this happens if a LOD is generated after the user leaves the world.
|
||||||
|
if (MC.getWrappedClientWorld() == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// determine how many LODs to generate horizontally
|
||||||
|
byte minDetailLevel = region.getMinDetailLevel();
|
||||||
|
HorizontalResolution detail = DetailDistanceUtil.getLodGenDetail(minDetailLevel);
|
||||||
|
|
||||||
|
|
||||||
|
// determine how many LODs to generate vertically
|
||||||
|
//VerticalQuality verticalQuality = LodConfig.CLIENT.graphics.qualityOption.verticalQuality.get();
|
||||||
|
byte detailLevel = detail.detailLevel;
|
||||||
|
|
||||||
|
|
||||||
|
// generate the LODs
|
||||||
|
int posX;
|
||||||
|
int posZ;
|
||||||
|
for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
|
||||||
|
{
|
||||||
|
startX = detail.startX[i];
|
||||||
|
startZ = detail.startZ[i];
|
||||||
|
|
||||||
|
long[] data;
|
||||||
|
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ);
|
||||||
|
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.WORLD_HEIGHT / 2 + 1, DetailDistanceUtil.getMaxVerticalData(detailLevel));
|
||||||
|
|
||||||
|
|
||||||
|
//lodDim.clear(detailLevel, posX, posZ);
|
||||||
|
if (data != null && data.length != 0)
|
||||||
|
{
|
||||||
|
posX = LevelPosUtil.convert((byte) 0, chunk.getPos().getX() * 16 + startX, detail.detailLevel);
|
||||||
|
posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().getZ() * 16 + startZ, detail.detailLevel);
|
||||||
|
lodDim.addVerticalData(detailLevel, posX, posZ, data, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().getX(), chunk.getPos().getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** creates a vertical DataPoint */
|
||||||
|
private long[] createVerticalDataToMerge(HorizontalResolution detail, IChunkWrapper chunk, LodBuilderConfig config, int startX, int startZ)
|
||||||
|
{
|
||||||
|
// equivalent to 2^detailLevel
|
||||||
|
int size = 1 << detail.detailLevel;
|
||||||
|
|
||||||
|
long[] dataToMerge = ThreadMapUtil.getBuilderVerticalArray(detail.detailLevel);
|
||||||
|
int verticalData = DataPointUtil.WORLD_HEIGHT / 2 + 1;
|
||||||
|
|
||||||
|
AbstractChunkPosWrapper chunkPos = chunk.getPos();
|
||||||
|
int height;
|
||||||
|
int depth;
|
||||||
|
int color;
|
||||||
|
int light;
|
||||||
|
int lightSky;
|
||||||
|
int lightBlock;
|
||||||
|
int generation = config.distanceGenerationMode.complexity;
|
||||||
|
|
||||||
|
int xRel;
|
||||||
|
int zRel;
|
||||||
|
int xAbs;
|
||||||
|
int yAbs;
|
||||||
|
int zAbs;
|
||||||
|
boolean hasCeiling = MC.getWrappedClientWorld().getDimensionType().hasCeiling();
|
||||||
|
boolean hasSkyLight = MC.getWrappedClientWorld().getDimensionType().hasSkyLight();
|
||||||
|
boolean isDefault;
|
||||||
|
AbstractBlockPosWrapper blockPos = FACTORY.createBlockPos();
|
||||||
|
int index;
|
||||||
|
|
||||||
|
for (index = 0; index < size * size; index++)
|
||||||
|
{
|
||||||
|
xRel = startX + index % size;
|
||||||
|
zRel = startZ + index / size;
|
||||||
|
xAbs = chunkPos.getMinBlockX() + xRel;
|
||||||
|
zAbs = chunkPos.getMinBlockZ() + zRel;
|
||||||
|
|
||||||
|
//Calculate the height of the lod
|
||||||
|
yAbs = DataPointUtil.WORLD_HEIGHT - DataPointUtil.VERTICAL_OFFSET + 1;
|
||||||
|
int count = 0;
|
||||||
|
boolean topBlock = true;
|
||||||
|
while (yAbs > 0)
|
||||||
|
{
|
||||||
|
height = determineHeightPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
|
||||||
|
|
||||||
|
// If the lod is at the default height, it must be void data
|
||||||
|
if (height == DEFAULT_HEIGHT)
|
||||||
|
{
|
||||||
|
if (topBlock)
|
||||||
|
dataToMerge[index * verticalData] = DataPointUtil.createVoidDataPoint(generation);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
yAbs = height - 1;
|
||||||
|
// We search light on above air block
|
||||||
|
depth = determineBottomPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
|
||||||
|
if (hasCeiling && topBlock)
|
||||||
|
{
|
||||||
|
yAbs = depth;
|
||||||
|
blockPos.set(xAbs, yAbs, zAbs);
|
||||||
|
light = getLightValue(chunk, blockPos, true, hasSkyLight, true);
|
||||||
|
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs, blockPos);
|
||||||
|
blockPos.set(xAbs, yAbs - 1, zAbs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
blockPos.set(xAbs, yAbs, zAbs);
|
||||||
|
light = getLightValue(chunk, blockPos, hasCeiling, hasSkyLight, topBlock);
|
||||||
|
color = generateLodColor(chunk, config, xRel, yAbs, zRel, blockPos);
|
||||||
|
blockPos.set(xAbs, yAbs + 1, zAbs);
|
||||||
|
}
|
||||||
|
lightBlock = light & 0b1111;
|
||||||
|
lightSky = (light >> 4) & 0b1111;
|
||||||
|
isDefault = ((light >> 8)) == 1;
|
||||||
|
|
||||||
|
dataToMerge[index * verticalData + count] = DataPointUtil.createDataPoint(height - DataPointUtil.VERTICAL_OFFSET, depth - DataPointUtil.VERTICAL_OFFSET, color, lightSky, lightBlock, generation, isDefault);
|
||||||
|
topBlock = false;
|
||||||
|
yAbs = depth - 1;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dataToMerge;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the lowest valid point from the bottom.
|
||||||
|
* Used when creating a vertical LOD.
|
||||||
|
*/
|
||||||
|
private short determineBottomPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper blockPos)
|
||||||
|
{
|
||||||
|
short depth = DEFAULT_DEPTH;
|
||||||
|
|
||||||
|
for (int y = yAbs; y >= 0; y--)
|
||||||
|
{
|
||||||
|
blockPos.set(xAbs, y, zAbs);
|
||||||
|
if (!isLayerValidLodPoint(chunk, blockPos))
|
||||||
|
{
|
||||||
|
depth = (short) (y + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find the highest valid point from the Top */
|
||||||
|
private short determineHeightPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper blockPos)
|
||||||
|
{
|
||||||
|
short height = DEFAULT_HEIGHT;
|
||||||
|
if (config.useHeightmap)
|
||||||
|
height = (short) chunk.getHeightMapValue(xAbs, zAbs);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int y = yAbs; y >= 0; y--)
|
||||||
|
{
|
||||||
|
blockPos.set(xAbs, y, zAbs);
|
||||||
|
if (isLayerValidLodPoint(chunk, blockPos))
|
||||||
|
{
|
||||||
|
height = (short) (y + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// =====================//
|
||||||
|
// constructor helpers //
|
||||||
|
// =====================//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the color for the given chunk using biome water color, foliage
|
||||||
|
* color, and grass color.
|
||||||
|
*/
|
||||||
|
private int generateLodColor(IChunkWrapper chunk, LodBuilderConfig builderConfig, int xRel, int yAbs, int zRel, AbstractBlockPosWrapper blockPos)
|
||||||
|
{
|
||||||
|
int colorInt;
|
||||||
|
if (builderConfig.useBiomeColors)
|
||||||
|
{
|
||||||
|
// I have no idea why I need to bit shift to the right, but
|
||||||
|
// if I don't the biomes don't show up correctly.
|
||||||
|
colorInt = chunk.getBiome(xRel, yAbs, zRel).getColorForBiome(xRel, zRel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs, chunk.getPos().getMinBlockZ() + zRel);
|
||||||
|
colorInt = getColorForBlock(chunk, blockPos);
|
||||||
|
|
||||||
|
// if we are skipping non-full and non-solid blocks that means we ignore
|
||||||
|
// snow, flowers, etc. Get the above block so we can still get the color
|
||||||
|
// of the snow, flower, etc. that may be above this block
|
||||||
|
int aboveColorInt = 0;
|
||||||
|
if (config.client().worldGenerator().getBlocksToAvoid().nonFull || config.client().worldGenerator().getBlocksToAvoid().noCollision)
|
||||||
|
{
|
||||||
|
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs + 1, chunk.getPos().getMinBlockZ() + zRel);
|
||||||
|
aboveColorInt = getColorForBlock(chunk, blockPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (colorInt == 0 && yAbs > 0)
|
||||||
|
// if this block is invisible, check the block below it
|
||||||
|
// colorInt = generateLodColor(chunk, config, xRel, yAbs - 1, zRel, blockPos);
|
||||||
|
|
||||||
|
// override this block's color if there was a block above this
|
||||||
|
// and we were avoiding non-full/non-solid blocks
|
||||||
|
if (aboveColorInt != 0)
|
||||||
|
colorInt = aboveColorInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return colorInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the light value for the given block position */
|
||||||
|
private int getLightValue(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
|
||||||
|
{
|
||||||
|
int skyLight = 0;
|
||||||
|
int blockLight;
|
||||||
|
// 1 means the lighting is a guess
|
||||||
|
int isDefault = 0;
|
||||||
|
|
||||||
|
IWorldWrapper world = MC.getWrappedServerWorld();
|
||||||
|
|
||||||
|
int blockBrightness = chunk.getEmittedBrightness(blockPos);
|
||||||
|
// get the air block above or below this block
|
||||||
|
if (hasCeiling && topBlock)
|
||||||
|
blockPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ());
|
||||||
|
else
|
||||||
|
blockPos.set(blockPos.getX(), blockPos.getY() + 1, blockPos.getZ());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (world != null)
|
||||||
|
{
|
||||||
|
// server world sky light (always accurate)
|
||||||
|
blockLight = world.getBlockLight(blockPos);
|
||||||
|
if (topBlock && !hasCeiling && hasSkyLight)
|
||||||
|
skyLight = DEFAULT_MAX_LIGHT;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (hasSkyLight)
|
||||||
|
skyLight = world.getSkyLight(blockPos);
|
||||||
|
//else
|
||||||
|
// skyLight = 0;
|
||||||
|
}
|
||||||
|
if (!topBlock && skyLight == 15)
|
||||||
|
{
|
||||||
|
// we are on predicted terrain, and we don't know what the light here is,
|
||||||
|
// lets just take a guess
|
||||||
|
if (blockPos.getY() >= MC.getWrappedClientWorld().getSeaLevel() - 5)
|
||||||
|
{
|
||||||
|
skyLight = 12;
|
||||||
|
isDefault = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
skyLight = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
world = MC.getWrappedClientWorld();
|
||||||
|
if (world==null)
|
||||||
|
{
|
||||||
|
blockLight = 0;
|
||||||
|
skyLight = 12;
|
||||||
|
isDefault = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// client world sky light (almost never accurate)
|
||||||
|
blockLight = world.getBlockLight(blockPos);
|
||||||
|
// estimate what the lighting should be
|
||||||
|
if (hasSkyLight || !hasCeiling)
|
||||||
|
{
|
||||||
|
if (topBlock)
|
||||||
|
skyLight = DEFAULT_MAX_LIGHT;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (hasSkyLight)
|
||||||
|
skyLight = world.getSkyLight(blockPos);
|
||||||
|
//else
|
||||||
|
// skyLight = 0;
|
||||||
|
if (!chunk.isLightCorrect() && (skyLight == 0 || skyLight == 15))
|
||||||
|
{
|
||||||
|
// we don't know what the light here is,
|
||||||
|
// lets just take a guess
|
||||||
|
if (blockPos.getY() >= MC.getWrappedClientWorld().getSeaLevel() - 5)
|
||||||
|
{
|
||||||
|
skyLight = 12;
|
||||||
|
isDefault = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
skyLight = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blockLight = LodUtil.clamp(0, Math.max(blockLight, blockBrightness), DEFAULT_MAX_LIGHT);
|
||||||
|
|
||||||
|
return blockLight + (skyLight << 4) + (isDefault << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a color int for the given block. */
|
||||||
|
private int getColorForBlock(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
|
||||||
|
{
|
||||||
|
int colorOfBlock;
|
||||||
|
int colorInt;
|
||||||
|
|
||||||
|
int xRel = blockPos.getX() - chunk.getPos().getMinBlockX();
|
||||||
|
int zRel = blockPos.getZ() - chunk.getPos().getMinBlockZ();
|
||||||
|
//int x = blockPos.getX();
|
||||||
|
int y = blockPos.getY();
|
||||||
|
//int z = blockPos.getZ();
|
||||||
|
|
||||||
|
IBlockColorWrapper blockColorWrapper;
|
||||||
|
IBlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(blockPos);
|
||||||
|
|
||||||
|
if (chunk.isWaterLogged(blockPos))
|
||||||
|
blockColorWrapper = BLOCK_COLOR.getWaterColor();
|
||||||
|
else
|
||||||
|
blockColorWrapper = chunk.getBlockColorWrapper(blockPos);
|
||||||
|
|
||||||
|
if (blockShapeWrapper.isToAvoid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
colorOfBlock = blockColorWrapper.getColor();
|
||||||
|
|
||||||
|
|
||||||
|
if (blockColorWrapper.hasTint())
|
||||||
|
{
|
||||||
|
IBiomeWrapper biome = chunk.getBiome(xRel, y, zRel);
|
||||||
|
int tintValue;
|
||||||
|
if (blockColorWrapper.hasGrassTint())
|
||||||
|
// grass and green plants
|
||||||
|
tintValue = biome.getGrassTint(0,0);
|
||||||
|
else if (blockColorWrapper.hasFolliageTint())
|
||||||
|
tintValue = biome.getFolliageTint();
|
||||||
|
else
|
||||||
|
//we can reintroduce this with the wrappers
|
||||||
|
tintValue = biome.getWaterTint();
|
||||||
|
|
||||||
|
colorInt = ColorUtil.multiplyRGBcolors(tintValue | 0xFF000000, colorOfBlock);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
colorInt = colorOfBlock;
|
||||||
|
return colorInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Is the block at the given blockPos a valid LOD point? */
|
||||||
|
private boolean isLayerValidLodPoint(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
|
||||||
|
{
|
||||||
|
if (chunk.isWaterLogged(blockPos))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
boolean nonFullAvoidance = config.client().worldGenerator().getBlocksToAvoid().nonFull;
|
||||||
|
boolean noCollisionAvoidance = config.client().worldGenerator().getBlocksToAvoid().noCollision;
|
||||||
|
|
||||||
|
IBlockShapeWrapper block = chunk.getBlockShapeWrapper(blockPos);
|
||||||
|
return !block.isToAvoid()
|
||||||
|
&& !(nonFullAvoidance && block.isNonFull())
|
||||||
|
&& !(noCollisionAvoidance && block.hasNoCollision());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.builders.lodBuilding;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to easily configure how LodChunks are generated.
|
||||||
|
* Generally this will only be used if we want to generate a
|
||||||
|
* LodChunk using an incomplete Chunk, otherwise the defaults
|
||||||
|
* work best for a fully generated chunk (IE has correct surface blocks).
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 8-14-2021
|
||||||
|
*/
|
||||||
|
public class LodBuilderConfig
|
||||||
|
{
|
||||||
|
/** default: false */
|
||||||
|
public boolean useHeightmap;
|
||||||
|
/** default: false */
|
||||||
|
public boolean useBiomeColors;
|
||||||
|
/** default: true */
|
||||||
|
public boolean useSolidBlocksInColorGen;
|
||||||
|
/** default: server */
|
||||||
|
public DistanceGenerationMode distanceGenerationMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* default settings for a normal chunk <br>
|
||||||
|
* useHeightmap = false <br>
|
||||||
|
* useBiomeColors = false <br>
|
||||||
|
* useSolidBlocksInColorGen = true <br>
|
||||||
|
* generationMode = Server <br>
|
||||||
|
*/
|
||||||
|
public LodBuilderConfig()
|
||||||
|
{
|
||||||
|
useHeightmap = false;
|
||||||
|
useBiomeColors = false;
|
||||||
|
useSolidBlocksInColorGen = true;
|
||||||
|
distanceGenerationMode = DistanceGenerationMode.FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param newUseHeightmap default = false
|
||||||
|
* @param newUseBiomeColors default = false
|
||||||
|
* @param newUseSolidBlocksInBiomeColor default = true
|
||||||
|
* @param newDistanceGenerationMode default = Server
|
||||||
|
*/
|
||||||
|
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors,
|
||||||
|
boolean newUseSolidBlocksInBiomeColor, DistanceGenerationMode newDistanceGenerationMode)
|
||||||
|
{
|
||||||
|
useHeightmap = newUseHeightmap;
|
||||||
|
useBiomeColors = newUseBiomeColors;
|
||||||
|
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
|
||||||
|
distanceGenerationMode = newDistanceGenerationMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param newUseHeightmap default = false
|
||||||
|
* @param newUseBiomeColors default = false
|
||||||
|
* @param newUseSolidBlocksInBiomeColor default = true
|
||||||
|
*/
|
||||||
|
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, boolean newUseSolidBlocksInBiomeColor)
|
||||||
|
{
|
||||||
|
this();
|
||||||
|
useHeightmap = newUseHeightmap;
|
||||||
|
useBiomeColors = newUseBiomeColors;
|
||||||
|
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
|
||||||
|
distanceGenerationMode = newUseHeightmap ? DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT : DistanceGenerationMode.BIOME_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param newDistanceGenerationMode default = Server
|
||||||
|
*/
|
||||||
|
public LodBuilderConfig(DistanceGenerationMode newDistanceGenerationMode)
|
||||||
|
{
|
||||||
|
this();
|
||||||
|
distanceGenerationMode = newDistanceGenerationMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.builders.worldGeneration;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import com.seibel.lod.core.api.ClientApi;
|
||||||
|
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||||
|
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||||
|
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
import com.seibel.lod.core.util.SingletonHandler;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to generate a LodChunk at a given ChunkPos.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-20-2021
|
||||||
|
*/
|
||||||
|
public class LodGenWorker
|
||||||
|
{
|
||||||
|
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||||
|
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||||
|
|
||||||
|
public static ExecutorService genThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||||
|
|
||||||
|
private final LodChunkGenThread thread;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public LodGenWorker(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
|
||||||
|
LodBuilder newLodBuilder,
|
||||||
|
LodDimension newLodDimension, IWorldWrapper serverWorld)
|
||||||
|
{
|
||||||
|
// just a few sanity checks
|
||||||
|
if (newPos == null)
|
||||||
|
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos");
|
||||||
|
|
||||||
|
if (newLodBuilder == null)
|
||||||
|
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder");
|
||||||
|
|
||||||
|
if (newLodDimension == null)
|
||||||
|
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodDimension");
|
||||||
|
|
||||||
|
if (serverWorld == null)
|
||||||
|
throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
thread = new LodChunkGenThread(newPos, newGenerationMode,
|
||||||
|
newLodBuilder,
|
||||||
|
newLodDimension, serverWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void queueWork()
|
||||||
|
{
|
||||||
|
if (CONFIG.client().worldGenerator().getDistanceGenerationMode() == DistanceGenerationMode.FULL)
|
||||||
|
{
|
||||||
|
// if we are using FULL generation there is no reason
|
||||||
|
// to queue up a bunch of generation requests,
|
||||||
|
// because MC's internal server (as of 1.16.5) only
|
||||||
|
// responds with a single thread. And we don't
|
||||||
|
// want to cause more lag than necessary or queue up
|
||||||
|
// requests that may end up being unneeded.
|
||||||
|
thread.run();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Every other method can
|
||||||
|
// be done asynchronously
|
||||||
|
genThreads.execute(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
// useful for debugging
|
||||||
|
// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods());
|
||||||
|
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static class LodChunkGenThread implements Runnable
|
||||||
|
{
|
||||||
|
private final AbstractWorldGeneratorWrapper worldGenWrapper;
|
||||||
|
|
||||||
|
public final LodDimension lodDim;
|
||||||
|
public final DistanceGenerationMode generationMode;
|
||||||
|
|
||||||
|
private final AbstractChunkPosWrapper pos;
|
||||||
|
|
||||||
|
public LodChunkGenThread(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
|
||||||
|
LodBuilder newLodBuilder,
|
||||||
|
LodDimension newLodDimension, IWorldWrapper worldWrapper)
|
||||||
|
{
|
||||||
|
worldGenWrapper = FACTORY.createWorldGenerator(newLodBuilder, newLodDimension, worldWrapper);
|
||||||
|
|
||||||
|
pos = newPos;
|
||||||
|
generationMode = newGenerationMode;
|
||||||
|
lodDim = newLodDimension;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// only generate LodChunks if they can
|
||||||
|
// be added to the current LodDimension
|
||||||
|
|
||||||
|
if (lodDim.regionIsInRange(pos.getX() / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.getZ() / LodUtil.REGION_WIDTH_IN_CHUNKS))
|
||||||
|
{
|
||||||
|
switch (generationMode)
|
||||||
|
{
|
||||||
|
case NONE:
|
||||||
|
// don't generate
|
||||||
|
break;
|
||||||
|
case BIOME_ONLY:
|
||||||
|
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||||
|
// fastest
|
||||||
|
worldGenWrapper.generateBiomesOnly(pos, generationMode);
|
||||||
|
break;
|
||||||
|
case SURFACE:
|
||||||
|
// faster
|
||||||
|
worldGenWrapper.generateSurface(pos);
|
||||||
|
break;
|
||||||
|
case FEATURES:
|
||||||
|
// fast
|
||||||
|
worldGenWrapper.generateFeatures(pos);
|
||||||
|
break;
|
||||||
|
case FULL:
|
||||||
|
// very slow
|
||||||
|
worldGenWrapper.generateFull(pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// boolean dataExistence = lodDim.doesDataExist(new LevelPos((byte) 3, pos.x, pos.z));
|
||||||
|
// if (dataExistence)
|
||||||
|
// ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!");
|
||||||
|
// else
|
||||||
|
// ClientProxy.LOGGER.info(pos.x + " " + pos.z);
|
||||||
|
|
||||||
|
// shows the pool size, active threads, queued tasks and completed tasks
|
||||||
|
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||||
|
|
||||||
|
// long endTime = System.currentTimeMillis();
|
||||||
|
// System.out.println(endTime - startTime);
|
||||||
|
|
||||||
|
}// if in range
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ClientApi.LOGGER.error(LodChunkGenThread.class.getSimpleName() + ": ran into an error: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// decrement how many threads are running
|
||||||
|
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1);
|
||||||
|
|
||||||
|
// this position is no longer being generated
|
||||||
|
LodWorldGenerator.INSTANCE.positionsWaitingToBeGenerated.remove(pos);
|
||||||
|
}
|
||||||
|
}// run
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the current genThreads if they are running
|
||||||
|
* and then recreates the Executor service. <br><br>
|
||||||
|
* <p>
|
||||||
|
* This is done to clear any outstanding tasks
|
||||||
|
* that may exist after the player leaves their current world.
|
||||||
|
* If this isn't done unfinished tasks may be left in the queue
|
||||||
|
* preventing new LodChunks form being generated.
|
||||||
|
*/
|
||||||
|
public static void restartExecutorService()
|
||||||
|
{
|
||||||
|
if (genThreads != null && !genThreads.isShutdown())
|
||||||
|
{
|
||||||
|
genThreads.shutdownNow();
|
||||||
|
}
|
||||||
|
genThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.builders.worldGeneration;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||||
|
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||||
|
import com.seibel.lod.core.objects.PosToGenerateContainer;
|
||||||
|
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||||
|
import com.seibel.lod.core.render.LodRenderer;
|
||||||
|
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||||
|
import com.seibel.lod.core.util.LevelPosUtil;
|
||||||
|
import com.seibel.lod.core.util.LodThreadFactory;
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
import com.seibel.lod.core.util.SingletonHandler;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A singleton that handles all long distance LOD world generation.
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 9-25-2021
|
||||||
|
*/
|
||||||
|
public class LodWorldGenerator
|
||||||
|
{
|
||||||
|
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||||
|
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||||
|
private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||||
|
|
||||||
|
|
||||||
|
/** This holds the thread used to create LOD generation requests off the main thread. */
|
||||||
|
private final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator"));
|
||||||
|
|
||||||
|
/** we only want to queue up one generator thread at a time */
|
||||||
|
private boolean generatorThreadRunning = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many chunks to generate outside the player's view distance at one
|
||||||
|
* time. (or more specifically how many requests to make at one time). I
|
||||||
|
* multiply by 8 to make sure there is always a buffer of chunk requests, to
|
||||||
|
* make sure the CPU is always busy, and we can generate LODs as quickly as
|
||||||
|
* possible.
|
||||||
|
*/
|
||||||
|
public int maxChunkGenRequests;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This keeps track of how many chunk generation requests are on going. This is
|
||||||
|
* to limit how many chunks are queued at once. To prevent chunks from being
|
||||||
|
* generated for a long time in an area the player is no longer in.
|
||||||
|
*/
|
||||||
|
public final AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0);
|
||||||
|
|
||||||
|
public final Set<AbstractChunkPosWrapper> positionsWaitingToBeGenerated = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton copy of this object
|
||||||
|
*/
|
||||||
|
public static final LodWorldGenerator INSTANCE = new LodWorldGenerator();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private LodWorldGenerator()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues up LodNodeGenWorkers for the given lodDimension.
|
||||||
|
* @param renderer needed so the LodNodeGenWorkers can flag that the
|
||||||
|
* buffers need to be rebuilt.
|
||||||
|
*/
|
||||||
|
public void queueGenerationRequests(LodDimension lodDim, LodRenderer renderer, LodBuilder lodBuilder)
|
||||||
|
{
|
||||||
|
if (CONFIG.client().worldGenerator().getDistanceGenerationMode() != DistanceGenerationMode.NONE
|
||||||
|
&& !generatorThreadRunning
|
||||||
|
&& MC.hasSinglePlayerServer())
|
||||||
|
{
|
||||||
|
// the thread is now running, don't queue up another thread
|
||||||
|
generatorThreadRunning = true;
|
||||||
|
|
||||||
|
// just in case the config changed
|
||||||
|
maxChunkGenRequests = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads() * 8;
|
||||||
|
|
||||||
|
Thread generatorThread = new Thread(() ->
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// round the player's block position down to the nearest chunk BlockPos
|
||||||
|
int playerPosX = MC.getPlayerBlockPos().getX();
|
||||||
|
int playerPosZ = MC.getPlayerBlockPos().getZ();
|
||||||
|
|
||||||
|
|
||||||
|
//=======================================//
|
||||||
|
// fill in positionsWaitingToBeGenerated //
|
||||||
|
//=======================================//
|
||||||
|
|
||||||
|
IWorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||||
|
|
||||||
|
PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(
|
||||||
|
maxChunkGenRequests,
|
||||||
|
playerPosX,
|
||||||
|
playerPosZ);
|
||||||
|
|
||||||
|
|
||||||
|
byte detailLevel;
|
||||||
|
int posX;
|
||||||
|
int posZ;
|
||||||
|
int nearIndex = 0;
|
||||||
|
int farIndex = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < posToGenerate.getNumberOfPos(); i++)
|
||||||
|
{
|
||||||
|
// I wish there was a way to compress this code, but I'm not aware of
|
||||||
|
// an easy way to do so.
|
||||||
|
|
||||||
|
// add the near positions
|
||||||
|
if (posToGenerate.getNthDetail(nearIndex, true) != 0 && nearIndex < posToGenerate.getNumberOfNearPos())
|
||||||
|
{
|
||||||
|
detailLevel = (byte) (posToGenerate.getNthDetail(nearIndex, true) - 1);
|
||||||
|
posX = posToGenerate.getNthPosX(nearIndex, true);
|
||||||
|
posZ = posToGenerate.getNthPosZ(nearIndex, true);
|
||||||
|
nearIndex++;
|
||||||
|
|
||||||
|
AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
|
||||||
|
|
||||||
|
// prevent generating the same chunk multiple times
|
||||||
|
if (positionsWaitingToBeGenerated.contains(chunkPos))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// don't add more to the generation queue then allowed
|
||||||
|
if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
|
||||||
|
break;
|
||||||
|
|
||||||
|
positionsWaitingToBeGenerated.add(chunkPos);
|
||||||
|
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||||
|
LodGenWorker genWorker = new LodGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
|
||||||
|
genWorker.queueWork();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// add the far positions
|
||||||
|
if (posToGenerate.getNthDetail(farIndex, false) != 0 && farIndex < posToGenerate.getNumberOfFarPos())
|
||||||
|
{
|
||||||
|
detailLevel = (byte) (posToGenerate.getNthDetail(farIndex, false) - 1);
|
||||||
|
posX = posToGenerate.getNthPosX(farIndex, false);
|
||||||
|
posZ = posToGenerate.getNthPosZ(farIndex, false);
|
||||||
|
farIndex++;
|
||||||
|
|
||||||
|
AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
|
||||||
|
|
||||||
|
// don't add more to the generation queue then allowed
|
||||||
|
if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
|
||||||
|
continue;
|
||||||
|
//break;
|
||||||
|
|
||||||
|
// prevent generating the same chunk multiple times
|
||||||
|
if (positionsWaitingToBeGenerated.contains(chunkPos))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
positionsWaitingToBeGenerated.add(chunkPos);
|
||||||
|
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||||
|
LodGenWorker genWorker = new LodGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
|
||||||
|
genWorker.queueWork();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// this shouldn't ever happen, but just in case
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
generatorThreadRunning = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mainGenThread.execute(generatorThread);
|
||||||
|
} // if distanceGenerationMode != DistanceGenerationMode.NONE && !generatorThreadRunning
|
||||||
|
} // queueGenerationRequests
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.seibel.lod.core.dataFormat;
|
||||||
|
|
||||||
|
public class BlockDataFormat
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.seibel.lod.core.dataFormat;
|
||||||
|
|
||||||
|
public class ColorFormat
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.seibel.lod.core.dataFormat;
|
||||||
|
|
||||||
|
public class DepthHeightFormat
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.seibel.lod.core.dataFormat;
|
||||||
|
|
||||||
|
public class LightFormat
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.seibel.lod.core.dataFormat;
|
||||||
|
|
||||||
|
public class PositionDataFormat
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,529 @@
|
|||||||
|
package com.seibel.lod.core.enums;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.objects.math.Vec3i;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A (almost) exact copy of Minecraft's
|
||||||
|
* Direction enum.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-13-2021
|
||||||
|
*/
|
||||||
|
public enum LodDirection
|
||||||
|
{
|
||||||
|
DOWN(0, 1, -1, "down", LodDirection.AxisDirection.NEGATIVE, LodDirection.Axis.Y, new Vec3i(0, -1, 0)),
|
||||||
|
UP(1, 0, -1, "up", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.Y, new Vec3i(0, 1, 0)),
|
||||||
|
NORTH(2, 3, 2, "north", LodDirection.AxisDirection.NEGATIVE, LodDirection.Axis.Z, new Vec3i(0, 0, -1)),
|
||||||
|
SOUTH(3, 2, 0, "south", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.Z, new Vec3i(0, 0, 1)),
|
||||||
|
WEST(4, 5, 1, "west", LodDirection.AxisDirection.NEGATIVE, LodDirection.Axis.X, new Vec3i(-1, 0, 0)),
|
||||||
|
EAST(5, 4, 3, "east", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.X, new Vec3i(1, 0, 0));
|
||||||
|
|
||||||
|
// private final int data3d;
|
||||||
|
// private final int oppositeIndex;
|
||||||
|
// private final int data2d;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final LodDirection.Axis axis;
|
||||||
|
private final LodDirection.AxisDirection axisDirection;
|
||||||
|
private final Vec3i normal;
|
||||||
|
private static final LodDirection[] VALUES = values();
|
||||||
|
|
||||||
|
private static final Map<String, LodDirection> BY_NAME = Arrays.stream(VALUES).collect(Collectors.toMap(LodDirection::getName, (p_199787_0_) ->
|
||||||
|
{
|
||||||
|
return p_199787_0_;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// private static final LodDirection[] BY_3D_DATA = Arrays.stream(VALUES).sorted(Comparator.comparingInt((p_199790_0_) ->
|
||||||
|
// {
|
||||||
|
// return p_199790_0_.data3d;
|
||||||
|
// })).toArray((p_199788_0_) ->
|
||||||
|
// {
|
||||||
|
// return new LodDirection[p_199788_0_];
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// private static final LodDirection[] BY_2D_DATA = Arrays.stream(VALUES).filter((p_199786_0_) ->
|
||||||
|
// {
|
||||||
|
// return p_199786_0_.getAxis().isHorizontal();
|
||||||
|
// }).sorted(Comparator.comparingInt((p_199789_0_) ->
|
||||||
|
// {
|
||||||
|
// return p_199789_0_.data2d;
|
||||||
|
// })).toArray((p_199791_0_) ->
|
||||||
|
// {
|
||||||
|
// return new LodDirection[p_199791_0_];
|
||||||
|
// });
|
||||||
|
|
||||||
|
// private static final Long2ObjectMap<LodDirection> BY_NORMAL = Arrays.stream(VALUES).collect(Collectors.toMap((p_218385_0_) ->
|
||||||
|
// {
|
||||||
|
// return (new BlockPos(p_218385_0_.getNormal())).asLong();
|
||||||
|
// }, (p_218384_0_) ->
|
||||||
|
// {
|
||||||
|
// return p_218384_0_;
|
||||||
|
// }, (p_218386_0_, p_218386_1_) ->
|
||||||
|
// {
|
||||||
|
// throw new IllegalArgumentException("Duplicate keys");
|
||||||
|
// }, Long2ObjectOpenHashMap::new));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LodDirection(int p_i46016_3_, int p_i46016_4_, int p_i46016_5_, String p_i46016_6_, LodDirection.AxisDirection p_i46016_7_, LodDirection.Axis p_i46016_8_, Vec3i p_i46016_9_)
|
||||||
|
{
|
||||||
|
// this.data3d = p_i46016_3_;
|
||||||
|
// this.data2d = p_i46016_5_;
|
||||||
|
// this.oppositeIndex = p_i46016_4_;
|
||||||
|
this.name = p_i46016_6_;
|
||||||
|
this.axis = p_i46016_8_;
|
||||||
|
this.axisDirection = p_i46016_7_;
|
||||||
|
this.normal = p_i46016_9_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// public static LodDirection[] orderedByNearest(Entity p_196054_0_)
|
||||||
|
// {
|
||||||
|
// float f = p_196054_0_.getViewXRot(1.0F) * ((float) Math.PI / 180F);
|
||||||
|
// float f1 = -p_196054_0_.getViewYRot(1.0F) * ((float) Math.PI / 180F);
|
||||||
|
// float f2 = MathHelper.sin(f);
|
||||||
|
// float f3 = MathHelper.cos(f);
|
||||||
|
// float f4 = MathHelper.sin(f1);
|
||||||
|
// float f5 = MathHelper.cos(f1);
|
||||||
|
// boolean flag = f4 > 0.0F;
|
||||||
|
// boolean flag1 = f2 < 0.0F;
|
||||||
|
// boolean flag2 = f5 > 0.0F;
|
||||||
|
// float f6 = flag ? f4 : -f4;
|
||||||
|
// float f7 = flag1 ? -f2 : f2;
|
||||||
|
// float f8 = flag2 ? f5 : -f5;
|
||||||
|
// float f9 = f6 * f3;
|
||||||
|
// float f10 = f8 * f3;
|
||||||
|
// LodDirection lodDirection = flag ? EAST : WEST;
|
||||||
|
// LodDirection direction1 = flag1 ? UP : DOWN;
|
||||||
|
// LodDirection direction2 = flag2 ? SOUTH : NORTH;
|
||||||
|
// if (f6 > f8)
|
||||||
|
// {
|
||||||
|
// if (f7 > f9)
|
||||||
|
// {
|
||||||
|
// return makeDirectionArray(direction1, lodDirection, direction2);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// return f10 > f7 ? makeDirectionArray(lodDirection, direction2, direction1) : makeDirectionArray(lodDirection, direction1, direction2);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else if (f7 > f10)
|
||||||
|
// {
|
||||||
|
// return makeDirectionArray(direction1, direction2, lodDirection);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// return f9 > f7 ? makeDirectionArray(direction2, lodDirection, direction1) : makeDirectionArray(direction2, direction1, lodDirection);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private static LodDirection[] makeDirectionArray(LodDirection p_196053_0_, LodDirection p_196053_1_, LodDirection p_196053_2_)
|
||||||
|
// {
|
||||||
|
// return new LodDirection[] { p_196053_0_, p_196053_1_, p_196053_2_, p_196053_2_.getOpposite(), p_196053_1_.getOpposite(), p_196053_0_.getOpposite() };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static LodDirection rotate(Mat4f p_229385_0_, LodDirection p_229385_1_)
|
||||||
|
// {
|
||||||
|
// Vec3i Vec3i = p_229385_1_.getNormal();
|
||||||
|
// Vector4f vector4f = new Vector4f(Vec3i.getX(), Vec3i.getY(), Vec3i.getZ(), 0.0F);
|
||||||
|
// vector4f.transform(p_229385_0_);
|
||||||
|
// return getNearest(vector4f.x(), vector4f.y(), vector4f.z());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public Quaternion getRotation()
|
||||||
|
// {
|
||||||
|
// Quaternion quaternion = Vector3f.XP.rotationDegrees(90.0F);
|
||||||
|
// switch (this)
|
||||||
|
// {
|
||||||
|
// case DOWN:
|
||||||
|
// return Vector3f.XP.rotationDegrees(180.0F);
|
||||||
|
// case UP:
|
||||||
|
// return Quaternion.ONE.copy();
|
||||||
|
// case NORTH:
|
||||||
|
// quaternion.mul(Vector3f.ZP.rotationDegrees(180.0F));
|
||||||
|
// return quaternion;
|
||||||
|
// case SOUTH:
|
||||||
|
// return quaternion;
|
||||||
|
// case WEST:
|
||||||
|
// quaternion.mul(Vector3f.ZP.rotationDegrees(90.0F));
|
||||||
|
// return quaternion;
|
||||||
|
// case EAST:
|
||||||
|
// default:
|
||||||
|
// quaternion.mul(Vector3f.ZP.rotationDegrees(-90.0F));
|
||||||
|
// return quaternion;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public int get3DDataValue()
|
||||||
|
// {
|
||||||
|
// return this.data3d;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public int get2DDataValue()
|
||||||
|
// {
|
||||||
|
// return this.data2d;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public LodDirection.AxisDirection getAxisDirection()
|
||||||
|
{
|
||||||
|
return this.axisDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public LodDirection getOpposite()
|
||||||
|
// {
|
||||||
|
// return from3DDataValue(this.oppositeIndex);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public LodDirection getClockWise()
|
||||||
|
{
|
||||||
|
switch (this)
|
||||||
|
{
|
||||||
|
case NORTH:
|
||||||
|
return EAST;
|
||||||
|
case SOUTH:
|
||||||
|
return WEST;
|
||||||
|
case WEST:
|
||||||
|
return NORTH;
|
||||||
|
case EAST:
|
||||||
|
return SOUTH;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unable to get Y-rotated facing of " + this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LodDirection getCounterClockWise()
|
||||||
|
{
|
||||||
|
switch (this)
|
||||||
|
{
|
||||||
|
case NORTH:
|
||||||
|
return WEST;
|
||||||
|
case SOUTH:
|
||||||
|
return EAST;
|
||||||
|
case WEST:
|
||||||
|
return SOUTH;
|
||||||
|
case EAST:
|
||||||
|
return NORTH;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unable to get CCW facing of " + this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LodDirection.Axis getAxis()
|
||||||
|
{
|
||||||
|
return this.axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LodDirection byName(String name)
|
||||||
|
{
|
||||||
|
return name == null ? null : BY_NAME.get(name.toLowerCase(Locale.ROOT));
|
||||||
|
}
|
||||||
|
|
||||||
|
// public static LodDirection from3DDataValue(int p_82600_0_)
|
||||||
|
// {
|
||||||
|
// return BY_3D_DATA[MathHelper.abs(p_82600_0_ % BY_3D_DATA.length)];
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static LodDirection from2DDataValue(int p_176731_0_)
|
||||||
|
// {
|
||||||
|
// return BY_2D_DATA[MathHelper.abs(p_176731_0_ % BY_2D_DATA.length)];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @Nullable
|
||||||
|
// public static LodDirection fromNormal(int p_218383_0_, int p_218383_1_, int p_218383_2_)
|
||||||
|
// {
|
||||||
|
// return BY_NORMAL.get(BlockPos.asLong(p_218383_0_, p_218383_1_, p_218383_2_));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static LodDirection fromYRot(double p_176733_0_)
|
||||||
|
// {
|
||||||
|
// return from2DDataValue(MathHelper.floor(p_176733_0_ / 90.0D + 0.5D) & 3);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public static LodDirection fromAxisAndDirection(LodDirection.Axis p_211699_0_, LodDirection.AxisDirection p_211699_1_)
|
||||||
|
{
|
||||||
|
switch (p_211699_0_)
|
||||||
|
{
|
||||||
|
case X:
|
||||||
|
return p_211699_1_ == LodDirection.AxisDirection.POSITIVE ? EAST : WEST;
|
||||||
|
case Y:
|
||||||
|
return p_211699_1_ == LodDirection.AxisDirection.POSITIVE ? UP : DOWN;
|
||||||
|
case Z:
|
||||||
|
default:
|
||||||
|
return p_211699_1_ == LodDirection.AxisDirection.POSITIVE ? SOUTH : NORTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// public float toYRot()
|
||||||
|
// {
|
||||||
|
// return (this.data2d & 3) * 90;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static LodDirection getRandom(Random p_239631_0_)
|
||||||
|
// {
|
||||||
|
// return Util.getRandom(VALUES, p_239631_0_);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static LodDirection getNearest(double p_210769_0_, double p_210769_2_, double p_210769_4_)
|
||||||
|
// {
|
||||||
|
// return getNearest((float) p_210769_0_, (float) p_210769_2_, (float) p_210769_4_);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static LodDirection getNearest(float p_176737_0_, float p_176737_1_, float p_176737_2_)
|
||||||
|
// {
|
||||||
|
// LodDirection lodDirection = NORTH;
|
||||||
|
// float f = Float.MIN_VALUE;
|
||||||
|
//
|
||||||
|
// for (LodDirection direction1 : VALUES)
|
||||||
|
// {
|
||||||
|
// float f1 = p_176737_0_ * direction1.normal.x + p_176737_1_ * direction1.normal.y + p_176737_2_ * direction1.normal.z;
|
||||||
|
// if (f1 > f)
|
||||||
|
// {
|
||||||
|
// f = f1;
|
||||||
|
// lodDirection = direction1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return lodDirection;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public static LodDirection get(LodDirection.AxisDirection p_181076_0_, LodDirection.Axis p_181076_1_)
|
||||||
|
{
|
||||||
|
for (LodDirection lodDirection : VALUES)
|
||||||
|
{
|
||||||
|
if (lodDirection.getAxisDirection() == p_181076_0_ && lodDirection.getAxis() == p_181076_1_)
|
||||||
|
{
|
||||||
|
return lodDirection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("No such direction: " + p_181076_0_ + " " + p_181076_1_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3i getNormal()
|
||||||
|
{
|
||||||
|
return this.normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public boolean isFacingAngle(float p_243532_1_)
|
||||||
|
// {
|
||||||
|
// float f = p_243532_1_ * ((float) Math.PI / 180F);
|
||||||
|
// float f1 = -MathHelper.sin(f);
|
||||||
|
// float f2 = MathHelper.cos(f);
|
||||||
|
// return this.normal.getX() * f1 + this.normal.getZ() * f2 > 0.0F;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public enum Axis implements Predicate<LodDirection>
|
||||||
|
{
|
||||||
|
X("x")
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public int choose(int x, int y, int z)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double choose(double x, double y, double z)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Y("y")
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public int choose(int x, int y, int z)
|
||||||
|
{
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double choose(double x, double y, double z)
|
||||||
|
{
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Z("z")
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public int choose(int x, int y, int z)
|
||||||
|
{
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double choose(double x, double y, double z)
|
||||||
|
{
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final LodDirection.Axis[] VALUES = values();
|
||||||
|
|
||||||
|
private static final Map<String, LodDirection.Axis> BY_NAME = Arrays.stream(VALUES).collect(Collectors.toMap(LodDirection.Axis::getName, (p_199785_0_) ->
|
||||||
|
{
|
||||||
|
return p_199785_0_;
|
||||||
|
}));
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
Axis(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LodDirection.Axis byName(String name)
|
||||||
|
{
|
||||||
|
return BY_NAME.get(name.toLowerCase(Locale.ROOT));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVertical()
|
||||||
|
{
|
||||||
|
return this == Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHorizontal()
|
||||||
|
{
|
||||||
|
return this == X || this == Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public static LodDirection.Axis getRandom(Random p_239634_0_)
|
||||||
|
// {
|
||||||
|
// return Util.getRandom(VALUES, p_239634_0_);
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(LodDirection p_test_1_)
|
||||||
|
{
|
||||||
|
return p_test_1_ != null && p_test_1_.getAxis() == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public LodDirection.Plane getPlane()
|
||||||
|
// {
|
||||||
|
// switch (this)
|
||||||
|
// {
|
||||||
|
// case X:
|
||||||
|
// case Z:
|
||||||
|
// return LodDirection.Plane.HORIZONTAL;
|
||||||
|
// case Y:
|
||||||
|
// return LodDirection.Plane.VERTICAL;
|
||||||
|
// default:
|
||||||
|
// throw new Error("Someone's been tampering with the universe!");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
public abstract int choose(int p_196052_1_, int p_196052_2_, int p_196052_3_);
|
||||||
|
|
||||||
|
public abstract double choose(double p_196051_1_, double p_196051_3_, double p_196051_5_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AxisDirection
|
||||||
|
{
|
||||||
|
POSITIVE(1, "Towards positive"),
|
||||||
|
NEGATIVE(-1, "Towards negative");
|
||||||
|
|
||||||
|
private final int step;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
AxisDirection(int newStep, String newName)
|
||||||
|
{
|
||||||
|
this.step = newStep;
|
||||||
|
this.name = newName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStep()
|
||||||
|
{
|
||||||
|
return this.step;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LodDirection.AxisDirection opposite()
|
||||||
|
{
|
||||||
|
return this == POSITIVE ? NEGATIVE : POSITIVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// public static enum Plane implements Iterable<LodDirection>, Predicate<LodDirection>
|
||||||
|
// {
|
||||||
|
// HORIZONTAL(new LodDirection[] { LodDirection.NORTH, LodDirection.EAST, LodDirection.SOUTH, LodDirection.WEST }, new LodDirection.Axis[] { LodDirection.Axis.X, LodDirection.Axis.Z }),
|
||||||
|
// VERTICAL(new LodDirection[] { LodDirection.UP, LodDirection.DOWN }, new LodDirection.Axis[] { LodDirection.Axis.Y });
|
||||||
|
//
|
||||||
|
// private final LodDirection[] faces;
|
||||||
|
// private final LodDirection.Axis[] axis;
|
||||||
|
//
|
||||||
|
// private Plane(LodDirection[] p_i49393_3_, LodDirection.Axis[] p_i49393_4_)
|
||||||
|
// {
|
||||||
|
// this.faces = p_i49393_3_;
|
||||||
|
// this.axis = p_i49393_4_;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public LodDirection getRandomDirection(Random p_179518_1_)
|
||||||
|
// {
|
||||||
|
// return Util.getRandom(this.faces, p_179518_1_);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public LodDirection.Axis getRandomAxis(Random p_244803_1_)
|
||||||
|
// {
|
||||||
|
// return Util.getRandom(this.axis, p_244803_1_);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public boolean test(@Nullable LodDirection p_test_1_)
|
||||||
|
// {
|
||||||
|
// return p_test_1_ != null && p_test_1_.getAxis().getPlane() == this;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public Iterator<LodDirection> iterator()
|
||||||
|
// {
|
||||||
|
// return Iterators.forArray(this.faces);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public Stream<LodDirection> stream()
|
||||||
|
// {
|
||||||
|
// return Arrays.stream(this.faces);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public String getSerializedName()
|
||||||
|
{
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServerWorld, ClientWorld, Unknown
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-12-2021
|
||||||
|
*/
|
||||||
|
public enum WorldType
|
||||||
|
{
|
||||||
|
ServerWorld,
|
||||||
|
ClientWorld,
|
||||||
|
Unknown
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* heightmap <br>
|
||||||
|
* multi_lod <br>
|
||||||
|
*
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 11-16-2021
|
||||||
|
*/
|
||||||
|
public enum BlocksToAvoid
|
||||||
|
{
|
||||||
|
NONE(false, false),
|
||||||
|
|
||||||
|
NON_FULL(true, false),
|
||||||
|
|
||||||
|
NO_COLLISION(false, true),
|
||||||
|
|
||||||
|
BOTH(true, true);
|
||||||
|
|
||||||
|
public final boolean nonFull;
|
||||||
|
public final boolean noCollision;
|
||||||
|
|
||||||
|
BlocksToAvoid(boolean nonFull, boolean noCollision)
|
||||||
|
{
|
||||||
|
this.nonFull = nonFull;
|
||||||
|
this.noCollision = noCollision;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FREQUENT <br>
|
||||||
|
* NORMAL <br>
|
||||||
|
* RARE <br>
|
||||||
|
* <br>
|
||||||
|
* Determines how fast the buffers need to be regenerated
|
||||||
|
*
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 9-25-2021
|
||||||
|
*/
|
||||||
|
public enum BufferRebuildTimes
|
||||||
|
{
|
||||||
|
FREQUENT(1000, 500, 2500, 1),
|
||||||
|
|
||||||
|
NORMAL(2000, 1000, 5000, 4),
|
||||||
|
|
||||||
|
RARE(5000, 2000, 10000, 16);
|
||||||
|
|
||||||
|
public final int playerMoveTimeout;
|
||||||
|
public final int renderedChunkTimeout;
|
||||||
|
public final int chunkChangeTimeout;
|
||||||
|
public final int playerMoveDistance;
|
||||||
|
|
||||||
|
BufferRebuildTimes(int playerMoveTimeout, int renderedChunkTimeout, int chunkChangeTimeout, int playerMoveDistance)
|
||||||
|
{
|
||||||
|
this.playerMoveTimeout = playerMoveTimeout;
|
||||||
|
this.renderedChunkTimeout = renderedChunkTimeout;
|
||||||
|
this.chunkChangeTimeout = chunkChangeTimeout;
|
||||||
|
this.playerMoveDistance = playerMoveDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NONE <br>
|
||||||
|
* BIOME_ONLY <br>
|
||||||
|
* BIOME_ONLY_SIMULATE_HEIGHT <br>
|
||||||
|
* SURFACE <br>
|
||||||
|
* FEATURES <br>
|
||||||
|
* SERVER <br><br>
|
||||||
|
* <p>
|
||||||
|
* In order of fastest to slowest.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 8-7-2021
|
||||||
|
*/
|
||||||
|
public enum DistanceGenerationMode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Don't generate anything
|
||||||
|
*/
|
||||||
|
NONE((byte) 0),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only generate the biomes and use biome
|
||||||
|
* grass/foliage color, water color, or ice color
|
||||||
|
* to generate the color.
|
||||||
|
* Doesn't generate height, everything is shown at sea level.
|
||||||
|
* Multithreaded - Fastest (2-5 ms)
|
||||||
|
*/
|
||||||
|
BIOME_ONLY((byte) 1),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as BIOME_ONLY, except instead
|
||||||
|
* of always using sea level as the LOD height
|
||||||
|
* different biome types (mountain, ocean, forest, etc.)
|
||||||
|
* use predetermined heights to simulate having height data.
|
||||||
|
*/
|
||||||
|
BIOME_ONLY_SIMULATE_HEIGHT((byte) 2),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the world surface,
|
||||||
|
* this does NOT include caves, trees,
|
||||||
|
* or structures.
|
||||||
|
* Multithreaded - Faster (10-20 ms)
|
||||||
|
*/
|
||||||
|
SURFACE((byte) 3),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate everything except structures.
|
||||||
|
* NOTE: This may cause world generation bugs or instability,
|
||||||
|
* since some features cause concurrentModification exceptions.
|
||||||
|
* Multithreaded - Fast (15-20 ms)
|
||||||
|
*/
|
||||||
|
FEATURES((byte) 4),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask the server to generate/load each chunk.
|
||||||
|
* This is the most compatible, but causes server/simulation lag.
|
||||||
|
* This will also show player made structures if you
|
||||||
|
* are adding the mod on a pre-existing world.
|
||||||
|
* Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms)
|
||||||
|
*/
|
||||||
|
FULL((byte) 5);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The higher the number the more complete the generation is.
|
||||||
|
*/
|
||||||
|
public final byte complexity;
|
||||||
|
|
||||||
|
DistanceGenerationMode(byte complexity)
|
||||||
|
{
|
||||||
|
this.complexity = complexity;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AUTO <br>
|
||||||
|
* Near_First <br>
|
||||||
|
* Far_First <br>
|
||||||
|
* <br>
|
||||||
|
* Determines which LODs should have priority when generating
|
||||||
|
* outside the normal view distance.
|
||||||
|
*
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 12-1-2021
|
||||||
|
*/
|
||||||
|
public enum GenerationPriority
|
||||||
|
{
|
||||||
|
/** NEAR_FIRST when connected to servers and FAR_FIRST when on single player */
|
||||||
|
AUTO,
|
||||||
|
|
||||||
|
NEAR_FIRST,
|
||||||
|
|
||||||
|
FAR_FIRST
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto, Buffer_Storage, Sub_Data, Buffer_Mapping, Data
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 12-1-2021
|
||||||
|
*/
|
||||||
|
public enum GpuUploadMethod
|
||||||
|
{
|
||||||
|
/** Picks the best option based on the GPU the user has. */
|
||||||
|
AUTO,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default for NVIDIA if OpenGL 4.5 is supported. <br>
|
||||||
|
* Fast rendering, no stuttering.
|
||||||
|
*/
|
||||||
|
BUFFER_STORAGE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backup option for NVIDIA. <br>
|
||||||
|
* Fast rendering but may stutter when uploading.
|
||||||
|
*/
|
||||||
|
SUB_DATA,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default option for AMD/Intel. <br>
|
||||||
|
* May end up storing buffers in System memory. <br>
|
||||||
|
* Fast rending if in GPU memory, slow if in system memory, <br>
|
||||||
|
* but won't stutter when uploading.
|
||||||
|
*/
|
||||||
|
BUFFER_MAPPING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backup option for AMD/Intel. <br>
|
||||||
|
* Fast rendering but may stutter when uploading.
|
||||||
|
*/
|
||||||
|
DATA,
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lowest <br>
|
||||||
|
* Low <br>
|
||||||
|
* Medium <br>
|
||||||
|
* High <br>
|
||||||
|
* <br>
|
||||||
|
* this indicates the base of the quadratic function we use for the quality drop off
|
||||||
|
*
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 9-29-2021
|
||||||
|
*/
|
||||||
|
public enum HorizontalQuality
|
||||||
|
{
|
||||||
|
/** 1.0 AKA Linear */
|
||||||
|
LOWEST(1.0f),
|
||||||
|
|
||||||
|
/** exponent 1.5 */
|
||||||
|
LOW(1.5f),
|
||||||
|
|
||||||
|
/** exponent 2.0 */
|
||||||
|
MEDIUM(2.0f),
|
||||||
|
|
||||||
|
/** exponent 2.2 */
|
||||||
|
HIGH(2.2f);
|
||||||
|
|
||||||
|
public final double quadraticBase;
|
||||||
|
|
||||||
|
HorizontalQuality(double distanceUnit)
|
||||||
|
{
|
||||||
|
this.quadraticBase = distanceUnit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* chunk <Br>
|
||||||
|
* half_chunk <Br>
|
||||||
|
* four_blocks <br>
|
||||||
|
* two_blocks <Br>
|
||||||
|
* block <br>
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 9-25-2021
|
||||||
|
*/
|
||||||
|
public enum HorizontalResolution
|
||||||
|
{
|
||||||
|
/** render 256 LODs for each chunk */
|
||||||
|
BLOCK(16, 0),
|
||||||
|
|
||||||
|
/** render 64 LODs for each chunk */
|
||||||
|
TWO_BLOCKS(8, 1),
|
||||||
|
|
||||||
|
/** render 16 LODs for each chunk */
|
||||||
|
FOUR_BLOCKS(4, 2),
|
||||||
|
|
||||||
|
/** render 4 LODs for each chunk */
|
||||||
|
HALF_CHUNK(2, 3),
|
||||||
|
|
||||||
|
/** render 1 LOD for each chunk */
|
||||||
|
CHUNK(1, 4);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many DataPoints should
|
||||||
|
* be drawn per side, per LodChunk
|
||||||
|
*/
|
||||||
|
public final int dataPointLengthCount;
|
||||||
|
|
||||||
|
/** How wide each LOD DataPoint is */
|
||||||
|
public final int dataPointWidth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the same as detailLevel in LodQuadTreeNode,
|
||||||
|
* lowest is 0 highest is 9
|
||||||
|
*/
|
||||||
|
public final byte detailLevel;
|
||||||
|
|
||||||
|
/* Start/End X/Z give the block positions
|
||||||
|
* for each individual dataPoint in a LodChunk */
|
||||||
|
public final int[] startX;
|
||||||
|
public final int[] startZ;
|
||||||
|
|
||||||
|
public final int[] endX;
|
||||||
|
public final int[] endZ;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1st dimension: LodDetail.detailLevel <br>
|
||||||
|
* 2nd dimension: An array of all LodDetails that are less than or <br>
|
||||||
|
* equal to that detailLevel
|
||||||
|
*/
|
||||||
|
private static HorizontalResolution[][] lowerDetailArrays;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
HorizontalResolution(int newLengthCount, int newDetailLevel)
|
||||||
|
{
|
||||||
|
detailLevel = (byte) newDetailLevel;
|
||||||
|
dataPointLengthCount = newLengthCount;
|
||||||
|
dataPointWidth = 16 / dataPointLengthCount;
|
||||||
|
|
||||||
|
startX = new int[dataPointLengthCount * dataPointLengthCount];
|
||||||
|
endX = new int[dataPointLengthCount * dataPointLengthCount];
|
||||||
|
|
||||||
|
startZ = new int[dataPointLengthCount * dataPointLengthCount];
|
||||||
|
endZ = new int[dataPointLengthCount * dataPointLengthCount];
|
||||||
|
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (int x = 0; x < newLengthCount; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < newLengthCount; z++)
|
||||||
|
{
|
||||||
|
startX[index] = x * dataPointWidth;
|
||||||
|
startZ[index] = z * dataPointWidth;
|
||||||
|
|
||||||
|
endX[index] = (x * dataPointWidth) + dataPointWidth;
|
||||||
|
endZ[index] = (z * dataPointWidth) + dataPointWidth;
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}// constructor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of all LodDetails that have a detail level
|
||||||
|
* that is less than or equal to the given LodDetail
|
||||||
|
*/
|
||||||
|
public static HorizontalResolution[] getSelfAndLowerDetails(HorizontalResolution detail)
|
||||||
|
{
|
||||||
|
if (lowerDetailArrays == null)
|
||||||
|
{
|
||||||
|
// run first time setup
|
||||||
|
lowerDetailArrays = new HorizontalResolution[HorizontalResolution.values().length][];
|
||||||
|
|
||||||
|
// go through each LodDetail
|
||||||
|
for (HorizontalResolution currentDetail : HorizontalResolution.values())
|
||||||
|
{
|
||||||
|
ArrayList<HorizontalResolution> lowerDetails = new ArrayList<>();
|
||||||
|
|
||||||
|
// find the details lower than currentDetail
|
||||||
|
for (HorizontalResolution compareDetail : HorizontalResolution.values())
|
||||||
|
{
|
||||||
|
if (currentDetail.detailLevel <= compareDetail.detailLevel)
|
||||||
|
{
|
||||||
|
lowerDetails.add(compareDetail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// have the highest detail item first in the list
|
||||||
|
Collections.sort(lowerDetails);
|
||||||
|
Collections.reverse(lowerDetails);
|
||||||
|
|
||||||
|
lowerDetailArrays[currentDetail.detailLevel] = lowerDetails.toArray(new HorizontalResolution[lowerDetails.size()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lowerDetailArrays[detail.detailLevel];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns what detail level should be used at a given distance and maxDistance. */
|
||||||
|
public static HorizontalResolution getDetailForDistance(HorizontalResolution maxDetailLevel, int distance, int maxDistance)
|
||||||
|
{
|
||||||
|
HorizontalResolution[] lowerDetails = getSelfAndLowerDetails(maxDetailLevel);
|
||||||
|
int distanceBetweenDetails = maxDistance / lowerDetails.length;
|
||||||
|
int index = LodUtil.clamp(0, distance / distanceBetweenDetails, lowerDetails.length - 1);
|
||||||
|
|
||||||
|
return lowerDetails[index];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Low <br>
|
||||||
|
* Medium <br>
|
||||||
|
* High <br>
|
||||||
|
* <br>
|
||||||
|
* this is a quality scale for the detail drop-off
|
||||||
|
*
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 9-25-2021
|
||||||
|
*/
|
||||||
|
public enum HorizontalScale
|
||||||
|
{
|
||||||
|
/** Lods are 2D with heightMap */
|
||||||
|
LOW(64),
|
||||||
|
|
||||||
|
/** Lods expand in three dimension */
|
||||||
|
MEDIUM(128),
|
||||||
|
|
||||||
|
/** Lods expand in three dimension */
|
||||||
|
HIGH(256);
|
||||||
|
|
||||||
|
public final int distanceUnit;
|
||||||
|
|
||||||
|
HorizontalScale(int distanceUnit)
|
||||||
|
{
|
||||||
|
this.distanceUnit = distanceUnit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.AbstractLodTemplate;
|
||||||
|
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.CubicLodTemplate;
|
||||||
|
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.DynamicLodTemplate;
|
||||||
|
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.TriangularLodTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cubic, Triangular, Dynamic
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 10-10-2021
|
||||||
|
*/
|
||||||
|
public enum LodTemplate
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* LODs are rendered as
|
||||||
|
* rectangular prisms.
|
||||||
|
*/
|
||||||
|
CUBIC(new CubicLodTemplate()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LODs smoothly transition between
|
||||||
|
* each other.
|
||||||
|
*/
|
||||||
|
TRIANGULAR(new TriangularLodTemplate()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LODs smoothly transition between
|
||||||
|
* each other, unless a neighboring LOD
|
||||||
|
* is at a significantly different height.
|
||||||
|
*/
|
||||||
|
DYNAMIC(new DynamicLodTemplate());
|
||||||
|
|
||||||
|
|
||||||
|
public final AbstractLodTemplate template;
|
||||||
|
|
||||||
|
LodTemplate(AbstractLodTemplate newTemplate)
|
||||||
|
{
|
||||||
|
template = newTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NONE, GAME_SHADING
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 7-25-2020
|
||||||
|
*/
|
||||||
|
public enum ShadingMode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* LODs will have darker sides and bottoms to simulate
|
||||||
|
* Minecraft's fast lighting.
|
||||||
|
*/
|
||||||
|
GAME_SHADING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LODs will use ambient occlusion to mimic Minecarft's
|
||||||
|
* Fancy lighting.
|
||||||
|
*/
|
||||||
|
AMBIENT_OCCLUSION
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* None, Dynamic, Always
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This represents how far the LODs should overlap with
|
||||||
|
* the vanilla Minecraft terrain.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 10-11-2021
|
||||||
|
*/
|
||||||
|
public enum VanillaOverdraw
|
||||||
|
{
|
||||||
|
/** Never draw LODs where a minecraft chunk could be. */
|
||||||
|
NEVER,
|
||||||
|
|
||||||
|
/** Draw LODs over the farther minecraft chunks. */
|
||||||
|
DYNAMIC,
|
||||||
|
|
||||||
|
/** Draw LODs over all minecraft chunks. */
|
||||||
|
ALWAYS,
|
||||||
|
|
||||||
|
/** Draw LODs over border chunks. */
|
||||||
|
BORDER,
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* heightmap <br>
|
||||||
|
* multi_lod <br>
|
||||||
|
*
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 10-07-2021
|
||||||
|
*/
|
||||||
|
public enum VerticalQuality
|
||||||
|
{
|
||||||
|
LOW(
|
||||||
|
new int[] { 2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1 }
|
||||||
|
),
|
||||||
|
|
||||||
|
MEDIUM(
|
||||||
|
new int[] { 4,
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1 }
|
||||||
|
),
|
||||||
|
|
||||||
|
HIGH(
|
||||||
|
new int[] {
|
||||||
|
8,
|
||||||
|
8,
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1 }
|
||||||
|
);
|
||||||
|
|
||||||
|
public final int[] maxVerticalData;
|
||||||
|
|
||||||
|
VerticalQuality(int[] maxVerticalData)
|
||||||
|
{
|
||||||
|
this.maxVerticalData = maxVerticalData;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.rendering;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* off, detail, detail wireframe
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 8-28-2021
|
||||||
|
*/
|
||||||
|
public enum DebugMode
|
||||||
|
{
|
||||||
|
/** LODs are rendered normally */
|
||||||
|
OFF,
|
||||||
|
|
||||||
|
/** LOD colors are based on their detail */
|
||||||
|
SHOW_DETAIL,
|
||||||
|
|
||||||
|
/** LOD colors are based on their detail, and draws in wireframe. */
|
||||||
|
SHOW_DETAIL_WIREFRAME;
|
||||||
|
|
||||||
|
/** used when cycling through the different modes */
|
||||||
|
private DebugMode next;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
OFF.next = SHOW_DETAIL;
|
||||||
|
SHOW_DETAIL.next = SHOW_DETAIL_WIREFRAME;
|
||||||
|
SHOW_DETAIL_WIREFRAME.next = OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** returns the next debug mode */
|
||||||
|
public DebugMode getNext()
|
||||||
|
{
|
||||||
|
return this.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.rendering;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* USE_DEFAULT_FOG_COLOR, <br>
|
||||||
|
* USE_SKY_COLOR, <br>
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-27-2021
|
||||||
|
*/
|
||||||
|
public enum FogColorMode
|
||||||
|
{
|
||||||
|
/** Fog uses Minecraft's fog color. */
|
||||||
|
USE_WORLD_FOG_COLOR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replicates the effect of the clear sky mod.
|
||||||
|
* Making the fog blend in with the sky better
|
||||||
|
* https://www.curseforge.com/minecraft/mc-mods/clear-skies
|
||||||
|
* https://www.curseforge.com/minecraft/mc-mods/clear-skies-forge-port
|
||||||
|
* For it to look good you need one of those mods
|
||||||
|
*/
|
||||||
|
USE_SKY_COLOR,
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.rendering;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NEAR, FAR, or NEAR_AND_FAR.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-26-2021
|
||||||
|
*/
|
||||||
|
public enum FogDistance
|
||||||
|
{
|
||||||
|
NEAR,
|
||||||
|
FAR,
|
||||||
|
NEAR_AND_FAR
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.rendering;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* USE_OPTIFINE_FOG_SETTING, <br>
|
||||||
|
* FOG_ENABLED, <br>
|
||||||
|
* FOG_DISABLED <br>
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-27-2021
|
||||||
|
*/
|
||||||
|
public enum FogDrawMode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Use whatever Fog setting optifine is using.
|
||||||
|
* If optifine isn't installed this defaults to ALWAYS_DRAW_FOG.
|
||||||
|
*/
|
||||||
|
USE_OPTIFINE_SETTING,
|
||||||
|
|
||||||
|
FOG_ENABLED,
|
||||||
|
FOG_DISABLED
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.enums.rendering;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minecraft, Lod_Builder, None
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 10-1-2021
|
||||||
|
*/
|
||||||
|
public enum GLProxyContext
|
||||||
|
{
|
||||||
|
/** Minecraft's render thread */
|
||||||
|
MINECRAFT,
|
||||||
|
|
||||||
|
/** The context we send buffers to the GPU on */
|
||||||
|
LOD_BUILDER,
|
||||||
|
|
||||||
|
/** A context that can be used for miscellaneous tasks, owned by the GLProxy */
|
||||||
|
PROXY_WORKER,
|
||||||
|
|
||||||
|
/** used to un-bind threads */
|
||||||
|
NONE,
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.handlers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Cola
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 11-12-2021
|
||||||
|
*/
|
||||||
|
public class ChunkFileLoader
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
// public static IChunk getChunkFromFile(ChunkPos pos)
|
||||||
|
// {
|
||||||
|
// LevelWrapper clientLevel = MinecraftWrapper.INSTANCE.getWrappedClientLevel();
|
||||||
|
// if (clientLevel == null)
|
||||||
|
// return null;
|
||||||
|
// WorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(clientLevel.getDimensionType());
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// File file = new File(serverWorld.getSaveFolder().getParent() + File.separatorChar + "region", "r." + (pos.x >> 5) + "." + (pos.z >> 5) + ".mca");
|
||||||
|
// if(!file.exists())
|
||||||
|
// return null;
|
||||||
|
// IChunk loadedChunk = ChunkSerializer.read(
|
||||||
|
// serverWorld,
|
||||||
|
// serverWorld.getStructureManager(),
|
||||||
|
// serverWorld.getPoiManager(),
|
||||||
|
// pos,
|
||||||
|
// serverWorld.getChunkSource().chunkMap.read(pos)
|
||||||
|
// );
|
||||||
|
// boolean emptyChunk = true;
|
||||||
|
// for(int i = 0; i < 16; i++){
|
||||||
|
// for(int j = 0; j < 16; j++){
|
||||||
|
// emptyChunk &= loadedChunk.isYSpaceEmpty(i,j);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if(emptyChunk)
|
||||||
|
// return null;
|
||||||
|
// else
|
||||||
|
// return loadedChunk;
|
||||||
|
// }
|
||||||
|
// catch (Exception e)
|
||||||
|
// {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.handlers;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.enums.rendering.FogDrawMode;
|
||||||
|
import com.seibel.lod.core.objects.math.Mat4f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A singleton used to get variables from methods
|
||||||
|
* where they are private or potentially absent.
|
||||||
|
* Specifically the fog setting used by Optifine or the
|
||||||
|
* presence/absence of other mods.
|
||||||
|
* <p>
|
||||||
|
* This interface doesn't necessarily have to exist, but
|
||||||
|
* it makes using the singleton handler more uniform (always
|
||||||
|
* passing in interfaces), and it may be needed in the future if
|
||||||
|
* we find that reflection handlers need to be different for
|
||||||
|
* different MC versions.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-26-2021
|
||||||
|
*/
|
||||||
|
public interface IReflectionHandler
|
||||||
|
{
|
||||||
|
/** @returns Whether Optifine is set to render fog or not. */
|
||||||
|
FogDrawMode getFogDrawMode();
|
||||||
|
|
||||||
|
/** @returns if Vivecraft is present. Attempts to find the "VRRenderer" class. */
|
||||||
|
boolean vivecraftPresent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the projection matrix's clip planes.
|
||||||
|
* The projection matrix must be in column-major format.
|
||||||
|
*
|
||||||
|
* @param projectionMatrix The projection matrix to be modified.
|
||||||
|
* @param newNearClipPlane the new near clip plane value.
|
||||||
|
* @param newFarClipPlane the new far clip plane value.
|
||||||
|
* @return The modified matrix.
|
||||||
|
*/
|
||||||
|
Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane);
|
||||||
|
}
|
||||||
@@ -0,0 +1,485 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.handlers;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
|
||||||
|
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.api.ClientApi;
|
||||||
|
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||||
|
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||||
|
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||||
|
import com.seibel.lod.core.objects.lod.LodRegion;
|
||||||
|
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||||
|
import com.seibel.lod.core.objects.lod.VerticalLevelContainer;
|
||||||
|
import com.seibel.lod.core.util.LodThreadFactory;
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object handles creating LodRegions
|
||||||
|
* from files and saving LodRegion objects
|
||||||
|
* to file.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @author Cola
|
||||||
|
* @version 9-25-2021
|
||||||
|
*/
|
||||||
|
public class LodDimensionFileHandler
|
||||||
|
{
|
||||||
|
/** This is the dimension that owns this file handler */
|
||||||
|
private LodDimension lodDimension;
|
||||||
|
|
||||||
|
private final File dimensionDataSaveFolder;
|
||||||
|
|
||||||
|
/** lod */
|
||||||
|
private static final String FILE_NAME_PREFIX = "lod";
|
||||||
|
/** .txt */
|
||||||
|
private static final String FILE_EXTENSION = ".xz";
|
||||||
|
/** detail- */
|
||||||
|
private static final String DETAIL_FOLDER_NAME_PREFIX = "detail-";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* .tmp <br>
|
||||||
|
* Added to the end of the file path when saving to prevent
|
||||||
|
* nulling a currently existing file. <br>
|
||||||
|
* After the file finishes saving it will end with
|
||||||
|
* FILE_EXTENSION.
|
||||||
|
*/
|
||||||
|
private static final String TMP_FILE_EXTENSION = ".tmp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the file version currently accepted by this
|
||||||
|
* file handler, older versions (smaller numbers) will be deleted and overwritten,
|
||||||
|
* newer versions (larger numbers) will be ignored and won't be read.
|
||||||
|
*/
|
||||||
|
public static final int LOD_SAVE_FILE_VERSION = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow saving asynchronously, but never try to save multiple regions
|
||||||
|
* at a time
|
||||||
|
*/
|
||||||
|
private final ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLodDimension)
|
||||||
|
{
|
||||||
|
if (newSaveFolder == null)
|
||||||
|
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
|
||||||
|
|
||||||
|
dimensionDataSaveFolder = newSaveFolder;
|
||||||
|
lodDimension = newLodDimension;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// read from file //
|
||||||
|
//================//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the LodRegion at the given coordinates.
|
||||||
|
* Returns an empty region if the file doesn't exist.
|
||||||
|
*/
|
||||||
|
public LodRegion loadRegionFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
|
||||||
|
{
|
||||||
|
int regionX = regionPos.x;
|
||||||
|
int regionZ = regionPos.z;
|
||||||
|
LodRegion region = new LodRegion(LodUtil.REGION_DETAIL_LEVEL, regionPos, generationMode, verticalQuality);
|
||||||
|
|
||||||
|
for (byte tempDetailLevel = LodUtil.REGION_DETAIL_LEVEL; tempDetailLevel >= detailLevel; tempDetailLevel--)
|
||||||
|
{
|
||||||
|
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, tempDetailLevel, verticalQuality);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// if the fileName was null that means the folder is inaccessible
|
||||||
|
// for some reason
|
||||||
|
if (fileName == null)
|
||||||
|
throw new IllegalArgumentException("Unable to read region [" + regionX + ", " + regionZ + "] file, no fileName.");
|
||||||
|
|
||||||
|
File file = new File(fileName);
|
||||||
|
if (!file.exists())
|
||||||
|
{
|
||||||
|
//there is no file for current gen mode
|
||||||
|
//search others above current from the most to the least detailed
|
||||||
|
VerticalQuality tempVerticalQuality = VerticalQuality.HIGH;
|
||||||
|
do {
|
||||||
|
DistanceGenerationMode tempGenMode = DistanceGenerationMode.FULL;
|
||||||
|
do {
|
||||||
|
fileName = getFileNameAndPathForRegion(regionX, regionZ, tempGenMode, tempDetailLevel, verticalQuality);
|
||||||
|
if (fileName != null)
|
||||||
|
{
|
||||||
|
file = new File(fileName);
|
||||||
|
if (file.exists())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//decrease gen mode
|
||||||
|
if (tempGenMode == DistanceGenerationMode.FULL)
|
||||||
|
tempGenMode = DistanceGenerationMode.FEATURES;
|
||||||
|
else if (tempGenMode == DistanceGenerationMode.FEATURES)
|
||||||
|
tempGenMode = DistanceGenerationMode.SURFACE;
|
||||||
|
else if (tempGenMode == DistanceGenerationMode.SURFACE)
|
||||||
|
tempGenMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
|
||||||
|
else if (tempGenMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT)
|
||||||
|
tempGenMode = DistanceGenerationMode.BIOME_ONLY;
|
||||||
|
else if (tempGenMode == DistanceGenerationMode.BIOME_ONLY)
|
||||||
|
tempGenMode = DistanceGenerationMode.NONE;
|
||||||
|
} while (tempGenMode != generationMode);
|
||||||
|
if (fileName != null)
|
||||||
|
{
|
||||||
|
file = new File(fileName);
|
||||||
|
if (file.exists())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (tempVerticalQuality == VerticalQuality.HIGH)
|
||||||
|
tempVerticalQuality = VerticalQuality.MEDIUM;
|
||||||
|
else if (tempVerticalQuality == VerticalQuality.MEDIUM)
|
||||||
|
tempVerticalQuality = VerticalQuality.LOW;
|
||||||
|
} while (tempVerticalQuality != verticalQuality);
|
||||||
|
if (!file.exists())
|
||||||
|
//there wasn't a file, don't return anything
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// don't try parsing empty files
|
||||||
|
long dataSize = file.length();
|
||||||
|
dataSize -= 1;
|
||||||
|
if (dataSize > 0)
|
||||||
|
{
|
||||||
|
try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(file)))
|
||||||
|
{
|
||||||
|
int fileVersion;
|
||||||
|
fileVersion = inputStream.read();
|
||||||
|
|
||||||
|
// check if this file can be read by this file handler
|
||||||
|
if (fileVersion < 6)
|
||||||
|
{
|
||||||
|
// the file we are reading is an older version,
|
||||||
|
// close the reader and delete the file.
|
||||||
|
inputStream.close();
|
||||||
|
file.delete();
|
||||||
|
ClientApi.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ")"
|
||||||
|
+ " version found: " + fileVersion
|
||||||
|
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||||
|
+ ". File was been deleted.");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (fileVersion > LOD_SAVE_FILE_VERSION)
|
||||||
|
{
|
||||||
|
// the file we are reading is a newer version,
|
||||||
|
// close the reader and ignore the file, we don't
|
||||||
|
// want to accidentally delete anything the user may want.
|
||||||
|
inputStream.close();
|
||||||
|
ClientApi.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ")"
|
||||||
|
+ " version found: " + fileVersion
|
||||||
|
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||||
|
+ " this region will not be written to in order to protect the newer file.");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (fileVersion == 6)
|
||||||
|
{
|
||||||
|
//this is old, but readable version
|
||||||
|
byte[] data = ThreadMapUtil.getSaveContainer(tempDetailLevel);
|
||||||
|
inputStream.read(data);
|
||||||
|
inputStream.close();
|
||||||
|
// add the data to our region
|
||||||
|
region.addLevelContainer(new VerticalLevelContainer(data, 6));
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// this file is a readable version,
|
||||||
|
// read the file
|
||||||
|
byte[] data = ThreadMapUtil.getSaveContainer(tempDetailLevel);
|
||||||
|
inputStream.read(data);
|
||||||
|
inputStream.close();
|
||||||
|
// add the data to our region
|
||||||
|
region.addLevelContainer(new VerticalLevelContainer(data, LOD_SAVE_FILE_VERSION));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ioEx)
|
||||||
|
{
|
||||||
|
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: ");
|
||||||
|
ioEx.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// the buffered reader encountered a
|
||||||
|
// problem reading the file
|
||||||
|
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + e.getMessage() + "]: ");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}// for each detail level
|
||||||
|
|
||||||
|
if (region.getMinDetailLevel() >= detailLevel)
|
||||||
|
region.growTree(detailLevel);
|
||||||
|
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==============//
|
||||||
|
// Save to File //
|
||||||
|
//==============//
|
||||||
|
|
||||||
|
/** Save all dirty regions in this LodDimension to file */
|
||||||
|
public void saveDirtyRegionsToFileAsync()
|
||||||
|
{
|
||||||
|
fileWritingThreadPool.execute(saveDirtyRegionsThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Thread saveDirtyRegionsThread = new Thread(() ->
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < lodDimension.getWidth(); i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < lodDimension.getWidth(); j++)
|
||||||
|
{
|
||||||
|
if (lodDimension.GetIsRegionDirty(i, j) && lodDimension.getRegionByArrayIndex(i, j) != null)
|
||||||
|
{
|
||||||
|
saveRegionToFile(lodDimension.getRegionByArrayIndex(i, j));
|
||||||
|
lodDimension.SetIsRegionDirty(i, j, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a specific region to disk.<br>
|
||||||
|
* Note: <br>
|
||||||
|
* 1. If a file already exists for a newer version
|
||||||
|
* the file won't be written.<br>
|
||||||
|
* 2. This will save to the LodDimension that this
|
||||||
|
* handler is associated with.
|
||||||
|
*/
|
||||||
|
private void saveRegionToFile(LodRegion region)
|
||||||
|
{
|
||||||
|
for (byte detailLevel = region.getMinDetailLevel(); detailLevel <= LodUtil.REGION_DETAIL_LEVEL; detailLevel++)
|
||||||
|
{
|
||||||
|
String fileName = getFileNameAndPathForRegion(region.regionPosX, region.regionPosZ, region.getGenerationMode(), detailLevel, region.getVerticalQuality());
|
||||||
|
|
||||||
|
// if the fileName was null that means the folder is inaccessible
|
||||||
|
// for some reason
|
||||||
|
if (fileName == null)
|
||||||
|
{
|
||||||
|
ClientApi.LOGGER.warn("Unable to save region [" + region.regionPosX + ", " + region.regionPosZ + "] to file, file is inaccessible.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File oldFile = new File(fileName);
|
||||||
|
//ClientProxy.LOGGER.info("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] to file.");
|
||||||
|
byte[] temp = region.getLevel(detailLevel).toDataString();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// make sure the file and folder exists
|
||||||
|
if (!oldFile.exists())
|
||||||
|
{
|
||||||
|
// the file doesn't exist,
|
||||||
|
// create it and the folder if need be
|
||||||
|
if (!oldFile.getParentFile().exists())
|
||||||
|
oldFile.getParentFile().mkdirs();
|
||||||
|
oldFile.createNewFile();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// the file exists, make sure it
|
||||||
|
// is the correct version.
|
||||||
|
// (to make sure we don't overwrite a newer
|
||||||
|
// version file if it exists)
|
||||||
|
int fileVersion = LOD_SAVE_FILE_VERSION;
|
||||||
|
int isFull = 0;
|
||||||
|
try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(oldFile)))
|
||||||
|
{
|
||||||
|
fileVersion = inputStream.read();
|
||||||
|
inputStream.skip(1);
|
||||||
|
isFull = inputStream.read() & 0b10000000;
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this file can be written to by the file handler
|
||||||
|
if (fileVersion > LOD_SAVE_FILE_VERSION)
|
||||||
|
{
|
||||||
|
// the file we are reading is a newer version,
|
||||||
|
// don't write anything, we don't want to accidentally
|
||||||
|
// delete anything the user may want.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((temp[1] & 0b10000000) != 0b10000000 && isFull == 0b10000000)
|
||||||
|
{
|
||||||
|
// existing file is complete while new one is only partially generate
|
||||||
|
// this can happen is for some reason loading failed
|
||||||
|
// this doesn't fix the bug, but at least protects old data
|
||||||
|
ClientApi.LOGGER.error("LOD file write error. Attempted to overwrite complete region with incomplete one [" + fileName + "]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// if we got this far then we are good
|
||||||
|
// to overwrite the old file
|
||||||
|
}
|
||||||
|
// the old file is good, now create a new temporary save file
|
||||||
|
File newFile = new File(fileName + TMP_FILE_EXTENSION);
|
||||||
|
try (XZCompressorOutputStream outputStream = new XZCompressorOutputStream(new FileOutputStream(newFile), 3))
|
||||||
|
{
|
||||||
|
// add the version of this file
|
||||||
|
outputStream.write(LOD_SAVE_FILE_VERSION);
|
||||||
|
|
||||||
|
// add each LodChunk to the file
|
||||||
|
outputStream.write(temp);
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
// overwrite the old file with the new one
|
||||||
|
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ClientApi.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + e.getMessage() + "]: ");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void saveRegionFile (byte[] regionFile, RegionPos regionPos, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality)
|
||||||
|
{
|
||||||
|
int regionX = regionPos.x;
|
||||||
|
int regionZ = regionPos.z;
|
||||||
|
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality);
|
||||||
|
|
||||||
|
if (fileName != null)
|
||||||
|
{
|
||||||
|
File oldFile = new File(fileName);
|
||||||
|
File newFile = new File(fileName + TMP_FILE_EXTENSION);
|
||||||
|
try (OutputStream os = new FileOutputStream(newFile))
|
||||||
|
{
|
||||||
|
os.write(regionFile);
|
||||||
|
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
catch (IOException ioEx)
|
||||||
|
{
|
||||||
|
ClientApi.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + ioEx.getMessage() + "]: ");
|
||||||
|
ioEx.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getRegionFile (RegionPos regionPos, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality)
|
||||||
|
{
|
||||||
|
int regionX = regionPos.x;
|
||||||
|
int regionZ = regionPos.z;
|
||||||
|
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality);
|
||||||
|
if (fileName != null)
|
||||||
|
{
|
||||||
|
File file = new File(fileName);
|
||||||
|
try (InputStream is = new FileInputStream(file))
|
||||||
|
{
|
||||||
|
byte[] data = ThreadMapUtil.getSaveContainer(detailLevel);
|
||||||
|
is.read(data);
|
||||||
|
is.close();
|
||||||
|
return Arrays.copyOf(data, (int) file.length());
|
||||||
|
}
|
||||||
|
catch (IOException ioEx)
|
||||||
|
{
|
||||||
|
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: ");
|
||||||
|
ioEx.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// helper methods //
|
||||||
|
//================//
|
||||||
|
|
||||||
|
public int getHashFromFile(RegionPos regionPos, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality)
|
||||||
|
{
|
||||||
|
int regionX = regionPos.x;
|
||||||
|
int regionZ = regionPos.z;
|
||||||
|
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality);
|
||||||
|
if (fileName == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
File file = new File(fileName);
|
||||||
|
return file.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the file that should contain the
|
||||||
|
* region at the given x and z. <br>
|
||||||
|
* Returns null if this object isn't available to read and write. <br><br>
|
||||||
|
* <p>
|
||||||
|
* example: "lod.0.0.txt" <br>
|
||||||
|
* <p>
|
||||||
|
* Returns null if there is an IO or security Exception.
|
||||||
|
*/
|
||||||
|
private String getFileNameAndPathForRegion(int regionX, int regionZ, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// saveFolder is something like
|
||||||
|
// ".\Super Flat\DIM-1\data\"
|
||||||
|
// or
|
||||||
|
// ".\Super Flat\data\"
|
||||||
|
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
|
||||||
|
verticalQuality + File.separatorChar +
|
||||||
|
generationMode.toString() + File.separatorChar +
|
||||||
|
DETAIL_FOLDER_NAME_PREFIX + detailLevel + File.separatorChar +
|
||||||
|
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
|
||||||
|
}
|
||||||
|
catch (IOException | SecurityException e)
|
||||||
|
{
|
||||||
|
ClientApi.LOGGER.warn("Unable to get the filename for the region [" + regionX + ", " + regionZ + "], error: [" + e.getMessage() + "], stacktrace: ");
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.handlers;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.ModInfo;
|
||||||
|
import com.seibel.lod.core.enums.rendering.FogDrawMode;
|
||||||
|
import com.seibel.lod.core.objects.math.Mat4f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A singleton used to get variables from methods
|
||||||
|
* where they are private or potentially absent.
|
||||||
|
* Specifically the fog setting in Optifine or the
|
||||||
|
* presence/absence of other mods.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-26-2021
|
||||||
|
*/
|
||||||
|
public class ReflectionHandler implements IReflectionHandler
|
||||||
|
{
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME + "-" + ReflectionHandler.class.getSimpleName());
|
||||||
|
|
||||||
|
private static ReflectionHandler instance;
|
||||||
|
|
||||||
|
private Field ofFogField = null;
|
||||||
|
private final Object mcOptionsObject;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private ReflectionHandler(Field[] optionFields, Object newMcOptionsObject)
|
||||||
|
{
|
||||||
|
mcOptionsObject = newMcOptionsObject;
|
||||||
|
|
||||||
|
setupFogField(optionFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param optionFields the fields that should contain "ofFogType"
|
||||||
|
* @param newMcOptionsObject the object instance that contains "ofFogType"
|
||||||
|
* @return the ReflectionHandler just created
|
||||||
|
* @throws IllegalStateException if a ReflectionHandler already exists
|
||||||
|
*/
|
||||||
|
public static ReflectionHandler createSingleton(Field[] optionFields, Object newMcOptionsObject) throws IllegalStateException
|
||||||
|
{
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = new ReflectionHandler(optionFields, newMcOptionsObject);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** finds the Optifine fog type field */
|
||||||
|
private void setupFogField(Field[] optionFields)
|
||||||
|
{
|
||||||
|
// try and find the ofFogType variable in gameSettings
|
||||||
|
for (Field field : optionFields)
|
||||||
|
{
|
||||||
|
if (field.getName().equals("ofFogType"))
|
||||||
|
{
|
||||||
|
ofFogField = field;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we didn't find the field,
|
||||||
|
// either optifine isn't installed, or
|
||||||
|
// optifine changed the name of the variable
|
||||||
|
LOGGER.info(ReflectionHandler.class.getSimpleName() + ": unable to find the Optifine fog field. If Optifine isn't installed this can be ignored.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get what type of fog optifine is currently set to render.
|
||||||
|
* @return the fog quality
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public FogDrawMode getFogDrawMode()
|
||||||
|
{
|
||||||
|
if (ofFogField == null)
|
||||||
|
{
|
||||||
|
// either optifine isn't installed,
|
||||||
|
// the variable name was changed, or
|
||||||
|
// the setup method wasn't called yet.
|
||||||
|
return FogDrawMode.FOG_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
int returnNum = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
returnNum = (int) ofFogField.get(mcOptionsObject);
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException | IllegalAccessException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (returnNum)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case 0:
|
||||||
|
// optifine's "default" option,
|
||||||
|
// it should never be called in this case
|
||||||
|
|
||||||
|
// normal options
|
||||||
|
case 1: // fast
|
||||||
|
case 2: // fancy
|
||||||
|
return FogDrawMode.FOG_ENABLED;
|
||||||
|
case 3: // off
|
||||||
|
return FogDrawMode.FOG_DISABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Detect if Vivecraft is present. Attempts to find the "VRRenderer" class. */
|
||||||
|
@Override
|
||||||
|
public boolean vivecraftPresent()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Class.forName("org.vivecraft.provider.VRRenderer");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ignored)
|
||||||
|
{
|
||||||
|
LOGGER.info(ReflectionHandler.class.getSimpleName() + ": Vivecraft not detected.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the projection matrix's clip planes.
|
||||||
|
* The projection matrix must be in column-major format.
|
||||||
|
*
|
||||||
|
* @param projectionMatrix The projection matrix to be modified.
|
||||||
|
* @param newNearClipPlane the new near clip plane value.
|
||||||
|
* @param newFarClipPlane the new far clip plane value.
|
||||||
|
* @return The modified matrix.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane)
|
||||||
|
{
|
||||||
|
// find the matrix values.
|
||||||
|
float nearMatrixValue = -((newFarClipPlane + newNearClipPlane) / (newFarClipPlane - newNearClipPlane));
|
||||||
|
float farMatrixValue = -((2 * newFarClipPlane * newNearClipPlane) / (newFarClipPlane - newNearClipPlane));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// TODO this was originally created before we had the Mat4f object,
|
||||||
|
// so this doesn't need to be done with reflection anymore.
|
||||||
|
// And should be moved to RenderUtil
|
||||||
|
|
||||||
|
// get the fields of the projectionMatrix
|
||||||
|
Field[] fields = projectionMatrix.getClass().getDeclaredFields();
|
||||||
|
// bypass the security protections on the fields that encode near and far plane values.
|
||||||
|
fields[10].setAccessible(true);
|
||||||
|
fields[11].setAccessible(true);
|
||||||
|
// Change the values of the near and far plane.
|
||||||
|
fields[10].set(projectionMatrix, nearMatrixValue);
|
||||||
|
fields[11].set(projectionMatrix, farMatrixValue);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return projectionMatrix;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used when setting up configuration fields.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-14-2021
|
||||||
|
* @param <T> The data type this object is storing
|
||||||
|
*/
|
||||||
|
public class MinDefaultMax<T>
|
||||||
|
{
|
||||||
|
public final T minValue;
|
||||||
|
public final T defaultValue;
|
||||||
|
public final T maxValue;
|
||||||
|
|
||||||
|
public MinDefaultMax(T newMinValue, T newDefaultValue, T newMaxValue)
|
||||||
|
{
|
||||||
|
minValue = newMinValue;
|
||||||
|
defaultValue = newDefaultValue;
|
||||||
|
maxValue = newMaxValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.util.LevelPosUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the levelPos that need to be generated.
|
||||||
|
*
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 9-27-2021
|
||||||
|
*/
|
||||||
|
public class PosToGenerateContainer
|
||||||
|
{
|
||||||
|
private final int playerPosX;
|
||||||
|
private final int playerPosZ;
|
||||||
|
private final byte farMinDetail;
|
||||||
|
private int nearSize;
|
||||||
|
private int farSize;
|
||||||
|
|
||||||
|
// TODO what is the format of these two arrays? [detailLevel][4-children]?
|
||||||
|
private final int[][] nearPosToGenerate;
|
||||||
|
private final int[][] farPosToGenerate;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public PosToGenerateContainer(byte farMinDetail, int maxDataToGenerate, int playerPosX, int playerPosZ)
|
||||||
|
{
|
||||||
|
this.playerPosX = playerPosX;
|
||||||
|
this.playerPosZ = playerPosZ;
|
||||||
|
this.farMinDetail = farMinDetail;
|
||||||
|
nearSize = 0;
|
||||||
|
farSize = 0;
|
||||||
|
nearPosToGenerate = new int[maxDataToGenerate][4];
|
||||||
|
farPosToGenerate = new int[maxDataToGenerate][4];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO what is going on in this method?
|
||||||
|
public void addPosToGenerate(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
int distance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ);
|
||||||
|
int index;
|
||||||
|
|
||||||
|
if (detailLevel >= farMinDetail)
|
||||||
|
{
|
||||||
|
// We are introducing a position in the far array
|
||||||
|
|
||||||
|
if (farSize < farPosToGenerate.length)
|
||||||
|
farSize++;
|
||||||
|
|
||||||
|
index = farSize - 1;
|
||||||
|
while (index > 0 && LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) <= 0)
|
||||||
|
{
|
||||||
|
farPosToGenerate[index][0] = farPosToGenerate[index - 1][0];
|
||||||
|
farPosToGenerate[index][1] = farPosToGenerate[index - 1][1];
|
||||||
|
farPosToGenerate[index][2] = farPosToGenerate[index - 1][2];
|
||||||
|
farPosToGenerate[index][3] = farPosToGenerate[index - 1][3];
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (index != farSize - 1 || farSize != farPosToGenerate.length)
|
||||||
|
{
|
||||||
|
farPosToGenerate[index][0] = detailLevel + 1;
|
||||||
|
farPosToGenerate[index][1] = posX;
|
||||||
|
farPosToGenerate[index][2] = posZ;
|
||||||
|
farPosToGenerate[index][3] = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//We are introducing a position in the near array
|
||||||
|
|
||||||
|
if (nearSize < nearPosToGenerate.length)
|
||||||
|
nearSize++;
|
||||||
|
|
||||||
|
index = nearSize - 1;
|
||||||
|
while (index > 0 && LevelPosUtil.compareDistance(distance, nearPosToGenerate[index - 1][3]) <= 0)
|
||||||
|
{
|
||||||
|
nearPosToGenerate[index][0] = nearPosToGenerate[index - 1][0];
|
||||||
|
nearPosToGenerate[index][1] = nearPosToGenerate[index - 1][1];
|
||||||
|
nearPosToGenerate[index][2] = nearPosToGenerate[index - 1][2];
|
||||||
|
nearPosToGenerate[index][3] = nearPosToGenerate[index - 1][3];
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (index != nearSize - 1 || nearSize != nearPosToGenerate.length)
|
||||||
|
{
|
||||||
|
nearPosToGenerate[index][0] = detailLevel + 1;
|
||||||
|
nearPosToGenerate[index][1] = posX;
|
||||||
|
nearPosToGenerate[index][2] = posZ;
|
||||||
|
nearPosToGenerate[index][3] = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public int getNumberOfPos()
|
||||||
|
{
|
||||||
|
return nearSize + farSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumberOfNearPos()
|
||||||
|
{
|
||||||
|
return nearSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumberOfFarPos()
|
||||||
|
{
|
||||||
|
return farSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO what does getNth mean? could the name be more descriptive or is it just a index?
|
||||||
|
public int getNthDetail(int n, boolean near)
|
||||||
|
{
|
||||||
|
if (near)
|
||||||
|
return nearPosToGenerate[n][0];
|
||||||
|
else
|
||||||
|
return farPosToGenerate[n][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNthPosX(int n, boolean near)
|
||||||
|
{
|
||||||
|
if (near)
|
||||||
|
return nearPosToGenerate[n][1];
|
||||||
|
else
|
||||||
|
return farPosToGenerate[n][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNthPosZ(int n, boolean near)
|
||||||
|
{
|
||||||
|
if (near)
|
||||||
|
return nearPosToGenerate[n][2];
|
||||||
|
else
|
||||||
|
return farPosToGenerate[n][2];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNthGeneration(int n, boolean near)
|
||||||
|
{
|
||||||
|
if (near)
|
||||||
|
return nearPosToGenerate[n][3];
|
||||||
|
else
|
||||||
|
return farPosToGenerate[n][3];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append('\n');
|
||||||
|
builder.append('\n');
|
||||||
|
builder.append('\n');
|
||||||
|
builder.append("near pos to generate");
|
||||||
|
builder.append('\n');
|
||||||
|
for (int[] ints : nearPosToGenerate)
|
||||||
|
{
|
||||||
|
if (ints[0] == 0)
|
||||||
|
break;
|
||||||
|
builder.append(ints[0] - 1);
|
||||||
|
builder.append(" ");
|
||||||
|
builder.append(ints[1]);
|
||||||
|
builder.append(" ");
|
||||||
|
builder.append(ints[2]);
|
||||||
|
builder.append(" ");
|
||||||
|
builder.append(ints[3]);
|
||||||
|
builder.append('\n');
|
||||||
|
}
|
||||||
|
builder.append('\n');
|
||||||
|
|
||||||
|
builder.append("far pos to generate");
|
||||||
|
builder.append('\n');
|
||||||
|
for (int[] ints : farPosToGenerate)
|
||||||
|
{
|
||||||
|
if (ints[0] == 0)
|
||||||
|
break;
|
||||||
|
builder.append(ints[0] - 1);
|
||||||
|
builder.append(" ");
|
||||||
|
builder.append(ints[1]);
|
||||||
|
builder.append(" ");
|
||||||
|
builder.append(ints[2]);
|
||||||
|
builder.append(" ");
|
||||||
|
builder.append(ints[3]);
|
||||||
|
builder.append('\n');
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.api.ClientApi;
|
||||||
|
import com.seibel.lod.core.util.LevelPosUtil;
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds a levelPos that needs to be rendered.
|
||||||
|
*
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 9-18-2021
|
||||||
|
*/
|
||||||
|
public class PosToRenderContainer
|
||||||
|
{
|
||||||
|
public byte minDetail;
|
||||||
|
private int regionPosX;
|
||||||
|
private int regionPosZ;
|
||||||
|
private int numberOfPosToRender;
|
||||||
|
private int[] posToRender;
|
||||||
|
private byte[][] population;
|
||||||
|
|
||||||
|
public PosToRenderContainer(byte minDetail, int regionPosX, int regionPosZ)
|
||||||
|
{
|
||||||
|
this.minDetail = minDetail;
|
||||||
|
this.numberOfPosToRender = 0;
|
||||||
|
this.regionPosX = regionPosX;
|
||||||
|
this.regionPosZ = regionPosZ;
|
||||||
|
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
|
||||||
|
posToRender = new int[size * size * 3];
|
||||||
|
population = new byte[size][size];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPosToRender(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
// When rapidly changing dimensions the bufferBuilder can cause this,
|
||||||
|
// James isn't sure why, but this will prevent an exception at
|
||||||
|
// the very least (while stilling logging the problem).
|
||||||
|
if (numberOfPosToRender >= posToRender.length)
|
||||||
|
{
|
||||||
|
// This is might be due to dimensions having a different width
|
||||||
|
// when first loading in
|
||||||
|
ClientApi.LOGGER.error("Unable to addPosToRender. numberOfPosToRender [" + numberOfPosToRender + "] detailLevel [" + detailLevel + "] Pos [" + posX + "," + posZ + "]");
|
||||||
|
numberOfPosToRender++; // incrementing so we can see how many pos over the limit we would go
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if(numberOfPosToRender >= posToRender.length)
|
||||||
|
// posToRender = Arrays.copyOf(posToRender, posToRender.length*2);
|
||||||
|
posToRender[numberOfPosToRender * 3] = detailLevel;
|
||||||
|
posToRender[numberOfPosToRender * 3 + 1] = posX;
|
||||||
|
posToRender[numberOfPosToRender * 3 + 2] = posZ;
|
||||||
|
numberOfPosToRender++;
|
||||||
|
population[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel, posX, minDetail))]
|
||||||
|
[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel, posZ, minDetail))] = (byte) (detailLevel + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
if (LevelPosUtil.getRegion(detailLevel, posX) == regionPosX && LevelPosUtil.getRegion(detailLevel, posZ) == regionPosZ)
|
||||||
|
return (population[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel, posX, minDetail))]
|
||||||
|
[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel, posZ, minDetail))] == (detailLevel + 1));
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear(byte minDetail, int regionPosX, int regionPosZ)
|
||||||
|
{
|
||||||
|
this.numberOfPosToRender = 0;
|
||||||
|
this.regionPosX = regionPosX;
|
||||||
|
this.regionPosZ = regionPosZ;
|
||||||
|
if (this.minDetail == minDetail)
|
||||||
|
{
|
||||||
|
Arrays.fill(posToRender, 0);
|
||||||
|
for (byte[] bytes : population)
|
||||||
|
Arrays.fill(bytes, (byte) 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.minDetail = minDetail;
|
||||||
|
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
|
||||||
|
posToRender = new int[size * size * 3];
|
||||||
|
population = new byte[size][size];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumberOfPos()
|
||||||
|
{
|
||||||
|
return numberOfPosToRender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getNthDetailLevel(int n)
|
||||||
|
{
|
||||||
|
return (byte) posToRender[n * 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNthPosX(int n)
|
||||||
|
{
|
||||||
|
return posToRender[n * 3 + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNthPosZ(int n)
|
||||||
|
{
|
||||||
|
return posToRender[n * 3 + 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("To render ");
|
||||||
|
builder.append(numberOfPosToRender);
|
||||||
|
builder.append('\n');
|
||||||
|
for (int i = 0; i < numberOfPosToRender; i++)
|
||||||
|
{
|
||||||
|
builder.append(posToRender[i * 3]);
|
||||||
|
builder.append(" ");
|
||||||
|
builder.append(posToRender[i * 3 + 1]);
|
||||||
|
builder.append(" ");
|
||||||
|
builder.append(posToRender[i * 3 + 2]);
|
||||||
|
builder.append('\n');
|
||||||
|
}
|
||||||
|
builder.append('\n');
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,626 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.enums.LodDirection;
|
||||||
|
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||||
|
import com.seibel.lod.core.objects.math.Vec3i;
|
||||||
|
import com.seibel.lod.core.util.ColorUtil;
|
||||||
|
import com.seibel.lod.core.util.DataPointUtil;
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
import com.seibel.lod.core.util.SingletonHandler;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class handles all the vertex optimization that's needed for a column of lods. W
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 10-2-2021
|
||||||
|
*/
|
||||||
|
public class VertexOptimizer
|
||||||
|
{
|
||||||
|
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||||
|
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||||
|
|
||||||
|
public static final int ADJACENT_HEIGHT_INDEX = 0;
|
||||||
|
public static final int ADJACENT_DEPTH_INDEX = 1;
|
||||||
|
|
||||||
|
public static final int X = 0;
|
||||||
|
public static final int Y = 1;
|
||||||
|
public static final int Z = 2;
|
||||||
|
|
||||||
|
public static final int MIN = 0;
|
||||||
|
public static final int MAX = 1;
|
||||||
|
|
||||||
|
public static final int VOID_FACE = 0;
|
||||||
|
|
||||||
|
/** The six cardinal directions */
|
||||||
|
public static final LodDirection[] DIRECTIONS = new LodDirection[] {
|
||||||
|
LodDirection.UP,
|
||||||
|
LodDirection.DOWN,
|
||||||
|
LodDirection.WEST,
|
||||||
|
LodDirection.EAST,
|
||||||
|
LodDirection.NORTH,
|
||||||
|
LodDirection.SOUTH };
|
||||||
|
|
||||||
|
/** North, South, East, West */
|
||||||
|
public static final LodDirection[] ADJ_DIRECTIONS = new LodDirection[] {
|
||||||
|
LodDirection.EAST,
|
||||||
|
LodDirection.WEST,
|
||||||
|
LodDirection.SOUTH,
|
||||||
|
LodDirection.NORTH };
|
||||||
|
|
||||||
|
/** All the faces and vertices of a cube. This is used to extract the vertex from the column */
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static final Map<LodDirection, int[][]> DIRECTION_VERTEX_MAP = new HashMap<LodDirection, int[][]>()
|
||||||
|
{{
|
||||||
|
put(LodDirection.UP, new int[][] {
|
||||||
|
{ 0, 1, 0 }, // 0
|
||||||
|
{ 0, 1, 1 }, // 1
|
||||||
|
{ 1, 1, 1 }, // 2
|
||||||
|
|
||||||
|
{ 0, 1, 0 }, // 0
|
||||||
|
{ 1, 1, 1 }, // 2
|
||||||
|
{ 1, 1, 0 } // 3
|
||||||
|
});
|
||||||
|
put(LodDirection.DOWN, new int[][] {
|
||||||
|
{ 1, 0, 0 }, // 0
|
||||||
|
{ 1, 0, 1 }, // 1
|
||||||
|
{ 0, 0, 1 }, // 2
|
||||||
|
|
||||||
|
{ 1, 0, 0 }, // 0
|
||||||
|
{ 0, 0, 1 }, // 2
|
||||||
|
{ 0, 0, 0 } // 3
|
||||||
|
});
|
||||||
|
put(LodDirection.EAST, new int[][] {
|
||||||
|
{ 1, 1, 0 }, // 0
|
||||||
|
{ 1, 1, 1 }, // 1
|
||||||
|
{ 1, 0, 1 }, // 2
|
||||||
|
|
||||||
|
{ 1, 1, 0 }, // 0
|
||||||
|
{ 1, 0, 1 }, // 2
|
||||||
|
{ 1, 0, 0 } }); // 3
|
||||||
|
put(LodDirection.WEST, new int[][] {
|
||||||
|
{ 0, 0, 0 }, // 0
|
||||||
|
{ 0, 0, 1 }, // 1
|
||||||
|
{ 0, 1, 1 }, // 2
|
||||||
|
|
||||||
|
{ 0, 0, 0 }, // 0
|
||||||
|
{ 0, 1, 1 }, // 2
|
||||||
|
{ 0, 1, 0 } // 3
|
||||||
|
});
|
||||||
|
put(LodDirection.SOUTH, new int[][] {
|
||||||
|
{ 1, 0, 1 }, // 0
|
||||||
|
{ 1, 1, 1 }, // 1
|
||||||
|
{ 0, 1, 1 }, // 2
|
||||||
|
|
||||||
|
{ 1, 0, 1 }, // 0
|
||||||
|
{ 0, 1, 1 }, // 2
|
||||||
|
{ 0, 0, 1 } // 3
|
||||||
|
});
|
||||||
|
put(LodDirection.NORTH, new int[][] {
|
||||||
|
{ 0, 0, 0 }, // 0
|
||||||
|
{ 0, 1, 0 }, // 1
|
||||||
|
{ 1, 1, 0 }, // 2
|
||||||
|
|
||||||
|
{ 0, 0, 0 }, // 0
|
||||||
|
{ 1, 1, 0 }, // 2
|
||||||
|
{ 1, 0, 0 } // 3
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This indicates which position is invariable in the DIRECTION_VERTEX_MAP.
|
||||||
|
* Is used to extract the vertex
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static final Map<LodDirection, int[]> FACE_DIRECTION = new HashMap<LodDirection, int[]>()
|
||||||
|
{{
|
||||||
|
put(LodDirection.UP, new int[] { Y, MAX });
|
||||||
|
put(LodDirection.DOWN, new int[] { Y, MIN });
|
||||||
|
put(LodDirection.EAST, new int[] { X, MAX });
|
||||||
|
put(LodDirection.WEST, new int[] { X, MIN });
|
||||||
|
put(LodDirection.SOUTH, new int[] { Z, MAX });
|
||||||
|
put(LodDirection.NORTH, new int[] { Z, MIN });
|
||||||
|
}};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a map from Direction to the relative normal vector
|
||||||
|
* we are using this since I'm not sure if the getNormal create new object at every call
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static final Map<LodDirection, Vec3i> DIRECTION_NORMAL_MAP = new HashMap<LodDirection, Vec3i>()
|
||||||
|
{{
|
||||||
|
put(LodDirection.UP, LodDirection.UP.getNormal());
|
||||||
|
put(LodDirection.DOWN, LodDirection.DOWN.getNormal());
|
||||||
|
put(LodDirection.EAST, LodDirection.EAST.getNormal());
|
||||||
|
put(LodDirection.WEST, LodDirection.WEST.getNormal());
|
||||||
|
put(LodDirection.SOUTH, LodDirection.SOUTH.getNormal());
|
||||||
|
put(LodDirection.NORTH, LodDirection.NORTH.getNormal());
|
||||||
|
}};
|
||||||
|
|
||||||
|
/** We use this index for all array that are going to */
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static final Map<LodDirection, Integer> DIRECTION_INDEX = new HashMap<LodDirection, Integer>()
|
||||||
|
{{
|
||||||
|
put(LodDirection.UP, 0);
|
||||||
|
put(LodDirection.DOWN, 1);
|
||||||
|
put(LodDirection.EAST, 2);
|
||||||
|
put(LodDirection.WEST, 3);
|
||||||
|
put(LodDirection.SOUTH, 4);
|
||||||
|
put(LodDirection.NORTH, 5);
|
||||||
|
}};
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static final Map<LodDirection, Integer> ADJ_DIRECTION_INDEX = new HashMap<LodDirection, Integer>()
|
||||||
|
{{
|
||||||
|
put(LodDirection.EAST, 0);
|
||||||
|
put(LodDirection.WEST, 1);
|
||||||
|
put(LodDirection.SOUTH, 2);
|
||||||
|
put(LodDirection.NORTH, 3);
|
||||||
|
}};
|
||||||
|
/** holds the box's x, y, z offset */
|
||||||
|
public final int[] boxOffset;
|
||||||
|
/** holds the box's x, y, z width */
|
||||||
|
public final int[] boxWidth;
|
||||||
|
|
||||||
|
/** Holds each direction's color */
|
||||||
|
public final int[] colorMap;
|
||||||
|
/** The original color (before shading) of this box */
|
||||||
|
public int color;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final Map<LodDirection, int[]> adjHeight;
|
||||||
|
public final Map<LodDirection, int[]> adjDepth;
|
||||||
|
public final Map<LodDirection, byte[]> skyLights;
|
||||||
|
public byte blockLight;
|
||||||
|
|
||||||
|
/** Holds if the given direction should be culled or not */
|
||||||
|
public final boolean[] culling;
|
||||||
|
|
||||||
|
|
||||||
|
/** creates an empty box */
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public VertexOptimizer()
|
||||||
|
{
|
||||||
|
boxOffset = new int[3];
|
||||||
|
boxWidth = new int[3];
|
||||||
|
|
||||||
|
colorMap = new int[6];
|
||||||
|
skyLights = new HashMap<LodDirection, byte[]>()
|
||||||
|
{{
|
||||||
|
put(LodDirection.UP, new byte[1]);
|
||||||
|
put(LodDirection.DOWN, new byte[1]);
|
||||||
|
put(LodDirection.EAST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
put(LodDirection.WEST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
put(LodDirection.SOUTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
put(LodDirection.NORTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
}};
|
||||||
|
adjHeight = new HashMap<LodDirection, int[]>()
|
||||||
|
{{
|
||||||
|
put(LodDirection.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
put(LodDirection.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
put(LodDirection.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
}};
|
||||||
|
adjDepth = new HashMap<LodDirection, int[]>()
|
||||||
|
{{
|
||||||
|
put(LodDirection.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
put(LodDirection.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
put(LodDirection.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||||
|
}};
|
||||||
|
|
||||||
|
culling = new boolean[6];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the light of the columns */
|
||||||
|
public void setLights(int skyLight, int blockLight)
|
||||||
|
{
|
||||||
|
this.blockLight = (byte) blockLight;
|
||||||
|
skyLights.get(LodDirection.UP)[0] = (byte) skyLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the color of the columns
|
||||||
|
* @param color color to add
|
||||||
|
* @param adjShadeDisabled this array indicates which face does not need shading
|
||||||
|
*/
|
||||||
|
public void setColor(int color, boolean[] adjShadeDisabled)
|
||||||
|
{
|
||||||
|
this.color = color;
|
||||||
|
for (LodDirection lodDirection : DIRECTIONS)
|
||||||
|
{
|
||||||
|
if (!adjShadeDisabled[DIRECTION_INDEX.get(lodDirection)])
|
||||||
|
colorMap[DIRECTION_INDEX.get(lodDirection)] = ColorUtil.applyShade(color, MC.getShade(lodDirection));
|
||||||
|
else
|
||||||
|
colorMap[DIRECTION_INDEX.get(lodDirection)] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lodDirection of the face of which we want to get the color
|
||||||
|
* @return color of the face
|
||||||
|
*/
|
||||||
|
public int getColor(LodDirection lodDirection)
|
||||||
|
{
|
||||||
|
if (CONFIG.client().advanced().debugging().getDebugMode() != DebugMode.SHOW_DETAIL)
|
||||||
|
return colorMap[DIRECTION_INDEX.get(lodDirection)];
|
||||||
|
else
|
||||||
|
return ColorUtil.applyShade(color, MC.getShade(lodDirection));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public byte getSkyLight(LodDirection lodDirection, int verticalIndex)
|
||||||
|
{
|
||||||
|
if(lodDirection == LodDirection.UP || lodDirection == LodDirection.DOWN)
|
||||||
|
return skyLights.get(lodDirection)[0];
|
||||||
|
else
|
||||||
|
return skyLights.get(lodDirection)[verticalIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public int getBlockLight()
|
||||||
|
{
|
||||||
|
return blockLight;
|
||||||
|
}
|
||||||
|
/** clears this box, resetting everything to default values */
|
||||||
|
public void reset()
|
||||||
|
{
|
||||||
|
Arrays.fill(boxWidth, 0);
|
||||||
|
Arrays.fill(boxOffset, 0);
|
||||||
|
Arrays.fill(colorMap, 0);
|
||||||
|
blockLight = 0;
|
||||||
|
for (LodDirection lodDirection : ADJ_DIRECTIONS)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < adjHeight.get(lodDirection).length; i++)
|
||||||
|
{
|
||||||
|
adjHeight.get(lodDirection)[i] = VOID_FACE;
|
||||||
|
adjDepth.get(lodDirection)[i] = VOID_FACE;
|
||||||
|
skyLights.get(lodDirection)[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** determine which faces should be culled */
|
||||||
|
public void setUpCulling(int cullingDistance, AbstractBlockPosWrapper playerPos)
|
||||||
|
{
|
||||||
|
for (LodDirection lodDirection : DIRECTIONS)
|
||||||
|
{
|
||||||
|
if (lodDirection == LodDirection.DOWN || lodDirection == LodDirection.WEST || lodDirection == LodDirection.NORTH)
|
||||||
|
culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) > getFacePos(lodDirection) + cullingDistance;
|
||||||
|
|
||||||
|
else if (lodDirection == LodDirection.UP || lodDirection == LodDirection.EAST || lodDirection == LodDirection.SOUTH)
|
||||||
|
culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) < getFacePos(lodDirection) - cullingDistance;
|
||||||
|
|
||||||
|
culling[DIRECTION_INDEX.get(lodDirection)] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lodDirection direction that we want to check if it's culled
|
||||||
|
* @return true if and only if the face of the direction is culled
|
||||||
|
*/
|
||||||
|
public boolean isCulled(LodDirection lodDirection)
|
||||||
|
{
|
||||||
|
return culling[DIRECTION_INDEX.get(lodDirection)];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method create all the shared face culling based on the adjacent data
|
||||||
|
* @param adjData data adjacent to the column we are going to render
|
||||||
|
*/
|
||||||
|
public void setAdjData(Map<LodDirection, long[]> adjData)
|
||||||
|
{
|
||||||
|
int height;
|
||||||
|
int depth;
|
||||||
|
int minY = getMinY();
|
||||||
|
int maxY = getMaxY();
|
||||||
|
long singleAdjDataPoint;
|
||||||
|
|
||||||
|
/* TODO implement attached vertical face culling
|
||||||
|
//Up direction case
|
||||||
|
if(DataPointUtil.doesItExist(adjData.get(Direction.UP)))
|
||||||
|
{
|
||||||
|
height = DataPointUtil.getHeight(singleAdjDataPoint);
|
||||||
|
depth = DataPointUtil.getDepth(singleAdjDataPoint);
|
||||||
|
}*/
|
||||||
|
//Down direction case
|
||||||
|
singleAdjDataPoint = adjData.get(LodDirection.DOWN)[0];
|
||||||
|
if(DataPointUtil.doesItExist(singleAdjDataPoint))
|
||||||
|
skyLights.get(LodDirection.DOWN)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||||
|
else
|
||||||
|
skyLights.get(LodDirection.DOWN)[0] = skyLights.get(LodDirection.UP)[0];
|
||||||
|
//other sided
|
||||||
|
//TODO clean some similar cases
|
||||||
|
for (LodDirection lodDirection : ADJ_DIRECTIONS)
|
||||||
|
{
|
||||||
|
if (isCulled(lodDirection))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
long[] dataPoint = adjData.get(lodDirection);
|
||||||
|
if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0]))
|
||||||
|
{
|
||||||
|
adjHeight.get(lodDirection)[0] = maxY;
|
||||||
|
adjDepth.get(lodDirection)[0] = minY;
|
||||||
|
adjHeight.get(lodDirection)[1] = VOID_FACE;
|
||||||
|
adjDepth.get(lodDirection)[1] = VOID_FACE;
|
||||||
|
skyLights.get(lodDirection)[0] = 15; //in void set full skylight
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
int faceToDraw = 0;
|
||||||
|
boolean firstFace = true;
|
||||||
|
boolean toFinish = false;
|
||||||
|
int toFinishIndex = 0;
|
||||||
|
boolean allAbove = true;
|
||||||
|
for (i = 0; i < dataPoint.length; i++)
|
||||||
|
{
|
||||||
|
singleAdjDataPoint = dataPoint[i];
|
||||||
|
|
||||||
|
if (DataPointUtil.isVoid(singleAdjDataPoint) || !DataPointUtil.doesItExist(singleAdjDataPoint))
|
||||||
|
break;
|
||||||
|
|
||||||
|
height = DataPointUtil.getHeight(singleAdjDataPoint);
|
||||||
|
depth = DataPointUtil.getDepth(singleAdjDataPoint);
|
||||||
|
|
||||||
|
if (depth <= maxY)
|
||||||
|
{
|
||||||
|
allAbove = false;
|
||||||
|
if (height < minY)
|
||||||
|
{
|
||||||
|
// the adj data is lower than the current data
|
||||||
|
|
||||||
|
if (firstFace)
|
||||||
|
{
|
||||||
|
adjHeight.get(lodDirection)[0] = getMaxY();
|
||||||
|
adjDepth.get(lodDirection)[0] = getMinY();
|
||||||
|
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
adjDepth.get(lodDirection)[faceToDraw] = getMinY();
|
||||||
|
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||||
|
}
|
||||||
|
faceToDraw++;
|
||||||
|
toFinish = false;
|
||||||
|
|
||||||
|
// break since all the other data will be lower
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (depth <= minY)
|
||||||
|
{
|
||||||
|
if (height >= maxY)
|
||||||
|
{
|
||||||
|
// the adj data is inside the current data
|
||||||
|
// don't draw the face
|
||||||
|
adjHeight.get(lodDirection)[0] = VOID_FACE;
|
||||||
|
adjDepth.get(lodDirection)[0] = VOID_FACE;
|
||||||
|
}
|
||||||
|
else // height < maxY
|
||||||
|
{
|
||||||
|
// the adj data intersects the lower part of the current data
|
||||||
|
// if this is the only face, use the maxY and break,
|
||||||
|
// if there was another face we finish the last one and break
|
||||||
|
if (firstFace)
|
||||||
|
{
|
||||||
|
adjHeight.get(lodDirection)[0] = getMaxY();
|
||||||
|
adjDepth.get(lodDirection)[0] = height;
|
||||||
|
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
adjDepth.get(lodDirection)[faceToDraw] = height;
|
||||||
|
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||||
|
}
|
||||||
|
toFinish = false;
|
||||||
|
faceToDraw++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (height >= maxY)//depth > minY &&
|
||||||
|
{
|
||||||
|
// the adj data intersects the higher part of the current data
|
||||||
|
// we start the creation of a new face
|
||||||
|
adjHeight.get(lodDirection)[faceToDraw] = depth;
|
||||||
|
//skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||||
|
firstFace = false;
|
||||||
|
toFinish = true;
|
||||||
|
toFinishIndex = i + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if (depth > minY && height < maxY)
|
||||||
|
|
||||||
|
// the adj data is contained in the current data
|
||||||
|
if (firstFace)
|
||||||
|
{
|
||||||
|
adjHeight.get(lodDirection)[0] = getMaxY();
|
||||||
|
}
|
||||||
|
|
||||||
|
adjDepth.get(lodDirection)[faceToDraw] = height;
|
||||||
|
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||||
|
faceToDraw++;
|
||||||
|
adjHeight.get(lodDirection)[faceToDraw] = depth;
|
||||||
|
firstFace = false;
|
||||||
|
toFinish = true;
|
||||||
|
toFinishIndex = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allAbove)
|
||||||
|
{
|
||||||
|
adjHeight.get(lodDirection)[0] = getMaxY();
|
||||||
|
adjDepth.get(lodDirection)[0] = getMinY();
|
||||||
|
skyLights.get(lodDirection)[0] = skyLights.get(LodDirection.UP)[0];
|
||||||
|
faceToDraw++;
|
||||||
|
}
|
||||||
|
else if (toFinish)
|
||||||
|
{
|
||||||
|
adjDepth.get(lodDirection)[faceToDraw] = minY;
|
||||||
|
if(toFinishIndex < dataPoint.length)
|
||||||
|
{
|
||||||
|
singleAdjDataPoint = dataPoint[toFinishIndex];
|
||||||
|
if (DataPointUtil.doesItExist(singleAdjDataPoint))
|
||||||
|
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||||
|
else
|
||||||
|
skyLights.get(lodDirection)[faceToDraw] = skyLights.get(LodDirection.UP)[0];
|
||||||
|
}
|
||||||
|
faceToDraw++;
|
||||||
|
}
|
||||||
|
|
||||||
|
adjHeight.get(lodDirection)[faceToDraw] = VOID_FACE;
|
||||||
|
adjDepth.get(lodDirection)[faceToDraw] = VOID_FACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** We use this method to set the various width of the column */
|
||||||
|
public void setWidth(int xWidth, int yWidth, int zWidth)
|
||||||
|
{
|
||||||
|
boxWidth[X] = xWidth;
|
||||||
|
boxWidth[Y] = yWidth;
|
||||||
|
boxWidth[Z] = zWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** We use this method to set the various offset of the column */
|
||||||
|
public void setOffset(int xOffset, int yOffset, int zOffset)
|
||||||
|
{
|
||||||
|
boxOffset[X] = xOffset;
|
||||||
|
boxOffset[Y] = yOffset;
|
||||||
|
boxOffset[Z] = zOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method return the position of a face in the axis of the face
|
||||||
|
* This is useful for the face culling
|
||||||
|
* @param lodDirection that we want to check
|
||||||
|
* @return position in the axis of the face
|
||||||
|
*/
|
||||||
|
public int getFacePos(LodDirection lodDirection)
|
||||||
|
{
|
||||||
|
return boxOffset[FACE_DIRECTION.get(lodDirection)[0]] + boxWidth[FACE_DIRECTION.get(lodDirection)[0]] * FACE_DIRECTION.get(lodDirection)[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns true if the given direction should be rendered.
|
||||||
|
*/
|
||||||
|
public boolean shouldRenderFace(LodDirection lodDirection, int adjIndex)
|
||||||
|
{
|
||||||
|
if (lodDirection == LodDirection.UP || lodDirection == LodDirection.DOWN)
|
||||||
|
return adjIndex == 0;
|
||||||
|
return !(adjHeight.get(lodDirection)[adjIndex] == VOID_FACE && adjDepth.get(lodDirection)[adjIndex] == VOID_FACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lodDirection direction of the face we want to render
|
||||||
|
* @param vertexIndex index of the vertex of the face (0-1-2-3)
|
||||||
|
* @return position x of the relative vertex
|
||||||
|
*/
|
||||||
|
public int getX(LodDirection lodDirection, int vertexIndex)
|
||||||
|
{
|
||||||
|
return boxOffset[X] + boxWidth[X] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][X];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lodDirection direction of the face we want to render
|
||||||
|
* @param vertexIndex index of the vertex of the face (0-1-2-3)
|
||||||
|
* @return position y of the relative vertex
|
||||||
|
*/
|
||||||
|
public int getY(LodDirection lodDirection, int vertexIndex)
|
||||||
|
{
|
||||||
|
return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lodDirection direction of the face we want to render
|
||||||
|
* @param vertexIndex index of the vertex of the face (0-1-2-3)
|
||||||
|
* @param adjIndex, index of the n-th culled face of this direction
|
||||||
|
* @return position x of the relative vertex
|
||||||
|
*/
|
||||||
|
public int getY(LodDirection lodDirection, int vertexIndex, int adjIndex)
|
||||||
|
{
|
||||||
|
if (lodDirection == LodDirection.DOWN || lodDirection == LodDirection.UP)
|
||||||
|
return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// this could probably be cleaned up more,
|
||||||
|
// but it still works
|
||||||
|
if (1 - DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y] == ADJACENT_HEIGHT_INDEX)
|
||||||
|
return adjHeight.get(lodDirection)[adjIndex];
|
||||||
|
else
|
||||||
|
return adjDepth.get(lodDirection)[adjIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lodDirection direction of the face we want to render
|
||||||
|
* @param vertexIndex index of the vertex of the face (0-1-2-3)
|
||||||
|
* @return position z of the relative vertex
|
||||||
|
*/
|
||||||
|
public int getZ(LodDirection lodDirection, int vertexIndex)
|
||||||
|
{
|
||||||
|
return boxOffset[Z] + boxWidth[Z] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Z];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinX()
|
||||||
|
{
|
||||||
|
return boxOffset[X];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxX()
|
||||||
|
{
|
||||||
|
return boxOffset[X] + boxWidth[X];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinY()
|
||||||
|
{
|
||||||
|
return boxOffset[Y];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxY()
|
||||||
|
{
|
||||||
|
return boxOffset[Y] + boxWidth[Y];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinZ()
|
||||||
|
{
|
||||||
|
return boxOffset[Z];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxZ()
|
||||||
|
{
|
||||||
|
return boxOffset[Z] + boxWidth[Z];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects.lod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A level container is a quad tree level
|
||||||
|
*/
|
||||||
|
public interface LevelContainer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* With this you can add data to the level container
|
||||||
|
* @param data actual data to add in an array of long format.
|
||||||
|
* @param posX x position in the detail level
|
||||||
|
* @param posZ z position in the detail level
|
||||||
|
* @param index z position in the detail level
|
||||||
|
* @return true if correctly added, false otherwise
|
||||||
|
*/
|
||||||
|
boolean addData(long data, int posX, int posZ, int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With this you can add data to the level container
|
||||||
|
* @param data actual data to add in an array of long[] format.
|
||||||
|
* @param posX x position in the detail level
|
||||||
|
* @param posZ z position in the detail level
|
||||||
|
* @return true if correctly added, false otherwise
|
||||||
|
*/
|
||||||
|
boolean addVerticalData(long[] data, int posX, int posZ);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With this you can add data to the level container
|
||||||
|
* @param data actual data to add in an array of long format.
|
||||||
|
* @param posX x position in the detail level
|
||||||
|
* @param posZ z position in the detail level
|
||||||
|
* @return true if correctly added, false otherwise
|
||||||
|
*/
|
||||||
|
boolean addSingleData(long data, int posX, int posZ);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With this you can get data from the level container
|
||||||
|
* @param posX x position in the detail level
|
||||||
|
* @param posZ z position in the detail level
|
||||||
|
* @return the data in long array format
|
||||||
|
*/
|
||||||
|
long getData(int posX, int posZ, int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With this you can get data from the level container
|
||||||
|
* @param posX x position in the detail level
|
||||||
|
* @param posZ z position in the detail level
|
||||||
|
* @return the data in long array format
|
||||||
|
*/
|
||||||
|
long getSingleData(int posX, int posZ);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param posX x position in the detail level
|
||||||
|
* @param posZ z position in the detail level
|
||||||
|
* @return true only if the data exist
|
||||||
|
*/
|
||||||
|
boolean doesItExist(int posX, int posZ);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return return the detailLevel of this level container
|
||||||
|
*/
|
||||||
|
byte getDetailLevel();
|
||||||
|
|
||||||
|
|
||||||
|
int getMaxVerticalData();
|
||||||
|
|
||||||
|
/** Clears the dataPoint at the given array index */
|
||||||
|
void clear(int posX, int posZ);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This return a level container with detail level lower than the current level.
|
||||||
|
* The new level container may use information of this level.
|
||||||
|
* @return the new level container
|
||||||
|
*/
|
||||||
|
LevelContainer expand();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lowerLevelContainer lower level where we extract the data
|
||||||
|
* @param posX x position in the detail level to update
|
||||||
|
* @param posZ z position in the detail level to update
|
||||||
|
*/
|
||||||
|
void updateData(LevelContainer lowerLevelContainer, int posX, int posZ);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will give the data to save in the file
|
||||||
|
* @return data as a String
|
||||||
|
*/
|
||||||
|
byte[] toDataString();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will give the data to save in the file
|
||||||
|
* @return data as a String
|
||||||
|
*/
|
||||||
|
int getMaxNumberOfLods();
|
||||||
|
}
|
||||||
@@ -0,0 +1,913 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects.lod;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||||
|
import com.seibel.lod.core.enums.config.GenerationPriority;
|
||||||
|
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||||
|
import com.seibel.lod.core.handlers.LodDimensionFileHandler;
|
||||||
|
import com.seibel.lod.core.objects.PosToGenerateContainer;
|
||||||
|
import com.seibel.lod.core.objects.PosToRenderContainer;
|
||||||
|
import com.seibel.lod.core.util.DataPointUtil;
|
||||||
|
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||||
|
import com.seibel.lod.core.util.LevelPosUtil;
|
||||||
|
import com.seibel.lod.core.util.LodThreadFactory;
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
import com.seibel.lod.core.util.SingletonHandler;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object holds all loaded LOD regions
|
||||||
|
* for a given dimension. <Br><Br>
|
||||||
|
*
|
||||||
|
* <strong>Coordinate Standard: </strong><br>
|
||||||
|
* Coordinate called posX or posZ are relative LevelPos coordinates <br>
|
||||||
|
* unless stated otherwise. <br>
|
||||||
|
*
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-12-2021
|
||||||
|
*/
|
||||||
|
public class LodDimension
|
||||||
|
{
|
||||||
|
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||||
|
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||||
|
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||||
|
|
||||||
|
public final IDimensionTypeWrapper dimension;
|
||||||
|
|
||||||
|
/** measured in regions */
|
||||||
|
private volatile int width;
|
||||||
|
/** measured in regions */
|
||||||
|
private volatile int halfWidth;
|
||||||
|
|
||||||
|
// these three variables are private to force use of the getWidth() method
|
||||||
|
// which is a safer way to get the width then directly asking the arrays
|
||||||
|
/** stores all the regions in this dimension */
|
||||||
|
public volatile LodRegion[][] regions;
|
||||||
|
|
||||||
|
/** stores if the region at the given x and z index needs to be saved to disk */
|
||||||
|
private volatile boolean[][] isRegionDirty;
|
||||||
|
/** stores if the region at the given x and z index needs to be regenerated */
|
||||||
|
private volatile boolean[][] regenRegionBuffer;
|
||||||
|
/** stores if the buffer size at the given x and z index needs to be changed */
|
||||||
|
private volatile boolean[][] recreateRegionBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if true that means there are regions in this dimension
|
||||||
|
* that need to have their buffers rebuilt.
|
||||||
|
*/
|
||||||
|
public volatile boolean regenDimensionBuffers = false;
|
||||||
|
|
||||||
|
private LodDimensionFileHandler fileHandler;
|
||||||
|
|
||||||
|
private final RegionPos center;
|
||||||
|
|
||||||
|
/** prevents the cutAndExpandThread from expanding at the same location multiple times */
|
||||||
|
private volatile AbstractChunkPosWrapper lastExpandedChunk;
|
||||||
|
/** prevents the cutAndExpandThread from cutting at the same location multiple times */
|
||||||
|
private volatile AbstractChunkPosWrapper lastCutChunk;
|
||||||
|
private final ExecutorService cutAndExpandThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - Cut and Expand"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the dimension centered at (0,0)
|
||||||
|
* @param newWidth in regions
|
||||||
|
*/
|
||||||
|
public LodDimension(IDimensionTypeWrapper newDimension, LodWorld lodWorld, int newWidth)
|
||||||
|
{
|
||||||
|
lastCutChunk = null;
|
||||||
|
lastExpandedChunk = null;
|
||||||
|
dimension = newDimension;
|
||||||
|
width = newWidth;
|
||||||
|
halfWidth = width / 2;
|
||||||
|
|
||||||
|
if (newDimension != null && lodWorld != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// determine the save folder
|
||||||
|
File saveDir;
|
||||||
|
if (MC.hasSinglePlayerServer())
|
||||||
|
{
|
||||||
|
// local world
|
||||||
|
|
||||||
|
IWorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(newDimension);
|
||||||
|
saveDir = new File(serverWorld.getSaveFolder().getCanonicalFile().getPath() + File.separatorChar + "lod");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// connected to server
|
||||||
|
|
||||||
|
saveDir = new File(MC.getGameDirectory().getCanonicalFile().getPath() +
|
||||||
|
File.separatorChar + "Distant_Horizons_server_data" + File.separatorChar + MC.getCurrentDimensionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
fileHandler = new LodDimensionFileHandler(saveDir, this);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
// the file handler wasn't able to be created
|
||||||
|
// we won't be able to read or write any files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
regions = new LodRegion[width][width];
|
||||||
|
isRegionDirty = new boolean[width][width];
|
||||||
|
regenRegionBuffer = new boolean[width][width];
|
||||||
|
recreateRegionBuffer = new boolean[width][width];
|
||||||
|
|
||||||
|
center = new RegionPos(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the center of this LodDimension and move all owned
|
||||||
|
* regions over by the given x and z offset. <br><br>
|
||||||
|
* <p>
|
||||||
|
* Synchronized to prevent multiple moves happening on top of each other.
|
||||||
|
*/
|
||||||
|
public synchronized void move(RegionPos regionOffset)
|
||||||
|
{
|
||||||
|
int xOffset = regionOffset.x;
|
||||||
|
int zOffset = regionOffset.z;
|
||||||
|
|
||||||
|
// if the x or z offset is equal to or greater than
|
||||||
|
// the total width, just delete the current data
|
||||||
|
// and update the centerX and/or centerZ
|
||||||
|
if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
for (int z = 0; z < width; z++)
|
||||||
|
regions[x][z] = null;
|
||||||
|
|
||||||
|
// update the new center
|
||||||
|
center.x += xOffset;
|
||||||
|
center.z += zOffset;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// X
|
||||||
|
if (xOffset > 0)
|
||||||
|
{
|
||||||
|
// move everything over to the left (as the center moves to the right)
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < width; z++)
|
||||||
|
{
|
||||||
|
if (x + xOffset < width)
|
||||||
|
regions[x][z] = regions[x + xOffset][z];
|
||||||
|
else
|
||||||
|
regions[x][z] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// move everything over to the right (as the center moves to the left)
|
||||||
|
for (int x = width - 1; x >= 0; x--)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < width; z++)
|
||||||
|
{
|
||||||
|
if (x + xOffset >= 0)
|
||||||
|
regions[x][z] = regions[x + xOffset][z];
|
||||||
|
else
|
||||||
|
regions[x][z] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Z
|
||||||
|
if (zOffset > 0)
|
||||||
|
{
|
||||||
|
// move everything up (as the center moves down)
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < width; z++)
|
||||||
|
{
|
||||||
|
if (z + zOffset < width)
|
||||||
|
regions[x][z] = regions[x][z + zOffset];
|
||||||
|
else
|
||||||
|
regions[x][z] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// move everything down (as the center moves up)
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
for (int z = width - 1; z >= 0; z--)
|
||||||
|
{
|
||||||
|
if (z + zOffset >= 0)
|
||||||
|
regions[x][z] = regions[x][z + zOffset];
|
||||||
|
else
|
||||||
|
regions[x][z] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// update the new center
|
||||||
|
center.x += xOffset;
|
||||||
|
center.z += zOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the region at the given LevelPos
|
||||||
|
* <br>
|
||||||
|
* Returns null if the region doesn't exist
|
||||||
|
* or is outside the loaded area.
|
||||||
|
*/
|
||||||
|
public LodRegion getRegion(byte detailLevel, int levelPosX, int levelPosZ)
|
||||||
|
{
|
||||||
|
int xRegion = LevelPosUtil.getRegion(detailLevel, levelPosX);
|
||||||
|
int zRegion = LevelPosUtil.getRegion(detailLevel, levelPosZ);
|
||||||
|
int xIndex = (xRegion - center.x) + halfWidth;
|
||||||
|
int zIndex = (zRegion - center.z) + halfWidth;
|
||||||
|
|
||||||
|
if (!regionIsInRange(xRegion, zRegion))
|
||||||
|
return null;
|
||||||
|
// throw new ArrayIndexOutOfBoundsException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " out of range");
|
||||||
|
else if (regions[xIndex][zIndex] == null)
|
||||||
|
return null;
|
||||||
|
else if (regions[xIndex][zIndex].getMinDetailLevel() > detailLevel)
|
||||||
|
return null;
|
||||||
|
//throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " currently only reach level " + regions[xIndex][zIndex].getMinDetailLevel());
|
||||||
|
|
||||||
|
return regions[xIndex][zIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the region at the given X and Z
|
||||||
|
* <br>
|
||||||
|
* Returns null if the region doesn't exist
|
||||||
|
* or is outside the loaded area.
|
||||||
|
*/
|
||||||
|
public LodRegion getRegion(int regionPosX, int regionPosZ)
|
||||||
|
{
|
||||||
|
int xIndex = (regionPosX - center.x) + halfWidth;
|
||||||
|
int zIndex = (regionPosZ - center.z) + halfWidth;
|
||||||
|
|
||||||
|
if (!regionIsInRange(regionPosX, regionPosZ))
|
||||||
|
return null;
|
||||||
|
//throw new ArrayIndexOutOfBoundsException("Region " + regionPosX + " " + regionPosZ + " out of range");
|
||||||
|
|
||||||
|
return regions[xIndex][zIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Useful when iterating over every region. */
|
||||||
|
public LodRegion getRegionByArrayIndex(int xIndex, int zIndex)
|
||||||
|
{
|
||||||
|
return regions[xIndex][zIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite the LodRegion at the location of newRegion with newRegion.
|
||||||
|
* @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension.
|
||||||
|
*/
|
||||||
|
public synchronized void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException
|
||||||
|
{
|
||||||
|
int xIndex = (newRegion.regionPosX - center.x) + halfWidth;
|
||||||
|
int zIndex = (newRegion.regionPosZ - center.z) + halfWidth;
|
||||||
|
|
||||||
|
if (!regionIsInRange(newRegion.regionPosX, newRegion.regionPosZ))
|
||||||
|
// out of range
|
||||||
|
throw new ArrayIndexOutOfBoundsException("Region " + newRegion.regionPosX + ", " + newRegion.regionPosZ + " out of range");
|
||||||
|
|
||||||
|
regions[xIndex][zIndex] = newRegion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes nodes that are a higher detail then necessary, freeing
|
||||||
|
* up memory.
|
||||||
|
*/
|
||||||
|
public void cutRegionNodesAsync(int playerPosX, int playerPosZ)
|
||||||
|
{
|
||||||
|
AbstractChunkPosWrapper newPlayerChunk = FACTORY.createChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
|
||||||
|
|
||||||
|
if (lastCutChunk == null)
|
||||||
|
lastCutChunk = FACTORY.createChunkPos(newPlayerChunk.getX() + 1, newPlayerChunk.getZ() - 1);
|
||||||
|
|
||||||
|
// don't run the tree cutter multiple times
|
||||||
|
// for the same location
|
||||||
|
if (newPlayerChunk.getX() != lastCutChunk.getX() || newPlayerChunk.getZ() != lastCutChunk.getZ())
|
||||||
|
{
|
||||||
|
lastCutChunk = newPlayerChunk;
|
||||||
|
|
||||||
|
Thread thread = new Thread(() ->
|
||||||
|
{
|
||||||
|
int regionX;
|
||||||
|
int regionZ;
|
||||||
|
int minDistance;
|
||||||
|
byte detail;
|
||||||
|
byte minAllowedDetailLevel;
|
||||||
|
|
||||||
|
// go over every region in the dimension
|
||||||
|
for (int x = 0; x < regions.length; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < regions.length; z++)
|
||||||
|
{
|
||||||
|
regionX = (x + center.x) - halfWidth;
|
||||||
|
regionZ = (z + center.z) - halfWidth;
|
||||||
|
|
||||||
|
if (regions[x][z] != null)
|
||||||
|
{
|
||||||
|
// check what detail level this region should be
|
||||||
|
// and cut it if it is higher then that
|
||||||
|
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
|
||||||
|
detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance);
|
||||||
|
minAllowedDetailLevel = DetailDistanceUtil.getCutLodDetail(detail);
|
||||||
|
|
||||||
|
if (regions[x][z].getMinDetailLevel() > minAllowedDetailLevel)
|
||||||
|
{
|
||||||
|
regions[x][z].cutTree(minAllowedDetailLevel);
|
||||||
|
recreateRegionBuffer[x][z] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}// region z
|
||||||
|
}// region z
|
||||||
|
});
|
||||||
|
|
||||||
|
cutAndExpandThread.execute(thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Either expands or loads all regions in the rendered LOD area */
|
||||||
|
public void expandOrLoadRegionsAsync(int playerPosX, int playerPosZ)
|
||||||
|
{
|
||||||
|
DistanceGenerationMode generationMode = CONFIG.client().worldGenerator().getDistanceGenerationMode();
|
||||||
|
AbstractChunkPosWrapper newPlayerChunk = FACTORY.createChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
|
||||||
|
VerticalQuality verticalQuality = CONFIG.client().graphics().quality().getVerticalQuality();
|
||||||
|
|
||||||
|
|
||||||
|
if (lastExpandedChunk == null)
|
||||||
|
lastExpandedChunk = FACTORY.createChunkPos(newPlayerChunk.getX() + 1, newPlayerChunk.getZ() - 1);
|
||||||
|
|
||||||
|
// don't run the expander multiple times
|
||||||
|
// for the same location
|
||||||
|
if (newPlayerChunk.getX() != lastExpandedChunk.getX() || newPlayerChunk.getZ() != lastExpandedChunk.getZ())
|
||||||
|
{
|
||||||
|
lastExpandedChunk = newPlayerChunk;
|
||||||
|
|
||||||
|
Thread thread = new Thread(() ->
|
||||||
|
{
|
||||||
|
int regionX;
|
||||||
|
int regionZ;
|
||||||
|
LodRegion region;
|
||||||
|
int minDistance;
|
||||||
|
byte detail;
|
||||||
|
byte levelToGen;
|
||||||
|
|
||||||
|
for (int x = 0; x < regions.length; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < regions.length; z++)
|
||||||
|
{
|
||||||
|
regionX = (x + center.x) - halfWidth;
|
||||||
|
regionZ = (z + center.z) - halfWidth;
|
||||||
|
final RegionPos regionPos = new RegionPos(regionX, regionZ);
|
||||||
|
region = regions[x][z];
|
||||||
|
|
||||||
|
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
|
||||||
|
detail = DetailDistanceUtil.getTreeGenDetailFromDistance(minDistance);
|
||||||
|
levelToGen = DetailDistanceUtil.getLodGenDetail(detail).detailLevel;
|
||||||
|
|
||||||
|
// check that the region isn't null and at least this detail level
|
||||||
|
if (region == null || region.getGenerationMode() != generationMode)
|
||||||
|
{
|
||||||
|
// First case, region has to be created
|
||||||
|
|
||||||
|
// try to get the region from file
|
||||||
|
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
|
||||||
|
|
||||||
|
// if there is no region file create an empty region
|
||||||
|
if (regions[x][z] == null)
|
||||||
|
regions[x][z] = new LodRegion(levelToGen, regionPos, generationMode, verticalQuality);
|
||||||
|
|
||||||
|
regenRegionBuffer[x][z] = true;
|
||||||
|
regenDimensionBuffers = true;
|
||||||
|
recreateRegionBuffer[x][z] = true;
|
||||||
|
}
|
||||||
|
else if (region.getMinDetailLevel() > levelToGen)
|
||||||
|
{
|
||||||
|
// Second case, the region exists at a higher detail level.
|
||||||
|
|
||||||
|
// Expand the region by introducing the missing layer
|
||||||
|
region.growTree(levelToGen);
|
||||||
|
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
|
||||||
|
recreateRegionBuffer[x][z] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cutAndExpandThread.execute(thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use addVerticalData when possible.
|
||||||
|
* Add the given LOD to this dimension at the coordinate
|
||||||
|
* stored in the LOD. If an LOD already exists at the given
|
||||||
|
* coordinate it will be overwritten.
|
||||||
|
*/
|
||||||
|
public Boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data, boolean dontSave)
|
||||||
|
{
|
||||||
|
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
|
||||||
|
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
|
||||||
|
|
||||||
|
// don't continue if the region can't be saved
|
||||||
|
LodRegion region = getRegion(regionPosX, regionPosZ);
|
||||||
|
if (region == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boolean nodeAdded = region.addData(detailLevel, posX, posZ, verticalIndex, data);
|
||||||
|
|
||||||
|
// only save valid LODs to disk
|
||||||
|
if (!dontSave && fileHandler != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// mark the region as dirty, so it will be saved to disk
|
||||||
|
int xIndex = (regionPosX - center.x) + halfWidth;
|
||||||
|
int zIndex = (regionPosZ - center.z) + halfWidth;
|
||||||
|
|
||||||
|
isRegionDirty[xIndex][zIndex] = true;
|
||||||
|
regenRegionBuffer[xIndex][zIndex] = true;
|
||||||
|
regenDimensionBuffers = true;
|
||||||
|
}
|
||||||
|
catch (ArrayIndexOutOfBoundsException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
// If this happens, the method was probably
|
||||||
|
// called when the dimension was changing size.
|
||||||
|
// Hopefully this shouldn't be an issue.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add whole column of LODs to this dimension at the coordinate
|
||||||
|
* stored in the LOD. If an LOD already exists at the given
|
||||||
|
* coordinate it will be overwritten.
|
||||||
|
*/
|
||||||
|
public Boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data, boolean dontSave)
|
||||||
|
{
|
||||||
|
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
|
||||||
|
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
|
||||||
|
|
||||||
|
// don't continue if the region can't be saved
|
||||||
|
LodRegion region = getRegion(regionPosX, regionPosZ);
|
||||||
|
if (region == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boolean nodeAdded = region.addVerticalData(detailLevel, posX, posZ, data);
|
||||||
|
|
||||||
|
// only save valid LODs to disk
|
||||||
|
if (!dontSave && fileHandler != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// mark the region as dirty, so it will be saved to disk
|
||||||
|
int xIndex = (regionPosX - center.x) + halfWidth;
|
||||||
|
int zIndex = (regionPosZ - center.z) + halfWidth;
|
||||||
|
|
||||||
|
isRegionDirty[xIndex][zIndex] = true;
|
||||||
|
regenRegionBuffer[xIndex][zIndex] = true;
|
||||||
|
regenDimensionBuffers = true;
|
||||||
|
}
|
||||||
|
catch (ArrayIndexOutOfBoundsException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
// If this happens, the method was probably
|
||||||
|
// called when the dimension was changing size.
|
||||||
|
// Hopefully this shouldn't be an issue.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** marks the region at the given region position to have its buffer rebuilt */
|
||||||
|
public void markRegionBufferToRegen(int xRegion, int zRegion)
|
||||||
|
{
|
||||||
|
int xIndex = (xRegion - center.x) + halfWidth;
|
||||||
|
int zIndex = (zRegion - center.z) + halfWidth;
|
||||||
|
regenRegionBuffer[xIndex][zIndex] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns every position that need to be generated based on the position of the player
|
||||||
|
*/
|
||||||
|
public PosToGenerateContainer getPosToGenerate(int maxDataToGenerate, int playerBlockPosX, int playerBlockPosZ)
|
||||||
|
{
|
||||||
|
PosToGenerateContainer posToGenerate;
|
||||||
|
LodRegion lodRegion;
|
||||||
|
// all the following values are used for the spiral matrix visit
|
||||||
|
// x and z are the matrix coord
|
||||||
|
// dx and dz is the next move on the coordinate in the range -1 0 +1
|
||||||
|
int x, z, dx, dz, t;
|
||||||
|
x = 0;
|
||||||
|
z = 0;
|
||||||
|
dx = 0;
|
||||||
|
dz = -1;
|
||||||
|
|
||||||
|
// We can use two type of generation scheduling
|
||||||
|
switch (CONFIG.client().worldGenerator().getGenerationPriority())
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case NEAR_FIRST:
|
||||||
|
//in the NEAR_FIRST generation scheduling we prioritize the nearest un-generated position to the player
|
||||||
|
//the chunk position to generate will be stored in a posToGenerate object
|
||||||
|
posToGenerate = new PosToGenerateContainer((byte) 10, maxDataToGenerate, playerBlockPosX, playerBlockPosZ);
|
||||||
|
|
||||||
|
int playerChunkX = LevelPosUtil.getChunkPos(LodUtil.BLOCK_DETAIL_LEVEL, playerBlockPosX);
|
||||||
|
int playerChunkZ = LevelPosUtil.getChunkPos(LodUtil.BLOCK_DETAIL_LEVEL, playerBlockPosZ);
|
||||||
|
|
||||||
|
int complexity;
|
||||||
|
int xChunkToCheck;
|
||||||
|
int zChunkToCheck;
|
||||||
|
byte detailLevel;
|
||||||
|
int posX;
|
||||||
|
int posZ;
|
||||||
|
long data;
|
||||||
|
int numbChunksWide = (width) * 32;
|
||||||
|
int circleLimit = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
//posToGenerate is using an insertion sort algorithm which can become really fast if the
|
||||||
|
//original data order is almost ordered. For this reason we explore the matrix of the position to generate
|
||||||
|
//with a spiral matrix visit (a square spiral is almost ordered in the "nearest to farthest" order)
|
||||||
|
for (int i = 0; i < numbChunksWide * numbChunksWide; i++)
|
||||||
|
{
|
||||||
|
//Firstly we check if the posToGenerate has been filled
|
||||||
|
if (maxDataToGenerate == 0)
|
||||||
|
{
|
||||||
|
maxDataToGenerate--;
|
||||||
|
//if it has been filled then we set a stop distance
|
||||||
|
//the stop distance will be current distance (generically x) per square root of 2
|
||||||
|
//this would guarantee a circular generation since (Math.abs(x) * 1.41f) is the
|
||||||
|
//radius of a circle that inscribe a square
|
||||||
|
circleLimit = (int) (Math.abs(x) * 1.41f);
|
||||||
|
}
|
||||||
|
//This second if check if we reached the circleLimit decided in the previous if
|
||||||
|
//if so we stop
|
||||||
|
else if (maxDataToGenerate < 0)
|
||||||
|
{
|
||||||
|
if (circleLimit < Math.abs(x) && circleLimit < Math.abs(z))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
xChunkToCheck = x + playerChunkX;
|
||||||
|
zChunkToCheck = z + playerChunkZ;
|
||||||
|
|
||||||
|
//we get the lod region in which the chunk is present
|
||||||
|
lodRegion = getRegion(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, zChunkToCheck);
|
||||||
|
if (lodRegion == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//Now we check if the current chunk has been generated with the correct complexity
|
||||||
|
//if(lodRegion.isChunkPreGenerated(xChunkToCheck,zChunkToCheck))
|
||||||
|
// complexity = DistanceGenerationMode.SERVER.complexity;
|
||||||
|
//else
|
||||||
|
complexity = CONFIG.client().worldGenerator().getDistanceGenerationMode().complexity;
|
||||||
|
|
||||||
|
|
||||||
|
//we create the level position info of the chunk
|
||||||
|
detailLevel = lodRegion.getMinDetailLevel();
|
||||||
|
posX = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, detailLevel);
|
||||||
|
posZ = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, zChunkToCheck, detailLevel);
|
||||||
|
|
||||||
|
data = getSingleData(detailLevel, posX, posZ);
|
||||||
|
|
||||||
|
//we will generate the position only if the current generation complexity is lower than the target one.
|
||||||
|
//an un-generated area will always have 0 generation
|
||||||
|
if (DataPointUtil.getGenerationMode(data) < complexity)
|
||||||
|
{
|
||||||
|
posToGenerate.addPosToGenerate(detailLevel, posX, posZ);
|
||||||
|
if (maxDataToGenerate >= 0)
|
||||||
|
maxDataToGenerate--;
|
||||||
|
}
|
||||||
|
|
||||||
|
//with this code section we find the next chunk to check
|
||||||
|
if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z)))
|
||||||
|
{
|
||||||
|
t = dx;
|
||||||
|
dx = -dz;
|
||||||
|
dz = t;
|
||||||
|
}
|
||||||
|
x += dx;
|
||||||
|
z += dz;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case FAR_FIRST:
|
||||||
|
//in the FAR_FIRST generation we dedicate part of the generation process to the far region with really
|
||||||
|
//low detail quality.
|
||||||
|
|
||||||
|
posToGenerate = new PosToGenerateContainer((byte) 8, maxDataToGenerate, playerBlockPosX, playerBlockPosZ);
|
||||||
|
|
||||||
|
int xRegion;
|
||||||
|
int zRegion;
|
||||||
|
|
||||||
|
for (int i = 0; i < width * width; i++)
|
||||||
|
{
|
||||||
|
xRegion = x + center.x;
|
||||||
|
zRegion = z + center.z;
|
||||||
|
|
||||||
|
//All of this is handled directly by the region, which scan every pos from top to bottom of the quad tree
|
||||||
|
lodRegion = getRegion(xRegion, zRegion);
|
||||||
|
if (lodRegion != null)
|
||||||
|
lodRegion.getPosToGenerate(posToGenerate, playerBlockPosX, playerBlockPosZ);
|
||||||
|
|
||||||
|
|
||||||
|
//with this code section we find the next chunk to check
|
||||||
|
if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z)))
|
||||||
|
{
|
||||||
|
t = dx;
|
||||||
|
dx = -dz;
|
||||||
|
dz = t;
|
||||||
|
}
|
||||||
|
x += dx;
|
||||||
|
z += dz;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return posToGenerate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills the posToRender with the position to render for the regionPos given in input
|
||||||
|
*/
|
||||||
|
public void getPosToRender(PosToRenderContainer posToRender, RegionPos regionPos, int playerPosX,
|
||||||
|
int playerPosZ)
|
||||||
|
{
|
||||||
|
LodRegion region = getRegion(regionPos.x, regionPos.z);
|
||||||
|
|
||||||
|
// use FAR_FIRST on local worlds and NEAR_FIRST on servers
|
||||||
|
GenerationPriority generationPriority = CONFIG.client().worldGenerator().getGenerationPriority() == GenerationPriority.AUTO && MC.hasSinglePlayerServer() ? GenerationPriority.FAR_FIRST : GenerationPriority.NEAR_FIRST;
|
||||||
|
boolean requireCorrectDetailLevel = generationPriority == GenerationPriority.NEAR_FIRST;
|
||||||
|
|
||||||
|
if (region != null)
|
||||||
|
region.getPosToRender(posToRender, playerPosX, playerPosZ, requireCorrectDetailLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines how many vertical LODs could be used
|
||||||
|
* for the given region at the given detail level
|
||||||
|
*/
|
||||||
|
public int getMaxVerticalData(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||||
|
throw new IllegalArgumentException("getMaxVerticalData given a level of [" + detailLevel + "] when [" + LodUtil.REGION_DETAIL_LEVEL + "] is the max.");
|
||||||
|
|
||||||
|
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||||
|
if (region == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return region.getMaxVerticalData(detailLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data point at the given X and Z coordinates
|
||||||
|
* in this dimension.
|
||||||
|
* <br>
|
||||||
|
* Returns null if the LodChunk doesn't exist or
|
||||||
|
* is outside the loaded area.
|
||||||
|
*/
|
||||||
|
public long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
|
||||||
|
{
|
||||||
|
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||||
|
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||||
|
|
||||||
|
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||||
|
if (region == null)
|
||||||
|
return DataPointUtil.EMPTY_DATA;
|
||||||
|
|
||||||
|
return region.getData(detailLevel, posX, posZ, verticalIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data point at the given X and Z coordinates
|
||||||
|
* in this dimension.
|
||||||
|
* <br>
|
||||||
|
* Returns null if the LodChunk doesn't exist or
|
||||||
|
* is outside the loaded area.
|
||||||
|
*/
|
||||||
|
public long getSingleData(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||||
|
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||||
|
|
||||||
|
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||||
|
if (region == null)
|
||||||
|
return DataPointUtil.EMPTY_DATA;
|
||||||
|
|
||||||
|
return region.getSingleData(detailLevel, posX, posZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clears the given region */
|
||||||
|
public void clear(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||||
|
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||||
|
|
||||||
|
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||||
|
if (region == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
region.clear(detailLevel, posX, posZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if the buffer at the given array index needs
|
||||||
|
* to have its buffer regenerated.
|
||||||
|
*/
|
||||||
|
public boolean doesRegionNeedBufferRegen(int xIndex, int zIndex)
|
||||||
|
{
|
||||||
|
return regenRegionBuffer[xIndex][zIndex] || recreateRegionBuffer[xIndex][zIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets if the buffer at the given array index needs
|
||||||
|
* to have its buffer regenerated.
|
||||||
|
*/
|
||||||
|
public void setRegenRegionBufferByArrayIndex(int xIndex, int zIndex, boolean newRegen)
|
||||||
|
{
|
||||||
|
regenRegionBuffer[xIndex][zIndex] = newRegen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data point at the given LevelPos
|
||||||
|
* in this dimension.
|
||||||
|
* <br>
|
||||||
|
* Returns null if the LodChunk doesn't exist or
|
||||||
|
* is outside the loaded area.
|
||||||
|
*/
|
||||||
|
public void updateData(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||||
|
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||||
|
|
||||||
|
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||||
|
if (region == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
region.updateArea(detailLevel, posX, posZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if a region exists at the given LevelPos */
|
||||||
|
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||||
|
return region != null && region.doesDataExist(detailLevel, posX, posZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the region at the given RegionPos from file,
|
||||||
|
* if a file exists for that region.
|
||||||
|
*/
|
||||||
|
public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel,
|
||||||
|
DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
|
||||||
|
{
|
||||||
|
return fileHandler != null ? fileHandler.loadRegionFromFile(detailLevel, regionPos, generationMode, verticalQuality) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Save all dirty regions in this LodDimension to file. */
|
||||||
|
public void saveDirtyRegionsToFileAsync()
|
||||||
|
{
|
||||||
|
fileHandler.saveDirtyRegionsToFileAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Return true if the chunk has been pregenerated in game */
|
||||||
|
//public boolean isChunkPreGenerated(int xChunkPosWrapper, int zChunkPosWrapper)
|
||||||
|
//{
|
||||||
|
//
|
||||||
|
// LodRegion region = getRegion(LodUtil.CHUNK_DETAIL_LEVEL, xChunkPosWrapper, zChunkPosWrapper);
|
||||||
|
// if (region == null)
|
||||||
|
// return false;
|
||||||
|
//
|
||||||
|
// return region.isChunkPreGenerated(xChunkPosWrapper, zChunkPosWrapper);
|
||||||
|
//}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the region at the given RegionPos
|
||||||
|
* is within the loaded range.
|
||||||
|
*/
|
||||||
|
public boolean regionIsInRange(int regionX, int regionZ)
|
||||||
|
{
|
||||||
|
int xIndex = (regionX - center.x) + halfWidth;
|
||||||
|
int zIndex = (regionZ - center.z) + halfWidth;
|
||||||
|
|
||||||
|
return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the dimension's center region position X value */
|
||||||
|
public int getCenterRegionPosX()
|
||||||
|
{
|
||||||
|
return center.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the dimension's center region position Z value */
|
||||||
|
public int getCenterRegionPosZ()
|
||||||
|
{
|
||||||
|
return center.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** returns the width of the dimension in regions */
|
||||||
|
public int getWidth()
|
||||||
|
{
|
||||||
|
// we want to get the length directly from the
|
||||||
|
// source to make sure it is in sync with region
|
||||||
|
// and isRegionDirty
|
||||||
|
return regions != null ? regions.length : width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update the width of this dimension, in regions */
|
||||||
|
public void setRegionWidth(int newWidth)
|
||||||
|
{
|
||||||
|
width = newWidth;
|
||||||
|
halfWidth = width/ 2;
|
||||||
|
|
||||||
|
regions = new LodRegion[width][width];
|
||||||
|
isRegionDirty = new boolean[width][width];
|
||||||
|
regenRegionBuffer = new boolean[width][width];
|
||||||
|
recreateRegionBuffer = new boolean[width][width];
|
||||||
|
|
||||||
|
// populate isRegionDirty
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
for (int j = 0; j < width; j++)
|
||||||
|
isRegionDirty[i][j] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.append("Dimension : \n");
|
||||||
|
for (LodRegion[] lodRegions : regions)
|
||||||
|
{
|
||||||
|
for (LodRegion region : lodRegions)
|
||||||
|
{
|
||||||
|
if (region == null)
|
||||||
|
stringBuilder.append("n");
|
||||||
|
else
|
||||||
|
stringBuilder.append(region.getMinDetailLevel());
|
||||||
|
stringBuilder.append("\t");
|
||||||
|
}
|
||||||
|
stringBuilder.append("\n");
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean GetIsRegionDirty(int i, int j)
|
||||||
|
{
|
||||||
|
return isRegionDirty[i][j];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetIsRegionDirty(int i, int j, boolean val)
|
||||||
|
{
|
||||||
|
isRegionDirty[i][j] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,611 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects.lod;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||||
|
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||||
|
import com.seibel.lod.core.objects.PosToGenerateContainer;
|
||||||
|
import com.seibel.lod.core.objects.PosToRenderContainer;
|
||||||
|
import com.seibel.lod.core.util.DataPointUtil;
|
||||||
|
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||||
|
import com.seibel.lod.core.util.LevelPosUtil;
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object holds all loaded LevelContainers acting as a quad tree
|
||||||
|
* for a given region. <Br><Br>
|
||||||
|
*
|
||||||
|
* <strong>Coordinate Standard: </strong><br>
|
||||||
|
* Coordinate called posX or posZ are relative LevelPos coordinates <br>
|
||||||
|
* unless stated otherwise. <br>
|
||||||
|
*
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 10-10-2021
|
||||||
|
*/
|
||||||
|
public class LodRegion
|
||||||
|
{
|
||||||
|
/** Number of detail level supported by a region */
|
||||||
|
private static final byte POSSIBLE_LOD = 10;
|
||||||
|
|
||||||
|
|
||||||
|
/** Holds the lowest (least detailed) detail level in this region */
|
||||||
|
private byte minDetailLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This holds all data for this region
|
||||||
|
*/
|
||||||
|
private final LevelContainer[] dataContainer;
|
||||||
|
|
||||||
|
/** This chunk Pos has been generated */
|
||||||
|
//private final boolean[] preGeneratedChunkPos;
|
||||||
|
|
||||||
|
/** the generation mode for this region */
|
||||||
|
private final DistanceGenerationMode generationMode;
|
||||||
|
/** the vertical quality of this region */
|
||||||
|
private final VerticalQuality verticalQuality;
|
||||||
|
|
||||||
|
/** this region's x RegionPos */
|
||||||
|
public final int regionPosX;
|
||||||
|
/** this region's z RegionPos */
|
||||||
|
public final int regionPosZ;
|
||||||
|
|
||||||
|
public LodRegion(byte minDetailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
|
||||||
|
{
|
||||||
|
this.minDetailLevel = minDetailLevel;
|
||||||
|
this.regionPosX = regionPos.x;
|
||||||
|
this.regionPosZ = regionPos.z;
|
||||||
|
this.verticalQuality = verticalQuality;
|
||||||
|
this.generationMode = generationMode;
|
||||||
|
dataContainer = new LevelContainer[POSSIBLE_LOD];
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize all the different matrices
|
||||||
|
for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
|
||||||
|
{
|
||||||
|
dataContainer[lod] = new VerticalLevelContainer(lod);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean fileFound = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
preGeneratedChunkPos = new boolean[32 * 32];
|
||||||
|
if (MinecraftWrapper.INSTANCE.hasSinglePlayerServer() && LodConfig.CLIENT.worldGenerator.useExperimentalPreGenLoading.get())
|
||||||
|
{
|
||||||
|
File regionFileDirHead;
|
||||||
|
File regionFileDirParent;
|
||||||
|
// local world
|
||||||
|
|
||||||
|
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(MinecraftWrapper.INSTANCE.getCurrentDimension());
|
||||||
|
|
||||||
|
// provider needs a separate variable to prevent
|
||||||
|
// the compiler from complaining
|
||||||
|
StringBuilder string = new StringBuilder();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ServerChunkProvider provider = serverWorld.getChunkSource();
|
||||||
|
|
||||||
|
//System.out.println(provider.dataStorage.dataFolder);
|
||||||
|
regionFileDirHead = new File(provider.dataStorage.dataFolder.getCanonicalFile().getParentFile().toPath().toAbsolutePath().toString() + File.separatorChar + "region", "r." + regionPosZ + "." + regionPosX + ".mca");
|
||||||
|
if (regionFileDirHead.exists())
|
||||||
|
{
|
||||||
|
regionFileDirParent = regionFileDirHead.getParentFile();
|
||||||
|
//string.append(regionFileDirParent.toString());
|
||||||
|
string.append(regionFileDirHead);
|
||||||
|
RegionFile regionFile = new RegionFile(regionFileDirHead, regionFileDirParent, true);
|
||||||
|
for (int x = 0; x < 32; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < 32; z++)
|
||||||
|
{
|
||||||
|
preGeneratedChunkPos[x * 32 + z] = regionFile.doesChunkExist(new ChunkPos(regionPosX * 32 + x, regionPosZ * 32 + z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string.append("region " + regionPosX + " " + regionPosZ + "\n");
|
||||||
|
for (int x = 0; x < 32; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < 32; z++)
|
||||||
|
{
|
||||||
|
//regionFile.doesChunkExist()
|
||||||
|
string.append(preGeneratedChunkPos[x * 32 + z] + "\t");
|
||||||
|
}
|
||||||
|
string.append("\n");
|
||||||
|
}
|
||||||
|
regionFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
System.out.println(string);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Return true if the chunk has been pregenerated in game */
|
||||||
|
//public boolean isChunkPreGenerated(int xChunkPos, int zChunkPos)
|
||||||
|
//{
|
||||||
|
// xChunkPos = LevelPosUtil.getRegionModule(LodUtil.CHUNK_DETAIL_LEVEL, xChunkPos);
|
||||||
|
// zChunkPos = LevelPosUtil.getRegionModule(LodUtil.CHUNK_DETAIL_LEVEL, zChunkPos);
|
||||||
|
// return preGeneratedChunkPos[xChunkPos * 32 + zChunkPos];
|
||||||
|
//}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the data point into the region.
|
||||||
|
* <p>
|
||||||
|
* TODO this will always return true unless it has
|
||||||
|
* @return true if the data was added successfully
|
||||||
|
*/
|
||||||
|
public boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data)
|
||||||
|
{
|
||||||
|
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||||
|
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||||
|
|
||||||
|
// The dataContainer could have null entries if the
|
||||||
|
// detailLevel changes.
|
||||||
|
if (this.dataContainer[detailLevel] == null)
|
||||||
|
{
|
||||||
|
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataContainer[detailLevel].addData(data, posX, posZ, verticalIndex);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the vertical data into the region.
|
||||||
|
* <p>
|
||||||
|
* TODO this will always return true unless it has
|
||||||
|
* @return true if the data was added successfully
|
||||||
|
*/
|
||||||
|
public boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data)
|
||||||
|
{
|
||||||
|
//position is already relative
|
||||||
|
//posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||||
|
//posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||||
|
|
||||||
|
// The dataContainer could have null entries if the
|
||||||
|
// detailLevel changes.
|
||||||
|
if (this.dataContainer[detailLevel] == null)
|
||||||
|
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
|
||||||
|
|
||||||
|
return this.dataContainer[detailLevel].addVerticalData(data, posX, posZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dataPoint at the given relative position.
|
||||||
|
* @return the data at the relative pos and detail level,
|
||||||
|
* 0 if the data doesn't exist.
|
||||||
|
*/
|
||||||
|
public long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
|
||||||
|
{
|
||||||
|
return dataContainer[detailLevel].getData(posX, posZ, verticalIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dataPoint at the given relative position.
|
||||||
|
* @return the data at the relative pos and detail level,
|
||||||
|
* 0 if the data doesn't exist.
|
||||||
|
*/
|
||||||
|
public long getSingleData(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
return dataContainer[detailLevel].getSingleData(posX, posZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the datapoint at the given relative position
|
||||||
|
*/
|
||||||
|
public void clear(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
dataContainer[detailLevel].clear(posX, posZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will fill the posToGenerate array with all levelPos that
|
||||||
|
* are render-able.
|
||||||
|
* <p>
|
||||||
|
* TODO why don't we return the posToGenerate, it would make this easier to understand
|
||||||
|
*/
|
||||||
|
public void getPosToGenerate(PosToGenerateContainer posToGenerate,
|
||||||
|
int playerBlockPosX, int playerBlockPosZ)
|
||||||
|
{
|
||||||
|
getPosToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerBlockPosX, playerBlockPosZ);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A recursive method that fills the posToGenerate array with all levelPos that
|
||||||
|
* need to be generated.
|
||||||
|
* <p>
|
||||||
|
* TODO why don't we return the posToGenerate, it would make this easier to understand
|
||||||
|
*/
|
||||||
|
private void getPosToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel,
|
||||||
|
int childOffsetPosX, int childOffsetPosZ, int playerPosX, int playerPosZ)
|
||||||
|
{
|
||||||
|
// equivalent to 2^(...)
|
||||||
|
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||||
|
|
||||||
|
// calculate what LevelPos are in range to generate
|
||||||
|
int maxDistance = LevelPosUtil.maxDistance(detailLevel, childOffsetPosX, childOffsetPosZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||||
|
|
||||||
|
// determine this child's levelPos
|
||||||
|
byte childDetailLevel = (byte) (detailLevel - 1);
|
||||||
|
int childPosX = childOffsetPosX * 2;
|
||||||
|
int childPosZ = childOffsetPosZ * 2;
|
||||||
|
|
||||||
|
int childSize = 1 << (LodUtil.REGION_DETAIL_LEVEL - childDetailLevel);
|
||||||
|
|
||||||
|
byte targetDetailLevel = DetailDistanceUtil.getLodGenDetail(DetailDistanceUtil.getGenerationDetailFromDistance(maxDistance)).detailLevel;
|
||||||
|
if (targetDetailLevel <= detailLevel)
|
||||||
|
{
|
||||||
|
if (targetDetailLevel == detailLevel)
|
||||||
|
{
|
||||||
|
if (!doesDataExist(detailLevel, childOffsetPosX, childOffsetPosZ))
|
||||||
|
posToGenerate.addPosToGenerate(detailLevel, childOffsetPosX + regionPosX * size, childOffsetPosZ + regionPosZ * size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// we want at max one request per chunk (since the world generator creates chunks).
|
||||||
|
// So for lod smaller than a chunk, only recurse down
|
||||||
|
// the top right child
|
||||||
|
|
||||||
|
if (detailLevel > LodUtil.CHUNK_DETAIL_LEVEL)
|
||||||
|
{
|
||||||
|
int ungeneratedChildren = 0;
|
||||||
|
|
||||||
|
// make sure all children are generated to this detailLevel
|
||||||
|
for (int x = 0; x <= 1; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z <= 1; z++)
|
||||||
|
{
|
||||||
|
if (!doesDataExist(childDetailLevel, childPosX + x, childPosZ + z))
|
||||||
|
{
|
||||||
|
ungeneratedChildren++;
|
||||||
|
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + x + regionPosX * childSize, childPosZ + z + regionPosZ * childSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only if all the children are correctly generated
|
||||||
|
// should we go deeper
|
||||||
|
if (ungeneratedChildren == 0)
|
||||||
|
for (int x = 0; x <= 1; x++)
|
||||||
|
for (int z = 0; z <= 1; z++)
|
||||||
|
getPosToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The detail Level is smaller than a chunk.
|
||||||
|
// Only recurse down the top right child.
|
||||||
|
|
||||||
|
if (DetailDistanceUtil.getLodGenDetail(childDetailLevel).detailLevel <= (childDetailLevel))
|
||||||
|
{
|
||||||
|
if (!doesDataExist(childDetailLevel, childPosX, childPosZ))
|
||||||
|
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + regionPosX * childSize, childPosZ + regionPosZ * childSize);
|
||||||
|
else
|
||||||
|
getPosToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we have gone beyond the target Detail level
|
||||||
|
// we can stop generating
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will fill the posToRender array with all levelPos that
|
||||||
|
* are render-able.
|
||||||
|
* <p>
|
||||||
|
* TODO why don't we return the posToRender, it would make this easier to understand
|
||||||
|
*/
|
||||||
|
public void getPosToRender(PosToRenderContainer posToRender,
|
||||||
|
int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
|
||||||
|
{
|
||||||
|
getPosToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ, requireCorrectDetailLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will fill the posToRender array with all levelPos that
|
||||||
|
* are render-able.
|
||||||
|
* <p>
|
||||||
|
* TODO why don't we return the posToRender, it would make this easier to understand
|
||||||
|
* TODO this needs some more comments, James was only able to figure out part of it
|
||||||
|
*/
|
||||||
|
private void getPosToRender(PosToRenderContainer posToRender,
|
||||||
|
byte detailLevel, int posX, int posZ,
|
||||||
|
int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
|
||||||
|
{
|
||||||
|
// equivalent to 2^(...)
|
||||||
|
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||||
|
|
||||||
|
byte desiredLevel;
|
||||||
|
int maxDistance;
|
||||||
|
int minDistance;
|
||||||
|
int childLevel;
|
||||||
|
|
||||||
|
|
||||||
|
// calculate the LevelPos that are in range
|
||||||
|
maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||||
|
desiredLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance));
|
||||||
|
minDistance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||||
|
childLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(minDistance));
|
||||||
|
|
||||||
|
if (detailLevel == childLevel - 1)
|
||||||
|
{
|
||||||
|
posToRender.addPosToRender(detailLevel,
|
||||||
|
posX + regionPosX * size,
|
||||||
|
posZ + regionPosZ * size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
//if (desiredLevel > detailLevel)
|
||||||
|
//{
|
||||||
|
// we have gone beyond the target Detail level
|
||||||
|
// we can stop generating
|
||||||
|
//} else
|
||||||
|
if (desiredLevel == detailLevel)
|
||||||
|
{
|
||||||
|
posToRender.addPosToRender(detailLevel,
|
||||||
|
posX + regionPosX * size,
|
||||||
|
posZ + regionPosZ * size);
|
||||||
|
}
|
||||||
|
else //case where (detailLevel > desiredLevel)
|
||||||
|
{
|
||||||
|
int childPosX = posX * 2;
|
||||||
|
int childPosZ = posZ * 2;
|
||||||
|
byte childDetailLevel = (byte) (detailLevel - 1);
|
||||||
|
int childrenCount = 0;
|
||||||
|
|
||||||
|
for (int x = 0; x <= 1; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z <= 1; z++)
|
||||||
|
{
|
||||||
|
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z))
|
||||||
|
{
|
||||||
|
if (!requireCorrectDetailLevel)
|
||||||
|
childrenCount++;
|
||||||
|
else
|
||||||
|
getPosToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!requireCorrectDetailLevel)
|
||||||
|
{
|
||||||
|
// If all the four children exist go deeper
|
||||||
|
if (childrenCount == 4)
|
||||||
|
{
|
||||||
|
for (int x = 0; x <= 1; x++)
|
||||||
|
for (int z = 0; z <= 1; z++)
|
||||||
|
getPosToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
posToRender.addPosToRender(detailLevel,
|
||||||
|
posX + regionPosX * size,
|
||||||
|
posZ + regionPosZ * size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates all children.
|
||||||
|
* <p>
|
||||||
|
* TODO could this be renamed mergeArea?
|
||||||
|
*/
|
||||||
|
public void updateArea(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
int startX;
|
||||||
|
int startZ;
|
||||||
|
|
||||||
|
// TODO what are each of these loops updating?
|
||||||
|
for (byte down = (byte) (minDetailLevel + 1); down <= detailLevel; down++)
|
||||||
|
{
|
||||||
|
startX = LevelPosUtil.convert(detailLevel, posX, down);
|
||||||
|
startZ = LevelPosUtil.convert(detailLevel, posZ, down);
|
||||||
|
width = 1 << (detailLevel - down);
|
||||||
|
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
for (int z = 0; z < width; z++)
|
||||||
|
update(down, startX + x, startZ + z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (byte up = (byte) (detailLevel + 1); up <= LodUtil.REGION_DETAIL_LEVEL; up++)
|
||||||
|
{
|
||||||
|
update(up,
|
||||||
|
LevelPosUtil.convert(detailLevel, posX, up),
|
||||||
|
LevelPosUtil.convert(detailLevel, posZ, up));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the child at the given relative Pos
|
||||||
|
* <p>
|
||||||
|
* TODO could this be renamed mergeChildData?
|
||||||
|
*/
|
||||||
|
private void update(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
dataContainer[detailLevel].updateData(dataContainer[detailLevel - 1], posX, posZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if data exists at the given relative Pos.
|
||||||
|
*/
|
||||||
|
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
if (detailLevel < minDetailLevel)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||||
|
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||||
|
|
||||||
|
if (dataContainer[detailLevel] == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return dataContainer[detailLevel].doesItExist(posX, posZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the generation mode for the data point at the given relative pos.
|
||||||
|
*/
|
||||||
|
public byte getGenerationMode(byte detailLevel, int posX, int posZ)
|
||||||
|
{
|
||||||
|
if (dataContainer[detailLevel].doesItExist(posX, posZ))
|
||||||
|
// We take the bottom information always
|
||||||
|
// TODO what does that mean? bottom of what?
|
||||||
|
return DataPointUtil.getGenerationMode(dataContainer[detailLevel].getSingleData(posX, posZ));
|
||||||
|
else
|
||||||
|
return DistanceGenerationMode.NONE.complexity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the lowest (least detailed) detail level in this region
|
||||||
|
* TODO is that right?
|
||||||
|
*/
|
||||||
|
public byte getMinDetailLevel()
|
||||||
|
{
|
||||||
|
return minDetailLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the LevelContainer for the detailLevel
|
||||||
|
* @throws IllegalArgumentException if the detailLevel is less than minDetailLevel
|
||||||
|
*/
|
||||||
|
public LevelContainer getLevel(byte detailLevel)
|
||||||
|
{
|
||||||
|
if (detailLevel < minDetailLevel)
|
||||||
|
throw new IllegalArgumentException("getLevel asked for a detail level that does not exist: minimum: [" + minDetailLevel + "] level requested: [" + detailLevel + "]");
|
||||||
|
|
||||||
|
return dataContainer[detailLevel];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the levelContainer to this Region, updating the minDetailLevel
|
||||||
|
* if necessary.
|
||||||
|
* @throws IllegalArgumentException if the LevelContainer's detailLevel
|
||||||
|
* is 2 or more detail levels lower than the
|
||||||
|
* minDetailLevel of this region.
|
||||||
|
*/
|
||||||
|
public void addLevelContainer(LevelContainer levelContainer)
|
||||||
|
{
|
||||||
|
if (levelContainer.getDetailLevel() < minDetailLevel - 1)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"the LevelContainer's detailLevel was "
|
||||||
|
+ "[" + levelContainer.getDetailLevel() + "] but this region "
|
||||||
|
+ "only allows adding LevelContainers with a "
|
||||||
|
+ "detail level of [" + (minDetailLevel - 1) + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (levelContainer.getDetailLevel() == minDetailLevel - 1)
|
||||||
|
minDetailLevel = levelContainer.getDetailLevel();
|
||||||
|
|
||||||
|
dataContainer[levelContainer.getDetailLevel()] = levelContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO James thinks cutTree and growTree (which he renamed to match cutTree)
|
||||||
|
// should have more descriptive names, to make sure the "Tree" portion isn't
|
||||||
|
// confused with Minecraft trees (the plant).
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes any dataContainers that are higher than
|
||||||
|
* the given detailLevel
|
||||||
|
*/
|
||||||
|
public void cutTree(byte detailLevel)
|
||||||
|
{
|
||||||
|
if (detailLevel > minDetailLevel)
|
||||||
|
{
|
||||||
|
for (byte detailLevelIndex = 0; detailLevelIndex < detailLevel; detailLevelIndex++)
|
||||||
|
dataContainer[detailLevelIndex] = null;
|
||||||
|
|
||||||
|
minDetailLevel = detailLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make this region more detailed to the detailLevel given.
|
||||||
|
* TODO is that correct?
|
||||||
|
*/
|
||||||
|
public void growTree(byte detailLevel)
|
||||||
|
{
|
||||||
|
if (detailLevel < minDetailLevel)
|
||||||
|
{
|
||||||
|
for (byte detailLevelIndex = (byte) (minDetailLevel - 1); detailLevelIndex >= detailLevel; detailLevelIndex--)
|
||||||
|
{
|
||||||
|
if (dataContainer[detailLevelIndex + 1] == null)
|
||||||
|
dataContainer[detailLevelIndex + 1] = new VerticalLevelContainer((byte) (detailLevelIndex + 1));
|
||||||
|
|
||||||
|
dataContainer[detailLevelIndex] = dataContainer[detailLevelIndex + 1].expand();
|
||||||
|
}
|
||||||
|
minDetailLevel = detailLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return RegionPos of this lod region
|
||||||
|
*/
|
||||||
|
public RegionPos getRegionPos()
|
||||||
|
{
|
||||||
|
return new RegionPos(regionPosX, regionPosZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns how many LODs are in this region
|
||||||
|
*/
|
||||||
|
public int getNumberOfLods()
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (LevelContainer container : dataContainer)
|
||||||
|
count += container.getMaxNumberOfLods();
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VerticalQuality getVerticalQuality()
|
||||||
|
{
|
||||||
|
return verticalQuality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DistanceGenerationMode getGenerationMode()
|
||||||
|
{
|
||||||
|
return generationMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxVerticalData(byte detailLevel)
|
||||||
|
{
|
||||||
|
return dataContainer[detailLevel].getMaxVerticalData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects.lod;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.api.ClientApi;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This stores all LODs for a given world.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version 9-27-2021
|
||||||
|
*/
|
||||||
|
public class LodWorld
|
||||||
|
{
|
||||||
|
/** name of this world */
|
||||||
|
private String worldName;
|
||||||
|
|
||||||
|
/** dimensions in this world */
|
||||||
|
private Map<IDimensionTypeWrapper, LodDimension> lodDimensions;
|
||||||
|
|
||||||
|
/** If true then the LOD world is setup and ready to use */
|
||||||
|
private boolean isWorldLoaded = false;
|
||||||
|
|
||||||
|
/** the name given to the world if it isn't loaded */
|
||||||
|
public static final String NO_WORLD_LOADED = "No world loaded";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public LodWorld()
|
||||||
|
{
|
||||||
|
worldName = NO_WORLD_LOADED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the LodWorld with the given newWorldName. <br>
|
||||||
|
* This should be done whenever loading a new world. <br><br>
|
||||||
|
* <p>
|
||||||
|
* Note a System.gc() call may be in order after calling this <Br>
|
||||||
|
* since a lot of LOD data is now homeless. <br>
|
||||||
|
* @param newWorldName name of the world
|
||||||
|
*/
|
||||||
|
public void selectWorld(String newWorldName)
|
||||||
|
{
|
||||||
|
if (newWorldName.isEmpty())
|
||||||
|
{
|
||||||
|
deselectWorld();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (worldName.equals(newWorldName))
|
||||||
|
// don't recreate everything if we
|
||||||
|
// didn't actually change worlds
|
||||||
|
return;
|
||||||
|
|
||||||
|
worldName = newWorldName;
|
||||||
|
lodDimensions = new Hashtable<>();
|
||||||
|
isWorldLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the worldName to "No world loaded"
|
||||||
|
* and clear the lodDimensions Map. <br>
|
||||||
|
* This should be done whenever unloaded a world. <br><br>
|
||||||
|
* <p>
|
||||||
|
* Note a System.gc() call may be in order after calling this <Br>
|
||||||
|
* since a lot of LOD data is now homeless. <br>
|
||||||
|
*/
|
||||||
|
public void deselectWorld()
|
||||||
|
{
|
||||||
|
worldName = NO_WORLD_LOADED;
|
||||||
|
lodDimensions = null;
|
||||||
|
isWorldLoaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds newDimension to this world, if a LodDimension
|
||||||
|
* already exists for the given dimension it is replaced.
|
||||||
|
*/
|
||||||
|
public void addLodDimension(LodDimension newDimension)
|
||||||
|
{
|
||||||
|
if (lodDimensions == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lodDimensions.put(newDimension.dimension, newDimension);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns null if no LodDimension exists for the given dimension
|
||||||
|
*/
|
||||||
|
public LodDimension getLodDimension(IDimensionTypeWrapper dimType)
|
||||||
|
{
|
||||||
|
if (lodDimensions == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return lodDimensions.get(dimType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizes the max width in regions that each LodDimension
|
||||||
|
* should use.
|
||||||
|
*/
|
||||||
|
public void resizeDimensionRegionWidth(int newRegionWidth)
|
||||||
|
{
|
||||||
|
if (lodDimensions == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
saveAllDimensions();
|
||||||
|
|
||||||
|
for (IDimensionTypeWrapper key : lodDimensions.keySet())
|
||||||
|
lodDimensions.get(key).setRegionWidth(newRegionWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests all dimensions save any dirty regions they may have.
|
||||||
|
*/
|
||||||
|
public void saveAllDimensions()
|
||||||
|
{
|
||||||
|
if (lodDimensions == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO we should only print this if lods were actually saved to file
|
||||||
|
// but that requires a LodDimension.hasDirtyRegions() method or something similar
|
||||||
|
ClientApi.LOGGER.info("Saving LODs");
|
||||||
|
|
||||||
|
for (IDimensionTypeWrapper key : lodDimensions.keySet())
|
||||||
|
lodDimensions.get(key).saveDirtyRegionsToFileAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean getIsWorldNotLoaded()
|
||||||
|
{
|
||||||
|
return !isWorldLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWorldName()
|
||||||
|
{
|
||||||
|
return worldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "World name: " + worldName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects.lod;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
import com.seibel.lod.core.util.SingletonHandler;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||||
|
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is similar to ChunkPos or BlockPos.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 8-21-2021
|
||||||
|
*/
|
||||||
|
public class RegionPos
|
||||||
|
{
|
||||||
|
private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||||
|
|
||||||
|
|
||||||
|
public int x;
|
||||||
|
public int z;
|
||||||
|
|
||||||
|
|
||||||
|
/** Sets x and z to 0 */
|
||||||
|
public RegionPos()
|
||||||
|
{
|
||||||
|
x = 0;
|
||||||
|
z = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** simple constructor that sets x and z to new x and z. */
|
||||||
|
public RegionPos(int newX, int newZ)
|
||||||
|
{
|
||||||
|
x = newX;
|
||||||
|
z = newZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts from a BlockPos to a RegionPos */
|
||||||
|
public RegionPos(AbstractBlockPosWrapper pos)
|
||||||
|
{
|
||||||
|
this(WRAPPER_FACTORY.createChunkPos(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts from a ChunkPos to a RegionPos */
|
||||||
|
public RegionPos(AbstractChunkPosWrapper pos)
|
||||||
|
{
|
||||||
|
x = Math.floorDiv(pos.getX(), LodUtil.REGION_WIDTH_IN_CHUNKS);
|
||||||
|
z = Math.floorDiv(pos.getZ(), LodUtil.REGION_WIDTH_IN_CHUNKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the ChunkPos at the center of this region */
|
||||||
|
public AbstractChunkPosWrapper chunkPos()
|
||||||
|
{
|
||||||
|
return WRAPPER_FACTORY.createChunkPos(
|
||||||
|
(x * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2,
|
||||||
|
(z * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the BlockPos at the center of this region */
|
||||||
|
public AbstractBlockPosWrapper blockPos()
|
||||||
|
{
|
||||||
|
return chunkPos().getWorldPosition()
|
||||||
|
.offset(LodUtil.CHUNK_WIDTH / 2, 0, LodUtil.CHUNK_WIDTH / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "(" + x + "," + z + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects.lod;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.util.DataPointUtil;
|
||||||
|
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||||
|
import com.seibel.lod.core.util.LevelPosUtil;
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Leonardo Amato
|
||||||
|
* @version ??
|
||||||
|
*/
|
||||||
|
public class VerticalLevelContainer implements LevelContainer
|
||||||
|
{
|
||||||
|
|
||||||
|
public final byte detailLevel;
|
||||||
|
public final int size;
|
||||||
|
public final int maxVerticalData;
|
||||||
|
|
||||||
|
public final long[] dataContainer;
|
||||||
|
|
||||||
|
public VerticalLevelContainer(byte detailLevel)
|
||||||
|
{
|
||||||
|
this.detailLevel = detailLevel;
|
||||||
|
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||||
|
maxVerticalData = DetailDistanceUtil.getMaxVerticalData(detailLevel);
|
||||||
|
dataContainer = new long[size * size * DetailDistanceUtil.getMaxVerticalData(detailLevel)];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte getDetailLevel()
|
||||||
|
{
|
||||||
|
return detailLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear(int posX, int posZ)
|
||||||
|
{
|
||||||
|
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||||
|
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||||
|
for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++)
|
||||||
|
{
|
||||||
|
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = DataPointUtil.EMPTY_DATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addData(long data, int posX, int posZ, int verticalIndex)
|
||||||
|
{
|
||||||
|
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||||
|
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||||
|
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addVerticalData(long[] data, int posX, int posZ)
|
||||||
|
{
|
||||||
|
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||||
|
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||||
|
for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++)
|
||||||
|
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data[verticalIndex];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addSingleData(long data, int posX, int posZ)
|
||||||
|
{
|
||||||
|
return addData(data, posX, posZ, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getData(int posX, int posZ, int verticalIndex)
|
||||||
|
{
|
||||||
|
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||||
|
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||||
|
return dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSingleData(int posX, int posZ)
|
||||||
|
{
|
||||||
|
return getData(posX, posZ, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxVerticalData()
|
||||||
|
{
|
||||||
|
return maxVerticalData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize()
|
||||||
|
{
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doesItExist(int posX, int posZ)
|
||||||
|
{
|
||||||
|
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||||
|
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||||
|
return DataPointUtil.doesItExist(getSingleData(posX, posZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
public VerticalLevelContainer(byte[] inputData, int version)
|
||||||
|
{
|
||||||
|
int tempMaxVerticalData;
|
||||||
|
int tempIndex;
|
||||||
|
int index = 0;
|
||||||
|
long newData;
|
||||||
|
detailLevel = inputData[index];
|
||||||
|
index++;
|
||||||
|
tempMaxVerticalData = inputData[index] & 0b01111111;
|
||||||
|
index++;
|
||||||
|
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||||
|
int x = size * size * tempMaxVerticalData;
|
||||||
|
long[] tempDataContainer = new long[x];
|
||||||
|
|
||||||
|
if (version == 6)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < x; i++)
|
||||||
|
{
|
||||||
|
newData = 0;
|
||||||
|
for (tempIndex = 0; tempIndex < 8; tempIndex++)
|
||||||
|
newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
|
||||||
|
index += 8;
|
||||||
|
|
||||||
|
newData = DataPointUtil.createDataPoint(
|
||||||
|
DataPointUtil.getAlpha(newData),
|
||||||
|
DataPointUtil.getRed(newData),
|
||||||
|
DataPointUtil.getGreen(newData),
|
||||||
|
DataPointUtil.getBlue(newData),
|
||||||
|
DataPointUtil.getHeight(newData) - DataPointUtil.VERTICAL_OFFSET,
|
||||||
|
DataPointUtil.getDepth(newData) - DataPointUtil.VERTICAL_OFFSET,
|
||||||
|
DataPointUtil.getLightSky(newData),
|
||||||
|
DataPointUtil.getLightBlock(newData),
|
||||||
|
DataPointUtil.getGenerationMode(newData),
|
||||||
|
DataPointUtil.getFlag(newData)
|
||||||
|
);
|
||||||
|
tempDataContainer[i] = newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //if (version == 7)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < x; i++)
|
||||||
|
{
|
||||||
|
newData = 0;
|
||||||
|
for (tempIndex = 0; tempIndex < 8; tempIndex++)
|
||||||
|
newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
|
||||||
|
index += 8;
|
||||||
|
tempDataContainer[i] = newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempMaxVerticalData > DetailDistanceUtil.getMaxVerticalData(detailLevel))
|
||||||
|
{
|
||||||
|
int tempMaxVerticalData2 = DetailDistanceUtil.getMaxVerticalData(detailLevel);
|
||||||
|
long[] dataToMerge = new long[tempMaxVerticalData];
|
||||||
|
long[] tempDataContainer2 = new long[size * size * tempMaxVerticalData2];
|
||||||
|
for (int i = 0; i < size * size; i++)
|
||||||
|
{
|
||||||
|
System.arraycopy(tempDataContainer, i * tempMaxVerticalData, dataToMerge, 0, tempMaxVerticalData);
|
||||||
|
dataToMerge = DataPointUtil.mergeMultiData(dataToMerge, tempMaxVerticalData, tempMaxVerticalData2);
|
||||||
|
System.arraycopy(dataToMerge, 0, tempDataContainer2, i * tempMaxVerticalData2, tempMaxVerticalData2);
|
||||||
|
}
|
||||||
|
maxVerticalData = tempMaxVerticalData2;
|
||||||
|
this.dataContainer = tempDataContainer2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
maxVerticalData = tempMaxVerticalData;
|
||||||
|
this.dataContainer = tempDataContainer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LevelContainer expand()
|
||||||
|
{
|
||||||
|
return new VerticalLevelContainer((byte) (getDetailLevel() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateData(LevelContainer lowerLevelContainer, int posX, int posZ)
|
||||||
|
{
|
||||||
|
//We reset the array
|
||||||
|
long[] dataToMerge = ThreadMapUtil.getVerticalUpdateArray(detailLevel);
|
||||||
|
|
||||||
|
int lowerMaxVertical = dataToMerge.length / 4;
|
||||||
|
int childPosX;
|
||||||
|
int childPosZ;
|
||||||
|
long[] data;
|
||||||
|
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||||
|
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||||
|
for (int x = 0; x <= 1; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z <= 1; z++)
|
||||||
|
{
|
||||||
|
childPosX = 2 * posX + x;
|
||||||
|
childPosZ = 2 * posZ + z;
|
||||||
|
for (int verticalIndex = 0; verticalIndex < lowerMaxVertical; verticalIndex++)
|
||||||
|
dataToMerge[(z * 2 + x) * lowerMaxVertical + verticalIndex] = lowerLevelContainer.getData(childPosX, childPosZ, verticalIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data = DataPointUtil.mergeMultiData(dataToMerge, lowerMaxVertical, getMaxVerticalData());
|
||||||
|
|
||||||
|
addVerticalData(data, posX, posZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] toDataString()
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
int x = size * size;
|
||||||
|
int tempIndex;
|
||||||
|
long current;
|
||||||
|
boolean allGenerated = true;
|
||||||
|
byte[] tempData = ThreadMapUtil.getSaveContainer(detailLevel);
|
||||||
|
|
||||||
|
tempData[index] = detailLevel;
|
||||||
|
index++;
|
||||||
|
tempData[index] = (byte) maxVerticalData;
|
||||||
|
index++;
|
||||||
|
int j;
|
||||||
|
for (int i = 0; i < x; i++)
|
||||||
|
{
|
||||||
|
for (j = 0; j < maxVerticalData; j++)
|
||||||
|
{
|
||||||
|
current = dataContainer[i * maxVerticalData + j];
|
||||||
|
for (tempIndex = 0; tempIndex < 8; tempIndex++)
|
||||||
|
tempData[index + tempIndex] = (byte) (current >>> (8 * tempIndex));
|
||||||
|
index += 8;
|
||||||
|
}
|
||||||
|
if(!DataPointUtil.doesItExist(dataContainer[i]))
|
||||||
|
allGenerated = false;
|
||||||
|
}
|
||||||
|
if (allGenerated)
|
||||||
|
tempData[1] |= 0b10000000;
|
||||||
|
return tempData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||||
|
stringBuilder.append(detailLevel);
|
||||||
|
stringBuilder.append(DATA_DELIMITER);
|
||||||
|
for (int x = 0; x < size; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < size; z++)
|
||||||
|
{
|
||||||
|
//Converting the dataToHex
|
||||||
|
stringBuilder.append(Long.toHexString(dataContainer[x][z][0]));
|
||||||
|
stringBuilder.append(DATA_DELIMITER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
*/
|
||||||
|
return " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxNumberOfLods()
|
||||||
|
{
|
||||||
|
return size * size * getMaxVerticalData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,543 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects.math;
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A (almost) exact copy of Minecraft's 1.16.5
|
||||||
|
* implementation of a 4x4 float matrix.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-11-2021
|
||||||
|
*/
|
||||||
|
public class Mat4f
|
||||||
|
{
|
||||||
|
private float m00;
|
||||||
|
private float m01;
|
||||||
|
private float m02;
|
||||||
|
private float m03;
|
||||||
|
private float m10;
|
||||||
|
private float m11;
|
||||||
|
private float m12;
|
||||||
|
private float m13;
|
||||||
|
private float m20;
|
||||||
|
private float m21;
|
||||||
|
private float m22;
|
||||||
|
private float m23;
|
||||||
|
private float m30;
|
||||||
|
private float m31;
|
||||||
|
private float m32;
|
||||||
|
private float m33;
|
||||||
|
|
||||||
|
|
||||||
|
public Mat4f()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mat4f(Mat4f sourceMatrix)
|
||||||
|
{
|
||||||
|
this.m00 = sourceMatrix.m00;
|
||||||
|
this.m01 = sourceMatrix.m01;
|
||||||
|
this.m02 = sourceMatrix.m02;
|
||||||
|
this.m03 = sourceMatrix.m03;
|
||||||
|
this.m10 = sourceMatrix.m10;
|
||||||
|
this.m11 = sourceMatrix.m11;
|
||||||
|
this.m12 = sourceMatrix.m12;
|
||||||
|
this.m13 = sourceMatrix.m13;
|
||||||
|
this.m20 = sourceMatrix.m20;
|
||||||
|
this.m21 = sourceMatrix.m21;
|
||||||
|
this.m22 = sourceMatrix.m22;
|
||||||
|
this.m23 = sourceMatrix.m23;
|
||||||
|
this.m30 = sourceMatrix.m30;
|
||||||
|
this.m31 = sourceMatrix.m31;
|
||||||
|
this.m32 = sourceMatrix.m32;
|
||||||
|
this.m33 = sourceMatrix.m33;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quaternions are not currently needed/implemented
|
||||||
|
public Matrix4float(Quaternion p_i48104_1_)
|
||||||
|
{
|
||||||
|
float f = p_i48104_1_.i();
|
||||||
|
float f1 = p_i48104_1_.j();
|
||||||
|
float f2 = p_i48104_1_.k();
|
||||||
|
float f3 = p_i48104_1_.r();
|
||||||
|
float f4 = 2.0F * f * f;
|
||||||
|
float f5 = 2.0F * f1 * f1;
|
||||||
|
float f6 = 2.0F * f2 * f2;
|
||||||
|
this.m00 = 1.0F - f5 - f6;
|
||||||
|
this.m11 = 1.0F - f6 - f4;
|
||||||
|
this.m22 = 1.0F - f4 - f5;
|
||||||
|
this.m33 = 1.0F;
|
||||||
|
float f7 = f * f1;
|
||||||
|
float f8 = f1 * f2;
|
||||||
|
float f9 = f2 * f;
|
||||||
|
float f10 = f * f3;
|
||||||
|
float f11 = f1 * f3;
|
||||||
|
float f12 = f2 * f3;
|
||||||
|
this.m10 = 2.0F * (f7 + f12);
|
||||||
|
this.m01 = 2.0F * (f7 - f12);
|
||||||
|
this.m20 = 2.0F * (f9 - f11);
|
||||||
|
this.m02 = 2.0F * (f9 + f11);
|
||||||
|
this.m21 = 2.0F * (f8 + f10);
|
||||||
|
this.m12 = 2.0F * (f8 - f10);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if (this == obj)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (obj != null && this.getClass() == obj.getClass())
|
||||||
|
{
|
||||||
|
Mat4f otherMatrix = (Mat4f) obj;
|
||||||
|
return Float.compare(otherMatrix.m00, this.m00) == 0
|
||||||
|
&& Float.compare(otherMatrix.m01, this.m01) == 0
|
||||||
|
&& Float.compare(otherMatrix.m02, this.m02) == 0
|
||||||
|
&& Float.compare(otherMatrix.m03, this.m03) == 0
|
||||||
|
&& Float.compare(otherMatrix.m10, this.m10) == 0
|
||||||
|
&& Float.compare(otherMatrix.m11, this.m11) == 0
|
||||||
|
&& Float.compare(otherMatrix.m12, this.m12) == 0
|
||||||
|
&& Float.compare(otherMatrix.m13, this.m13) == 0
|
||||||
|
&& Float.compare(otherMatrix.m20, this.m20) == 0
|
||||||
|
&& Float.compare(otherMatrix.m21, this.m21) == 0
|
||||||
|
&& Float.compare(otherMatrix.m22, this.m22) == 0
|
||||||
|
&& Float.compare(otherMatrix.m23, this.m23) == 0
|
||||||
|
&& Float.compare(otherMatrix.m30, this.m30) == 0
|
||||||
|
&& Float.compare(otherMatrix.m31, this.m31) == 0
|
||||||
|
&& Float.compare(otherMatrix.m32, this.m32) == 0
|
||||||
|
&& Float.compare(otherMatrix.m33, this.m33) == 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
int i = this.m00 != 0.0F ? Float.floatToIntBits(this.m00) : 0;
|
||||||
|
i = 31 * i + (this.m01 != 0.0F ? Float.floatToIntBits(this.m01) : 0);
|
||||||
|
i = 31 * i + (this.m02 != 0.0F ? Float.floatToIntBits(this.m02) : 0);
|
||||||
|
i = 31 * i + (this.m03 != 0.0F ? Float.floatToIntBits(this.m03) : 0);
|
||||||
|
i = 31 * i + (this.m10 != 0.0F ? Float.floatToIntBits(this.m10) : 0);
|
||||||
|
i = 31 * i + (this.m11 != 0.0F ? Float.floatToIntBits(this.m11) : 0);
|
||||||
|
i = 31 * i + (this.m12 != 0.0F ? Float.floatToIntBits(this.m12) : 0);
|
||||||
|
i = 31 * i + (this.m13 != 0.0F ? Float.floatToIntBits(this.m13) : 0);
|
||||||
|
i = 31 * i + (this.m20 != 0.0F ? Float.floatToIntBits(this.m20) : 0);
|
||||||
|
i = 31 * i + (this.m21 != 0.0F ? Float.floatToIntBits(this.m21) : 0);
|
||||||
|
i = 31 * i + (this.m22 != 0.0F ? Float.floatToIntBits(this.m22) : 0);
|
||||||
|
i = 31 * i + (this.m23 != 0.0F ? Float.floatToIntBits(this.m23) : 0);
|
||||||
|
i = 31 * i + (this.m30 != 0.0F ? Float.floatToIntBits(this.m30) : 0);
|
||||||
|
i = 31 * i + (this.m31 != 0.0F ? Float.floatToIntBits(this.m31) : 0);
|
||||||
|
i = 31 * i + (this.m32 != 0.0F ? Float.floatToIntBits(this.m32) : 0);
|
||||||
|
return 31 * i + (this.m33 != 0.0F ? Float.floatToIntBits(this.m33) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "Matrix4f:\n" +
|
||||||
|
this.m00 + " " + this.m01 + " " + this.m02 + " " + this.m03 + "\n" +
|
||||||
|
this.m10 + " " + this.m11 + " " + this.m12 + " " + this.m13 + "\n" +
|
||||||
|
this.m20 + " " + this.m21 + " " + this.m22 + " " + this.m23 + "\n" +
|
||||||
|
this.m30 + " " + this.m31 + " " + this.m32 + " " + this.m33 + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void store(FloatBuffer floatBuffer)
|
||||||
|
{
|
||||||
|
floatBuffer.put(bufferIndex(0, 0), this.m00);
|
||||||
|
floatBuffer.put(bufferIndex(0, 1), this.m01);
|
||||||
|
floatBuffer.put(bufferIndex(0, 2), this.m02);
|
||||||
|
floatBuffer.put(bufferIndex(0, 3), this.m03);
|
||||||
|
floatBuffer.put(bufferIndex(1, 0), this.m10);
|
||||||
|
floatBuffer.put(bufferIndex(1, 1), this.m11);
|
||||||
|
floatBuffer.put(bufferIndex(1, 2), this.m12);
|
||||||
|
floatBuffer.put(bufferIndex(1, 3), this.m13);
|
||||||
|
floatBuffer.put(bufferIndex(2, 0), this.m20);
|
||||||
|
floatBuffer.put(bufferIndex(2, 1), this.m21);
|
||||||
|
floatBuffer.put(bufferIndex(2, 2), this.m22);
|
||||||
|
floatBuffer.put(bufferIndex(2, 3), this.m23);
|
||||||
|
floatBuffer.put(bufferIndex(3, 0), this.m30);
|
||||||
|
floatBuffer.put(bufferIndex(3, 1), this.m31);
|
||||||
|
floatBuffer.put(bufferIndex(3, 2), this.m32);
|
||||||
|
floatBuffer.put(bufferIndex(3, 3), this.m33);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int bufferIndex(int xIndex, int zIndex)
|
||||||
|
{
|
||||||
|
return (zIndex * 4) + xIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setIdentity()
|
||||||
|
{
|
||||||
|
this.m00 = 1.0F;
|
||||||
|
this.m01 = 0.0F;
|
||||||
|
this.m02 = 0.0F;
|
||||||
|
this.m03 = 0.0F;
|
||||||
|
this.m10 = 0.0F;
|
||||||
|
this.m11 = 1.0F;
|
||||||
|
this.m12 = 0.0F;
|
||||||
|
this.m13 = 0.0F;
|
||||||
|
this.m20 = 0.0F;
|
||||||
|
this.m21 = 0.0F;
|
||||||
|
this.m22 = 1.0F;
|
||||||
|
this.m23 = 0.0F;
|
||||||
|
this.m30 = 0.0F;
|
||||||
|
this.m31 = 0.0F;
|
||||||
|
this.m32 = 0.0F;
|
||||||
|
this.m33 = 1.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** adjugate and determinate */
|
||||||
|
public float adjugateAndDet()
|
||||||
|
{
|
||||||
|
float f = this.m00 * this.m11 - this.m01 * this.m10;
|
||||||
|
float f1 = this.m00 * this.m12 - this.m02 * this.m10;
|
||||||
|
float f2 = this.m00 * this.m13 - this.m03 * this.m10;
|
||||||
|
float f3 = this.m01 * this.m12 - this.m02 * this.m11;
|
||||||
|
float f4 = this.m01 * this.m13 - this.m03 * this.m11;
|
||||||
|
float f5 = this.m02 * this.m13 - this.m03 * this.m12;
|
||||||
|
float f6 = this.m20 * this.m31 - this.m21 * this.m30;
|
||||||
|
float f7 = this.m20 * this.m32 - this.m22 * this.m30;
|
||||||
|
float f8 = this.m20 * this.m33 - this.m23 * this.m30;
|
||||||
|
float f9 = this.m21 * this.m32 - this.m22 * this.m31;
|
||||||
|
float f10 = this.m21 * this.m33 - this.m23 * this.m31;
|
||||||
|
float f11 = this.m22 * this.m33 - this.m23 * this.m32;
|
||||||
|
float f12 = this.m11 * f11 - this.m12 * f10 + this.m13 * f9;
|
||||||
|
float f13 = -this.m10 * f11 + this.m12 * f8 - this.m13 * f7;
|
||||||
|
float f14 = this.m10 * f10 - this.m11 * f8 + this.m13 * f6;
|
||||||
|
float f15 = -this.m10 * f9 + this.m11 * f7 - this.m12 * f6;
|
||||||
|
float f16 = -this.m01 * f11 + this.m02 * f10 - this.m03 * f9;
|
||||||
|
float f17 = this.m00 * f11 - this.m02 * f8 + this.m03 * f7;
|
||||||
|
float f18 = -this.m00 * f10 + this.m01 * f8 - this.m03 * f6;
|
||||||
|
float f19 = this.m00 * f9 - this.m01 * f7 + this.m02 * f6;
|
||||||
|
float f20 = this.m31 * f5 - this.m32 * f4 + this.m33 * f3;
|
||||||
|
float f21 = -this.m30 * f5 + this.m32 * f2 - this.m33 * f1;
|
||||||
|
float f22 = this.m30 * f4 - this.m31 * f2 + this.m33 * f;
|
||||||
|
float f23 = -this.m30 * f3 + this.m31 * f1 - this.m32 * f;
|
||||||
|
float f24 = -this.m21 * f5 + this.m22 * f4 - this.m23 * f3;
|
||||||
|
float f25 = this.m20 * f5 - this.m22 * f2 + this.m23 * f1;
|
||||||
|
float f26 = -this.m20 * f4 + this.m21 * f2 - this.m23 * f;
|
||||||
|
float f27 = this.m20 * f3 - this.m21 * f1 + this.m22 * f;
|
||||||
|
this.m00 = f12;
|
||||||
|
this.m10 = f13;
|
||||||
|
this.m20 = f14;
|
||||||
|
this.m30 = f15;
|
||||||
|
this.m01 = f16;
|
||||||
|
this.m11 = f17;
|
||||||
|
this.m21 = f18;
|
||||||
|
this.m31 = f19;
|
||||||
|
this.m02 = f20;
|
||||||
|
this.m12 = f21;
|
||||||
|
this.m22 = f22;
|
||||||
|
this.m32 = f23;
|
||||||
|
this.m03 = f24;
|
||||||
|
this.m13 = f25;
|
||||||
|
this.m23 = f26;
|
||||||
|
this.m33 = f27;
|
||||||
|
return f * f11 - f1 * f10 + f2 * f9 + f3 * f8 - f4 * f7 + f5 * f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void transpose()
|
||||||
|
{
|
||||||
|
float f = this.m10;
|
||||||
|
this.m10 = this.m01;
|
||||||
|
this.m01 = f;
|
||||||
|
f = this.m20;
|
||||||
|
this.m20 = this.m02;
|
||||||
|
this.m02 = f;
|
||||||
|
f = this.m21;
|
||||||
|
this.m21 = this.m12;
|
||||||
|
this.m12 = f;
|
||||||
|
f = this.m30;
|
||||||
|
this.m30 = this.m03;
|
||||||
|
this.m03 = f;
|
||||||
|
f = this.m31;
|
||||||
|
this.m31 = this.m13;
|
||||||
|
this.m13 = f;
|
||||||
|
f = this.m32;
|
||||||
|
this.m32 = this.m23;
|
||||||
|
this.m23 = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean invert()
|
||||||
|
{
|
||||||
|
float det = this.adjugateAndDet();
|
||||||
|
if (Math.abs(det) > 1.0E-6F)
|
||||||
|
{
|
||||||
|
this.multiply(det);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void multiply(Mat4f multMatrix)
|
||||||
|
{
|
||||||
|
float f = this.m00 * multMatrix.m00 + this.m01 * multMatrix.m10 + this.m02 * multMatrix.m20 + this.m03 * multMatrix.m30;
|
||||||
|
float f1 = this.m00 * multMatrix.m01 + this.m01 * multMatrix.m11 + this.m02 * multMatrix.m21 + this.m03 * multMatrix.m31;
|
||||||
|
float f2 = this.m00 * multMatrix.m02 + this.m01 * multMatrix.m12 + this.m02 * multMatrix.m22 + this.m03 * multMatrix.m32;
|
||||||
|
float f3 = this.m00 * multMatrix.m03 + this.m01 * multMatrix.m13 + this.m02 * multMatrix.m23 + this.m03 * multMatrix.m33;
|
||||||
|
float f4 = this.m10 * multMatrix.m00 + this.m11 * multMatrix.m10 + this.m12 * multMatrix.m20 + this.m13 * multMatrix.m30;
|
||||||
|
float f5 = this.m10 * multMatrix.m01 + this.m11 * multMatrix.m11 + this.m12 * multMatrix.m21 + this.m13 * multMatrix.m31;
|
||||||
|
float f6 = this.m10 * multMatrix.m02 + this.m11 * multMatrix.m12 + this.m12 * multMatrix.m22 + this.m13 * multMatrix.m32;
|
||||||
|
float f7 = this.m10 * multMatrix.m03 + this.m11 * multMatrix.m13 + this.m12 * multMatrix.m23 + this.m13 * multMatrix.m33;
|
||||||
|
float f8 = this.m20 * multMatrix.m00 + this.m21 * multMatrix.m10 + this.m22 * multMatrix.m20 + this.m23 * multMatrix.m30;
|
||||||
|
float f9 = this.m20 * multMatrix.m01 + this.m21 * multMatrix.m11 + this.m22 * multMatrix.m21 + this.m23 * multMatrix.m31;
|
||||||
|
float f10 = this.m20 * multMatrix.m02 + this.m21 * multMatrix.m12 + this.m22 * multMatrix.m22 + this.m23 * multMatrix.m32;
|
||||||
|
float f11 = this.m20 * multMatrix.m03 + this.m21 * multMatrix.m13 + this.m22 * multMatrix.m23 + this.m23 * multMatrix.m33;
|
||||||
|
float f12 = this.m30 * multMatrix.m00 + this.m31 * multMatrix.m10 + this.m32 * multMatrix.m20 + this.m33 * multMatrix.m30;
|
||||||
|
float f13 = this.m30 * multMatrix.m01 + this.m31 * multMatrix.m11 + this.m32 * multMatrix.m21 + this.m33 * multMatrix.m31;
|
||||||
|
float f14 = this.m30 * multMatrix.m02 + this.m31 * multMatrix.m12 + this.m32 * multMatrix.m22 + this.m33 * multMatrix.m32;
|
||||||
|
float f15 = this.m30 * multMatrix.m03 + this.m31 * multMatrix.m13 + this.m32 * multMatrix.m23 + this.m33 * multMatrix.m33;
|
||||||
|
this.m00 = f;
|
||||||
|
this.m01 = f1;
|
||||||
|
this.m02 = f2;
|
||||||
|
this.m03 = f3;
|
||||||
|
this.m10 = f4;
|
||||||
|
this.m11 = f5;
|
||||||
|
this.m12 = f6;
|
||||||
|
this.m13 = f7;
|
||||||
|
this.m20 = f8;
|
||||||
|
this.m21 = f9;
|
||||||
|
this.m22 = f10;
|
||||||
|
this.m23 = f11;
|
||||||
|
this.m30 = f12;
|
||||||
|
this.m31 = f13;
|
||||||
|
this.m32 = f14;
|
||||||
|
this.m33 = f15;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quaternions aren't currently needed/implemented
|
||||||
|
public void multiply(Quaternion p_226596_1_)
|
||||||
|
{
|
||||||
|
this.multiply(new Matrix4f(p_226596_1_));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void multiply(float scalar)
|
||||||
|
{
|
||||||
|
this.m00 *= scalar;
|
||||||
|
this.m01 *= scalar;
|
||||||
|
this.m02 *= scalar;
|
||||||
|
this.m03 *= scalar;
|
||||||
|
this.m10 *= scalar;
|
||||||
|
this.m11 *= scalar;
|
||||||
|
this.m12 *= scalar;
|
||||||
|
this.m13 *= scalar;
|
||||||
|
this.m20 *= scalar;
|
||||||
|
this.m21 *= scalar;
|
||||||
|
this.m22 *= scalar;
|
||||||
|
this.m23 *= scalar;
|
||||||
|
this.m30 *= scalar;
|
||||||
|
this.m31 *= scalar;
|
||||||
|
this.m32 *= scalar;
|
||||||
|
this.m33 *= scalar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mat4f perspective(double fov, float widthHeightRatio, float nearClipPlane, float farClipPlane)
|
||||||
|
{
|
||||||
|
float f = (float) (1.0D / Math.tan(fov * ((float) Math.PI / 180F) / 2.0D));
|
||||||
|
Mat4f matrix = new Mat4f();
|
||||||
|
matrix.m00 = f / widthHeightRatio;
|
||||||
|
matrix.m11 = f;
|
||||||
|
matrix.m22 = (farClipPlane + nearClipPlane) / (nearClipPlane - farClipPlane);
|
||||||
|
matrix.m32 = -1.0F;
|
||||||
|
matrix.m23 = 2.0F * farClipPlane * nearClipPlane / (nearClipPlane - farClipPlane);
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* not currently needed/implemented
|
||||||
|
* Also the parameter names should be double checked as they may be incorrect
|
||||||
|
public static Matrix4Float orthographic(float left, float right, float top, float bottom)
|
||||||
|
{
|
||||||
|
Matrix4Float matrix4f = new Matrix4Float();
|
||||||
|
matrix4f.m00 = 2.0F / left;
|
||||||
|
matrix4f.m11 = 2.0F / right;
|
||||||
|
float f = bottom - top;
|
||||||
|
matrix4f.m22 = -2.0F / f;
|
||||||
|
matrix4f.m33 = 1.0F;
|
||||||
|
matrix4f.m03 = -1.0F;
|
||||||
|
matrix4f.m13 = -1.0F;
|
||||||
|
matrix4f.m23 = -(bottom + top) / f;
|
||||||
|
return matrix4f;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: what kind of translation is this?
|
||||||
|
* and how is this different from "multiplyTranslationMatrix"?
|
||||||
|
*/
|
||||||
|
public void translate(Vec3f vec)
|
||||||
|
{
|
||||||
|
this.m03 += vec.x;
|
||||||
|
this.m13 += vec.y;
|
||||||
|
this.m23 += vec.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** originally "translate" from Minecraft's MatrixStack */
|
||||||
|
public void multiplyTranslationMatrix(double x, double y, double z)
|
||||||
|
{
|
||||||
|
multiply(createTranslateMatrix((float)x, (float)y, (float)z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mat4f copy()
|
||||||
|
{
|
||||||
|
return new Mat4f(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mat4f createScaleMatrix(float x, float y, float z)
|
||||||
|
{
|
||||||
|
Mat4f matrix = new Mat4f();
|
||||||
|
matrix.m00 = x;
|
||||||
|
matrix.m11 = y;
|
||||||
|
matrix.m22 = z;
|
||||||
|
matrix.m33 = 1.0F;
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mat4f createTranslateMatrix(float x, float y, float z)
|
||||||
|
{
|
||||||
|
Mat4f matrix = new Mat4f();
|
||||||
|
matrix.m00 = 1.0F;
|
||||||
|
matrix.m11 = 1.0F;
|
||||||
|
matrix.m22 = 1.0F;
|
||||||
|
matrix.m33 = 1.0F;
|
||||||
|
matrix.m03 = x;
|
||||||
|
matrix.m13 = y;
|
||||||
|
matrix.m23 = z;
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Forge start
|
||||||
|
public Mat4f(float[] values)
|
||||||
|
{
|
||||||
|
m00 = values[0];
|
||||||
|
m01 = values[1];
|
||||||
|
m02 = values[2];
|
||||||
|
m03 = values[3];
|
||||||
|
m10 = values[4];
|
||||||
|
m11 = values[5];
|
||||||
|
m12 = values[6];
|
||||||
|
m13 = values[7];
|
||||||
|
m20 = values[8];
|
||||||
|
m21 = values[9];
|
||||||
|
m22 = values[10];
|
||||||
|
m23 = values[11];
|
||||||
|
m30 = values[12];
|
||||||
|
m31 = values[13];
|
||||||
|
m32 = values[14];
|
||||||
|
m33 = values[15];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mat4f(FloatBuffer buffer)
|
||||||
|
{
|
||||||
|
this(buffer.array());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(Mat4f mat)
|
||||||
|
{
|
||||||
|
this.m00 = mat.m00;
|
||||||
|
this.m01 = mat.m01;
|
||||||
|
this.m02 = mat.m02;
|
||||||
|
this.m03 = mat.m03;
|
||||||
|
this.m10 = mat.m10;
|
||||||
|
this.m11 = mat.m11;
|
||||||
|
this.m12 = mat.m12;
|
||||||
|
this.m13 = mat.m13;
|
||||||
|
this.m20 = mat.m20;
|
||||||
|
this.m21 = mat.m21;
|
||||||
|
this.m22 = mat.m22;
|
||||||
|
this.m23 = mat.m23;
|
||||||
|
this.m30 = mat.m30;
|
||||||
|
this.m31 = mat.m31;
|
||||||
|
this.m32 = mat.m32;
|
||||||
|
this.m33 = mat.m33;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Mat4f other)
|
||||||
|
{
|
||||||
|
m00 += other.m00;
|
||||||
|
m01 += other.m01;
|
||||||
|
m02 += other.m02;
|
||||||
|
m03 += other.m03;
|
||||||
|
m10 += other.m10;
|
||||||
|
m11 += other.m11;
|
||||||
|
m12 += other.m12;
|
||||||
|
m13 += other.m13;
|
||||||
|
m20 += other.m20;
|
||||||
|
m21 += other.m21;
|
||||||
|
m22 += other.m22;
|
||||||
|
m23 += other.m23;
|
||||||
|
m30 += other.m30;
|
||||||
|
m31 += other.m31;
|
||||||
|
m32 += other.m32;
|
||||||
|
m33 += other.m33;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void multiplyBackward(Mat4f other)
|
||||||
|
{
|
||||||
|
Mat4f copy = other.copy();
|
||||||
|
copy.multiply(this);
|
||||||
|
this.set(copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTranslation(float x, float y, float z)
|
||||||
|
{
|
||||||
|
this.m00 = 1.0F;
|
||||||
|
this.m11 = 1.0F;
|
||||||
|
this.m22 = 1.0F;
|
||||||
|
this.m33 = 1.0F;
|
||||||
|
this.m03 = x;
|
||||||
|
this.m13 = y;
|
||||||
|
this.m23 = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the values that store the clipping planes.
|
||||||
|
* Formula for calculating matrix values is the same that OpenGL uses when making matrices.
|
||||||
|
*
|
||||||
|
* @param nearClip New near clipping plane value.
|
||||||
|
* @param farClip New far clipping plane value.
|
||||||
|
*/
|
||||||
|
public void setClipPlanes(float nearClip,float farClip)
|
||||||
|
{
|
||||||
|
//convert to matrix values, formula copied from a textbook / openGL specification.
|
||||||
|
float matNearClip = -((farClip + nearClip) / (farClip - nearClip));
|
||||||
|
float matFarClip = -((2 * farClip * nearClip) / (farClip - nearClip));
|
||||||
|
//set new values for the clip planes.
|
||||||
|
this.m22 = matNearClip;
|
||||||
|
this.m23 = matFarClip;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects.math;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is closer to MC's implementation of a
|
||||||
|
* 3 element float vector than a 3 element double
|
||||||
|
* vector. Hopefully that shouldn't cause any issues.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-18-2021
|
||||||
|
*/
|
||||||
|
public class Vec3d
|
||||||
|
{
|
||||||
|
public static Vec3d XNeg = new Vec3d(-1.0F, 0.0F, 0.0F);
|
||||||
|
public static Vec3d XPos = new Vec3d(1.0F, 0.0F, 0.0F);
|
||||||
|
public static Vec3d YNeg = new Vec3d(0.0F, -1.0F, 0.0F);
|
||||||
|
public static Vec3d YPos = new Vec3d(0.0F, 1.0F, 0.0F);
|
||||||
|
public static Vec3d ZNeg = new Vec3d(0.0F, 0.0F, -1.0F);
|
||||||
|
public static Vec3d ZPos = new Vec3d(0.0F, 0.0F, 1.0F);
|
||||||
|
|
||||||
|
public static final Vec3d ZERO_VECTOR = new Vec3d(0.0D, 0.0D, 0.0D);
|
||||||
|
|
||||||
|
public double x;
|
||||||
|
public double y;
|
||||||
|
public double z;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3d()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3d(double x, double y, double z)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if (this == obj)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (obj != null && this.getClass() == obj.getClass())
|
||||||
|
{
|
||||||
|
Vec3d Vec3f = (Vec3d) obj;
|
||||||
|
if (Double.compare(Vec3f.x, this.x) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (Double.compare(Vec3f.y, this.y) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Double.compare(Vec3f.z, this.z) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
long longVal = Double.doubleToLongBits(this.x);
|
||||||
|
|
||||||
|
int intVal = (int) (longVal ^ longVal >>> 32);
|
||||||
|
longVal = Double.doubleToLongBits(this.y);
|
||||||
|
intVal = 31 * intVal + (int) (longVal ^ longVal >>> 32);
|
||||||
|
longVal = Double.doubleToLongBits(this.z);
|
||||||
|
|
||||||
|
return 31 * intVal + (int) (longVal ^ longVal >>> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mul(double scalar)
|
||||||
|
{
|
||||||
|
this.x *= scalar;
|
||||||
|
this.y *= scalar;
|
||||||
|
this.z *= scalar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mul(double x, double y, double z)
|
||||||
|
{
|
||||||
|
this.x *= x;
|
||||||
|
this.y *= y;
|
||||||
|
this.z *= z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clamp(double min, double max)
|
||||||
|
{
|
||||||
|
this.x = LodUtil.clamp(min, this.x, max);
|
||||||
|
this.y = LodUtil.clamp(min, this.y, max);
|
||||||
|
this.z = LodUtil.clamp(min, this.z, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(double x, double y, double z)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(double x, double y, double z)
|
||||||
|
{
|
||||||
|
this.x += x;
|
||||||
|
this.y += y;
|
||||||
|
this.z += z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Vec3d vector)
|
||||||
|
{
|
||||||
|
this.x += vector.x;
|
||||||
|
this.y += vector.y;
|
||||||
|
this.z += vector.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subtract(Vec3d vector)
|
||||||
|
{
|
||||||
|
this.x -= vector.x;
|
||||||
|
this.y -= vector.y;
|
||||||
|
this.z -= vector.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double dotProduct(Vec3d vector)
|
||||||
|
{
|
||||||
|
return this.x * vector.x + this.y * vector.y + this.z * vector.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3d normalize()
|
||||||
|
{
|
||||||
|
double value = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
||||||
|
return value < 1.0E-4D ? ZERO_VECTOR : new Vec3d(this.x / value, this.y / value, this.z / value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void crossProduct(Vec3d vector)
|
||||||
|
{
|
||||||
|
double f = this.x;
|
||||||
|
double f1 = this.y;
|
||||||
|
double f2 = this.z;
|
||||||
|
double f3 = vector.x;
|
||||||
|
double f4 = vector.y;
|
||||||
|
double f5 = vector.z;
|
||||||
|
this.x = f1 * f5 - f2 * f4;
|
||||||
|
this.y = f2 * f3 - f * f5;
|
||||||
|
this.z = f * f4 - f1 * f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Matrix3f is not currently needed/implemented
|
||||||
|
public void transform(Matrix3f p_229188_1_)
|
||||||
|
{
|
||||||
|
double f = this.x;
|
||||||
|
double f1 = this.y;
|
||||||
|
double f2 = this.z;
|
||||||
|
this.x = p_229188_1_.m00 * f + p_229188_1_.m01 * f1 + p_229188_1_.m02 * f2;
|
||||||
|
this.y = p_229188_1_.m10 * f + p_229188_1_.m11 * f1 + p_229188_1_.m12 * f2;
|
||||||
|
this.z = p_229188_1_.m20 * f + p_229188_1_.m21 * f1 + p_229188_1_.m22 * f2;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Quaternions are not currently needed/implemented
|
||||||
|
public void transform(Quaternion p_214905_1_)
|
||||||
|
{
|
||||||
|
Quaternion quaternion = new Quaternion(p_214905_1_);
|
||||||
|
quaternion.mul(new Quaternion(this.x(), this.y(), this.z(), 0.0F));
|
||||||
|
Quaternion quaternion1 = new Quaternion(p_214905_1_);
|
||||||
|
quaternion1.conj();
|
||||||
|
quaternion.mul(quaternion1);
|
||||||
|
this.set(quaternion.i(), quaternion.j(), quaternion.k());
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* not currently needed
|
||||||
|
* percent may actually be partial ticks (which is available when rendering)
|
||||||
|
public void linearInterp(Vec3f resultingVector, double percent)
|
||||||
|
{
|
||||||
|
double f = 1.0F - percent;
|
||||||
|
this.x = this.x * f + resultingVector.x * percent;
|
||||||
|
this.y = this.y * f + resultingVector.y * percent;
|
||||||
|
this.z = this.z * f + resultingVector.z * percent;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Quaternions are not currently needed/implemented
|
||||||
|
public Quaternion rotation(double p_229193_1_)
|
||||||
|
{
|
||||||
|
return new Quaternion(this, p_229193_1_, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public Quaternion rotationDegrees(double p_229187_1_)
|
||||||
|
{
|
||||||
|
return new Quaternion(this, p_229187_1_, true);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public Vec3d copy()
|
||||||
|
{
|
||||||
|
return new Vec3d(this.x, this.y, this.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not currently needed/implemented
|
||||||
|
public void map(double2doubleFunction p_229191_1_)
|
||||||
|
{
|
||||||
|
this.x = p_229191_1_.get(this.x);
|
||||||
|
this.y = p_229191_1_.get(this.y);
|
||||||
|
this.z = p_229191_1_.get(this.z);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "[" + this.x + ", " + this.y + ", " + this.z + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forge start
|
||||||
|
public Vec3d(double[] values)
|
||||||
|
{
|
||||||
|
set(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(double[] values)
|
||||||
|
{
|
||||||
|
this.x = values[0];
|
||||||
|
this.y = values[1];
|
||||||
|
this.z = values[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects.math;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A (almost) exact copy of Minecraft's 1.16.5
|
||||||
|
* implementation of a 3 element float vector.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-11-2021
|
||||||
|
*/
|
||||||
|
public class Vec3f
|
||||||
|
{
|
||||||
|
public static Vec3f XNeg = new Vec3f(-1.0F, 0.0F, 0.0F);
|
||||||
|
public static Vec3f XPos = new Vec3f(1.0F, 0.0F, 0.0F);
|
||||||
|
public static Vec3f YNeg = new Vec3f(0.0F, -1.0F, 0.0F);
|
||||||
|
public static Vec3f YPos = new Vec3f(0.0F, 1.0F, 0.0F);
|
||||||
|
public static Vec3f ZNeg = new Vec3f(0.0F, 0.0F, -1.0F);
|
||||||
|
public static Vec3f ZPos = new Vec3f(0.0F, 0.0F, 1.0F);
|
||||||
|
|
||||||
|
|
||||||
|
public float x;
|
||||||
|
public float y;
|
||||||
|
public float z;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3f()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3f(float x, float y, float z)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if (this == obj)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (obj != null && this.getClass() == obj.getClass())
|
||||||
|
{
|
||||||
|
Vec3f Vec3f = (Vec3f) obj;
|
||||||
|
if (Float.compare(Vec3f.x, this.x) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (Float.compare(Vec3f.y, this.y) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Float.compare(Vec3f.z, this.z) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
int i = Float.floatToIntBits(this.x);
|
||||||
|
i = 31 * i + Float.floatToIntBits(this.y);
|
||||||
|
return 31 * i + Float.floatToIntBits(this.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mul(float scalar)
|
||||||
|
{
|
||||||
|
this.x *= scalar;
|
||||||
|
this.y *= scalar;
|
||||||
|
this.z *= scalar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mul(float x, float y, float z)
|
||||||
|
{
|
||||||
|
this.x *= x;
|
||||||
|
this.y *= y;
|
||||||
|
this.z *= z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clamp(float min, float max)
|
||||||
|
{
|
||||||
|
this.x = LodUtil.clamp(min, this.x, max);
|
||||||
|
this.y = LodUtil.clamp(min, this.y, max);
|
||||||
|
this.z = LodUtil.clamp(min, this.z, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(float x, float y, float z)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(float x, float y, float z)
|
||||||
|
{
|
||||||
|
this.x += x;
|
||||||
|
this.y += y;
|
||||||
|
this.z += z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Vec3f vector)
|
||||||
|
{
|
||||||
|
this.x += vector.x;
|
||||||
|
this.y += vector.y;
|
||||||
|
this.z += vector.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subtract(Vec3f vector)
|
||||||
|
{
|
||||||
|
this.x -= vector.x;
|
||||||
|
this.y -= vector.y;
|
||||||
|
this.z -= vector.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float dotProduct(Vec3f vector)
|
||||||
|
{
|
||||||
|
return this.x * vector.x + this.y * vector.y + this.z * vector.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean normalize()
|
||||||
|
{
|
||||||
|
float squaredSum = this.x * this.x + this.y * this.y + this.z * this.z;
|
||||||
|
if (squaredSum < 1.0E-5D)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float f1 = LodUtil.fastInvSqrt(squaredSum);
|
||||||
|
this.x *= f1;
|
||||||
|
this.y *= f1;
|
||||||
|
this.z *= f1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void crossProduct(Vec3f vector)
|
||||||
|
{
|
||||||
|
float f = this.x;
|
||||||
|
float f1 = this.y;
|
||||||
|
float f2 = this.z;
|
||||||
|
float f3 = vector.x;
|
||||||
|
float f4 = vector.y;
|
||||||
|
float f5 = vector.z;
|
||||||
|
this.x = f1 * f5 - f2 * f4;
|
||||||
|
this.y = f2 * f3 - f * f5;
|
||||||
|
this.z = f * f4 - f1 * f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Matrix3f is not currently needed/implemented
|
||||||
|
public void transform(Matrix3f p_229188_1_)
|
||||||
|
{
|
||||||
|
float f = this.x;
|
||||||
|
float f1 = this.y;
|
||||||
|
float f2 = this.z;
|
||||||
|
this.x = p_229188_1_.m00 * f + p_229188_1_.m01 * f1 + p_229188_1_.m02 * f2;
|
||||||
|
this.y = p_229188_1_.m10 * f + p_229188_1_.m11 * f1 + p_229188_1_.m12 * f2;
|
||||||
|
this.z = p_229188_1_.m20 * f + p_229188_1_.m21 * f1 + p_229188_1_.m22 * f2;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Quaternions are not currently needed/implemented
|
||||||
|
public void transform(Quaternion p_214905_1_)
|
||||||
|
{
|
||||||
|
Quaternion quaternion = new Quaternion(p_214905_1_);
|
||||||
|
quaternion.mul(new Quaternion(this.x(), this.y(), this.z(), 0.0F));
|
||||||
|
Quaternion quaternion1 = new Quaternion(p_214905_1_);
|
||||||
|
quaternion1.conj();
|
||||||
|
quaternion.mul(quaternion1);
|
||||||
|
this.set(quaternion.i(), quaternion.j(), quaternion.k());
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* not currently needed
|
||||||
|
* percent may actually be partial ticks (which is available when rendering)
|
||||||
|
public void linearInterp(Vec3f resultingVector, float percent)
|
||||||
|
{
|
||||||
|
float f = 1.0F - percent;
|
||||||
|
this.x = this.x * f + resultingVector.x * percent;
|
||||||
|
this.y = this.y * f + resultingVector.y * percent;
|
||||||
|
this.z = this.z * f + resultingVector.z * percent;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Quaternions are not currently needed/implemented
|
||||||
|
public Quaternion rotation(float p_229193_1_)
|
||||||
|
{
|
||||||
|
return new Quaternion(this, p_229193_1_, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public Quaternion rotationDegrees(float p_229187_1_)
|
||||||
|
{
|
||||||
|
return new Quaternion(this, p_229187_1_, true);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public Vec3f copy()
|
||||||
|
{
|
||||||
|
return new Vec3f(this.x, this.y, this.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not currently needed/implemented
|
||||||
|
public void map(Float2FloatFunction p_229191_1_)
|
||||||
|
{
|
||||||
|
this.x = p_229191_1_.get(this.x);
|
||||||
|
this.y = p_229191_1_.get(this.y);
|
||||||
|
this.z = p_229191_1_.get(this.z);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "[" + this.x + ", " + this.y + ", " + this.z + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forge start
|
||||||
|
public Vec3f(float[] values)
|
||||||
|
{
|
||||||
|
set(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(float[] values)
|
||||||
|
{
|
||||||
|
this.x = values[0];
|
||||||
|
this.y = values[1];
|
||||||
|
this.z = values[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects.math;
|
||||||
|
|
||||||
|
import com.seibel.lod.core.util.LodUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A (almost) exact copy of Minecraft's 1.16.5
|
||||||
|
* implementation of a 3 element integer vector.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-11-2021
|
||||||
|
*/
|
||||||
|
public class Vec3i
|
||||||
|
{
|
||||||
|
public static Vec3i XNeg = new Vec3i(-1, 0, 0);
|
||||||
|
public static Vec3i XPos = new Vec3i(1, 0, 0);
|
||||||
|
public static Vec3i YNeg = new Vec3i(0, -1, 0);
|
||||||
|
public static Vec3i YPos = new Vec3i(0, 1, 0);
|
||||||
|
public static Vec3i ZNeg = new Vec3i(0, 0, -1);
|
||||||
|
public static Vec3i ZPos = new Vec3i(0, 0, 1);
|
||||||
|
|
||||||
|
|
||||||
|
public int x;
|
||||||
|
public int y;
|
||||||
|
public int z;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3i()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3i(int x, int y, int z)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if (this == obj)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (obj != null && this.getClass() == obj.getClass())
|
||||||
|
{
|
||||||
|
Vec3i Vec3f = (Vec3i) obj;
|
||||||
|
if (Float.compare(Vec3f.x, this.x) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (Float.compare(Vec3f.y, this.y) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Float.compare(Vec3f.z, this.z) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
int i = Float.floatToIntBits(this.x);
|
||||||
|
i = 31 * i + Float.floatToIntBits(this.y);
|
||||||
|
return 31 * i + Float.floatToIntBits(this.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mul(float scalar)
|
||||||
|
{
|
||||||
|
this.x *= scalar;
|
||||||
|
this.y *= scalar;
|
||||||
|
this.z *= scalar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mul(float x, float y, float z)
|
||||||
|
{
|
||||||
|
this.x *= x;
|
||||||
|
this.y *= y;
|
||||||
|
this.z *= z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clamp(int min, int max)
|
||||||
|
{
|
||||||
|
this.x = LodUtil.clamp(min, this.x, max);
|
||||||
|
this.y = LodUtil.clamp(min, this.y, max);
|
||||||
|
this.z = LodUtil.clamp(min, this.z, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int x, int y, int z)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(int x, int y, int z)
|
||||||
|
{
|
||||||
|
this.x += x;
|
||||||
|
this.y += y;
|
||||||
|
this.z += z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Vec3i vector)
|
||||||
|
{
|
||||||
|
this.x += vector.x;
|
||||||
|
this.y += vector.y;
|
||||||
|
this.z += vector.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subtract(Vec3i vector)
|
||||||
|
{
|
||||||
|
this.x -= vector.x;
|
||||||
|
this.y -= vector.y;
|
||||||
|
this.z -= vector.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double distSqr(double x, double y, double z, boolean centerOfBlock)
|
||||||
|
{
|
||||||
|
double offset = centerOfBlock ? 0.5 : 0.0;
|
||||||
|
double xAdd = this.x + offset - x;
|
||||||
|
double yAdd = this.y + offset - y;
|
||||||
|
double zAdd = this.z + offset - z;
|
||||||
|
return (xAdd * xAdd) + (yAdd * yAdd) + (zAdd * zAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int distManhattan(Vec3i otherVec)
|
||||||
|
{
|
||||||
|
float xSub = Math.abs(otherVec.x - this.x);
|
||||||
|
float ySub = Math.abs(otherVec.y - this.y);
|
||||||
|
float zSub = Math.abs(otherVec.z - this.z);
|
||||||
|
return (int) (xSub + ySub + zSub);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** inner product */
|
||||||
|
public float dotProduct(Vec3i vector)
|
||||||
|
{
|
||||||
|
return (this.x * vector.x) + (this.y * vector.y) + (this.z * vector.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cross product */
|
||||||
|
public Vec3i cross(Vec3i otherVec)
|
||||||
|
{
|
||||||
|
return new Vec3i(
|
||||||
|
(this.y * otherVec.z) - (this.z * otherVec.y),
|
||||||
|
(this.z * otherVec.x) - (this.x * otherVec.z),
|
||||||
|
(this.x * otherVec.y) - (this.y * otherVec.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3i copy()
|
||||||
|
{
|
||||||
|
return new Vec3i(this.x, this.y, this.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "[" + this.x + ", " + this.y + ", " + this.z + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Forge start
|
||||||
|
public Vec3i(int[] values)
|
||||||
|
{
|
||||||
|
set(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int[] values)
|
||||||
|
{
|
||||||
|
this.x = values[0];
|
||||||
|
this.y = values[1];
|
||||||
|
this.z = values[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||||
|
* licensed under the GNU GPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 com.seibel.lod.core.objects.opengl;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A (almost) exact copy of MC's
|
||||||
|
* DefaultVertexFormats class.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 11-13-2021
|
||||||
|
*/
|
||||||
|
public class DefaultLodVertexFormats
|
||||||
|
{
|
||||||
|
public static final LodVertexFormatElement ELEMENT_POSITION = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.FLOAT, 3);
|
||||||
|
public static final LodVertexFormatElement ELEMENT_COLOR = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 4);
|
||||||
|
public static final LodVertexFormatElement ELEMENT_UV = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.FLOAT, 2);
|
||||||
|
public static final LodVertexFormatElement ELEMENT_LIGHT_MAP_UV = new LodVertexFormatElement(1, LodVertexFormatElement.DataType.SHORT, 2);
|
||||||
|
public static final LodVertexFormatElement ELEMENT_NORMAL = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 3);
|
||||||
|
public static final LodVertexFormatElement ELEMENT_PADDING = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 1);
|
||||||
|
|
||||||
|
|
||||||
|
public static final LodVertexFormat POSITION = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).build());
|
||||||
|
public static final LodVertexFormat POSITION_COLOR = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).build());
|
||||||
|
public static final LodVertexFormat POSITION_COLOR_LIGHTMAP = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_LIGHT_MAP_UV).build());
|
||||||
|
public static final LodVertexFormat POSITION_TEX = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_UV).build());
|
||||||
|
public static final LodVertexFormat POSITION_COLOR_TEX = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).build());
|
||||||
|
public static final LodVertexFormat POSITION_COLOR_TEX_LIGHTMAP = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).add(ELEMENT_LIGHT_MAP_UV).build());
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user