Compare commits
77 Commits
falling-sa
...
title-scre
Author | SHA1 | Date | |
---|---|---|---|
|
22a744f65a | ||
|
c6de19cf19 | ||
|
c1a57f7d7a | ||
162b2249b1
|
|||
dececb4589
|
|||
ec17eb7065
|
|||
e4d0570200
|
|||
92f9639c4b
|
|||
f350467942
|
|||
23b0af1731 | |||
0389f97e59 | |||
706800218c
|
|||
ca2c7b58d8
|
|||
359879b0fe
|
|||
a06d8ee056
|
|||
c8138faabe
|
|||
49d283e7a3
|
|||
c93f0df30d
|
|||
75ea7baf9c
|
|||
c4a9a17c7c | |||
b4ff5114bd
|
|||
efc6a8ecd0
|
|||
4b10dc82ed
|
|||
|
c04894a0c9 | ||
576cfed99f
|
|||
|
5af1b7309d | ||
|
0f0a94811f | ||
28b19c8f35
|
|||
|
51bcca1499 | ||
|
ce9e95e5ce | ||
eb5aa59941
|
|||
409bbdb680
|
|||
|
0c41350ae7 | ||
a633c8324e
|
|||
6b33f231b4 | |||
e2308b825d
|
|||
|
e0f6a08740 | ||
|
2820e01974 | ||
|
46bcb85044 | ||
|
c5dfe3d0b7 | ||
a85fc27f8b
|
|||
711e4a2bb4
|
|||
0100c8791d
|
|||
|
e967a64401 | ||
d2ffe1fe0e
|
|||
f4300558d5
|
|||
cd16334db8
|
|||
41a2909f7c
|
|||
a222ea8f67
|
|||
|
98250cd524 | ||
f74c731a3d
|
|||
98c383bf7d
|
|||
dd80df2cf2
|
|||
|
9dcb3a7748 | ||
1727a2a4a1
|
|||
20fb8f0597
|
|||
1d28f32865
|
|||
0ccc108ddd
|
|||
c7e7d3bdac
|
|||
62729f5873
|
|||
84864f8947
|
|||
d01ef3654f
|
|||
|
fae09edb16 | ||
|
e4ced6507e | ||
|
b7dcbb0f30 | ||
|
9c26418354 | ||
|
6891d3a095 | ||
|
8167c40f64 | ||
|
8bc23acb61 | ||
|
254faca0a5 | ||
|
0c66f1751e | ||
|
c88dea6030 | ||
|
6521cb5749 | ||
|
94db44e443 | ||
|
53f72b068a | ||
|
a9ca5f6b17 | ||
|
4ab7cb738e |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -6,4 +6,4 @@
|
|||||||
* text=auto eol=lf
|
* text=auto eol=lf
|
||||||
|
|
||||||
*.bat text eol=crlf
|
*.bat text eol=crlf
|
||||||
|
*.nsi text eol=crlf
|
||||||
|
470
build.gradle
470
build.gradle
@@ -1,23 +1,29 @@
|
|||||||
/*
|
/*
|
||||||
* build.gradle for Progressia
|
* Build logic for Progressia
|
||||||
|
* build.gradle
|
||||||
|
*
|
||||||
|
* Please refer to
|
||||||
|
*
|
||||||
|
* docs/building/BuildScriptReference.md
|
||||||
|
*
|
||||||
|
* for user reference.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
// Apply the java-library plugin to add support for Java Library
|
id 'java'
|
||||||
id 'java-library'
|
|
||||||
|
|
||||||
/*
|
// GrGit
|
||||||
* Uncomment the following line to enable the Eclipse plugin.
|
// A JGit wrapper for Groovy used to access git commit info
|
||||||
* This is only necessary if you don't use Buildship plugin from the IDE
|
id 'org.ajoberstar.grgit' version '4.1.1'
|
||||||
*/
|
|
||||||
//id 'eclipse'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We're Java 8 for now.
|
* Configure Java version
|
||||||
* Why? As of 2020, most users have Oracle Java, which only supports Java 8.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
java {
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
@@ -34,16 +40,17 @@ compileJava {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dependencies
|
* Dependencies
|
||||||
*/
|
*/
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
jcenter()
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Specify Windcorp Maven repository
|
* Windcorp Maven repository
|
||||||
* Currently used by:
|
* Currently used by:
|
||||||
* - ru.windcorp.fork.io.github.java-graphics:glm:1.0.1
|
* - ru.windcorp.fork.io.github.java-graphics:glm:1.0.1
|
||||||
*/
|
*/
|
||||||
@@ -66,230 +73,315 @@ dependencies {
|
|||||||
|
|
||||||
// Log4j
|
// Log4j
|
||||||
// A logging library
|
// A logging library
|
||||||
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.3'
|
implementation 'org.apache.logging.log4j:log4j-api:2.17.0'
|
||||||
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.3'
|
implementation 'org.apache.logging.log4j:log4j-core:2.17.0'
|
||||||
|
|
||||||
// JUnit
|
// JUnit
|
||||||
// A unit-testing library
|
// A unit-testing library
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
|
|
||||||
// See LWJGL dependencies below
|
// Also see LWJGL dependencies in build_logic/lwjgl.gradle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Progressia uses the following LWJGL libraries:
|
* Version resolution
|
||||||
* - Core libraries
|
|
||||||
* - OpenGL
|
|
||||||
* - OpenAL
|
|
||||||
* - GLFW
|
|
||||||
* - STB
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
import org.ajoberstar.grgit.*
|
||||||
* LWJGL
|
|
||||||
* (auto-generated script)
|
|
||||||
* ((here be dragons))
|
|
||||||
*/
|
|
||||||
|
|
||||||
import org.gradle.internal.os.OperatingSystem
|
// Pattern: vMAJOR.MINOR.PATCH[-SUFFIX]
|
||||||
|
project.ext.tagFormat = /^v(\d+)\.(\d+)\.(\d+)(-[\w\-]*)?$/
|
||||||
|
|
||||||
project.ext.lwjglVersion = "3.2.3"
|
String version_parseVersion(String tag, boolean increment) {
|
||||||
|
try {
|
||||||
|
|
||||||
switch (OperatingSystem.current()) {
|
def data = (tag =~ tagFormat)[0]
|
||||||
case OperatingSystem.LINUX:
|
|
||||||
def osArch = System.getProperty("os.arch")
|
|
||||||
project.ext.lwjglNatives = osArch.startsWith("arm") || osArch.startsWith("aarch64")
|
|
||||||
? "natives-linux-${osArch.contains("64") || osArch.startsWith("armv8") ? "arm64" : "arm32"}"
|
|
||||||
: "natives-linux"
|
|
||||||
break
|
|
||||||
case OperatingSystem.MAC_OS:
|
|
||||||
project.ext.lwjglNatives = "natives-macos"
|
|
||||||
break
|
|
||||||
case OperatingSystem.WINDOWS:
|
|
||||||
project.ext.lwjglNatives = System.getProperty("os.arch").contains("64") ? "natives-windows" : "natives-windows-x86"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
def major = data[1]
|
||||||
implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion")
|
def minor = data[2]
|
||||||
|
def patch = data[3] as int
|
||||||
|
def suffix = data[4] ?: ''
|
||||||
|
|
||||||
implementation "org.lwjgl:lwjgl"
|
if (increment) {
|
||||||
implementation "org.lwjgl:lwjgl-glfw"
|
def oldVersion = "$major.$minor.$patch$suffix"
|
||||||
implementation "org.lwjgl:lwjgl-openal"
|
patch++
|
||||||
implementation "org.lwjgl:lwjgl-opengl"
|
def newVersion = "$major.$minor.$patch$suffix"
|
||||||
implementation "org.lwjgl:lwjgl-stb"
|
|
||||||
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
|
logger.info "Version parsed from Git: $oldVersion, incremented to $newVersion"
|
||||||
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
|
return newVersion
|
||||||
runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives"
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
|
|
||||||
}
|
|
||||||
|
|
||||||
// LWJGL END
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tasks
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Additional native libraries specification
|
|
||||||
*/
|
|
||||||
|
|
||||||
project.ext.platforms = new HashSet<>()
|
|
||||||
|
|
||||||
task addNativeDependencies {
|
|
||||||
doFirst {
|
|
||||||
def archs = project.ext.platforms
|
|
||||||
|
|
||||||
switch (archs.size()) {
|
|
||||||
case 0:
|
|
||||||
println "Adding LWJGL native dependencies for local platform only:\n\t$lwjglNatives"
|
|
||||||
archs.add project.ext.lwjglNatives
|
|
||||||
break
|
|
||||||
case 1:
|
|
||||||
println "Adding LWJGL native dependencies for platform\n\t" + archs.get(0)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
println "Adding LWJGL native dependencies for platforms:\n\t" + archs.join("\n\t")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (project.ext.lwjglNatives.isEmpty()) println "WTF"
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
archs.each { arch ->
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl::$arch"
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl-glfw::$arch"
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl-openal::$arch"
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl-opengl::$arch"
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl-stb::$arch"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileJava.mustRunAfter addNativeDependencies // Make sure runtimeOnly has not been resolved
|
|
||||||
|
|
||||||
task requestLinuxDependencies {
|
|
||||||
description 'Adds linux, linux-arm64 and linux-arm32 native libraries to built artifacts.'
|
|
||||||
doFirst {
|
|
||||||
project.ext.platforms.addAll(['natives-linux', 'natives-linux-arm64', 'natives-linux-arm32'])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
task requestWindowsDependencies {
|
|
||||||
description 'Adds windows and windows-x86 native libraries to built artifacts.'
|
|
||||||
doFirst {
|
|
||||||
project.ext.platforms.addAll(['natives-windows', 'natives-windows-x86'])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
task requestMacOSDependencies {
|
|
||||||
description 'Adds macos native libraries to built artifacts.'
|
|
||||||
doFirst {
|
|
||||||
project.ext.platforms.addAll(['natives-macos'])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def dependencySpecificationTasks = tasks.findAll { task -> task.name.startsWith('request') && task.name.endsWith('Dependencies') }
|
|
||||||
|
|
||||||
task requestCrossPlatformDependencies {
|
|
||||||
description 'Adds native libraries for all available platforms to built artifacts.'
|
|
||||||
dependsOn dependencySpecificationTasks
|
|
||||||
}
|
|
||||||
|
|
||||||
addNativeDependencies.mustRunAfter dependencySpecificationTasks
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determines if the provided dependency should be packaged
|
|
||||||
*/
|
|
||||||
def isDependencyRequested(String dep) {
|
|
||||||
if (dep.endsWith(".jar")) {
|
|
||||||
dep = dep.substring(0, dep.length() - ".jar".length())
|
|
||||||
}
|
|
||||||
|
|
||||||
return !dep.contains("natives-") ||
|
|
||||||
project.ext.platforms.contains(dep.substring(dep.indexOf("natives-"), dep.length()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Manifest specification
|
|
||||||
*/
|
|
||||||
|
|
||||||
task specifyLocalManifest {
|
|
||||||
dependsOn addNativeDependencies // Make sure all native dependencies are specified
|
|
||||||
|
|
||||||
doFirst {
|
|
||||||
def classPath = []
|
|
||||||
|
|
||||||
configurations.runtimeClasspath.each {
|
|
||||||
if (isDependencyRequested(it.getName())) {
|
|
||||||
classPath.add("lib/" + it.getName())
|
|
||||||
} else {
|
} else {
|
||||||
println "\tRemoving from JAR classpath (not requested): " + it.getName()
|
def newVersion = "$major.$minor.$patch$suffix"
|
||||||
|
logger.info "Version parsed from Git: $newVersion"
|
||||||
|
return newVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (any) {
|
||||||
|
logger.warn "Could not parse version from tag \"$tag\""
|
||||||
|
return tag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (classPath.size() == configurations.runtimeClasspath.size()) {
|
Tag version_findRelevantTag(Grgit git) {
|
||||||
println "Nothing removed from JAR classpath"
|
def tags = git.tag.list()
|
||||||
|
|
||||||
|
def commits = [ git.head() ]
|
||||||
|
def visited = new HashSet<>()
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (commits.isEmpty()) return null
|
||||||
|
|
||||||
|
def nextCommits = new HashSet<>()
|
||||||
|
|
||||||
|
def formatSpecificationPrinted = false
|
||||||
|
for (def commit : commits) {
|
||||||
|
def tag = tags.findAll { it.commit == commit } ?.max { it.dateTime }
|
||||||
|
|
||||||
|
if (tag != null) {
|
||||||
|
if (tag.name ==~ tagFormat) {
|
||||||
|
return tag
|
||||||
|
} else {
|
||||||
|
if (!formatSpecificationPrinted) {
|
||||||
|
formatSpecificationPrinted = true
|
||||||
|
logger.info 'Expecting tag format: vMAJOR.MINOR.PATCH[-SUFFIX]'
|
||||||
|
}
|
||||||
|
logger.info 'Ignoring tag due to invalid format: {}', tag.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
nextCommits.addAll commit.parentIds.collect(git.resolve.&toCommit)
|
||||||
manifest {
|
}
|
||||||
attributes(
|
|
||||||
"Main-Class": "ru.windcorp.progressia.client.ProgressiaClientMain",
|
visited.addAll commits
|
||||||
"Class-Path": configurations.runtimeClasspath.collect { "lib/" + it.getName() } .findAll { isDependencyRequested(it) } .join(' ')
|
nextCommits.removeAll visited
|
||||||
|
commits = nextCommits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task resolveVersion {
|
||||||
|
description 'Resolves version information from Git repository or project properties.'
|
||||||
|
|
||||||
|
doFirst {
|
||||||
|
try {
|
||||||
|
def git = Grgit.open(dir: project.projectDir)
|
||||||
|
|
||||||
|
project.ext.commit = git.head().id
|
||||||
|
project.ext.branch = git.branch.current().name
|
||||||
|
|
||||||
|
if (project.version != 'unspecified') {
|
||||||
|
// Leave version as-is
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
def tag = version_findRelevantTag(git)
|
||||||
|
if (tag == null) {
|
||||||
|
|
||||||
|
String suffix
|
||||||
|
if (project.hasProperty('buildId')) {
|
||||||
|
suffix = project.buildId;
|
||||||
|
} else {
|
||||||
|
suffix = java.time.ZonedDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern('yyyy_MM_dd'))
|
||||||
|
}
|
||||||
|
project.version = "999.0.0-$suffix"
|
||||||
|
|
||||||
|
logger.warn 'Git repository does not contain an applicable tag, using dummy version {}\nSpecify version with -Pversion=1.2.3 or create a Git tag named v1.2.3', project.version
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
project.version = version_parseVersion(tag.name, tag.commit != git.head())
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (org.eclipse.jgit.errors.RepositoryNotFoundException e) {
|
||||||
|
if (project.version == 'unspecified') project.version = 'dev'
|
||||||
|
project.ext.commit = '-'
|
||||||
|
project.ext.branch = '-'
|
||||||
|
|
||||||
|
logger.warn 'No Git repository found in project root, using dummy version {}\nSpecify version with -Pversion=1.2.3 or create a Git tag named v1.2.3', project.version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
if (!project.hasProperty('buildId')) {
|
||||||
|
project.ext.buildId = '-'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configure JAR manifest
|
||||||
|
*/
|
||||||
|
|
||||||
|
task configureManifest {
|
||||||
|
description 'Populates JAR manifest with Main-Class, Class-Path and version metadata.'
|
||||||
|
|
||||||
|
jar.dependsOn configureManifest
|
||||||
|
dependsOn resolveVersion
|
||||||
|
|
||||||
|
doFirst {
|
||||||
|
jar.manifest.attributes(
|
||||||
|
'Main-Class': 'ru.windcorp.progressia.client.ProgressiaClientMain',
|
||||||
|
'Class-Path': configurations.runtimeClasspath.collect { "lib/${it.name}" } .join(' '),
|
||||||
|
|
||||||
|
'Specification-Title': 'Progressia',
|
||||||
|
|
||||||
|
'Implementation-Title': 'Progressia',
|
||||||
|
'Implementation-Version': project.version,
|
||||||
|
'Implementation-Version-Git-Commit': project.commit,
|
||||||
|
'Implementation-Version-Git-Branch': project.branch,
|
||||||
|
'Implementation-Version-BuildId': project.buildId,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jar.dependsOn specifyLocalManifest
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Library export
|
* Copy libraries into buil/libs/lib directory, next to Progressia.jar
|
||||||
*/
|
*/
|
||||||
|
|
||||||
task exportLibs(type: Sync) {
|
task exportLibs(type: Sync) {
|
||||||
mustRunAfter addNativeDependencies
|
description 'Copies runtime libraries into a subdirectory next to the output JAR.'
|
||||||
|
|
||||||
|
jar.dependsOn exportLibs
|
||||||
|
|
||||||
into libsDirectory.get().getAsFile().getPath() + "/lib"
|
|
||||||
exclude { !isDependencyRequested(it.getName()) }
|
|
||||||
from configurations.runtimeClasspath
|
from configurations.runtimeClasspath
|
||||||
|
into 'build/libs/lib'
|
||||||
}
|
}
|
||||||
|
|
||||||
jar.dependsOn(exportLibs)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Packaging
|
* Apply LWJGL logic
|
||||||
|
*/
|
||||||
|
apply from: 'build_logic/lwjgl.gradle'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Packaging working directory configuration
|
||||||
*/
|
*/
|
||||||
|
|
||||||
task packageDebian(type: Exec) {
|
import java.nio.file.*
|
||||||
description 'Builds the project and creates a Debain package.'
|
import java.nio.file.attribute.*
|
||||||
group 'Progressia'
|
|
||||||
|
|
||||||
dependsOn build
|
task createPackagingDirs() {
|
||||||
dependsOn requestLinuxDependencies
|
description 'Resets build/tmp/packaging directory.'
|
||||||
|
|
||||||
commandLine './buildPackages.sh', 'debian'
|
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
println "Debian package available in build_packages/"
|
def tmpDir = buildDir.toPath().resolve 'tmp/packaging'
|
||||||
|
|
||||||
|
def nuke = new SimpleFileVisitor<Path>() {
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
|
Files.delete(file);
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
|
||||||
|
if (e == null) {
|
||||||
|
Files.delete(dir);
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
} else {
|
||||||
|
// directory iteration failed
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task packageWindows(type: Exec) {
|
// Fckn nuke tmpDir from orbit
|
||||||
description 'Builds the project and creates a Windows installer.'
|
// I'm so done with deleting file trees in Java/Groovy/whatever
|
||||||
group 'Progressia'
|
// ...Not using File.deleteDir() because the latter recurses into symlinks, and we don't want to wipe build/libs/lib
|
||||||
|
if (Files.exists(tmpDir)) {
|
||||||
|
Files.walkFileTree tmpDir, nuke
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.createDirectories tmpDir.resolve('workingDir')
|
||||||
|
Files.createDirectories buildDir.toPath().resolve('packages')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task linkBuildOutputForPackaging() {
|
||||||
|
description 'Symlinks the contents of build/libs into packaging working directory.'
|
||||||
|
|
||||||
dependsOn build
|
dependsOn build
|
||||||
dependsOn requestWindowsDependencies
|
dependsOn createPackagingDirs
|
||||||
|
|
||||||
commandLine './buildPackages.sh', 'windows'
|
onlyIf { preparePackaging.ext.mode == 'symlink' }
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
println "Windows installer available in build_packages/"
|
def from = buildDir.toPath().resolve 'libs'
|
||||||
|
def into = buildDir.toPath().resolve "tmp/packaging/workingDir/${preparePackaging.ext.buildDest}"
|
||||||
|
|
||||||
|
Files.createDirectories into
|
||||||
|
|
||||||
|
Files.list(from).each {
|
||||||
|
def fileName = it.fileName.toString()
|
||||||
|
|
||||||
|
// Exclude all JARs except the current one
|
||||||
|
if (fileName ==~ "${project.name}.*\\.jar" && fileName != tasks.jar.archiveFileName.get())
|
||||||
|
return
|
||||||
|
|
||||||
|
Files.createSymbolicLink into.resolve(it.fileName), it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task copyBuildOutputForPackaging(type: Copy) {
|
||||||
|
description 'Copies the contents of build/libs into packaging working directory.'
|
||||||
|
|
||||||
|
dependsOn build
|
||||||
|
dependsOn createPackagingDirs
|
||||||
|
|
||||||
|
onlyIf { preparePackaging.ext.mode == 'copy' }
|
||||||
|
|
||||||
|
from 'build/libs'
|
||||||
|
filesMatching("${project.name}*.jar") {
|
||||||
|
include tasks.jar.archiveFileName.get()
|
||||||
|
}
|
||||||
|
into "build/tmp/packaging/workingDir/${ -> preparePackaging.ext.buildDest}"
|
||||||
|
}
|
||||||
|
|
||||||
|
task preparePackaging {
|
||||||
|
preparePackaging.ext.buildDest = ''
|
||||||
|
preparePackaging.ext.mode = 'symlink'
|
||||||
|
|
||||||
|
dependsOn createPackagingDirs
|
||||||
|
dependsOn linkBuildOutputForPackaging
|
||||||
|
dependsOn copyBuildOutputForPackaging
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apply all packaging scripts
|
||||||
|
*/
|
||||||
|
|
||||||
|
new File(projectDir, 'build_logic/packaging').list().each {
|
||||||
|
apply from: "build_logic/packaging/$it/script.gradle"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure no more than one packaging task is scheduled
|
||||||
|
*/
|
||||||
|
|
||||||
|
gradle.taskGraph.whenReady { graph ->
|
||||||
|
if (graph.allTasks.count { it.name ==~ /package[^_]*/ } > 1) {
|
||||||
|
def offenders = graph.allTasks.findAll { it.name ==~ /package[^_]*/ }
|
||||||
|
throw new GradleException("Cannot execute multiple package tasks within a single build\n" +
|
||||||
|
"\tOffending tasks: $offenders")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convenience build tasks
|
||||||
|
*/
|
||||||
|
|
||||||
task buildCrossPlatform {
|
task buildCrossPlatform {
|
||||||
description 'Builds the project including native libraries for all available platforms.'
|
description 'Builds the project including native libraries for all available platforms.'
|
||||||
@@ -299,17 +391,17 @@ task buildCrossPlatform {
|
|||||||
dependsOn build
|
dependsOn build
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
println "Native libraries for all platforms have been added"
|
logger.info 'Native libraries for all platforms have been added'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task buildLocal {
|
task buildLocal {
|
||||||
description "Builds the project including only native libraries for current platform ($lwjglNatives)."
|
description "Builds the project including only native libraries for current platform (${lwjgl.localArch})."
|
||||||
group 'Progressia'
|
group 'Progressia'
|
||||||
|
|
||||||
dependsOn build
|
dependsOn build
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
println "Native libraries only for platform $lwjglNatives have been added"
|
logger.info "Native libraries only for platform ${lwjgl.localArch} have been added"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
191
buildPackages.sh
191
buildPackages.sh
@@ -1,191 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
#
|
|
||||||
# Progressia
|
|
||||||
# Copyright (C) 2020-2021 Wind Corporation and contributors
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
|
|
||||||
echoerr() { echo "$@" 1>&2; }
|
|
||||||
|
|
||||||
buildDebianPackage() {
|
|
||||||
|
|
||||||
# Commands that must be available to execute this action
|
|
||||||
requiredCommands='dpkg-deb fakeroot'
|
|
||||||
|
|
||||||
# Package name. Sync with control file manually!
|
|
||||||
name='progressia-techdemo'
|
|
||||||
# Version that the package will receive. Sync with control file manually!
|
|
||||||
version='1.0_all'
|
|
||||||
|
|
||||||
# This directory will be copied into $tmpDir
|
|
||||||
templateDirectory="build_packages/DEB/template"
|
|
||||||
|
|
||||||
# Files that must be present
|
|
||||||
requiredFiles="$templateDirectory/DEBIAN/control"
|
|
||||||
|
|
||||||
nameAndVersion="$name-$version"
|
|
||||||
tmpDir="build_packages/DEB/$nameAndVersion"
|
|
||||||
outputFile="build_packages/DEB/$nameAndVersion.deb"
|
|
||||||
|
|
||||||
echo "Checking environment to build Debian package"
|
|
||||||
|
|
||||||
for item in $requiredCommands; do
|
|
||||||
if command -v "$item" &> /dev/null; then
|
|
||||||
echo "- $item found"
|
|
||||||
else
|
|
||||||
echoerr "Command $item not found, cannot package"
|
|
||||||
exit 100
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
for file in $requiredFiles; do
|
|
||||||
if ! [ -r "$file" ]; then
|
|
||||||
echoerr "$file is missing or not readable, cannot package"
|
|
||||||
exit 101
|
|
||||||
else
|
|
||||||
echo "- $file is present and readable"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Environment OK; packaging Debian package"
|
|
||||||
exitCode=0
|
|
||||||
|
|
||||||
{
|
|
||||||
shareDir="$tmpDir/usr/share/progressia"
|
|
||||||
|
|
||||||
mkdir -p "$tmpDir" &&
|
|
||||||
mkdir -p "$shareDir" &&
|
|
||||||
cp -r "$templateDirectory"/* "$tmpDir" &&
|
|
||||||
cp -r 'build/libs/lib' "$shareDir/lib" &&
|
|
||||||
cp 'build/libs/Progressia.jar' "$shareDir/Progressia.jar" &&
|
|
||||||
echo "------ DPKG-DEB ------" &&
|
|
||||||
fakeroot dpkg-deb --build "$tmpDir" &&
|
|
||||||
echo "---- DPKG-DEB END ----" &&
|
|
||||||
mv "$outputFile" build_packages
|
|
||||||
} || {
|
|
||||||
echoerr "Could not create Debian package"
|
|
||||||
exitCode=1
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
if [ -d "$tmpDir" ]; then
|
|
||||||
rm -r "$tmpDir"
|
|
||||||
fi
|
|
||||||
echo "Cleaned up"
|
|
||||||
} || {
|
|
||||||
echoerr "Could not clean up after packaging Debian package"
|
|
||||||
exitCode=2
|
|
||||||
}
|
|
||||||
|
|
||||||
exit "$exitCode"
|
|
||||||
}
|
|
||||||
|
|
||||||
buildWindowsInstaller() {
|
|
||||||
|
|
||||||
# Commands that must be available to execute this action
|
|
||||||
requiredCommands='makensis'
|
|
||||||
|
|
||||||
# NSIS configuration file that must be present
|
|
||||||
configurationFile='build_packages/NSIS/ProgressiaInstaller.nsi'
|
|
||||||
|
|
||||||
# File that will be output
|
|
||||||
outputFile='build_packages/NSIS/ProgressiaInstaller.exe'
|
|
||||||
|
|
||||||
echo "Checking environment to build Windows installer"
|
|
||||||
|
|
||||||
for item in $requiredCommands; do
|
|
||||||
if command -v "$item" &> /dev/null; then
|
|
||||||
echo "- $item found"
|
|
||||||
else
|
|
||||||
echoerr "Command $item not found, cannot build"
|
|
||||||
exit 100
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if ! [ -r "$configurationFile" ]; then
|
|
||||||
echoerr "$configurationFile is missing or not readable, cannot build"
|
|
||||||
exit 101
|
|
||||||
else
|
|
||||||
echo "- $configurationFile is present and readable"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Environment OK; building Windows installer"
|
|
||||||
exitCode=0
|
|
||||||
|
|
||||||
{
|
|
||||||
cp -r 'build/libs/lib' 'build_packages/NSIS/lib' &&
|
|
||||||
cp 'build/libs/Progressia.jar' 'build_packages/NSIS/Progressia.jar' &&
|
|
||||||
cp 'LICENSE' 'build_packages/NSIS/LICENSE.txt' &&
|
|
||||||
echo "------ NSIS ------" &&
|
|
||||||
makensis "$configurationFile" &&
|
|
||||||
echo "---- NSIS END ----" &&
|
|
||||||
mv "$outputFile" build_packages
|
|
||||||
} || {
|
|
||||||
echoerr "Could not build Windows installer"
|
|
||||||
exitCode=1
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
if [ -d 'build_packages/NSIS/lib' ]; then
|
|
||||||
rm -r 'build_packages/NSIS/lib'
|
|
||||||
fi
|
|
||||||
if [ -e 'build_packages/NSIS/Progressia.jar' ]; then
|
|
||||||
rm 'build_packages/NSIS/Progressia.jar'
|
|
||||||
fi
|
|
||||||
if [ -e 'build_packages/NSIS/LICENSE.txt' ]; then
|
|
||||||
rm 'build_packages/NSIS/LICENSE.txt'
|
|
||||||
fi
|
|
||||||
echo "Cleaned up"
|
|
||||||
} || {
|
|
||||||
echoerr "Could not clean up after building Windows installer"
|
|
||||||
exitCode=2
|
|
||||||
}
|
|
||||||
|
|
||||||
exit "$exitCode"
|
|
||||||
}
|
|
||||||
|
|
||||||
printUsage() {
|
|
||||||
echoerr "Usage: $0 TARGET"
|
|
||||||
echoerr " where TARGET is 'debian' or 'windows'"
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
echoerr "Too many arguments."
|
|
||||||
printUsage
|
|
||||||
exit 202
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
"debian")
|
|
||||||
buildDebianPackage
|
|
||||||
;;
|
|
||||||
"windows")
|
|
||||||
buildWindowsInstaller
|
|
||||||
;;
|
|
||||||
"")
|
|
||||||
echoerr "No action specified"
|
|
||||||
printUsage
|
|
||||||
exit 200
|
|
||||||
;;
|
|
||||||
"--help" | "-help" | "help" | "?")
|
|
||||||
printUsage
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echoerr "Unknown action '$1'"
|
|
||||||
printUsage
|
|
||||||
exit 201
|
|
||||||
;;
|
|
||||||
esac
|
|
117
build_logic/lwjgl.gradle
Normal file
117
build_logic/lwjgl.gradle
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* Build logic for Progressia
|
||||||
|
* LWJGL dependency logic
|
||||||
|
*/
|
||||||
|
|
||||||
|
project.ext.lwjgl = new HashMap<>()
|
||||||
|
|
||||||
|
// Version of LWJGL
|
||||||
|
lwjgl.version = '3.2.3'
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Target platforms for current operation.
|
||||||
|
* This is filled in by the request* tasks. This is referenced by the addLwjglNatives task.
|
||||||
|
* When empty, current platform is assumed.
|
||||||
|
*/
|
||||||
|
lwjgl.targets = new HashSet<>()
|
||||||
|
|
||||||
|
// LWJGL components. To include org.lwjgl:lwjgl-foobar, add 'foobar' to this list.
|
||||||
|
lwjgl.libraries = [
|
||||||
|
'opengl',
|
||||||
|
'glfw',
|
||||||
|
'openal',
|
||||||
|
'stb'
|
||||||
|
]
|
||||||
|
|
||||||
|
// Determine the architecture of the build environment
|
||||||
|
import org.gradle.internal.os.OperatingSystem
|
||||||
|
switch (OperatingSystem.current()) {
|
||||||
|
case OperatingSystem.LINUX:
|
||||||
|
def osArch = System.getProperty('os.arch')
|
||||||
|
lwjgl.localArch = osArch.startsWith('arm') || osArch.startsWith('aarch64')
|
||||||
|
? "linux-${osArch.contains('64') || osArch.startsWith('armv8') ? 'arm64' : 'arm32'}"
|
||||||
|
: 'linux'
|
||||||
|
break
|
||||||
|
case OperatingSystem.MAC_OS:
|
||||||
|
lwjgl.localArch = 'macos'
|
||||||
|
break
|
||||||
|
case OperatingSystem.WINDOWS:
|
||||||
|
lwjgl.localArch = System.getProperty('os.arch').contains('64') ? 'windows' : 'windows-x86'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare pure-Java dependencies
|
||||||
|
dependencies {
|
||||||
|
// BOM
|
||||||
|
implementation platform("org.lwjgl:lwjgl-bom:${lwjgl.version}")
|
||||||
|
|
||||||
|
// Core
|
||||||
|
implementation 'org.lwjgl:lwjgl'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
lwjgl.libraries.each { implementation "org.lwjgl:lwjgl-$it" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds LWJGL native libraries to runtimeOnly configuration
|
||||||
|
*/
|
||||||
|
task lwjgl_addNativesToRuntimeOnly {
|
||||||
|
// Make sure runtimeOnly has not been resolved
|
||||||
|
compileJava.dependsOn lwjgl_addNativesToRuntimeOnly
|
||||||
|
configureManifest.dependsOn lwjgl_addNativesToRuntimeOnly
|
||||||
|
exportLibs.dependsOn lwjgl_addNativesToRuntimeOnly
|
||||||
|
|
||||||
|
doFirst {
|
||||||
|
if (project.hasProperty('forceTargets')) {
|
||||||
|
try {
|
||||||
|
def oldTargets = lwjgl.targets.join(',')
|
||||||
|
|
||||||
|
lwjgl.targets.clear()
|
||||||
|
lwjgl.targets.addAll project.forceTargets.split(',')*.trim().collect { it == 'local' ? lwjgl.localArch : it }
|
||||||
|
|
||||||
|
logger.info 'Overriding selected platforms {} with {}', oldTargets, lwjgl.targets.join(',')
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new GradleException("Could not parse forceTargets \"${project.forceTargets}\", expecting platform-1,platform-2,local", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lwjgl.targets.isEmpty()) {
|
||||||
|
logger.info 'Adding LWJGL native dependencies for local platform only: {}', lwjgl.localArch
|
||||||
|
lwjgl.targets.add lwjgl.localArch
|
||||||
|
} else {
|
||||||
|
logger.info 'Adding LWJGL native dependencies for platforms: {}', lwjgl.targets.sort().join(', ')
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
lwjgl.targets.each { target ->
|
||||||
|
runtimeOnly "org.lwjgl:lwjgl::natives-$target"
|
||||||
|
lwjgl.libraries.each { lib ->
|
||||||
|
runtimeOnly "org.lwjgl:lwjgl-$lib::natives-$target"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task requestCrossPlatformDependencies {
|
||||||
|
description 'Adds LWJGL natives for all available platforms.'
|
||||||
|
|
||||||
|
lwjgl_addNativesToRuntimeOnly.mustRunAfter requestCrossPlatformDependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
def requestTask(String name, String... targets) {
|
||||||
|
def theTask = task "request${name}Dependencies"
|
||||||
|
|
||||||
|
theTask.doFirst {
|
||||||
|
lwjgl.targets.addAll targets
|
||||||
|
}
|
||||||
|
|
||||||
|
theTask.description "Adds LWJGL natives for $name (${targets.join(', ')})."
|
||||||
|
|
||||||
|
requestCrossPlatformDependencies.dependsOn theTask
|
||||||
|
lwjgl_addNativesToRuntimeOnly.mustRunAfter theTask
|
||||||
|
}
|
||||||
|
|
||||||
|
requestTask 'Linux', 'linux', 'linux-arm32', 'linux-arm64'
|
||||||
|
requestTask 'Windows', 'windows', 'windows-x86'
|
||||||
|
requestTask 'MacOS', 'macos'
|
38
build_logic/packaging/deb/script.gradle
Normal file
38
build_logic/packaging/deb/script.gradle
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
task packageDeb_processResources(type: Copy) {
|
||||||
|
dependsOn resolveVersion
|
||||||
|
dependsOn preparePackaging
|
||||||
|
|
||||||
|
from 'src/packaging/deb'
|
||||||
|
|
||||||
|
filesMatching('DEBIAN/control') {
|
||||||
|
expand(version: { -> project.version})
|
||||||
|
}
|
||||||
|
|
||||||
|
into 'build/tmp/packaging/workingDir'
|
||||||
|
}
|
||||||
|
|
||||||
|
task packageDeb_configure() {
|
||||||
|
preparePackaging.mustRunAfter packageDeb_configure
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
tasks.preparePackaging.ext.buildDest = '/usr/share/progressia'
|
||||||
|
tasks.preparePackaging.ext.mode = 'copy'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task packageDeb(type: Exec) {
|
||||||
|
description 'Builds the project and creates a Debian package.'
|
||||||
|
group 'Progressia'
|
||||||
|
|
||||||
|
dependsOn packageDeb_configure
|
||||||
|
dependsOn requestLinuxDependencies
|
||||||
|
dependsOn build
|
||||||
|
dependsOn preparePackaging
|
||||||
|
|
||||||
|
dependsOn packageDeb_processResources
|
||||||
|
|
||||||
|
executable 'dpkg-deb'
|
||||||
|
args '--root-owner-group'
|
||||||
|
args '--build', 'build/tmp/packaging/workingDir'
|
||||||
|
args 'build/packages'
|
||||||
|
}
|
50
build_logic/packaging/nsis/script.gradle
Normal file
50
build_logic/packaging/nsis/script.gradle
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
task packageNsis_processResources(type: Copy) {
|
||||||
|
dependsOn preparePackaging
|
||||||
|
|
||||||
|
from ('src/packaging/nsis') {
|
||||||
|
exclude 'left_side.png'
|
||||||
|
}
|
||||||
|
from('LICENSE') {
|
||||||
|
rename 'LICENSE', 'LICENSE.txt'
|
||||||
|
}
|
||||||
|
into 'build/tmp/packaging/workingDir'
|
||||||
|
}
|
||||||
|
|
||||||
|
task packageNsis_generateIcon(type: Exec) {
|
||||||
|
mustRunAfter preparePackaging
|
||||||
|
|
||||||
|
executable 'convert'
|
||||||
|
args files('src/main/resources/assets/icons/*.original.png').files*.path
|
||||||
|
args 'build/tmp/packaging/workingDir/logo.ico'
|
||||||
|
}
|
||||||
|
|
||||||
|
task packageNsis_generateLeftSide(type: Exec) {
|
||||||
|
mustRunAfter preparePackaging
|
||||||
|
|
||||||
|
executable 'convert'
|
||||||
|
args 'src/packaging/nsis/left_side.png'
|
||||||
|
args '-alpha', 'off'
|
||||||
|
args 'BMP3:build/tmp/packaging/workingDir/left_side.bmp'
|
||||||
|
}
|
||||||
|
|
||||||
|
task packageNsis(type: Exec) {
|
||||||
|
description 'Builds the project and creates a Windows NSIS installer.'
|
||||||
|
group 'Progressia'
|
||||||
|
|
||||||
|
dependsOn requestWindowsDependencies
|
||||||
|
dependsOn build
|
||||||
|
dependsOn resolveVersion
|
||||||
|
dependsOn preparePackaging
|
||||||
|
|
||||||
|
dependsOn packageNsis_processResources
|
||||||
|
dependsOn packageNsis_generateIcon
|
||||||
|
dependsOn packageNsis_generateLeftSide
|
||||||
|
|
||||||
|
executable 'makensis'
|
||||||
|
args '-NOCONFIG'
|
||||||
|
args "-DPROJECT_NAME=${project.name}"
|
||||||
|
args "-DPROJECT_VERSION=${ -> project.version}"
|
||||||
|
args "-DMAIN_JAR_FILE=${ -> project.tasks.jar.archiveFileName.get()}"
|
||||||
|
args "-DOUTPUT_DIR=${project.buildDir.absolutePath}/packages"
|
||||||
|
args 'build/tmp/packaging/workingDir/config.nsi'
|
||||||
|
}
|
41
build_logic/packaging/zip/script.gradle
Normal file
41
build_logic/packaging/zip/script.gradle
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
task packageZip_processResources(type: Copy) {
|
||||||
|
dependsOn preparePackaging
|
||||||
|
|
||||||
|
from ('src/packaging/zip') {
|
||||||
|
filesMatching('start.*') {
|
||||||
|
filter(
|
||||||
|
org.apache.tools.ant.filters.ReplaceTokens,
|
||||||
|
tokens: [mainJarFile: project.tasks.jar.archiveFileName.get()]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
from ('src/main/resource/assets/icons/logo256.original.png') {
|
||||||
|
rename 'logo256.original.png', 'logo.png'
|
||||||
|
}
|
||||||
|
from('LICENSE') {
|
||||||
|
rename 'LICENSE', 'LICENSE.txt'
|
||||||
|
}
|
||||||
|
into 'build/tmp/packaging/workingDir'
|
||||||
|
}
|
||||||
|
|
||||||
|
task packageZip(type: Zip) {
|
||||||
|
description 'Builds the project and creates a cross-platform ZIP package.'
|
||||||
|
group 'Progressia'
|
||||||
|
|
||||||
|
dependsOn resolveVersion
|
||||||
|
dependsOn requestCrossPlatformDependencies
|
||||||
|
dependsOn build
|
||||||
|
dependsOn preparePackaging
|
||||||
|
|
||||||
|
dependsOn packageZip_processResources
|
||||||
|
|
||||||
|
archiveBaseName = project.name
|
||||||
|
archiveAppendix = 'universal'
|
||||||
|
|
||||||
|
doFirst {
|
||||||
|
archiveVersion = project.version
|
||||||
|
}
|
||||||
|
|
||||||
|
from 'build/tmp/packaging/workingDir'
|
||||||
|
destinationDirectory = file('build/packages')
|
||||||
|
}
|
Binary file not shown.
Before Width: | Height: | Size: 151 KiB |
Binary file not shown.
Before Width: | Height: | Size: 187 KiB |
12
docs/ProgressiaRegion.md
Normal file
12
docs/ProgressiaRegion.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Progressia Region File
|
||||||
|
## Description
|
||||||
|
The `.progressia_region` file type is used for all region files in the game Progressia. Each region file contains a cube of 16x16x16 chunks.
|
||||||
|
## Header
|
||||||
|
The header of the file is 16 400 bytes. Every file starts with the string byte sequence `\x50\x52\x4F\x47` (UTF-8 for `PROG`), followed with the three integer values of the region position, in region coordinates. After this, there is exactly 16KiB of space in the header, which stores the offsets to the chunks' data. This space holds an integer, 4 bytes, for each chunk in the region. The integer value starts at 0 for every chunk, and is changed to the location of the chunk data once created. These are indexed in order by flattening the 3D in-chunk coordinates into a number between 0 and 4095 according to the formula `offset = 256*x+ 16*y + z` for chunk at (x, y, z). To convert from this offset value to the offset in bytes, use `byte_offset = 16400 + 64*n`.
|
||||||
|
## Sectors
|
||||||
|
Sectors are what is used to store chunk data, and are not linear, but are followed until they reach an ending block. Each is 64 bytes, which is used in the header section to find the byte offset. Each sector starts with a identification byte, followed by the sector data.
|
||||||
|
|
||||||
|
0. Ending - This sector is empty, and marks the end of the chunk data (This may change in the future.
|
||||||
|
1. Data - This sector contains chunk data for a single chunk. The second byte of this sector contains a counter byte, which is a form of "checksum" to make sure that the program is reading the proper sectors in order. This starts at 0 for the first data sector and increments by one for each new data sector.
|
||||||
|
2. Partition Link - This sector only contains another offset value, which is where the next sector is. This allows for infinite chunk size, avoiding "chunk dupes" as were present in Minecraft without reverting any chunks.
|
||||||
|
3. Bulk Data - These would be used for many chunks in the same region that contain exactly the same data, e.g. all solid chunks underground. Exists so the program knows not to overwrite them, and just make new chunk data if modified. Not yet implemented.
|
@@ -1,8 +1,10 @@
|
|||||||
# Build Guide
|
# Build Guide
|
||||||
|
|
||||||
This document is a guide to building Progressia from source.
|
This document is a guide to building Progressia from source. For quick reference, see
|
||||||
|
[Build Script Reference](BuildScriptReference.md).
|
||||||
|
|
||||||
Compilation should be possible on all platforms that support JDK 8 or later, however, packaging scripts require Bash.
|
Compilation should be possible on all platforms that support JDK 8 or later, however, packaging scripts require
|
||||||
|
additional programs in `PATH`.
|
||||||
|
|
||||||
This guide assumes you are familiar with using a terminal or Windows Command Prompt or PowerShell.
|
This guide assumes you are familiar with using a terminal or Windows Command Prompt or PowerShell.
|
||||||
|
|
||||||
@@ -150,54 +152,46 @@ GNU/Linux and Windows natives:
|
|||||||
./gradlew build requestLinuxDependencies requestWindowsDependencies
|
./gradlew build requestLinuxDependencies requestWindowsDependencies
|
||||||
```
|
```
|
||||||
|
|
||||||
For finer control please edit `build.gradle` manually by adding the desired natives to the `project.ext.platforms` set like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
project.ext.platforms = new HashSet<>()
|
|
||||||
project.ext.platforms.add 'natives-windows-x86'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Packaging
|
## Packaging
|
||||||
|
|
||||||
A Debian package and a Windows installer can be created automatically on systems that support Bash. These tasks are delegated
|
A universal ZIP distribution, a Debian package and a Windows NSIS installer may be created automatically by the build
|
||||||
by Gradle to `buildPackages.sh` in repository root. This script checks the environment and assembles the requested output; the
|
script.
|
||||||
resulting files are moved into `build_packages`.
|
|
||||||
|
### Creating a universal ZIP package
|
||||||
|
|
||||||
|
A universal cross-platform ZIP archive can be created with the following Gradle task:
|
||||||
|
|
||||||
|
```
|
||||||
|
./gradlew packageZip
|
||||||
|
```
|
||||||
|
|
||||||
|
Gradle will then build all artifacts necessary to run the game on all available platforms and package game files,
|
||||||
|
libraries, launch scripts, etc. into a compressed ZIP archive.
|
||||||
|
|
||||||
|
The resulting file can be found in `build/packages/`
|
||||||
|
|
||||||
### Creating a Debian package
|
### Creating a Debian package
|
||||||
|
|
||||||
A Debian package can be created with the following Gradle task:
|
A Debian package can be created with the following Gradle task:
|
||||||
|
|
||||||
```
|
```
|
||||||
./gradlew packageDebian
|
./gradlew packageDeb
|
||||||
```
|
```
|
||||||
|
|
||||||
Gradle will then build all artifacts necessary to run the game on GNU/Linux (all three architectures) and invoke
|
Gradle will then build all artifacts necessary to run the game on GNU/Linux (all three architectures) and invoke
|
||||||
`./buildPackages.sh debian`. Commands `dpkg-deb` and `fakeroot` must be available in system path in order to build the package.
|
`dpkg-deb`. Commands `dpkg-deb` must be available in system path in order to build the package.
|
||||||
|
|
||||||
### Creating a Windows installer
|
### Creating a Windows installer
|
||||||
|
|
||||||
A Windows installer can be created with the following Gradle task:
|
A Windows NSIS installer can be created with the following Gradle task:
|
||||||
|
|
||||||
```
|
```
|
||||||
./gradlew packageWindows
|
./gradlew packageNsis
|
||||||
```
|
```
|
||||||
|
|
||||||
Gradle will then build all artifacts necessary to run the game on Windows (both x64 and x86 architectures) and invoke
|
Gradle will then build all artifacts necessary to run the game on Windows (both x64 and x86 architectures) and invoke
|
||||||
`./buildPackages.sh windows`.
|
`makensis`.
|
||||||
|
|
||||||
Windows installers are implemented with [NSIS](https://nsis.sourceforge.io/). Command `makensis` must be available in system
|
Windows installers are implemented with [NSIS](https://nsis.sourceforge.io/). [ImageMagick](https://imagemagick.org),
|
||||||
path in order to build the installer.
|
a command-line image editing tool, is used to generate some assets for the installer. Commands `makensis` and
|
||||||
|
`convert` (from ImageMagick) must be available in system path in order to build the installer.
|
||||||
## Gradle tasks summary
|
|
||||||
|
|
||||||
- `buildLocal` – creates a build optimized for current platform. Use this to quickly build the game during development.
|
|
||||||
- `buildCrossPlatform` – creates a build that supports all known architectures. Use this to build a universal version of the game.
|
|
||||||
- `build` – currently a synonym of `buildLocal`; creates a default build.
|
|
||||||
- `packageDebian` – creates a Debian package. Do not invoke together with `packageWindows`.
|
|
||||||
- `packageWindows` – creates a Windows installer. Do not invoke together with `packageDebian`.
|
|
||||||
- `requestLinuxDependencies` – requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included when building.
|
|
||||||
- `requestWindowsDependencies` – requests that `natives-windows` and `natives-windows-x86` binaries are included when building.
|
|
||||||
- `requestMacOSDependencies` – requests that `natives-macos` binaries are included when building.
|
|
||||||
- `requestCrossPlatformDependencies` – requests that all binaries are included when building.
|
|
||||||
|
|
||||||
All other basic and Java-related Gradle tasks are available as well.
|
|
||||||
|
103
docs/building/BuildScriptReference.md
Normal file
103
docs/building/BuildScriptReference.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# Build Script Reference
|
||||||
|
|
||||||
|
This document is a user's reference for the build script of Progressia. For a beginner-friendly guide, see
|
||||||
|
[Build Guide](BuildGuide.md).
|
||||||
|
|
||||||
|
## Gradle tasks summary
|
||||||
|
|
||||||
|
- `buildLocal` – creates a build optimized for current platform. Use this to quickly build the game during development.
|
||||||
|
- `buildCrossPlatform` – creates a build that supports all known architectures. Use this to build a universal version of the game.
|
||||||
|
- `build` – currently a synonym of `buildLocal`; creates a default build.
|
||||||
|
- `packageZip` – creates a universal ZIP. Incompatible with other `package` tasks.
|
||||||
|
- `packageDeb` – creates a Debian package. Incompatible with other `package` tasks.
|
||||||
|
- `packageNsis` – creates a Windows NSIS installer. Incompatible with other `package` tasks.
|
||||||
|
- `requestLinuxDependencies` – requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included when building.
|
||||||
|
- `requestWindowsDependencies` – requests that `natives-windows` and `natives-windows-x86` binaries are included when building.
|
||||||
|
- `requestMacOSDependencies` – requests that `natives-macos` binaries are included when building.
|
||||||
|
- `requestCrossPlatformDependencies` – requests that all binaries are included when building.
|
||||||
|
|
||||||
|
To execute a task, run `./gradlew <task-name>`.
|
||||||
|
|
||||||
|
`build`-type tasks output the executable JAR and all libraries required at runtime into `build/libs`. `package`-type
|
||||||
|
tasks output packages into `build/packages`.
|
||||||
|
|
||||||
|
## Packaging tasks
|
||||||
|
|
||||||
|
Some packaging tasks require additional software in `PATH`.
|
||||||
|
|
||||||
|
| Task | Commands required in `PATH` |
|
||||||
|
|---------------|------------------------------------------|
|
||||||
|
| `packageDeb` | `dpkg-deb` |
|
||||||
|
| `packageZip` | _none_ |
|
||||||
|
| `packageNsis` | `makensis`, `convert` (from ImageMagick) |
|
||||||
|
|
||||||
|
## Version and metadata
|
||||||
|
|
||||||
|
### Version scheme
|
||||||
|
|
||||||
|
Progressia builds are identified by four parameters: version, Git commit, Git branch and build ID.
|
||||||
|
|
||||||
|
Versions roughly follow [semantic versioning](https://semver.org/spec/v2.0.0.html), with each version fitting the
|
||||||
|
`MAJOR.MINOR.PATCH[-SUFFIX]` pattern. Depending on the build environment (see below), version is either "real" with
|
||||||
|
no metadata (e.g. `0.43.2` or `1.2.1-beta`) or a dummy fallback with build metadata (e.g. `999.0.0-2021_07_23` or
|
||||||
|
`999.0.0-WJ3`).
|
||||||
|
|
||||||
|
### Version detection
|
||||||
|
|
||||||
|
Build script considers three scenarios when determining the version:
|
||||||
|
|
||||||
|
1. `version` project property is set explicitly. This may be done in a variety of ways, for example with command line
|
||||||
|
argument `-Pversion=1.2.3`
|
||||||
|
(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties))
|
||||||
|
2. Local Git repository is found, and HEAD is tagged appropriately: version is the tag name with leading `v`
|
||||||
|
stripped. Example: `v1.2.3` is version `1.2.3`
|
||||||
|
3. Local Git repository is found, and some ancestor of HEAD is tagged appropriately: version is the tag name with
|
||||||
|
leading `v` stripped and PATCH incremented by one. Example: `v1.2.3` is version `1.2.4`
|
||||||
|
|
||||||
|
Tags not named like `vMAJOR.MINOR.PATCH[-SUFFIX]` are ignored for cases 2 and 3.
|
||||||
|
|
||||||
|
In all other cases, a fallback dummy value is used for version, appended with build ID or current date.
|
||||||
|
|
||||||
|
### Git metadata
|
||||||
|
|
||||||
|
Git commit and Git branch are correspond to the state of the local Git repository, if any. In case Git metadata is
|
||||||
|
unavailable, `-` fallback is used for both fields.
|
||||||
|
|
||||||
|
### Build ID
|
||||||
|
|
||||||
|
Build ID uniquely identifies artifacts produced by automated build systems. For example, builds executed by WindCorp
|
||||||
|
Jenkins suite have build IDs like `WJ3` or `WJ142`. Build ID must be provided explicitly; it is `-` unless specified
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
Build ID may be set with `buildId` project property. This may be done in a variety of ways, for example with command
|
||||||
|
line argument `-PbuildId=WJ3`
|
||||||
|
(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties)).
|
||||||
|
|
||||||
|
## Native libraries
|
||||||
|
|
||||||
|
LWJGL uses native libraries. Build script declares platform-specific dependencies based on the set of target
|
||||||
|
platforms, `project.ext.lwjgl.targets` (aka `lwjgl.targets`). These dependencies are added to `runtimeOnly`
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
When this set is empty, the script selects natives for current platform. Otherwise, all platforms in the set are
|
||||||
|
included.
|
||||||
|
|
||||||
|
`lwjgl.targets` is populated automatically by packaging tasks and by `buildCrossPlatform`. To add extra targets,
|
||||||
|
``requestXxxDependencies` tasks may be used.
|
||||||
|
|
||||||
|
Target selection mechanism may be overridden with `forceTargets` project property. This may be done in a variety of
|
||||||
|
ways, for example with command line argument `-PforceTargets=windows-x86,local`
|
||||||
|
(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties)). The
|
||||||
|
value is a comma-separated list of target architectures. `local` target will be replaced with the automatically
|
||||||
|
detected current architecture.
|
||||||
|
|
||||||
|
### Available targets
|
||||||
|
|
||||||
|
| Name | Task |
|
||||||
|
|---------------|------------------------------|
|
||||||
|
| `linux` | `requestLinuxDependencies` |
|
||||||
|
| `linux-arm32` | `requestLinuxDependencies` |
|
||||||
|
| `linux-arm64` | `requestLinuxDependencies` |
|
||||||
|
| `windows` | `requestWindowsDependencies` |
|
||||||
|
| `windows-x86` | `requestWindowsDependencies` |
|
||||||
|
| `macos` | `requestMacOSDependencies` |
|
@@ -42,3 +42,16 @@ Run configurations are used by Intellij IDEA to specify how a project must be ru
|
|||||||
9. Click 'Apply' to save changes.
|
9. Click 'Apply' to save changes.
|
||||||
|
|
||||||
Step 8 is required to specify that the game must run in some directory other than the project root, which is the default in Intellij IDEA.
|
Step 8 is required to specify that the game must run in some directory other than the project root, which is the default in Intellij IDEA.
|
||||||
|
|
||||||
|
### Applying formatting templates
|
||||||
|
|
||||||
|
Windcorp's Progressia repository is formatted with a style defined for Eclipse IDE (sic) in
|
||||||
|
`templates_and_presets/eclipse_ide`.
|
||||||
|
Please apply these templates to the project to automatically format the source in a similar fashion.
|
||||||
|
|
||||||
|
1. In project context menu, click 'File->Properties'. (`Ctrl+Alt+S`)
|
||||||
|
2. In 'Editor' > 'Code Style' > 'Java', press gear icon, then click 'Import Scheme' > 'Eclipse code style'
|
||||||
|
3. In Scheme select 'Project'
|
||||||
|
4. Open the file `templates_and_presets/eclipse_ide/FormatterProfile.xml` in 'Select Path'.
|
||||||
|
5. Inside 'Import Scheme' widow click 'Current Scheme' check box after press OK
|
||||||
|
|
||||||
|
1
logs/game.log
Normal file
1
logs/game.log
Normal file
@@ -0,0 +1 @@
|
|||||||
|
22:26:25.948 [Music Thread ] WARN ru.windcorp.progressia.test.TestMusicPlayer > No music found
|
@@ -72,9 +72,9 @@ public class OpenSimplex2S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 2D SuperSimplex noise, with Y pointing down the main diagonal. Might be
|
* 2D SuperSimplex noise, with Y pointing down the main diagonal.
|
||||||
* better for a 2D sandbox style game, where Y is vertical. Probably
|
* Might be better for a 2D sandbox style game, where Y is vertical.
|
||||||
* slightly less optimal for heightmaps or continent maps.
|
* Probably slightly less optimal for heightmaps or continent maps.
|
||||||
*/
|
*/
|
||||||
public double noise2_XBeforeY(double x, double y) {
|
public double noise2_XBeforeY(double x, double y) {
|
||||||
|
|
||||||
@@ -86,8 +86,8 @@ public class OpenSimplex2S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 2D SuperSimplex noise base. Lookup table implementation inspired by
|
* 2D SuperSimplex noise base.
|
||||||
* DigitalShadow.
|
* Lookup table implementation inspired by DigitalShadow.
|
||||||
*/
|
*/
|
||||||
private double noise2_Base(double xs, double ys) {
|
private double noise2_Base(double xs, double ys) {
|
||||||
double value = 0;
|
double value = 0;
|
||||||
@@ -98,7 +98,10 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
// Index to point list
|
// Index to point list
|
||||||
int a = (int)(xsi + ysi);
|
int a = (int)(xsi + ysi);
|
||||||
int index = (a << 2) | (int) (xsi - ysi / 2 + 1 - a / 2.0) << 3 | (int) (ysi - xsi / 2 + 1 - a / 2.0) << 4;
|
int index =
|
||||||
|
(a << 2) |
|
||||||
|
(int)(xsi - ysi / 2 + 1 - a / 2.0) << 3 |
|
||||||
|
(int)(ysi - xsi / 2 + 1 - a / 2.0) << 4;
|
||||||
|
|
||||||
double ssi = (xsi + ysi) * -0.211324865405187;
|
double ssi = (xsi + ysi) * -0.211324865405187;
|
||||||
double xi = xsi + ssi, yi = ysi + ssi;
|
double xi = xsi + ssi, yi = ysi + ssi;
|
||||||
@@ -109,8 +112,7 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
double dx = xi + c.dx, dy = yi + c.dy;
|
double dx = xi + c.dx, dy = yi + c.dy;
|
||||||
double attn = 2.0 / 3.0 - dx * dx - dy * dy;
|
double attn = 2.0 / 3.0 - dx * dx - dy * dy;
|
||||||
if (attn <= 0)
|
if (attn <= 0) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
int pxm = (xsb + c.xsv) & PMASK, pym = (ysb + c.ysv) & PMASK;
|
int pxm = (xsb + c.xsv) & PMASK, pym = (ysb + c.ysv) & PMASK;
|
||||||
Grad2 grad = permGrad2[perm[pxm] ^ pym];
|
Grad2 grad = permGrad2[perm[pxm] ^ pym];
|
||||||
@@ -124,16 +126,15 @@ public class OpenSimplex2S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 3D Re-oriented 8-point BCC noise, classic orientation Proper substitute
|
* 3D Re-oriented 8-point BCC noise, classic orientation
|
||||||
* for what 3D SuperSimplex would be, in light of Forbidden Formulae. Use
|
* Proper substitute for what 3D SuperSimplex would be,
|
||||||
* noise3_XYBeforeZ or noise3_XZBeforeY instead, wherever appropriate.
|
* in light of Forbidden Formulae.
|
||||||
|
* Use noise3_XYBeforeZ or noise3_XZBeforeY instead, wherever appropriate.
|
||||||
*/
|
*/
|
||||||
public double noise3_Classic(double x, double y, double z) {
|
public double noise3_Classic(double x, double y, double z) {
|
||||||
|
|
||||||
// Re-orient the cubic lattices via rotation, to produce the expected
|
// Re-orient the cubic lattices via rotation, to produce the expected look on cardinal planar slices.
|
||||||
// look on cardinal planar slices.
|
// If texturing objects that don't tend to have cardinal plane faces, you could even remove this.
|
||||||
// If texturing objects that don't tend to have cardinal plane faces,
|
|
||||||
// you could even remove this.
|
|
||||||
// Orthonormal rotation. Not a skew transform.
|
// Orthonormal rotation. Not a skew transform.
|
||||||
double r = (2.0 / 3.0) * (x + y + z);
|
double r = (2.0 / 3.0) * (x + y + z);
|
||||||
double xr = r - x, yr = r - y, zr = r - z;
|
double xr = r - x, yr = r - y, zr = r - z;
|
||||||
@@ -144,17 +145,15 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 3D Re-oriented 8-point BCC noise, with better visual isotropy in (X, Y).
|
* 3D Re-oriented 8-point BCC noise, with better visual isotropy in (X, Y).
|
||||||
* Recommended for 3D terrain and time-varied animations. The Z coordinate
|
* Recommended for 3D terrain and time-varied animations.
|
||||||
* should always be the "different" coordinate in your use case. If Y is
|
* The Z coordinate should always be the "different" coordinate in your use case.
|
||||||
* vertical in world coordinates, call noise3_XYBeforeZ(x, z, Y) or use
|
* If Y is vertical in world coordinates, call noise3_XYBeforeZ(x, z, Y) or use noise3_XZBeforeY.
|
||||||
* noise3_XZBeforeY. If Z is vertical in world coordinates, call
|
* If Z is vertical in world coordinates, call noise3_XYBeforeZ(x, y, Z).
|
||||||
* noise3_XYBeforeZ(x, y, Z). For a time varied animation, call
|
* For a time varied animation, call noise3_XYBeforeZ(x, y, T).
|
||||||
* noise3_XYBeforeZ(x, y, T).
|
|
||||||
*/
|
*/
|
||||||
public double noise3_XYBeforeZ(double x, double y, double z) {
|
public double noise3_XYBeforeZ(double x, double y, double z) {
|
||||||
|
|
||||||
// Re-orient the cubic lattices without skewing, to make X and Y
|
// Re-orient the cubic lattices without skewing, to make X and Y triangular like 2D.
|
||||||
// triangular like 2D.
|
|
||||||
// Orthonormal rotation. Not a skew transform.
|
// Orthonormal rotation. Not a skew transform.
|
||||||
double xy = x + y;
|
double xy = x + y;
|
||||||
double s2 = xy * -0.211324865405187;
|
double s2 = xy * -0.211324865405187;
|
||||||
@@ -168,23 +167,20 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 3D Re-oriented 8-point BCC noise, with better visual isotropy in (X, Z).
|
* 3D Re-oriented 8-point BCC noise, with better visual isotropy in (X, Z).
|
||||||
* Recommended for 3D terrain and time-varied animations. The Y coordinate
|
* Recommended for 3D terrain and time-varied animations.
|
||||||
* should always be the "different" coordinate in your use case. If Y is
|
* The Y coordinate should always be the "different" coordinate in your use case.
|
||||||
* vertical in world coordinates, call noise3_XZBeforeY(x, Y, z). If Z is
|
* If Y is vertical in world coordinates, call noise3_XZBeforeY(x, Y, z).
|
||||||
* vertical in world coordinates, call noise3_XZBeforeY(x, Z, y) or use
|
* If Z is vertical in world coordinates, call noise3_XZBeforeY(x, Z, y) or use noise3_XYBeforeZ.
|
||||||
* noise3_XYBeforeZ. For a time varied animation, call noise3_XZBeforeY(x,
|
* For a time varied animation, call noise3_XZBeforeY(x, T, y) or use noise3_XYBeforeZ.
|
||||||
* T, y) or use noise3_XYBeforeZ.
|
|
||||||
*/
|
*/
|
||||||
public double noise3_XZBeforeY(double x, double y, double z) {
|
public double noise3_XZBeforeY(double x, double y, double z) {
|
||||||
|
|
||||||
// Re-orient the cubic lattices without skewing, to make X and Z
|
// Re-orient the cubic lattices without skewing, to make X and Z triangular like 2D.
|
||||||
// triangular like 2D.
|
|
||||||
// Orthonormal rotation. Not a skew transform.
|
// Orthonormal rotation. Not a skew transform.
|
||||||
double xz = x + z;
|
double xz = x + z;
|
||||||
double s2 = xz * -0.211324865405187;
|
double s2 = xz * -0.211324865405187;
|
||||||
double yy = y * 0.577350269189626;
|
double yy = y * 0.577350269189626;
|
||||||
double xr = x + s2 - yy;
|
double xr = x + s2 - yy; double zr = z + s2 - yy;
|
||||||
double zr = z + s2 - yy;
|
|
||||||
double yr = xz * 0.577350269189626 + yy;
|
double yr = xz * 0.577350269189626 + yy;
|
||||||
|
|
||||||
// Evaluate both lattices to form a BCC lattice.
|
// Evaluate both lattices to form a BCC lattice.
|
||||||
@@ -192,10 +188,10 @@ public class OpenSimplex2S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate overlapping cubic lattices for 3D Re-oriented BCC noise. Lookup
|
* Generate overlapping cubic lattices for 3D Re-oriented BCC noise.
|
||||||
* table implementation inspired by DigitalShadow. It was actually faster to
|
* Lookup table implementation inspired by DigitalShadow.
|
||||||
* narrow down the points in the loop itself, than to build up the index
|
* It was actually faster to narrow down the points in the loop itself,
|
||||||
* with enough info to isolate 8 points.
|
* than to build up the index with enough info to isolate 8 points.
|
||||||
*/
|
*/
|
||||||
private double noise3_BCC(double xr, double yr, double zr) {
|
private double noise3_BCC(double xr, double yr, double zr) {
|
||||||
|
|
||||||
@@ -203,10 +199,8 @@ public class OpenSimplex2S {
|
|||||||
int xrb = fastFloor(xr), yrb = fastFloor(yr), zrb = fastFloor(zr);
|
int xrb = fastFloor(xr), yrb = fastFloor(yr), zrb = fastFloor(zr);
|
||||||
double xri = xr - xrb, yri = yr - yrb, zri = zr - zrb;
|
double xri = xr - xrb, yri = yr - yrb, zri = zr - zrb;
|
||||||
|
|
||||||
// Identify which octant of the cube we're in. This determines which
|
// Identify which octant of the cube we're in. This determines which cell
|
||||||
// cell
|
// in the other cubic lattice we're in, and also narrows down one point on each.
|
||||||
// in the other cubic lattice we're in, and also narrows down one point
|
|
||||||
// on each.
|
|
||||||
int xht = (int)(xri + 0.5), yht = (int)(yri + 0.5), zht = (int)(zri + 0.5);
|
int xht = (int)(xri + 0.5), yht = (int)(yri + 0.5), zht = (int)(zri + 0.5);
|
||||||
int index = (xht << 0) | (yht << 1) | (zht << 2);
|
int index = (xht << 0) | (yht << 1) | (zht << 2);
|
||||||
|
|
||||||
@@ -236,9 +230,9 @@ public class OpenSimplex2S {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the 2D noise over a large area. Propagates by flood-fill instead
|
* Generate the 2D noise over a large area.
|
||||||
* of iterating over a range. Results may occasionally slightly exceed [-1,
|
* Propagates by flood-fill instead of iterating over a range.
|
||||||
* 1] due to the grid-snapped pre-generated kernel.
|
* Results may occasionally slightly exceed [-1, 1] due to the grid-snapped pre-generated kernel.
|
||||||
*/
|
*/
|
||||||
public void generate2(GenerateContext2D context, double[][] buffer, int x0, int y0) {
|
public void generate2(GenerateContext2D context, double[][] buffer, int x0, int y0) {
|
||||||
int height = buffer.length;
|
int height = buffer.length;
|
||||||
@@ -247,12 +241,11 @@ public class OpenSimplex2S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the 2D noise over a large area. Propagates by flood-fill instead
|
* Generate the 2D noise over a large area.
|
||||||
* of iterating over a range. Results may occasionally slightly exceed [-1,
|
* Propagates by flood-fill instead of iterating over a range.
|
||||||
* 1] due to the grid-snapped pre-generated kernel.
|
* Results may occasionally slightly exceed [-1, 1] due to the grid-snapped pre-generated kernel.
|
||||||
*/
|
*/
|
||||||
public void generate2(GenerateContext2D context, double[][] buffer, int x0, int y0, int width, int height,
|
public void generate2(GenerateContext2D context, double[][] buffer, int x0, int y0, int width, int height, int skipX, int skipY) {
|
||||||
int skipX, int skipY) {
|
|
||||||
Queue<AreaGenLatticePoint2D> queue = new LinkedList<AreaGenLatticePoint2D>();
|
Queue<AreaGenLatticePoint2D> queue = new LinkedList<AreaGenLatticePoint2D>();
|
||||||
Set<AreaGenLatticePoint2D> seen = new HashSet<AreaGenLatticePoint2D>();
|
Set<AreaGenLatticePoint2D> seen = new HashSet<AreaGenLatticePoint2D>();
|
||||||
|
|
||||||
@@ -273,13 +266,10 @@ public class OpenSimplex2S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get started with one point/vertex.
|
// Get started with one point/vertex.
|
||||||
// For some lattices, you might need to try a handful of points in the
|
// For some lattices, you might need to try a handful of points in the cell,
|
||||||
// cell,
|
// or flip a couple of coordinates, to guarantee it or a neighbor contributes.
|
||||||
// or flip a couple of coordinates, to guarantee it or a neighbor
|
|
||||||
// contributes.
|
|
||||||
// For An* lattices, the base coordinate seems fine.
|
// For An* lattices, the base coordinate seems fine.
|
||||||
double x0f = x0Skipped * context.xFrequency;
|
double x0f = x0Skipped * context.xFrequency; double y0f = y0Skipped * context.yFrequency;
|
||||||
double y0f = y0Skipped * context.yFrequency;
|
|
||||||
double x0s = context.orientation.s00 * x0f + context.orientation.s01 * y0f;
|
double x0s = context.orientation.s00 * x0f + context.orientation.s01 * y0f;
|
||||||
double y0s = context.orientation.s10 * x0f + context.orientation.s11 * y0f;
|
double y0s = context.orientation.s10 * x0f + context.orientation.s11 * y0f;
|
||||||
int x0sb = fastFloor(x0s), y0sb = fastFloor(y0s);
|
int x0sb = fastFloor(x0s), y0sb = fastFloor(y0s);
|
||||||
@@ -297,16 +287,11 @@ public class OpenSimplex2S {
|
|||||||
Grad2 grad = context.orientation.gradients[perm[perm[pxm] ^ pym]];
|
Grad2 grad = context.orientation.gradients[perm[perm[pxm] ^ pym]];
|
||||||
double gx = grad.dx * context.xFrequency;
|
double gx = grad.dx * context.xFrequency;
|
||||||
double gy = grad.dy * context.yFrequency;
|
double gy = grad.dy * context.yFrequency;
|
||||||
double gOff = 0.5 * (gx + gy); // to correct for (0.5, 0.5)-offset
|
double gOff = 0.5 * (gx + gy); // to correct for (0.5, 0.5)-offset kernel
|
||||||
// kernel
|
|
||||||
|
|
||||||
// Contribution kernel bounds
|
// Contribution kernel bounds
|
||||||
int yy0 = destPointY - scaledRadiusY;
|
int yy0 = destPointY - scaledRadiusY; if (yy0 < y0Skipped) yy0 = y0Skipped;
|
||||||
if (yy0 < y0Skipped)
|
int yy1 = destPointY + scaledRadiusY; if (yy1 > y0 + height) yy1 = y0 + height;
|
||||||
yy0 = y0Skipped;
|
|
||||||
int yy1 = destPointY + scaledRadiusY;
|
|
||||||
if (yy1 > y0 + height)
|
|
||||||
yy1 = y0 + height;
|
|
||||||
|
|
||||||
// For each row of the contribution circle,
|
// For each row of the contribution circle,
|
||||||
for (int yy = yy0; yy < yy1; yy++) {
|
for (int yy = yy0; yy < yy1; yy++) {
|
||||||
@@ -315,22 +300,16 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
// Set up bounds so we only loop over what we need to
|
// Set up bounds so we only loop over what we need to
|
||||||
int thisScaledRadiusX = context.kernelBounds[ky];
|
int thisScaledRadiusX = context.kernelBounds[ky];
|
||||||
int xx0 = destPointX - thisScaledRadiusX;
|
int xx0 = destPointX - thisScaledRadiusX; if (xx0 < x0Skipped) xx0 = x0Skipped;
|
||||||
if (xx0 < x0Skipped)
|
int xx1 = destPointX + thisScaledRadiusX; if (xx1 > x0 + width) xx1 = x0 + width;
|
||||||
xx0 = x0Skipped;
|
|
||||||
int xx1 = destPointX + thisScaledRadiusX;
|
|
||||||
if (xx1 > x0 + width)
|
|
||||||
xx1 = x0 + width;
|
|
||||||
|
|
||||||
// For each point on that row
|
// For each point on that row
|
||||||
for (int xx = xx0; xx < xx1; xx++) {
|
for (int xx = xx0; xx < xx1; xx++) {
|
||||||
int dx = xx - destPointX;
|
int dx = xx - destPointX;
|
||||||
int kx = dx + scaledRadiusX;
|
int kx = dx + scaledRadiusX;
|
||||||
|
|
||||||
// gOff accounts for our choice to offset the pre-generated
|
// gOff accounts for our choice to offset the pre-generated kernel by (0.5, 0.5) to avoid the zero center.
|
||||||
// kernel by (0.5, 0.5) to avoid the zero center.
|
// I found almost no difference in performance using gOff vs not (under 1ns diff per value on my system)
|
||||||
// I found almost no difference in performance using gOff vs
|
|
||||||
// not (under 1ns diff per value on my system)
|
|
||||||
double extrapolation = gx * dx + gy * dy + gOff;
|
double extrapolation = gx * dx + gy * dy + gOff;
|
||||||
buffer[yy - y0][xx - x0] += kernel[ky][kx] * extrapolation;
|
buffer[yy - y0][xx - x0] += kernel[ky][kx] * extrapolation;
|
||||||
|
|
||||||
@@ -339,14 +318,13 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
// For each neighbor of the point
|
// For each neighbor of the point
|
||||||
for (int i = 0; i < NEIGHBOR_MAP_2D.length; i++) {
|
for (int i = 0; i < NEIGHBOR_MAP_2D.length; i++) {
|
||||||
AreaGenLatticePoint2D neighbor = new AreaGenLatticePoint2D(context, point.xsv + NEIGHBOR_MAP_2D[i][0],
|
AreaGenLatticePoint2D neighbor = new AreaGenLatticePoint2D(context,
|
||||||
point.ysv + NEIGHBOR_MAP_2D[i][1]);
|
point.xsv + NEIGHBOR_MAP_2D[i][0], point.ysv + NEIGHBOR_MAP_2D[i][1]);
|
||||||
|
|
||||||
// If it's in range of the buffer region and not seen before
|
// If it's in range of the buffer region and not seen before
|
||||||
if (neighbor.destPointX + scaledRadiusX >= x0Skipped
|
if (neighbor.destPointX + scaledRadiusX >= x0Skipped && neighbor.destPointX - scaledRadiusX <= x0 + width - 1
|
||||||
&& neighbor.destPointX - scaledRadiusX <= x0 + width - 1
|
&& neighbor.destPointY + scaledRadiusY >= y0Skipped && neighbor.destPointY - scaledRadiusY <= y0 + height - 1
|
||||||
&& neighbor.destPointY + scaledRadiusY >= y0Skipped
|
&& !seen.contains(neighbor)) {
|
||||||
&& neighbor.destPointY - scaledRadiusY <= y0 + height - 1 && !seen.contains(neighbor)) {
|
|
||||||
|
|
||||||
// Add it to the queue so we can process it at some point
|
// Add it to the queue so we can process it at some point
|
||||||
queue.add(neighbor);
|
queue.add(neighbor);
|
||||||
@@ -359,9 +337,9 @@ public class OpenSimplex2S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the 3D noise over a large area/volume. Propagates by flood-fill
|
* Generate the 3D noise over a large area/volume.
|
||||||
* instead of iterating over a range. Results may occasionally slightly
|
* Propagates by flood-fill instead of iterating over a range.
|
||||||
* exceed [-1, 1] due to the grid-snapped pre-generated kernel.
|
* Results may occasionally slightly exceed [-1, 1] due to the grid-snapped pre-generated kernel.
|
||||||
*/
|
*/
|
||||||
public void generate3(GenerateContext3D context, double[][][] buffer, int x0, int y0, int z0) {
|
public void generate3(GenerateContext3D context, double[][][] buffer, int x0, int y0, int z0) {
|
||||||
int depth = buffer.length;
|
int depth = buffer.length;
|
||||||
@@ -371,12 +349,11 @@ public class OpenSimplex2S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the 3D noise over a large area/volume. Propagates by flood-fill
|
* Generate the 3D noise over a large area/volume.
|
||||||
* instead of iterating over a range. Results may occasionally slightly
|
* Propagates by flood-fill instead of iterating over a range.
|
||||||
* exceed [-1, 1] due to the grid-snapped pre-generated kernel.
|
* Results may occasionally slightly exceed [-1, 1] due to the grid-snapped pre-generated kernel.
|
||||||
*/
|
*/
|
||||||
public void generate3(GenerateContext3D context, double[][][] buffer, int x0, int y0, int z0, int width, int height,
|
public void generate3(GenerateContext3D context, double[][][] buffer, int x0, int y0, int z0, int width, int height, int depth, int skipX, int skipY, int skipZ) {
|
||||||
int depth, int skipX, int skipY, int skipZ) {
|
|
||||||
Queue<AreaGenLatticePoint3D> queue = new LinkedList<AreaGenLatticePoint3D>();
|
Queue<AreaGenLatticePoint3D> queue = new LinkedList<AreaGenLatticePoint3D>();
|
||||||
Set<AreaGenLatticePoint3D> seen = new HashSet<AreaGenLatticePoint3D>();
|
Set<AreaGenLatticePoint3D> seen = new HashSet<AreaGenLatticePoint3D>();
|
||||||
|
|
||||||
@@ -388,10 +365,8 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
// Quaternion multiplication for rotation.
|
// Quaternion multiplication for rotation.
|
||||||
// https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/
|
// https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/
|
||||||
double qx = context.orientation.qx, qy = context.orientation.qy, qz = context.orientation.qz,
|
double qx = context.orientation.qx, qy = context.orientation.qy, qz = context.orientation.qz, qw = context.orientation.qw;
|
||||||
qw = context.orientation.qw;
|
double x0f = x0Skipped * context.xFrequency, y0f = y0Skipped * context.yFrequency, z0f = z0Skipped * context.zFrequency;
|
||||||
double x0f = x0Skipped * context.xFrequency, y0f = y0Skipped * context.yFrequency,
|
|
||||||
z0f = z0Skipped * context.zFrequency;
|
|
||||||
double tx = 2 * (qy * z0f - qz * y0f);
|
double tx = 2 * (qy * z0f - qz * y0f);
|
||||||
double ty = 2 * (qz * x0f - qx * z0f);
|
double ty = 2 * (qz * x0f - qx * z0f);
|
||||||
double tz = 2 * (qx * y0f - qy * x0f);
|
double tz = 2 * (qx * y0f - qy * x0f);
|
||||||
@@ -417,16 +392,11 @@ public class OpenSimplex2S {
|
|||||||
double gx = grad.dx * context.xFrequency;
|
double gx = grad.dx * context.xFrequency;
|
||||||
double gy = grad.dy * context.yFrequency;
|
double gy = grad.dy * context.yFrequency;
|
||||||
double gz = grad.dz * context.zFrequency;
|
double gz = grad.dz * context.zFrequency;
|
||||||
double gOff = 0.5 * (gx + gy + gz); // to correct for (0.5, 0.5,
|
double gOff = 0.5 * (gx + gy + gz); // to correct for (0.5, 0.5, 0.5)-offset kernel
|
||||||
// 0.5)-offset kernel
|
|
||||||
|
|
||||||
// Contribution kernel bounds.
|
// Contribution kernel bounds.
|
||||||
int zz0 = destPointZ - scaledRadiusZ;
|
int zz0 = destPointZ - scaledRadiusZ; if (zz0 < z0Skipped) zz0 = z0Skipped;
|
||||||
if (zz0 < z0Skipped)
|
int zz1 = destPointZ + scaledRadiusZ; if (zz1 > z0 + depth) zz1 = z0 + depth;
|
||||||
zz0 = z0Skipped;
|
|
||||||
int zz1 = destPointZ + scaledRadiusZ;
|
|
||||||
if (zz1 > z0 + depth)
|
|
||||||
zz1 = z0 + depth;
|
|
||||||
|
|
||||||
// For each x/y slice of the contribution sphere,
|
// For each x/y slice of the contribution sphere,
|
||||||
for (int zz = zz0; zz < zz1; zz++) {
|
for (int zz = zz0; zz < zz1; zz++) {
|
||||||
@@ -435,12 +405,8 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
// Set up bounds so we only loop over what we need to
|
// Set up bounds so we only loop over what we need to
|
||||||
int thisScaledRadiusY = context.kernelBoundsY[kz];
|
int thisScaledRadiusY = context.kernelBoundsY[kz];
|
||||||
int yy0 = destPointY - thisScaledRadiusY;
|
int yy0 = destPointY - thisScaledRadiusY; if (yy0 < y0Skipped) yy0 = y0Skipped;
|
||||||
if (yy0 < y0Skipped)
|
int yy1 = destPointY + thisScaledRadiusY; if (yy1 > y0 + height) yy1 = y0 + height;
|
||||||
yy0 = y0Skipped;
|
|
||||||
int yy1 = destPointY + thisScaledRadiusY;
|
|
||||||
if (yy1 > y0 + height)
|
|
||||||
yy1 = y0 + height;
|
|
||||||
|
|
||||||
// For each row of the contribution circle,
|
// For each row of the contribution circle,
|
||||||
for (int yy = yy0; yy < yy1; yy++) {
|
for (int yy = yy0; yy < yy1; yy++) {
|
||||||
@@ -449,21 +415,15 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
// Set up bounds so we only loop over what we need to
|
// Set up bounds so we only loop over what we need to
|
||||||
int thisScaledRadiusX = context.kernelBoundsX[kz][ky];
|
int thisScaledRadiusX = context.kernelBoundsX[kz][ky];
|
||||||
int xx0 = destPointX - thisScaledRadiusX;
|
int xx0 = destPointX - thisScaledRadiusX; if (xx0 < x0Skipped) xx0 = x0Skipped;
|
||||||
if (xx0 < x0Skipped)
|
int xx1 = destPointX + thisScaledRadiusX; if (xx1 > x0 + width) xx1 = x0 + width;
|
||||||
xx0 = x0Skipped;
|
|
||||||
int xx1 = destPointX + thisScaledRadiusX;
|
|
||||||
if (xx1 > x0 + width)
|
|
||||||
xx1 = x0 + width;
|
|
||||||
|
|
||||||
// For each point on that row
|
// For each point on that row
|
||||||
for (int xx = xx0; xx < xx1; xx++) {
|
for (int xx = xx0; xx < xx1; xx++) {
|
||||||
int dx = xx - destPointX;
|
int dx = xx - destPointX;
|
||||||
int kx = dx + scaledRadiusX;
|
int kx = dx + scaledRadiusX;
|
||||||
|
|
||||||
// gOff accounts for our choice to offset the
|
// gOff accounts for our choice to offset the pre-generated kernel by (0.5, 0.5, 0.5) to avoid the zero center.
|
||||||
// pre-generated kernel by (0.5, 0.5, 0.5) to avoid the
|
|
||||||
// zero center.
|
|
||||||
double extrapolation = gx * dx + gy * dy + gz * dz + gOff;
|
double extrapolation = gx * dx + gy * dy + gz * dz + gOff;
|
||||||
buffer[zz - z0][yy - y0][xx - x0] += kernel[kz][ky][kx] * extrapolation;
|
buffer[zz - z0][yy - y0][xx - x0] += kernel[kz][ky][kx] * extrapolation;
|
||||||
|
|
||||||
@@ -475,16 +435,13 @@ public class OpenSimplex2S {
|
|||||||
for (int i = 0; i < NEIGHBOR_MAP_3D[0].length; i++) {
|
for (int i = 0; i < NEIGHBOR_MAP_3D[0].length; i++) {
|
||||||
int l = point.lattice;
|
int l = point.lattice;
|
||||||
AreaGenLatticePoint3D neighbor = new AreaGenLatticePoint3D(context,
|
AreaGenLatticePoint3D neighbor = new AreaGenLatticePoint3D(context,
|
||||||
point.xsv + NEIGHBOR_MAP_3D[l][i][0], point.ysv + NEIGHBOR_MAP_3D[l][i][1],
|
point.xsv + NEIGHBOR_MAP_3D[l][i][0], point.ysv + NEIGHBOR_MAP_3D[l][i][1], point.zsv + NEIGHBOR_MAP_3D[l][i][2], 1 ^ l);
|
||||||
point.zsv + NEIGHBOR_MAP_3D[l][i][2], 1 ^ l);
|
|
||||||
|
|
||||||
// If it's in range of the buffer region and not seen before
|
// If it's in range of the buffer region and not seen before
|
||||||
if (neighbor.destPointX + scaledRadiusX >= x0Skipped
|
if (neighbor.destPointX + scaledRadiusX >= x0Skipped && neighbor.destPointX - scaledRadiusX <= x0 + width - 1
|
||||||
&& neighbor.destPointX - scaledRadiusX <= x0 + width - 1
|
&& neighbor.destPointY + scaledRadiusY >= y0Skipped && neighbor.destPointY - scaledRadiusY <= y0 + height - 1
|
||||||
&& neighbor.destPointY + scaledRadiusY >= y0Skipped
|
&& neighbor.destPointZ + scaledRadiusZ >= z0Skipped && neighbor.destPointZ - scaledRadiusZ <= z0 + depth - 1
|
||||||
&& neighbor.destPointY - scaledRadiusY <= y0 + height - 1
|
&& !seen.contains(neighbor)) {
|
||||||
&& neighbor.destPointZ + scaledRadiusZ >= z0Skipped
|
|
||||||
&& neighbor.destPointZ - scaledRadiusZ <= z0 + depth - 1 && !seen.contains(neighbor)) {
|
|
||||||
|
|
||||||
// Add it to the queue so we can process it at some point
|
// Add it to the queue so we can process it at some point
|
||||||
queue.add(neighbor);
|
queue.add(neighbor);
|
||||||
@@ -518,35 +475,11 @@ public class OpenSimplex2S {
|
|||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
int i1, j1, i2, j2;
|
int i1, j1, i2, j2;
|
||||||
if ((i & 1) == 0) {
|
if ((i & 1) == 0) {
|
||||||
if ((i & 2) == 0) {
|
if ((i & 2) == 0) { i1 = -1; j1 = 0; } else { i1 = 1; j1 = 0; }
|
||||||
i1 = -1;
|
if ((i & 4) == 0) { i2 = 0; j2 = -1; } else { i2 = 0; j2 = 1; }
|
||||||
j1 = 0;
|
|
||||||
} else {
|
} else {
|
||||||
i1 = 1;
|
if ((i & 2) != 0) { i1 = 2; j1 = 1; } else { i1 = 0; j1 = 1; }
|
||||||
j1 = 0;
|
if ((i & 4) != 0) { i2 = 1; j2 = 2; } else { i2 = 1; j2 = 0; }
|
||||||
}
|
|
||||||
if ((i & 4) == 0) {
|
|
||||||
i2 = 0;
|
|
||||||
j2 = -1;
|
|
||||||
} else {
|
|
||||||
i2 = 0;
|
|
||||||
j2 = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((i & 2) != 0) {
|
|
||||||
i1 = 2;
|
|
||||||
j1 = 1;
|
|
||||||
} else {
|
|
||||||
i1 = 0;
|
|
||||||
j1 = 1;
|
|
||||||
}
|
|
||||||
if ((i & 4) != 0) {
|
|
||||||
i2 = 1;
|
|
||||||
j2 = 2;
|
|
||||||
} else {
|
|
||||||
i2 = 1;
|
|
||||||
j2 = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
LOOKUP_2D[i * 4 + 0] = new LatticePoint2D(0, 0);
|
LOOKUP_2D[i * 4 + 0] = new LatticePoint2D(0, 0);
|
||||||
LOOKUP_2D[i * 4 + 1] = new LatticePoint2D(1, 1);
|
LOOKUP_2D[i * 4 + 1] = new LatticePoint2D(1, 1);
|
||||||
@@ -556,15 +489,10 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
int i1, j1, k1, i2, j2, k2;
|
int i1, j1, k1, i2, j2, k2;
|
||||||
i1 = (i >> 0) & 1;
|
i1 = (i >> 0) & 1; j1 = (i >> 1) & 1; k1 = (i >> 2) & 1;
|
||||||
j1 = (i >> 1) & 1;
|
i2 = i1 ^ 1; j2 = j1 ^ 1; k2 = k1 ^ 1;
|
||||||
k1 = (i >> 2) & 1;
|
|
||||||
i2 = i1 ^ 1;
|
|
||||||
j2 = j1 ^ 1;
|
|
||||||
k2 = k1 ^ 1;
|
|
||||||
|
|
||||||
// The two points within this octant, one from each of the two cubic
|
// The two points within this octant, one from each of the two cubic half-lattices.
|
||||||
// half-lattices.
|
|
||||||
LatticePoint3D c0 = new LatticePoint3D(i1, j1, k1, 0);
|
LatticePoint3D c0 = new LatticePoint3D(i1, j1, k1, 0);
|
||||||
LatticePoint3D c1 = new LatticePoint3D(i1 + i2, j1 + j2, k1 + k2, 1);
|
LatticePoint3D c1 = new LatticePoint3D(i1 + i2, j1 + j2, k1 + k2, 1);
|
||||||
|
|
||||||
@@ -597,36 +525,27 @@ public class OpenSimplex2S {
|
|||||||
c1.nextOnFailure = c1.nextOnSuccess = c2;
|
c1.nextOnFailure = c1.nextOnSuccess = c2;
|
||||||
|
|
||||||
// If c2 is in range, then we know c3 and c4 are not.
|
// If c2 is in range, then we know c3 and c4 are not.
|
||||||
c2.nextOnFailure = c3;
|
c2.nextOnFailure = c3; c2.nextOnSuccess = c5;
|
||||||
c2.nextOnSuccess = c5;
|
c3.nextOnFailure = c4; c3.nextOnSuccess = c4;
|
||||||
c3.nextOnFailure = c4;
|
|
||||||
c3.nextOnSuccess = c4;
|
|
||||||
|
|
||||||
// If c4 is in range, then we know c5 is not.
|
// If c4 is in range, then we know c5 is not.
|
||||||
c4.nextOnFailure = c5;
|
c4.nextOnFailure = c5; c4.nextOnSuccess = c6;
|
||||||
c4.nextOnSuccess = c6;
|
|
||||||
c5.nextOnFailure = c5.nextOnSuccess = c6;
|
c5.nextOnFailure = c5.nextOnSuccess = c6;
|
||||||
|
|
||||||
// If c6 is in range, then we know c7 and c8 are not.
|
// If c6 is in range, then we know c7 and c8 are not.
|
||||||
c6.nextOnFailure = c7;
|
c6.nextOnFailure = c7; c6.nextOnSuccess = c9;
|
||||||
c6.nextOnSuccess = c9;
|
c7.nextOnFailure = c8; c7.nextOnSuccess = c8;
|
||||||
c7.nextOnFailure = c8;
|
|
||||||
c7.nextOnSuccess = c8;
|
|
||||||
|
|
||||||
// If c8 is in range, then we know c9 is not.
|
// If c8 is in range, then we know c9 is not.
|
||||||
c8.nextOnFailure = c9;
|
c8.nextOnFailure = c9; c8.nextOnSuccess = cA;
|
||||||
c8.nextOnSuccess = cA;
|
|
||||||
c9.nextOnFailure = c9.nextOnSuccess = cA;
|
c9.nextOnFailure = c9.nextOnSuccess = cA;
|
||||||
|
|
||||||
// If cA is in range, then we know cB and cC are not.
|
// If cA is in range, then we know cB and cC are not.
|
||||||
cA.nextOnFailure = cB;
|
cA.nextOnFailure = cB; cA.nextOnSuccess = cD;
|
||||||
cA.nextOnSuccess = cD;
|
cB.nextOnFailure = cC; cB.nextOnSuccess = cC;
|
||||||
cB.nextOnFailure = cC;
|
|
||||||
cB.nextOnSuccess = cC;
|
|
||||||
|
|
||||||
// If cC is in range, then we know cD is not.
|
// If cC is in range, then we know cD is not.
|
||||||
cC.nextOnFailure = cD;
|
cC.nextOnFailure = cD; cC.nextOnSuccess = null;
|
||||||
cC.nextOnSuccess = null;
|
|
||||||
cD.nextOnFailure = cD.nextOnSuccess = null;
|
cD.nextOnFailure = cD.nextOnSuccess = null;
|
||||||
|
|
||||||
LOOKUP_3D[i] = c0;
|
LOOKUP_3D[i] = c0;
|
||||||
@@ -635,24 +554,28 @@ public class OpenSimplex2S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hexagon surrounding each vertex.
|
// Hexagon surrounding each vertex.
|
||||||
private static final int[][] NEIGHBOR_MAP_2D = { { 1, 0 }, { 1, 1 }, { 0, 1 }, { 0, -1 }, { -1, -1 }, { -1, 0 } };
|
private static final int[][] NEIGHBOR_MAP_2D = {
|
||||||
|
{ 1, 0 }, { 1, 1 }, { 0, 1 }, { 0, -1 }, { -1, -1 }, { -1, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
// Cube surrounding each vertex.
|
// Cube surrounding each vertex.
|
||||||
// Alternates between half-lattices.
|
// Alternates between half-lattices.
|
||||||
private static final int[][][] NEIGHBOR_MAP_3D = {
|
private static final int[][][] NEIGHBOR_MAP_3D = {
|
||||||
{ { 1024, 1024, 1024 }, { 1025, 1024, 1024 }, { 1024, 1025, 1024 }, { 1025, 1025, 1024 },
|
{
|
||||||
{ 1024, 1024, 1025 }, { 1025, 1024, 1025 }, { 1024, 1025, 1025 }, { 1025, 1025, 1025 } },
|
{ 1024, 1024, 1024 }, { 1025, 1024, 1024 }, { 1024, 1025, 1024 }, { 1025, 1025, 1024 },
|
||||||
{ { -1024, -1024, -1024 }, { -1025, -1024, 1024 }, { -1024, -1025, -1024 }, { -1025, -1025, -1024 },
|
{ 1024, 1024, 1025 }, { 1025, 1024, 1025 }, { 1024, 1025, 1025 }, { 1025, 1025, 1025 }
|
||||||
{ -1024, -1024, -1025 }, { -1025, -1024, -1025 }, { -1024, -1025, -1025 },
|
},
|
||||||
{ -1025, -1025, 1025 } }, };
|
{
|
||||||
|
{ -1024, -1024, -1024 }, { -1025, -1024, 1024 }, { -1024, -1025, -1024 }, { -1025, -1025, -1024 },
|
||||||
|
{ -1024, -1024, -1025 }, { -1025, -1024, -1025 }, { -1024, -1025, -1025 }, { -1025, -1025, 1025 }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
private static class LatticePoint2D {
|
private static class LatticePoint2D {
|
||||||
int xsv, ysv;
|
int xsv, ysv;
|
||||||
double dx, dy;
|
double dx, dy;
|
||||||
|
|
||||||
public LatticePoint2D(int xsv, int ysv) {
|
public LatticePoint2D(int xsv, int ysv) {
|
||||||
this.xsv = xsv;
|
this.xsv = xsv; this.ysv = ysv;
|
||||||
this.ysv = ysv;
|
|
||||||
double ssv = (xsv + ysv) * -0.211324865405187;
|
double ssv = (xsv + ysv) * -0.211324865405187;
|
||||||
this.dx = -xsv - ssv;
|
this.dx = -xsv - ssv;
|
||||||
this.dy = -ysv - ssv;
|
this.dy = -ysv - ssv;
|
||||||
@@ -663,42 +586,29 @@ public class OpenSimplex2S {
|
|||||||
public double dxr, dyr, dzr;
|
public double dxr, dyr, dzr;
|
||||||
public int xrv, yrv, zrv;
|
public int xrv, yrv, zrv;
|
||||||
LatticePoint3D nextOnFailure, nextOnSuccess;
|
LatticePoint3D nextOnFailure, nextOnSuccess;
|
||||||
|
|
||||||
public LatticePoint3D(int xrv, int yrv, int zrv, int lattice) {
|
public LatticePoint3D(int xrv, int yrv, int zrv, int lattice) {
|
||||||
this.dxr = -xrv + lattice * 0.5;
|
this.dxr = -xrv + lattice * 0.5; this.dyr = -yrv + lattice * 0.5; this.dzr = -zrv + lattice * 0.5;
|
||||||
this.dyr = -yrv + lattice * 0.5;
|
this.xrv = xrv + lattice * 1024; this.yrv = yrv + lattice * 1024; this.zrv = zrv + lattice * 1024;
|
||||||
this.dzr = -zrv + lattice * 0.5;
|
|
||||||
this.xrv = xrv + lattice * 1024;
|
|
||||||
this.yrv = yrv + lattice * 1024;
|
|
||||||
this.zrv = zrv + lattice * 1024;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class AreaGenLatticePoint2D {
|
private static class AreaGenLatticePoint2D {
|
||||||
int xsv, ysv;
|
int xsv, ysv;
|
||||||
int destPointX, destPointY;
|
int destPointX, destPointY;
|
||||||
|
|
||||||
public AreaGenLatticePoint2D(GenerateContext2D context, int xsv, int ysv) {
|
public AreaGenLatticePoint2D(GenerateContext2D context, int xsv, int ysv) {
|
||||||
this.xsv = xsv;
|
this.xsv = xsv; this.ysv = ysv;
|
||||||
this.ysv = ysv;
|
|
||||||
|
|
||||||
// Matrix multiplication for inverse rotation. Simplex skew
|
//Matrix multiplication for inverse rotation. Simplex skew transforms have always been shorthand for matrices.
|
||||||
// transforms have always been shorthand for matrices.
|
this.destPointX = (int)Math.ceil((context.orientation.t00 * xsv + context.orientation.t01 * ysv) * context.xFrequencyInverse);
|
||||||
this.destPointX = (int) Math
|
this.destPointY = (int)Math.ceil((context.orientation.t10 * xsv + context.orientation.t11 * ysv) * context.yFrequencyInverse);
|
||||||
.ceil((context.orientation.t00 * xsv + context.orientation.t01 * ysv) * context.xFrequencyInverse);
|
|
||||||
this.destPointY = (int) Math
|
|
||||||
.ceil((context.orientation.t10 * xsv + context.orientation.t11 * ysv) * context.yFrequencyInverse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return xsv * 7841 + ysv;
|
return xsv * 7841 + ysv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (!(obj instanceof AreaGenLatticePoint2D))
|
if (!(obj instanceof AreaGenLatticePoint2D)) return false;
|
||||||
return false;
|
|
||||||
AreaGenLatticePoint2D other = (AreaGenLatticePoint2D) obj;
|
AreaGenLatticePoint2D other = (AreaGenLatticePoint2D) obj;
|
||||||
return (other.xsv == this.xsv && other.ysv == this.ysv);
|
return (other.xsv == this.xsv && other.ysv == this.ysv);
|
||||||
}
|
}
|
||||||
@@ -707,20 +617,15 @@ public class OpenSimplex2S {
|
|||||||
private static class AreaGenLatticePoint3D {
|
private static class AreaGenLatticePoint3D {
|
||||||
int xsv, ysv, zsv, lattice;
|
int xsv, ysv, zsv, lattice;
|
||||||
int destPointX, destPointY, destPointZ;
|
int destPointX, destPointY, destPointZ;
|
||||||
|
|
||||||
public AreaGenLatticePoint3D(GenerateContext3D context, int xsv, int ysv, int zsv, int lattice) {
|
public AreaGenLatticePoint3D(GenerateContext3D context, int xsv, int ysv, int zsv, int lattice) {
|
||||||
this.xsv = xsv;
|
this.xsv = xsv; this.ysv = ysv; this.zsv = zsv; this.lattice = lattice;
|
||||||
this.ysv = ysv;
|
|
||||||
this.zsv = zsv;
|
|
||||||
this.lattice = lattice;
|
|
||||||
double xr = (xsv - lattice * 1024.5);
|
double xr = (xsv - lattice * 1024.5);
|
||||||
double yr = (ysv - lattice * 1024.5);
|
double yr = (ysv - lattice * 1024.5);
|
||||||
double zr = (zsv - lattice * 1024.5);
|
double zr = (zsv - lattice * 1024.5);
|
||||||
|
|
||||||
// Quaternion multiplication for inverse rotation.
|
// Quaternion multiplication for inverse rotation.
|
||||||
// https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/
|
// https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/
|
||||||
double qx = -context.orientation.qx, qy = -context.orientation.qy, qz = -context.orientation.qz,
|
double qx = -context.orientation.qx, qy = -context.orientation.qy, qz = -context.orientation.qz, qw = context.orientation.qw;
|
||||||
qw = context.orientation.qw;
|
|
||||||
double tx = 2 * (qy * zr - qz * yr);
|
double tx = 2 * (qy * zr - qz * yr);
|
||||||
double ty = 2 * (qz * xr - qx * zr);
|
double ty = 2 * (qz * xr - qx * zr);
|
||||||
double tz = 2 * (qx * yr - qy * xr);
|
double tz = 2 * (qx * yr - qy * xr);
|
||||||
@@ -732,19 +637,15 @@ public class OpenSimplex2S {
|
|||||||
this.destPointY = (int)Math.ceil(yrr * context.yFrequencyInverse);
|
this.destPointY = (int)Math.ceil(yrr * context.yFrequencyInverse);
|
||||||
this.destPointZ = (int)Math.ceil(zrr * context.zFrequencyInverse);
|
this.destPointZ = (int)Math.ceil(zrr * context.zFrequencyInverse);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return xsv * 2122193 + ysv * 2053 + zsv * 2 + lattice;
|
return xsv * 2122193 + ysv * 2053 + zsv * 2 + lattice;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (!(obj instanceof AreaGenLatticePoint3D))
|
if (!(obj instanceof AreaGenLatticePoint3D)) return false;
|
||||||
return false;
|
|
||||||
AreaGenLatticePoint3D other = (AreaGenLatticePoint3D) obj;
|
AreaGenLatticePoint3D other = (AreaGenLatticePoint3D) obj;
|
||||||
return (other.xsv == this.xsv && other.ysv == this.ysv && other.zsv == this.zsv
|
return (other.xsv == this.xsv && other.ysv == this.ysv && other.zsv == this.zsv && other.lattice == this.lattice);
|
||||||
&& other.lattice == this.lattice);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -760,8 +661,7 @@ public class OpenSimplex2S {
|
|||||||
int[] kernelBounds;
|
int[] kernelBounds;
|
||||||
LatticeOrientation2D orientation;
|
LatticeOrientation2D orientation;
|
||||||
|
|
||||||
public GenerateContext2D(LatticeOrientation2D orientation, double xFrequency, double yFrequency,
|
public GenerateContext2D(LatticeOrientation2D orientation, double xFrequency, double yFrequency, double amplitude) {
|
||||||
double amplitude) {
|
|
||||||
|
|
||||||
// These will be used by every call to generate
|
// These will be used by every call to generate
|
||||||
this.orientation = orientation;
|
this.orientation = orientation;
|
||||||
@@ -783,9 +683,10 @@ public class OpenSimplex2S {
|
|||||||
for (int yy = 0; yy < scaledRadiusY * 2; yy++) {
|
for (int yy = 0; yy < scaledRadiusY * 2; yy++) {
|
||||||
|
|
||||||
// Pre-generate boundary of circle
|
// Pre-generate boundary of circle
|
||||||
kernelBounds[yy] = (int) Math.ceil(Math.sqrt(
|
kernelBounds[yy] = (int)Math.ceil(
|
||||||
1.0 - (yy + 0.5 - scaledRadiusY) * (yy + 0.5 - scaledRadiusY) / (scaledRadiusY * scaledRadiusY))
|
Math.sqrt(1.0
|
||||||
* scaledRadiusX);
|
- (yy + 0.5 - scaledRadiusY) * (yy + 0.5 - scaledRadiusY) / (scaledRadiusY * scaledRadiusY)
|
||||||
|
) * scaledRadiusX);
|
||||||
|
|
||||||
if (yy < scaledRadiusY) {
|
if (yy < scaledRadiusY) {
|
||||||
kernel[yy] = new double[scaledRadiusX * 2];
|
kernel[yy] = new double[scaledRadiusX * 2];
|
||||||
@@ -823,8 +724,7 @@ public class OpenSimplex2S {
|
|||||||
int[][] kernelBoundsX;
|
int[][] kernelBoundsX;
|
||||||
LatticeOrientation3D orientation;
|
LatticeOrientation3D orientation;
|
||||||
|
|
||||||
public GenerateContext3D(LatticeOrientation3D orientation, double xFrequency, double yFrequency,
|
public GenerateContext3D(LatticeOrientation3D orientation, double xFrequency, double yFrequency, double zFrequency, double amplitude) {
|
||||||
double zFrequency, double amplitude) {
|
|
||||||
|
|
||||||
// These will be used by every call to generate
|
// These will be used by every call to generate
|
||||||
this.orientation = orientation;
|
this.orientation = orientation;
|
||||||
@@ -851,9 +751,9 @@ public class OpenSimplex2S {
|
|||||||
for (int zz = 0; zz < scaledRadiusZ * 2; zz++) {
|
for (int zz = 0; zz < scaledRadiusZ * 2; zz++) {
|
||||||
|
|
||||||
// Pre-generate boundary of sphere
|
// Pre-generate boundary of sphere
|
||||||
kernelBoundsY[zz] = (int) Math.ceil(Math.sqrt(
|
kernelBoundsY[zz] = (int)Math.ceil(
|
||||||
1.0 - (zz + 0.5 - scaledRadiusZ) * (zz + 0.5 - scaledRadiusZ) / (scaledRadiusZ * scaledRadiusZ))
|
Math.sqrt(1.0 - (zz + 0.5 - scaledRadiusZ) * (zz + 0.5 - scaledRadiusZ)
|
||||||
* scaledRadiusY);
|
/ (scaledRadiusZ * scaledRadiusZ)) * scaledRadiusY);
|
||||||
|
|
||||||
if (zz < scaledRadiusZ) {
|
if (zz < scaledRadiusZ) {
|
||||||
kernel[zz] = new double[scaledRadiusY * 2][];
|
kernel[zz] = new double[scaledRadiusY * 2][];
|
||||||
@@ -867,12 +767,11 @@ public class OpenSimplex2S {
|
|||||||
for (int yy = 0; yy < scaledRadiusY * 2; yy++) {
|
for (int yy = 0; yy < scaledRadiusY * 2; yy++) {
|
||||||
|
|
||||||
// Pre-generate boundary of sphere
|
// Pre-generate boundary of sphere
|
||||||
kernelBoundsX[zz][yy] = (int) Math.ceil(Math.sqrt(1.0
|
kernelBoundsX[zz][yy] = (int)Math.ceil(
|
||||||
- (yy + 0.5 - scaledRadiusY) * (yy + 0.5 - scaledRadiusY)
|
Math.sqrt(1.0
|
||||||
/ (scaledRadiusY * scaledRadiusY)
|
- (yy + 0.5 - scaledRadiusY) * (yy + 0.5 - scaledRadiusY) / (scaledRadiusY * scaledRadiusY)
|
||||||
- (zz + 0.5 - scaledRadiusZ) * (zz + 0.5 - scaledRadiusZ)
|
- (zz + 0.5 - scaledRadiusZ) * (zz + 0.5 - scaledRadiusZ) / (scaledRadiusZ * scaledRadiusZ)
|
||||||
/ (scaledRadiusZ * scaledRadiusZ))
|
) * scaledRadiusX);
|
||||||
* scaledRadiusX);
|
|
||||||
|
|
||||||
if (yy < scaledRadiusY) {
|
if (yy < scaledRadiusY) {
|
||||||
kernel[zz][yy] = new double[scaledRadiusX * 2];
|
kernel[zz][yy] = new double[scaledRadiusX * 2];
|
||||||
@@ -891,8 +790,7 @@ public class OpenSimplex2S {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else
|
} else kernel[zz][yy] = kernel[zz][2 * scaledRadiusY - yy - 1];
|
||||||
kernel[zz][yy] = kernel[zz][2 * scaledRadiusY - yy - 1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -900,50 +798,40 @@ public class OpenSimplex2S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum LatticeOrientation2D {
|
public enum LatticeOrientation2D {
|
||||||
// Simplex skew transforms have always been shorthand for the matrices
|
// Simplex skew transforms have always been shorthand for the matrices they represent.
|
||||||
// they represent.
|
// But when we bake the rotation into the skew transform, we need to use the general form.
|
||||||
// But when we bake the rotation into the skew transform, we need to use
|
Standard(GRADIENTS_2D,
|
||||||
// the general form.
|
1.366025403784439, 0.366025403784439, 0.366025403784439, 1.366025403784439,
|
||||||
Standard(GRADIENTS_2D, 1.366025403784439, 0.366025403784439, 0.366025403784439, 1.366025403784439,
|
0.788675134594813, -0.211324865405187, -0.211324865405187, 0.788675134594813),
|
||||||
0.788675134594813, -0.211324865405187, -0.211324865405187,
|
XBeforeY(GRADIENTS_2D_X_BEFORE_Y,
|
||||||
0.788675134594813), XBeforeY(GRADIENTS_2D_X_BEFORE_Y, 0.7071067811865476, 1.224744871380249,
|
0.7071067811865476, 1.224744871380249, -0.7071067811865476, 1.224744871380249,
|
||||||
-0.7071067811865476, 1.224744871380249, 0.7071067811865476, -0.7071067811865476,
|
0.7071067811865476, -0.7071067811865476, 0.40824829046764305, 0.40824829046764305);
|
||||||
0.40824829046764305, 0.40824829046764305);
|
|
||||||
|
|
||||||
Grad2[] gradients;
|
Grad2[] gradients;
|
||||||
double s00, s01, s10, s11;
|
double s00, s01, s10, s11;
|
||||||
double t00, t01, t10, t11;
|
double t00, t01, t10, t11;
|
||||||
|
|
||||||
private LatticeOrientation2D(Grad2[] gradients, double s00, double s01, double s10, double s11, double t00,
|
private LatticeOrientation2D(Grad2[] gradients,
|
||||||
double t01, double t10, double t11) {
|
double s00, double s01, double s10, double s11,
|
||||||
|
double t00, double t01, double t10, double t11) {
|
||||||
this.gradients = gradients;
|
this.gradients = gradients;
|
||||||
this.s00 = s00;
|
this.s00 = s00; this.s01 = s01; this.s10 = s10; this.s11 = s11;
|
||||||
this.s01 = s01;
|
this.t00 = t00; this.t01 = t01; this.t10 = t10; this.t11 = t11;
|
||||||
this.s10 = s10;
|
|
||||||
this.s11 = s11;
|
|
||||||
this.t00 = t00;
|
|
||||||
this.t01 = t01;
|
|
||||||
this.t10 = t10;
|
|
||||||
this.t11 = t11;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum LatticeOrientation3D {
|
public enum LatticeOrientation3D {
|
||||||
// Quaternions for 3D. Could use matrices, but I already wrote this code
|
// Quaternions for 3D. Could use matrices, but I already wrote this code before I moved them into here.
|
||||||
// before I moved them into here.
|
Classic(GRADIENTS_3D_CLASSIC, 0.577350269189626, 0.577350269189626, 0.577350269189626, 0),
|
||||||
Classic(GRADIENTS_3D_CLASSIC, 0.577350269189626, 0.577350269189626, 0.577350269189626, 0), XYBeforeZ(
|
XYBeforeZ(GRADIENTS_3D_XY_BEFORE_Z, 0.3250575836718682, -0.3250575836718682, 0, 0.8880738339771154),
|
||||||
GRADIENTS_3D_XY_BEFORE_Z, 0.3250575836718682, -0.3250575836718682, 0, 0.8880738339771154), XZBeforeY(
|
XZBeforeY(GRADIENTS_3D_XZ_BEFORE_Y, -0.3250575836718682, 0, 0.3250575836718682, 0.8880738339771154);
|
||||||
GRADIENTS_3D_XZ_BEFORE_Y, -0.3250575836718682, 0, 0.3250575836718682, 0.8880738339771154);
|
|
||||||
|
|
||||||
Grad3[] gradients;
|
Grad3[] gradients;
|
||||||
double qx, qy, qz, qw;
|
double qx, qy, qz, qw;
|
||||||
|
|
||||||
private LatticeOrientation3D(Grad3[] gradients, double qx, double qy, double qz, double qw) {
|
private LatticeOrientation3D(Grad3[] gradients, double qx, double qy, double qz, double qw) {
|
||||||
this.gradients = gradients;
|
this.gradients = gradients;
|
||||||
this.qx = qx;
|
this.qx = qx; this.qy = qy; this.qz = qz; this.qw = qw;
|
||||||
this.qy = qy;
|
|
||||||
this.qz = qz;
|
|
||||||
this.qw = qw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -953,20 +841,15 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
public static class Grad2 {
|
public static class Grad2 {
|
||||||
double dx, dy;
|
double dx, dy;
|
||||||
|
|
||||||
public Grad2(double dx, double dy) {
|
public Grad2(double dx, double dy) {
|
||||||
this.dx = dx;
|
this.dx = dx; this.dy = dy;
|
||||||
this.dy = dy;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Grad3 {
|
public static class Grad3 {
|
||||||
double dx, dy, dz;
|
double dx, dy, dz;
|
||||||
|
|
||||||
public Grad3(double dx, double dy, double dz) {
|
public Grad3(double dx, double dy, double dz) {
|
||||||
this.dx = dx;
|
this.dx = dx; this.dy = dy; this.dz = dz;
|
||||||
this.dy = dy;
|
|
||||||
this.dz = dz;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -978,23 +861,35 @@ public class OpenSimplex2S {
|
|||||||
|
|
||||||
GRADIENTS_2D = new Grad2[PSIZE];
|
GRADIENTS_2D = new Grad2[PSIZE];
|
||||||
GRADIENTS_2D_X_BEFORE_Y = new Grad2[PSIZE];
|
GRADIENTS_2D_X_BEFORE_Y = new Grad2[PSIZE];
|
||||||
Grad2[] grad2 = { new Grad2(0.130526192220052, 0.99144486137381),
|
Grad2[] grad2 = {
|
||||||
new Grad2(0.38268343236509, 0.923879532511287), new Grad2(0.608761429008721, 0.793353340291235),
|
new Grad2( 0.130526192220052, 0.99144486137381),
|
||||||
new Grad2(0.793353340291235, 0.608761429008721), new Grad2(0.923879532511287, 0.38268343236509),
|
new Grad2( 0.38268343236509, 0.923879532511287),
|
||||||
new Grad2(0.99144486137381, 0.130526192220051), new Grad2(0.99144486137381, -0.130526192220051),
|
new Grad2( 0.608761429008721, 0.793353340291235),
|
||||||
new Grad2(0.923879532511287, -0.38268343236509), new Grad2(0.793353340291235, -0.60876142900872),
|
new Grad2( 0.793353340291235, 0.608761429008721),
|
||||||
new Grad2(0.608761429008721, -0.793353340291235), new Grad2(0.38268343236509, -0.923879532511287),
|
new Grad2( 0.923879532511287, 0.38268343236509),
|
||||||
new Grad2(0.130526192220052, -0.99144486137381), new Grad2(-0.130526192220052, -0.99144486137381),
|
new Grad2( 0.99144486137381, 0.130526192220051),
|
||||||
new Grad2(-0.38268343236509, -0.923879532511287), new Grad2(-0.608761429008721, -0.793353340291235),
|
new Grad2( 0.99144486137381, -0.130526192220051),
|
||||||
new Grad2(-0.793353340291235, -0.608761429008721), new Grad2(-0.923879532511287, -0.38268343236509),
|
new Grad2( 0.923879532511287, -0.38268343236509),
|
||||||
new Grad2(-0.99144486137381, -0.130526192220052), new Grad2(-0.99144486137381, 0.130526192220051),
|
new Grad2( 0.793353340291235, -0.60876142900872),
|
||||||
new Grad2(-0.923879532511287, 0.38268343236509), new Grad2(-0.793353340291235, 0.608761429008721),
|
new Grad2( 0.608761429008721, -0.793353340291235),
|
||||||
new Grad2(-0.608761429008721, 0.793353340291235), new Grad2(-0.38268343236509, 0.923879532511287),
|
new Grad2( 0.38268343236509, -0.923879532511287),
|
||||||
new Grad2(-0.130526192220052, 0.99144486137381) };
|
new Grad2( 0.130526192220052, -0.99144486137381),
|
||||||
|
new Grad2(-0.130526192220052, -0.99144486137381),
|
||||||
|
new Grad2(-0.38268343236509, -0.923879532511287),
|
||||||
|
new Grad2(-0.608761429008721, -0.793353340291235),
|
||||||
|
new Grad2(-0.793353340291235, -0.608761429008721),
|
||||||
|
new Grad2(-0.923879532511287, -0.38268343236509),
|
||||||
|
new Grad2(-0.99144486137381, -0.130526192220052),
|
||||||
|
new Grad2(-0.99144486137381, 0.130526192220051),
|
||||||
|
new Grad2(-0.923879532511287, 0.38268343236509),
|
||||||
|
new Grad2(-0.793353340291235, 0.608761429008721),
|
||||||
|
new Grad2(-0.608761429008721, 0.793353340291235),
|
||||||
|
new Grad2(-0.38268343236509, 0.923879532511287),
|
||||||
|
new Grad2(-0.130526192220052, 0.99144486137381)
|
||||||
|
};
|
||||||
Grad2[] grad2XBeforeY = new Grad2[grad2.length];
|
Grad2[] grad2XBeforeY = new Grad2[grad2.length];
|
||||||
for (int i = 0; i < grad2.length; i++) {
|
for (int i = 0; i < grad2.length; i++) {
|
||||||
grad2[i].dx /= N2;
|
grad2[i].dx /= N2; grad2[i].dy /= N2;
|
||||||
grad2[i].dy /= N2;
|
|
||||||
|
|
||||||
// Unrotated gradients for XBeforeY 2D
|
// Unrotated gradients for XBeforeY 2D
|
||||||
double xx = grad2[i].dx * 0.7071067811865476;
|
double xx = grad2[i].dx * 0.7071067811865476;
|
||||||
@@ -1010,43 +905,61 @@ public class OpenSimplex2S {
|
|||||||
GRADIENTS_3D_CLASSIC = new Grad3[PSIZE];
|
GRADIENTS_3D_CLASSIC = new Grad3[PSIZE];
|
||||||
GRADIENTS_3D_XY_BEFORE_Z = new Grad3[PSIZE];
|
GRADIENTS_3D_XY_BEFORE_Z = new Grad3[PSIZE];
|
||||||
GRADIENTS_3D_XZ_BEFORE_Y = new Grad3[PSIZE];
|
GRADIENTS_3D_XZ_BEFORE_Y = new Grad3[PSIZE];
|
||||||
Grad3[] grad3 = { new Grad3(-2.22474487139, -2.22474487139, -1.0),
|
Grad3[] grad3 = {
|
||||||
|
new Grad3(-2.22474487139, -2.22474487139, -1.0),
|
||||||
new Grad3(-2.22474487139, -2.22474487139, 1.0),
|
new Grad3(-2.22474487139, -2.22474487139, 1.0),
|
||||||
new Grad3(-3.0862664687972017, -1.1721513422464978, 0.0),
|
new Grad3(-3.0862664687972017, -1.1721513422464978, 0.0),
|
||||||
new Grad3(-1.1721513422464978, -3.0862664687972017, 0.0),
|
new Grad3(-1.1721513422464978, -3.0862664687972017, 0.0),
|
||||||
new Grad3(-2.22474487139, -1.0, -2.22474487139), new Grad3(-2.22474487139, 1.0, -2.22474487139),
|
new Grad3(-2.22474487139, -1.0, -2.22474487139),
|
||||||
|
new Grad3(-2.22474487139, 1.0, -2.22474487139),
|
||||||
new Grad3(-1.1721513422464978, 0.0, -3.0862664687972017),
|
new Grad3(-1.1721513422464978, 0.0, -3.0862664687972017),
|
||||||
new Grad3(-3.0862664687972017, 0.0, -1.1721513422464978),
|
new Grad3(-3.0862664687972017, 0.0, -1.1721513422464978),
|
||||||
new Grad3(-2.22474487139, -1.0, 2.22474487139), new Grad3(-2.22474487139, 1.0, 2.22474487139),
|
new Grad3(-2.22474487139, -1.0, 2.22474487139),
|
||||||
|
new Grad3(-2.22474487139, 1.0, 2.22474487139),
|
||||||
new Grad3(-3.0862664687972017, 0.0, 1.1721513422464978),
|
new Grad3(-3.0862664687972017, 0.0, 1.1721513422464978),
|
||||||
new Grad3(-1.1721513422464978, 0.0, 3.0862664687972017), new Grad3(-2.22474487139, 2.22474487139, -1.0),
|
new Grad3(-1.1721513422464978, 0.0, 3.0862664687972017),
|
||||||
new Grad3(-2.22474487139, 2.22474487139, 1.0), new Grad3(-1.1721513422464978, 3.0862664687972017, 0.0),
|
new Grad3(-2.22474487139, 2.22474487139, -1.0),
|
||||||
|
new Grad3(-2.22474487139, 2.22474487139, 1.0),
|
||||||
|
new Grad3(-1.1721513422464978, 3.0862664687972017, 0.0),
|
||||||
new Grad3(-3.0862664687972017, 1.1721513422464978, 0.0),
|
new Grad3(-3.0862664687972017, 1.1721513422464978, 0.0),
|
||||||
new Grad3(-1.0, -2.22474487139, -2.22474487139), new Grad3(1.0, -2.22474487139, -2.22474487139),
|
new Grad3(-1.0, -2.22474487139, -2.22474487139),
|
||||||
|
new Grad3( 1.0, -2.22474487139, -2.22474487139),
|
||||||
new Grad3( 0.0, -3.0862664687972017, -1.1721513422464978),
|
new Grad3( 0.0, -3.0862664687972017, -1.1721513422464978),
|
||||||
new Grad3( 0.0, -1.1721513422464978, -3.0862664687972017),
|
new Grad3( 0.0, -1.1721513422464978, -3.0862664687972017),
|
||||||
new Grad3(-1.0, -2.22474487139, 2.22474487139), new Grad3(1.0, -2.22474487139, 2.22474487139),
|
new Grad3(-1.0, -2.22474487139, 2.22474487139),
|
||||||
|
new Grad3( 1.0, -2.22474487139, 2.22474487139),
|
||||||
new Grad3( 0.0, -1.1721513422464978, 3.0862664687972017),
|
new Grad3( 0.0, -1.1721513422464978, 3.0862664687972017),
|
||||||
new Grad3(0.0, -3.0862664687972017, 1.1721513422464978), new Grad3(-1.0, 2.22474487139, -2.22474487139),
|
new Grad3( 0.0, -3.0862664687972017, 1.1721513422464978),
|
||||||
new Grad3(1.0, 2.22474487139, -2.22474487139), new Grad3(0.0, 1.1721513422464978, -3.0862664687972017),
|
new Grad3(-1.0, 2.22474487139, -2.22474487139),
|
||||||
new Grad3(0.0, 3.0862664687972017, -1.1721513422464978), new Grad3(-1.0, 2.22474487139, 2.22474487139),
|
new Grad3( 1.0, 2.22474487139, -2.22474487139),
|
||||||
new Grad3(1.0, 2.22474487139, 2.22474487139), new Grad3(0.0, 3.0862664687972017, 1.1721513422464978),
|
new Grad3( 0.0, 1.1721513422464978, -3.0862664687972017),
|
||||||
new Grad3(0.0, 1.1721513422464978, 3.0862664687972017), new Grad3(2.22474487139, -2.22474487139, -1.0),
|
new Grad3( 0.0, 3.0862664687972017, -1.1721513422464978),
|
||||||
new Grad3(2.22474487139, -2.22474487139, 1.0), new Grad3(1.1721513422464978, -3.0862664687972017, 0.0),
|
new Grad3(-1.0, 2.22474487139, 2.22474487139),
|
||||||
new Grad3(3.0862664687972017, -1.1721513422464978, 0.0), new Grad3(2.22474487139, -1.0, -2.22474487139),
|
new Grad3( 1.0, 2.22474487139, 2.22474487139),
|
||||||
new Grad3(2.22474487139, 1.0, -2.22474487139), new Grad3(3.0862664687972017, 0.0, -1.1721513422464978),
|
new Grad3( 0.0, 3.0862664687972017, 1.1721513422464978),
|
||||||
new Grad3(1.1721513422464978, 0.0, -3.0862664687972017), new Grad3(2.22474487139, -1.0, 2.22474487139),
|
new Grad3( 0.0, 1.1721513422464978, 3.0862664687972017),
|
||||||
new Grad3(2.22474487139, 1.0, 2.22474487139), new Grad3(1.1721513422464978, 0.0, 3.0862664687972017),
|
new Grad3( 2.22474487139, -2.22474487139, -1.0),
|
||||||
new Grad3(3.0862664687972017, 0.0, 1.1721513422464978), new Grad3(2.22474487139, 2.22474487139, -1.0),
|
new Grad3( 2.22474487139, -2.22474487139, 1.0),
|
||||||
new Grad3(2.22474487139, 2.22474487139, 1.0), new Grad3(3.0862664687972017, 1.1721513422464978, 0.0),
|
new Grad3( 1.1721513422464978, -3.0862664687972017, 0.0),
|
||||||
new Grad3(1.1721513422464978, 3.0862664687972017, 0.0) };
|
new Grad3( 3.0862664687972017, -1.1721513422464978, 0.0),
|
||||||
|
new Grad3( 2.22474487139, -1.0, -2.22474487139),
|
||||||
|
new Grad3( 2.22474487139, 1.0, -2.22474487139),
|
||||||
|
new Grad3( 3.0862664687972017, 0.0, -1.1721513422464978),
|
||||||
|
new Grad3( 1.1721513422464978, 0.0, -3.0862664687972017),
|
||||||
|
new Grad3( 2.22474487139, -1.0, 2.22474487139),
|
||||||
|
new Grad3( 2.22474487139, 1.0, 2.22474487139),
|
||||||
|
new Grad3( 1.1721513422464978, 0.0, 3.0862664687972017),
|
||||||
|
new Grad3( 3.0862664687972017, 0.0, 1.1721513422464978),
|
||||||
|
new Grad3( 2.22474487139, 2.22474487139, -1.0),
|
||||||
|
new Grad3( 2.22474487139, 2.22474487139, 1.0),
|
||||||
|
new Grad3( 3.0862664687972017, 1.1721513422464978, 0.0),
|
||||||
|
new Grad3( 1.1721513422464978, 3.0862664687972017, 0.0)
|
||||||
|
};
|
||||||
Grad3[] grad3Classic = new Grad3[grad3.length];
|
Grad3[] grad3Classic = new Grad3[grad3.length];
|
||||||
Grad3[] grad3XYBeforeZ = new Grad3[grad3.length];
|
Grad3[] grad3XYBeforeZ = new Grad3[grad3.length];
|
||||||
Grad3[] grad3XZBeforeY = new Grad3[grad3.length];
|
Grad3[] grad3XZBeforeY = new Grad3[grad3.length];
|
||||||
for (int i = 0; i < grad3.length; i++) {
|
for (int i = 0; i < grad3.length; i++) {
|
||||||
grad3[i].dx /= N3;
|
grad3[i].dx /= N3; grad3[i].dy /= N3; grad3[i].dz /= N3;
|
||||||
grad3[i].dy /= N3;
|
|
||||||
grad3[i].dz /= N3;
|
|
||||||
double gxr = grad3[i].dx, gyr = grad3[i].dy, gzr = grad3[i].dz;
|
double gxr = grad3[i].dx, gyr = grad3[i].dy, gzr = grad3[i].dz;
|
||||||
|
|
||||||
// Unrotated gradients for classic 3D
|
// Unrotated gradients for classic 3D
|
||||||
|
@@ -611,7 +611,8 @@ public class ArrayUtil {
|
|||||||
int end = offset + length;
|
int end = offset + length;
|
||||||
if (end > arrayLength || offset < 0)
|
if (end > arrayLength || offset < 0)
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Array contains [0; " + arrayLength + "), requested [" + offset + "; " + end + ")");
|
"Array contains [0; " + arrayLength + "), requested [" + offset + "; " + end + ")"
|
||||||
|
);
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
@@ -627,7 +628,8 @@ public class ArrayUtil {
|
|||||||
|
|
||||||
if (end > arrayLength || start < 0)
|
if (end > arrayLength || start < 0)
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Array contains [0; " + arrayLength + "), requested [" + start + "; " + end + ")");
|
"Array contains [0; " + arrayLength + "), requested [" + start + "; " + end + ")"
|
||||||
|
);
|
||||||
|
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
@@ -30,8 +30,18 @@ public class PrimitiveUtil {
|
|||||||
private static final Map<Class<?>, Object> PRIMITIVE_TO_NULL = new HashMap<>();
|
private static final Map<Class<?>, Object> PRIMITIVE_TO_NULL = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
for (Class<?> boxed : new Class<?>[] { Boolean.class, Byte.class, Short.class, Character.class, Integer.class,
|
for (
|
||||||
Long.class, Float.class, Double.class }) {
|
Class<?> boxed : new Class<?>[] {
|
||||||
|
Boolean.class,
|
||||||
|
Byte.class,
|
||||||
|
Short.class,
|
||||||
|
Character.class,
|
||||||
|
Integer.class,
|
||||||
|
Long.class,
|
||||||
|
Float.class,
|
||||||
|
Double.class
|
||||||
|
}
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
PRIMITIVE_TO_BOXED.put((Class<?>) boxed.getField("TYPE").get(null), boxed);
|
PRIMITIVE_TO_BOXED.put((Class<?>) boxed.getField("TYPE").get(null), boxed);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@@ -40,7 +40,8 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains static methods to create {@link Stream Streams} that synchronize
|
* Contains static methods to create {@link Stream Streams} that synchronize
|
||||||
* their <a href=
|
* their
|
||||||
|
* <a href=
|
||||||
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
|
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
|
||||||
* terminal operations</a> on a given monitor.
|
* terminal operations</a> on a given monitor.
|
||||||
*
|
*
|
||||||
@@ -1069,18 +1070,21 @@ public class SyncStreams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the given {@link Stream} to make all <a href=
|
* Wraps the given {@link Stream} to make all
|
||||||
|
* <a href=
|
||||||
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
|
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
|
||||||
* terminal operations</a> acquire the provided monitor's lock before
|
* terminal operations</a> acquire the provided monitor's lock before
|
||||||
* execution. Intermediate operations return streams that are also
|
* execution. Intermediate operations
|
||||||
* synchronized on the same object. The created stream will behave
|
* return streams that are also synchronized on the same object. The created
|
||||||
* identically to the provided stream in all other aspects. Use this to
|
* stream will behave identically
|
||||||
* synchronize access to stream's source.
|
* to the provided stream in all other aspects. Use this to synchronize
|
||||||
|
* access to stream's source.
|
||||||
* <p>
|
* <p>
|
||||||
* <i>The returned {@code Stream}'s {@link Stream#iterator() iterator()} and
|
* <i>The returned {@code Stream}'s {@link Stream#iterator() iterator()} and
|
||||||
* {@link Stream#spliterator() spliterator()} methods return regular
|
* {@link Stream#spliterator()
|
||||||
* non-synchronized iterators and spliterators respectively</i>. It is the
|
* spliterator()} methods return regular non-synchronized iterators and
|
||||||
* user's responsibility to avoid concurrency issues:
|
* spliterators respectively</i>. It
|
||||||
|
* is the user's responsibility to avoid concurrency issues:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* synchronized (stream.getMonitor()) {
|
* synchronized (stream.getMonitor()) {
|
||||||
@@ -1099,17 +1103,14 @@ public class SyncStreams {
|
|||||||
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
|
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param <T>
|
* @param <T> the class of objects in the Stream
|
||||||
* the class of objects in the Stream
|
* @param stream the stream to wrap.
|
||||||
* @param stream
|
* @param monitor the object that the stream will use for synchronization.
|
||||||
* the stream to wrap.
|
* When {@code null}, the stream
|
||||||
* @param monitor
|
* will synchronize on itself.
|
||||||
* the object that the stream will use for synchronization. When
|
|
||||||
* {@code null}, the stream will synchronize on itself.
|
|
||||||
* @return a {@link SyncStream SyncStream<T>} synchronized on
|
* @return a {@link SyncStream SyncStream<T>} synchronized on
|
||||||
* {@code monitor} and backed by {@code stream}.
|
* {@code monitor} and backed by {@code stream}.
|
||||||
* @throws NullPointerException
|
* @throws NullPointerException if {@code stream == null}.
|
||||||
* if {@code stream == null}.
|
|
||||||
*/
|
*/
|
||||||
public static <T> SyncStream<T> synchronizedStream(Stream<T> stream, Object monitor) {
|
public static <T> SyncStream<T> synchronizedStream(Stream<T> stream, Object monitor) {
|
||||||
Objects.requireNonNull(stream, "stream cannot be null");
|
Objects.requireNonNull(stream, "stream cannot be null");
|
||||||
@@ -1117,19 +1118,22 @@ public class SyncStreams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the given {@link IntStream} to make all <a href=
|
* Wraps the given {@link IntStream} to make all
|
||||||
|
* <a href=
|
||||||
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
|
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
|
||||||
* terminal operations</a> acquire the provided monitor's lock before
|
* terminal operations</a> acquire the provided monitor's lock before
|
||||||
* execution. Intermediate operations return streams that are also
|
* execution. Intermediate operations
|
||||||
* synchronized on the same object. The created stream will behave
|
* return streams that are also synchronized on the same object. The created
|
||||||
* identically to the provided stream in all other aspects. Use this to
|
* stream will behave identically
|
||||||
* synchronize access to stream's source.
|
* to the provided stream in all other aspects. Use this to synchronize
|
||||||
|
* access to stream's source.
|
||||||
* <p>
|
* <p>
|
||||||
* <i>The returned {@code IntStream}'s {@link IntStream#iterator()
|
* <i>The returned {@code IntStream}'s {@link IntStream#iterator()
|
||||||
* iterator()} and {@link IntStream#spliterator() spliterator()} methods
|
* iterator()} and
|
||||||
* return regular non-synchronized iterators and spliterators
|
* {@link IntStream#spliterator() spliterator()} methods return regular
|
||||||
* respectively</i>. It is the user's responsibility to avoid concurrency
|
* non-synchronized iterators and
|
||||||
* issues:
|
* spliterators respectively</i>. It is the user's responsibility to avoid
|
||||||
|
* concurrency issues:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* synchronized (stream.getMonitor()) {
|
* synchronized (stream.getMonitor()) {
|
||||||
@@ -1148,15 +1152,13 @@ public class SyncStreams {
|
|||||||
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
|
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param stream
|
* @param stream the stream to wrap.
|
||||||
* the stream to wrap.
|
* @param monitor the object that the stream will use for synchronization.
|
||||||
* @param monitor
|
* When {@code null}, the stream
|
||||||
* the object that the stream will use for synchronization. When
|
* will synchronize on itself.
|
||||||
* {@code null}, the stream will synchronize on itself.
|
|
||||||
* @return a {@link SyncIntStream} synchronized on {@code monitor} and
|
* @return a {@link SyncIntStream} synchronized on {@code monitor} and
|
||||||
* backed by {@code stream}.
|
* backed by {@code stream}.
|
||||||
* @throws NullPointerException
|
* @throws NullPointerException if {@code stream == null}.
|
||||||
* if {@code stream == null}.
|
|
||||||
*/
|
*/
|
||||||
public static SyncIntStream synchronizedStream(IntStream stream, Object monitor) {
|
public static SyncIntStream synchronizedStream(IntStream stream, Object monitor) {
|
||||||
Objects.requireNonNull(stream, "stream cannot be null");
|
Objects.requireNonNull(stream, "stream cannot be null");
|
||||||
@@ -1164,19 +1166,22 @@ public class SyncStreams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the given {@link LongStream} to make all <a href=
|
* Wraps the given {@link LongStream} to make all
|
||||||
|
* <a href=
|
||||||
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
|
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
|
||||||
* terminal operations</a> acquire the provided monitor's lock before
|
* terminal operations</a> acquire the provided monitor's lock before
|
||||||
* execution. Intermediate operations return streams that are also
|
* execution. Intermediate operations
|
||||||
* synchronized on the same object. The created stream will behave
|
* return streams that are also synchronized on the same object. The created
|
||||||
* identically to the provided stream in all other aspects. Use this to
|
* stream will behave identically
|
||||||
* synchronize access to stream's source.
|
* to the provided stream in all other aspects. Use this to synchronize
|
||||||
|
* access to stream's source.
|
||||||
* <p>
|
* <p>
|
||||||
* <i>The returned {@code LongStream}'s {@link LongStream#iterator()
|
* <i>The returned {@code LongStream}'s {@link LongStream#iterator()
|
||||||
* iterator()} and {@link LongStream#spliterator() spliterator()} methods
|
* iterator()} and
|
||||||
* return regular non-synchronized iterators and spliterators
|
* {@link LongStream#spliterator() spliterator()} methods return regular
|
||||||
* respectively</i>. It is the user's responsibility to avoid concurrency
|
* non-synchronized iterators and
|
||||||
* issues:
|
* spliterators respectively</i>. It is the user's responsibility to avoid
|
||||||
|
* concurrency issues:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* synchronized (stream.getMonitor()) {
|
* synchronized (stream.getMonitor()) {
|
||||||
@@ -1195,15 +1200,13 @@ public class SyncStreams {
|
|||||||
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
|
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param stream
|
* @param stream the stream to wrap.
|
||||||
* the stream to wrap.
|
* @param monitor the object that the stream will use for synchronization.
|
||||||
* @param monitor
|
* When {@code null}, the stream
|
||||||
* the object that the stream will use for synchronization. When
|
* will synchronize on itself.
|
||||||
* {@code null}, the stream will synchronize on itself.
|
|
||||||
* @return a {@link SyncLongStream} synchronized on {@code monitor} and
|
* @return a {@link SyncLongStream} synchronized on {@code monitor} and
|
||||||
* backed by {@code stream}.
|
* backed by {@code stream}.
|
||||||
* @throws NullPointerException
|
* @throws NullPointerException if {@code stream == null}.
|
||||||
* if {@code stream == null}.
|
|
||||||
*/
|
*/
|
||||||
public static SyncLongStream synchronizedStream(LongStream stream, Object monitor) {
|
public static SyncLongStream synchronizedStream(LongStream stream, Object monitor) {
|
||||||
Objects.requireNonNull(stream, "stream cannot be null");
|
Objects.requireNonNull(stream, "stream cannot be null");
|
||||||
@@ -1211,19 +1214,22 @@ public class SyncStreams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the given {@link DoubleStream} to make all <a href=
|
* Wraps the given {@link DoubleStream} to make all
|
||||||
|
* <a href=
|
||||||
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
|
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
|
||||||
* terminal operations</a> acquire the provided monitor's lock before
|
* terminal operations</a> acquire the provided monitor's lock before
|
||||||
* execution. Intermediate operations return streams that are also
|
* execution. Intermediate operations
|
||||||
* synchronized on the same object. The created stream will behave
|
* return streams that are also synchronized on the same object. The created
|
||||||
* identically to the provided stream in all other aspects. Use this to
|
* stream will behave identically
|
||||||
* synchronize access to stream's source.
|
* to the provided stream in all other aspects. Use this to synchronize
|
||||||
|
* access to stream's source.
|
||||||
* <p>
|
* <p>
|
||||||
* <i>The returned {@code DoubleStream}'s {@link DoubleStream#iterator()
|
* <i>The returned {@code DoubleStream}'s {@link DoubleStream#iterator()
|
||||||
* iterator()} and {@link DoubleStream#spliterator() spliterator()} methods
|
* iterator()} and
|
||||||
* return regular non-synchronized iterators and spliterators
|
* {@link DoubleStream#spliterator() spliterator()} methods return regular
|
||||||
* respectively</i>. It is the user's responsibility to avoid concurrency
|
* non-synchronized iterators and
|
||||||
* issues:
|
* spliterators respectively</i>. It is the user's responsibility to avoid
|
||||||
|
* concurrency issues:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* synchronized (stream.getMonitor()) {
|
* synchronized (stream.getMonitor()) {
|
||||||
@@ -1242,15 +1248,13 @@ public class SyncStreams {
|
|||||||
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
|
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param stream
|
* @param stream the stream to wrap.
|
||||||
* the stream to wrap.
|
* @param monitor the object that the stream will use for synchronization.
|
||||||
* @param monitor
|
* When {@code null}, the stream
|
||||||
* the object that the stream will use for synchronization. When
|
* will synchronize on itself.
|
||||||
* {@code null}, the stream will synchronize on itself.
|
|
||||||
* @return a {@link SyncDoubleStream} synchronized on {@code monitor} and
|
* @return a {@link SyncDoubleStream} synchronized on {@code monitor} and
|
||||||
* backed by {@code stream}.
|
* backed by {@code stream}.
|
||||||
* @throws NullPointerException
|
* @throws NullPointerException if {@code stream == null}.
|
||||||
* if {@code stream == null}.
|
|
||||||
*/
|
*/
|
||||||
public static SyncDoubleStream synchronizedStream(DoubleStream stream, Object monitor) {
|
public static SyncDoubleStream synchronizedStream(DoubleStream stream, Object monitor) {
|
||||||
Objects.requireNonNull(stream, "stream cannot be null");
|
Objects.requireNonNull(stream, "stream cannot be null");
|
||||||
|
@@ -103,8 +103,14 @@ public class Escaper {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Escaper JAVA = new Escaper('\\', 'u', "tbnrf'\"".toCharArray(), "\t\b\n\r\f\'\"".toCharArray(),
|
public static final Escaper JAVA = new Escaper(
|
||||||
true, true);
|
'\\',
|
||||||
|
'u',
|
||||||
|
"tbnrf'\"".toCharArray(),
|
||||||
|
"\t\b\n\r\f\'\"".toCharArray(),
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
private final char escapeChar;
|
private final char escapeChar;
|
||||||
private final char unicodeEscapeChar;
|
private final char unicodeEscapeChar;
|
||||||
@@ -114,8 +120,14 @@ public class Escaper {
|
|||||||
private final boolean preferUnicode;
|
private final boolean preferUnicode;
|
||||||
private final boolean strict;
|
private final boolean strict;
|
||||||
|
|
||||||
protected Escaper(char escapeChar, char unicodeEscapeChar, char[] safes, char[] unsafes, boolean preferUnicode,
|
protected Escaper(
|
||||||
boolean strict) {
|
char escapeChar,
|
||||||
|
char unicodeEscapeChar,
|
||||||
|
char[] safes,
|
||||||
|
char[] unsafes,
|
||||||
|
boolean preferUnicode,
|
||||||
|
boolean strict
|
||||||
|
) {
|
||||||
this.escapeChar = escapeChar;
|
this.escapeChar = escapeChar;
|
||||||
this.unicodeEscapeChar = unicodeEscapeChar;
|
this.unicodeEscapeChar = unicodeEscapeChar;
|
||||||
this.safes = safes;
|
this.safes = safes;
|
||||||
@@ -140,7 +152,8 @@ public class Escaper {
|
|||||||
for (char c : unsafes) {
|
for (char c : unsafes) {
|
||||||
if (c == escapeChar)
|
if (c == escapeChar)
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Unsafe characters contain escape chatacter (escape character is escaped automatically)");
|
"Unsafe characters contain escape chatacter (escape character is escaped automatically)"
|
||||||
|
);
|
||||||
if (c == unicodeEscapeChar)
|
if (c == unicodeEscapeChar)
|
||||||
throw new IllegalArgumentException("Unsafe characters contain Unicode escape chatacter");
|
throw new IllegalArgumentException("Unsafe characters contain Unicode escape chatacter");
|
||||||
}
|
}
|
||||||
@@ -160,7 +173,11 @@ public class Escaper {
|
|||||||
end = Integer.MAX_VALUE;
|
end = Integer.MAX_VALUE;
|
||||||
else
|
else
|
||||||
end = src.getPosition() + length;
|
end = src.getPosition() + length;
|
||||||
while (src.has() && src.getPosition() < end && (until == null || !until.test(src.current())))
|
while (
|
||||||
|
src.has() &&
|
||||||
|
src.getPosition() < end &&
|
||||||
|
(until == null || !until.test(src.current()))
|
||||||
|
)
|
||||||
escape(src.consume(), output);
|
escape(src.consume(), output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +225,11 @@ public class Escaper {
|
|||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
while (src.has() && src.getPosition() < end && (until == null || !until.test(src.current()))) {
|
while (
|
||||||
|
src.has() &&
|
||||||
|
src.getPosition() < end &&
|
||||||
|
(until == null || !until.test(src.current()))
|
||||||
|
) {
|
||||||
result += getEscapedLength(src.consume());
|
result += getEscapedLength(src.consume());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +257,11 @@ public class Escaper {
|
|||||||
end = Integer.MAX_VALUE;
|
end = Integer.MAX_VALUE;
|
||||||
else
|
else
|
||||||
end = src.getPosition() + length;
|
end = src.getPosition() + length;
|
||||||
while (src.has() && src.getPosition() < end && (until == null || !until.test(src.current()))) {
|
while (
|
||||||
|
src.has() &&
|
||||||
|
src.getPosition() < end &&
|
||||||
|
(until == null || !until.test(src.current()))
|
||||||
|
) {
|
||||||
output.accept(unescapeOneSequence(src));
|
output.accept(unescapeOneSequence(src));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,8 +282,10 @@ public class Escaper {
|
|||||||
|
|
||||||
if (src.current() == unicodeEscapeChar) {
|
if (src.current() == unicodeEscapeChar) {
|
||||||
src.next();
|
src.next();
|
||||||
return (char) (hexValue(src.consume()) << (4 * 3) | hexValue(src.consume()) << (4 * 2)
|
return (char) (hexValue(src.consume()) << (4 * 3) |
|
||||||
| hexValue(src.consume()) << (4 * 1) | hexValue(src.consume()) << (4 * 0));
|
hexValue(src.consume()) << (4 * 2) |
|
||||||
|
hexValue(src.consume()) << (4 * 1) |
|
||||||
|
hexValue(src.consume()) << (4 * 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = ArrayUtil.firstIndexOf(safes, src.current());
|
int index = ArrayUtil.firstIndexOf(safes, src.current());
|
||||||
@@ -288,7 +315,11 @@ public class Escaper {
|
|||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
while (src.has() && src.getPosition() < end && (until == null || !until.test(src.current()))) {
|
while (
|
||||||
|
src.has() &&
|
||||||
|
src.getPosition() < end &&
|
||||||
|
(until == null || !until.test(src.current()))
|
||||||
|
) {
|
||||||
skipOneSequence(src);
|
skipOneSequence(src);
|
||||||
result++;
|
result++;
|
||||||
}
|
}
|
||||||
@@ -297,7 +328,11 @@ public class Escaper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void skipOneSequence(CharReader src) {
|
public void skipOneSequence(CharReader src) {
|
||||||
if (src.current() == escapeChar && src.next() == unicodeEscapeChar) {
|
if (
|
||||||
|
src.current() == escapeChar
|
||||||
|
&&
|
||||||
|
src.next() == unicodeEscapeChar
|
||||||
|
) {
|
||||||
src.advance(4);
|
src.advance(4);
|
||||||
}
|
}
|
||||||
src.next();
|
src.next();
|
||||||
|
@@ -41,8 +41,13 @@ public class StringUtil {
|
|||||||
private static final String EMPTY_PLACEHOLDER = "[empty]";
|
private static final String EMPTY_PLACEHOLDER = "[empty]";
|
||||||
private static final String DEFAULT_SEPARATOR = "; ";
|
private static final String DEFAULT_SEPARATOR = "; ";
|
||||||
|
|
||||||
public static <T> String arrayToString(T[] array, String separator, String empty, String nullPlaceholder,
|
public static <T> String arrayToString(
|
||||||
String nullArray) {
|
T[] array,
|
||||||
|
String separator,
|
||||||
|
String empty,
|
||||||
|
String nullPlaceholder,
|
||||||
|
String nullArray
|
||||||
|
) {
|
||||||
|
|
||||||
if (separator == null) {
|
if (separator == null) {
|
||||||
throw new IllegalArgumentException(new NullPointerException());
|
throw new IllegalArgumentException(new NullPointerException());
|
||||||
@@ -74,8 +79,13 @@ public class StringUtil {
|
|||||||
return arrayToString(array, DEFAULT_SEPARATOR);
|
return arrayToString(array, DEFAULT_SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String iteratorToString(Iterator<?> iterator, String separator, String empty, String nullPlaceholder,
|
public static String iteratorToString(
|
||||||
String nullIterator) {
|
Iterator<?> iterator,
|
||||||
|
String separator,
|
||||||
|
String empty,
|
||||||
|
String nullPlaceholder,
|
||||||
|
String nullIterator
|
||||||
|
) {
|
||||||
|
|
||||||
if (separator == null) {
|
if (separator == null) {
|
||||||
throw new IllegalArgumentException(new NullPointerException());
|
throw new IllegalArgumentException(new NullPointerException());
|
||||||
@@ -109,8 +119,13 @@ public class StringUtil {
|
|||||||
return iteratorToString(iterator, DEFAULT_SEPARATOR);
|
return iteratorToString(iterator, DEFAULT_SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String iterableToString(Iterable<?> iterable, String separator, String empty, String nullPlaceholder,
|
public static String iterableToString(
|
||||||
String nullIterable) {
|
Iterable<?> iterable,
|
||||||
|
String separator,
|
||||||
|
String empty,
|
||||||
|
String nullPlaceholder,
|
||||||
|
String nullIterable
|
||||||
|
) {
|
||||||
|
|
||||||
if (separator == null) {
|
if (separator == null) {
|
||||||
throw new IllegalArgumentException(new NullPointerException());
|
throw new IllegalArgumentException(new NullPointerException());
|
||||||
@@ -131,8 +146,14 @@ public class StringUtil {
|
|||||||
return iterableToString(iterable, DEFAULT_SEPARATOR);
|
return iterableToString(iterable, DEFAULT_SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> String supplierToString(IntFunction<T> supplier, int length, String separator, String empty,
|
public static <T> String supplierToString(
|
||||||
String nullPlaceholder, String nullSupplier) {
|
IntFunction<T> supplier,
|
||||||
|
int length,
|
||||||
|
String separator,
|
||||||
|
String empty,
|
||||||
|
String nullPlaceholder,
|
||||||
|
String nullSupplier
|
||||||
|
) {
|
||||||
|
|
||||||
if (separator == null)
|
if (separator == null)
|
||||||
throw new IllegalArgumentException(new NullPointerException());
|
throw new IllegalArgumentException(new NullPointerException());
|
||||||
@@ -142,15 +163,28 @@ public class StringUtil {
|
|||||||
return empty;
|
return empty;
|
||||||
|
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
return supplierToStringExactly(supplier, length, separator, nullPlaceholder);
|
return supplierToStringExactly(
|
||||||
|
supplier,
|
||||||
|
length,
|
||||||
|
separator,
|
||||||
|
nullPlaceholder
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return supplierToStringUntilNull(supplier, separator, empty);
|
return supplierToStringUntilNull(
|
||||||
|
supplier,
|
||||||
|
separator,
|
||||||
|
empty
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> String supplierToStringExactly(IntFunction<T> supplier, int length, String separator,
|
private static <T> String supplierToStringExactly(
|
||||||
String nullPlaceholder) {
|
IntFunction<T> supplier,
|
||||||
|
int length,
|
||||||
|
String separator,
|
||||||
|
String nullPlaceholder
|
||||||
|
) {
|
||||||
T element = supplier.apply(0);
|
T element = supplier.apply(0);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(element == null ? nullPlaceholder : element.toString());
|
StringBuilder sb = new StringBuilder(element == null ? nullPlaceholder : element.toString());
|
||||||
@@ -164,7 +198,11 @@ public class StringUtil {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> String supplierToStringUntilNull(IntFunction<T> supplier, String separator, String empty) {
|
private static <T> String supplierToStringUntilNull(
|
||||||
|
IntFunction<T> supplier,
|
||||||
|
String separator,
|
||||||
|
String empty
|
||||||
|
) {
|
||||||
T element = supplier.apply(0);
|
T element = supplier.apply(0);
|
||||||
|
|
||||||
if (element == null) {
|
if (element == null) {
|
||||||
@@ -328,7 +366,11 @@ public class StringUtil {
|
|||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
charLoop: for (char c : src.toCharArray()) {
|
charLoop: for (char c : src.toCharArray()) {
|
||||||
if ((resultIndex + 1) < arrayLength && test.test(c)) {
|
if (
|
||||||
|
(resultIndex + 1) < arrayLength
|
||||||
|
&&
|
||||||
|
test.test(c)
|
||||||
|
) {
|
||||||
result[resultIndex] = resetStringBuilder(sb);
|
result[resultIndex] = resetStringBuilder(sb);
|
||||||
++resultIndex;
|
++resultIndex;
|
||||||
continue charLoop;
|
continue charLoop;
|
||||||
@@ -347,17 +389,17 @@ public class StringUtil {
|
|||||||
* index.
|
* index.
|
||||||
* <p>
|
* <p>
|
||||||
* Indices {@code 0} and {@code src.length() - 1} produce {@code str}
|
* Indices {@code 0} and {@code src.length() - 1} produce {@code str}
|
||||||
* excluding the specified character and {@code ""}.
|
* excluding
|
||||||
|
* the specified character and {@code ""}.
|
||||||
* <p>
|
* <p>
|
||||||
*
|
*
|
||||||
* @param src
|
* @param src the String to split
|
||||||
* the String to split
|
* @param at index to split at
|
||||||
* @param at
|
* @throws IllegalArgumentException if the index is out of bounds for
|
||||||
* index to split at
|
* {@code src}
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* if the index is out of bounds for {@code src}
|
|
||||||
* @return an array containing the substrings, in order of encounter in
|
* @return an array containing the substrings, in order of encounter in
|
||||||
* {@code src}. Its length is always 2.
|
* {@code src}.
|
||||||
|
* Its length is always 2.
|
||||||
*/
|
*/
|
||||||
public static String[] splitAt(String src, int at) {
|
public static String[] splitAt(String src, int at) {
|
||||||
Objects.requireNonNull(src, "src");
|
Objects.requireNonNull(src, "src");
|
||||||
@@ -374,7 +416,10 @@ public class StringUtil {
|
|||||||
return new String[] { src.substring(0, src.length() - 1), "" };
|
return new String[] { src.substring(0, src.length() - 1), "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
return new String[] { src.substring(0, at), src.substring(at + 1) };
|
return new String[] {
|
||||||
|
src.substring(0, at),
|
||||||
|
src.substring(at + 1)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -382,7 +427,8 @@ public class StringUtil {
|
|||||||
* indices.
|
* indices.
|
||||||
* <p>
|
* <p>
|
||||||
* Indices {@code 0} and {@code src.length() - 1} produce extra zero-length
|
* Indices {@code 0} and {@code src.length() - 1} produce extra zero-length
|
||||||
* outputs. Duplicate indices produce extra zero-length outputs.
|
* outputs.
|
||||||
|
* Duplicate indices produce extra zero-length outputs.
|
||||||
* <p>
|
* <p>
|
||||||
* Examples:
|
* Examples:
|
||||||
*
|
*
|
||||||
@@ -393,14 +439,13 @@ public class StringUtil {
|
|||||||
* splitAt("a.b", 1, 1, 1) -> {"a", "", "", "b"}
|
* splitAt("a.b", 1, 1, 1) -> {"a", "", "", "b"}
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param src
|
* @param src the String to split
|
||||||
* the String to split
|
* @param at indices to split at, in any order
|
||||||
* @param at
|
* @throws IllegalArgumentException if some index is out of bounds for
|
||||||
* indices to split at, in any order
|
* {@code src}
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* if some index is out of bounds for {@code src}
|
|
||||||
* @return an array containing the substrings, in order of encounter in
|
* @return an array containing the substrings, in order of encounter in
|
||||||
* {@code src}. Its length is always {@code at.length + 1}.
|
* {@code src}.
|
||||||
|
* Its length is always {@code at.length + 1}.
|
||||||
*/
|
*/
|
||||||
public static String[] splitAt(String src, int... at) {
|
public static String[] splitAt(String src, int... at) {
|
||||||
Objects.requireNonNull(src, "src");
|
Objects.requireNonNull(src, "src");
|
||||||
@@ -508,8 +553,10 @@ public class StringUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (endPos < beginPos) {
|
if (endPos < beginPos) {
|
||||||
throw new IllegalArgumentException("endPos must be greater than or equal to beginPos (endPos=" + endPos
|
throw new IllegalArgumentException(
|
||||||
+ ", beginPos=" + beginPos + ")");
|
"endPos must be greater than or equal to beginPos (endPos="
|
||||||
|
+ endPos + ", beginPos=" + beginPos + ")"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endPos >= Math.min(a.length, b.length)) {
|
if (endPos >= Math.min(a.length, b.length)) {
|
||||||
@@ -545,7 +592,8 @@ public class StringUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds and returns the index of the specified appearance of the specified
|
* Finds and returns the index of the specified appearance of the specified
|
||||||
* character in the given array. The search starts at index 0.
|
* character
|
||||||
|
* in the given array. The search starts at index 0.
|
||||||
* <p>
|
* <p>
|
||||||
* Examples:
|
* Examples:
|
||||||
* <p>
|
* <p>
|
||||||
@@ -582,12 +630,10 @@ public class StringUtil {
|
|||||||
* </tr>
|
* </tr>
|
||||||
* </table>
|
* </table>
|
||||||
*
|
*
|
||||||
* @param src
|
* @param src - the array to search in.
|
||||||
* - the array to search in.
|
* @param target - the character to search for.
|
||||||
* @param target
|
* @param skip - the amount of <code>target</code> characters to be
|
||||||
* - the character to search for.
|
* skipped.
|
||||||
* @param skip
|
|
||||||
* - the amount of <code>target</code> characters to be skipped.
|
|
||||||
* @return The index of the <code>skip+1</code>th <code>target</code>
|
* @return The index of the <code>skip+1</code>th <code>target</code>
|
||||||
* character or -1, if none found.
|
* character or -1, if none found.
|
||||||
* @see StringUtil#indexFromEnd(char[], char, int)
|
* @see StringUtil#indexFromEnd(char[], char, int)
|
||||||
@@ -607,7 +653,8 @@ public class StringUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds and returns the index of the specified appearance of the specified
|
* Finds and returns the index of the specified appearance of the specified
|
||||||
* character in the given array. The search starts at index
|
* character
|
||||||
|
* in the given array. The search starts at index
|
||||||
* <code>src.length - 1</code>.
|
* <code>src.length - 1</code>.
|
||||||
* <p>
|
* <p>
|
||||||
* Examples:
|
* Examples:
|
||||||
@@ -645,15 +692,13 @@ public class StringUtil {
|
|||||||
* </tr>
|
* </tr>
|
||||||
* </table>
|
* </table>
|
||||||
*
|
*
|
||||||
* @param src
|
* @param src - the array to search in.
|
||||||
* - the array to search in.
|
* @param target - the character to search for.
|
||||||
* @param target
|
* @param skip - the amount of <code>target</code> characters to be
|
||||||
* - the character to search for.
|
* skipped.
|
||||||
* @param skip
|
|
||||||
* - the amount of <code>target</code> characters to be skipped.
|
|
||||||
* @return The index of the <code>skip+1</code>th
|
* @return The index of the <code>skip+1</code>th
|
||||||
* <code>target</code>character from the end of the array or -1, if
|
* <code>target</code>character
|
||||||
* none found.
|
* from the end of the array or -1, if none found.
|
||||||
* @see StringUtil#indexFromBeginning(char[], char, int)
|
* @see StringUtil#indexFromBeginning(char[], char, int)
|
||||||
*/
|
*/
|
||||||
public static int indexFromEnd(char[] src, char target, int skip) {
|
public static int indexFromEnd(char[] src, char target, int skip) {
|
||||||
@@ -828,8 +873,12 @@ public class StringUtil {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void buildCombinations(StringBuilder sb, Collection<String> result, Iterable<String>[] parts,
|
private static void buildCombinations(
|
||||||
int index) {
|
StringBuilder sb,
|
||||||
|
Collection<String> result,
|
||||||
|
Iterable<String>[] parts,
|
||||||
|
int index
|
||||||
|
) {
|
||||||
if (index >= parts.length) {
|
if (index >= parts.length) {
|
||||||
result.add(sb.toString());
|
result.add(sb.toString());
|
||||||
} else {
|
} else {
|
||||||
@@ -855,8 +904,13 @@ public class StringUtil {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void buildCombinations(StringBuilder sb, String[] result, int[] resultIndex, String[][] parts,
|
private static void buildCombinations(
|
||||||
int index) {
|
StringBuilder sb,
|
||||||
|
String[] result,
|
||||||
|
int[] resultIndex,
|
||||||
|
String[][] parts,
|
||||||
|
int index
|
||||||
|
) {
|
||||||
if (index >= parts.length) {
|
if (index >= parts.length) {
|
||||||
result[resultIndex[0]++] = sb.toString();
|
result[resultIndex[0]++] = sb.toString();
|
||||||
} else {
|
} else {
|
||||||
@@ -931,7 +985,10 @@ public class StringUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static char hexDigit(long value, int digit) {
|
private static char hexDigit(long value, int digit) {
|
||||||
return hexDigit((int) (value >>> (4 * digit)) & 0xF);
|
return hexDigit(
|
||||||
|
(int) (value >>> (4 * digit))
|
||||||
|
& 0xF
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static char hexDigit(int value) {
|
public static char hexDigit(int value) {
|
||||||
|
@@ -27,9 +27,10 @@ public abstract class AbstractCharReader implements CharReader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Current position of this CharReader. The reader maps its input to
|
* Current position of this CharReader. The reader maps its input to
|
||||||
* positions starting from 0. Positions that are negative or lower than 0
|
* positions starting from 0.
|
||||||
* are invalid. {@link #current()} will throw an exception if position is
|
* Positions that are negative or lower than 0 are invalid.
|
||||||
* invalid.
|
* {@link #current()}
|
||||||
|
* will throw an exception if position is invalid.
|
||||||
*/
|
*/
|
||||||
protected int position = 0;
|
protected int position = 0;
|
||||||
|
|
||||||
|
@@ -51,12 +51,9 @@ public abstract class BufferedCharReader extends AbstractCharReader {
|
|||||||
/**
|
/**
|
||||||
* Acquires next characters and stores them in the array.
|
* Acquires next characters and stores them in the array.
|
||||||
*
|
*
|
||||||
* @param buffer
|
* @param buffer the output array
|
||||||
* the output array
|
* @param offset index of the first character
|
||||||
* @param offset
|
* @param length maximum amount of characters to be pulled
|
||||||
* index of the first character
|
|
||||||
* @param length
|
|
||||||
* maximum amount of characters to be pulled
|
|
||||||
* @return the amount of characters actually pulled
|
* @return the amount of characters actually pulled
|
||||||
*/
|
*/
|
||||||
protected int pullChars(char[] buffer, int offset, int length) {
|
protected int pullChars(char[] buffer, int offset, int length) {
|
||||||
|
@@ -179,7 +179,8 @@ public interface CharReader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Skips to the end of the current line. Both <code>"\n"</code>,
|
* Skips to the end of the current line. Both <code>"\n"</code>,
|
||||||
* <code>"\r"</code> and <code>"\r\n"</code> are considered line separators.
|
* <code>"\r"</code>
|
||||||
|
* and <code>"\r\n"</code> are considered line separators.
|
||||||
*
|
*
|
||||||
* @return the amount of characters in the skipped line
|
* @return the amount of characters in the skipped line
|
||||||
*/
|
*/
|
||||||
|
@@ -38,7 +38,8 @@ public class StringCharReader extends AbstractCharReader {
|
|||||||
int end = offset + length;
|
int end = offset + length;
|
||||||
if (end > str.length() || offset < 0)
|
if (end > str.length() || offset < 0)
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"String contains [0; " + str.length() + "), requested [" + offset + "; " + end + ")");
|
"String contains [0; " + str.length() + "), requested [" + offset + "; " + end + ")"
|
||||||
|
);
|
||||||
|
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
|
@@ -45,15 +45,8 @@ public interface ThrowingBiConsumer<T, U, E extends Exception> {
|
|||||||
|
|
||||||
public static <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> concat(
|
public static <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> concat(
|
||||||
ThrowingBiConsumer<? super T, ? super U, ? extends E> first,
|
ThrowingBiConsumer<? super T, ? super U, ? extends E> first,
|
||||||
ThrowingBiConsumer<? super T, ? super U, ? extends E> second) {
|
ThrowingBiConsumer<? super T, ? super U, ? extends E> second
|
||||||
return (t, u) -> {
|
) {
|
||||||
first.accept(t, u);
|
|
||||||
second.accept(t, u);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> concat(BiConsumer<? super T, ? super U> first,
|
|
||||||
ThrowingBiConsumer<? super T, ? super U, E> second) {
|
|
||||||
return (t, u) -> {
|
return (t, u) -> {
|
||||||
first.accept(t, u);
|
first.accept(t, u);
|
||||||
second.accept(t, u);
|
second.accept(t, u);
|
||||||
@@ -61,7 +54,19 @@ public interface ThrowingBiConsumer<T, U, E extends Exception> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> concat(
|
public static <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> concat(
|
||||||
ThrowingBiConsumer<? super T, ? super U, E> first, BiConsumer<? super T, ? super U> second) {
|
BiConsumer<? super T, ? super U> first,
|
||||||
|
ThrowingBiConsumer<? super T, ? super U, E> second
|
||||||
|
) {
|
||||||
|
return (t, u) -> {
|
||||||
|
first.accept(t, u);
|
||||||
|
second.accept(t, u);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> concat(
|
||||||
|
ThrowingBiConsumer<? super T, ? super U, E> first,
|
||||||
|
BiConsumer<? super T, ? super U> second
|
||||||
|
) {
|
||||||
return (t, u) -> {
|
return (t, u) -> {
|
||||||
first.accept(t, u);
|
first.accept(t, u);
|
||||||
second.accept(t, u);
|
second.accept(t, u);
|
||||||
|
@@ -39,24 +39,30 @@ public interface ThrowingConsumer<T, E extends Exception> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(ThrowingConsumer<? super T, ? extends E> first,
|
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(
|
||||||
ThrowingConsumer<? super T, ? extends E> second) {
|
ThrowingConsumer<? super T, ? extends E> first,
|
||||||
|
ThrowingConsumer<? super T, ? extends E> second
|
||||||
|
) {
|
||||||
return t -> {
|
return t -> {
|
||||||
first.accept(t);
|
first.accept(t);
|
||||||
second.accept(t);
|
second.accept(t);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(Consumer<? super T> first,
|
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(
|
||||||
ThrowingConsumer<? super T, ? extends E> second) {
|
Consumer<? super T> first,
|
||||||
|
ThrowingConsumer<? super T, ? extends E> second
|
||||||
|
) {
|
||||||
return t -> {
|
return t -> {
|
||||||
first.accept(t);
|
first.accept(t);
|
||||||
second.accept(t);
|
second.accept(t);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(ThrowingConsumer<? super T, ? extends E> first,
|
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(
|
||||||
Consumer<? super T> second) {
|
ThrowingConsumer<? super T, ? extends E> first,
|
||||||
|
Consumer<? super T> second
|
||||||
|
) {
|
||||||
return t -> {
|
return t -> {
|
||||||
first.accept(t);
|
first.accept(t);
|
||||||
second.accept(t);
|
second.accept(t);
|
||||||
|
@@ -28,8 +28,10 @@ public interface ThrowingFunction<T, R, E extends Exception> {
|
|||||||
R apply(T t) throws E;
|
R apply(T t) throws E;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
default Function<T, R> withHandler(BiConsumer<? super T, ? super E> handler,
|
default Function<T, R> withHandler(
|
||||||
Function<? super T, ? extends R> value) {
|
BiConsumer<? super T, ? super E> handler,
|
||||||
|
Function<? super T, ? extends R> value
|
||||||
|
) {
|
||||||
return t -> {
|
return t -> {
|
||||||
try {
|
try {
|
||||||
return apply(t);
|
return apply(t);
|
||||||
@@ -57,17 +59,22 @@ public interface ThrowingFunction<T, R, E extends Exception> {
|
|||||||
|
|
||||||
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(
|
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(
|
||||||
ThrowingFunction<? super T, I, ? extends E> first,
|
ThrowingFunction<? super T, I, ? extends E> first,
|
||||||
ThrowingFunction<? super I, ? extends R, ? extends E> second) {
|
ThrowingFunction<? super I, ? extends R, ? extends E> second
|
||||||
return t -> second.apply(first.apply(t));
|
) {
|
||||||
}
|
|
||||||
|
|
||||||
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(Function<? super T, I> first,
|
|
||||||
ThrowingFunction<? super I, ? extends R, E> second) {
|
|
||||||
return t -> second.apply(first.apply(t));
|
return t -> second.apply(first.apply(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(
|
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(
|
||||||
ThrowingFunction<? super T, I, E> first, Function<? super I, ? extends R> second) {
|
Function<? super T, I> first,
|
||||||
|
ThrowingFunction<? super I, ? extends R, E> second
|
||||||
|
) {
|
||||||
|
return t -> second.apply(first.apply(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(
|
||||||
|
ThrowingFunction<? super T, I, E> first,
|
||||||
|
Function<? super I, ? extends R> second
|
||||||
|
) {
|
||||||
return t -> second.apply(first.apply(t));
|
return t -> second.apply(first.apply(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -38,8 +38,10 @@ public interface ThrowingRunnable<E extends Exception> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends Exception> ThrowingRunnable<E> concat(ThrowingRunnable<? extends E> first,
|
public static <E extends Exception> ThrowingRunnable<E> concat(
|
||||||
ThrowingRunnable<? extends E> second) {
|
ThrowingRunnable<? extends E> first,
|
||||||
|
ThrowingRunnable<? extends E> second
|
||||||
|
) {
|
||||||
return () -> {
|
return () -> {
|
||||||
first.run();
|
first.run();
|
||||||
second.run();
|
second.run();
|
||||||
|
@@ -49,8 +49,10 @@ public class RangeIterator<E> implements Iterator<E> {
|
|||||||
public E next() {
|
public E next() {
|
||||||
update();
|
update();
|
||||||
if (nextIndex >= from + amount) {
|
if (nextIndex >= from + amount) {
|
||||||
throw new NoSuchElementException("RangeIterator about to retrieve element " + nextIndex
|
throw new NoSuchElementException(
|
||||||
+ " which exceeds upper boundary " + (from + amount));
|
"RangeIterator about to retrieve element " + nextIndex
|
||||||
|
+ " which exceeds upper boundary " + (from + amount)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
E result = parent.next();
|
E result = parent.next();
|
||||||
|
@@ -18,6 +18,177 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia;
|
package ru.windcorp.progressia;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.jar.Attributes;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class providing access to build metadata.
|
||||||
|
*/
|
||||||
public class Progressia {
|
public class Progressia {
|
||||||
|
|
||||||
|
private static final String NAME = "Progressia";
|
||||||
|
private static String version;
|
||||||
|
private static String gitCommit;
|
||||||
|
private static String gitBranch;
|
||||||
|
private static String buildId;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
Manifest manifest = findManifest();
|
||||||
|
|
||||||
|
if (manifest == null) {
|
||||||
|
setDevelopmentMetadata();
|
||||||
|
LogManager.getLogger().info(
|
||||||
|
"Manifest with Specification-Title not found. "
|
||||||
|
+ "Either you are in a development environment or something has gone horribly wrong with classloaders."
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
fillMetadata(manifest);
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
CrashReports.crash(t, "Something went wrong while loading metadata");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Manifest findManifest() {
|
||||||
|
try {
|
||||||
|
Enumeration<URL> resources = Progressia.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
|
||||||
|
Collection<IOException> exceptions = new ArrayList<>();
|
||||||
|
|
||||||
|
while (resources.hasMoreElements()) {
|
||||||
|
URL url = resources.nextElement();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
Manifest manifest = new Manifest(url.openStream());
|
||||||
|
Attributes mainAttributes = manifest.getMainAttributes();
|
||||||
|
if (NAME.equals(mainAttributes.getValue("Specification-Title"))) {
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
exceptions.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exceptions.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
IOException scapegoat = null;
|
||||||
|
for (IOException e : exceptions) {
|
||||||
|
if (scapegoat == null) {
|
||||||
|
scapegoat = e;
|
||||||
|
} else {
|
||||||
|
scapegoat.addSuppressed(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw CrashReports.report(scapegoat, "Could not read manifest");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw CrashReports.report(e, "Could not read manifest");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setDevelopmentMetadata() {
|
||||||
|
version = "dev";
|
||||||
|
gitCommit = "-";
|
||||||
|
gitBranch = "-";
|
||||||
|
buildId = "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fillMetadata(Manifest manifest) {
|
||||||
|
version = getAttributeOrCrash(manifest, "Implementation-Version");
|
||||||
|
gitCommit = getAttributeOrCrash(manifest, "Implementation-Version-Git-Commit");
|
||||||
|
gitBranch = getAttributeOrCrash(manifest, "Implementation-Version-Git-Branch");
|
||||||
|
buildId = getAttributeOrCrash(manifest, "Implementation-Version-BuildId");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getAttributeOrCrash(Manifest manifest, String key) {
|
||||||
|
String result = manifest.getMainAttributes().getValue(key);
|
||||||
|
if (result == null) {
|
||||||
|
throw CrashReports.report(null, "Manifest exists but attribute " + key + " not found");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the version of the game as a String. Version data is retrieved
|
||||||
|
* from a {@code META-INF/MANIFEST.MF} file located in the main JAR. Version
|
||||||
|
* format depends on way the game was built:
|
||||||
|
* <ul>
|
||||||
|
* <li><code>dev</code> if no matching manifest was found, e.g. when launching from an IDE</li>
|
||||||
|
* <li>The value of <code>Implementation-Version</code> specified in the manifest:
|
||||||
|
* <ul>
|
||||||
|
* <li>[Stage-]Major.Minor.Patch, e.g. <code>alpha-0.3.2</code> or <code>1.4.2</code>, for released versions</li>
|
||||||
|
* <li>BuildId, e.g. <code>WJ7</code>, for snapshots built by automation systems</li>
|
||||||
|
* <li>YYYY-MM-DD, e.g. <code>2021-12-32</code>, for snapshots built manually</li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @return the version
|
||||||
|
*/
|
||||||
|
public static String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFullerVersion() {
|
||||||
|
if (isDefaultGitBranch() || "-".equals(gitBranch)) {
|
||||||
|
return version;
|
||||||
|
} else {
|
||||||
|
return String.format("%s/%s", version, gitBranch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the buildId or <code>"-"</code>
|
||||||
|
*/
|
||||||
|
public static String getBuildId() {
|
||||||
|
return buildId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the Git commit or <code>"-"</code>
|
||||||
|
*/
|
||||||
|
public static String getGitCommit() {
|
||||||
|
return gitCommit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getGitCommitShort() {
|
||||||
|
if (gitCommit == null || "-".equals(gitCommit)) {
|
||||||
|
return gitCommit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gitCommit.substring(0, Math.min(7, gitCommit.length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the Git branch or <code>"-"</code>
|
||||||
|
*/
|
||||||
|
public static String getGitBranch() {
|
||||||
|
return gitBranch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDefaultGitBranch() {
|
||||||
|
return "master".equals(gitBranch) || "main".equals(gitBranch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFullVersion() {
|
||||||
|
return String.format("%s/%s/%s/%s", version, gitBranch, getGitCommitShort(), buildId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -18,22 +18,37 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia;
|
package ru.windcorp.progressia;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.GUI;
|
||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer;
|
import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer;
|
||||||
import ru.windcorp.progressia.common.util.crash.providers.*;
|
import ru.windcorp.progressia.common.util.crash.providers.*;
|
||||||
|
import ru.windcorp.progressia.test.LayerTitle;
|
||||||
|
|
||||||
public class ProgressiaLauncher {
|
public class ProgressiaLauncher {
|
||||||
|
|
||||||
public static String[] arguments;
|
public static String[] arguments;
|
||||||
|
private static Proxy proxy;
|
||||||
|
|
||||||
public static void launch(String[] args, Proxy proxy) {
|
public static void launch(String[] args, Proxy proxy) {
|
||||||
arguments = args.clone();
|
arguments = args.clone();
|
||||||
setupCrashReports();
|
setupCrashReports();
|
||||||
|
|
||||||
|
LogManager.getRootLogger().info("Launching " + Progressia.getName() + " version " + Progressia.getFullVersion());
|
||||||
|
|
||||||
proxy.initialize();
|
proxy.initialize();
|
||||||
|
ProgressiaLauncher.proxy = proxy;
|
||||||
|
GUI.addTopLayer(new LayerTitle("Title"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Proxy getProxy() {
|
||||||
|
return proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setupCrashReports() {
|
private static void setupCrashReports() {
|
||||||
// Context providers
|
// Context providers
|
||||||
|
CrashReports.registerProvider(new VersionProvider());
|
||||||
CrashReports.registerProvider(new OSContextProvider());
|
CrashReports.registerProvider(new OSContextProvider());
|
||||||
CrashReports.registerProvider(new RAMContextProvider());
|
CrashReports.registerProvider(new RAMContextProvider());
|
||||||
CrashReports.registerProvider(new JavaVersionContextProvider());
|
CrashReports.registerProvider(new JavaVersionContextProvider());
|
||||||
|
@@ -69,7 +69,11 @@ public class Client {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCamera().setAnchor(new EntityAnchor(getWorld().getEntityRenderable(entity)));
|
getCamera().setAnchor(
|
||||||
|
new EntityAnchor(
|
||||||
|
getWorld().getEntityRenderable(entity)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,6 @@ import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
|||||||
import ru.windcorp.progressia.client.localization.Localizer;
|
import ru.windcorp.progressia.client.localization.Localizer;
|
||||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
import ru.windcorp.progressia.server.ServerState;
|
|
||||||
import ru.windcorp.progressia.test.TestContent;
|
import ru.windcorp.progressia.test.TestContent;
|
||||||
import ru.windcorp.progressia.test.TestMusicPlayer;
|
import ru.windcorp.progressia.test.TestMusicPlayer;
|
||||||
|
|
||||||
@@ -38,12 +37,16 @@ public class ClientProxy implements Proxy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
|
||||||
GraphicsBackend.initialize();
|
GraphicsBackend.initialize();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init);
|
RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init);
|
||||||
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
||||||
RenderTaskQueue.waitAndInvoke(() -> Typefaces
|
RenderTaskQueue.waitAndInvoke(
|
||||||
.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz"))));
|
() -> Typefaces
|
||||||
|
.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz")))
|
||||||
|
);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw CrashReports.report(e, "ClientProxy failed");
|
throw CrashReports.report(e, "ClientProxy failed");
|
||||||
}
|
}
|
||||||
@@ -56,10 +59,6 @@ public class ClientProxy implements Proxy {
|
|||||||
|
|
||||||
AudioSystem.initialize();
|
AudioSystem.initialize();
|
||||||
|
|
||||||
ServerState.startServer();
|
|
||||||
ClientState.connectToLocalServer();
|
|
||||||
|
|
||||||
TestMusicPlayer.start();
|
TestMusicPlayer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -20,10 +20,13 @@ package ru.windcorp.progressia.client;
|
|||||||
|
|
||||||
import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel;
|
import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel;
|
||||||
import ru.windcorp.progressia.client.graphics.GUI;
|
import ru.windcorp.progressia.client.graphics.GUI;
|
||||||
|
import ru.windcorp.progressia.client.graphics.Layer;
|
||||||
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
|
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
|
||||||
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
||||||
|
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
|
||||||
import ru.windcorp.progressia.server.ServerState;
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
import ru.windcorp.progressia.test.LayerAbout;
|
import ru.windcorp.progressia.test.LayerAbout;
|
||||||
|
import ru.windcorp.progressia.test.LayerTestText;
|
||||||
import ru.windcorp.progressia.test.LayerTestUI;
|
import ru.windcorp.progressia.test.LayerTestUI;
|
||||||
import ru.windcorp.progressia.test.TestContent;
|
import ru.windcorp.progressia.test.TestContent;
|
||||||
|
|
||||||
@@ -43,18 +46,48 @@ public class ClientState {
|
|||||||
|
|
||||||
DefaultWorldData world = new DefaultWorldData();
|
DefaultWorldData world = new DefaultWorldData();
|
||||||
|
|
||||||
LocalServerCommsChannel channel = new LocalServerCommsChannel(ServerState.getInstance());
|
LocalServerCommsChannel channel = new LocalServerCommsChannel(
|
||||||
|
ServerState.getInstance()
|
||||||
|
);
|
||||||
|
|
||||||
Client client = new Client(world, channel);
|
Client client = new Client(world, channel);
|
||||||
|
|
||||||
channel.connect(TestContent.PLAYER_LOGIN);
|
channel.connect(TestContent.PLAYER_LOGIN);
|
||||||
|
|
||||||
setInstance(client);
|
setInstance(client);
|
||||||
|
displayLoadingScreen();
|
||||||
|
|
||||||
GUI.addBottomLayer(new LayerWorld(client));
|
}
|
||||||
GUI.addTopLayer(new LayerTestUI());
|
|
||||||
GUI.addTopLayer(new LayerAbout());
|
|
||||||
|
|
||||||
|
private static void displayLoadingScreen() {
|
||||||
|
GUI.addTopLayer(new LayerTestText("Text", new MutableStringLocalized("LayerText.Load"), layer -> {
|
||||||
|
Client client = ClientState.getInstance();
|
||||||
|
|
||||||
|
// TODO refacetor and remove
|
||||||
|
if (client != null) {
|
||||||
|
client.getComms().processPackets();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client != null && client.getLocalPlayer().hasEntity()) {
|
||||||
|
GUI.removeLayer(layer);
|
||||||
|
|
||||||
|
// TODO refactor, this shouldn't be here
|
||||||
|
LayerWorld layerWorld = new LayerWorld(client);
|
||||||
|
LayerTestUI layerUI = new LayerTestUI();
|
||||||
|
LayerAbout layerAbout = new LayerAbout();
|
||||||
|
GUI.addBottomLayer(layerWorld);
|
||||||
|
GUI.addTopLayer(layerUI);
|
||||||
|
GUI.addTopLayer(layerAbout);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void disconnectFromLocalServer() {
|
||||||
|
getInstance().getComms().disconnect();
|
||||||
|
|
||||||
|
for (Layer layer : GUI.getLayers()) {
|
||||||
|
GUI.removeLayer(layer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClientState() {
|
private ClientState() {
|
||||||
|
@@ -43,7 +43,10 @@ public class AudioManager {
|
|||||||
private static Speaker musicSpeaker;
|
private static Speaker musicSpeaker;
|
||||||
|
|
||||||
public static void initAL() {
|
public static void initAL() {
|
||||||
String defaultDeviceName = alcGetString(0, ALC_DEFAULT_DEVICE_SPECIFIER);
|
String defaultDeviceName = alcGetString(
|
||||||
|
0,
|
||||||
|
ALC_DEFAULT_DEVICE_SPECIFIER
|
||||||
|
);
|
||||||
|
|
||||||
device = alcOpenDevice(defaultDeviceName);
|
device = alcOpenDevice(defaultDeviceName);
|
||||||
|
|
||||||
@@ -72,7 +75,10 @@ public class AudioManager {
|
|||||||
lastSoundIndex = 0;
|
lastSoundIndex = 0;
|
||||||
}
|
}
|
||||||
speaker = soundSpeakers.get(lastSoundIndex);
|
speaker = soundSpeakers.get(lastSoundIndex);
|
||||||
} while (speaker.getState().equals(Speaker.State.PLAYING_LOOP));
|
} while (
|
||||||
|
speaker.getState()
|
||||||
|
.equals(Speaker.State.PLAYING_LOOP)
|
||||||
|
);
|
||||||
return speaker;
|
return speaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,7 +29,10 @@ public class AudioSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void loadAudioData() {
|
static void loadAudioData() {
|
||||||
AudioManager.loadSound(ResourceManager.getResource("assets/sounds/block_destroy_clap.ogg"),
|
AudioManager.loadSound(
|
||||||
"Progressia:BlockDestroy", AudioFormat.MONO);
|
ResourceManager.getResource("assets/sounds/block_destroy_clap.ogg"),
|
||||||
|
"Progressia:BlockDestroy",
|
||||||
|
AudioFormat.MONO
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,10 @@ import glm.vec._3.Vec3;
|
|||||||
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
||||||
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
||||||
|
|
||||||
public class Music extends Sound {
|
public class Music
|
||||||
|
extends Sound {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Music(SoundType soundType, int timeLength, float pitch, float gain) {
|
public Music(SoundType soundType, int timeLength, float pitch, float gain) {
|
||||||
super(soundType, timeLength, new Vec3(), new Vec3(), pitch, gain);
|
super(soundType, timeLength, new Vec3(), new Vec3(), pitch, gain);
|
||||||
|
@@ -40,7 +40,14 @@ public class Sound {
|
|||||||
this(AudioRegistry.getInstance().get(id));
|
this(AudioRegistry.getInstance().get(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sound(String id, int timeLength, Vec3 position, Vec3 velocity, float pitch, float gain) {
|
public Sound(
|
||||||
|
String id,
|
||||||
|
int timeLength,
|
||||||
|
Vec3 position,
|
||||||
|
Vec3 velocity,
|
||||||
|
float pitch,
|
||||||
|
float gain
|
||||||
|
) {
|
||||||
this(id);
|
this(id);
|
||||||
this.position = position;
|
this.position = position;
|
||||||
this.velocity = velocity;
|
this.velocity = velocity;
|
||||||
@@ -48,7 +55,14 @@ public class Sound {
|
|||||||
this.gain = gain;
|
this.gain = gain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sound(SoundType soundType, int timeLength, Vec3 position, Vec3 velocity, float pitch, float gain) {
|
public Sound(
|
||||||
|
SoundType soundType,
|
||||||
|
int timeLength,
|
||||||
|
Vec3 position,
|
||||||
|
Vec3 velocity,
|
||||||
|
float pitch,
|
||||||
|
float gain
|
||||||
|
) {
|
||||||
this(soundType);
|
this(soundType);
|
||||||
this.position = position;
|
this.position = position;
|
||||||
this.velocity = velocity;
|
this.velocity = velocity;
|
||||||
|
@@ -39,7 +39,12 @@ public class AudioReader {
|
|||||||
|
|
||||||
ShortBuffer rawAudio = decodeVorbis(resource, channelBuffer, rateBuffer);
|
ShortBuffer rawAudio = decodeVorbis(resource, channelBuffer, rateBuffer);
|
||||||
|
|
||||||
return new SoundType(id, rawAudio, format, rateBuffer.get(0));
|
return new SoundType(
|
||||||
|
id,
|
||||||
|
rawAudio,
|
||||||
|
format,
|
||||||
|
rateBuffer.get(0)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SoundType readAsMono(Resource resource, String id) {
|
public static SoundType readAsMono(Resource resource, String id) {
|
||||||
@@ -50,7 +55,15 @@ public class AudioReader {
|
|||||||
return readAsSpecified(resource, id, AL_FORMAT_STEREO16);
|
return readAsSpecified(resource, id, AL_FORMAT_STEREO16);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ShortBuffer decodeVorbis(Resource dataToDecode, IntBuffer channelsBuffer, IntBuffer rateBuffer) {
|
private static ShortBuffer decodeVorbis(
|
||||||
return stb_vorbis_decode_memory(dataToDecode.readAsBytes(), channelsBuffer, rateBuffer);
|
Resource dataToDecode,
|
||||||
|
IntBuffer channelsBuffer,
|
||||||
|
IntBuffer rateBuffer
|
||||||
|
) {
|
||||||
|
return stb_vorbis_decode_memory(
|
||||||
|
dataToDecode.readAsBytes(),
|
||||||
|
channelsBuffer,
|
||||||
|
rateBuffer
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -55,8 +55,9 @@ public class Listener {
|
|||||||
if (isInWorld) {
|
if (isInWorld) {
|
||||||
|
|
||||||
if (wasInWorld) {
|
if (wasInWorld) {
|
||||||
velocity.set(camera.getLastAnchorPosition()).sub(position)
|
velocity.set(camera.getLastAnchorPosition()).sub(position).div(
|
||||||
.div((float) GraphicsInterface.getFrameLength());
|
(float) GraphicsInterface.getFrameLength()
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// If !wasInWorld, previous position is nonsence. Assume 0.
|
// If !wasInWorld, previous position is nonsence. Assume 0.
|
||||||
velocity.set(0);
|
velocity.set(0);
|
||||||
@@ -71,9 +72,9 @@ public class Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only apply if there is a chance that params changed. This can only
|
* Only apply if there is a chance that params changed.
|
||||||
* happen if we are in world now (isInWorld) or we just left world
|
* This can only happen if we are in world now (isInWorld) or we just
|
||||||
* (wasInWorld, then we need to reset).
|
* left world (wasInWorld, then we need to reset).
|
||||||
*/
|
*/
|
||||||
if (isInWorld || wasInWorld) {
|
if (isInWorld || wasInWorld) {
|
||||||
applyParams();
|
applyParams();
|
||||||
@@ -90,7 +91,17 @@ public class Listener {
|
|||||||
private void applyParams() {
|
private void applyParams() {
|
||||||
alListener3f(AL_POSITION, position.x, position.y, position.z);
|
alListener3f(AL_POSITION, position.x, position.y, position.z);
|
||||||
alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z);
|
alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z);
|
||||||
alListenerfv(AL_ORIENTATION, new float[] { oriAt.x, oriAt.y, oriAt.z, oriUp.x, oriUp.y, oriUp.z });
|
alListenerfv(
|
||||||
|
AL_ORIENTATION,
|
||||||
|
new float[] {
|
||||||
|
oriAt.x,
|
||||||
|
oriAt.y,
|
||||||
|
oriAt.z,
|
||||||
|
oriUp.x,
|
||||||
|
oriUp.y,
|
||||||
|
oriUp.z
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,12 @@ public class SoundType extends Namespaced {
|
|||||||
private int audioBuffer;
|
private int audioBuffer;
|
||||||
private double duration;
|
private double duration;
|
||||||
|
|
||||||
public SoundType(String id, ShortBuffer rawAudio, int format, int sampleRate) {
|
public SoundType(
|
||||||
|
String id,
|
||||||
|
ShortBuffer rawAudio,
|
||||||
|
int format,
|
||||||
|
int sampleRate
|
||||||
|
) {
|
||||||
super(id);
|
super(id);
|
||||||
this.rawAudio = rawAudio;
|
this.rawAudio = rawAudio;
|
||||||
this.sampleRate = sampleRate;
|
this.sampleRate = sampleRate;
|
||||||
|
@@ -24,7 +24,9 @@ import static org.lwjgl.openal.AL11.*;
|
|||||||
public class Speaker {
|
public class Speaker {
|
||||||
|
|
||||||
public enum State {
|
public enum State {
|
||||||
NOT_PLAYING, PLAYING, PLAYING_LOOP
|
NOT_PLAYING,
|
||||||
|
PLAYING,
|
||||||
|
PLAYING_LOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buffers
|
// Buffers
|
||||||
@@ -47,7 +49,13 @@ public class Speaker {
|
|||||||
setAudioData(audioData);
|
setAudioData(audioData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Speaker(int audioData, Vec3 position, Vec3 velocity, float pitch, float gain) {
|
public Speaker(
|
||||||
|
int audioData,
|
||||||
|
Vec3 position,
|
||||||
|
Vec3 velocity,
|
||||||
|
float pitch,
|
||||||
|
float gain
|
||||||
|
) {
|
||||||
setAudioData(audioData);
|
setAudioData(audioData);
|
||||||
setPosition(position);
|
setPosition(position);
|
||||||
setVelocity(velocity);
|
setVelocity(velocity);
|
||||||
@@ -55,7 +63,12 @@ public class Speaker {
|
|||||||
setGain(gain);
|
setGain(gain);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Speaker(Vec3 position, Vec3 velocity, float pitch, float gain) {
|
public Speaker(
|
||||||
|
Vec3 position,
|
||||||
|
Vec3 velocity,
|
||||||
|
float pitch,
|
||||||
|
float gain
|
||||||
|
) {
|
||||||
setPosition(position);
|
setPosition(position);
|
||||||
setVelocity(velocity);
|
setVelocity(velocity);
|
||||||
setPitch(pitch);
|
setPitch(pitch);
|
||||||
|
@@ -39,7 +39,9 @@ public class DefaultClientCommsListener implements CommsListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onPacketReceived(Packet packet) {
|
public void onPacketReceived(Packet packet) {
|
||||||
if (packet instanceof PacketAffectWorld) {
|
if (packet instanceof PacketAffectWorld) {
|
||||||
((PacketAffectWorld) packet).apply(getClient().getWorld().getData());
|
((PacketAffectWorld) packet).apply(
|
||||||
|
getClient().getWorld().getData()
|
||||||
|
);
|
||||||
} else if (packet instanceof PacketSetLocalPlayer) {
|
} else if (packet instanceof PacketSetLocalPlayer) {
|
||||||
setLocalPlayer((PacketSetLocalPlayer) packet);
|
setLocalPlayer((PacketSetLocalPlayer) packet);
|
||||||
}
|
}
|
||||||
|
@@ -33,12 +33,17 @@ public class ControlTriggerLambda extends ControlTriggerInputBased {
|
|||||||
private final Predicate<InputEvent> predicate;
|
private final Predicate<InputEvent> predicate;
|
||||||
private final BiConsumer<InputEvent, ControlData> dataWriter;
|
private final BiConsumer<InputEvent, ControlData> dataWriter;
|
||||||
|
|
||||||
public ControlTriggerLambda(String id, Predicate<InputEvent> predicate,
|
public ControlTriggerLambda(
|
||||||
BiConsumer<InputEvent, ControlData> dataWriter) {
|
String id,
|
||||||
|
Predicate<InputEvent> predicate,
|
||||||
|
BiConsumer<InputEvent, ControlData> dataWriter
|
||||||
|
) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|
||||||
this.packetId = NamespacedUtil.getId(NamespacedUtil.getNamespace(id),
|
this.packetId = NamespacedUtil.getId(
|
||||||
"ControlKeyPress" + NamespacedUtil.getName(id));
|
NamespacedUtil.getNamespace(id),
|
||||||
|
"ControlKeyPress" + NamespacedUtil.getName(id)
|
||||||
|
);
|
||||||
|
|
||||||
this.predicate = predicate;
|
this.predicate = predicate;
|
||||||
this.dataWriter = dataWriter;
|
this.dataWriter = dataWriter;
|
||||||
@@ -49,7 +54,10 @@ public class ControlTriggerLambda extends ControlTriggerInputBased {
|
|||||||
if (!predicate.test(event))
|
if (!predicate.test(event))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
PacketControl packet = new PacketControl(packetId, ControlDataRegistry.getInstance().create(getId()));
|
PacketControl packet = new PacketControl(
|
||||||
|
packetId,
|
||||||
|
ControlDataRegistry.getInstance().create(getId())
|
||||||
|
);
|
||||||
|
|
||||||
dataWriter.accept(event, packet.getControl());
|
dataWriter.accept(event, packet.getControl());
|
||||||
|
|
||||||
|
@@ -29,7 +29,11 @@ public class ControlTriggerLocalLambda extends ControlTriggerInputBased {
|
|||||||
private final Predicate<InputEvent> predicate;
|
private final Predicate<InputEvent> predicate;
|
||||||
private final Consumer<InputEvent> action;
|
private final Consumer<InputEvent> action;
|
||||||
|
|
||||||
public ControlTriggerLocalLambda(String id, Predicate<InputEvent> predicate, Consumer<InputEvent> action) {
|
public ControlTriggerLocalLambda(
|
||||||
|
String id,
|
||||||
|
Predicate<InputEvent> predicate,
|
||||||
|
Consumer<InputEvent> action
|
||||||
|
) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|
||||||
this.predicate = predicate;
|
this.predicate = predicate;
|
||||||
|
@@ -27,119 +27,216 @@ import ru.windcorp.progressia.common.comms.controls.ControlData;
|
|||||||
|
|
||||||
public class ControlTriggers {
|
public class ControlTriggers {
|
||||||
|
|
||||||
public static ControlTriggerInputBased of(String id, BiConsumer<InputEvent, ControlData> dataWriter,
|
public static ControlTriggerInputBased of(
|
||||||
Predicate<InputEvent> predicate) {
|
String id,
|
||||||
|
BiConsumer<InputEvent, ControlData> dataWriter,
|
||||||
|
Predicate<InputEvent> predicate
|
||||||
|
) {
|
||||||
return new ControlTriggerLambda(id, predicate, dataWriter);
|
return new ControlTriggerLambda(id, predicate, dataWriter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlTriggerInputBased of(String id, Consumer<ControlData> dataWriter,
|
public static ControlTriggerInputBased of(
|
||||||
Predicate<InputEvent> predicate) {
|
String id,
|
||||||
return of(id, (input, control) -> dataWriter.accept(control), predicate);
|
Consumer<ControlData> dataWriter,
|
||||||
|
Predicate<InputEvent> predicate
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
(input, control) -> dataWriter.accept(control),
|
||||||
|
predicate
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlTriggerInputBased of(String id, Predicate<InputEvent> predicate) {
|
public static ControlTriggerInputBased of(
|
||||||
return of(id, (input, control) -> {
|
String id,
|
||||||
}, predicate);
|
Predicate<InputEvent> predicate
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
(input, control) -> {
|
||||||
|
},
|
||||||
|
predicate
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static <I extends InputEvent> ControlTriggerInputBased of(String id, Class<I> inputType,
|
public static <I extends InputEvent> ControlTriggerInputBased of(
|
||||||
BiConsumer<I, ControlData> dataWriter, Predicate<I>... predicates) {
|
String id,
|
||||||
return of(id, createCheckedDataWriter(inputType, dataWriter),
|
Class<I> inputType,
|
||||||
createCheckedCompoundPredicate(inputType, predicates));
|
BiConsumer<I, ControlData> dataWriter,
|
||||||
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
createCheckedDataWriter(inputType, dataWriter),
|
||||||
|
createCheckedCompoundPredicate(inputType, predicates)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static <I extends InputEvent> ControlTriggerInputBased of(String id, Class<I> inputType,
|
public static <I extends InputEvent> ControlTriggerInputBased of(
|
||||||
Consumer<ControlData> dataWriter, Predicate<I>... predicates) {
|
String id,
|
||||||
return of(id, inputType, (input, control) -> dataWriter.accept(control), predicates);
|
Class<I> inputType,
|
||||||
|
Consumer<ControlData> dataWriter,
|
||||||
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
inputType,
|
||||||
|
(input, control) -> dataWriter.accept(control),
|
||||||
|
predicates
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static <I extends InputEvent> ControlTriggerInputBased of(String id, Class<I> inputType,
|
public static <I extends InputEvent> ControlTriggerInputBased of(
|
||||||
Predicate<I>... predicates) {
|
String id,
|
||||||
return of(id, (input, control) -> {
|
Class<I> inputType,
|
||||||
}, createCheckedCompoundPredicate(inputType, predicates));
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
(input, control) -> {
|
||||||
|
},
|
||||||
|
createCheckedCompoundPredicate(inputType, predicates)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static ControlTriggerInputBased of(String id, BiConsumer<InputEvent, ControlData> dataWriter,
|
public static ControlTriggerInputBased of(
|
||||||
Predicate<InputEvent>... predicates) {
|
String id,
|
||||||
return of(id, InputEvent.class, dataWriter, predicates);
|
BiConsumer<InputEvent, ControlData> dataWriter,
|
||||||
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
InputEvent.class,
|
||||||
|
dataWriter,
|
||||||
|
predicates
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static <I extends InputEvent> ControlTriggerInputBased of(String id, Consumer<ControlData> dataWriter,
|
public static <I extends InputEvent> ControlTriggerInputBased of(
|
||||||
Predicate<InputEvent>... predicates) {
|
String id,
|
||||||
return of(id, (input, control) -> dataWriter.accept(control), predicates);
|
Consumer<ControlData> dataWriter,
|
||||||
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
(input, control) -> dataWriter.accept(control),
|
||||||
|
predicates
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static ControlTriggerInputBased of(String id, Predicate<InputEvent>... predicates) {
|
public static ControlTriggerInputBased of(
|
||||||
return of(id, InputEvent.class, (input, control) -> {
|
String id,
|
||||||
}, predicates);
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
InputEvent.class,
|
||||||
|
(input, control) -> {
|
||||||
|
},
|
||||||
|
predicates
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
public static ControlTriggerInputBased localOf(
|
||||||
//
|
String id,
|
||||||
///
|
Consumer<InputEvent> action,
|
||||||
///
|
Predicate<InputEvent> predicate
|
||||||
//
|
) {
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
public static ControlTriggerInputBased localOf(String id, Consumer<InputEvent> action,
|
|
||||||
Predicate<InputEvent> predicate) {
|
|
||||||
return new ControlTriggerLocalLambda(id, predicate, action);
|
return new ControlTriggerLocalLambda(id, predicate, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlTriggerInputBased localOf(String id, Runnable action, Predicate<InputEvent> predicate) {
|
public static ControlTriggerInputBased localOf(
|
||||||
return localOf(id, input -> action.run(), predicate);
|
String id,
|
||||||
|
Runnable action,
|
||||||
|
Predicate<InputEvent> predicate
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
input -> action.run(),
|
||||||
|
predicate
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static <I extends InputEvent> ControlTriggerInputBased localOf(String id, Class<I> inputType,
|
public static <I extends InputEvent> ControlTriggerInputBased localOf(
|
||||||
Consumer<I> action, Predicate<I>... predicates) {
|
String id,
|
||||||
return localOf(id, createCheckedAction(inputType, action),
|
Class<I> inputType,
|
||||||
createCheckedCompoundPredicate(inputType, predicates));
|
Consumer<I> action,
|
||||||
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
createCheckedAction(inputType, action),
|
||||||
|
createCheckedCompoundPredicate(inputType, predicates)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static <I extends InputEvent> ControlTriggerInputBased localOf(String id, Class<I> inputType,
|
public static <I extends InputEvent> ControlTriggerInputBased localOf(
|
||||||
Runnable action, Predicate<I>... predicates) {
|
String id,
|
||||||
return localOf(id, inputType, input -> action.run(), predicates);
|
Class<I> inputType,
|
||||||
|
Runnable action,
|
||||||
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
inputType,
|
||||||
|
input -> action.run(),
|
||||||
|
predicates
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static ControlTriggerInputBased localOf(String id, Consumer<InputEvent> action,
|
public static ControlTriggerInputBased localOf(
|
||||||
Predicate<InputEvent>... predicates) {
|
String id,
|
||||||
return localOf(id, InputEvent.class, action, predicates);
|
Consumer<InputEvent> action,
|
||||||
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
InputEvent.class,
|
||||||
|
action,
|
||||||
|
predicates
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static <I extends InputEvent> ControlTriggerInputBased localOf(String id, Runnable action,
|
public static <I extends InputEvent> ControlTriggerInputBased localOf(
|
||||||
Predicate<InputEvent>... predicates) {
|
String id,
|
||||||
return of(id, input -> action.run(), predicates);
|
Runnable action,
|
||||||
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
input -> action.run(),
|
||||||
|
predicates
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <I extends InputEvent> BiConsumer<InputEvent, ControlData> createCheckedDataWriter(
|
private static <I extends InputEvent> BiConsumer<InputEvent, ControlData> createCheckedDataWriter(
|
||||||
Class<I> inputType, BiConsumer<I, ControlData> dataWriter) {
|
Class<I> inputType,
|
||||||
|
BiConsumer<I, ControlData> dataWriter
|
||||||
|
) {
|
||||||
return (inputEvent, control) -> dataWriter.accept(inputType.cast(inputEvent), control);
|
return (inputEvent, control) -> dataWriter.accept(inputType.cast(inputEvent), control);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <I extends InputEvent> Consumer<InputEvent> createCheckedAction(Class<I> inputType,
|
private static <I extends InputEvent> Consumer<InputEvent> createCheckedAction(
|
||||||
Consumer<I> action) {
|
Class<I> inputType,
|
||||||
|
Consumer<I> action
|
||||||
|
) {
|
||||||
return inputEvent -> action.accept(inputType.cast(inputEvent));
|
return inputEvent -> action.accept(inputType.cast(inputEvent));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <I extends InputEvent> Predicate<InputEvent> createCheckedCompoundPredicate(Class<I> inputType,
|
private static <I extends InputEvent> Predicate<InputEvent> createCheckedCompoundPredicate(
|
||||||
Predicate<I>[] predicates) {
|
Class<I> inputType,
|
||||||
|
Predicate<I>[] predicates
|
||||||
|
) {
|
||||||
return new CompoundCastPredicate<>(inputType, predicates);
|
return new CompoundCastPredicate<>(inputType, predicates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
package ru.windcorp.progressia.client.comms.controls;
|
package ru.windcorp.progressia.client.comms.controls;
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.Client;
|
import ru.windcorp.progressia.client.Client;
|
||||||
import ru.windcorp.progressia.client.graphics.input.bus.Input;
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||||
|
|
||||||
public class InputBasedControls {
|
public class InputBasedControls {
|
||||||
@@ -32,15 +32,16 @@ public class InputBasedControls {
|
|||||||
this.client = client;
|
this.client = client;
|
||||||
|
|
||||||
this.controls = ControlTriggerRegistry.getInstance().values().stream()
|
this.controls = ControlTriggerRegistry.getInstance().values().stream()
|
||||||
.filter(ControlTriggerInputBased.class::isInstance).toArray(ControlTriggerInputBased[]::new);
|
.filter(ControlTriggerInputBased.class::isInstance)
|
||||||
|
.toArray(ControlTriggerInputBased[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleInput(Input input) {
|
public void handleInput(InputEvent event) {
|
||||||
for (ControlTriggerInputBased c : controls) {
|
for (ControlTriggerInputBased c : controls) {
|
||||||
Packet packet = c.onInputEvent(input.getEvent());
|
Packet packet = c.onInputEvent(event);
|
||||||
|
|
||||||
if (packet != null) {
|
if (packet != null) {
|
||||||
input.consume();
|
event.consume();
|
||||||
client.getComms().sendPacket(packet);
|
client.getComms().sendPacket(packet);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@ package ru.windcorp.progressia.client.comms.localhost;
|
|||||||
import ru.windcorp.progressia.client.comms.ServerCommsChannel;
|
import ru.windcorp.progressia.client.comms.ServerCommsChannel;
|
||||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
|
|
||||||
public class LocalServerCommsChannel extends ServerCommsChannel {
|
public class LocalServerCommsChannel extends ServerCommsChannel {
|
||||||
|
|
||||||
@@ -34,7 +35,11 @@ public class LocalServerCommsChannel extends ServerCommsChannel {
|
|||||||
public void connect(String login) {
|
public void connect(String login) {
|
||||||
setState(State.CONNECTED);
|
setState(State.CONNECTED);
|
||||||
|
|
||||||
this.localClient = new LocalClient(server.getClientManager().grabClientId(), login, this);
|
this.localClient = new LocalClient(
|
||||||
|
server.getClientManager().grabClientId(),
|
||||||
|
login,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
|
||||||
server.getClientManager().addClient(localClient);
|
server.getClientManager().addClient(localClient);
|
||||||
}
|
}
|
||||||
@@ -50,7 +55,7 @@ public class LocalServerCommsChannel extends ServerCommsChannel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
// Do nothing
|
ServerState.getInstance().getClientManager().disconnectClient(localClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -21,16 +21,10 @@ package ru.windcorp.progressia.client.graphics;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import com.google.common.eventbus.Subscribe;
|
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
||||||
import ru.windcorp.progressia.client.graphics.input.CursorEvent;
|
|
||||||
import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent;
|
|
||||||
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
|
||||||
import ru.windcorp.progressia.client.graphics.input.WheelEvent;
|
|
||||||
import ru.windcorp.progressia.client.graphics.input.bus.Input;
|
|
||||||
|
|
||||||
public class GUI {
|
public class GUI {
|
||||||
|
|
||||||
@@ -45,19 +39,11 @@ public class GUI {
|
|||||||
private static final List<LayerStackModification> MODIFICATION_QUEUE = Collections
|
private static final List<LayerStackModification> MODIFICATION_QUEUE = Collections
|
||||||
.synchronizedList(new ArrayList<>());
|
.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
private static class ModifiableInput extends Input {
|
|
||||||
@Override
|
|
||||||
public void initialize(InputEvent event, Target target) {
|
|
||||||
super.initialize(event, target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final ModifiableInput THE_INPUT = new ModifiableInput();
|
|
||||||
|
|
||||||
private GUI() {
|
private GUI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addBottomLayer(Layer layer) {
|
public static void addBottomLayer(Layer layer) {
|
||||||
|
Objects.requireNonNull(layer, "layer");
|
||||||
modify(layers -> {
|
modify(layers -> {
|
||||||
layers.add(layer);
|
layers.add(layer);
|
||||||
layer.onAdded();
|
layer.onAdded();
|
||||||
@@ -65,6 +51,7 @@ public class GUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void addTopLayer(Layer layer) {
|
public static void addTopLayer(Layer layer) {
|
||||||
|
Objects.requireNonNull(layer, "layer");
|
||||||
modify(layers -> {
|
modify(layers -> {
|
||||||
layers.add(0, layer);
|
layers.add(0, layer);
|
||||||
layer.onAdded();
|
layer.onAdded();
|
||||||
@@ -72,6 +59,7 @@ public class GUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void removeLayer(Layer layer) {
|
public static void removeLayer(Layer layer) {
|
||||||
|
Objects.requireNonNull(layer, "layer");
|
||||||
modify(layers -> {
|
modify(layers -> {
|
||||||
layers.remove(layer);
|
layers.remove(layer);
|
||||||
layer.onRemoved();
|
layer.onRemoved();
|
||||||
@@ -122,43 +110,12 @@ public class GUI {
|
|||||||
LAYERS.forEach(Layer::invalidate);
|
LAYERS.forEach(Layer::invalidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void dispatchInputEvent(InputEvent event) {
|
public static void dispatchInput(InputEvent event) {
|
||||||
Input.Target target;
|
synchronized (LAYERS) {
|
||||||
|
for (int i = 0; i < LAYERS.size(); ++i) {
|
||||||
if (event instanceof KeyEvent) {
|
LAYERS.get(i).handleInput(event);
|
||||||
if (((KeyEvent) event).isMouse()) {
|
|
||||||
target = Input.Target.HOVERED;
|
|
||||||
} else {
|
|
||||||
target = Input.Target.FOCUSED;
|
|
||||||
}
|
}
|
||||||
} else if (event instanceof CursorEvent) {
|
|
||||||
target = Input.Target.HOVERED;
|
|
||||||
} else if (event instanceof WheelEvent) {
|
|
||||||
target = Input.Target.HOVERED;
|
|
||||||
} else if (event instanceof FrameResizeEvent) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
target = Input.Target.ALL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
THE_INPUT.initialize(event, target);
|
|
||||||
LAYERS.forEach(l -> l.handleInput(THE_INPUT));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object getEventSubscriber() {
|
|
||||||
return new Object() {
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onFrameResized(FrameResizeEvent event) {
|
|
||||||
GUI.invalidateEverything();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onInput(InputEvent event) {
|
|
||||||
dispatchInputEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@ package ru.windcorp.progressia.client.graphics;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
||||||
import ru.windcorp.progressia.client.graphics.input.bus.Input;
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
|
|
||||||
public abstract class Layer {
|
public abstract class Layer {
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ public abstract class Layer {
|
|||||||
|
|
||||||
protected abstract void doRender();
|
protected abstract void doRender();
|
||||||
|
|
||||||
protected abstract void handleInput(Input input);
|
public abstract void handleInput(InputEvent input);
|
||||||
|
|
||||||
protected int getWidth() {
|
protected int getWidth() {
|
||||||
return GraphicsInterface.getFrameWidth();
|
return GraphicsInterface.getFrameWidth();
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
package ru.windcorp.progressia.client.graphics.backend;
|
package ru.windcorp.progressia.client.graphics.backend;
|
||||||
|
|
||||||
import glm.vec._2.i.Vec2i;
|
import glm.vec._2.i.Vec2i;
|
||||||
|
|
||||||
import org.lwjgl.glfw.GLFWVidMode;
|
import org.lwjgl.glfw.GLFWVidMode;
|
||||||
|
|
||||||
import static org.lwjgl.glfw.GLFW.*;
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
@@ -43,6 +44,13 @@ public class GraphicsBackend {
|
|||||||
private static boolean isGLFWInitialized = false;
|
private static boolean isGLFWInitialized = false;
|
||||||
private static boolean isOpenGLInitialized = false;
|
private static boolean isOpenGLInitialized = false;
|
||||||
|
|
||||||
|
private static boolean allowDisablingCursor;
|
||||||
|
static {
|
||||||
|
String key = GraphicsBackend.class.getName() + ".allowDisablingCursor";
|
||||||
|
allowDisablingCursor = Boolean.parseBoolean(System.getProperty(key, "true"));
|
||||||
|
}
|
||||||
|
private static boolean forceCursorToCenter = false;
|
||||||
|
|
||||||
private GraphicsBackend() {
|
private GraphicsBackend() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +122,10 @@ public class GraphicsBackend {
|
|||||||
frameLength = now - frameStart;
|
frameLength = now - frameStart;
|
||||||
frameStart = now;
|
frameStart = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (forceCursorToCenter) {
|
||||||
|
glfwSetCursorPos(windowHandle, FRAME_SIZE.x / 2.0, FRAME_SIZE.y / 2.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void endFrame() {
|
static void endFrame() {
|
||||||
@@ -159,14 +171,27 @@ public class GraphicsBackend {
|
|||||||
|
|
||||||
public static void setFullscreen() {
|
public static void setFullscreen() {
|
||||||
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
glfwSetWindowMonitor(getWindowHandle(), glfwGetPrimaryMonitor(), 0, 0, vidmode.width(), vidmode.height(), 0);
|
glfwSetWindowMonitor(
|
||||||
|
getWindowHandle(),
|
||||||
|
glfwGetPrimaryMonitor(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
vidmode.width(),
|
||||||
|
vidmode.height(),
|
||||||
|
0);
|
||||||
isFullscreen = true;
|
isFullscreen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setWindowed() {
|
public static void setWindowed() {
|
||||||
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
glfwSetWindowMonitor(getWindowHandle(), 0, (vidmode.width() - getFrameWidth()) / 2,
|
glfwSetWindowMonitor(
|
||||||
(vidmode.height() - getFrameHeight()) / 2, getFrameWidth(), getFrameHeight(), 0);
|
getWindowHandle(),
|
||||||
|
0,
|
||||||
|
(vidmode.width() - getFrameWidth()) / 2,
|
||||||
|
(vidmode.height() - getFrameHeight()) / 2,
|
||||||
|
getFrameWidth(),
|
||||||
|
getFrameHeight(),
|
||||||
|
0);
|
||||||
isFullscreen = false;
|
isFullscreen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,10 +206,18 @@ public class GraphicsBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isMouseCaptured() {
|
public static boolean isMouseCaptured() {
|
||||||
|
if (!allowDisablingCursor) {
|
||||||
|
return forceCursorToCenter;
|
||||||
|
}
|
||||||
return glfwGetInputMode(windowHandle, GLFW_CURSOR) == GLFW_CURSOR_DISABLED;
|
return glfwGetInputMode(windowHandle, GLFW_CURSOR) == GLFW_CURSOR_DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setMouseCaptured(boolean capture) {
|
public static void setMouseCaptured(boolean capture) {
|
||||||
|
if (!allowDisablingCursor) {
|
||||||
|
forceCursorToCenter = capture;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int mode = capture ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL;
|
int mode = capture ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL;
|
||||||
glfwSetInputMode(windowHandle, GLFW_CURSOR, mode);
|
glfwSetInputMode(windowHandle, GLFW_CURSOR, mode);
|
||||||
|
|
||||||
|
@@ -69,6 +69,10 @@ public class GraphicsInterface {
|
|||||||
InputHandler.register(listener);
|
InputHandler.register(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void unsubscribeFromInputEvents(Object listener) {
|
||||||
|
InputHandler.unregister(listener);
|
||||||
|
}
|
||||||
|
|
||||||
public static void startNextLayer() {
|
public static void startNextLayer() {
|
||||||
GraphicsBackend.startNextLayer();
|
GraphicsBackend.startNextLayer();
|
||||||
}
|
}
|
||||||
|
@@ -39,6 +39,7 @@ public class InputHandler {
|
|||||||
|
|
||||||
public void initialize(int key, int scancode, int action, int mods) {
|
public void initialize(int key, int scancode, int action, int mods) {
|
||||||
this.setTime(GraphicsInterface.getTime());
|
this.setTime(GraphicsInterface.getTime());
|
||||||
|
this.setConsumed(false);
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.scancode = scancode;
|
this.scancode = scancode;
|
||||||
this.action = action;
|
this.action = action;
|
||||||
@@ -49,11 +50,17 @@ public class InputHandler {
|
|||||||
|
|
||||||
private static final ModifiableKeyEvent THE_KEY_EVENT = new ModifiableKeyEvent();
|
private static final ModifiableKeyEvent THE_KEY_EVENT = new ModifiableKeyEvent();
|
||||||
|
|
||||||
static void handleKeyInput(long window, int key, int scancode, int action, int mods) {
|
static void handleKeyInput(
|
||||||
|
long window,
|
||||||
|
int key,
|
||||||
|
int scancode,
|
||||||
|
int action,
|
||||||
|
int mods
|
||||||
|
) {
|
||||||
if (GraphicsBackend.getWindowHandle() != window)
|
if (GraphicsBackend.getWindowHandle() != window)
|
||||||
return;
|
return;
|
||||||
THE_KEY_EVENT.initialize(key, scancode, action, mods);
|
THE_KEY_EVENT.initialize(key, scancode, action, mods);
|
||||||
dispatch(THE_KEY_EVENT);
|
INPUT_EVENT_BUS.post(THE_KEY_EVENT);
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case GLFW.GLFW_PRESS:
|
case GLFW.GLFW_PRESS:
|
||||||
@@ -65,7 +72,12 @@ public class InputHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleMouseButtonInput(long window, int key, int action, int mods) {
|
static void handleMouseButtonInput(
|
||||||
|
long window,
|
||||||
|
int key,
|
||||||
|
int action,
|
||||||
|
int mods
|
||||||
|
) {
|
||||||
handleKeyInput(window, key, Integer.MAX_VALUE - key, action, mods);
|
handleKeyInput(window, key, Integer.MAX_VALUE - key, action, mods);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +91,7 @@ public class InputHandler {
|
|||||||
|
|
||||||
public void initialize(double x, double y) {
|
public void initialize(double x, double y) {
|
||||||
this.setTime(GraphicsInterface.getTime());
|
this.setTime(GraphicsInterface.getTime());
|
||||||
|
this.setConsumed(false);
|
||||||
getNewPosition().set(x, y);
|
getNewPosition().set(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +99,11 @@ public class InputHandler {
|
|||||||
|
|
||||||
private static final ModifiableCursorMoveEvent THE_CURSOR_MOVE_EVENT = new ModifiableCursorMoveEvent();
|
private static final ModifiableCursorMoveEvent THE_CURSOR_MOVE_EVENT = new ModifiableCursorMoveEvent();
|
||||||
|
|
||||||
static void handleMouseMoveInput(long window, double x, double y) {
|
static void handleMouseMoveInput(
|
||||||
|
long window,
|
||||||
|
double x,
|
||||||
|
double y
|
||||||
|
) {
|
||||||
if (GraphicsBackend.getWindowHandle() != window)
|
if (GraphicsBackend.getWindowHandle() != window)
|
||||||
return;
|
return;
|
||||||
y = GraphicsInterface.getFrameHeight() - y; // Flip y axis
|
y = GraphicsInterface.getFrameHeight() - y; // Flip y axis
|
||||||
@@ -94,7 +111,7 @@ public class InputHandler {
|
|||||||
InputTracker.initializeCursorPosition(x, y);
|
InputTracker.initializeCursorPosition(x, y);
|
||||||
|
|
||||||
THE_CURSOR_MOVE_EVENT.initialize(x, y);
|
THE_CURSOR_MOVE_EVENT.initialize(x, y);
|
||||||
dispatch(THE_CURSOR_MOVE_EVENT);
|
INPUT_EVENT_BUS.post(THE_CURSOR_MOVE_EVENT);
|
||||||
|
|
||||||
InputTracker.getCursorPosition().set(x, y);
|
InputTracker.getCursorPosition().set(x, y);
|
||||||
}
|
}
|
||||||
@@ -109,6 +126,7 @@ public class InputHandler {
|
|||||||
|
|
||||||
public void initialize(double xOffset, double yOffset) {
|
public void initialize(double xOffset, double yOffset) {
|
||||||
this.setTime(GraphicsInterface.getTime());
|
this.setTime(GraphicsInterface.getTime());
|
||||||
|
this.setConsumed(false);
|
||||||
this.getOffset().set(xOffset, yOffset);
|
this.getOffset().set(xOffset, yOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,11 +134,15 @@ public class InputHandler {
|
|||||||
|
|
||||||
private static final ModifiableWheelScrollEvent THE_WHEEL_SCROLL_EVENT = new ModifiableWheelScrollEvent();
|
private static final ModifiableWheelScrollEvent THE_WHEEL_SCROLL_EVENT = new ModifiableWheelScrollEvent();
|
||||||
|
|
||||||
static void handleWheelScroll(long window, double xoffset, double yoffset) {
|
static void handleWheelScroll(
|
||||||
|
long window,
|
||||||
|
double xoffset,
|
||||||
|
double yoffset
|
||||||
|
) {
|
||||||
if (GraphicsBackend.getWindowHandle() != window)
|
if (GraphicsBackend.getWindowHandle() != window)
|
||||||
return;
|
return;
|
||||||
THE_WHEEL_SCROLL_EVENT.initialize(xoffset, yoffset);
|
THE_WHEEL_SCROLL_EVENT.initialize(xoffset, yoffset);
|
||||||
dispatch(THE_WHEEL_SCROLL_EVENT);
|
INPUT_EVENT_BUS.post(THE_WHEEL_SCROLL_EVENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FrameResizeEvent
|
// FrameResizeEvent
|
||||||
@@ -133,6 +155,7 @@ public class InputHandler {
|
|||||||
|
|
||||||
public void initialize(int width, int height) {
|
public void initialize(int width, int height) {
|
||||||
this.setTime(GraphicsInterface.getTime());
|
this.setTime(GraphicsInterface.getTime());
|
||||||
|
this.setConsumed(false);
|
||||||
this.getNewSize().set(width, height);
|
this.getNewSize().set(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,19 +166,22 @@ public class InputHandler {
|
|||||||
/*
|
/*
|
||||||
* NB: this is NOT a GLFW callback, the raw callback is in GraphicsBackend
|
* NB: this is NOT a GLFW callback, the raw callback is in GraphicsBackend
|
||||||
*/
|
*/
|
||||||
static void handleFrameResize(int width, int height) {
|
static void handleFrameResize(
|
||||||
|
int width,
|
||||||
|
int height
|
||||||
|
) {
|
||||||
THE_FRAME_RESIZE_EVENT.initialize(width, height);
|
THE_FRAME_RESIZE_EVENT.initialize(width, height);
|
||||||
dispatch(THE_FRAME_RESIZE_EVENT);
|
INPUT_EVENT_BUS.post(THE_FRAME_RESIZE_EVENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
|
|
||||||
private static void dispatch(InputEvent event) {
|
|
||||||
INPUT_EVENT_BUS.post(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void register(Object listener) {
|
public static void register(Object listener) {
|
||||||
INPUT_EVENT_BUS.register(listener);
|
INPUT_EVENT_BUS.register(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void unregister(Object listener) {
|
||||||
|
INPUT_EVENT_BUS.unregister(listener);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,10 @@ import gnu.trove.set.hash.TIntHashSet;
|
|||||||
|
|
||||||
public class InputTracker {
|
public class InputTracker {
|
||||||
|
|
||||||
private static final Vec2d CURSOR_POSITION = new Vec2d(Double.NaN, Double.NaN);
|
private static final Vec2d CURSOR_POSITION = new Vec2d(
|
||||||
|
Double.NaN,
|
||||||
|
Double.NaN
|
||||||
|
);
|
||||||
|
|
||||||
private static final TIntSet PRESSED_KEYS = new TIntHashSet(256);
|
private static final TIntSet PRESSED_KEYS = new TIntHashSet(256);
|
||||||
|
|
||||||
|
@@ -22,9 +22,23 @@ import static org.lwjgl.opengl.GL11.*;
|
|||||||
import static org.lwjgl.glfw.GLFW.*;
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
import static org.lwjgl.system.MemoryUtil.*;
|
import static org.lwjgl.system.MemoryUtil.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFWImage;
|
||||||
import org.lwjgl.opengl.GL;
|
import org.lwjgl.opengl.GL;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.Progressia;
|
||||||
import ru.windcorp.progressia.client.graphics.GUI;
|
import ru.windcorp.progressia.client.graphics.GUI;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.TextureDataEditor;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.TextureLoader;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
|
||||||
|
import ru.windcorp.progressia.common.resource.Resource;
|
||||||
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
class LWJGLInitializer {
|
class LWJGLInitializer {
|
||||||
|
|
||||||
@@ -59,7 +73,13 @@ class LWJGLInitializer {
|
|||||||
glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
|
glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
|
||||||
glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
|
glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
|
||||||
|
|
||||||
long handle = glfwCreateWindow(900, 900, "ProgressiaTest", NULL, NULL);
|
long handle = glfwCreateWindow(
|
||||||
|
800,
|
||||||
|
600,
|
||||||
|
Progressia.getName() + " " + Progressia.getFullerVersion(),
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
// TODO Check that handle != NULL
|
// TODO Check that handle != NULL
|
||||||
|
|
||||||
@@ -75,8 +95,30 @@ class LWJGLInitializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void createWindowIcons() {
|
private static void createWindowIcons() {
|
||||||
// TODO Auto-generated method stub
|
if (glfwGetVersionString().toLowerCase().contains("wayland")) {
|
||||||
|
// glfwSetWindowIcon is not supported on Wayland
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String prefix = "assets/icons/";
|
||||||
|
|
||||||
|
String[] sizes = ResourceManager.getResource(prefix + "logoSizes.txt").readAsString().split(" ");
|
||||||
|
|
||||||
|
try (GLFWImage.Buffer buffer = GLFWImage.malloc(sizes.length)) {
|
||||||
|
for (int i = 0; i < sizes.length; ++i) {
|
||||||
|
Resource resource = ResourceManager.getResource(prefix + "logo" + sizes[i].trim() + ".png");
|
||||||
|
TextureDataEditor icon = TextureLoader.loadPixels(resource, new TextureSettings(false, true));
|
||||||
|
|
||||||
|
buffer.position(i)
|
||||||
|
.width(icon.getContentWidth())
|
||||||
|
.height(icon.getContentHeight())
|
||||||
|
.pixels(icon.getData().getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwSetWindowIcon(GraphicsBackend.getWindowHandle(), buffer);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw CrashReports.report(e, "Could not load window icons");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void initializeOpenGL() {
|
private static void initializeOpenGL() {
|
||||||
@@ -92,16 +134,35 @@ class LWJGLInitializer {
|
|||||||
private static void setupWindowCallbacks() {
|
private static void setupWindowCallbacks() {
|
||||||
long handle = GraphicsBackend.getWindowHandle();
|
long handle = GraphicsBackend.getWindowHandle();
|
||||||
|
|
||||||
glfwSetFramebufferSizeCallback(handle, GraphicsBackend::onFrameResized);
|
glfwSetFramebufferSizeCallback(
|
||||||
|
handle,
|
||||||
|
GraphicsBackend::onFrameResized
|
||||||
|
);
|
||||||
|
|
||||||
glfwSetKeyCallback(handle, InputHandler::handleKeyInput);
|
glfwSetKeyCallback(handle, InputHandler::handleKeyInput);
|
||||||
glfwSetMouseButtonCallback(handle, InputHandler::handleMouseButtonInput);
|
glfwSetMouseButtonCallback(
|
||||||
|
handle,
|
||||||
|
InputHandler::handleMouseButtonInput
|
||||||
|
);
|
||||||
|
|
||||||
glfwSetCursorPosCallback(handle, InputHandler::handleMouseMoveInput);
|
glfwSetCursorPosCallback(handle, InputHandler::handleMouseMoveInput);
|
||||||
|
|
||||||
glfwSetScrollCallback(handle, InputHandler::handleWheelScroll);
|
glfwSetScrollCallback(handle, InputHandler::handleWheelScroll);
|
||||||
|
|
||||||
GraphicsInterface.subscribeToInputEvents(GUI.getEventSubscriber());
|
GraphicsInterface.subscribeToInputEvents(new Object() {
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onFrameResized(FrameResizeEvent event) {
|
||||||
|
GUI.invalidateEverything();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onInputEvent(InputEvent event) {
|
||||||
|
GUI.dispatchInput(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -34,13 +34,19 @@ public class OpenGLObjectTracker {
|
|||||||
private static final ReferenceQueue<OpenGLDeletable> DELETE_QUEUE = new ReferenceQueue<>();
|
private static final ReferenceQueue<OpenGLDeletable> DELETE_QUEUE = new ReferenceQueue<>();
|
||||||
|
|
||||||
public synchronized static void register(OpenGLDeletable object, IntConsumer glDeleter) {
|
public synchronized static void register(OpenGLDeletable object, IntConsumer glDeleter) {
|
||||||
GLPhantomReference<OpenGLDeletable> glRef = new GLPhantomReference<>(object, DELETE_QUEUE, object.getHandle(),
|
GLPhantomReference<OpenGLDeletable> glRef = new GLPhantomReference<>(
|
||||||
glDeleter);
|
object,
|
||||||
|
DELETE_QUEUE,
|
||||||
|
object.getHandle(),
|
||||||
|
glDeleter
|
||||||
|
);
|
||||||
TO_DELETE.add(glRef);
|
TO_DELETE.add(glRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteAllObjects() {
|
public static void deleteAllObjects() {
|
||||||
for (GLPhantomReference<OpenGLDeletable> glRef : TO_DELETE) {
|
for (
|
||||||
|
GLPhantomReference<OpenGLDeletable> glRef : TO_DELETE
|
||||||
|
) {
|
||||||
glRef.clear();
|
glRef.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,16 +75,20 @@ public class OpenGLObjectTracker {
|
|||||||
* It is possible to create a phantom reference with a {@code null}
|
* It is possible to create a phantom reference with a {@code null}
|
||||||
* queue, but such a reference is completely useless: Its {@code get}
|
* queue, but such a reference is completely useless: Its {@code get}
|
||||||
* method will always return {@code null} and, since it does not have a
|
* method will always return {@code null} and, since it does not have a
|
||||||
* queue, it will never be enqueued.
|
* queue,
|
||||||
|
* it will never be enqueued.
|
||||||
*
|
*
|
||||||
* @param referent
|
* @param referent the object the new phantom reference will refer to
|
||||||
* the object the new phantom reference will refer to
|
* @param q the queue with which the reference is to be
|
||||||
* @param q
|
* registered,
|
||||||
* the queue with which the reference is to be registered, or
|
* or {@code null} if registration is not required
|
||||||
* {@code null} if registration is not required
|
|
||||||
*/
|
*/
|
||||||
public GLPhantomReference(T referent, ReferenceQueue<? super T> q, int referentGLhandle,
|
public GLPhantomReference(
|
||||||
IntConsumer GLDeleter) {
|
T referent,
|
||||||
|
ReferenceQueue<? super T> q,
|
||||||
|
int referentGLhandle,
|
||||||
|
IntConsumer GLDeleter
|
||||||
|
) {
|
||||||
super(referent, q);
|
super(referent, q);
|
||||||
this.referentGLhandle = referentGLhandle;
|
this.referentGLhandle = referentGLhandle;
|
||||||
this.GLDeleter = GLDeleter;
|
this.GLDeleter = GLDeleter;
|
||||||
|
@@ -41,7 +41,11 @@ public class RenderTaskQueue {
|
|||||||
HANDLER.invokeNow(task);
|
HANDLER.invokeNow(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends Exception> void waitAndInvoke(ThrowingRunnable<E> task) throws InterruptedException, E {
|
public static <E extends Exception> void waitAndInvoke(
|
||||||
|
ThrowingRunnable<E> task
|
||||||
|
)
|
||||||
|
throws InterruptedException,
|
||||||
|
E {
|
||||||
HANDLER.waitAndInvoke(task);
|
HANDLER.waitAndInvoke(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,7 +23,9 @@ import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
|
|||||||
import static org.lwjgl.opengl.GL15.GL_STREAM_DRAW;
|
import static org.lwjgl.opengl.GL15.GL_STREAM_DRAW;
|
||||||
|
|
||||||
public enum Usage { // TODO add _COPY and _READ, pref. as another enum
|
public enum Usage { // TODO add _COPY and _READ, pref. as another enum
|
||||||
STATIC(GL_STATIC_DRAW), DYNAMIC(GL_DYNAMIC_DRAW), STREAM(GL_STREAM_DRAW);
|
STATIC(GL_STATIC_DRAW),
|
||||||
|
DYNAMIC(GL_DYNAMIC_DRAW),
|
||||||
|
STREAM(GL_STREAM_DRAW);
|
||||||
|
|
||||||
private final int glCode;
|
private final int glCode;
|
||||||
|
|
||||||
|
@@ -28,7 +28,8 @@ import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGL
|
|||||||
public class VertexBufferObject implements OpenGLDeletable {
|
public class VertexBufferObject implements OpenGLDeletable {
|
||||||
|
|
||||||
public static enum BindTarget {
|
public static enum BindTarget {
|
||||||
ARRAY(GL_ARRAY_BUFFER), ELEMENT_ARRAY(GL_ELEMENT_ARRAY_BUFFER);
|
ARRAY(GL_ARRAY_BUFFER),
|
||||||
|
ELEMENT_ARRAY(GL_ELEMENT_ARRAY_BUFFER);
|
||||||
|
|
||||||
private final int glCode;
|
private final int glCode;
|
||||||
|
|
||||||
|
@@ -32,7 +32,12 @@ public class CombinedShader extends Shader {
|
|||||||
for (int i = 1; i < resources.length; ++i) {
|
for (int i = 1; i < resources.length; ++i) {
|
||||||
if (ShaderType.guessByResourceName(resources[i]) != first) {
|
if (ShaderType.guessByResourceName(resources[i]) != first) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Deduced shader types of " + resources[0] + " and " + resources[i] + " differ");
|
"Deduced shader types of "
|
||||||
|
+ resources[0]
|
||||||
|
+ " and "
|
||||||
|
+ resources[i]
|
||||||
|
+ " differ"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,8 +71,19 @@ public class CombinedShader extends Shader {
|
|||||||
if (contents.codePointAt(versionIndex) == '#') {
|
if (contents.codePointAt(versionIndex) == '#') {
|
||||||
final String versionAnnotation = "#version ";
|
final String versionAnnotation = "#version ";
|
||||||
|
|
||||||
if (contents.regionMatches(versionIndex, versionAnnotation, 0, versionAnnotation.length())) {
|
if (
|
||||||
contents = contents.substring(versionIndex + versionAnnotation.length() + "120".length());
|
contents.regionMatches(
|
||||||
|
versionIndex,
|
||||||
|
versionAnnotation,
|
||||||
|
0,
|
||||||
|
versionAnnotation.length()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
contents = contents.substring(
|
||||||
|
versionIndex
|
||||||
|
+ versionAnnotation.length()
|
||||||
|
+ "120".length()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -57,7 +57,10 @@ public class Shader implements OpenGLDeletable {
|
|||||||
if (resource.contains("fsh"))
|
if (resource.contains("fsh"))
|
||||||
return FRAGMENT;
|
return FRAGMENT;
|
||||||
|
|
||||||
throw new IllegalArgumentException("Cannot deduce shader type from resource name \"" + resource + "\"");
|
throw new IllegalArgumentException(
|
||||||
|
"Cannot deduce shader type from resource name \"" +
|
||||||
|
resource + "\""
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +90,10 @@ public class Shader implements OpenGLDeletable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Shader(String resource) {
|
public Shader(String resource) {
|
||||||
this(ShaderType.guessByResourceName(resource), getShaderResource(resource).readAsString());
|
this(
|
||||||
|
ShaderType.guessByResourceName(resource),
|
||||||
|
getShaderResource(resource).readAsString()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -51,29 +51,104 @@ public class AttributeVertexArray extends Attribute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(int size, boolean normalized, int stride, ByteBuffer pointer) {
|
public void set(
|
||||||
glVertexAttribPointer(handle, size, GL_BYTE, normalized, stride, pointer);
|
int size,
|
||||||
|
boolean normalized,
|
||||||
|
int stride,
|
||||||
|
ByteBuffer pointer
|
||||||
|
) {
|
||||||
|
glVertexAttribPointer(
|
||||||
|
handle,
|
||||||
|
size,
|
||||||
|
GL_BYTE,
|
||||||
|
normalized,
|
||||||
|
stride,
|
||||||
|
pointer
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(int size, boolean normalized, int stride, FloatBuffer pointer) {
|
public void set(
|
||||||
glVertexAttribPointer(handle, size, GL_FLOAT, normalized, stride, pointer);
|
int size,
|
||||||
|
boolean normalized,
|
||||||
|
int stride,
|
||||||
|
FloatBuffer pointer
|
||||||
|
) {
|
||||||
|
glVertexAttribPointer(
|
||||||
|
handle,
|
||||||
|
size,
|
||||||
|
GL_FLOAT,
|
||||||
|
normalized,
|
||||||
|
stride,
|
||||||
|
pointer
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(int size, boolean normalized, int stride, IntBuffer pointer) {
|
public void set(
|
||||||
glVertexAttribPointer(handle, size, GL_INT, normalized, stride, pointer);
|
int size,
|
||||||
|
boolean normalized,
|
||||||
|
int stride,
|
||||||
|
IntBuffer pointer
|
||||||
|
) {
|
||||||
|
glVertexAttribPointer(
|
||||||
|
handle,
|
||||||
|
size,
|
||||||
|
GL_INT,
|
||||||
|
normalized,
|
||||||
|
stride,
|
||||||
|
pointer
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(int size, boolean normalized, int stride, ShortBuffer pointer) {
|
public void set(
|
||||||
glVertexAttribPointer(handle, size, GL_SHORT, normalized, stride, pointer);
|
int size,
|
||||||
|
boolean normalized,
|
||||||
|
int stride,
|
||||||
|
ShortBuffer pointer
|
||||||
|
) {
|
||||||
|
glVertexAttribPointer(
|
||||||
|
handle,
|
||||||
|
size,
|
||||||
|
GL_SHORT,
|
||||||
|
normalized,
|
||||||
|
stride,
|
||||||
|
pointer
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(int size, int type, boolean normalized, int stride, long pointer) {
|
public void set(
|
||||||
glVertexAttribPointer(handle, size, type, normalized, stride, pointer);
|
int size,
|
||||||
|
int type,
|
||||||
|
boolean normalized,
|
||||||
|
int stride,
|
||||||
|
long pointer
|
||||||
|
) {
|
||||||
|
glVertexAttribPointer(
|
||||||
|
handle,
|
||||||
|
size,
|
||||||
|
type,
|
||||||
|
normalized,
|
||||||
|
stride,
|
||||||
|
pointer
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(int size, int type, boolean normalized, int stride, VertexBufferObject vbo, long offset) {
|
public void set(
|
||||||
|
int size,
|
||||||
|
int type,
|
||||||
|
boolean normalized,
|
||||||
|
int stride,
|
||||||
|
VertexBufferObject vbo,
|
||||||
|
long offset
|
||||||
|
) {
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo.getHandle());
|
glBindBuffer(GL_ARRAY_BUFFER, vbo.getHandle());
|
||||||
glVertexAttribPointer(handle, size, type, normalized, stride, offset);
|
glVertexAttribPointer(
|
||||||
|
handle,
|
||||||
|
size,
|
||||||
|
type,
|
||||||
|
normalized,
|
||||||
|
stride,
|
||||||
|
offset
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -37,7 +37,8 @@ public abstract class FlatRenderHelper extends ShapeRenderHelper {
|
|||||||
float width = GraphicsInterface.getFrameWidth();
|
float width = GraphicsInterface.getFrameWidth();
|
||||||
float height = GraphicsInterface.getFrameHeight();
|
float height = GraphicsInterface.getFrameHeight();
|
||||||
|
|
||||||
return finalTransform.identity().translate(-1, -1, 0).scale(2 / width, 2 / height, 1 / MAX_DEPTH)
|
return finalTransform.identity().translate(-1, -1, 0)
|
||||||
|
.scale(2 / width, 2 / height, 1 / MAX_DEPTH)
|
||||||
.mul(getTransform());
|
.mul(getTransform());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -33,8 +33,10 @@ public class FlatRenderProgram extends ShapeRenderProgram {
|
|||||||
private static FlatRenderProgram def = null;
|
private static FlatRenderProgram def = null;
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
def = new FlatRenderProgram(new String[] { "FlatDefault.vertex.glsl" },
|
def = new FlatRenderProgram(
|
||||||
new String[] { "FlatDefault.fragment.glsl" });
|
new String[] { "FlatDefault.vertex.glsl" },
|
||||||
|
new String[] { "FlatDefault.fragment.glsl" }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FlatRenderProgram getDefault() {
|
public static FlatRenderProgram getDefault() {
|
||||||
@@ -46,13 +48,20 @@ public class FlatRenderProgram extends ShapeRenderProgram {
|
|||||||
private static final String FLAT_VERTEX_SHADER_RESOURCE = "Flat.vertex.glsl";
|
private static final String FLAT_VERTEX_SHADER_RESOURCE = "Flat.vertex.glsl";
|
||||||
private static final String FLAT_FRAGMENT_SHADER_RESOURCE = "Flat.fragment.glsl";
|
private static final String FLAT_FRAGMENT_SHADER_RESOURCE = "Flat.fragment.glsl";
|
||||||
|
|
||||||
private static final String MASK_COUNT_UNIFORM_NAME = "maskCount", MASKS_UNIFORM_NAME = "masks";
|
private static final String MASK_COUNT_UNIFORM_NAME = "maskCount",
|
||||||
|
MASKS_UNIFORM_NAME = "masks";
|
||||||
|
|
||||||
private final Uniform1Int maskCountUniform;
|
private final Uniform1Int maskCountUniform;
|
||||||
private final Uniform2Float masksUniform;
|
private final Uniform2Float masksUniform;
|
||||||
|
|
||||||
public FlatRenderProgram(String[] vertexShaderResources, String[] fragmentShaderResources) {
|
public FlatRenderProgram(
|
||||||
super(attachVertexShader(vertexShaderResources), attachFragmentShader(fragmentShaderResources));
|
String[] vertexShaderResources,
|
||||||
|
String[] fragmentShaderResources
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
attachVertexShader(vertexShaderResources),
|
||||||
|
attachFragmentShader(fragmentShaderResources)
|
||||||
|
);
|
||||||
|
|
||||||
this.maskCountUniform = getUniform(MASK_COUNT_UNIFORM_NAME).as1Int();
|
this.maskCountUniform = getUniform(MASK_COUNT_UNIFORM_NAME).as1Int();
|
||||||
this.masksUniform = getUniform(MASKS_UNIFORM_NAME).as2Float();
|
this.masksUniform = getUniform(MASKS_UNIFORM_NAME).as2Float();
|
||||||
|
@@ -88,7 +88,8 @@ public class Mask {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "(" + getStartX() + "; " + getStartY() + ") -> (" + getEndX() + "; " + getEndY() + ")";
|
return "(" + getStartX() + "; " + getStartY() +
|
||||||
|
") -> (" + getEndX() + "; " + getEndY() + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -24,8 +24,9 @@ import org.lwjgl.BufferUtils;
|
|||||||
|
|
||||||
public class MaskStack {
|
public class MaskStack {
|
||||||
|
|
||||||
private final FloatBuffer buffer = BufferUtils
|
private final FloatBuffer buffer = BufferUtils.createFloatBuffer(
|
||||||
.createFloatBuffer(FlatRenderProgram.MASK_STACK_SIZE * TransformedMask.SIZE_IN_FLOATS);
|
FlatRenderProgram.MASK_STACK_SIZE * TransformedMask.SIZE_IN_FLOATS
|
||||||
|
);
|
||||||
|
|
||||||
public void pushMask(TransformedMask mask) {
|
public void pushMask(TransformedMask mask) {
|
||||||
mask.writeToBuffer(buffer);
|
mask.writeToBuffer(buffer);
|
||||||
|
@@ -42,8 +42,14 @@ public class TransformedMask {
|
|||||||
private Vec4 endXstartY = null;
|
private Vec4 endXstartY = null;
|
||||||
private Vec4 endXendY = null;
|
private Vec4 endXendY = null;
|
||||||
|
|
||||||
public TransformedMask(Vec2 origin, Vec2 width, Vec2 height, Vec2 counterOrigin, Vec2 counterWidth,
|
public TransformedMask(
|
||||||
Vec2 counterHeight) {
|
Vec2 origin,
|
||||||
|
Vec2 width,
|
||||||
|
Vec2 height,
|
||||||
|
Vec2 counterOrigin,
|
||||||
|
Vec2 counterWidth,
|
||||||
|
Vec2 counterHeight
|
||||||
|
) {
|
||||||
set(origin, width, height, counterOrigin, counterWidth, counterHeight);
|
set(origin, width, height, counterOrigin, counterWidth, counterHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,8 +61,14 @@ public class TransformedMask {
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransformedMask set(Vec2 origin, Vec2 width, Vec2 height, Vec2 counterOrigin, Vec2 counterWidth,
|
public TransformedMask set(
|
||||||
Vec2 counterHeight) {
|
Vec2 origin,
|
||||||
|
Vec2 width,
|
||||||
|
Vec2 height,
|
||||||
|
Vec2 counterOrigin,
|
||||||
|
Vec2 counterWidth,
|
||||||
|
Vec2 counterHeight
|
||||||
|
) {
|
||||||
this.origin.set(origin.x, origin.y);
|
this.origin.set(origin.x, origin.y);
|
||||||
this.width.set(width.x, width.y);
|
this.width.set(width.x, width.y);
|
||||||
this.height.set(height.x, height.y);
|
this.height.set(height.x, height.y);
|
||||||
@@ -100,17 +112,35 @@ public class TransformedMask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setFields() {
|
private void setFields() {
|
||||||
origin.set(startXstartY.x, startXstartY.y);
|
origin.set(
|
||||||
|
startXstartY.x,
|
||||||
|
startXstartY.y
|
||||||
|
);
|
||||||
|
|
||||||
width.set(endXstartY.x - startXstartY.x, endXstartY.y - startXstartY.y);
|
width.set(
|
||||||
|
endXstartY.x - startXstartY.x,
|
||||||
|
endXstartY.y - startXstartY.y
|
||||||
|
);
|
||||||
|
|
||||||
height.set(startXendY.x - startXstartY.x, startXendY.y - startXstartY.y);
|
height.set(
|
||||||
|
startXendY.x - startXstartY.x,
|
||||||
|
startXendY.y - startXstartY.y
|
||||||
|
);
|
||||||
|
|
||||||
counterOrigin.set(endXendY.x, endXendY.y);
|
counterOrigin.set(
|
||||||
|
endXendY.x,
|
||||||
|
endXendY.y
|
||||||
|
);
|
||||||
|
|
||||||
counterWidth.set(startXendY.x - endXendY.x, startXendY.y - endXendY.y);
|
counterWidth.set(
|
||||||
|
startXendY.x - endXendY.x,
|
||||||
|
startXendY.y - endXendY.y
|
||||||
|
);
|
||||||
|
|
||||||
counterHeight.set(endXstartY.x - endXendY.x, endXstartY.y - endXendY.y);
|
counterHeight.set(
|
||||||
|
endXstartY.x - endXendY.x,
|
||||||
|
endXstartY.y - endXendY.y
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeToBuffer(FloatBuffer output) {
|
public void writeToBuffer(FloatBuffer output) {
|
||||||
|
@@ -68,11 +68,17 @@ public class Font {
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Renderable assemble(CharSequence chars, float maxWidth) {
|
public Renderable assemble(
|
||||||
|
CharSequence chars,
|
||||||
|
float maxWidth
|
||||||
|
) {
|
||||||
return typeface.assembleStatic(chars, style, align, maxWidth, color);
|
return typeface.assembleStatic(chars, style, align, maxWidth, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Renderable assembleDynamic(Supplier<CharSequence> supplier, float maxWidth) {
|
public Renderable assembleDynamic(
|
||||||
|
Supplier<CharSequence> supplier,
|
||||||
|
float maxWidth
|
||||||
|
) {
|
||||||
return typeface.assembleDynamic(supplier, style, align, maxWidth, color);
|
return typeface.assembleDynamic(supplier, style, align, maxWidth, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,8 +102,7 @@ public class Font {
|
|||||||
* Creates a new {@link Font} with the specified {@code style} exactly. This
|
* Creates a new {@link Font} with the specified {@code style} exactly. This
|
||||||
* object's style is ignored.
|
* object's style is ignored.
|
||||||
*
|
*
|
||||||
* @param style
|
* @param style the new style
|
||||||
* the new style
|
|
||||||
* @return the new font
|
* @return the new font
|
||||||
*/
|
*/
|
||||||
public Font withStyle(int style) {
|
public Font withStyle(int style) {
|
||||||
|
@@ -45,7 +45,7 @@ public class GNUUnifontLoader {
|
|||||||
|
|
||||||
private static final AtlasGroup ATLAS_GROUP_GNU_UNIFONT = new AtlasGroup("GNUUnifont", 1 << 12);
|
private static final AtlasGroup ATLAS_GROUP_GNU_UNIFONT = new AtlasGroup("GNUUnifont", 1 << 12);
|
||||||
|
|
||||||
private static final TextureSettings TEXTURE_SETTINGS = new TextureSettings(false);
|
private static final TextureSettings TEXTURE_SETTINGS = new TextureSettings(false, false);
|
||||||
|
|
||||||
private static final int BITS_PER_HEX_DIGIT = 4;
|
private static final int BITS_PER_HEX_DIGIT = 4;
|
||||||
private static final int PREFIX_LENGTH = "0000:".length();
|
private static final int PREFIX_LENGTH = "0000:".length();
|
||||||
@@ -81,7 +81,8 @@ public class GNUUnifontLoader {
|
|||||||
|
|
||||||
private static BufferedReader createReader(Resource resource) throws IOException {
|
private static BufferedReader createReader(Resource resource) throws IOException {
|
||||||
return new BufferedReader(
|
return new BufferedReader(
|
||||||
new InputStreamReader(new GZIPInputStream(resource.getInputStream()), StandardCharsets.UTF_8));
|
new InputStreamReader(new GZIPInputStream(resource.getInputStream()), StandardCharsets.UTF_8)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream<String> createStream(BufferedReader reader) {
|
private static Stream<String> createStream(BufferedReader reader) {
|
||||||
@@ -96,8 +97,13 @@ public class GNUUnifontLoader {
|
|||||||
|
|
||||||
char c = getChar(declar);
|
char c = getChar(declar);
|
||||||
|
|
||||||
TextureDataEditor editor = new TextureDataEditor(width, GNUUnifont.HEIGHT, width, GNUUnifont.HEIGHT,
|
TextureDataEditor editor = new TextureDataEditor(
|
||||||
TEXTURE_SETTINGS);
|
width,
|
||||||
|
GNUUnifont.HEIGHT,
|
||||||
|
width,
|
||||||
|
GNUUnifont.HEIGHT,
|
||||||
|
TEXTURE_SETTINGS
|
||||||
|
);
|
||||||
|
|
||||||
for (int y = 0; y < GNUUnifont.HEIGHT; ++y) {
|
for (int y = 0; y < GNUUnifont.HEIGHT; ++y) {
|
||||||
for (int x = 0; x < width; ++x) {
|
for (int x = 0; x < width; ++x) {
|
||||||
@@ -159,7 +165,8 @@ public class GNUUnifontLoader {
|
|||||||
|
|
||||||
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))) {
|
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))) {
|
||||||
throw new IOException(
|
throw new IOException(
|
||||||
"Illegal char in declar \"" + declar + "\" at index " + i + "; expected 0-9A-F");
|
"Illegal char in declar \"" + declar + "\" at index " + i + "; expected 0-9A-F"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +190,8 @@ public class GNUUnifontLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Collector<AtlasGlyph, ?, TCharObjectMap<Texture>> createMapper() {
|
private static Collector<AtlasGlyph, ?, TCharObjectMap<Texture>> createMapper() {
|
||||||
return Collector.of(TCharObjectHashMap<Texture>::new,
|
return Collector.of(
|
||||||
|
TCharObjectHashMap<Texture>::new,
|
||||||
|
|
||||||
(map, glyph) -> map.put(glyph.c, glyph.texture),
|
(map, glyph) -> map.put(glyph.c, glyph.texture),
|
||||||
|
|
||||||
@@ -192,7 +200,8 @@ public class GNUUnifontLoader {
|
|||||||
return a;
|
return a;
|
||||||
},
|
},
|
||||||
|
|
||||||
Characteristics.UNORDERED);
|
Characteristics.UNORDERED
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GNUUnifontLoader() {
|
private GNUUnifontLoader() {
|
||||||
|
@@ -29,8 +29,12 @@ import ru.windcorp.progressia.common.util.Vectors;
|
|||||||
public abstract class Typeface extends Named {
|
public abstract class Typeface extends Named {
|
||||||
|
|
||||||
public static class Style {
|
public static class Style {
|
||||||
public static final int BOLD = 1 << 0, ITALIC = 1 << 1, UNDERLINED = 1 << 2, STRIKETHRU = 1 << 3,
|
public static final int BOLD = 1 << 0,
|
||||||
SHADOW = 1 << 4, OUTLINED = 1 << 5;
|
ITALIC = 1 << 1,
|
||||||
|
UNDERLINED = 1 << 2,
|
||||||
|
STRIKETHRU = 1 << 3,
|
||||||
|
SHADOW = 1 << 4,
|
||||||
|
OUTLINED = 1 << 5;
|
||||||
|
|
||||||
public static final int PLAIN = 0;
|
public static final int PLAIN = 0;
|
||||||
|
|
||||||
@@ -67,19 +71,40 @@ public abstract class Typeface extends Named {
|
|||||||
super(name);
|
super(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Renderable assembleStatic(CharSequence chars, int style, float align, float maxWidth, Vec4 color);
|
public abstract Renderable assembleStatic(
|
||||||
|
CharSequence chars,
|
||||||
|
int style,
|
||||||
|
float align,
|
||||||
|
float maxWidth,
|
||||||
|
Vec4 color
|
||||||
|
);
|
||||||
|
|
||||||
public abstract Renderable assembleDynamic(Supplier<CharSequence> supplier, int style, float align, float maxWidth,
|
public abstract Renderable assembleDynamic(
|
||||||
Vec4 color);
|
Supplier<CharSequence> supplier,
|
||||||
|
int style,
|
||||||
|
float align,
|
||||||
|
float maxWidth,
|
||||||
|
Vec4 color
|
||||||
|
);
|
||||||
|
|
||||||
public int getWidth(CharSequence chars, int style, float align, float maxWidth) {
|
public int getWidth(
|
||||||
|
CharSequence chars,
|
||||||
|
int style,
|
||||||
|
float align,
|
||||||
|
float maxWidth
|
||||||
|
) {
|
||||||
Vec2i v = Vectors.grab2i();
|
Vec2i v = Vectors.grab2i();
|
||||||
v = getSize(chars, style, align, maxWidth, v);
|
v = getSize(chars, style, align, maxWidth, v);
|
||||||
Vectors.release(v);
|
Vectors.release(v);
|
||||||
return v.x;
|
return v.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHeight(CharSequence chars, int style, float align, float maxWidth) {
|
public int getHeight(
|
||||||
|
CharSequence chars,
|
||||||
|
int style,
|
||||||
|
float align,
|
||||||
|
float maxWidth
|
||||||
|
) {
|
||||||
Vec2i v = Vectors.grab2i();
|
Vec2i v = Vectors.grab2i();
|
||||||
v = getSize(chars, style, align, maxWidth, v);
|
v = getSize(chars, style, align, maxWidth, v);
|
||||||
Vectors.release(v);
|
Vectors.release(v);
|
||||||
@@ -88,7 +113,13 @@ public abstract class Typeface extends Named {
|
|||||||
|
|
||||||
public abstract int getLineHeight();
|
public abstract int getLineHeight();
|
||||||
|
|
||||||
public abstract Vec2i getSize(CharSequence chars, int style, float align, float maxWidth, Vec2i result);
|
public abstract Vec2i getSize(
|
||||||
|
CharSequence chars,
|
||||||
|
int style,
|
||||||
|
float align,
|
||||||
|
float maxWidth,
|
||||||
|
Vec2i result
|
||||||
|
);
|
||||||
|
|
||||||
public abstract boolean supports(char c);
|
public abstract boolean supports(char c);
|
||||||
|
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.gui;
|
||||||
|
|
||||||
|
import glm.mat._4.Mat4;
|
||||||
|
import glm.vec._3.Vec3;
|
||||||
|
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
|
|
||||||
|
public class Background extends GUILayer {
|
||||||
|
|
||||||
|
protected Texture backgroundTexture;
|
||||||
|
|
||||||
|
public Background(String name, Layout layout, Texture inTexture) {
|
||||||
|
super(name, layout);
|
||||||
|
|
||||||
|
backgroundTexture = inTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assemble(RenderTarget target) {
|
||||||
|
getRoot().setBounds(0, 0, getWidth(), getHeight());
|
||||||
|
getRoot().invalidate();
|
||||||
|
target.pushTransform(new Mat4(1).translate(new Vec3(0,0,500)));
|
||||||
|
target.drawTexture(0, 0, getWidth(), getHeight(), backgroundTexture);
|
||||||
|
target.popTransform();
|
||||||
|
getRoot().assemble(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -43,9 +43,9 @@ public abstract class BasicButton extends Component {
|
|||||||
private boolean isPressed = false;
|
private boolean isPressed = false;
|
||||||
private final Collection<Consumer<BasicButton>> actions = Collections.synchronizedCollection(new ArrayList<>());
|
private final Collection<Consumer<BasicButton>> actions = Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
public BasicButton(String name, String label, Font labelFont) {
|
public BasicButton(String name, Label label) {
|
||||||
super(name);
|
super(name);
|
||||||
this.label = new Label(name + ".Label", labelFont, label);
|
this.label = label;
|
||||||
|
|
||||||
setLayout(new LayoutAlign(10));
|
setLayout(new LayoutAlign(10));
|
||||||
addChild(this.label);
|
addChild(this.label);
|
||||||
@@ -54,18 +54,17 @@ public abstract class BasicButton extends Component {
|
|||||||
reassembleAt(ARTrigger.HOVER, ARTrigger.FOCUS, ARTrigger.ENABLE);
|
reassembleAt(ARTrigger.HOVER, ARTrigger.FOCUS, ARTrigger.ENABLE);
|
||||||
|
|
||||||
// Click triggers
|
// Click triggers
|
||||||
addListener(KeyEvent.class, e -> {
|
addInputListener(KeyEvent.class, e -> {
|
||||||
if (e.isRepeat()) {
|
if (e.isRepeat())
|
||||||
return false;
|
return;
|
||||||
} else if (
|
|
||||||
|
if (
|
||||||
e.isLeftMouseButton() ||
|
e.isLeftMouseButton() ||
|
||||||
e.getKey() == GLFW.GLFW_KEY_SPACE ||
|
e.getKey() == GLFW.GLFW_KEY_SPACE ||
|
||||||
e.getKey() == GLFW.GLFW_KEY_ENTER
|
e.getKey() == GLFW.GLFW_KEY_ENTER
|
||||||
) {
|
) {
|
||||||
setPressed(e.isPress());
|
setPressed(e.isPress());
|
||||||
return true;
|
e.consume();
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -104,6 +103,10 @@ public abstract class BasicButton extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BasicButton(String name, String label, Font labelFont) {
|
||||||
|
this(name, new Label(name + ".Label", labelFont, label));
|
||||||
|
}
|
||||||
|
|
||||||
public BasicButton(String name, String label) {
|
public BasicButton(String name, String label) {
|
||||||
this(name, label, new Font());
|
this(name, label, new Font());
|
||||||
}
|
}
|
||||||
@@ -120,6 +123,7 @@ public abstract class BasicButton extends Component {
|
|||||||
public void setPressed(boolean isPressed) {
|
public void setPressed(boolean isPressed) {
|
||||||
if (this.isPressed != isPressed) {
|
if (this.isPressed != isPressed) {
|
||||||
this.isPressed = isPressed;
|
this.isPressed = isPressed;
|
||||||
|
requestReassembly();
|
||||||
|
|
||||||
if (isPressed) {
|
if (isPressed) {
|
||||||
takeFocus();
|
takeFocus();
|
||||||
|
@@ -23,12 +23,20 @@ import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
|
|||||||
import ru.windcorp.progressia.client.graphics.font.Font;
|
import ru.windcorp.progressia.client.graphics.font.Font;
|
||||||
import ru.windcorp.progressia.client.graphics.Colors;
|
import ru.windcorp.progressia.client.graphics.Colors;
|
||||||
|
|
||||||
|
/** Class for a traditional button that gets clicked to activate
|
||||||
|
*
|
||||||
|
* @author opfromthestart
|
||||||
|
*/
|
||||||
public class Button extends BasicButton {
|
public class Button extends BasicButton {
|
||||||
|
|
||||||
public Button(String name, String label, Font labelFont) {
|
public Button(String name, String label, Font labelFont) {
|
||||||
super(name, label, labelFont);
|
super(name, label, labelFont);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Button(String name, Label label) {
|
||||||
|
super(name, label);
|
||||||
|
}
|
||||||
|
|
||||||
public Button(String name, String label) {
|
public Button(String name, String label) {
|
||||||
this(name, label, new Font());
|
this(name, label, new Font());
|
||||||
}
|
}
|
||||||
@@ -47,9 +55,7 @@ public class Button extends BasicButton {
|
|||||||
|
|
||||||
// Inside area
|
// Inside area
|
||||||
|
|
||||||
if (isPressed()) {
|
if (!isPressed()) {
|
||||||
// Do nothing
|
|
||||||
} else {
|
|
||||||
Vec4 backgroundColor;
|
Vec4 backgroundColor;
|
||||||
if (isHovered() && isEnabled()) {
|
if (isHovered() && isEnabled()) {
|
||||||
backgroundColor = Colors.HOVER_BLUE;
|
backgroundColor = Colors.HOVER_BLUE;
|
||||||
|
@@ -25,8 +25,6 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import org.lwjgl.glfw.GLFW;
|
|
||||||
|
|
||||||
import com.google.common.eventbus.EventBus;
|
import com.google.common.eventbus.EventBus;
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
@@ -39,9 +37,10 @@ import ru.windcorp.progressia.client.graphics.gui.event.EnableEvent;
|
|||||||
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
|
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
|
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.event.ParentChangedEvent;
|
import ru.windcorp.progressia.client.graphics.gui.event.ParentChangedEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.bus.Input;
|
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||||
import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
|
import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
|
||||||
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
|
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
|
||||||
import ru.windcorp.progressia.common.util.Named;
|
import ru.windcorp.progressia.common.util.Named;
|
||||||
@@ -55,7 +54,7 @@ public class Component extends Named {
|
|||||||
private Component parent = null;
|
private Component parent = null;
|
||||||
|
|
||||||
private EventBus eventBus = null;
|
private EventBus eventBus = null;
|
||||||
private InputBus inputBus = null;
|
private final InputBus inputBus = new InputBus(this);
|
||||||
|
|
||||||
private int x, y;
|
private int x, y;
|
||||||
private int width, height;
|
private int width, height;
|
||||||
@@ -76,6 +75,9 @@ public class Component extends Named {
|
|||||||
|
|
||||||
public Component(String name) {
|
public Component(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
|
|
||||||
|
// Update hover flag when cursor moves
|
||||||
|
addInputListener(CursorMoveEvent.class, this::updateHoverFlag, InputBus.Option.ALWAYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Component getParent() {
|
public Component getParent() {
|
||||||
@@ -522,6 +524,10 @@ public class Component extends Named {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateHoverFlag(CursorMoveEvent e) {
|
||||||
|
setHovered(contains((int) InputTracker.getCursorX(), (int) InputTracker.getCursorY()));
|
||||||
|
}
|
||||||
|
|
||||||
public void addListener(Object listener) {
|
public void addListener(Object listener) {
|
||||||
if (eventBus == null) {
|
if (eventBus == null) {
|
||||||
eventBus = ReportingEventBus.create(getName());
|
eventBus = ReportingEventBus.create(getName());
|
||||||
@@ -542,118 +548,32 @@ public class Component extends Named {
|
|||||||
eventBus.post(event);
|
eventBus.post(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends InputEvent> void addListener(Class<? extends T> type, boolean handlesConsumed,
|
public <T extends InputEvent> void addInputListener(Class<? extends T> type, InputListener<T> listener, InputBus.Option... options) {
|
||||||
InputListener<T> listener) {
|
inputBus.register(type, listener, options);
|
||||||
if (inputBus == null) {
|
|
||||||
inputBus = new InputBus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputBus.register(type, handlesConsumed, listener);
|
public void addKeyListener(KeyMatcher matcher, InputListener<? super KeyEvent> listener, InputBus.Option... options) {
|
||||||
|
inputBus.register(matcher, listener, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends InputEvent> void addListener(Class<? extends T> type, InputListener<T> listener) {
|
public void removeInputListener(InputListener<?> listener) {
|
||||||
if (inputBus == null) {
|
|
||||||
inputBus = new InputBus();
|
|
||||||
}
|
|
||||||
|
|
||||||
inputBus.register(type, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeListener(InputListener<?> listener) {
|
|
||||||
if (inputBus != null) {
|
if (inputBus != null) {
|
||||||
inputBus.unregister(listener);
|
inputBus.unregister(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleInput(Input input) {
|
protected boolean passInputToChildren(InputEvent e) {
|
||||||
if (inputBus != null && isEnabled()) {
|
|
||||||
inputBus.dispatch(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispatchInput(Input input) {
|
|
||||||
try {
|
|
||||||
switch (input.getTarget()) {
|
|
||||||
case FOCUSED:
|
|
||||||
dispatchInputToFocused(input);
|
|
||||||
break;
|
|
||||||
case HOVERED:
|
|
||||||
dispatchInputToHovered(input);
|
|
||||||
break;
|
|
||||||
case ALL:
|
|
||||||
default:
|
|
||||||
dispatchInputToAll(input);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw CrashReports.report(e, "Could not dispatch input to Component %s", this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dispatchInputToFocused(Input input) {
|
|
||||||
Component c = findFocused();
|
|
||||||
|
|
||||||
if (c == null)
|
|
||||||
return;
|
|
||||||
if (attemptFocusTransfer(input, c))
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (c != null) {
|
|
||||||
c.handleInput(input);
|
|
||||||
c = c.getParent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dispatchInputToHovered(Input input) {
|
|
||||||
getChildren().forEach(child -> {
|
|
||||||
if (child.containsCursor()) {
|
|
||||||
child.setHovered(true);
|
|
||||||
|
|
||||||
if (!input.isConsumed()) {
|
|
||||||
child.dispatchInput(input);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
child.setHovered(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
handleInput(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dispatchInputToAll(Input input) {
|
|
||||||
getChildren().forEach(c -> c.dispatchInput(input));
|
|
||||||
handleInput(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean attemptFocusTransfer(Input input, Component focused) {
|
|
||||||
if (input.isConsumed())
|
|
||||||
return false;
|
|
||||||
if (!(input.getEvent() instanceof KeyEvent))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
KeyEvent keyInput = (KeyEvent) input.getEvent();
|
|
||||||
|
|
||||||
if (keyInput.getKey() == GLFW.GLFW_KEY_TAB && !keyInput.isRelease()) {
|
|
||||||
input.consume();
|
|
||||||
if (keyInput.hasShift()) {
|
|
||||||
focused.focusPrevious();
|
|
||||||
} else {
|
|
||||||
focused.focusNext();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
InputBus getInputBus() {
|
||||||
|
return inputBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean contains(int x, int y) {
|
public synchronized boolean contains(int x, int y) {
|
||||||
return x >= getX() && x < getX() + getWidth() && y >= getY() && y < getY() + getHeight();
|
return x >= getX() && x < getX() + getWidth() && y >= getY() && y < getY() + getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsCursor() {
|
|
||||||
return contains((int) InputTracker.getCursorX(), (int) InputTracker.getCursorY());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void requestReassembly() {
|
public void requestReassembly() {
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parent.requestReassembly();
|
parent.requestReassembly();
|
||||||
@@ -804,7 +724,7 @@ public class Component extends Named {
|
|||||||
for (ARTrigger trigger : triggers) {
|
for (ARTrigger trigger : triggers) {
|
||||||
if (!autoReassemblyTriggerObjects.containsKey(trigger)) {
|
if (!autoReassemblyTriggerObjects.containsKey(trigger)) {
|
||||||
Object triggerObject = createTriggerObject(trigger);
|
Object triggerObject = createTriggerObject(trigger);
|
||||||
addListener(trigger);
|
addListener(triggerObject);
|
||||||
autoReassemblyTriggerObjects.put(trigger, triggerObject);
|
autoReassemblyTriggerObjects.put(trigger, triggerObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package ru.windcorp.progressia.client.graphics.gui;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import glm.vec._2.d.Vec2d;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.DragEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.DragStartEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.DragStopEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
|
||||||
|
|
||||||
|
public class DragManager {
|
||||||
|
|
||||||
|
private Component component;
|
||||||
|
|
||||||
|
private boolean isDragged = false;
|
||||||
|
private final Vec2d change = new Vec2d();
|
||||||
|
|
||||||
|
public void install(Component c) {
|
||||||
|
Objects.requireNonNull(c, "c");
|
||||||
|
if (c == component) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (component != null) {
|
||||||
|
throw new IllegalStateException("Already installed on " + component + "; attempted to install on " + c);
|
||||||
|
}
|
||||||
|
|
||||||
|
component = c;
|
||||||
|
|
||||||
|
c.addInputListener(CursorMoveEvent.class, this::onCursorMove, InputBus.Option.ALWAYS);
|
||||||
|
c.addKeyListener(KeyMatcher.LMB, this::onLMB, InputBus.Option.ALWAYS, InputBus.Option.IGNORE_ACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onCursorMove(CursorMoveEvent e) {
|
||||||
|
if (isDragged) {
|
||||||
|
Vec2d currentChange = e.getChange(null);
|
||||||
|
change.add(currentChange);
|
||||||
|
component.dispatchEvent(new DragEvent(component, currentChange, change));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onLMB(KeyEvent e) {
|
||||||
|
if (isDragged && e.isRelease()) {
|
||||||
|
|
||||||
|
isDragged = false;
|
||||||
|
component.dispatchEvent(new DragStopEvent(component, change));
|
||||||
|
|
||||||
|
} else if (!isDragged && !e.isConsumed() && e.isPress() && component.isHovered()) {
|
||||||
|
|
||||||
|
isDragged = true;
|
||||||
|
change.set(0, 0);
|
||||||
|
component.dispatchEvent(new DragStartEvent(component));
|
||||||
|
e.consume();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -18,9 +18,17 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.graphics.gui;
|
package ru.windcorp.progressia.client.graphics.gui;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.flat.AssembledFlatLayer;
|
import ru.windcorp.progressia.client.graphics.flat.AssembledFlatLayer;
|
||||||
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
|
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
|
||||||
import ru.windcorp.progressia.client.graphics.input.bus.Input;
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
|
||||||
|
import ru.windcorp.progressia.common.util.LowOverheadCache;
|
||||||
|
import ru.windcorp.progressia.common.util.StashingStack;
|
||||||
|
|
||||||
public abstract class GUILayer extends AssembledFlatLayer {
|
public abstract class GUILayer extends AssembledFlatLayer {
|
||||||
|
|
||||||
@@ -33,7 +41,9 @@ public abstract class GUILayer extends AssembledFlatLayer {
|
|||||||
|
|
||||||
public GUILayer(String name, Layout layout) {
|
public GUILayer(String name, Layout layout) {
|
||||||
super(name);
|
super(name);
|
||||||
|
|
||||||
getRoot().setLayout(layout);
|
getRoot().setLayout(layout);
|
||||||
|
getRoot().addInputListener(KeyEvent.class, this::attemptFocusTransfer, InputBus.Option.IGNORE_FOCUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Component getRoot() {
|
public Component getRoot() {
|
||||||
@@ -47,9 +57,85 @@ public abstract class GUILayer extends AssembledFlatLayer {
|
|||||||
getRoot().assemble(target);
|
getRoot().assemble(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stack frame for {@link #handleInput(InputEvent)}.
|
||||||
|
*/
|
||||||
|
private static class EventHandlingFrame {
|
||||||
|
Component component;
|
||||||
|
Iterator<Component> children;
|
||||||
|
|
||||||
|
void init(Component c) {
|
||||||
|
component = c;
|
||||||
|
children = c.getChildren().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
component = null;
|
||||||
|
children = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stacks for {@link #handleInput(InputEvent)}.
|
||||||
|
*/
|
||||||
|
private final LowOverheadCache<StashingStack<EventHandlingFrame>> pathCache = new LowOverheadCache<>(
|
||||||
|
() -> new StashingStack<>(64, EventHandlingFrame::new)
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is essentially a depth-first iteration of the component tree. The
|
||||||
|
* recursive procedure has been unrolled to reduce call stack length.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void handleInput(Input input) {
|
public void handleInput(InputEvent event) {
|
||||||
getRoot().dispatchInput(input);
|
StashingStack<EventHandlingFrame> path = pathCache.grab();
|
||||||
|
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
throw new IllegalStateException("path is not empty: " + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.push().init(root);
|
||||||
|
|
||||||
|
while (!path.isEmpty()) {
|
||||||
|
|
||||||
|
Iterator<Component> it = path.peek().children;
|
||||||
|
if (it.hasNext()) {
|
||||||
|
|
||||||
|
Component c = it.next();
|
||||||
|
|
||||||
|
if (c.isEnabled()) {
|
||||||
|
if (c.getChildren().isEmpty() || !c.passInputToChildren(event)) {
|
||||||
|
c.getInputBus().dispatch(event);
|
||||||
|
} else {
|
||||||
|
path.push().init(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
path.peek().component.getInputBus().dispatch(event);
|
||||||
|
path.pop().reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pathCache.release(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attemptFocusTransfer(KeyEvent e) {
|
||||||
|
Component focused = getRoot().findFocused();
|
||||||
|
|
||||||
|
if (focused == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.getKey() == GLFW.GLFW_KEY_TAB && !e.isRelease()) {
|
||||||
|
e.consume();
|
||||||
|
if (e.hasShift()) {
|
||||||
|
focused.focusPrevious();
|
||||||
|
} else {
|
||||||
|
focused.focusNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
0
src/main/java/ru/windcorp/progressia/client/graphics/gui/Group.java
Normal file → Executable file
0
src/main/java/ru/windcorp/progressia/client/graphics/gui/Group.java
Normal file → Executable file
@@ -85,7 +85,6 @@ public class Label extends Component {
|
|||||||
|
|
||||||
public void setFont(Font font) {
|
public void setFont(Font font) {
|
||||||
this.font = font;
|
this.font = font;
|
||||||
requestReassembly();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCurrentText() {
|
public String getCurrentText() {
|
||||||
|
0
src/main/java/ru/windcorp/progressia/client/graphics/gui/Panel.java
Executable file → Normal file
0
src/main/java/ru/windcorp/progressia/client/graphics/gui/Panel.java
Executable file → Normal file
@@ -104,25 +104,22 @@ public class RadioButton extends BasicButton {
|
|||||||
group.addChild(basicChild);
|
group.addChild(basicChild);
|
||||||
addChild(group);
|
addChild(group);
|
||||||
|
|
||||||
addListener(KeyEvent.class, e -> {
|
addInputListener(KeyEvent.class, e -> {
|
||||||
if (e.isRelease()) return false;
|
if (e.isRelease()) return;
|
||||||
|
|
||||||
if (e.getKey() == GLFW.GLFW_KEY_LEFT || e.getKey() == GLFW.GLFW_KEY_UP) {
|
if (e.getKey() == GLFW.GLFW_KEY_LEFT || e.getKey() == GLFW.GLFW_KEY_UP) {
|
||||||
if (this.group != null) {
|
if (this.group != null) {
|
||||||
this.group.selectPrevious();
|
this.group.selectPrevious();
|
||||||
this.group.getSelected().takeFocus();
|
this.group.getSelected().takeFocus();
|
||||||
}
|
}
|
||||||
|
e.consume();
|
||||||
return true;
|
|
||||||
} else if (e.getKey() == GLFW.GLFW_KEY_RIGHT || e.getKey() == GLFW.GLFW_KEY_DOWN) {
|
} else if (e.getKey() == GLFW.GLFW_KEY_RIGHT || e.getKey() == GLFW.GLFW_KEY_DOWN) {
|
||||||
if (this.group != null) {
|
if (this.group != null) {
|
||||||
this.group.selectNext();
|
this.group.selectNext();
|
||||||
this.group.getSelected().takeFocus();
|
this.group.getSelected().takeFocus();
|
||||||
}
|
}
|
||||||
return true;
|
e.consume();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
addAction(b -> setChecked(true));
|
addAction(b -> setChecked(true));
|
||||||
@@ -149,7 +146,8 @@ public class RadioButton extends BasicButton {
|
|||||||
group.selectNext();
|
group.selectNext();
|
||||||
removeAction(group.listener);
|
removeAction(group.listener);
|
||||||
group.buttons.remove(this);
|
group.buttons.remove(this);
|
||||||
group.getSelected(); // Clear reference if this was the only button in the group
|
group.getSelected(); // Clear reference if this was the only button
|
||||||
|
// in the group
|
||||||
}
|
}
|
||||||
|
|
||||||
this.group = group;
|
this.group = group;
|
||||||
@@ -178,7 +176,8 @@ public class RadioButton extends BasicButton {
|
|||||||
this.checked = checked;
|
this.checked = checked;
|
||||||
|
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
group.listener.accept(this); // Failsafe for manual invocations of setChecked()
|
group.listener.accept(this); // Failsafe for manual invocations of
|
||||||
|
// setChecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.gui;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
|
|
||||||
|
public class TextureComponent extends Component {
|
||||||
|
|
||||||
|
private final Texture texture;
|
||||||
|
|
||||||
|
public TextureComponent(String name, Texture texture2) {
|
||||||
|
super(name);
|
||||||
|
|
||||||
|
texture = texture2;
|
||||||
|
setPreferredSize(texture.getSprite().getWidth(),texture.getSprite().getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assembleSelf(RenderTarget target)
|
||||||
|
{
|
||||||
|
target.drawTexture(getX(), getY(), getWidth(), getHeight(), texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package ru.windcorp.progressia.client.graphics.gui.event;
|
||||||
|
|
||||||
|
import glm.vec._2.d.Vec2d;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Component;
|
||||||
|
|
||||||
|
public class DragEvent extends ComponentEvent {
|
||||||
|
|
||||||
|
private final Vec2d currentChange = new Vec2d();
|
||||||
|
private final Vec2d totalChange = new Vec2d();
|
||||||
|
|
||||||
|
public DragEvent(Component component, Vec2d currentChange, Vec2d totalChange) {
|
||||||
|
super(component);
|
||||||
|
this.currentChange.set(currentChange.x, currentChange.y);
|
||||||
|
this.totalChange.set(totalChange.x, totalChange.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec2d getCurrentChange() {
|
||||||
|
return currentChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCurrentChangeX() {
|
||||||
|
return currentChange.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCurrentChangeY() {
|
||||||
|
return currentChange.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec2d getTotalChange() {
|
||||||
|
return totalChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTotalChangeX() {
|
||||||
|
return totalChange.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTotalChangeY() {
|
||||||
|
return totalChange.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package ru.windcorp.progressia.client.graphics.gui.event;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Component;
|
||||||
|
|
||||||
|
public class DragStartEvent extends ComponentEvent {
|
||||||
|
|
||||||
|
public DragStartEvent(Component component) {
|
||||||
|
super(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -15,48 +15,30 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
package ru.windcorp.progressia.client.graphics.gui.event;
|
||||||
|
|
||||||
package ru.windcorp.progressia.client.graphics.input.bus;
|
import glm.vec._2.d.Vec2d;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Component;
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
public class DragStopEvent extends ComponentEvent {
|
||||||
|
|
||||||
public class Input {
|
private final Vec2d totalChange = new Vec2d();
|
||||||
|
|
||||||
public static enum Target {
|
public DragStopEvent(Component component, Vec2d totalChange) {
|
||||||
FOCUSED, HOVERED, ALL
|
super(component);
|
||||||
|
this.totalChange.set(totalChange.x, totalChange.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputEvent event;
|
public Vec2d getTotalChange() {
|
||||||
|
return totalChange;
|
||||||
private boolean isConsumed;
|
|
||||||
|
|
||||||
private Target target;
|
|
||||||
|
|
||||||
protected void initialize(InputEvent event, Target target) {
|
|
||||||
this.event = event;
|
|
||||||
this.target = target;
|
|
||||||
|
|
||||||
this.isConsumed = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputEvent getEvent() {
|
public double getTotalChangeX() {
|
||||||
return event;
|
return totalChange.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConsumed() {
|
public double getTotalChangeY() {
|
||||||
return isConsumed;
|
return totalChange.y;
|
||||||
}
|
|
||||||
|
|
||||||
public void setConsumed(boolean isConsumed) {
|
|
||||||
this.isConsumed = isConsumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void consume() {
|
|
||||||
setConsumed(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Target getTarget() {
|
|
||||||
return target;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -27,8 +27,8 @@ import ru.windcorp.progressia.client.graphics.gui.Layout;
|
|||||||
|
|
||||||
public class LayoutAlign implements Layout {
|
public class LayoutAlign implements Layout {
|
||||||
|
|
||||||
private final int margin;
|
protected final int margin;
|
||||||
private double alignX, alignY;
|
protected double alignX, alignY;
|
||||||
|
|
||||||
public LayoutAlign(double alignX, double alignY, int margin) {
|
public LayoutAlign(double alignX, double alignY, int margin) {
|
||||||
this.alignX = alignX;
|
this.alignX = alignX;
|
||||||
@@ -56,8 +56,13 @@ public class LayoutAlign implements Layout {
|
|||||||
size.x = min(size.x, cWidth);
|
size.x = min(size.x, cWidth);
|
||||||
size.y = min(size.y, cHeight);
|
size.y = min(size.y, cHeight);
|
||||||
|
|
||||||
child.setBounds(c.getX() + (int) ((cWidth - size.x) * alignX) + margin,
|
child.setBounds(
|
||||||
c.getY() + (int) ((cHeight - size.y) * alignY) + margin, size);
|
c.getX() +
|
||||||
|
(int) ((cWidth - size.x) * alignX) + margin,
|
||||||
|
c.getY() +
|
||||||
|
(int) ((cHeight - size.y) * alignY) + margin,
|
||||||
|
size
|
||||||
|
);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -66,7 +71,9 @@ public class LayoutAlign implements Layout {
|
|||||||
public Vec2i calculatePreferredSize(Component c) {
|
public Vec2i calculatePreferredSize(Component c) {
|
||||||
Vec2i result = new Vec2i(0, 0);
|
Vec2i result = new Vec2i(0, 0);
|
||||||
|
|
||||||
c.getChildren().stream().map(child -> child.getPreferredSize()).forEach(size -> {
|
c.getChildren().stream()
|
||||||
|
.map(Component::getPreferredSize)
|
||||||
|
.forEach(size -> {
|
||||||
result.x = max(size.x, result.x);
|
result.x = max(size.x, result.x);
|
||||||
result.y = max(size.y, result.y);
|
result.y = max(size.y, result.y);
|
||||||
});
|
});
|
||||||
|
@@ -26,7 +26,9 @@ import ru.windcorp.progressia.client.graphics.gui.Layout;
|
|||||||
|
|
||||||
public class LayoutBorderHorizontal implements Layout {
|
public class LayoutBorderHorizontal implements Layout {
|
||||||
|
|
||||||
public static final String CENTER = "Center", LEFT = "Left", RIGHT = "Right";
|
public static final String CENTER = "Center",
|
||||||
|
LEFT = "Left",
|
||||||
|
RIGHT = "Right";
|
||||||
|
|
||||||
private final int margin;
|
private final int margin;
|
||||||
|
|
||||||
@@ -49,17 +51,32 @@ public class LayoutBorderHorizontal implements Layout {
|
|||||||
if (child.getLayoutHint() == LEFT) {
|
if (child.getLayoutHint() == LEFT) {
|
||||||
childSize = child.getPreferredSize();
|
childSize = child.getPreferredSize();
|
||||||
left = childSize.x + margin;
|
left = childSize.x + margin;
|
||||||
child.setBounds(c.getX(), c.getY(), childSize.x, c.getHeight());
|
child.setBounds(
|
||||||
|
c.getX(),
|
||||||
|
c.getY(),
|
||||||
|
childSize.x,
|
||||||
|
c.getHeight()
|
||||||
|
);
|
||||||
} else if (child.getLayoutHint() == RIGHT) {
|
} else if (child.getLayoutHint() == RIGHT) {
|
||||||
childSize = child.getPreferredSize();
|
childSize = child.getPreferredSize();
|
||||||
right = childSize.x + margin;
|
right = childSize.x + margin;
|
||||||
child.setBounds(c.getX() + c.getWidth() - childSize.x, c.getY(), childSize.x, c.getHeight());
|
child.setBounds(
|
||||||
|
c.getX() + c.getWidth() - childSize.x,
|
||||||
|
c.getY(),
|
||||||
|
childSize.x,
|
||||||
|
c.getHeight()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Component child : c.getChildren()) {
|
for (Component child : c.getChildren()) {
|
||||||
if (child.getLayoutHint() == CENTER) {
|
if (child.getLayoutHint() == CENTER) {
|
||||||
child.setBounds(c.getX() + left, c.getY(), c.getWidth() - left - right, c.getHeight());
|
child.setBounds(
|
||||||
|
c.getX() + left,
|
||||||
|
c.getY(),
|
||||||
|
c.getWidth() - left - right,
|
||||||
|
c.getHeight()
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,9 @@ import ru.windcorp.progressia.client.graphics.gui.Layout;
|
|||||||
|
|
||||||
public class LayoutBorderVertical implements Layout {
|
public class LayoutBorderVertical implements Layout {
|
||||||
|
|
||||||
public static final String CENTER = "Center", UP = "Up", DOWN = "Down";
|
public static final String CENTER = "Center",
|
||||||
|
UP = "Up",
|
||||||
|
DOWN = "Down";
|
||||||
|
|
||||||
private final int margin;
|
private final int margin;
|
||||||
|
|
||||||
@@ -49,17 +51,32 @@ public class LayoutBorderVertical implements Layout {
|
|||||||
if (child.getLayoutHint() == UP) {
|
if (child.getLayoutHint() == UP) {
|
||||||
childSize = child.getPreferredSize();
|
childSize = child.getPreferredSize();
|
||||||
top = childSize.y + margin;
|
top = childSize.y + margin;
|
||||||
child.setBounds(c.getX(), c.getY(), c.getWidth(), childSize.y);
|
child.setBounds(
|
||||||
|
c.getX(),
|
||||||
|
c.getY(),
|
||||||
|
c.getWidth(),
|
||||||
|
childSize.y
|
||||||
|
);
|
||||||
} else if (child.getLayoutHint() == DOWN) {
|
} else if (child.getLayoutHint() == DOWN) {
|
||||||
childSize = child.getPreferredSize();
|
childSize = child.getPreferredSize();
|
||||||
bottom = childSize.y + margin;
|
bottom = childSize.y + margin;
|
||||||
child.setBounds(c.getX(), c.getY() + c.getHeight() - childSize.y, c.getWidth(), childSize.y);
|
child.setBounds(
|
||||||
|
c.getX(),
|
||||||
|
c.getY() + c.getHeight() - childSize.y,
|
||||||
|
c.getWidth(),
|
||||||
|
childSize.y
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Component child : c.getChildren()) {
|
for (Component child : c.getChildren()) {
|
||||||
if (child.getLayoutHint() == CENTER) {
|
if (child.getLayoutHint() == CENTER) {
|
||||||
child.setBounds(c.getX(), c.getY() + top, c.getWidth(), c.getHeight() - top - bottom);
|
child.setBounds(
|
||||||
|
c.getX(),
|
||||||
|
c.getY() + top,
|
||||||
|
c.getWidth(),
|
||||||
|
c.getHeight() - top - bottom
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,56 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.gui.layout;
|
||||||
|
|
||||||
|
import glm.vec._2.i.Vec2i;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Component;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Layout;
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
|
public class LayoutEdges implements Layout {
|
||||||
|
|
||||||
|
private final int margin;
|
||||||
|
|
||||||
|
public LayoutEdges(int margin) {
|
||||||
|
this.margin = margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void layout(Component c) {
|
||||||
|
for (int i=0;i<2;i++)
|
||||||
|
{
|
||||||
|
Component child = c.getChild(i);
|
||||||
|
|
||||||
|
Vec2i size = child.getPreferredSize();
|
||||||
|
|
||||||
|
int cWidth = c.getWidth() - 2 * margin;
|
||||||
|
int cHeight = c.getHeight() - 2 * margin;
|
||||||
|
|
||||||
|
size.x = min(size.x, cWidth);
|
||||||
|
size.y = min(size.y, cHeight);
|
||||||
|
|
||||||
|
if (i==0) {
|
||||||
|
child.setBounds(
|
||||||
|
c.getX() + margin,
|
||||||
|
c.getY(),
|
||||||
|
size
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
child.setBounds(
|
||||||
|
1920 - size.x - margin,
|
||||||
|
c.getY(),
|
||||||
|
size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec2i calculatePreferredSize(Component c) {
|
||||||
|
Vec2i result = new Vec2i(1920,0);
|
||||||
|
c.getChildren().stream()
|
||||||
|
.map(Component::getPreferredSize)
|
||||||
|
.forEach(size -> result.y = max(Math.abs(size.y), result.y));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@@ -43,7 +43,8 @@ public class LayoutHorizontal implements Layout {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void layout(Component c) {
|
public void layout(Component c) {
|
||||||
int x = c.getX() + margin, y = c.getY() + margin;
|
int x = c.getX() + margin,
|
||||||
|
y = c.getY() + margin;
|
||||||
|
|
||||||
int width;
|
int width;
|
||||||
|
|
||||||
|
@@ -43,7 +43,8 @@ public class LayoutVertical implements Layout {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void layout(Component c) {
|
public void layout(Component c) {
|
||||||
int x = c.getX() + margin, y = c.getY() + c.getHeight();
|
int x = c.getX() + margin,
|
||||||
|
y = c.getY() + c.getHeight();
|
||||||
|
|
||||||
synchronized (c.getChildren()) {
|
synchronized (c.getChildren()) {
|
||||||
for (Component child : c.getChildren()) {
|
for (Component child : c.getChildren()) {
|
||||||
|
@@ -33,7 +33,6 @@ import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
|
|||||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
||||||
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.bus.Input;
|
|
||||||
import ru.windcorp.progressia.client.localization.MutableString;
|
import ru.windcorp.progressia.client.localization.MutableString;
|
||||||
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
|
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
|
||||||
|
|
||||||
@@ -97,11 +96,9 @@ public class MenuLayer extends GUILayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleInput(Input input) {
|
public void handleInput(InputEvent event) {
|
||||||
|
|
||||||
if (!input.isConsumed()) {
|
|
||||||
InputEvent event = input.getEvent();
|
|
||||||
|
|
||||||
|
if (!event.isConsumed()) {
|
||||||
if (event instanceof KeyEvent) {
|
if (event instanceof KeyEvent) {
|
||||||
KeyEvent keyEvent = (KeyEvent) event;
|
KeyEvent keyEvent = (KeyEvent) event;
|
||||||
if (keyEvent.isPress() && keyEvent.getKey() == GLFW.GLFW_KEY_ESCAPE) {
|
if (keyEvent.isPress() && keyEvent.getKey() == GLFW.GLFW_KEY_ESCAPE) {
|
||||||
@@ -110,8 +107,8 @@ public class MenuLayer extends GUILayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.handleInput(input);
|
super.handleInput(event);
|
||||||
input.consume();
|
event.consume();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.graphics.input;
|
package ru.windcorp.progressia.client.graphics.input;
|
||||||
|
|
||||||
import glm.vec._2.Vec2;
|
|
||||||
import glm.vec._2.d.Vec2d;
|
import glm.vec._2.d.Vec2d;
|
||||||
|
|
||||||
public class CursorMoveEvent extends CursorEvent {
|
public class CursorMoveEvent extends CursorEvent {
|
||||||
@@ -81,20 +80,31 @@ public class CursorMoveEvent extends CursorEvent {
|
|||||||
return getNewY() - getPreviousY();
|
return getNewY() - getPreviousY();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vec2 getChange(Vec2 result) {
|
public Vec2d getChange(Vec2d result) {
|
||||||
|
if (result == null) {
|
||||||
|
result = new Vec2d();
|
||||||
|
}
|
||||||
return result.set(getChangeX(), getChangeY());
|
return result.set(getChangeX(), getChangeY());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CursorMoveEvent snapshot() {
|
public CursorMoveEvent snapshot() {
|
||||||
return new StaticMouseMoveEvent(getPreviousPosition(), getNewPosition(), getTime());
|
return new StaticMouseMoveEvent(
|
||||||
|
getPreviousPosition(),
|
||||||
|
getNewPosition(),
|
||||||
|
getTime()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class StaticMouseMoveEvent extends CursorMoveEvent {
|
private class StaticMouseMoveEvent extends CursorMoveEvent {
|
||||||
|
|
||||||
private final Vec2d previousPosition = new Vec2d();
|
private final Vec2d previousPosition = new Vec2d();
|
||||||
|
|
||||||
public StaticMouseMoveEvent(Vec2d previousPosition, Vec2d newPosition, double time) {
|
public StaticMouseMoveEvent(
|
||||||
|
Vec2d previousPosition,
|
||||||
|
Vec2d newPosition,
|
||||||
|
double time
|
||||||
|
) {
|
||||||
super(newPosition, time);
|
super(newPosition, time);
|
||||||
this.previousPosition.set(previousPosition.x, previousPosition.y);
|
this.previousPosition.set(previousPosition.x, previousPosition.y);
|
||||||
}
|
}
|
||||||
|
@@ -67,7 +67,11 @@ public class FrameResizeEvent extends InputEvent {
|
|||||||
|
|
||||||
private final Vec2i previousSize;
|
private final Vec2i previousSize;
|
||||||
|
|
||||||
public StaticFrameResizeEvent(Vec2i newSize, Vec2i previousSize, double time) {
|
public StaticFrameResizeEvent(
|
||||||
|
Vec2i newSize,
|
||||||
|
Vec2i previousSize,
|
||||||
|
double time
|
||||||
|
) {
|
||||||
super(newSize, time);
|
super(newSize, time);
|
||||||
this.previousSize = previousSize;
|
this.previousSize = previousSize;
|
||||||
}
|
}
|
||||||
|
@@ -18,10 +18,32 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.graphics.input;
|
package ru.windcorp.progressia.client.graphics.input;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instance of user input.
|
||||||
|
* <p>
|
||||||
|
* User input events are typically generated by graphics backend between frames
|
||||||
|
* and passed to the graphics layers from top to bottom. Layers that use
|
||||||
|
* {@link Component}s will forward this event through the Component hierarchy.
|
||||||
|
* <p>
|
||||||
|
* Events have a {@code consumed} flag. A freshly-generated event will have this
|
||||||
|
* flag set to {@code false}. Event listeners that process the event will
|
||||||
|
* usually choose to raise the flag ("consume the event") to ask future
|
||||||
|
* listeners to ignore this event. This is done to avoid multiple UI interfaces
|
||||||
|
* reacting to single input. By default, listeners will not receive consumed
|
||||||
|
* events; however, some listeners may choose to receive, handle and even
|
||||||
|
* un-consume the event.
|
||||||
|
* <p>
|
||||||
|
* {@code InputEvent} objects may be reused for future input events after their
|
||||||
|
* processing is complete; to obtain a static copy, use {@link #snapshot()}.
|
||||||
|
*/
|
||||||
public abstract class InputEvent {
|
public abstract class InputEvent {
|
||||||
|
|
||||||
private double time;
|
private double time;
|
||||||
|
|
||||||
|
private boolean isConsumed = false;
|
||||||
|
|
||||||
public InputEvent(double time) {
|
public InputEvent(double time) {
|
||||||
this.time = time;
|
this.time = time;
|
||||||
}
|
}
|
||||||
@@ -36,4 +58,16 @@ public abstract class InputEvent {
|
|||||||
|
|
||||||
public abstract InputEvent snapshot();
|
public abstract InputEvent snapshot();
|
||||||
|
|
||||||
|
public boolean isConsumed() {
|
||||||
|
return isConsumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConsumed(boolean isConsumed) {
|
||||||
|
this.isConsumed = isConsumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void consume() {
|
||||||
|
setConsumed(true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -27,7 +27,13 @@ public class KeyEvent extends InputEvent {
|
|||||||
protected int action;
|
protected int action;
|
||||||
protected int mods;
|
protected int mods;
|
||||||
|
|
||||||
protected KeyEvent(int key, int scancode, int action, int mods, double time) {
|
protected KeyEvent(
|
||||||
|
int key,
|
||||||
|
int scancode,
|
||||||
|
int action,
|
||||||
|
int mods,
|
||||||
|
double time
|
||||||
|
) {
|
||||||
super(time);
|
super(time);
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.scancode = scancode;
|
this.scancode = scancode;
|
||||||
|
@@ -18,16 +18,75 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.graphics.input;
|
package ru.windcorp.progressia.client.graphics.input;
|
||||||
|
|
||||||
import java.util.function.Predicate;
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
public class KeyMatcher {
|
public class KeyMatcher {
|
||||||
|
|
||||||
|
private static final Pattern DECLAR_SPLIT_REGEX = Pattern.compile("\\s*\\+\\s*");
|
||||||
|
private static final Map<String, Integer> MOD_TOKENS = ImmutableMap.of(
|
||||||
|
"SHIFT", GLFW.GLFW_MOD_SHIFT,
|
||||||
|
"CONTROL", GLFW.GLFW_MOD_CONTROL,
|
||||||
|
"ALT", GLFW.GLFW_MOD_ALT,
|
||||||
|
"SUPER", GLFW.GLFW_MOD_SUPER
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final KeyMatcher LMB = new KeyMatcher(GLFW.GLFW_MOUSE_BUTTON_LEFT);
|
||||||
|
public static final KeyMatcher RMB = new KeyMatcher(GLFW.GLFW_MOUSE_BUTTON_RIGHT);
|
||||||
|
public static final KeyMatcher MMB = new KeyMatcher(GLFW.GLFW_MOUSE_BUTTON_MIDDLE);
|
||||||
|
|
||||||
private final int key;
|
private final int key;
|
||||||
private final int mods;
|
private final int mods;
|
||||||
|
|
||||||
protected KeyMatcher(int key, int mods) {
|
public KeyMatcher(int key, int mods) {
|
||||||
|
this.key = key;
|
||||||
|
this.mods = mods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyMatcher(int key) {
|
||||||
|
this.key = key;
|
||||||
|
this.mods = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyMatcher(String declar) {
|
||||||
|
String[] tokens = DECLAR_SPLIT_REGEX.split(declar);
|
||||||
|
if (tokens.length == 0) {
|
||||||
|
throw new IllegalArgumentException("No tokens found in \"" + declar + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
int key = -1;
|
||||||
|
int mods = 0;
|
||||||
|
|
||||||
|
for (String token : tokens) {
|
||||||
|
token = token.toUpperCase();
|
||||||
|
|
||||||
|
if (MOD_TOKENS.containsKey(token)) {
|
||||||
|
int mod = MOD_TOKENS.get(token);
|
||||||
|
if ((mods & mod) != 0) {
|
||||||
|
throw new IllegalArgumentException("Duplicate modifier \"" + token + "\" in \"" + declar + "\"");
|
||||||
|
}
|
||||||
|
mods |= mod;
|
||||||
|
} else if (key != -1) {
|
||||||
|
throw new IllegalArgumentException("Too many non-modifier tokens in \"" + declar + "\": maximum one key, first offender: \"" + token + "\"");
|
||||||
|
} else {
|
||||||
|
token = token.replace(' ', '_');
|
||||||
|
|
||||||
|
if (token.startsWith("KEYPAD_")) {
|
||||||
|
token = "KP_" + token.substring("KEYPAD_".length());
|
||||||
|
}
|
||||||
|
|
||||||
|
key = Keys.getCode(token);
|
||||||
|
|
||||||
|
if (key == -1) {
|
||||||
|
throw new IllegalArgumentException("Unknown token \"" + token + "\" in \"" + declar + "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.mods = mods;
|
this.mods = mods;
|
||||||
}
|
}
|
||||||
@@ -43,6 +102,15 @@ public class KeyMatcher {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean matchesIgnoringAction(KeyEvent event) {
|
||||||
|
if (event.getKey() != getKey())
|
||||||
|
return false;
|
||||||
|
if ((event.getMods() & getMods()) != getMods())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public int getKey() {
|
public int getKey() {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
@@ -51,48 +119,24 @@ public class KeyMatcher {
|
|||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static KeyMatcher.Builder of(int key) {
|
public KeyMatcher with(int modifier) {
|
||||||
return new KeyMatcher.Builder(key);
|
return new KeyMatcher(key, mods | modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public KeyMatcher withShift() {
|
||||||
|
|
||||||
private final int key;
|
|
||||||
private int mods = 0;
|
|
||||||
|
|
||||||
public Builder(int key) {
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder with(int modifier) {
|
|
||||||
this.mods += modifier;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder withShift() {
|
|
||||||
return with(GLFW.GLFW_MOD_SHIFT);
|
return with(GLFW.GLFW_MOD_SHIFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withCtrl() {
|
public KeyMatcher withCtrl() {
|
||||||
return with(GLFW.GLFW_MOD_CONTROL);
|
return with(GLFW.GLFW_MOD_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withAlt() {
|
public KeyMatcher withAlt() {
|
||||||
return with(GLFW.GLFW_MOD_ALT);
|
return with(GLFW.GLFW_MOD_ALT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withSuper() {
|
public KeyMatcher withSuper() {
|
||||||
return with(GLFW.GLFW_MOD_SUPER);
|
return with(GLFW.GLFW_MOD_SUPER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyMatcher build() {
|
|
||||||
return new KeyMatcher(key, mods);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Predicate<KeyEvent> matcher() {
|
|
||||||
return build()::matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -46,12 +46,17 @@ public class Keys {
|
|||||||
private static final String MOUSE_BUTTON_PREFIX = "GLFW_MOUSE_BUTTON_";
|
private static final String MOUSE_BUTTON_PREFIX = "GLFW_MOUSE_BUTTON_";
|
||||||
|
|
||||||
private static final Set<String> IGNORE_FIELDS = new HashSet<>(
|
private static final Set<String> IGNORE_FIELDS = new HashSet<>(
|
||||||
Arrays.asList("GLFW_KEY_UNKNOWN", "GLFW_KEY_LAST", "GLFW_MOUSE_BUTTON_LAST", "GLFW_MOUSE_BUTTON_1", // Alias
|
Arrays.asList(
|
||||||
|
"GLFW_KEY_UNKNOWN",
|
||||||
|
"GLFW_KEY_LAST",
|
||||||
|
"GLFW_MOUSE_BUTTON_LAST",
|
||||||
|
"GLFW_MOUSE_BUTTON_1", // Alias
|
||||||
// for
|
// for
|
||||||
// LEFT
|
// LEFT
|
||||||
"GLFW_MOUSE_BUTTON_2", // Alias for RIGHT
|
"GLFW_MOUSE_BUTTON_2", // Alias for RIGHT
|
||||||
"GLFW_MOUSE_BUTTON_3" // Alias for MIDDLE
|
"GLFW_MOUSE_BUTTON_3" // Alias for MIDDLE
|
||||||
));
|
)
|
||||||
|
);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
initializeDictionary();
|
initializeDictionary();
|
||||||
@@ -95,8 +100,14 @@ public class Keys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (CODES_TO_NAMES.containsKey(value)) {
|
if (CODES_TO_NAMES.containsKey(value)) {
|
||||||
throw CrashReports.report(null, "Duplicate keys: %s and %s both map to %d(0x%s)", CODES_TO_NAMES.get(value),
|
throw CrashReports.report(
|
||||||
name, value, Integer.toHexString(value));
|
null,
|
||||||
|
"Duplicate keys: %s and %s both map to %d(0x%s)",
|
||||||
|
CODES_TO_NAMES.get(value),
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
Integer.toHexString(value)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
CODES_TO_NAMES.put(value, name);
|
CODES_TO_NAMES.put(value, name);
|
||||||
@@ -128,7 +139,7 @@ public class Keys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int getCode(String internalName) {
|
public static int getCode(String internalName) {
|
||||||
if (NAMES_TO_CODES.containsKey(internalName)) {
|
if (!NAMES_TO_CODES.containsKey(internalName)) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
return NAMES_TO_CODES.get(internalName);
|
return NAMES_TO_CODES.get(internalName);
|
||||||
|
@@ -20,55 +20,396 @@ package ru.windcorp.progressia.client.graphics.input.bus;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import ru.windcorp.jputil.ArrayUtil;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Component;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.CursorEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.WheelEvent;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event bus optionally related to a {@link Component} that delivers input
|
||||||
|
* events to input listeners. This bus may skip listeners based on circumstance;
|
||||||
|
* behavior can be customized for each listener with {@link Option}s.
|
||||||
|
* <p>
|
||||||
|
* By default, events are filtered by four checks before being delivered to each
|
||||||
|
* listener:
|
||||||
|
* <ol>
|
||||||
|
* <li><em>Consumption check</em>: unless {@link Option#RECEIVE_CONSUMED
|
||||||
|
* RECEIVE_CONSUMED} is set, events that are consumed will not be
|
||||||
|
* delivered.</li>
|
||||||
|
* <li><em>Hover check</em>: for certain event types (for example,
|
||||||
|
* {@link WheelEvent} or {@link KeyEvent} that {@link KeyEvent#isMouse()
|
||||||
|
* isMouse()}), the event will only be delivered if the component is hovered.
|
||||||
|
* This check may be bypassed with option {@link Option#IGNORE_HOVER
|
||||||
|
* IGNORE_HOVER} or made mandatory for all events with
|
||||||
|
* {@link Option#REQUIRE_HOVER REQUIRE_HOVER}. Hover check automatically
|
||||||
|
* succeeds if no component is provided.</li>
|
||||||
|
* <li><em>Focus check</em>: for certain event types (for example,
|
||||||
|
* {@link KeyEvent} that {@code !isMouse()}), the event will only be delivered
|
||||||
|
* if the component has focus. This check may be bypassed with option
|
||||||
|
* {@link Option#IGNORE_FOCUS IGNORE_FOCUS} or made mandatory for all events
|
||||||
|
* with {@link Option#REQUIRE_FOCUS REQUIRE_FOCUS}. Focus check automatically
|
||||||
|
* succeeds if no component is provided.</li>
|
||||||
|
* <li><em>Type check</em>: events of type {@code E} are only delivered to
|
||||||
|
* listeners registered with event type {@code T} if objects of type {@code E}
|
||||||
|
* can be cast to {@code T}.</li>
|
||||||
|
* </ol>
|
||||||
|
* Checks 1-3 are bypassed when option {@link Option#ALWAYS ALWAYS} is
|
||||||
|
* specified.
|
||||||
|
*/
|
||||||
public class InputBus {
|
public class InputBus {
|
||||||
|
|
||||||
private static class WrappedListener {
|
/**
|
||||||
|
* Options that allow customization of checks for listeners.
|
||||||
|
*/
|
||||||
|
public enum Option {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ignore checks for consumed events, hover and focus; deliver event if
|
||||||
|
* at all possible. This is shorthand for {@link #RECEIVE_CONSUMED},
|
||||||
|
* {@link #IGNORE_HOVER} and {@link #IGNORE_FOCUS}.
|
||||||
|
*/
|
||||||
|
ALWAYS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive events that were previously consumed.
|
||||||
|
*/
|
||||||
|
RECEIVE_CONSUMED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not process events if the listener is registered with a component
|
||||||
|
* and the component is not hovered.
|
||||||
|
*/
|
||||||
|
REQUIRE_HOVER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deliver events even if the event is limited to hovered components by
|
||||||
|
* default.
|
||||||
|
*/
|
||||||
|
IGNORE_HOVER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not process events if the listener is registered with a component
|
||||||
|
* and the component is not focused.
|
||||||
|
*/
|
||||||
|
REQUIRE_FOCUS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deliver events even if the event is limited to focused components by
|
||||||
|
* default.
|
||||||
|
*/
|
||||||
|
IGNORE_FOCUS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deliver events according to
|
||||||
|
* {@link KeyMatcher#matchesIgnoringAction(KeyEvent)} rather than
|
||||||
|
* {@link KeyMatcher#matches(KeyEvent)} when a {@link KeyMatcher} is
|
||||||
|
* specified.
|
||||||
|
*/
|
||||||
|
IGNORE_ACTION;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum YesNoDefault {
|
||||||
|
YES, NO, DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener with check preferences resolved and type specified.
|
||||||
|
*/
|
||||||
|
private class WrappedListener {
|
||||||
|
|
||||||
private final Class<?> type;
|
private final Class<?> type;
|
||||||
private final boolean handleConsumed;
|
|
||||||
|
private final boolean dropIfConsumed;
|
||||||
|
private final YesNoDefault dropIfNotHovered;
|
||||||
|
private final YesNoDefault dropIfNotFocused;
|
||||||
|
|
||||||
private final InputListener<?> listener;
|
private final InputListener<?> listener;
|
||||||
|
|
||||||
public WrappedListener(Class<?> type, boolean handleConsumed, InputListener<?> listener) {
|
public WrappedListener(
|
||||||
|
Class<?> type,
|
||||||
|
boolean dropIfConsumed,
|
||||||
|
YesNoDefault dropIfNotHovered,
|
||||||
|
YesNoDefault dropIfNotFocused,
|
||||||
|
InputListener<?> listener
|
||||||
|
) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.handleConsumed = handleConsumed;
|
this.dropIfConsumed = dropIfConsumed;
|
||||||
|
this.dropIfNotHovered = dropIfNotHovered;
|
||||||
|
this.dropIfNotFocused = dropIfNotFocused;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handles(Input input) {
|
private boolean handles(InputEvent input) {
|
||||||
return (!input.isConsumed() || handleConsumed) && type.isInstance(input.getEvent());
|
if (dropIfConsumed && input.isConsumed())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (dropIfNotHovered) {
|
||||||
|
case YES:
|
||||||
|
if (!isHovered())
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case NO:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
if (isHovered())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (input instanceof KeyEvent && ((KeyEvent) input).isMouse())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (input instanceof CursorEvent)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (input instanceof WheelEvent)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (dropIfNotFocused) {
|
||||||
|
case YES:
|
||||||
|
if (!isFocused())
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case NO:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
if (isFocused())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (input instanceof KeyEvent && !((KeyEvent) input).isMouse())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!type.isInstance(input))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes the listener if the event is deemed appropriate by the four
|
||||||
|
* checks.
|
||||||
|
*
|
||||||
|
* @param event the event to deliver
|
||||||
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void handle(Input input) {
|
public void handle(InputEvent event) {
|
||||||
if (handles(input)) {
|
if (handles(event)) {
|
||||||
boolean consumed = ((InputListener<InputEvent>) listener)
|
// A runtime check of types has been performed; this is safe.
|
||||||
.handle((InputEvent) type.cast(input.getEvent()));
|
InputListener<InputEvent> castListener = (InputListener<InputEvent>) listener;
|
||||||
|
|
||||||
input.setConsumed(consumed);
|
try {
|
||||||
|
castListener.handle(event);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw CrashReports.report(
|
||||||
|
e,
|
||||||
|
"InputListener %s for component %s has failed to receive event %s",
|
||||||
|
listener,
|
||||||
|
owner,
|
||||||
|
event
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The component queried for focus and hover. May be {@code null}.
|
||||||
|
*/
|
||||||
|
private final Component owner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registered listeners.
|
||||||
|
*/
|
||||||
private final Collection<WrappedListener> listeners = new ArrayList<>(4);
|
private final Collection<WrappedListener> listeners = new ArrayList<>(4);
|
||||||
|
|
||||||
public void dispatch(Input input) {
|
/**
|
||||||
listeners.forEach(l -> l.handle(input));
|
* Creates a new input bus that consults the specified {@link Component} to
|
||||||
|
* determine hover and focus.
|
||||||
|
*
|
||||||
|
* @param owner the component to use for hover and focus tests
|
||||||
|
* @see #InputBus()
|
||||||
|
*/
|
||||||
|
public InputBus(Component owner) {
|
||||||
|
this.owner = Objects.requireNonNull(owner, "owner");
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends InputEvent> void register(Class<? extends T> type, boolean handlesConsumed,
|
/**
|
||||||
InputListener<T> listener) {
|
* Creates a new input bus that assumes all hover and focus checks are
|
||||||
listeners.add(new WrappedListener(type, handlesConsumed, listener));
|
* successful.
|
||||||
|
*
|
||||||
|
* @see #InputBus(Component)
|
||||||
|
*/
|
||||||
|
public InputBus() {
|
||||||
|
this.owner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends InputEvent> void register(Class<? extends T> type, InputListener<T> listener) {
|
/**
|
||||||
register(type, false, listener);
|
* Determines whether hover should be assumed for this event bus.
|
||||||
|
*
|
||||||
|
* @return {@code true} iff no component is linked or the linked component
|
||||||
|
* is hovered
|
||||||
|
*/
|
||||||
|
private boolean isHovered() {
|
||||||
|
return owner == null ? true : owner.isHovered();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether focus should be assumed for this event bus.
|
||||||
|
*
|
||||||
|
* @return {@code true} iff no component is linked or the linked component
|
||||||
|
* is focused
|
||||||
|
*/
|
||||||
|
private boolean isFocused() {
|
||||||
|
return owner == null ? true : owner.isFocused();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches (delivers) the provided event to all appropriate listeners.
|
||||||
|
*
|
||||||
|
* @param event the event to process
|
||||||
|
*/
|
||||||
|
public void dispatch(InputEvent event) {
|
||||||
|
Objects.requireNonNull(event, "event");
|
||||||
|
for (WrappedListener listener : listeners) {
|
||||||
|
listener.handle(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a listener on this bus.
|
||||||
|
* <p>
|
||||||
|
* {@code type} specifies the class of events that should be passed to this
|
||||||
|
* listener. Only events of types that extend, implement or equal
|
||||||
|
* {@code type} are processed.
|
||||||
|
* <p>
|
||||||
|
* Zero or more {@link Option}s may be specified to enable or disable the
|
||||||
|
* processing of certain events in certain circumstances. See
|
||||||
|
* {@linkplain InputBus class description} for a detailed breakdown of the
|
||||||
|
* checks performed and the effects of various options. When providing
|
||||||
|
* options to this method, later options override the effects of previous
|
||||||
|
* options.
|
||||||
|
* <p>
|
||||||
|
* Option {@link Option#IGNORE_ACTION IGNORE_ACTION} is ignored silently.
|
||||||
|
*
|
||||||
|
* @param type the event class to deliver
|
||||||
|
* @param listener the listener
|
||||||
|
* @param options the options for this listener
|
||||||
|
*/
|
||||||
|
public <T extends InputEvent> void register(
|
||||||
|
Class<? extends T> type,
|
||||||
|
InputListener<T> listener,
|
||||||
|
Option... options
|
||||||
|
) {
|
||||||
|
Objects.requireNonNull(type, "type");
|
||||||
|
Objects.requireNonNull(listener, "listener");
|
||||||
|
|
||||||
|
boolean dropIfConsumed = true;
|
||||||
|
YesNoDefault dropIfNotHovered = YesNoDefault.DEFAULT;
|
||||||
|
YesNoDefault dropIfNotFocused = YesNoDefault.DEFAULT;
|
||||||
|
|
||||||
|
if (options != null) {
|
||||||
|
for (Option option : options) {
|
||||||
|
switch (option) {
|
||||||
|
case ALWAYS:
|
||||||
|
dropIfConsumed = false;
|
||||||
|
dropIfNotHovered = YesNoDefault.NO;
|
||||||
|
dropIfNotFocused = YesNoDefault.NO;
|
||||||
|
break;
|
||||||
|
case RECEIVE_CONSUMED:
|
||||||
|
dropIfConsumed = false;
|
||||||
|
break;
|
||||||
|
case REQUIRE_HOVER:
|
||||||
|
dropIfNotHovered = YesNoDefault.YES;
|
||||||
|
break;
|
||||||
|
case IGNORE_HOVER:
|
||||||
|
dropIfNotFocused = YesNoDefault.NO;
|
||||||
|
break;
|
||||||
|
case REQUIRE_FOCUS:
|
||||||
|
dropIfNotHovered = YesNoDefault.YES;
|
||||||
|
break;
|
||||||
|
case IGNORE_FOCUS:
|
||||||
|
dropIfNotFocused = YesNoDefault.NO;
|
||||||
|
break;
|
||||||
|
case IGNORE_ACTION:
|
||||||
|
// Ignore
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unexpected option " + option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners.add(new WrappedListener(type, dropIfConsumed, dropIfNotHovered, dropIfNotFocused, listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a {@link KeyEvent} listener on this bus. An event has to match
|
||||||
|
* the provided {@link KeyMatcher} to be delivered to the listener.
|
||||||
|
* <p>
|
||||||
|
* Zero or more {@link Option}s may be specified to enable or disable the
|
||||||
|
* processing of certain events in certain circumstances. See
|
||||||
|
* {@linkplain InputBus class description} for a detailed breakdown of the
|
||||||
|
* checks performed and the effects of various options. When providing
|
||||||
|
* options to this method, later options override the effects of previous
|
||||||
|
* options.
|
||||||
|
* <p>
|
||||||
|
* Option {@link Option#IGNORE_ACTION IGNORE_ACTION} requests that events
|
||||||
|
* are delivered according to
|
||||||
|
* {@link KeyMatcher#matchesIgnoringAction(KeyEvent)} rather than
|
||||||
|
* {@link KeyMatcher#matches(KeyEvent)}.
|
||||||
|
* specified.
|
||||||
|
*
|
||||||
|
* @param matcher an event filter
|
||||||
|
* @param listener the listener
|
||||||
|
* @param options the options for this listener
|
||||||
|
*/
|
||||||
|
public void register(KeyMatcher matcher, InputListener<? super KeyEvent> listener, Option... options) {
|
||||||
|
Objects.requireNonNull(matcher, "matcher");
|
||||||
|
Objects.requireNonNull(listener, "listener");
|
||||||
|
|
||||||
|
InputListener<KeyEvent> filteringListener;
|
||||||
|
|
||||||
|
if (ArrayUtil.firstIndexOf(options, Option.IGNORE_ACTION) != -1) {
|
||||||
|
filteringListener = e -> {
|
||||||
|
if (matcher.matchesIgnoringAction(e)) {
|
||||||
|
listener.handle(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
filteringListener = e -> {
|
||||||
|
if (matcher.matches(e)) {
|
||||||
|
listener.handle(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
register(KeyEvent.class, filteringListener, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all occurrences of the provided listener from this bus.
|
||||||
|
*
|
||||||
|
* @param listener the listener to unregister
|
||||||
|
*/
|
||||||
public void unregister(InputListener<?> listener) {
|
public void unregister(InputListener<?> listener) {
|
||||||
|
if (listener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
listeners.removeIf(l -> l.listener == listener);
|
listeners.removeIf(l -> l.listener == listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,6 +23,6 @@ import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
|||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface InputListener<T extends InputEvent> {
|
public interface InputListener<T extends InputEvent> {
|
||||||
|
|
||||||
boolean handle(T event);
|
void handle(T event);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -33,14 +33,22 @@ public abstract class DynamicModel extends Model {
|
|||||||
private final Mat4[] transforms;
|
private final Mat4[] transforms;
|
||||||
private final boolean[] dynamics;
|
private final boolean[] dynamics;
|
||||||
|
|
||||||
public DynamicModel(Renderable[] parts, Mat4[] transforms, boolean[] dynamic) {
|
public DynamicModel(
|
||||||
|
Renderable[] parts,
|
||||||
|
Mat4[] transforms,
|
||||||
|
boolean[] dynamic
|
||||||
|
) {
|
||||||
super(parts);
|
super(parts);
|
||||||
this.transforms = transforms;
|
this.transforms = transforms;
|
||||||
this.dynamics = dynamic;
|
this.dynamics = dynamic;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicModel(Builder builder) {
|
public DynamicModel(Builder builder) {
|
||||||
this(builder.getParts(), builder.getTransforms(), builder.getDynamics());
|
this(
|
||||||
|
builder.getParts(),
|
||||||
|
builder.getTransforms(),
|
||||||
|
builder.getDynamics()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -70,7 +78,11 @@ public abstract class DynamicModel extends Model {
|
|||||||
protected Builder() {
|
protected Builder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Builder addPart(Renderable part, Mat4 transform, boolean isDynamic) {
|
private Builder addPart(
|
||||||
|
Renderable part,
|
||||||
|
Mat4 transform,
|
||||||
|
boolean isDynamic
|
||||||
|
) {
|
||||||
parts.add(Objects.requireNonNull(part, "part"));
|
parts.add(Objects.requireNonNull(part, "part"));
|
||||||
transforms.add(Objects.requireNonNull(transform, "transform"));
|
transforms.add(Objects.requireNonNull(transform, "transform"));
|
||||||
dynamics.add(isDynamic);
|
dynamics.add(isDynamic);
|
||||||
@@ -78,15 +90,22 @@ public abstract class DynamicModel extends Model {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addStaticPart(Renderable part, Mat4 transform) {
|
public Builder addStaticPart(
|
||||||
|
Renderable part,
|
||||||
|
Mat4 transform
|
||||||
|
) {
|
||||||
return addPart(part, new Mat4(transform), false);
|
return addPart(part, new Mat4(transform), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addDynamicPart(Renderable part) {
|
public Builder addDynamicPart(
|
||||||
|
Renderable part
|
||||||
|
) {
|
||||||
return addPart(part, new Mat4(), true);
|
return addPart(part, new Mat4(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addStaticPart(Renderable part) {
|
public Builder addStaticPart(
|
||||||
|
Renderable part
|
||||||
|
) {
|
||||||
return addStaticPart(part, IDENTITY);
|
return addStaticPart(part, IDENTITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user