Migrate CRLF file endings to LF
This commit is contained in:
38
.gitattributes
vendored
Executable file → Normal file
38
.gitattributes
vendored
Executable file → Normal file
@@ -1,19 +1,19 @@
|
||||
#
|
||||
# https://help.github.com/articles/dealing-with-line-endings/
|
||||
#
|
||||
# These are explicitly windows files and should use crlf
|
||||
*.bat text eol=crlf
|
||||
|
||||
# The rest of project files should use lf
|
||||
*.gradle text eol=lf
|
||||
*.kt text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.java text eol=lf
|
||||
*.fs text eol=lf
|
||||
*.vs text eol=lf
|
||||
*.properties text eol=lf
|
||||
*.proto text eol=lf
|
||||
gradlew text eol=lf
|
||||
|
||||
# Binary files
|
||||
*.jar binary
|
||||
#
|
||||
# https://help.github.com/articles/dealing-with-line-endings/
|
||||
#
|
||||
# These are explicitly windows files and should use crlf
|
||||
*.bat text eol=crlf
|
||||
|
||||
# The rest of project files should use lf
|
||||
*.gradle text eol=lf
|
||||
*.kt text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.java text eol=lf
|
||||
*.fs text eol=lf
|
||||
*.vs text eol=lf
|
||||
*.properties text eol=lf
|
||||
*.proto text eol=lf
|
||||
gradlew text eol=lf
|
||||
|
||||
# Binary files
|
||||
*.jar binary
|
||||
|
||||
276
.gitignore
vendored
Executable file → Normal file
276
.gitignore
vendored
Executable file → Normal file
@@ -1,138 +1,138 @@
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/java,gradle,intellij
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle,intellij
|
||||
|
||||
### Intellij ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Intellij Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
||||
.idea/**/sonarlint/
|
||||
|
||||
# SonarQube Plugin
|
||||
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
||||
.idea/**/sonarIssues.xml
|
||||
|
||||
# Markdown Navigator plugin
|
||||
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
||||
.idea/**/markdown-navigator.xml
|
||||
.idea/**/markdown-navigator-enh.xml
|
||||
.idea/**/markdown-navigator/
|
||||
|
||||
# Cache file creation bug
|
||||
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
||||
.idea/$CACHE_FILE$
|
||||
|
||||
# CodeStream plugin
|
||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
||||
.idea/codestream.xml
|
||||
|
||||
### Java ###
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
### Gradle ###
|
||||
.gradle
|
||||
build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
### Gradle Patch ###
|
||||
**/build/
|
||||
|
||||
### Textures and other resources ###
|
||||
*.png
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/java,gradle,intellij
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/java,gradle,intellij
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle,intellij
|
||||
|
||||
### Intellij ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Intellij Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
||||
.idea/**/sonarlint/
|
||||
|
||||
# SonarQube Plugin
|
||||
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
||||
.idea/**/sonarIssues.xml
|
||||
|
||||
# Markdown Navigator plugin
|
||||
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
||||
.idea/**/markdown-navigator.xml
|
||||
.idea/**/markdown-navigator-enh.xml
|
||||
.idea/**/markdown-navigator/
|
||||
|
||||
# Cache file creation bug
|
||||
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
||||
.idea/$CACHE_FILE$
|
||||
|
||||
# CodeStream plugin
|
||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
||||
.idea/codestream.xml
|
||||
|
||||
### Java ###
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
### Gradle ###
|
||||
.gradle
|
||||
build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
### Gradle Patch ###
|
||||
**/build/
|
||||
|
||||
### Textures and other resources ###
|
||||
*.png
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/java,gradle,intellij
|
||||
|
||||
100
editor/build.gradle
Executable file → Normal file
100
editor/build.gradle
Executable file → Normal file
@@ -1,50 +1,50 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.4.10'
|
||||
id 'org.openjfx.javafxplugin' version '0.0.8'
|
||||
id 'org.springframework.boot' version "$springBootVersion"
|
||||
id 'io.spring.dependency-management' version "$springDependencyManagementVersion"
|
||||
id 'idea'
|
||||
}
|
||||
|
||||
group 'com.bartlomiejpluta.base'
|
||||
version 'unspecified'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.kotlin.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
javafx {
|
||||
version = "11.0.2"
|
||||
modules = ['javafx.controls', 'javafx.graphics']
|
||||
}
|
||||
|
||||
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "14"
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "14"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":proto")
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib"
|
||||
implementation "no.tornado:tornadofx:${tornadoFxVersion}"
|
||||
implementation platform("org.kordamp.ikonli:ikonli-bom:${ikonliVersion}")
|
||||
implementation 'org.kordamp.ikonli:ikonli-javafx'
|
||||
implementation 'org.kordamp.ikonli:ikonli-fontawesome-pack'
|
||||
|
||||
// Spring
|
||||
implementation 'org.springframework.boot:spring-boot-starter'
|
||||
}
|
||||
|
||||
build {
|
||||
dependsOn(":proto:build")
|
||||
}
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.4.10'
|
||||
id 'org.openjfx.javafxplugin' version '0.0.8'
|
||||
id 'org.springframework.boot' version "$springBootVersion"
|
||||
id 'io.spring.dependency-management' version "$springDependencyManagementVersion"
|
||||
id 'idea'
|
||||
}
|
||||
|
||||
group 'com.bartlomiejpluta.base'
|
||||
version 'unspecified'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.kotlin.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
javafx {
|
||||
version = "11.0.2"
|
||||
modules = ['javafx.controls', 'javafx.graphics']
|
||||
}
|
||||
|
||||
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "14"
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "14"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":proto")
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib"
|
||||
implementation "no.tornado:tornadofx:${tornadoFxVersion}"
|
||||
implementation platform("org.kordamp.ikonli:ikonli-bom:${ikonliVersion}")
|
||||
implementation 'org.kordamp.ikonli:ikonli-javafx'
|
||||
implementation 'org.kordamp.ikonli:ikonli-fontawesome-pack'
|
||||
|
||||
// Spring
|
||||
implementation 'org.springframework.boot:spring-boot-starter'
|
||||
}
|
||||
|
||||
build {
|
||||
dependsOn(":proto:build")
|
||||
}
|
||||
|
||||
68
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/App.kt
Executable file → Normal file
68
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/App.kt
Executable file → Normal file
@@ -1,35 +1,35 @@
|
||||
package com.bartlomiejpluta.base.editor
|
||||
|
||||
import com.bartlomiejpluta.base.editor.main.view.MainView
|
||||
import org.springframework.boot.SpringApplication
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import tornadofx.App
|
||||
import tornadofx.DIContainer
|
||||
import tornadofx.FX
|
||||
import tornadofx.launch
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@SpringBootApplication
|
||||
open class EditorApp : App(MainView::class) {
|
||||
private lateinit var context: ConfigurableApplicationContext
|
||||
|
||||
override fun init() {
|
||||
this.context = SpringApplication.run(this.javaClass)
|
||||
context.autowireCapableBeanFactory.autowireBean(this)
|
||||
|
||||
FX.dicontainer = object : DIContainer {
|
||||
override fun <T : Any> getInstance(type: KClass<T>): T = context.getBean(type.java)
|
||||
override fun <T : Any> getInstance(type: KClass<T>, name: String): T = context.getBean(name, type.java)
|
||||
}
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
super.stop()
|
||||
context.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
launch<EditorApp>(args)
|
||||
package com.bartlomiejpluta.base.editor
|
||||
|
||||
import com.bartlomiejpluta.base.editor.main.view.MainView
|
||||
import org.springframework.boot.SpringApplication
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import tornadofx.App
|
||||
import tornadofx.DIContainer
|
||||
import tornadofx.FX
|
||||
import tornadofx.launch
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@SpringBootApplication
|
||||
open class EditorApp : App(MainView::class) {
|
||||
private lateinit var context: ConfigurableApplicationContext
|
||||
|
||||
override fun init() {
|
||||
this.context = SpringApplication.run(this.javaClass)
|
||||
context.autowireCapableBeanFactory.autowireBean(this)
|
||||
|
||||
FX.dicontainer = object : DIContainer {
|
||||
override fun <T : Any> getInstance(type: KClass<T>): T = context.getBean(type.java)
|
||||
override fun <T : Any> getInstance(type: KClass<T>, name: String): T = context.getBean(name, type.java)
|
||||
}
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
super.stop()
|
||||
context.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
launch<EditorApp>(args)
|
||||
}
|
||||
4
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/context/UndoableContext.kt
Executable file → Normal file
4
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/context/UndoableContext.kt
Executable file → Normal file
@@ -1,3 +1,3 @@
|
||||
package com.bartlomiejpluta.base.editor.command.context
|
||||
|
||||
package com.bartlomiejpluta.base.editor.command.context
|
||||
|
||||
interface UndoableContext
|
||||
8
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/context/UndoableScope.kt
Executable file → Normal file
8
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/context/UndoableScope.kt
Executable file → Normal file
@@ -1,5 +1,5 @@
|
||||
package com.bartlomiejpluta.base.editor.command.context
|
||||
|
||||
import tornadofx.Scope
|
||||
|
||||
package com.bartlomiejpluta.base.editor.command.context
|
||||
|
||||
import tornadofx.Scope
|
||||
|
||||
class UndoableScope : UndoableContext, Scope()
|
||||
8
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/base/Command.kt
Executable file → Normal file
8
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/base/Command.kt
Executable file → Normal file
@@ -1,5 +1,5 @@
|
||||
package com.bartlomiejpluta.base.editor.command.model.base
|
||||
|
||||
interface Command {
|
||||
fun execute()
|
||||
package com.bartlomiejpluta.base.editor.command.model.base
|
||||
|
||||
interface Command {
|
||||
fun execute()
|
||||
}
|
||||
40
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/base/SimpleCommand.kt
Executable file → Normal file
40
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/base/SimpleCommand.kt
Executable file → Normal file
@@ -1,21 +1,21 @@
|
||||
package com.bartlomiejpluta.base.editor.command.model.base
|
||||
|
||||
class SimpleCommand<T>(
|
||||
override val commandName: String,
|
||||
private val formerValue: T,
|
||||
private val value: T,
|
||||
private val execute: (T) -> Unit
|
||||
) : Undoable, Command {
|
||||
|
||||
override fun undo() {
|
||||
execute(formerValue)
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
execute()
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
execute(value)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.command.model.base
|
||||
|
||||
class SimpleCommand<T>(
|
||||
override val commandName: String,
|
||||
private val formerValue: T,
|
||||
private val value: T,
|
||||
private val execute: (T) -> Unit
|
||||
) : Undoable, Command {
|
||||
|
||||
override fun undo() {
|
||||
execute(formerValue)
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
execute()
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
execute(value)
|
||||
}
|
||||
}
|
||||
12
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/base/Undoable.kt
Executable file → Normal file
12
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/base/Undoable.kt
Executable file → Normal file
@@ -1,7 +1,7 @@
|
||||
package com.bartlomiejpluta.base.editor.command.model.base
|
||||
|
||||
interface Undoable {
|
||||
fun undo()
|
||||
fun redo()
|
||||
val commandName: String
|
||||
package com.bartlomiejpluta.base.editor.command.model.base
|
||||
|
||||
interface Undoable {
|
||||
fun undo()
|
||||
fun redo()
|
||||
val commandName: String
|
||||
}
|
||||
44
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/map/CreateLayerCommand.kt
Executable file → Normal file
44
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/map/CreateLayerCommand.kt
Executable file → Normal file
@@ -1,23 +1,23 @@
|
||||
package com.bartlomiejpluta.base.editor.command.model.map
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Command
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
|
||||
class CreateLayerCommand(private val map: GameMap, private val layer: Layer): Undoable, Command {
|
||||
|
||||
override fun execute() {
|
||||
map.layers += layer
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
map.layers -= layer
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
execute()
|
||||
}
|
||||
|
||||
override val commandName = "Create map layer"
|
||||
package com.bartlomiejpluta.base.editor.command.model.map
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Command
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
|
||||
class CreateLayerCommand(private val map: GameMap, private val layer: Layer): Undoable, Command {
|
||||
|
||||
override fun execute() {
|
||||
map.layers += layer
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
map.layers -= layer
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
execute()
|
||||
}
|
||||
|
||||
override val commandName = "Create map layer"
|
||||
}
|
||||
42
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/map/MoveLayerCommand.kt
Executable file → Normal file
42
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/map/MoveLayerCommand.kt
Executable file → Normal file
@@ -1,22 +1,22 @@
|
||||
package com.bartlomiejpluta.base.editor.command.model.map
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Command
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
import tornadofx.swap
|
||||
|
||||
class MoveLayerCommand(private val map: GameMap, private val currentIndex: Int, private val newIndex: Int) : Undoable, Command {
|
||||
override fun execute() {
|
||||
map.layers.swap(currentIndex, newIndex)
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
map.layers.swap(newIndex, currentIndex)
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
execute()
|
||||
}
|
||||
|
||||
override val commandName = "Move layer"
|
||||
package com.bartlomiejpluta.base.editor.command.model.map
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Command
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
import tornadofx.swap
|
||||
|
||||
class MoveLayerCommand(private val map: GameMap, private val currentIndex: Int, private val newIndex: Int) : Undoable, Command {
|
||||
override fun execute() {
|
||||
map.layers.swap(currentIndex, newIndex)
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
map.layers.swap(newIndex, currentIndex)
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
execute()
|
||||
}
|
||||
|
||||
override val commandName = "Move layer"
|
||||
}
|
||||
48
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/map/RemoveLayerCommand.kt
Executable file → Normal file
48
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/map/RemoveLayerCommand.kt
Executable file → Normal file
@@ -1,25 +1,25 @@
|
||||
package com.bartlomiejpluta.base.editor.command.model.map
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Command
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
|
||||
class RemoveLayerCommand(private val map: GameMap, private val layerIndex: Int) : Undoable, Command {
|
||||
private var layer: Layer? = null
|
||||
|
||||
override fun execute() {
|
||||
layer = map.layers.removeAt(layerIndex)
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
map.layers.add(layerIndex, layer)
|
||||
layer = null
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
execute()
|
||||
}
|
||||
|
||||
override val commandName = "Remove layer"
|
||||
package com.bartlomiejpluta.base.editor.command.model.map
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Command
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
|
||||
class RemoveLayerCommand(private val map: GameMap, private val layerIndex: Int) : Undoable, Command {
|
||||
private var layer: Layer? = null
|
||||
|
||||
override fun execute() {
|
||||
layer = map.layers.removeAt(layerIndex)
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
map.layers.add(layerIndex, layer)
|
||||
layer = null
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
execute()
|
||||
}
|
||||
|
||||
override val commandName = "Remove layer"
|
||||
}
|
||||
44
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/map/RenameLayerCommand.kt
Executable file → Normal file
44
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/model/map/RenameLayerCommand.kt
Executable file → Normal file
@@ -1,23 +1,23 @@
|
||||
package com.bartlomiejpluta.base.editor.command.model.map
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Command
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
|
||||
class RenameLayerCommand(private val layer: Layer, private val newName: String) : Undoable, Command {
|
||||
private val formerName = layer.name
|
||||
|
||||
override fun execute() {
|
||||
layer.name = newName
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
layer.name = formerName
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
execute()
|
||||
}
|
||||
|
||||
override val commandName = "Rename layer"
|
||||
package com.bartlomiejpluta.base.editor.command.model.map
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Command
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
|
||||
class RenameLayerCommand(private val layer: Layer, private val newName: String) : Undoable, Command {
|
||||
private val formerName = layer.name
|
||||
|
||||
override fun execute() {
|
||||
layer.name = newName
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
layer.name = formerName
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
execute()
|
||||
}
|
||||
|
||||
override val commandName = "Rename layer"
|
||||
}
|
||||
242
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/service/DefaultUndoRedoService.kt
Executable file → Normal file
242
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/service/DefaultUndoRedoService.kt
Executable file → Normal file
@@ -1,122 +1,122 @@
|
||||
package com.bartlomiejpluta.base.editor.command.service
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableContext
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Component
|
||||
import java.lang.Integer.toHexString
|
||||
import java.util.*
|
||||
|
||||
@Component
|
||||
class DefaultUndoRedoService : UndoRedoService {
|
||||
private val undo: Deque<Pair<Undoable, UndoableContext>> = ArrayDeque()
|
||||
private val redo: Deque<Pair<Undoable, UndoableContext>> = ArrayDeque()
|
||||
|
||||
var sizeMax = 30
|
||||
set(value) {
|
||||
if (value >= 0) {
|
||||
for (i in 0 until undo.size - value) {
|
||||
undo.removeLast()
|
||||
}
|
||||
|
||||
field = value
|
||||
}
|
||||
}
|
||||
|
||||
override fun push(undoable: Undoable) {
|
||||
push(undoable, GLOBAL_CONTEXT)
|
||||
}
|
||||
|
||||
override fun push(undoable: Undoable, context: UndoableContext) {
|
||||
if (undo.size == sizeMax) {
|
||||
log.debug("The max size of [undo] stack has been reached. Removing the last item...")
|
||||
undo.removeLast()
|
||||
}
|
||||
|
||||
log.debug("Pushing item to [undo] stack: ${undoable.commandName} (ctx: ${toHexString(context.hashCode())})")
|
||||
undo.push(undoable to context)
|
||||
redo.clear()
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
if (undo.isNotEmpty()) {
|
||||
undo.pop().let {
|
||||
log.debug("Performing undo: ${it.first.commandName}")
|
||||
it.first.undo()
|
||||
redo.push(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun undo(context: UndoableContext) {
|
||||
if (undo.isNotEmpty()) {
|
||||
undo.firstOrNull { it.second === context }?.let {
|
||||
log.debug("Performing contextual (ctx: ${toHexString(context.hashCode())}) undo: ${it.first.commandName}")
|
||||
undo.remove(it)
|
||||
it.first.undo()
|
||||
redo.push(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
if (redo.isNotEmpty()) {
|
||||
redo.pop().let {
|
||||
log.debug("Performing redo: ${it.first.commandName}")
|
||||
it.first.redo()
|
||||
undo.push(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun redo(context: UndoableContext) {
|
||||
if (redo.isNotEmpty()) {
|
||||
redo.firstOrNull { it.second === context }?.let {
|
||||
log.debug("Performing contextual (ctx: ${toHexString(context.hashCode())}) redo: ${it.first.commandName}")
|
||||
redo.remove(it)
|
||||
it.first.redo()
|
||||
undo.push(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
log.debug("Clearing [undo] and [redo] stacks")
|
||||
undo.clear()
|
||||
redo.clear()
|
||||
}
|
||||
|
||||
override fun clear(context: UndoableContext) {
|
||||
log.debug("Clearing [undo] and [redo] stacks (ctx: ${toHexString(context.hashCode())})")
|
||||
undo.removeIf { it.second == context }
|
||||
redo.removeIf { it.second == context }
|
||||
}
|
||||
|
||||
override val lastUndoable: Undoable?
|
||||
get() = undo.first?.first
|
||||
|
||||
override val lastRedoable: Undoable?
|
||||
get() = redo.first?.first
|
||||
|
||||
override val undoCommandName: String
|
||||
get() = undo.first?.first?.commandName ?: ""
|
||||
|
||||
override val redoCommandName: String
|
||||
get() = redo.first?.first?.commandName ?: ""
|
||||
|
||||
override fun lastUndoable(context: UndoableContext) = undo.firstOrNull { it.second === context }?.first
|
||||
|
||||
override fun lastRedoable(context: UndoableContext) = redo.firstOrNull { it.second === context }?.first
|
||||
|
||||
override fun undoCommandName(context: UndoableContext) =
|
||||
undo.firstOrNull { it.second === context }?.first?.commandName ?: ""
|
||||
|
||||
override fun redoCommandName(context: UndoableContext) =
|
||||
redo.firstOrNull { it.second === context }?.first?.commandName ?: ""
|
||||
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(DefaultUndoRedoService::class.java)
|
||||
private val GLOBAL_CONTEXT = object : UndoableContext {}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.command.service
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableContext
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Component
|
||||
import java.lang.Integer.toHexString
|
||||
import java.util.*
|
||||
|
||||
@Component
|
||||
class DefaultUndoRedoService : UndoRedoService {
|
||||
private val undo: Deque<Pair<Undoable, UndoableContext>> = ArrayDeque()
|
||||
private val redo: Deque<Pair<Undoable, UndoableContext>> = ArrayDeque()
|
||||
|
||||
var sizeMax = 30
|
||||
set(value) {
|
||||
if (value >= 0) {
|
||||
for (i in 0 until undo.size - value) {
|
||||
undo.removeLast()
|
||||
}
|
||||
|
||||
field = value
|
||||
}
|
||||
}
|
||||
|
||||
override fun push(undoable: Undoable) {
|
||||
push(undoable, GLOBAL_CONTEXT)
|
||||
}
|
||||
|
||||
override fun push(undoable: Undoable, context: UndoableContext) {
|
||||
if (undo.size == sizeMax) {
|
||||
log.debug("The max size of [undo] stack has been reached. Removing the last item...")
|
||||
undo.removeLast()
|
||||
}
|
||||
|
||||
log.debug("Pushing item to [undo] stack: ${undoable.commandName} (ctx: ${toHexString(context.hashCode())})")
|
||||
undo.push(undoable to context)
|
||||
redo.clear()
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
if (undo.isNotEmpty()) {
|
||||
undo.pop().let {
|
||||
log.debug("Performing undo: ${it.first.commandName}")
|
||||
it.first.undo()
|
||||
redo.push(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun undo(context: UndoableContext) {
|
||||
if (undo.isNotEmpty()) {
|
||||
undo.firstOrNull { it.second === context }?.let {
|
||||
log.debug("Performing contextual (ctx: ${toHexString(context.hashCode())}) undo: ${it.first.commandName}")
|
||||
undo.remove(it)
|
||||
it.first.undo()
|
||||
redo.push(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
if (redo.isNotEmpty()) {
|
||||
redo.pop().let {
|
||||
log.debug("Performing redo: ${it.first.commandName}")
|
||||
it.first.redo()
|
||||
undo.push(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun redo(context: UndoableContext) {
|
||||
if (redo.isNotEmpty()) {
|
||||
redo.firstOrNull { it.second === context }?.let {
|
||||
log.debug("Performing contextual (ctx: ${toHexString(context.hashCode())}) redo: ${it.first.commandName}")
|
||||
redo.remove(it)
|
||||
it.first.redo()
|
||||
undo.push(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
log.debug("Clearing [undo] and [redo] stacks")
|
||||
undo.clear()
|
||||
redo.clear()
|
||||
}
|
||||
|
||||
override fun clear(context: UndoableContext) {
|
||||
log.debug("Clearing [undo] and [redo] stacks (ctx: ${toHexString(context.hashCode())})")
|
||||
undo.removeIf { it.second == context }
|
||||
redo.removeIf { it.second == context }
|
||||
}
|
||||
|
||||
override val lastUndoable: Undoable?
|
||||
get() = undo.first?.first
|
||||
|
||||
override val lastRedoable: Undoable?
|
||||
get() = redo.first?.first
|
||||
|
||||
override val undoCommandName: String
|
||||
get() = undo.first?.first?.commandName ?: ""
|
||||
|
||||
override val redoCommandName: String
|
||||
get() = redo.first?.first?.commandName ?: ""
|
||||
|
||||
override fun lastUndoable(context: UndoableContext) = undo.firstOrNull { it.second === context }?.first
|
||||
|
||||
override fun lastRedoable(context: UndoableContext) = redo.firstOrNull { it.second === context }?.first
|
||||
|
||||
override fun undoCommandName(context: UndoableContext) =
|
||||
undo.firstOrNull { it.second === context }?.first?.commandName ?: ""
|
||||
|
||||
override fun redoCommandName(context: UndoableContext) =
|
||||
redo.firstOrNull { it.second === context }?.first?.commandName ?: ""
|
||||
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(DefaultUndoRedoService::class.java)
|
||||
private val GLOBAL_CONTEXT = object : UndoableContext {}
|
||||
}
|
||||
}
|
||||
50
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/service/UndoRedoService.kt
Executable file → Normal file
50
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/service/UndoRedoService.kt
Executable file → Normal file
@@ -1,26 +1,26 @@
|
||||
package com.bartlomiejpluta.base.editor.command.service
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableContext
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
|
||||
interface UndoRedoService {
|
||||
fun push(undoable: Undoable)
|
||||
fun undo()
|
||||
fun redo()
|
||||
fun clear()
|
||||
|
||||
fun push(undoable: Undoable, context: UndoableContext)
|
||||
fun undo(context: UndoableContext)
|
||||
fun redo(context: UndoableContext)
|
||||
fun clear(context: UndoableContext)
|
||||
|
||||
val lastUndoable: Undoable?
|
||||
val lastRedoable: Undoable?
|
||||
val undoCommandName: String
|
||||
val redoCommandName: String
|
||||
|
||||
fun lastUndoable(context: UndoableContext): Undoable?
|
||||
fun lastRedoable(context: UndoableContext): Undoable?
|
||||
fun undoCommandName(context: UndoableContext): String
|
||||
fun redoCommandName(context: UndoableContext): String
|
||||
package com.bartlomiejpluta.base.editor.command.service
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableContext
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
|
||||
interface UndoRedoService {
|
||||
fun push(undoable: Undoable)
|
||||
fun undo()
|
||||
fun redo()
|
||||
fun clear()
|
||||
|
||||
fun push(undoable: Undoable, context: UndoableContext)
|
||||
fun undo(context: UndoableContext)
|
||||
fun redo(context: UndoableContext)
|
||||
fun clear(context: UndoableContext)
|
||||
|
||||
val lastUndoable: Undoable?
|
||||
val lastRedoable: Undoable?
|
||||
val undoCommandName: String
|
||||
val redoCommandName: String
|
||||
|
||||
fun lastUndoable(context: UndoableContext): Undoable?
|
||||
fun lastRedoable(context: UndoableContext): Undoable?
|
||||
fun undoCommandName(context: UndoableContext): String
|
||||
fun redoCommandName(context: UndoableContext): String
|
||||
}
|
||||
12
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/common/serial/Deserializer.kt
Executable file → Normal file
12
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/common/serial/Deserializer.kt
Executable file → Normal file
@@ -1,7 +1,7 @@
|
||||
package com.bartlomiejpluta.base.editor.common.serial
|
||||
|
||||
import java.io.InputStream
|
||||
|
||||
interface Deserializer<T> {
|
||||
fun deserialize(input: InputStream): T
|
||||
package com.bartlomiejpluta.base.editor.common.serial
|
||||
|
||||
import java.io.InputStream
|
||||
|
||||
interface Deserializer<T> {
|
||||
fun deserialize(input: InputStream): T
|
||||
}
|
||||
14
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/common/serial/Serializer.kt
Executable file → Normal file
14
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/common/serial/Serializer.kt
Executable file → Normal file
@@ -1,8 +1,8 @@
|
||||
package com.bartlomiejpluta.base.editor.common.serial
|
||||
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
interface Serializer<T> {
|
||||
fun serialize(item: T, output: OutputStream)
|
||||
package com.bartlomiejpluta.base.editor.common.serial
|
||||
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
interface Serializer<T> {
|
||||
fun serialize(item: T, output: OutputStream)
|
||||
}
|
||||
12
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/event/RedrawMapRequestEvent.kt
Executable file → Normal file
12
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/event/RedrawMapRequestEvent.kt
Executable file → Normal file
@@ -1,7 +1,7 @@
|
||||
package com.bartlomiejpluta.base.editor.event
|
||||
|
||||
import tornadofx.EventBus.RunOn.ApplicationThread
|
||||
import tornadofx.EventBus.RunOn.BackgroundThread
|
||||
import tornadofx.FXEvent
|
||||
|
||||
package com.bartlomiejpluta.base.editor.event
|
||||
|
||||
import tornadofx.EventBus.RunOn.ApplicationThread
|
||||
import tornadofx.EventBus.RunOn.BackgroundThread
|
||||
import tornadofx.FXEvent
|
||||
|
||||
object RedrawMapRequestEvent : FXEvent(ApplicationThread)
|
||||
116
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/controller/MainController.kt
Executable file → Normal file
116
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/controller/MainController.kt
Executable file → Normal file
@@ -1,59 +1,59 @@
|
||||
package com.bartlomiejpluta.base.editor.main.controller
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
import com.bartlomiejpluta.base.editor.map.view.MapSettingsFragment
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import com.bartlomiejpluta.base.editor.project.manager.ProjectManager
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import com.bartlomiejpluta.base.editor.project.view.ProjectSettingsFragment
|
||||
import com.bartlomiejpluta.base.editor.project.viewmodel.ProjectVM
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.stage.FileChooser
|
||||
import org.springframework.stereotype.Component
|
||||
import tornadofx.*
|
||||
import kotlin.collections.set
|
||||
|
||||
@Component
|
||||
class MainController : Controller() {
|
||||
// In the future it'll be pulled from TileSetService or something like that
|
||||
private val tileset = TileSet(resources.image("/textures/tileset.png"), 160, 8)
|
||||
private val projectManager: ProjectManager by di()
|
||||
|
||||
val openProject = SimpleObjectProperty<Project?>()
|
||||
val openMaps = observableMapOf<Scope, GameMap>()
|
||||
|
||||
fun createEmptyProject() {
|
||||
val project = Project()
|
||||
val vm = ProjectVM(project)
|
||||
|
||||
setInScope(vm)
|
||||
val modal = find<ProjectSettingsFragment>().apply { openModal(block = true, resizable = false) }
|
||||
|
||||
if(modal.result) {
|
||||
openProject.value = project
|
||||
projectManager.saveProject(project)
|
||||
}
|
||||
}
|
||||
|
||||
fun createEmptyMap() {
|
||||
val map = GameMap(tileset)
|
||||
val scope = UndoableScope()
|
||||
val vm = GameMapVM(map)
|
||||
setInScope(vm, scope)
|
||||
|
||||
val modal = find<MapSettingsFragment>(scope).apply { openModal(block = true, resizable = false) }
|
||||
|
||||
if (modal.result) {
|
||||
openMaps[scope] = map
|
||||
}
|
||||
}
|
||||
|
||||
fun loadProject() {
|
||||
chooseFile(
|
||||
title = "Load Project",
|
||||
filters = arrayOf(FileChooser.ExtensionFilter("BASE Editor Project (*.bep)", "*.bep")),
|
||||
).getOrNull(0)?.let { openProject.value = projectManager.openProject(it) }
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.main.controller
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
import com.bartlomiejpluta.base.editor.map.view.MapSettingsFragment
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import com.bartlomiejpluta.base.editor.project.manager.ProjectManager
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import com.bartlomiejpluta.base.editor.project.view.ProjectSettingsFragment
|
||||
import com.bartlomiejpluta.base.editor.project.viewmodel.ProjectVM
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.stage.FileChooser
|
||||
import org.springframework.stereotype.Component
|
||||
import tornadofx.*
|
||||
import kotlin.collections.set
|
||||
|
||||
@Component
|
||||
class MainController : Controller() {
|
||||
// In the future it'll be pulled from TileSetService or something like that
|
||||
private val tileset = TileSet(resources.image("/textures/tileset.png"), 160, 8)
|
||||
private val projectManager: ProjectManager by di()
|
||||
|
||||
val openProject = SimpleObjectProperty<Project?>()
|
||||
val openMaps = observableMapOf<Scope, GameMap>()
|
||||
|
||||
fun createEmptyProject() {
|
||||
val project = Project()
|
||||
val vm = ProjectVM(project)
|
||||
|
||||
setInScope(vm)
|
||||
val modal = find<ProjectSettingsFragment>().apply { openModal(block = true, resizable = false) }
|
||||
|
||||
if(modal.result) {
|
||||
openProject.value = project
|
||||
projectManager.saveProject(project)
|
||||
}
|
||||
}
|
||||
|
||||
fun createEmptyMap() {
|
||||
val map = GameMap(tileset)
|
||||
val scope = UndoableScope()
|
||||
val vm = GameMapVM(map)
|
||||
setInScope(vm, scope)
|
||||
|
||||
val modal = find<MapSettingsFragment>(scope).apply { openModal(block = true, resizable = false) }
|
||||
|
||||
if (modal.result) {
|
||||
openMaps[scope] = map
|
||||
}
|
||||
}
|
||||
|
||||
fun loadProject() {
|
||||
chooseFile(
|
||||
title = "Load Project",
|
||||
filters = arrayOf(FileChooser.ExtensionFilter("BASE Editor Project (*.bep)", "*.bep")),
|
||||
).getOrNull(0)?.let { openProject.value = projectManager.openProject(it) }
|
||||
}
|
||||
}
|
||||
78
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainMenuView.kt
Executable file → Normal file
78
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainMenuView.kt
Executable file → Normal file
@@ -1,40 +1,40 @@
|
||||
package com.bartlomiejpluta.base.editor.main.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.main.controller.MainController
|
||||
import tornadofx.*
|
||||
|
||||
class MainMenuView : View() {
|
||||
private val mainController: MainController by di()
|
||||
|
||||
override val root = menubar {
|
||||
menu("File") {
|
||||
menu("New") {
|
||||
item("Project...") {
|
||||
action {
|
||||
mainController.createEmptyProject()
|
||||
}
|
||||
}
|
||||
|
||||
item("Map...") {
|
||||
enableWhen(mainController.openProject.isNotNull)
|
||||
action {
|
||||
mainController.createEmptyMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu("Open") {
|
||||
item("Project...") {
|
||||
action {
|
||||
mainController.loadProject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu("Edit") {
|
||||
item("Undo")
|
||||
item("Redo")
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.main.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.main.controller.MainController
|
||||
import tornadofx.*
|
||||
|
||||
class MainMenuView : View() {
|
||||
private val mainController: MainController by di()
|
||||
|
||||
override val root = menubar {
|
||||
menu("File") {
|
||||
menu("New") {
|
||||
item("Project...") {
|
||||
action {
|
||||
mainController.createEmptyProject()
|
||||
}
|
||||
}
|
||||
|
||||
item("Map...") {
|
||||
enableWhen(mainController.openProject.isNotNull)
|
||||
action {
|
||||
mainController.createEmptyMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu("Open") {
|
||||
item("Project...") {
|
||||
action {
|
||||
mainController.loadProject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu("Edit") {
|
||||
item("Undo")
|
||||
item("Redo")
|
||||
}
|
||||
}
|
||||
}
|
||||
66
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainView.kt
Executable file → Normal file
66
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainView.kt
Executable file → Normal file
@@ -1,34 +1,34 @@
|
||||
package com.bartlomiejpluta.base.editor.main.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.main.controller.MainController
|
||||
import com.bartlomiejpluta.base.editor.map.view.MapFragment
|
||||
import javafx.scene.control.Tab
|
||||
import tornadofx.*
|
||||
|
||||
|
||||
class MainView : View("BASE Game Editor") {
|
||||
private val mainController: MainController by di()
|
||||
|
||||
private val mainMenuView = find<MainMenuView>()
|
||||
|
||||
init {
|
||||
mainController.openProject.addListener { _, _, project ->
|
||||
val projectName = project?.let { " :: ${it.name} (${it.sourceDirectory.absolutePath})" } ?: ""
|
||||
title = "BASE Game Editor$projectName"
|
||||
}
|
||||
}
|
||||
|
||||
override val root = borderpane {
|
||||
top = mainMenuView.root
|
||||
|
||||
center = tabpane {
|
||||
tabs.bind(mainController.openMaps) { scope, map ->
|
||||
Tab().apply {
|
||||
textProperty().bindBidirectional(map.nameProperty)
|
||||
content = find<MapFragment>(scope).root
|
||||
setOnClosed { mainController.openMaps.remove(scope) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.main.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.main.controller.MainController
|
||||
import com.bartlomiejpluta.base.editor.map.view.MapFragment
|
||||
import javafx.scene.control.Tab
|
||||
import tornadofx.*
|
||||
|
||||
|
||||
class MainView : View("BASE Game Editor") {
|
||||
private val mainController: MainController by di()
|
||||
|
||||
private val mainMenuView = find<MainMenuView>()
|
||||
|
||||
init {
|
||||
mainController.openProject.addListener { _, _, project ->
|
||||
val projectName = project?.let { " :: ${it.name} (${it.sourceDirectory.absolutePath})" } ?: ""
|
||||
title = "BASE Game Editor$projectName"
|
||||
}
|
||||
}
|
||||
|
||||
override val root = borderpane {
|
||||
top = mainMenuView.root
|
||||
|
||||
center = tabpane {
|
||||
tabs.bind(mainController.openMaps) { scope, map ->
|
||||
Tab().apply {
|
||||
textProperty().bindBidirectional(map.nameProperty)
|
||||
content = find<MapFragment>(scope).root
|
||||
setOnClosed { mainController.openMaps.remove(scope) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
194
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/canvas/MapCanvas.kt
Executable file → Normal file
194
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/canvas/MapCanvas.kt
Executable file → Normal file
@@ -1,98 +1,98 @@
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
|
||||
import com.bartlomiejpluta.base.editor.render.model.Renderable
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
import javafx.scene.paint.Color
|
||||
|
||||
|
||||
class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, private val painter: MapPainter) : Renderable {
|
||||
var tileSet = map.tileSet
|
||||
private var tileWidth = map.tileWidth
|
||||
private var tileHeight = map.tileHeight
|
||||
|
||||
|
||||
override fun render(gc: GraphicsContext) {
|
||||
gc.clearRect(0.0, 0.0, gc.canvas.width, gc.canvas.height)
|
||||
|
||||
renderBackground(gc)
|
||||
renderUnderlyingLayers(gc)
|
||||
renderCover(gc)
|
||||
renderSelectedLayer(gc)
|
||||
renderGrid(gc)
|
||||
painter.render(gc)
|
||||
}
|
||||
|
||||
private fun renderSelectedLayer(gc: GraphicsContext) {
|
||||
map.layers.getOrNull(editorStateVM.selectedLayer) ?. let { dispatchLayerRender(gc, it) }
|
||||
}
|
||||
|
||||
private fun renderCover(gc: GraphicsContext) {
|
||||
if(!editorStateVM.coverUnderlyingLayers) {
|
||||
return
|
||||
}
|
||||
|
||||
gc.fill = Color.color(0.0, 0.0, 0.0, 0.4)
|
||||
gc.fillRect(0.0, 0.0, map.width, map.height)
|
||||
}
|
||||
|
||||
private fun renderUnderlyingLayers(gc: GraphicsContext) {
|
||||
for(layer in map.layers.dropLast(if(editorStateVM.selectedLayer < 0) 0 else map.layers.size - editorStateVM.selectedLayer)) {
|
||||
dispatchLayerRender(gc, layer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dispatchLayerRender(gc: GraphicsContext, layer: Layer) {
|
||||
when (layer) {
|
||||
is TileLayer -> renderTileLayer(gc, layer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderBackground(gc: GraphicsContext) {
|
||||
for (row in 0 until map.rows) {
|
||||
for (column in 0 until map.columns) {
|
||||
gc.fill = if ((row + column) % 2 == 0) BACKGROUND_COLOR1 else BACKGROUND_COLOR2
|
||||
gc.fillRect(column * tileWidth, row * tileHeight, tileWidth, tileHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderTileLayer(gc: GraphicsContext, tileLayer: TileLayer) {
|
||||
for ((row, columns) in tileLayer.layer.withIndex()) {
|
||||
for ((column, tile) in columns.withIndex()) {
|
||||
if (tile != null) {
|
||||
gc.drawImage(tile.image, column * tile.image.width, row * tile.image.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderGrid(gc: GraphicsContext) {
|
||||
if(!editorStateVM.showGrid) {
|
||||
return
|
||||
}
|
||||
|
||||
gc.lineWidth = 1.5
|
||||
|
||||
gc.strokeLine(0.0, 0.0, map.width, 0.0)
|
||||
gc.strokeLine(0.0, 0.0, 0.0, map.height)
|
||||
gc.strokeLine(map.width, 0.0, map.width, map.height)
|
||||
gc.strokeLine(0.0, map.height, map.width, map.height)
|
||||
|
||||
for (row in 0 until map.rows) {
|
||||
gc.strokeLine(0.0, row * tileHeight, map.width, row * tileHeight)
|
||||
}
|
||||
|
||||
for (column in 0 until map.columns) {
|
||||
gc.strokeLine(column * tileWidth, 0.0, column * tileWidth, map.height)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val BACKGROUND_COLOR1 = Color.color(1.0, 1.0, 1.0, 1.0)
|
||||
private val BACKGROUND_COLOR2 = Color.color(0.8, 0.8, 0.8, 1.0)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
|
||||
import com.bartlomiejpluta.base.editor.render.model.Renderable
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
import javafx.scene.paint.Color
|
||||
|
||||
|
||||
class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, private val painter: MapPainter) : Renderable {
|
||||
var tileSet = map.tileSet
|
||||
private var tileWidth = map.tileWidth
|
||||
private var tileHeight = map.tileHeight
|
||||
|
||||
|
||||
override fun render(gc: GraphicsContext) {
|
||||
gc.clearRect(0.0, 0.0, gc.canvas.width, gc.canvas.height)
|
||||
|
||||
renderBackground(gc)
|
||||
renderUnderlyingLayers(gc)
|
||||
renderCover(gc)
|
||||
renderSelectedLayer(gc)
|
||||
renderGrid(gc)
|
||||
painter.render(gc)
|
||||
}
|
||||
|
||||
private fun renderSelectedLayer(gc: GraphicsContext) {
|
||||
map.layers.getOrNull(editorStateVM.selectedLayer) ?. let { dispatchLayerRender(gc, it) }
|
||||
}
|
||||
|
||||
private fun renderCover(gc: GraphicsContext) {
|
||||
if(!editorStateVM.coverUnderlyingLayers) {
|
||||
return
|
||||
}
|
||||
|
||||
gc.fill = Color.color(0.0, 0.0, 0.0, 0.4)
|
||||
gc.fillRect(0.0, 0.0, map.width, map.height)
|
||||
}
|
||||
|
||||
private fun renderUnderlyingLayers(gc: GraphicsContext) {
|
||||
for(layer in map.layers.dropLast(if(editorStateVM.selectedLayer < 0) 0 else map.layers.size - editorStateVM.selectedLayer)) {
|
||||
dispatchLayerRender(gc, layer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dispatchLayerRender(gc: GraphicsContext, layer: Layer) {
|
||||
when (layer) {
|
||||
is TileLayer -> renderTileLayer(gc, layer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderBackground(gc: GraphicsContext) {
|
||||
for (row in 0 until map.rows) {
|
||||
for (column in 0 until map.columns) {
|
||||
gc.fill = if ((row + column) % 2 == 0) BACKGROUND_COLOR1 else BACKGROUND_COLOR2
|
||||
gc.fillRect(column * tileWidth, row * tileHeight, tileWidth, tileHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderTileLayer(gc: GraphicsContext, tileLayer: TileLayer) {
|
||||
for ((row, columns) in tileLayer.layer.withIndex()) {
|
||||
for ((column, tile) in columns.withIndex()) {
|
||||
if (tile != null) {
|
||||
gc.drawImage(tile.image, column * tile.image.width, row * tile.image.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderGrid(gc: GraphicsContext) {
|
||||
if(!editorStateVM.showGrid) {
|
||||
return
|
||||
}
|
||||
|
||||
gc.lineWidth = 1.5
|
||||
|
||||
gc.strokeLine(0.0, 0.0, map.width, 0.0)
|
||||
gc.strokeLine(0.0, 0.0, 0.0, map.height)
|
||||
gc.strokeLine(map.width, 0.0, map.width, map.height)
|
||||
gc.strokeLine(0.0, map.height, map.width, map.height)
|
||||
|
||||
for (row in 0 until map.rows) {
|
||||
gc.strokeLine(0.0, row * tileHeight, map.width, row * tileHeight)
|
||||
}
|
||||
|
||||
for (column in 0 until map.columns) {
|
||||
gc.strokeLine(column * tileWidth, 0.0, column * tileWidth, map.height)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val BACKGROUND_COLOR1 = Color.color(1.0, 1.0, 1.0, 1.0)
|
||||
private val BACKGROUND_COLOR2 = Color.color(0.8, 0.8, 0.8, 1.0)
|
||||
}
|
||||
}
|
||||
186
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/canvas/MapPainter.kt
Executable file → Normal file
186
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/canvas/MapPainter.kt
Executable file → Normal file
@@ -1,94 +1,94 @@
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEventHandler
|
||||
import com.bartlomiejpluta.base.editor.render.model.Renderable
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.input.MouseEvent
|
||||
import javafx.scene.paint.Color
|
||||
|
||||
class MapPainter(
|
||||
private val mapVM: GameMapVM,
|
||||
private val brushVM: BrushVM,
|
||||
private val editorStateVM: EditorStateVM,
|
||||
private val paintingCallback: (MapPaintingTrace) -> Unit
|
||||
) : Renderable, MapMouseEventHandler {
|
||||
private val tileWidth = mapVM.tileSet.tileWidth.toDouble()
|
||||
private val tileHeight = mapVM.tileSet.tileHeight.toDouble()
|
||||
|
||||
private var currentTrace: MapPaintingTrace? = null
|
||||
|
||||
override fun render(gc: GraphicsContext) {
|
||||
val alpha = gc.globalAlpha
|
||||
gc.globalAlpha = 0.4
|
||||
|
||||
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
|
||||
renderTile(gc, row, column, centerRow, centerColumn, tile)
|
||||
}
|
||||
|
||||
gc.globalAlpha = alpha
|
||||
}
|
||||
|
||||
private fun renderTile(gc: GraphicsContext, row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) {
|
||||
val x = tileWidth * (editorStateVM.cursorColumn - centerColumn + column)
|
||||
val y = tileHeight * (editorStateVM.cursorRow - centerRow + row)
|
||||
|
||||
when {
|
||||
tile != null -> renderPaintingBrushTile(gc, tile, x, y)
|
||||
else -> renderEraserTile(gc, x, y)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderPaintingBrushTile(gc: GraphicsContext, tile: Tile, x: Double, y: Double) =
|
||||
gc.drawImage(tile.image, x, y)
|
||||
|
||||
private fun renderEraserTile(gc: GraphicsContext, x: Double, y: Double) {
|
||||
gc.fill = Color.WHITE
|
||||
gc.fillRect(x, y, tileWidth, tileHeight)
|
||||
}
|
||||
|
||||
override fun handleMouseInput(event: MapMouseEvent) {
|
||||
editorStateVM.cursorRowProperty.value = event.row
|
||||
editorStateVM.cursorColumnProperty.value = event.column
|
||||
|
||||
when (event.type) {
|
||||
MouseEvent.MOUSE_PRESSED -> beginTrace(event)
|
||||
MouseEvent.MOUSE_DRAGGED -> proceedTrace(event)
|
||||
MouseEvent.MOUSE_RELEASED -> commitTrace(event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun beginTrace(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY && editorStateVM.selectedLayer >= 0) {
|
||||
currentTrace = MapPaintingTrace(mapVM, "Paint trace").apply {
|
||||
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
|
||||
paint(editorStateVM.selectedLayer, editorStateVM.cursorRow - centerRow + row, editorStateVM.cursorColumn - centerColumn + column, tile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedTrace(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
currentTrace?.apply {
|
||||
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
|
||||
paint(editorStateVM.selectedLayer, editorStateVM.cursorRow - centerRow + row, editorStateVM.cursorColumn - centerColumn + column, tile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun commitTrace(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
currentTrace?.let {
|
||||
paintingCallback(it)
|
||||
currentTrace = null
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEventHandler
|
||||
import com.bartlomiejpluta.base.editor.render.model.Renderable
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.input.MouseEvent
|
||||
import javafx.scene.paint.Color
|
||||
|
||||
class MapPainter(
|
||||
private val mapVM: GameMapVM,
|
||||
private val brushVM: BrushVM,
|
||||
private val editorStateVM: EditorStateVM,
|
||||
private val paintingCallback: (MapPaintingTrace) -> Unit
|
||||
) : Renderable, MapMouseEventHandler {
|
||||
private val tileWidth = mapVM.tileSet.tileWidth.toDouble()
|
||||
private val tileHeight = mapVM.tileSet.tileHeight.toDouble()
|
||||
|
||||
private var currentTrace: MapPaintingTrace? = null
|
||||
|
||||
override fun render(gc: GraphicsContext) {
|
||||
val alpha = gc.globalAlpha
|
||||
gc.globalAlpha = 0.4
|
||||
|
||||
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
|
||||
renderTile(gc, row, column, centerRow, centerColumn, tile)
|
||||
}
|
||||
|
||||
gc.globalAlpha = alpha
|
||||
}
|
||||
|
||||
private fun renderTile(gc: GraphicsContext, row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) {
|
||||
val x = tileWidth * (editorStateVM.cursorColumn - centerColumn + column)
|
||||
val y = tileHeight * (editorStateVM.cursorRow - centerRow + row)
|
||||
|
||||
when {
|
||||
tile != null -> renderPaintingBrushTile(gc, tile, x, y)
|
||||
else -> renderEraserTile(gc, x, y)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderPaintingBrushTile(gc: GraphicsContext, tile: Tile, x: Double, y: Double) =
|
||||
gc.drawImage(tile.image, x, y)
|
||||
|
||||
private fun renderEraserTile(gc: GraphicsContext, x: Double, y: Double) {
|
||||
gc.fill = Color.WHITE
|
||||
gc.fillRect(x, y, tileWidth, tileHeight)
|
||||
}
|
||||
|
||||
override fun handleMouseInput(event: MapMouseEvent) {
|
||||
editorStateVM.cursorRowProperty.value = event.row
|
||||
editorStateVM.cursorColumnProperty.value = event.column
|
||||
|
||||
when (event.type) {
|
||||
MouseEvent.MOUSE_PRESSED -> beginTrace(event)
|
||||
MouseEvent.MOUSE_DRAGGED -> proceedTrace(event)
|
||||
MouseEvent.MOUSE_RELEASED -> commitTrace(event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun beginTrace(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY && editorStateVM.selectedLayer >= 0) {
|
||||
currentTrace = MapPaintingTrace(mapVM, "Paint trace").apply {
|
||||
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
|
||||
paint(editorStateVM.selectedLayer, editorStateVM.cursorRow - centerRow + row, editorStateVM.cursorColumn - centerColumn + column, tile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedTrace(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
currentTrace?.apply {
|
||||
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
|
||||
paint(editorStateVM.selectedLayer, editorStateVM.cursorRow - centerRow + row, editorStateVM.cursorColumn - centerColumn + column, tile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun commitTrace(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
currentTrace?.let {
|
||||
paintingCallback(it)
|
||||
currentTrace = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
110
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/canvas/MapPaintingTrace.kt
Executable file → Normal file
110
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/canvas/MapPaintingTrace.kt
Executable file → Normal file
@@ -1,56 +1,56 @@
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
|
||||
|
||||
data class MapPaintingTrace(val map: GameMapVM, override val commandName: String) : Undoable {
|
||||
private val trace = mutableListOf<Element>()
|
||||
|
||||
fun paint(layerIndex: Int, row: Int, column: Int, tile: Tile?) {
|
||||
if (row >= map.rows || column >= map.columns || row < 0 || column < 0 || layerIndex < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
val layer = (map.layers[layerIndex] as TileLayer).layer
|
||||
val formerTile = layer[row][column]
|
||||
|
||||
if (trace.isEmpty()) {
|
||||
trace += Element(layerIndex, row, column, formerTile, tile)
|
||||
layer[row][column] = tile
|
||||
return
|
||||
}
|
||||
|
||||
val tileAlreadyPainted =
|
||||
trace.find { it.layerIndex == layerIndex && it.row == row && it.column == column } != null
|
||||
|
||||
if (!tileAlreadyPainted) {
|
||||
trace += Element(layerIndex, row, column, formerTile, tile)
|
||||
layer[row][column] = tile
|
||||
}
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
trace.forEach {
|
||||
(map.layers[it.layerIndex] as TileLayer).layer[it.row][it.column] = it.formerTile
|
||||
}
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
trace.forEach {
|
||||
(map.layers[it.layerIndex] as TileLayer).layer[it.row][it.column] = it.tile
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private data class Element(
|
||||
val layerIndex: Int,
|
||||
val row: Int,
|
||||
val column: Int,
|
||||
val formerTile: Tile?,
|
||||
val tile: Tile?
|
||||
)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
|
||||
|
||||
data class MapPaintingTrace(val map: GameMapVM, override val commandName: String) : Undoable {
|
||||
private val trace = mutableListOf<Element>()
|
||||
|
||||
fun paint(layerIndex: Int, row: Int, column: Int, tile: Tile?) {
|
||||
if (row >= map.rows || column >= map.columns || row < 0 || column < 0 || layerIndex < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
val layer = (map.layers[layerIndex] as TileLayer).layer
|
||||
val formerTile = layer[row][column]
|
||||
|
||||
if (trace.isEmpty()) {
|
||||
trace += Element(layerIndex, row, column, formerTile, tile)
|
||||
layer[row][column] = tile
|
||||
return
|
||||
}
|
||||
|
||||
val tileAlreadyPainted =
|
||||
trace.find { it.layerIndex == layerIndex && it.row == row && it.column == column } != null
|
||||
|
||||
if (!tileAlreadyPainted) {
|
||||
trace += Element(layerIndex, row, column, formerTile, tile)
|
||||
layer[row][column] = tile
|
||||
}
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
trace.forEach {
|
||||
(map.layers[it.layerIndex] as TileLayer).layer[it.row][it.column] = it.formerTile
|
||||
}
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
trace.forEach {
|
||||
(map.layers[it.layerIndex] as TileLayer).layer[it.row][it.column] = it.tile
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private data class Element(
|
||||
val layerIndex: Int,
|
||||
val row: Int,
|
||||
val column: Int,
|
||||
val formerTile: Tile?,
|
||||
val tile: Tile?
|
||||
)
|
||||
}
|
||||
}
|
||||
104
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/component/MapPane.kt
Executable file → Normal file
104
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/component/MapPane.kt
Executable file → Normal file
@@ -1,53 +1,53 @@
|
||||
package com.bartlomiejpluta.base.editor.map.component
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.canvas.MapCanvas
|
||||
import com.bartlomiejpluta.base.editor.map.canvas.MapPainter
|
||||
import com.bartlomiejpluta.base.editor.map.canvas.MapPaintingTrace
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.event.EventHandler
|
||||
import javafx.scene.canvas.Canvas
|
||||
import javafx.scene.input.MouseEvent
|
||||
|
||||
class MapPane(
|
||||
private val mapVM: GameMapVM,
|
||||
brushVM: BrushVM,
|
||||
editorStateVM: EditorStateVM,
|
||||
paintingCallback: (MapPaintingTrace) -> Unit
|
||||
) : Canvas(), EventHandler<MouseEvent> {
|
||||
private val painter = MapPainter(mapVM, brushVM, editorStateVM, paintingCallback)
|
||||
private val mapCanvas = MapCanvas(mapVM, editorStateVM, painter)
|
||||
|
||||
init {
|
||||
onMouseMoved = this
|
||||
onMouseDragged = this
|
||||
onMousePressed = this
|
||||
onMouseReleased = this
|
||||
|
||||
widthProperty().bind(mapVM.widthProperty)
|
||||
heightProperty().bind(mapVM.heightProperty)
|
||||
|
||||
mapVM.item.rowsProperty.addListener { _, _, _ -> render() }
|
||||
mapVM.item.columnsProperty.addListener { _, _, _ -> render() }
|
||||
|
||||
editorStateVM.showGridProperty.addListener { _, _, _ -> render() }
|
||||
editorStateVM.selectedLayerProperty.addListener { _, _, _ -> render() }
|
||||
editorStateVM.coverUnderlyingLayersProperty.addListener { _, _, _ -> render() }
|
||||
|
||||
render()
|
||||
}
|
||||
|
||||
fun render() {
|
||||
mapCanvas.render(graphicsContext2D)
|
||||
}
|
||||
|
||||
override fun handle(event: MouseEvent?) {
|
||||
if (event != null) {
|
||||
painter.handleMouseInput(MapMouseEvent.of(event, mapVM.tileSet))
|
||||
}
|
||||
|
||||
mapCanvas.render(graphicsContext2D)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.component
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.canvas.MapCanvas
|
||||
import com.bartlomiejpluta.base.editor.map.canvas.MapPainter
|
||||
import com.bartlomiejpluta.base.editor.map.canvas.MapPaintingTrace
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.event.EventHandler
|
||||
import javafx.scene.canvas.Canvas
|
||||
import javafx.scene.input.MouseEvent
|
||||
|
||||
class MapPane(
|
||||
private val mapVM: GameMapVM,
|
||||
brushVM: BrushVM,
|
||||
editorStateVM: EditorStateVM,
|
||||
paintingCallback: (MapPaintingTrace) -> Unit
|
||||
) : Canvas(), EventHandler<MouseEvent> {
|
||||
private val painter = MapPainter(mapVM, brushVM, editorStateVM, paintingCallback)
|
||||
private val mapCanvas = MapCanvas(mapVM, editorStateVM, painter)
|
||||
|
||||
init {
|
||||
onMouseMoved = this
|
||||
onMouseDragged = this
|
||||
onMousePressed = this
|
||||
onMouseReleased = this
|
||||
|
||||
widthProperty().bind(mapVM.widthProperty)
|
||||
heightProperty().bind(mapVM.heightProperty)
|
||||
|
||||
mapVM.item.rowsProperty.addListener { _, _, _ -> render() }
|
||||
mapVM.item.columnsProperty.addListener { _, _, _ -> render() }
|
||||
|
||||
editorStateVM.showGridProperty.addListener { _, _, _ -> render() }
|
||||
editorStateVM.selectedLayerProperty.addListener { _, _, _ -> render() }
|
||||
editorStateVM.coverUnderlyingLayersProperty.addListener { _, _, _ -> render() }
|
||||
|
||||
render()
|
||||
}
|
||||
|
||||
fun render() {
|
||||
mapCanvas.render(graphicsContext2D)
|
||||
}
|
||||
|
||||
override fun handle(event: MouseEvent?) {
|
||||
if (event != null) {
|
||||
painter.handleMouseInput(MapMouseEvent.of(event, mapVM.tileSet))
|
||||
}
|
||||
|
||||
mapCanvas.render(graphicsContext2D)
|
||||
}
|
||||
}
|
||||
194
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/model/brush/Brush.kt
Executable file → Normal file
194
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/model/brush/Brush.kt
Executable file → Normal file
@@ -1,97 +1,97 @@
|
||||
package com.bartlomiejpluta.base.editor.map.model.brush
|
||||
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.collections.ObservableList
|
||||
import tornadofx.asObservable
|
||||
import tornadofx.getValue
|
||||
import tornadofx.observableListOf
|
||||
import tornadofx.setValue
|
||||
|
||||
class Brush {
|
||||
val brush: ObservableList<Tile>
|
||||
|
||||
val rowsProperty = SimpleIntegerProperty(0)
|
||||
var rows by rowsProperty
|
||||
private set
|
||||
|
||||
val columnsProperty = SimpleIntegerProperty(0)
|
||||
var columns by columnsProperty
|
||||
private set
|
||||
|
||||
val rangeProperty = SimpleIntegerProperty(1)
|
||||
var range by rangeProperty
|
||||
private set
|
||||
|
||||
val modeProperty = SimpleObjectProperty(BrushMode.PAINTING_MODE)
|
||||
var mode by modeProperty
|
||||
private set
|
||||
|
||||
private constructor(brushArray: Array<Array<Tile>>) {
|
||||
rowsProperty.value = brushArray.size
|
||||
|
||||
brush = observableListOf()
|
||||
|
||||
brushArray.forEach { brush.addAll(it) }
|
||||
|
||||
if (rowsProperty.value > 0) {
|
||||
columns = brush.size / rowsProperty.value
|
||||
}
|
||||
}
|
||||
|
||||
private constructor(brush: List<Tile>, rows: Int, columns: Int) {
|
||||
this.rows = rows
|
||||
this.columns = columns
|
||||
|
||||
this.brush = brush.asObservable()
|
||||
}
|
||||
|
||||
fun forEach(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
|
||||
return when {
|
||||
range > 1 || mode == BrushMode.ERASING_MODE -> forEachInRangedBrush(consumer)
|
||||
else -> forEachInRegularBrush(consumer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forEachInRangedBrush(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
|
||||
val center = range / 2
|
||||
|
||||
(0 until range).forEach { row ->
|
||||
(0 until range).forEach { column ->
|
||||
consumer(row, column, center, center, getTileByMode(brush[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTileByMode(tile: Tile) = when (mode) {
|
||||
BrushMode.PAINTING_MODE -> tile
|
||||
BrushMode.ERASING_MODE -> null
|
||||
}
|
||||
|
||||
private fun forEachInRegularBrush(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
|
||||
val centerRow = rows / 2
|
||||
val centerColumn = columns / 2
|
||||
|
||||
brush.forEachIndexed { id, tile ->
|
||||
consumer(id / columns, id % columns, centerRow, centerColumn, getTileByMode(tile))
|
||||
}
|
||||
}
|
||||
|
||||
private fun clone() = Brush(brush, rows, columns).apply {
|
||||
this.range = this@Brush.range
|
||||
this.mode = this@Brush.mode
|
||||
}
|
||||
|
||||
fun withRange(range: Int) = clone().apply {
|
||||
this.range = range
|
||||
}
|
||||
|
||||
fun withMode(mode: BrushMode) = clone().apply {
|
||||
this.mode = mode
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(brushArray: Array<Array<Tile>>) = Brush(brushArray)
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.model.brush
|
||||
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.collections.ObservableList
|
||||
import tornadofx.asObservable
|
||||
import tornadofx.getValue
|
||||
import tornadofx.observableListOf
|
||||
import tornadofx.setValue
|
||||
|
||||
class Brush {
|
||||
val brush: ObservableList<Tile>
|
||||
|
||||
val rowsProperty = SimpleIntegerProperty(0)
|
||||
var rows by rowsProperty
|
||||
private set
|
||||
|
||||
val columnsProperty = SimpleIntegerProperty(0)
|
||||
var columns by columnsProperty
|
||||
private set
|
||||
|
||||
val rangeProperty = SimpleIntegerProperty(1)
|
||||
var range by rangeProperty
|
||||
private set
|
||||
|
||||
val modeProperty = SimpleObjectProperty(BrushMode.PAINTING_MODE)
|
||||
var mode by modeProperty
|
||||
private set
|
||||
|
||||
private constructor(brushArray: Array<Array<Tile>>) {
|
||||
rowsProperty.value = brushArray.size
|
||||
|
||||
brush = observableListOf()
|
||||
|
||||
brushArray.forEach { brush.addAll(it) }
|
||||
|
||||
if (rowsProperty.value > 0) {
|
||||
columns = brush.size / rowsProperty.value
|
||||
}
|
||||
}
|
||||
|
||||
private constructor(brush: List<Tile>, rows: Int, columns: Int) {
|
||||
this.rows = rows
|
||||
this.columns = columns
|
||||
|
||||
this.brush = brush.asObservable()
|
||||
}
|
||||
|
||||
fun forEach(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
|
||||
return when {
|
||||
range > 1 || mode == BrushMode.ERASING_MODE -> forEachInRangedBrush(consumer)
|
||||
else -> forEachInRegularBrush(consumer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forEachInRangedBrush(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
|
||||
val center = range / 2
|
||||
|
||||
(0 until range).forEach { row ->
|
||||
(0 until range).forEach { column ->
|
||||
consumer(row, column, center, center, getTileByMode(brush[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTileByMode(tile: Tile) = when (mode) {
|
||||
BrushMode.PAINTING_MODE -> tile
|
||||
BrushMode.ERASING_MODE -> null
|
||||
}
|
||||
|
||||
private fun forEachInRegularBrush(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
|
||||
val centerRow = rows / 2
|
||||
val centerColumn = columns / 2
|
||||
|
||||
brush.forEachIndexed { id, tile ->
|
||||
consumer(id / columns, id % columns, centerRow, centerColumn, getTileByMode(tile))
|
||||
}
|
||||
}
|
||||
|
||||
private fun clone() = Brush(brush, rows, columns).apply {
|
||||
this.range = this@Brush.range
|
||||
this.mode = this@Brush.mode
|
||||
}
|
||||
|
||||
fun withRange(range: Int) = clone().apply {
|
||||
this.range = range
|
||||
}
|
||||
|
||||
fun withMode(mode: BrushMode) = clone().apply {
|
||||
this.mode = mode
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(brushArray: Array<Array<Tile>>) = Brush(brushArray)
|
||||
}
|
||||
}
|
||||
|
||||
10
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/model/brush/BrushMode.kt
Executable file → Normal file
10
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/model/brush/BrushMode.kt
Executable file → Normal file
@@ -1,6 +1,6 @@
|
||||
package com.bartlomiejpluta.base.editor.map.model.brush
|
||||
|
||||
enum class BrushMode {
|
||||
PAINTING_MODE,
|
||||
ERASING_MODE
|
||||
package com.bartlomiejpluta.base.editor.map.model.brush
|
||||
|
||||
enum class BrushMode {
|
||||
PAINTING_MODE,
|
||||
ERASING_MODE
|
||||
}
|
||||
22
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/model/layer/Layer.kt
Executable file → Normal file
22
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/model/layer/Layer.kt
Executable file → Normal file
@@ -1,12 +1,12 @@
|
||||
package com.bartlomiejpluta.base.editor.map.model.layer
|
||||
|
||||
import javafx.beans.property.StringProperty
|
||||
|
||||
interface Layer {
|
||||
var name: String
|
||||
val nameProperty: StringProperty
|
||||
|
||||
fun resize(rows: Int, columns: Int)
|
||||
|
||||
fun clone(): Layer
|
||||
package com.bartlomiejpluta.base.editor.map.model.layer
|
||||
|
||||
import javafx.beans.property.StringProperty
|
||||
|
||||
interface Layer {
|
||||
var name: String
|
||||
val nameProperty: StringProperty
|
||||
|
||||
fun resize(rows: Int, columns: Int)
|
||||
|
||||
fun clone(): Layer
|
||||
}
|
||||
60
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/model/layer/TileLayer.kt
Executable file → Normal file
60
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/model/layer/TileLayer.kt
Executable file → Normal file
@@ -1,31 +1,31 @@
|
||||
package com.bartlomiejpluta.base.editor.map.model.layer
|
||||
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
|
||||
class TileLayer(
|
||||
name: String,
|
||||
rows: Int,
|
||||
columns: Int,
|
||||
layer: Array<Array<Tile?>> = Array(rows) { Array(columns) { null } }
|
||||
) : Layer {
|
||||
var layer = layer
|
||||
private set
|
||||
|
||||
override val nameProperty = SimpleStringProperty(name)
|
||||
override var name: String by nameProperty
|
||||
|
||||
override fun resize(rows: Int, columns: Int) {
|
||||
layer = Array(rows) { row ->
|
||||
Array(columns) { column ->
|
||||
layer.getOrNull(row)?.getOrNull(column)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun clone() = TileLayer(name, 0, 0).apply {
|
||||
layer = this@TileLayer.layer
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.model.layer
|
||||
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
|
||||
class TileLayer(
|
||||
name: String,
|
||||
rows: Int,
|
||||
columns: Int,
|
||||
layer: Array<Array<Tile?>> = Array(rows) { Array(columns) { null } }
|
||||
) : Layer {
|
||||
var layer = layer
|
||||
private set
|
||||
|
||||
override val nameProperty = SimpleStringProperty(name)
|
||||
override var name: String by nameProperty
|
||||
|
||||
override fun resize(rows: Int, columns: Int) {
|
||||
layer = Array(rows) { row ->
|
||||
Array(columns) { column ->
|
||||
layer.getOrNull(row)?.getOrNull(column)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun clone() = TileLayer(name, 0, 0).apply {
|
||||
layer = this@TileLayer.layer
|
||||
}
|
||||
}
|
||||
108
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/model/map/GameMap.kt
Executable file → Normal file
108
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/model/map/GameMap.kt
Executable file → Normal file
@@ -1,54 +1,54 @@
|
||||
package com.bartlomiejpluta.base.editor.map.model.map
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
|
||||
import javafx.beans.property.SimpleDoubleProperty
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import tornadofx.getValue
|
||||
import tornadofx.observableListOf
|
||||
import tornadofx.setValue
|
||||
|
||||
|
||||
class GameMap(val tileSet: TileSet) {
|
||||
val layers = observableListOf<Layer>()
|
||||
|
||||
val nameProperty = SimpleStringProperty()
|
||||
var name by nameProperty
|
||||
|
||||
val tileWidth = tileSet.tileWidth.toDouble()
|
||||
val tileHeight = tileSet.tileHeight.toDouble()
|
||||
|
||||
val rowsProperty = SimpleIntegerProperty(INITIAL_ROWS)
|
||||
var rows by rowsProperty
|
||||
|
||||
val columnsProperty = SimpleIntegerProperty(INITIAL_COLUMNS)
|
||||
var columns by columnsProperty
|
||||
|
||||
val widthProperty = SimpleDoubleProperty(INITIAL_COLUMNS * tileWidth)
|
||||
var width by widthProperty
|
||||
private set
|
||||
|
||||
val heightProperty = SimpleDoubleProperty(INITIAL_ROWS * tileHeight)
|
||||
var height by heightProperty
|
||||
private set
|
||||
|
||||
init {
|
||||
rowsProperty.addListener { _, _, newValue ->
|
||||
val newRows = newValue.toInt()
|
||||
height = newRows * tileWidth
|
||||
layers.forEach { it.resize(newRows, columns) }
|
||||
}
|
||||
|
||||
columnsProperty.addListener { _, _, newValue ->
|
||||
val newColumns = newValue.toInt()
|
||||
width = newColumns * tileWidth
|
||||
layers.forEach { it.resize(rows, newColumns) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INITIAL_ROWS = 20
|
||||
private const val INITIAL_COLUMNS = 20
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.model.map
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
|
||||
import javafx.beans.property.SimpleDoubleProperty
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import tornadofx.getValue
|
||||
import tornadofx.observableListOf
|
||||
import tornadofx.setValue
|
||||
|
||||
|
||||
class GameMap(val tileSet: TileSet) {
|
||||
val layers = observableListOf<Layer>()
|
||||
|
||||
val nameProperty = SimpleStringProperty()
|
||||
var name by nameProperty
|
||||
|
||||
val tileWidth = tileSet.tileWidth.toDouble()
|
||||
val tileHeight = tileSet.tileHeight.toDouble()
|
||||
|
||||
val rowsProperty = SimpleIntegerProperty(INITIAL_ROWS)
|
||||
var rows by rowsProperty
|
||||
|
||||
val columnsProperty = SimpleIntegerProperty(INITIAL_COLUMNS)
|
||||
var columns by columnsProperty
|
||||
|
||||
val widthProperty = SimpleDoubleProperty(INITIAL_COLUMNS * tileWidth)
|
||||
var width by widthProperty
|
||||
private set
|
||||
|
||||
val heightProperty = SimpleDoubleProperty(INITIAL_ROWS * tileHeight)
|
||||
var height by heightProperty
|
||||
private set
|
||||
|
||||
init {
|
||||
rowsProperty.addListener { _, _, newValue ->
|
||||
val newRows = newValue.toInt()
|
||||
height = newRows * tileWidth
|
||||
layers.forEach { it.resize(newRows, columns) }
|
||||
}
|
||||
|
||||
columnsProperty.addListener { _, _, newValue ->
|
||||
val newColumns = newValue.toInt()
|
||||
width = newColumns * tileWidth
|
||||
layers.forEach { it.resize(rows, newColumns) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INITIAL_ROWS = 20
|
||||
private const val INITIAL_COLUMNS = 20
|
||||
}
|
||||
}
|
||||
|
||||
10
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/serial/MapDeserializer.kt
Executable file → Normal file
10
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/serial/MapDeserializer.kt
Executable file → Normal file
@@ -1,6 +1,6 @@
|
||||
package com.bartlomiejpluta.base.editor.map.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.common.serial.Deserializer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
|
||||
package com.bartlomiejpluta.base.editor.map.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.common.serial.Deserializer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
|
||||
interface MapDeserializer : Deserializer<GameMap>
|
||||
10
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/serial/MapSerializer.kt
Executable file → Normal file
10
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/serial/MapSerializer.kt
Executable file → Normal file
@@ -1,6 +1,6 @@
|
||||
package com.bartlomiejpluta.base.editor.map.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.common.serial.Serializer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
|
||||
package com.bartlomiejpluta.base.editor.map.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.common.serial.Serializer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
|
||||
interface MapSerializer : Serializer<GameMap>
|
||||
102
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/serial/ProtobufMapDeserializer.kt
Executable file → Normal file
102
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/serial/ProtobufMapDeserializer.kt
Executable file → Normal file
@@ -1,52 +1,52 @@
|
||||
package com.bartlomiejpluta.base.editor.map.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
|
||||
import com.bartlomiejpluta.base.proto.GameMapProto
|
||||
import org.springframework.stereotype.Component
|
||||
import tornadofx.ResourceLookup
|
||||
import java.io.InputStream
|
||||
|
||||
@Component
|
||||
class ProtobufMapDeserializer : MapDeserializer {
|
||||
private val resources = ResourceLookup(this)
|
||||
private val tileset = TileSet(resources.image("/textures/tileset.png"), 160, 8)
|
||||
|
||||
override fun deserialize(input: InputStream): GameMap {
|
||||
val map = GameMap(tileset)
|
||||
val proto = GameMapProto.GameMap.parseFrom(input)
|
||||
map.name = proto.name
|
||||
map.rows = proto.rows
|
||||
map.columns = proto.columns
|
||||
|
||||
proto.layersList.forEach {
|
||||
map.layers.add(deserializeLayer(map.rows, map.columns, tileset, it))
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
private fun deserializeLayer(rows: Int, columns: Int, tileSet: TileSet, proto: GameMapProto.Layer): Layer {
|
||||
return when {
|
||||
proto.hasTileLayer() -> deserializeTileLayer(rows, columns, tileSet, proto)
|
||||
|
||||
else -> throw IllegalStateException("Not supported layer type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun deserializeTileLayer(rows: Int, columns: Int, tileSet: TileSet, proto: GameMapProto.Layer): Layer {
|
||||
val layer: Array<Array<Tile?>> = Array(rows) { Array(columns) { null } }
|
||||
|
||||
proto.tileLayer.tilesList.forEachIndexed { index, tile ->
|
||||
layer[index / columns][index % columns] = when(tile) {
|
||||
0 -> null
|
||||
else -> tileSet.getTile(tile-1)
|
||||
}
|
||||
}
|
||||
|
||||
return TileLayer(proto.name, rows, columns, layer)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
|
||||
import com.bartlomiejpluta.base.proto.GameMapProto
|
||||
import org.springframework.stereotype.Component
|
||||
import tornadofx.ResourceLookup
|
||||
import java.io.InputStream
|
||||
|
||||
@Component
|
||||
class ProtobufMapDeserializer : MapDeserializer {
|
||||
private val resources = ResourceLookup(this)
|
||||
private val tileset = TileSet(resources.image("/textures/tileset.png"), 160, 8)
|
||||
|
||||
override fun deserialize(input: InputStream): GameMap {
|
||||
val map = GameMap(tileset)
|
||||
val proto = GameMapProto.GameMap.parseFrom(input)
|
||||
map.name = proto.name
|
||||
map.rows = proto.rows
|
||||
map.columns = proto.columns
|
||||
|
||||
proto.layersList.forEach {
|
||||
map.layers.add(deserializeLayer(map.rows, map.columns, tileset, it))
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
private fun deserializeLayer(rows: Int, columns: Int, tileSet: TileSet, proto: GameMapProto.Layer): Layer {
|
||||
return when {
|
||||
proto.hasTileLayer() -> deserializeTileLayer(rows, columns, tileSet, proto)
|
||||
|
||||
else -> throw IllegalStateException("Not supported layer type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun deserializeTileLayer(rows: Int, columns: Int, tileSet: TileSet, proto: GameMapProto.Layer): Layer {
|
||||
val layer: Array<Array<Tile?>> = Array(rows) { Array(columns) { null } }
|
||||
|
||||
proto.tileLayer.tilesList.forEachIndexed { index, tile ->
|
||||
layer[index / columns][index % columns] = when(tile) {
|
||||
0 -> null
|
||||
else -> tileSet.getTile(tile-1)
|
||||
}
|
||||
}
|
||||
|
||||
return TileLayer(proto.name, rows, columns, layer)
|
||||
}
|
||||
}
|
||||
66
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/serial/ProtobufMapSerializer.kt
Executable file → Normal file
66
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/serial/ProtobufMapSerializer.kt
Executable file → Normal file
@@ -1,34 +1,34 @@
|
||||
package com.bartlomiejpluta.base.editor.map.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
import com.bartlomiejpluta.base.proto.GameMapProto
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.OutputStream
|
||||
|
||||
@Component
|
||||
class ProtobufMapSerializer : MapSerializer {
|
||||
|
||||
override fun serialize(item: GameMap, output: OutputStream) {
|
||||
val protoMap = GameMapProto.GameMap.newBuilder()
|
||||
protoMap.name = item.name
|
||||
protoMap.rows = item.rows
|
||||
protoMap.columns = item.columns
|
||||
|
||||
item.layers.forEach { layer -> protoMap.addLayers(serializeLayer(layer)) }
|
||||
|
||||
protoMap.build().writeTo(output)
|
||||
}
|
||||
|
||||
private fun serializeLayer(layer: Layer): GameMapProto.Layer {
|
||||
return when (layer) {
|
||||
is TileLayer -> layer.layer.flatMap { it.asIterable() }
|
||||
.fold(GameMapProto.TileLayer.newBuilder()) { acc, tile -> acc.addTiles((tile?.id?.plus(1)) ?: 0) }
|
||||
.build()
|
||||
.let { GameMapProto.Layer.newBuilder().setName(layer.name).setTileLayer(it).build() }
|
||||
|
||||
else -> throw IllegalStateException("Not supported layer type")
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
import com.bartlomiejpluta.base.proto.GameMapProto
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.OutputStream
|
||||
|
||||
@Component
|
||||
class ProtobufMapSerializer : MapSerializer {
|
||||
|
||||
override fun serialize(item: GameMap, output: OutputStream) {
|
||||
val protoMap = GameMapProto.GameMap.newBuilder()
|
||||
protoMap.name = item.name
|
||||
protoMap.rows = item.rows
|
||||
protoMap.columns = item.columns
|
||||
|
||||
item.layers.forEach { layer -> protoMap.addLayers(serializeLayer(layer)) }
|
||||
|
||||
protoMap.build().writeTo(output)
|
||||
}
|
||||
|
||||
private fun serializeLayer(layer: Layer): GameMapProto.Layer {
|
||||
return when (layer) {
|
||||
is TileLayer -> layer.layer.flatMap { it.asIterable() }
|
||||
.fold(GameMapProto.TileLayer.newBuilder()) { acc, tile -> acc.addTiles((tile?.id?.plus(1)) ?: 0) }
|
||||
.build()
|
||||
.let { GameMapProto.Layer.newBuilder().setName(layer.name).setTileLayer(it).build() }
|
||||
|
||||
else -> throw IllegalStateException("Not supported layer type")
|
||||
}
|
||||
}
|
||||
}
|
||||
72
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapFragment.kt
Executable file → Normal file
72
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapFragment.kt
Executable file → Normal file
@@ -1,36 +1,36 @@
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.tileset.view.TileSetView
|
||||
import tornadofx.*
|
||||
|
||||
|
||||
class MapFragment : Fragment() {
|
||||
override val scope = super.scope as UndoableScope
|
||||
|
||||
private val mapView = find<MapView>()
|
||||
private val layersView = find<MapLayersView>()
|
||||
private val tileSetView = find<TileSetView>()
|
||||
private val toolbarView = find<MapToolbarView>()
|
||||
private val statusBarView = find<MapStatusBarView>()
|
||||
|
||||
override val root = borderpane {
|
||||
top = toolbarView.root
|
||||
|
||||
center = mapView.root
|
||||
|
||||
right = drawer(multiselect = true) {
|
||||
item("Layers", expanded = true) {
|
||||
this += layersView.root
|
||||
}
|
||||
|
||||
item("Tile Set", expanded = true) {
|
||||
this += tileSetView.root.apply {
|
||||
maxHeightProperty().bind(this@item.heightProperty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bottom = statusBarView.root
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.tileset.view.TileSetView
|
||||
import tornadofx.*
|
||||
|
||||
|
||||
class MapFragment : Fragment() {
|
||||
override val scope = super.scope as UndoableScope
|
||||
|
||||
private val mapView = find<MapView>()
|
||||
private val layersView = find<MapLayersView>()
|
||||
private val tileSetView = find<TileSetView>()
|
||||
private val toolbarView = find<MapToolbarView>()
|
||||
private val statusBarView = find<MapStatusBarView>()
|
||||
|
||||
override val root = borderpane {
|
||||
top = toolbarView.root
|
||||
|
||||
center = mapView.root
|
||||
|
||||
right = drawer(multiselect = true) {
|
||||
item("Layers", expanded = true) {
|
||||
this += layersView.root
|
||||
}
|
||||
|
||||
item("Tile Set", expanded = true) {
|
||||
this += tileSetView.root.apply {
|
||||
maxHeightProperty().bind(this@item.heightProperty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bottom = statusBarView.root
|
||||
}
|
||||
}
|
||||
|
||||
188
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapLayersView.kt
Executable file → Normal file
188
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapLayersView.kt
Executable file → Normal file
@@ -1,95 +1,95 @@
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.command.model.map.CreateLayerCommand
|
||||
import com.bartlomiejpluta.base.editor.command.model.map.MoveLayerCommand
|
||||
import com.bartlomiejpluta.base.editor.command.model.map.RemoveLayerCommand
|
||||
import com.bartlomiejpluta.base.editor.command.model.map.RenameLayerCommand
|
||||
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
|
||||
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.control.TableView
|
||||
import org.kordamp.ikonli.javafx.FontIcon
|
||||
import tornadofx.*
|
||||
|
||||
class MapLayersView : View() {
|
||||
private val undoRedoService: UndoRedoService by di()
|
||||
|
||||
override val scope = super.scope as UndoableScope
|
||||
|
||||
private val mapVM = find<GameMapVM>()
|
||||
|
||||
private val editorStateVM = find<EditorStateVM>()
|
||||
|
||||
private var layersPane = TableView(mapVM.layers).apply {
|
||||
column("Layer Name", Layer::nameProperty).makeEditable().setOnEditCommit {
|
||||
val command = RenameLayerCommand(it.rowValue, it.newValue)
|
||||
command.execute()
|
||||
undoRedoService.push(command, scope)
|
||||
}
|
||||
|
||||
editorStateVM.selectedLayerProperty.bind(selectionModel.selectedIndexProperty())
|
||||
}
|
||||
|
||||
override val root = borderpane {
|
||||
center = layersPane
|
||||
|
||||
bottom = toolbar {
|
||||
button(graphic = FontIcon("fa-plus")) {
|
||||
action {
|
||||
val tileLayer = TileLayer("Layer ${mapVM.layers.size + 1}", mapVM.rows, mapVM.columns)
|
||||
val command = CreateLayerCommand(mapVM.item, tileLayer)
|
||||
command.execute()
|
||||
layersPane.selectionModel.select(mapVM.layers.size - 1)
|
||||
undoRedoService.push(command, scope)
|
||||
}
|
||||
}
|
||||
|
||||
button(graphic = FontIcon("fa-chevron-up")) {
|
||||
enableWhen(editorStateVM.selectedLayerProperty.greaterThan(0))
|
||||
action {
|
||||
val newIndex = editorStateVM.selectedLayer - 1
|
||||
val command = MoveLayerCommand(mapVM.item, editorStateVM.selectedLayer, newIndex)
|
||||
command.execute()
|
||||
layersPane.selectionModel.select(newIndex)
|
||||
fire(RedrawMapRequestEvent)
|
||||
undoRedoService.push(command, scope)
|
||||
}
|
||||
}
|
||||
|
||||
button(graphic = FontIcon("fa-chevron-down")) {
|
||||
enableWhen(
|
||||
editorStateVM.selectedLayerProperty.lessThan(mapVM.layers.sizeProperty().minus(1))
|
||||
.and(editorStateVM.selectedLayerProperty.greaterThanOrEqualTo(0))
|
||||
)
|
||||
action {
|
||||
val newIndex = editorStateVM.selectedLayer + 1
|
||||
val command = MoveLayerCommand(mapVM.item, editorStateVM.selectedLayer, newIndex)
|
||||
command.execute()
|
||||
layersPane.selectionModel.select(newIndex)
|
||||
fire(RedrawMapRequestEvent)
|
||||
undoRedoService.push(command, scope)
|
||||
}
|
||||
}
|
||||
|
||||
button(graphic = FontIcon("fa-trash")) {
|
||||
enableWhen(editorStateVM.selectedLayerProperty.greaterThanOrEqualTo(0))
|
||||
action {
|
||||
var index = editorStateVM.selectedLayer
|
||||
val command = RemoveLayerCommand(mapVM.item, index)
|
||||
command.execute()
|
||||
|
||||
if (--index >= 0) {
|
||||
layersPane.selectionModel.select(index)
|
||||
}
|
||||
|
||||
fire(RedrawMapRequestEvent)
|
||||
undoRedoService.push(command, scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.command.model.map.CreateLayerCommand
|
||||
import com.bartlomiejpluta.base.editor.command.model.map.MoveLayerCommand
|
||||
import com.bartlomiejpluta.base.editor.command.model.map.RemoveLayerCommand
|
||||
import com.bartlomiejpluta.base.editor.command.model.map.RenameLayerCommand
|
||||
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
|
||||
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.control.TableView
|
||||
import org.kordamp.ikonli.javafx.FontIcon
|
||||
import tornadofx.*
|
||||
|
||||
class MapLayersView : View() {
|
||||
private val undoRedoService: UndoRedoService by di()
|
||||
|
||||
override val scope = super.scope as UndoableScope
|
||||
|
||||
private val mapVM = find<GameMapVM>()
|
||||
|
||||
private val editorStateVM = find<EditorStateVM>()
|
||||
|
||||
private var layersPane = TableView(mapVM.layers).apply {
|
||||
column("Layer Name", Layer::nameProperty).makeEditable().setOnEditCommit {
|
||||
val command = RenameLayerCommand(it.rowValue, it.newValue)
|
||||
command.execute()
|
||||
undoRedoService.push(command, scope)
|
||||
}
|
||||
|
||||
editorStateVM.selectedLayerProperty.bind(selectionModel.selectedIndexProperty())
|
||||
}
|
||||
|
||||
override val root = borderpane {
|
||||
center = layersPane
|
||||
|
||||
bottom = toolbar {
|
||||
button(graphic = FontIcon("fa-plus")) {
|
||||
action {
|
||||
val tileLayer = TileLayer("Layer ${mapVM.layers.size + 1}", mapVM.rows, mapVM.columns)
|
||||
val command = CreateLayerCommand(mapVM.item, tileLayer)
|
||||
command.execute()
|
||||
layersPane.selectionModel.select(mapVM.layers.size - 1)
|
||||
undoRedoService.push(command, scope)
|
||||
}
|
||||
}
|
||||
|
||||
button(graphic = FontIcon("fa-chevron-up")) {
|
||||
enableWhen(editorStateVM.selectedLayerProperty.greaterThan(0))
|
||||
action {
|
||||
val newIndex = editorStateVM.selectedLayer - 1
|
||||
val command = MoveLayerCommand(mapVM.item, editorStateVM.selectedLayer, newIndex)
|
||||
command.execute()
|
||||
layersPane.selectionModel.select(newIndex)
|
||||
fire(RedrawMapRequestEvent)
|
||||
undoRedoService.push(command, scope)
|
||||
}
|
||||
}
|
||||
|
||||
button(graphic = FontIcon("fa-chevron-down")) {
|
||||
enableWhen(
|
||||
editorStateVM.selectedLayerProperty.lessThan(mapVM.layers.sizeProperty().minus(1))
|
||||
.and(editorStateVM.selectedLayerProperty.greaterThanOrEqualTo(0))
|
||||
)
|
||||
action {
|
||||
val newIndex = editorStateVM.selectedLayer + 1
|
||||
val command = MoveLayerCommand(mapVM.item, editorStateVM.selectedLayer, newIndex)
|
||||
command.execute()
|
||||
layersPane.selectionModel.select(newIndex)
|
||||
fire(RedrawMapRequestEvent)
|
||||
undoRedoService.push(command, scope)
|
||||
}
|
||||
}
|
||||
|
||||
button(graphic = FontIcon("fa-trash")) {
|
||||
enableWhen(editorStateVM.selectedLayerProperty.greaterThanOrEqualTo(0))
|
||||
action {
|
||||
var index = editorStateVM.selectedLayer
|
||||
val command = RemoveLayerCommand(mapVM.item, index)
|
||||
command.execute()
|
||||
|
||||
if (--index >= 0) {
|
||||
layersPane.selectionModel.select(index)
|
||||
}
|
||||
|
||||
fire(RedrawMapRequestEvent)
|
||||
undoRedoService.push(command, scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
164
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapSettingsFragment.kt
Executable file → Normal file
164
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapSettingsFragment.kt
Executable file → Normal file
@@ -1,83 +1,83 @@
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import org.kordamp.ikonli.javafx.FontIcon
|
||||
import tornadofx.*
|
||||
|
||||
class MapSettingsFragment : Fragment("Map Settings") {
|
||||
override val scope = super.scope as UndoableScope
|
||||
private val undoRedoService: UndoRedoService by di()
|
||||
|
||||
private val mapVM = find<GameMapVM>()
|
||||
|
||||
var result = false
|
||||
private set
|
||||
|
||||
override val root = form {
|
||||
icon = FontIcon("fa-map-o")
|
||||
|
||||
fieldset("Map Settings") {
|
||||
field("Map name") {
|
||||
textfield(mapVM.nameProperty) {
|
||||
required()
|
||||
whenDocked { requestFocus() }
|
||||
}
|
||||
}
|
||||
|
||||
field("Rows") {
|
||||
|
||||
textfield(mapVM.rowsProperty) {
|
||||
stripNonInteger()
|
||||
required()
|
||||
validator {
|
||||
when (it?.toIntOrNull()) {
|
||||
in 1..50 -> null
|
||||
in 50..100 -> warning("The map sizes over 50 can impact game performance")
|
||||
else -> error("The map size must be between 1 and 100")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
field("Columns") {
|
||||
textfield(mapVM.columnsProperty) {
|
||||
stripNonInteger()
|
||||
required()
|
||||
validator {
|
||||
when (it?.toIntOrNull()) {
|
||||
in 1..50 -> null
|
||||
in 50..100 -> warning("The map sizes over 50 can impact game performance")
|
||||
else -> error("The map size must be between 1 and 100")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label("Warning: Submitting the form will clear related undo/redo stacks!")
|
||||
}
|
||||
|
||||
buttonbar {
|
||||
button("Ok") {
|
||||
shortcut("Enter")
|
||||
|
||||
action {
|
||||
mapVM.commit {
|
||||
result = true
|
||||
undoRedoService.clear(scope)
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button("Reset") {
|
||||
action { mapVM.rollback() }
|
||||
}
|
||||
|
||||
button("Cancel") {
|
||||
action { close() }
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import org.kordamp.ikonli.javafx.FontIcon
|
||||
import tornadofx.*
|
||||
|
||||
class MapSettingsFragment : Fragment("Map Settings") {
|
||||
override val scope = super.scope as UndoableScope
|
||||
private val undoRedoService: UndoRedoService by di()
|
||||
|
||||
private val mapVM = find<GameMapVM>()
|
||||
|
||||
var result = false
|
||||
private set
|
||||
|
||||
override val root = form {
|
||||
icon = FontIcon("fa-map-o")
|
||||
|
||||
fieldset("Map Settings") {
|
||||
field("Map name") {
|
||||
textfield(mapVM.nameProperty) {
|
||||
required()
|
||||
whenDocked { requestFocus() }
|
||||
}
|
||||
}
|
||||
|
||||
field("Rows") {
|
||||
|
||||
textfield(mapVM.rowsProperty) {
|
||||
stripNonInteger()
|
||||
required()
|
||||
validator {
|
||||
when (it?.toIntOrNull()) {
|
||||
in 1..50 -> null
|
||||
in 50..100 -> warning("The map sizes over 50 can impact game performance")
|
||||
else -> error("The map size must be between 1 and 100")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
field("Columns") {
|
||||
textfield(mapVM.columnsProperty) {
|
||||
stripNonInteger()
|
||||
required()
|
||||
validator {
|
||||
when (it?.toIntOrNull()) {
|
||||
in 1..50 -> null
|
||||
in 50..100 -> warning("The map sizes over 50 can impact game performance")
|
||||
else -> error("The map size must be between 1 and 100")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label("Warning: Submitting the form will clear related undo/redo stacks!")
|
||||
}
|
||||
|
||||
buttonbar {
|
||||
button("Ok") {
|
||||
shortcut("Enter")
|
||||
|
||||
action {
|
||||
mapVM.commit {
|
||||
result = true
|
||||
undoRedoService.clear(scope)
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button("Reset") {
|
||||
action { mapVM.rollback() }
|
||||
}
|
||||
|
||||
button("Cancel") {
|
||||
action { close() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
66
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapStatusBarView.kt
Executable file → Normal file
66
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapStatusBarView.kt
Executable file → Normal file
@@ -1,34 +1,34 @@
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import javafx.beans.binding.Bindings
|
||||
import org.kordamp.ikonli.javafx.FontIcon
|
||||
import tornadofx.*
|
||||
|
||||
class MapStatusBarView : View() {
|
||||
|
||||
private val editorStateVM = find<EditorStateVM>()
|
||||
|
||||
override val root = hbox {
|
||||
spacing = 50.0
|
||||
paddingAll = 5.0
|
||||
|
||||
hbox {
|
||||
this += FontIcon("fa-search-minus")
|
||||
|
||||
slider(0.5..5.0) {
|
||||
bind(editorStateVM.zoomProperty)
|
||||
}
|
||||
|
||||
this += FontIcon("fa-search-plus")
|
||||
}
|
||||
|
||||
label(
|
||||
Bindings.format(
|
||||
"Cursor: %d, %d",
|
||||
editorStateVM.cursorColumnProperty.add(1),
|
||||
editorStateVM.cursorRowProperty.add(1)
|
||||
)
|
||||
)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import javafx.beans.binding.Bindings
|
||||
import org.kordamp.ikonli.javafx.FontIcon
|
||||
import tornadofx.*
|
||||
|
||||
class MapStatusBarView : View() {
|
||||
|
||||
private val editorStateVM = find<EditorStateVM>()
|
||||
|
||||
override val root = hbox {
|
||||
spacing = 50.0
|
||||
paddingAll = 5.0
|
||||
|
||||
hbox {
|
||||
this += FontIcon("fa-search-minus")
|
||||
|
||||
slider(0.5..5.0) {
|
||||
bind(editorStateVM.zoomProperty)
|
||||
}
|
||||
|
||||
this += FontIcon("fa-search-plus")
|
||||
}
|
||||
|
||||
label(
|
||||
Bindings.format(
|
||||
"Cursor: %d, %d",
|
||||
editorStateVM.cursorColumnProperty.add(1),
|
||||
editorStateVM.cursorRowProperty.add(1)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
208
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapToolbarView.kt
Executable file → Normal file
208
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapToolbarView.kt
Executable file → Normal file
@@ -1,105 +1,105 @@
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
|
||||
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.BrushMode
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.control.ToggleGroup
|
||||
import org.kordamp.ikonli.javafx.FontIcon
|
||||
import tornadofx.*
|
||||
|
||||
class MapToolbarView : View() {
|
||||
private val undoRedoService: UndoRedoService by di()
|
||||
|
||||
override val scope = super.scope as UndoableScope
|
||||
|
||||
private val mapVM = find<GameMapVM>()
|
||||
private val brushVM = find<BrushVM>()
|
||||
private val editorStateVM = find<EditorStateVM>()
|
||||
|
||||
private val brushMode = ToggleGroup().apply {
|
||||
brushVM.itemProperty.addListener { _, _, brush ->
|
||||
selectedValueProperty<BrushMode>().value = brush.mode
|
||||
}
|
||||
}
|
||||
|
||||
override val root = toolbar {
|
||||
button(graphic = FontIcon("fa-undo")) {
|
||||
shortcut("Ctrl+Z")
|
||||
action {
|
||||
undoRedoService.undo(scope)
|
||||
fire(RedrawMapRequestEvent)
|
||||
}
|
||||
}
|
||||
|
||||
button(graphic = FontIcon("fa-repeat")) {
|
||||
shortcut("Ctrl+Shift+Z")
|
||||
action {
|
||||
undoRedoService.redo(scope)
|
||||
fire(RedrawMapRequestEvent)
|
||||
}
|
||||
}
|
||||
|
||||
togglebutton {
|
||||
graphic = FontIcon("fa-window-restore")
|
||||
|
||||
action {
|
||||
editorStateVM.coverUnderlyingLayers = isSelected
|
||||
}
|
||||
}
|
||||
|
||||
togglebutton {
|
||||
graphic = FontIcon("fa-th")
|
||||
|
||||
action {
|
||||
editorStateVM.showGrid = isSelected
|
||||
}
|
||||
}
|
||||
|
||||
togglebutton(value = BrushMode.PAINTING_MODE, group = brushMode) {
|
||||
graphic = FontIcon("fa-paint-brush")
|
||||
|
||||
action {
|
||||
brushVM.item = brushVM.withMode(BrushMode.PAINTING_MODE)
|
||||
brushVM.commit()
|
||||
}
|
||||
}
|
||||
|
||||
togglebutton(value = BrushMode.ERASING_MODE, group = brushMode) {
|
||||
graphic = FontIcon("fa-eraser")
|
||||
|
||||
action {
|
||||
brushVM.item = brushVM.withMode(BrushMode.ERASING_MODE)
|
||||
brushVM.commit()
|
||||
}
|
||||
}
|
||||
|
||||
this += FontIcon("fa-paint-brush").apply { iconSize = 10 }
|
||||
|
||||
slider(1..5) {
|
||||
majorTickUnit = 1.0
|
||||
isSnapToTicks = true
|
||||
minorTickCount = 0
|
||||
|
||||
valueProperty().addListener { _, _, newValue ->
|
||||
brushVM.item = brushVM.withRange(newValue.toInt())
|
||||
brushVM.commit()
|
||||
}
|
||||
|
||||
brushVM.itemProperty.addListener { _, _, brush ->
|
||||
value = brush.range.toDouble()
|
||||
}
|
||||
}
|
||||
|
||||
this += FontIcon("fa-paint-brush").apply { iconSize = 15 }
|
||||
|
||||
button(graphic = FontIcon("fa-sliders")) {
|
||||
action {
|
||||
find<MapSettingsFragment>().openModal(block = true, resizable = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
|
||||
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.BrushMode
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.control.ToggleGroup
|
||||
import org.kordamp.ikonli.javafx.FontIcon
|
||||
import tornadofx.*
|
||||
|
||||
class MapToolbarView : View() {
|
||||
private val undoRedoService: UndoRedoService by di()
|
||||
|
||||
override val scope = super.scope as UndoableScope
|
||||
|
||||
private val mapVM = find<GameMapVM>()
|
||||
private val brushVM = find<BrushVM>()
|
||||
private val editorStateVM = find<EditorStateVM>()
|
||||
|
||||
private val brushMode = ToggleGroup().apply {
|
||||
brushVM.itemProperty.addListener { _, _, brush ->
|
||||
selectedValueProperty<BrushMode>().value = brush.mode
|
||||
}
|
||||
}
|
||||
|
||||
override val root = toolbar {
|
||||
button(graphic = FontIcon("fa-undo")) {
|
||||
shortcut("Ctrl+Z")
|
||||
action {
|
||||
undoRedoService.undo(scope)
|
||||
fire(RedrawMapRequestEvent)
|
||||
}
|
||||
}
|
||||
|
||||
button(graphic = FontIcon("fa-repeat")) {
|
||||
shortcut("Ctrl+Shift+Z")
|
||||
action {
|
||||
undoRedoService.redo(scope)
|
||||
fire(RedrawMapRequestEvent)
|
||||
}
|
||||
}
|
||||
|
||||
togglebutton {
|
||||
graphic = FontIcon("fa-window-restore")
|
||||
|
||||
action {
|
||||
editorStateVM.coverUnderlyingLayers = isSelected
|
||||
}
|
||||
}
|
||||
|
||||
togglebutton {
|
||||
graphic = FontIcon("fa-th")
|
||||
|
||||
action {
|
||||
editorStateVM.showGrid = isSelected
|
||||
}
|
||||
}
|
||||
|
||||
togglebutton(value = BrushMode.PAINTING_MODE, group = brushMode) {
|
||||
graphic = FontIcon("fa-paint-brush")
|
||||
|
||||
action {
|
||||
brushVM.item = brushVM.withMode(BrushMode.PAINTING_MODE)
|
||||
brushVM.commit()
|
||||
}
|
||||
}
|
||||
|
||||
togglebutton(value = BrushMode.ERASING_MODE, group = brushMode) {
|
||||
graphic = FontIcon("fa-eraser")
|
||||
|
||||
action {
|
||||
brushVM.item = brushVM.withMode(BrushMode.ERASING_MODE)
|
||||
brushVM.commit()
|
||||
}
|
||||
}
|
||||
|
||||
this += FontIcon("fa-paint-brush").apply { iconSize = 10 }
|
||||
|
||||
slider(1..5) {
|
||||
majorTickUnit = 1.0
|
||||
isSnapToTicks = true
|
||||
minorTickCount = 0
|
||||
|
||||
valueProperty().addListener { _, _, newValue ->
|
||||
brushVM.item = brushVM.withRange(newValue.toInt())
|
||||
brushVM.commit()
|
||||
}
|
||||
|
||||
brushVM.itemProperty.addListener { _, _, brush ->
|
||||
value = brush.range.toDouble()
|
||||
}
|
||||
}
|
||||
|
||||
this += FontIcon("fa-paint-brush").apply { iconSize = 15 }
|
||||
|
||||
button(graphic = FontIcon("fa-sliders")) {
|
||||
action {
|
||||
find<MapSettingsFragment>().openModal(block = true, resizable = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
126
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapView.kt
Executable file → Normal file
126
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/MapView.kt
Executable file → Normal file
@@ -1,64 +1,64 @@
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
|
||||
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
|
||||
import com.bartlomiejpluta.base.editor.map.component.MapPane
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.input.MouseEvent
|
||||
import javafx.scene.transform.Scale
|
||||
import tornadofx.View
|
||||
import tornadofx.group
|
||||
import tornadofx.plusAssign
|
||||
import tornadofx.scrollpane
|
||||
|
||||
|
||||
class MapView : View() {
|
||||
private val undoRedoService: UndoRedoService by di()
|
||||
|
||||
override val scope = super.scope as UndoableScope
|
||||
|
||||
private val mapVM = find<GameMapVM>()
|
||||
|
||||
private val brushVM = find<BrushVM>()
|
||||
|
||||
private val editorStateVM = find<EditorStateVM>()
|
||||
|
||||
private val mapPane = MapPane(mapVM, brushVM, editorStateVM) { undoRedoService.push(it, scope) }
|
||||
|
||||
private val zoom = Scale(1.0, 1.0, 0.0, 0.0).apply {
|
||||
xProperty().bind(editorStateVM.zoomProperty)
|
||||
yProperty().bind(editorStateVM.zoomProperty)
|
||||
}
|
||||
|
||||
init {
|
||||
brushVM.item = mapVM.tileSet.baseBrush
|
||||
brushVM.commit()
|
||||
|
||||
subscribe<RedrawMapRequestEvent> { mapPane.render() }
|
||||
}
|
||||
|
||||
override val root = scrollpane {
|
||||
prefWidth = 640.0
|
||||
prefHeight = 480.0
|
||||
isPannable = true
|
||||
|
||||
group {
|
||||
|
||||
// Let the ScrollPane.viewRect only pan on middle button.
|
||||
addEventHandler(MouseEvent.ANY) {
|
||||
if (it.button != MouseButton.MIDDLE) {
|
||||
it.consume()
|
||||
}
|
||||
}
|
||||
|
||||
group {
|
||||
this += mapPane
|
||||
transforms += zoom
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.map.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
|
||||
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
|
||||
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
|
||||
import com.bartlomiejpluta.base.editor.map.component.MapPane
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.input.MouseEvent
|
||||
import javafx.scene.transform.Scale
|
||||
import tornadofx.View
|
||||
import tornadofx.group
|
||||
import tornadofx.plusAssign
|
||||
import tornadofx.scrollpane
|
||||
|
||||
|
||||
class MapView : View() {
|
||||
private val undoRedoService: UndoRedoService by di()
|
||||
|
||||
override val scope = super.scope as UndoableScope
|
||||
|
||||
private val mapVM = find<GameMapVM>()
|
||||
|
||||
private val brushVM = find<BrushVM>()
|
||||
|
||||
private val editorStateVM = find<EditorStateVM>()
|
||||
|
||||
private val mapPane = MapPane(mapVM, brushVM, editorStateVM) { undoRedoService.push(it, scope) }
|
||||
|
||||
private val zoom = Scale(1.0, 1.0, 0.0, 0.0).apply {
|
||||
xProperty().bind(editorStateVM.zoomProperty)
|
||||
yProperty().bind(editorStateVM.zoomProperty)
|
||||
}
|
||||
|
||||
init {
|
||||
brushVM.item = mapVM.tileSet.baseBrush
|
||||
brushVM.commit()
|
||||
|
||||
subscribe<RedrawMapRequestEvent> { mapPane.render() }
|
||||
}
|
||||
|
||||
override val root = scrollpane {
|
||||
prefWidth = 640.0
|
||||
prefHeight = 480.0
|
||||
isPannable = true
|
||||
|
||||
group {
|
||||
|
||||
// Let the ScrollPane.viewRect only pan on middle button.
|
||||
addEventHandler(MouseEvent.ANY) {
|
||||
if (it.button != MouseButton.MIDDLE) {
|
||||
it.consume()
|
||||
}
|
||||
}
|
||||
|
||||
group {
|
||||
this += mapPane
|
||||
transforms += zoom
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/viewmodel/BrushVM.kt
Executable file → Normal file
56
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/viewmodel/BrushVM.kt
Executable file → Normal file
@@ -1,29 +1,29 @@
|
||||
package com.bartlomiejpluta.base.editor.map.viewmodel
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.BrushMode
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import tornadofx.ItemViewModel
|
||||
import tornadofx.getValue
|
||||
|
||||
class BrushVM : ItemViewModel<Brush>(Brush.of(arrayOf(arrayOf()))) {
|
||||
val brush = bind(Brush::brush)
|
||||
|
||||
val rowsProperty = bind(Brush::rowsProperty)
|
||||
val rows by rowsProperty
|
||||
|
||||
val columnsProperty = bind(Brush::columnsProperty)
|
||||
val columns by columnsProperty
|
||||
|
||||
val rangeProperty = bind(Brush::rangeProperty)
|
||||
val range by rangeProperty
|
||||
|
||||
val modeProperty = bind(Brush::modeProperty)
|
||||
val mode by modeProperty
|
||||
|
||||
fun forEach(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) = item.forEach(consumer)
|
||||
|
||||
fun withRange(range: Int) = item.withRange(range)
|
||||
|
||||
fun withMode(mode: BrushMode) = item.withMode(mode)
|
||||
package com.bartlomiejpluta.base.editor.map.viewmodel
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.BrushMode
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
import tornadofx.ItemViewModel
|
||||
import tornadofx.getValue
|
||||
|
||||
class BrushVM : ItemViewModel<Brush>(Brush.of(arrayOf(arrayOf()))) {
|
||||
val brush = bind(Brush::brush)
|
||||
|
||||
val rowsProperty = bind(Brush::rowsProperty)
|
||||
val rows by rowsProperty
|
||||
|
||||
val columnsProperty = bind(Brush::columnsProperty)
|
||||
val columns by columnsProperty
|
||||
|
||||
val rangeProperty = bind(Brush::rangeProperty)
|
||||
val range by rangeProperty
|
||||
|
||||
val modeProperty = bind(Brush::modeProperty)
|
||||
val mode by modeProperty
|
||||
|
||||
fun forEach(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) = item.forEach(consumer)
|
||||
|
||||
fun withRange(range: Int) = item.withRange(range)
|
||||
|
||||
fun withMode(mode: BrushMode) = item.withMode(mode)
|
||||
}
|
||||
54
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/viewmodel/EditorStateVM.kt
Executable file → Normal file
54
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/viewmodel/EditorStateVM.kt
Executable file → Normal file
@@ -1,28 +1,28 @@
|
||||
package com.bartlomiejpluta.base.editor.map.viewmodel
|
||||
|
||||
import javafx.beans.property.SimpleBooleanProperty
|
||||
import javafx.beans.property.SimpleDoubleProperty
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import tornadofx.ViewModel
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
|
||||
class EditorStateVM : ViewModel() {
|
||||
val selectedLayerProperty = SimpleIntegerProperty(0)
|
||||
var selectedLayer by selectedLayerProperty
|
||||
|
||||
val showGridProperty = SimpleBooleanProperty(true)
|
||||
var showGrid by showGridProperty
|
||||
|
||||
val coverUnderlyingLayersProperty = SimpleBooleanProperty(true)
|
||||
var coverUnderlyingLayers by coverUnderlyingLayersProperty
|
||||
|
||||
val zoomProperty = SimpleDoubleProperty(1.0)
|
||||
var zoom by zoomProperty
|
||||
|
||||
val cursorRowProperty = SimpleIntegerProperty(-1)
|
||||
val cursorRow by cursorRowProperty
|
||||
|
||||
val cursorColumnProperty = SimpleIntegerProperty(-1)
|
||||
val cursorColumn by cursorColumnProperty
|
||||
package com.bartlomiejpluta.base.editor.map.viewmodel
|
||||
|
||||
import javafx.beans.property.SimpleBooleanProperty
|
||||
import javafx.beans.property.SimpleDoubleProperty
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import tornadofx.ViewModel
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
|
||||
class EditorStateVM : ViewModel() {
|
||||
val selectedLayerProperty = SimpleIntegerProperty(0)
|
||||
var selectedLayer by selectedLayerProperty
|
||||
|
||||
val showGridProperty = SimpleBooleanProperty(true)
|
||||
var showGrid by showGridProperty
|
||||
|
||||
val coverUnderlyingLayersProperty = SimpleBooleanProperty(true)
|
||||
var coverUnderlyingLayers by coverUnderlyingLayersProperty
|
||||
|
||||
val zoomProperty = SimpleDoubleProperty(1.0)
|
||||
var zoom by zoomProperty
|
||||
|
||||
val cursorRowProperty = SimpleIntegerProperty(-1)
|
||||
val cursorRow by cursorRowProperty
|
||||
|
||||
val cursorColumnProperty = SimpleIntegerProperty(-1)
|
||||
val cursorColumn by cursorColumnProperty
|
||||
}
|
||||
78
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/viewmodel/GameMapVM.kt
Executable file → Normal file
78
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/viewmodel/GameMapVM.kt
Executable file → Normal file
@@ -1,39 +1,39 @@
|
||||
package com.bartlomiejpluta.base.editor.map.viewmodel
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
import javafx.beans.property.SimpleListProperty
|
||||
import tornadofx.ItemViewModel
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
|
||||
class GameMapVM(map: GameMap) : ItemViewModel<GameMap>(map) {
|
||||
val layers: SimpleListProperty<Layer> = bind(GameMap::layers)
|
||||
|
||||
val nameProperty = bind(GameMap::nameProperty)
|
||||
var name by nameProperty
|
||||
|
||||
val tileSetProperty = bind(GameMap::tileSet)
|
||||
val tileSet by tileSetProperty
|
||||
|
||||
val rowsProperty = bind(GameMap::rowsProperty)
|
||||
var rows by rowsProperty
|
||||
|
||||
val columnsProperty = bind(GameMap::columnsProperty)
|
||||
var columns by columnsProperty
|
||||
|
||||
val tileWidthProperty = bind(GameMap::tileWidth)
|
||||
val tileWidth by tileWidthProperty
|
||||
|
||||
val tileHeightProperty = bind(GameMap::tileHeight)
|
||||
val tileHeight by tileHeightProperty
|
||||
|
||||
val widthProperty = bind(GameMap::widthProperty)
|
||||
val width by widthProperty
|
||||
|
||||
val heightProperty = bind(GameMap::heightProperty)
|
||||
val height by heightProperty
|
||||
}
|
||||
|
||||
|
||||
|
||||
package com.bartlomiejpluta.base.editor.map.viewmodel
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
import javafx.beans.property.SimpleListProperty
|
||||
import tornadofx.ItemViewModel
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
|
||||
class GameMapVM(map: GameMap) : ItemViewModel<GameMap>(map) {
|
||||
val layers: SimpleListProperty<Layer> = bind(GameMap::layers)
|
||||
|
||||
val nameProperty = bind(GameMap::nameProperty)
|
||||
var name by nameProperty
|
||||
|
||||
val tileSetProperty = bind(GameMap::tileSet)
|
||||
val tileSet by tileSetProperty
|
||||
|
||||
val rowsProperty = bind(GameMap::rowsProperty)
|
||||
var rows by rowsProperty
|
||||
|
||||
val columnsProperty = bind(GameMap::columnsProperty)
|
||||
var columns by columnsProperty
|
||||
|
||||
val tileWidthProperty = bind(GameMap::tileWidth)
|
||||
val tileWidth by tileWidthProperty
|
||||
|
||||
val tileHeightProperty = bind(GameMap::tileHeight)
|
||||
val tileHeight by tileHeightProperty
|
||||
|
||||
val widthProperty = bind(GameMap::widthProperty)
|
||||
val width by widthProperty
|
||||
|
||||
val heightProperty = bind(GameMap::heightProperty)
|
||||
val height by heightProperty
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
62
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/manager/DefaultProjectManager.kt
Executable file → Normal file
62
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/manager/DefaultProjectManager.kt
Executable file → Normal file
@@ -1,32 +1,32 @@
|
||||
package com.bartlomiejpluta.base.editor.project.manager
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import com.bartlomiejpluta.base.editor.project.serial.ProjectDeserializer
|
||||
import com.bartlomiejpluta.base.editor.project.serial.ProjectSerializer
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
|
||||
@Component
|
||||
class DefaultProjectManager : ProjectManager {
|
||||
|
||||
@Autowired
|
||||
private lateinit var projectSerializer: ProjectSerializer
|
||||
|
||||
@Autowired
|
||||
private lateinit var projectDeserializer: ProjectDeserializer
|
||||
|
||||
override fun saveProject(project: Project) {
|
||||
project.sourceDirectory.mkdirs()
|
||||
|
||||
FileOutputStream(File(project.sourceDirectory, "project.bep")).use {
|
||||
projectSerializer.serialize(project, it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun openProject(file: File) = FileInputStream(file)
|
||||
.use { projectDeserializer.deserialize(it) }
|
||||
.apply { sourceDirectoryProperty.value = file.parentFile }
|
||||
package com.bartlomiejpluta.base.editor.project.manager
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import com.bartlomiejpluta.base.editor.project.serial.ProjectDeserializer
|
||||
import com.bartlomiejpluta.base.editor.project.serial.ProjectSerializer
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
|
||||
@Component
|
||||
class DefaultProjectManager : ProjectManager {
|
||||
|
||||
@Autowired
|
||||
private lateinit var projectSerializer: ProjectSerializer
|
||||
|
||||
@Autowired
|
||||
private lateinit var projectDeserializer: ProjectDeserializer
|
||||
|
||||
override fun saveProject(project: Project) {
|
||||
project.sourceDirectory.mkdirs()
|
||||
|
||||
FileOutputStream(File(project.sourceDirectory, "project.bep")).use {
|
||||
projectSerializer.serialize(project, it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun openProject(file: File) = FileInputStream(file)
|
||||
.use { projectDeserializer.deserialize(it) }
|
||||
.apply { sourceDirectoryProperty.value = file.parentFile }
|
||||
}
|
||||
16
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/manager/ProjectManager.kt
Executable file → Normal file
16
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/manager/ProjectManager.kt
Executable file → Normal file
@@ -1,9 +1,9 @@
|
||||
package com.bartlomiejpluta.base.editor.project.manager
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import java.io.File
|
||||
|
||||
interface ProjectManager {
|
||||
fun saveProject(project: Project)
|
||||
fun openProject(file: File): Project
|
||||
package com.bartlomiejpluta.base.editor.project.manager
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import java.io.File
|
||||
|
||||
interface ProjectManager {
|
||||
fun saveProject(project: Project)
|
||||
fun openProject(file: File): Project
|
||||
}
|
||||
28
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/model/Project.kt
Executable file → Normal file
28
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/model/Project.kt
Executable file → Normal file
@@ -1,14 +1,14 @@
|
||||
package com.bartlomiejpluta.base.editor.project.model
|
||||
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import tornadofx.*
|
||||
import java.io.File
|
||||
|
||||
class Project {
|
||||
val nameProperty = SimpleStringProperty()
|
||||
var name by nameProperty
|
||||
|
||||
val sourceDirectoryProperty = SimpleObjectProperty<File>()
|
||||
val sourceDirectory by sourceDirectoryProperty
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.project.model
|
||||
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import tornadofx.*
|
||||
import java.io.File
|
||||
|
||||
class Project {
|
||||
val nameProperty = SimpleStringProperty()
|
||||
var name by nameProperty
|
||||
|
||||
val sourceDirectoryProperty = SimpleObjectProperty<File>()
|
||||
val sourceDirectory by sourceDirectoryProperty
|
||||
}
|
||||
|
||||
10
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/serial/ProjectDeserializer.kt
Executable file → Normal file
10
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/serial/ProjectDeserializer.kt
Executable file → Normal file
@@ -1,6 +1,6 @@
|
||||
package com.bartlomiejpluta.base.editor.project.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.common.serial.Deserializer
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
|
||||
package com.bartlomiejpluta.base.editor.project.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.common.serial.Deserializer
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
|
||||
interface ProjectDeserializer : Deserializer<Project>
|
||||
10
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/serial/ProjectSerializer.kt
Executable file → Normal file
10
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/serial/ProjectSerializer.kt
Executable file → Normal file
@@ -1,6 +1,6 @@
|
||||
package com.bartlomiejpluta.base.editor.project.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.common.serial.Serializer
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
|
||||
package com.bartlomiejpluta.base.editor.project.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.common.serial.Serializer
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
|
||||
interface ProjectSerializer : Serializer<Project>
|
||||
34
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/serial/ProtobufProjectDeserializer.kt
Executable file → Normal file
34
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/serial/ProtobufProjectDeserializer.kt
Executable file → Normal file
@@ -1,18 +1,18 @@
|
||||
package com.bartlomiejpluta.base.editor.project.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import com.bartlomiejpluta.base.proto.ProjectProto
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.InputStream
|
||||
|
||||
@Component
|
||||
class ProtobufProjectDeserializer : ProjectDeserializer {
|
||||
|
||||
override fun deserialize(input: InputStream): Project {
|
||||
val proto = ProjectProto.Project.parseFrom(input)
|
||||
val project = Project()
|
||||
project.name = proto.name
|
||||
|
||||
return project
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.project.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import com.bartlomiejpluta.base.proto.ProjectProto
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.InputStream
|
||||
|
||||
@Component
|
||||
class ProtobufProjectDeserializer : ProjectDeserializer {
|
||||
|
||||
override fun deserialize(input: InputStream): Project {
|
||||
val proto = ProjectProto.Project.parseFrom(input)
|
||||
val project = Project()
|
||||
project.name = proto.name
|
||||
|
||||
return project
|
||||
}
|
||||
}
|
||||
30
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/serial/ProtobufProjectSerializer.kt
Executable file → Normal file
30
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/serial/ProtobufProjectSerializer.kt
Executable file → Normal file
@@ -1,16 +1,16 @@
|
||||
package com.bartlomiejpluta.base.editor.project.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import com.bartlomiejpluta.base.proto.ProjectProto
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.OutputStream
|
||||
|
||||
@Component
|
||||
class ProtobufProjectSerializer : ProjectSerializer {
|
||||
|
||||
override fun serialize(item: Project, output: OutputStream) {
|
||||
val proto = ProjectProto.Project.newBuilder()
|
||||
proto.name = item.name
|
||||
proto.build().writeTo(output)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.project.serial
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import com.bartlomiejpluta.base.proto.ProjectProto
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.OutputStream
|
||||
|
||||
@Component
|
||||
class ProtobufProjectSerializer : ProjectSerializer {
|
||||
|
||||
override fun serialize(item: Project, output: OutputStream) {
|
||||
val proto = ProjectProto.Project.newBuilder()
|
||||
proto.name = item.name
|
||||
proto.build().writeTo(output)
|
||||
}
|
||||
}
|
||||
190
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/view/ProjectSettingsFragment.kt
Executable file → Normal file
190
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/view/ProjectSettingsFragment.kt
Executable file → Normal file
@@ -1,96 +1,96 @@
|
||||
package com.bartlomiejpluta.base.editor.project.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.viewmodel.ProjectVM
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.beans.binding.Bindings.createStringBinding
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import tornadofx.*
|
||||
import java.io.File
|
||||
|
||||
class ProjectSettingsFragment : Fragment("Project Settings") {
|
||||
private val projectVM = find<ProjectVM>(FX.defaultScope)
|
||||
|
||||
private val rootDirectory = SimpleStringProperty("")
|
||||
private val projectDirectory = SimpleStringProperty("")
|
||||
private val directory = createStringBinding({
|
||||
File(rootDirectory.value, projectDirectory.value).absolutePath
|
||||
}, rootDirectory, projectDirectory)
|
||||
|
||||
init {
|
||||
directory.addListener { _, _, path -> projectVM.sourceDirectoryProperty.value = File(path) }
|
||||
}
|
||||
|
||||
var result = false
|
||||
private set
|
||||
|
||||
override val root = form {
|
||||
fieldset("Project Settings") {
|
||||
field("Project Name") {
|
||||
textfield(projectVM.nameProperty) {
|
||||
required()
|
||||
trimWhitespace()
|
||||
whenDocked { requestFocus() }
|
||||
}
|
||||
}
|
||||
|
||||
field("Project Location") {
|
||||
hbox {
|
||||
textfield(rootDirectory) {
|
||||
trimWhitespace()
|
||||
|
||||
projectVM.validationContext.addValidator(this, rootDirectory) {
|
||||
when {
|
||||
it.isNullOrBlank() -> error("Field is required")
|
||||
!File(it).exists() -> error("Provide valid path to the direction")
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button("Choose") {
|
||||
action {
|
||||
rootDirectory.value = chooseDirectory("Project Location")?.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
field("Project Directory Name") {
|
||||
textfield(projectDirectory) {
|
||||
trimWhitespace()
|
||||
|
||||
projectVM.validationContext.addValidator(this, projectDirectory) {
|
||||
when {
|
||||
it.isNullOrBlank() -> error("Field is required")
|
||||
File(directory.value).exists() -> error("The directory ${directory.value} already exists")
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label(Bindings.format("Directory:\n%s", directory))
|
||||
}
|
||||
|
||||
buttonbar {
|
||||
button("Ok") {
|
||||
action {
|
||||
projectVM.commit {
|
||||
result = true
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
shortcut("Enter")
|
||||
}
|
||||
|
||||
button("Reset") {
|
||||
action { projectVM.rollback() }
|
||||
}
|
||||
|
||||
button("Cancel") {
|
||||
action { close() }
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.project.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.viewmodel.ProjectVM
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.beans.binding.Bindings.createStringBinding
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import tornadofx.*
|
||||
import java.io.File
|
||||
|
||||
class ProjectSettingsFragment : Fragment("Project Settings") {
|
||||
private val projectVM = find<ProjectVM>(FX.defaultScope)
|
||||
|
||||
private val rootDirectory = SimpleStringProperty("")
|
||||
private val projectDirectory = SimpleStringProperty("")
|
||||
private val directory = createStringBinding({
|
||||
File(rootDirectory.value, projectDirectory.value).absolutePath
|
||||
}, rootDirectory, projectDirectory)
|
||||
|
||||
init {
|
||||
directory.addListener { _, _, path -> projectVM.sourceDirectoryProperty.value = File(path) }
|
||||
}
|
||||
|
||||
var result = false
|
||||
private set
|
||||
|
||||
override val root = form {
|
||||
fieldset("Project Settings") {
|
||||
field("Project Name") {
|
||||
textfield(projectVM.nameProperty) {
|
||||
required()
|
||||
trimWhitespace()
|
||||
whenDocked { requestFocus() }
|
||||
}
|
||||
}
|
||||
|
||||
field("Project Location") {
|
||||
hbox {
|
||||
textfield(rootDirectory) {
|
||||
trimWhitespace()
|
||||
|
||||
projectVM.validationContext.addValidator(this, rootDirectory) {
|
||||
when {
|
||||
it.isNullOrBlank() -> error("Field is required")
|
||||
!File(it).exists() -> error("Provide valid path to the direction")
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button("Choose") {
|
||||
action {
|
||||
rootDirectory.value = chooseDirectory("Project Location")?.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
field("Project Directory Name") {
|
||||
textfield(projectDirectory) {
|
||||
trimWhitespace()
|
||||
|
||||
projectVM.validationContext.addValidator(this, projectDirectory) {
|
||||
when {
|
||||
it.isNullOrBlank() -> error("Field is required")
|
||||
File(directory.value).exists() -> error("The directory ${directory.value} already exists")
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label(Bindings.format("Directory:\n%s", directory))
|
||||
}
|
||||
|
||||
buttonbar {
|
||||
button("Ok") {
|
||||
action {
|
||||
projectVM.commit {
|
||||
result = true
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
shortcut("Enter")
|
||||
}
|
||||
|
||||
button("Reset") {
|
||||
action { projectVM.rollback() }
|
||||
}
|
||||
|
||||
button("Cancel") {
|
||||
action { close() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/viewmodel/ProjectVM.kt
Executable file → Normal file
22
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/viewmodel/ProjectVM.kt
Executable file → Normal file
@@ -1,12 +1,12 @@
|
||||
package com.bartlomiejpluta.base.editor.project.viewmodel
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import tornadofx.*
|
||||
|
||||
class ProjectVM(project: Project) : ItemViewModel<Project>(project) {
|
||||
val nameProperty = bind(Project::nameProperty)
|
||||
val name by nameProperty
|
||||
|
||||
val sourceDirectoryProperty = bind(Project::sourceDirectoryProperty)
|
||||
val sourceDirectory by sourceDirectoryProperty
|
||||
package com.bartlomiejpluta.base.editor.project.viewmodel
|
||||
|
||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||
import tornadofx.*
|
||||
|
||||
class ProjectVM(project: Project) : ItemViewModel<Project>(project) {
|
||||
val nameProperty = bind(Project::nameProperty)
|
||||
val name by nameProperty
|
||||
|
||||
val sourceDirectoryProperty = bind(Project::sourceDirectoryProperty)
|
||||
val sourceDirectory by sourceDirectoryProperty
|
||||
}
|
||||
34
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/input/MapMouseEvent.kt
Executable file → Normal file
34
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/input/MapMouseEvent.kt
Executable file → Normal file
@@ -1,18 +1,18 @@
|
||||
package com.bartlomiejpluta.base.editor.render.input
|
||||
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
|
||||
import javafx.event.EventType
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.input.MouseEvent
|
||||
|
||||
class MapMouseEvent(val row: Int, val column: Int, val type: EventType<out MouseEvent>, val button: MouseButton) {
|
||||
|
||||
companion object {
|
||||
fun of(event: MouseEvent, tileSet: TileSet) = MapMouseEvent(
|
||||
(event.y / tileSet.tileHeight).toInt(),
|
||||
(event.x / tileSet.tileWidth).toInt(),
|
||||
event.eventType,
|
||||
event.button
|
||||
)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.render.input
|
||||
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
|
||||
import javafx.event.EventType
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.input.MouseEvent
|
||||
|
||||
class MapMouseEvent(val row: Int, val column: Int, val type: EventType<out MouseEvent>, val button: MouseButton) {
|
||||
|
||||
companion object {
|
||||
fun of(event: MouseEvent, tileSet: TileSet) = MapMouseEvent(
|
||||
(event.y / tileSet.tileHeight).toInt(),
|
||||
(event.x / tileSet.tileWidth).toInt(),
|
||||
event.eventType,
|
||||
event.button
|
||||
)
|
||||
}
|
||||
}
|
||||
8
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/input/MapMouseEventHandler.kt
Executable file → Normal file
8
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/input/MapMouseEventHandler.kt
Executable file → Normal file
@@ -1,5 +1,5 @@
|
||||
package com.bartlomiejpluta.base.editor.render.input
|
||||
|
||||
interface MapMouseEventHandler {
|
||||
fun handleMouseInput(event: MapMouseEvent)
|
||||
package com.bartlomiejpluta.base.editor.render.input
|
||||
|
||||
interface MapMouseEventHandler {
|
||||
fun handleMouseInput(event: MapMouseEvent)
|
||||
}
|
||||
12
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/model/Renderable.kt
Executable file → Normal file
12
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/model/Renderable.kt
Executable file → Normal file
@@ -1,7 +1,7 @@
|
||||
package com.bartlomiejpluta.base.editor.render.model
|
||||
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
|
||||
interface Renderable {
|
||||
fun render(gc: GraphicsContext)
|
||||
package com.bartlomiejpluta.base.editor.render.model
|
||||
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
|
||||
interface Renderable {
|
||||
fun render(gc: GraphicsContext)
|
||||
}
|
||||
14
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/resource/uid/manager/UIDManager.kt
Executable file → Normal file
14
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/resource/uid/manager/UIDManager.kt
Executable file → Normal file
@@ -1,8 +1,8 @@
|
||||
package com.bartlomiejpluta.base.editor.resource.uid.manager
|
||||
|
||||
import com.bartlomiejpluta.base.editor.resource.uid.model.UIDTarget
|
||||
|
||||
interface UIDManager {
|
||||
fun nextUID(target: UIDTarget): String
|
||||
fun loadData(target: UIDTarget, uids: Set<String>)
|
||||
package com.bartlomiejpluta.base.editor.resource.uid.manager
|
||||
|
||||
import com.bartlomiejpluta.base.editor.resource.uid.model.UIDTarget
|
||||
|
||||
interface UIDManager {
|
||||
fun nextUID(target: UIDTarget): String
|
||||
fun loadData(target: UIDTarget, uids: Set<String>)
|
||||
}
|
||||
50
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/resource/uid/manager/UUIDBasedUIDManager.kt
Executable file → Normal file
50
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/resource/uid/manager/UUIDBasedUIDManager.kt
Executable file → Normal file
@@ -1,26 +1,26 @@
|
||||
package com.bartlomiejpluta.base.editor.resource.uid.manager
|
||||
|
||||
import com.bartlomiejpluta.base.editor.resource.uid.model.UIDTarget
|
||||
import org.springframework.stereotype.Component
|
||||
import java.util.*
|
||||
|
||||
@Component
|
||||
class UUIDBasedUIDManager : UIDManager {
|
||||
private val registry = mutableMapOf<UIDTarget, Set<String>>()
|
||||
|
||||
override fun nextUID(target: UIDTarget): String {
|
||||
val set = registry.putIfAbsent(target, mutableSetOf())!!
|
||||
|
||||
var uid = ""
|
||||
|
||||
do {
|
||||
uid = UUID.randomUUID().toString()
|
||||
} while (uid !in set)
|
||||
|
||||
return uid
|
||||
}
|
||||
|
||||
override fun loadData(target: UIDTarget, uids: Set<String>) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.resource.uid.manager
|
||||
|
||||
import com.bartlomiejpluta.base.editor.resource.uid.model.UIDTarget
|
||||
import org.springframework.stereotype.Component
|
||||
import java.util.*
|
||||
|
||||
@Component
|
||||
class UUIDBasedUIDManager : UIDManager {
|
||||
private val registry = mutableMapOf<UIDTarget, Set<String>>()
|
||||
|
||||
override fun nextUID(target: UIDTarget): String {
|
||||
val set = registry.putIfAbsent(target, mutableSetOf())!!
|
||||
|
||||
var uid = ""
|
||||
|
||||
do {
|
||||
uid = UUID.randomUUID().toString()
|
||||
} while (uid !in set)
|
||||
|
||||
return uid
|
||||
}
|
||||
|
||||
override fun loadData(target: UIDTarget, uids: Set<String>) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
8
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/resource/uid/model/UIDTarget.kt
Executable file → Normal file
8
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/resource/uid/model/UIDTarget.kt
Executable file → Normal file
@@ -1,5 +1,5 @@
|
||||
package com.bartlomiejpluta.base.editor.resource.uid.model
|
||||
|
||||
enum class UIDTarget {
|
||||
MAP
|
||||
package com.bartlomiejpluta.base.editor.resource.uid.model
|
||||
|
||||
enum class UIDTarget {
|
||||
MAP
|
||||
}
|
||||
136
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/canvas/TileSetCanvas.kt
Executable file → Normal file
136
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/canvas/TileSetCanvas.kt
Executable file → Normal file
@@ -1,69 +1,69 @@
|
||||
package com.bartlomiejpluta.base.editor.tileset.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEventHandler
|
||||
import com.bartlomiejpluta.base.editor.render.model.Renderable
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.input.MouseEvent
|
||||
import javafx.scene.paint.Color
|
||||
|
||||
class TileSetCanvas(private val gameMapVM: GameMapVM, private val selection: TileSetSelection) : Renderable, MapMouseEventHandler {
|
||||
private var mouseRow = -1
|
||||
private var mouseColumn = -1
|
||||
|
||||
override fun render(gc: GraphicsContext) {
|
||||
gc.clearRect(0.0, 0.0, gc.canvas.width, gc.canvas.height)
|
||||
|
||||
renderTiles(gc)
|
||||
selection.render(gc)
|
||||
}
|
||||
|
||||
private fun renderTiles(gc: GraphicsContext) {
|
||||
gameMapVM.tileSet.forEach { row, column, tile ->
|
||||
gc.fill = if ((row + column) % 2 == 0) BACKGROUND_COLOR1 else BACKGROUND_COLOR2
|
||||
gc.fillRect(
|
||||
column * tile.image.width,
|
||||
row * tile.image.height,
|
||||
gameMapVM.tileSet.tileWidth.toDouble(),
|
||||
gameMapVM.tileSet.tileHeight.toDouble()
|
||||
)
|
||||
gc.drawImage(tile.image, column * tile.image.width, row * tile.image.height)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleMouseInput(event: MapMouseEvent) {
|
||||
mouseRow = event.row
|
||||
mouseColumn = event.column
|
||||
|
||||
when (event.type) {
|
||||
MouseEvent.MOUSE_PRESSED -> beginSelection(event)
|
||||
MouseEvent.MOUSE_DRAGGED -> proceedSelection(event)
|
||||
MouseEvent.MOUSE_RELEASED -> finishSelection(event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun beginSelection(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
selection.begin(event.row.toDouble(), event.column.toDouble())
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedSelection(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
selection.proceed(event.row.toDouble(), event.column.toDouble())
|
||||
}
|
||||
}
|
||||
|
||||
private fun finishSelection(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
selection.finish(event.row.toDouble(), event.column.toDouble())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val BACKGROUND_COLOR1 = Color.color(1.0, 1.0, 1.0, 1.0)
|
||||
private val BACKGROUND_COLOR2 = Color.color(0.95, 0.95, 0.95, 0.95)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.tileset.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEventHandler
|
||||
import com.bartlomiejpluta.base.editor.render.model.Renderable
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.input.MouseEvent
|
||||
import javafx.scene.paint.Color
|
||||
|
||||
class TileSetCanvas(private val gameMapVM: GameMapVM, private val selection: TileSetSelection) : Renderable, MapMouseEventHandler {
|
||||
private var mouseRow = -1
|
||||
private var mouseColumn = -1
|
||||
|
||||
override fun render(gc: GraphicsContext) {
|
||||
gc.clearRect(0.0, 0.0, gc.canvas.width, gc.canvas.height)
|
||||
|
||||
renderTiles(gc)
|
||||
selection.render(gc)
|
||||
}
|
||||
|
||||
private fun renderTiles(gc: GraphicsContext) {
|
||||
gameMapVM.tileSet.forEach { row, column, tile ->
|
||||
gc.fill = if ((row + column) % 2 == 0) BACKGROUND_COLOR1 else BACKGROUND_COLOR2
|
||||
gc.fillRect(
|
||||
column * tile.image.width,
|
||||
row * tile.image.height,
|
||||
gameMapVM.tileSet.tileWidth.toDouble(),
|
||||
gameMapVM.tileSet.tileHeight.toDouble()
|
||||
)
|
||||
gc.drawImage(tile.image, column * tile.image.width, row * tile.image.height)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleMouseInput(event: MapMouseEvent) {
|
||||
mouseRow = event.row
|
||||
mouseColumn = event.column
|
||||
|
||||
when (event.type) {
|
||||
MouseEvent.MOUSE_PRESSED -> beginSelection(event)
|
||||
MouseEvent.MOUSE_DRAGGED -> proceedSelection(event)
|
||||
MouseEvent.MOUSE_RELEASED -> finishSelection(event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun beginSelection(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
selection.begin(event.row.toDouble(), event.column.toDouble())
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedSelection(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
selection.proceed(event.row.toDouble(), event.column.toDouble())
|
||||
}
|
||||
}
|
||||
|
||||
private fun finishSelection(event: MapMouseEvent) {
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
selection.finish(event.row.toDouble(), event.column.toDouble())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val BACKGROUND_COLOR1 = Color.color(1.0, 1.0, 1.0, 1.0)
|
||||
private val BACKGROUND_COLOR2 = Color.color(0.95, 0.95, 0.95, 0.95)
|
||||
}
|
||||
}
|
||||
158
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/canvas/TileSetSelection.kt
Executable file → Normal file
158
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/canvas/TileSetSelection.kt
Executable file → Normal file
@@ -1,80 +1,80 @@
|
||||
package com.bartlomiejpluta.base.editor.tileset.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
|
||||
import com.bartlomiejpluta.base.editor.render.model.Renderable
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
import javafx.scene.paint.Color
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
class TileSetSelection(private val gameMapVM: GameMapVM, private val brushVM: BrushVM) : Renderable {
|
||||
private var startRow = 0.0
|
||||
private var startColumn = 0.0
|
||||
private var offsetRow = 0.0
|
||||
private var offsetColumn = 0.0
|
||||
private var x = 0.0
|
||||
private var y = 0.0
|
||||
private var width = gameMapVM.tileSet.tileWidth.toDouble()
|
||||
private var height = gameMapVM.tileSet.tileHeight.toDouble()
|
||||
|
||||
|
||||
fun begin(row: Double, column: Double) {
|
||||
startRow = row
|
||||
offsetRow = 0.0
|
||||
startColumn = column
|
||||
offsetColumn = 0.0
|
||||
|
||||
updateRect(row, column)
|
||||
}
|
||||
|
||||
private fun updateRect(row: Double, column: Double) {
|
||||
x = min(column, startColumn) * gameMapVM.tileSet.tileWidth
|
||||
y = min(row, startRow) * gameMapVM.tileSet.tileHeight
|
||||
width = (offsetColumn + 1) * gameMapVM.tileSet.tileWidth
|
||||
height = (offsetRow + 1) * gameMapVM.tileSet.tileHeight
|
||||
}
|
||||
|
||||
fun proceed(row: Double, column: Double) {
|
||||
offsetRow = abs(row - startRow)
|
||||
offsetColumn = abs(column - startColumn)
|
||||
|
||||
updateRect(row, column)
|
||||
}
|
||||
|
||||
fun finish(row: Double, column: Double) {
|
||||
proceed(row, column)
|
||||
|
||||
startRow = min(row, startRow)
|
||||
startColumn = min(column, startColumn)
|
||||
|
||||
val firstRow = startRow.toInt()
|
||||
val firstColumn = startColumn.toInt()
|
||||
val rows = offsetRow.toInt() + 1
|
||||
val columns = offsetColumn.toInt() + 1
|
||||
|
||||
val brushArray = Array(rows) { rowIndex ->
|
||||
Array(columns) { columnIndex ->
|
||||
gameMapVM.tileSet.getTile(firstRow + rowIndex, firstColumn + columnIndex)
|
||||
}
|
||||
}
|
||||
|
||||
brushVM.item = Brush.of(brushArray)
|
||||
brushVM.commit()
|
||||
}
|
||||
|
||||
override fun render(gc: GraphicsContext) {
|
||||
gc.fill = SELECTION_FILL_COLOR
|
||||
gc.fillRect(x, y, width, height)
|
||||
|
||||
gc.stroke = SELECTION_STROKE_COLOR
|
||||
gc.strokeRect(x, y, width, height)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val SELECTION_FILL_COLOR = Color.color(0.0, 0.7, 1.0, 0.4)
|
||||
private val SELECTION_STROKE_COLOR = Color.color(0.0, 0.7, 1.0, 1.0)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.tileset.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
|
||||
import com.bartlomiejpluta.base.editor.render.model.Renderable
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
import javafx.scene.paint.Color
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
class TileSetSelection(private val gameMapVM: GameMapVM, private val brushVM: BrushVM) : Renderable {
|
||||
private var startRow = 0.0
|
||||
private var startColumn = 0.0
|
||||
private var offsetRow = 0.0
|
||||
private var offsetColumn = 0.0
|
||||
private var x = 0.0
|
||||
private var y = 0.0
|
||||
private var width = gameMapVM.tileSet.tileWidth.toDouble()
|
||||
private var height = gameMapVM.tileSet.tileHeight.toDouble()
|
||||
|
||||
|
||||
fun begin(row: Double, column: Double) {
|
||||
startRow = row
|
||||
offsetRow = 0.0
|
||||
startColumn = column
|
||||
offsetColumn = 0.0
|
||||
|
||||
updateRect(row, column)
|
||||
}
|
||||
|
||||
private fun updateRect(row: Double, column: Double) {
|
||||
x = min(column, startColumn) * gameMapVM.tileSet.tileWidth
|
||||
y = min(row, startRow) * gameMapVM.tileSet.tileHeight
|
||||
width = (offsetColumn + 1) * gameMapVM.tileSet.tileWidth
|
||||
height = (offsetRow + 1) * gameMapVM.tileSet.tileHeight
|
||||
}
|
||||
|
||||
fun proceed(row: Double, column: Double) {
|
||||
offsetRow = abs(row - startRow)
|
||||
offsetColumn = abs(column - startColumn)
|
||||
|
||||
updateRect(row, column)
|
||||
}
|
||||
|
||||
fun finish(row: Double, column: Double) {
|
||||
proceed(row, column)
|
||||
|
||||
startRow = min(row, startRow)
|
||||
startColumn = min(column, startColumn)
|
||||
|
||||
val firstRow = startRow.toInt()
|
||||
val firstColumn = startColumn.toInt()
|
||||
val rows = offsetRow.toInt() + 1
|
||||
val columns = offsetColumn.toInt() + 1
|
||||
|
||||
val brushArray = Array(rows) { rowIndex ->
|
||||
Array(columns) { columnIndex ->
|
||||
gameMapVM.tileSet.getTile(firstRow + rowIndex, firstColumn + columnIndex)
|
||||
}
|
||||
}
|
||||
|
||||
brushVM.item = Brush.of(brushArray)
|
||||
brushVM.commit()
|
||||
}
|
||||
|
||||
override fun render(gc: GraphicsContext) {
|
||||
gc.fill = SELECTION_FILL_COLOR
|
||||
gc.fillRect(x, y, width, height)
|
||||
|
||||
gc.stroke = SELECTION_STROKE_COLOR
|
||||
gc.strokeRect(x, y, width, height)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val SELECTION_FILL_COLOR = Color.color(0.0, 0.7, 1.0, 0.4)
|
||||
private val SELECTION_STROKE_COLOR = Color.color(0.0, 0.7, 1.0, 1.0)
|
||||
}
|
||||
}
|
||||
82
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/component/TileSetPane.kt
Executable file → Normal file
82
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/component/TileSetPane.kt
Executable file → Normal file
@@ -1,42 +1,42 @@
|
||||
package com.bartlomiejpluta.base.editor.tileset.component
|
||||
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
|
||||
import com.bartlomiejpluta.base.editor.tileset.canvas.TileSetCanvas
|
||||
import com.bartlomiejpluta.base.editor.tileset.canvas.TileSetSelection
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.event.EventHandler
|
||||
import javafx.scene.canvas.Canvas
|
||||
import javafx.scene.input.MouseEvent
|
||||
|
||||
class TileSetPane(private val gameMapVM: GameMapVM, brushVM: BrushVM) : Canvas(), EventHandler<MouseEvent> {
|
||||
private val selection = TileSetSelection(gameMapVM, brushVM)
|
||||
private val tileSetCanvas = TileSetCanvas(gameMapVM, selection)
|
||||
|
||||
init {
|
||||
onMouseMoved = this
|
||||
onMouseDragged = this
|
||||
onMousePressed = this
|
||||
onMouseReleased = this
|
||||
|
||||
width = gameMapVM.tileSet.width.toDouble()
|
||||
height = gameMapVM.tileSet.height.toDouble()
|
||||
|
||||
|
||||
brushVM.itemProperty.addListener { _, _, _ -> render() }
|
||||
|
||||
render()
|
||||
}
|
||||
|
||||
private fun render() {
|
||||
tileSetCanvas.render(graphicsContext2D)
|
||||
}
|
||||
|
||||
override fun handle(event: MouseEvent?) {
|
||||
if (event != null) {
|
||||
tileSetCanvas.handleMouseInput(MapMouseEvent.of(event, gameMapVM.tileSet))
|
||||
}
|
||||
|
||||
tileSetCanvas.render(graphicsContext2D)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.tileset.component
|
||||
|
||||
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
|
||||
import com.bartlomiejpluta.base.editor.tileset.canvas.TileSetCanvas
|
||||
import com.bartlomiejpluta.base.editor.tileset.canvas.TileSetSelection
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import javafx.event.EventHandler
|
||||
import javafx.scene.canvas.Canvas
|
||||
import javafx.scene.input.MouseEvent
|
||||
|
||||
class TileSetPane(private val gameMapVM: GameMapVM, brushVM: BrushVM) : Canvas(), EventHandler<MouseEvent> {
|
||||
private val selection = TileSetSelection(gameMapVM, brushVM)
|
||||
private val tileSetCanvas = TileSetCanvas(gameMapVM, selection)
|
||||
|
||||
init {
|
||||
onMouseMoved = this
|
||||
onMouseDragged = this
|
||||
onMousePressed = this
|
||||
onMouseReleased = this
|
||||
|
||||
width = gameMapVM.tileSet.width.toDouble()
|
||||
height = gameMapVM.tileSet.height.toDouble()
|
||||
|
||||
|
||||
brushVM.itemProperty.addListener { _, _, _ -> render() }
|
||||
|
||||
render()
|
||||
}
|
||||
|
||||
private fun render() {
|
||||
tileSetCanvas.render(graphicsContext2D)
|
||||
}
|
||||
|
||||
override fun handle(event: MouseEvent?) {
|
||||
if (event != null) {
|
||||
tileSetCanvas.handleMouseInput(MapMouseEvent.of(event, gameMapVM.tileSet))
|
||||
}
|
||||
|
||||
tileSetCanvas.render(graphicsContext2D)
|
||||
}
|
||||
}
|
||||
108
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/model/Tile.kt
Executable file → Normal file
108
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/model/Tile.kt
Executable file → Normal file
@@ -1,55 +1,55 @@
|
||||
package com.bartlomiejpluta.base.editor.tileset.model
|
||||
|
||||
import javafx.scene.image.Image
|
||||
|
||||
import javafx.scene.image.PixelReader
|
||||
|
||||
import javafx.scene.image.WritableImage
|
||||
|
||||
|
||||
class Tile(
|
||||
tileSet: TileSet,
|
||||
row: Int,
|
||||
column: Int,
|
||||
val image: Image,
|
||||
) {
|
||||
val id = row * tileSet.columns + column
|
||||
|
||||
private fun cloneImage(image: Image): Image {
|
||||
val height = image.height.toInt()
|
||||
val width = image.width.toInt()
|
||||
val pixelReader = image.pixelReader
|
||||
val writableImage = WritableImage(width, height)
|
||||
val pixelWriter = writableImage.pixelWriter
|
||||
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val color = pixelReader.getColor(x, y)
|
||||
pixelWriter.setColor(x, y, color)
|
||||
}
|
||||
}
|
||||
return writableImage
|
||||
}
|
||||
|
||||
fun scale(image: Image, factor: Int): Image {
|
||||
val width = image.width.toInt()
|
||||
val height = image.height.toInt()
|
||||
val output = WritableImage(width * factor, height * factor)
|
||||
|
||||
val reader: PixelReader = image.pixelReader
|
||||
val writer = output.pixelWriter
|
||||
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val argb = reader.getArgb(x, y)
|
||||
for (dy in 0 until factor) {
|
||||
for (dx in 0 until factor) {
|
||||
writer.setArgb(x * factor + dx, y * factor + dy, argb)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.tileset.model
|
||||
|
||||
import javafx.scene.image.Image
|
||||
|
||||
import javafx.scene.image.PixelReader
|
||||
|
||||
import javafx.scene.image.WritableImage
|
||||
|
||||
|
||||
class Tile(
|
||||
tileSet: TileSet,
|
||||
row: Int,
|
||||
column: Int,
|
||||
val image: Image,
|
||||
) {
|
||||
val id = row * tileSet.columns + column
|
||||
|
||||
private fun cloneImage(image: Image): Image {
|
||||
val height = image.height.toInt()
|
||||
val width = image.width.toInt()
|
||||
val pixelReader = image.pixelReader
|
||||
val writableImage = WritableImage(width, height)
|
||||
val pixelWriter = writableImage.pixelWriter
|
||||
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val color = pixelReader.getColor(x, y)
|
||||
pixelWriter.setColor(x, y, color)
|
||||
}
|
||||
}
|
||||
return writableImage
|
||||
}
|
||||
|
||||
fun scale(image: Image, factor: Int): Image {
|
||||
val width = image.width.toInt()
|
||||
val height = image.height.toInt()
|
||||
val output = WritableImage(width * factor, height * factor)
|
||||
|
||||
val reader: PixelReader = image.pixelReader
|
||||
val writer = output.pixelWriter
|
||||
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val argb = reader.getArgb(x, y)
|
||||
for (dy in 0 until factor) {
|
||||
for (dx in 0 until factor) {
|
||||
writer.setArgb(x * factor + dx, y * factor + dy, argb)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
||||
124
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/model/TileSet.kt
Executable file → Normal file
124
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/model/TileSet.kt
Executable file → Normal file
@@ -1,63 +1,63 @@
|
||||
package com.bartlomiejpluta.base.editor.tileset.model
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import javafx.scene.image.Image
|
||||
import javafx.scene.image.PixelFormat
|
||||
import javafx.scene.image.WritableImage
|
||||
import tornadofx.getValue
|
||||
import tornadofx.toObservable
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
|
||||
class TileSet(private val image: Image, rows: Int, columns: Int) {
|
||||
val rowsProperty = SimpleIntegerProperty(rows)
|
||||
val rows by rowsProperty
|
||||
|
||||
val columnsProperty = SimpleIntegerProperty(columns)
|
||||
val columns by columnsProperty
|
||||
|
||||
val tileWidthProperty = SimpleIntegerProperty(image.width.toInt() / columns)
|
||||
val tileWidth by tileWidthProperty
|
||||
|
||||
val tileHeightProperty = SimpleIntegerProperty(image.height.toInt() / rows)
|
||||
val tileHeight by tileHeightProperty
|
||||
|
||||
val widthProperty = SimpleIntegerProperty(tileWidth * columns)
|
||||
val width by widthProperty
|
||||
|
||||
val heightProperty = SimpleIntegerProperty(tileHeight * rows)
|
||||
val height by heightProperty
|
||||
|
||||
val tiles = (0 until rows * columns).map { cropTile(it / columns, it % columns) }.toObservable()
|
||||
|
||||
val baseBrush: Brush
|
||||
get() = Brush.of(arrayOf(arrayOf(tiles[0])))
|
||||
|
||||
private fun cropTile(row: Int, column: Int): Tile {
|
||||
val reader = image.pixelReader
|
||||
val buffer = ByteBuffer.allocate(tileWidth * tileHeight * 4)
|
||||
reader.getPixels(
|
||||
column * tileWidth,
|
||||
row * tileHeight,
|
||||
tileWidth,
|
||||
tileHeight,
|
||||
PixelFormat.getByteBgraInstance(),
|
||||
buffer,
|
||||
4 * tileWidth
|
||||
)
|
||||
val tile = WritableImage(tileWidth, tileHeight)
|
||||
val writer = tile.pixelWriter
|
||||
writer.setPixels(0, 0, tileWidth, tileHeight, PixelFormat.getByteBgraInstance(), buffer, 4 * tileWidth)
|
||||
|
||||
return Tile(this, row, column, tile)
|
||||
}
|
||||
|
||||
fun getTile(row: Int, column: Int) = tiles[(row.coerceIn(0 until rows)) * columns + column.coerceIn(0 until columns)]
|
||||
|
||||
fun getTile(id: Int) = tiles[id]
|
||||
|
||||
fun forEach(consumer: (row: Int, column: Int, tile: Tile) -> Unit) = tiles.forEachIndexed { id, tile ->
|
||||
consumer(id / columns, id % columns, tile)
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.tileset.model
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import javafx.scene.image.Image
|
||||
import javafx.scene.image.PixelFormat
|
||||
import javafx.scene.image.WritableImage
|
||||
import tornadofx.getValue
|
||||
import tornadofx.toObservable
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
|
||||
class TileSet(private val image: Image, rows: Int, columns: Int) {
|
||||
val rowsProperty = SimpleIntegerProperty(rows)
|
||||
val rows by rowsProperty
|
||||
|
||||
val columnsProperty = SimpleIntegerProperty(columns)
|
||||
val columns by columnsProperty
|
||||
|
||||
val tileWidthProperty = SimpleIntegerProperty(image.width.toInt() / columns)
|
||||
val tileWidth by tileWidthProperty
|
||||
|
||||
val tileHeightProperty = SimpleIntegerProperty(image.height.toInt() / rows)
|
||||
val tileHeight by tileHeightProperty
|
||||
|
||||
val widthProperty = SimpleIntegerProperty(tileWidth * columns)
|
||||
val width by widthProperty
|
||||
|
||||
val heightProperty = SimpleIntegerProperty(tileHeight * rows)
|
||||
val height by heightProperty
|
||||
|
||||
val tiles = (0 until rows * columns).map { cropTile(it / columns, it % columns) }.toObservable()
|
||||
|
||||
val baseBrush: Brush
|
||||
get() = Brush.of(arrayOf(arrayOf(tiles[0])))
|
||||
|
||||
private fun cropTile(row: Int, column: Int): Tile {
|
||||
val reader = image.pixelReader
|
||||
val buffer = ByteBuffer.allocate(tileWidth * tileHeight * 4)
|
||||
reader.getPixels(
|
||||
column * tileWidth,
|
||||
row * tileHeight,
|
||||
tileWidth,
|
||||
tileHeight,
|
||||
PixelFormat.getByteBgraInstance(),
|
||||
buffer,
|
||||
4 * tileWidth
|
||||
)
|
||||
val tile = WritableImage(tileWidth, tileHeight)
|
||||
val writer = tile.pixelWriter
|
||||
writer.setPixels(0, 0, tileWidth, tileHeight, PixelFormat.getByteBgraInstance(), buffer, 4 * tileWidth)
|
||||
|
||||
return Tile(this, row, column, tile)
|
||||
}
|
||||
|
||||
fun getTile(row: Int, column: Int) = tiles[(row.coerceIn(0 until rows)) * columns + column.coerceIn(0 until columns)]
|
||||
|
||||
fun getTile(id: Int) = tiles[id]
|
||||
|
||||
fun forEach(consumer: (row: Int, column: Int, tile: Tile) -> Unit) = tiles.forEachIndexed { id, tile ->
|
||||
consumer(id / columns, id % columns, tile)
|
||||
}
|
||||
}
|
||||
36
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/view/TileSetView.kt
Executable file → Normal file
36
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/view/TileSetView.kt
Executable file → Normal file
@@ -1,19 +1,19 @@
|
||||
package com.bartlomiejpluta.base.editor.tileset.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.tileset.component.TileSetPane
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import tornadofx.View
|
||||
import tornadofx.plusAssign
|
||||
import tornadofx.scrollpane
|
||||
|
||||
class TileSetView : View() {
|
||||
private val gameMapVM = find<GameMapVM>()
|
||||
private val brushVM = find<BrushVM>()
|
||||
|
||||
private val tileSetPane = TileSetPane(gameMapVM, brushVM)
|
||||
|
||||
override val root = scrollpane {
|
||||
this += tileSetPane
|
||||
}
|
||||
package com.bartlomiejpluta.base.editor.tileset.view
|
||||
|
||||
import com.bartlomiejpluta.base.editor.tileset.component.TileSetPane
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
import tornadofx.View
|
||||
import tornadofx.plusAssign
|
||||
import tornadofx.scrollpane
|
||||
|
||||
class TileSetView : View() {
|
||||
private val gameMapVM = find<GameMapVM>()
|
||||
private val brushVM = find<BrushVM>()
|
||||
|
||||
private val tileSetPane = TileSetPane(gameMapVM, brushVM)
|
||||
|
||||
override val root = scrollpane {
|
||||
this += tileSetPane
|
||||
}
|
||||
}
|
||||
4
editor/src/main/resources/application.yml
Executable file → Normal file
4
editor/src/main/resources/application.yml
Executable file → Normal file
@@ -1,3 +1,3 @@
|
||||
logging:
|
||||
level:
|
||||
logging:
|
||||
level:
|
||||
com.bartlomiejpluta.base.editor: DEBUG
|
||||
0
engine/build.gradle
Executable file → Normal file
0
engine/build.gradle
Executable file → Normal file
222
engine/src/main/java/com/bartlomiejpluta/base/core/engine/DefaultGameEngine.java
Executable file → Normal file
222
engine/src/main/java/com/bartlomiejpluta/base/core/engine/DefaultGameEngine.java
Executable file → Normal file
@@ -1,111 +1,111 @@
|
||||
package com.bartlomiejpluta.base.core.engine;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.OffHeapGarbageCollector;
|
||||
import com.bartlomiejpluta.base.core.logic.GameLogic;
|
||||
import com.bartlomiejpluta.base.core.thread.ThreadManager;
|
||||
import com.bartlomiejpluta.base.core.time.ChronoMeter;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
import com.bartlomiejpluta.base.core.ui.WindowManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DefaultGameEngine implements GameEngine {
|
||||
private static final String THREAD_NAME = "Game Main Thread";
|
||||
|
||||
private final WindowManager windowManager;
|
||||
private final ThreadManager threadManager;
|
||||
private final GameLogic logic;
|
||||
private final OffHeapGarbageCollector garbageCollector;
|
||||
|
||||
private final Thread thread;
|
||||
private final Window window;
|
||||
private final ChronoMeter chrono;
|
||||
private final int targetUps;
|
||||
|
||||
private boolean running = false;
|
||||
|
||||
@Autowired
|
||||
public DefaultGameEngine(WindowManager windowManager,
|
||||
ThreadManager threadManager,
|
||||
GameLogic logic,
|
||||
OffHeapGarbageCollector garbageCollector,
|
||||
@Value("${app.window.title}") String title,
|
||||
@Value("${app.window.width}") int width,
|
||||
@Value("${app.window.height}") int height,
|
||||
@Value("${app.core.targetUps}") int targetUps) {
|
||||
this.windowManager = windowManager;
|
||||
this.threadManager = threadManager;
|
||||
this.logic = logic;
|
||||
this.garbageCollector = garbageCollector;
|
||||
|
||||
this.window = windowManager.createWindow(title, width, height);
|
||||
this.thread = threadManager.createThread(THREAD_NAME, this::run);
|
||||
this.chrono = new ChronoMeter();
|
||||
this.targetUps = targetUps;
|
||||
}
|
||||
|
||||
private void run() {
|
||||
try {
|
||||
init();
|
||||
loop();
|
||||
} finally {
|
||||
cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
private void init() {
|
||||
log.info("Initializing game engine");
|
||||
window.init();
|
||||
chrono.init();
|
||||
logic.init(window);
|
||||
}
|
||||
|
||||
private void loop() {
|
||||
log.info("Starting game loop");
|
||||
running = true;
|
||||
var dt = 0.0f;
|
||||
var accumulator = 0.0f;
|
||||
var step = 1.0f / targetUps;
|
||||
|
||||
while (running && !window.shouldClose()) {
|
||||
dt = chrono.getElapsedTime();
|
||||
accumulator += dt;
|
||||
|
||||
input();
|
||||
|
||||
while (accumulator >= step) {
|
||||
update(dt);
|
||||
accumulator -= step;
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
private void input() {
|
||||
logic.input(window);
|
||||
}
|
||||
|
||||
private void update(float dt) {
|
||||
logic.update(dt);
|
||||
}
|
||||
|
||||
private void render() {
|
||||
window.update();
|
||||
logic.render(window);
|
||||
}
|
||||
|
||||
private void cleanUp() {
|
||||
log.info("Performing off heap garbage collection");
|
||||
garbageCollector.cleanUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.engine;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.OffHeapGarbageCollector;
|
||||
import com.bartlomiejpluta.base.core.logic.GameLogic;
|
||||
import com.bartlomiejpluta.base.core.thread.ThreadManager;
|
||||
import com.bartlomiejpluta.base.core.time.ChronoMeter;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
import com.bartlomiejpluta.base.core.ui.WindowManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DefaultGameEngine implements GameEngine {
|
||||
private static final String THREAD_NAME = "Game Main Thread";
|
||||
|
||||
private final WindowManager windowManager;
|
||||
private final ThreadManager threadManager;
|
||||
private final GameLogic logic;
|
||||
private final OffHeapGarbageCollector garbageCollector;
|
||||
|
||||
private final Thread thread;
|
||||
private final Window window;
|
||||
private final ChronoMeter chrono;
|
||||
private final int targetUps;
|
||||
|
||||
private boolean running = false;
|
||||
|
||||
@Autowired
|
||||
public DefaultGameEngine(WindowManager windowManager,
|
||||
ThreadManager threadManager,
|
||||
GameLogic logic,
|
||||
OffHeapGarbageCollector garbageCollector,
|
||||
@Value("${app.window.title}") String title,
|
||||
@Value("${app.window.width}") int width,
|
||||
@Value("${app.window.height}") int height,
|
||||
@Value("${app.core.targetUps}") int targetUps) {
|
||||
this.windowManager = windowManager;
|
||||
this.threadManager = threadManager;
|
||||
this.logic = logic;
|
||||
this.garbageCollector = garbageCollector;
|
||||
|
||||
this.window = windowManager.createWindow(title, width, height);
|
||||
this.thread = threadManager.createThread(THREAD_NAME, this::run);
|
||||
this.chrono = new ChronoMeter();
|
||||
this.targetUps = targetUps;
|
||||
}
|
||||
|
||||
private void run() {
|
||||
try {
|
||||
init();
|
||||
loop();
|
||||
} finally {
|
||||
cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
private void init() {
|
||||
log.info("Initializing game engine");
|
||||
window.init();
|
||||
chrono.init();
|
||||
logic.init(window);
|
||||
}
|
||||
|
||||
private void loop() {
|
||||
log.info("Starting game loop");
|
||||
running = true;
|
||||
var dt = 0.0f;
|
||||
var accumulator = 0.0f;
|
||||
var step = 1.0f / targetUps;
|
||||
|
||||
while (running && !window.shouldClose()) {
|
||||
dt = chrono.getElapsedTime();
|
||||
accumulator += dt;
|
||||
|
||||
input();
|
||||
|
||||
while (accumulator >= step) {
|
||||
update(dt);
|
||||
accumulator -= step;
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
private void input() {
|
||||
logic.input(window);
|
||||
}
|
||||
|
||||
private void update(float dt) {
|
||||
logic.update(dt);
|
||||
}
|
||||
|
||||
private void render() {
|
||||
window.update();
|
||||
logic.render(window);
|
||||
}
|
||||
|
||||
private void cleanUp() {
|
||||
log.info("Performing off heap garbage collection");
|
||||
garbageCollector.cleanUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
|
||||
10
engine/src/main/java/com/bartlomiejpluta/base/core/engine/GameEngine.java
Executable file → Normal file
10
engine/src/main/java/com/bartlomiejpluta/base/core/engine/GameEngine.java
Executable file → Normal file
@@ -1,5 +1,5 @@
|
||||
package com.bartlomiejpluta.base.core.engine;
|
||||
|
||||
public interface GameEngine {
|
||||
void start();
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.engine;
|
||||
|
||||
public interface GameEngine {
|
||||
void start();
|
||||
}
|
||||
|
||||
44
engine/src/main/java/com/bartlomiejpluta/base/core/error/AppException.java
Executable file → Normal file
44
engine/src/main/java/com/bartlomiejpluta/base/core/error/AppException.java
Executable file → Normal file
@@ -1,22 +1,22 @@
|
||||
package com.bartlomiejpluta.base.core.error;
|
||||
|
||||
public class AppException extends RuntimeException {
|
||||
public AppException() {
|
||||
}
|
||||
|
||||
public AppException(String message, Object... args) {
|
||||
super(String.format(message, args));
|
||||
}
|
||||
|
||||
public AppException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public AppException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public AppException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.error;
|
||||
|
||||
public class AppException extends RuntimeException {
|
||||
public AppException() {
|
||||
}
|
||||
|
||||
public AppException(String message, Object... args) {
|
||||
super(String.format(message, args));
|
||||
}
|
||||
|
||||
public AppException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public AppException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public AppException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
10
engine/src/main/java/com/bartlomiejpluta/base/core/gc/Cleanable.java
Executable file → Normal file
10
engine/src/main/java/com/bartlomiejpluta/base/core/gc/Cleanable.java
Executable file → Normal file
@@ -1,5 +1,5 @@
|
||||
package com.bartlomiejpluta.base.core.gc;
|
||||
|
||||
public interface Cleanable {
|
||||
void cleanUp();
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gc;
|
||||
|
||||
public interface Cleanable {
|
||||
void cleanUp();
|
||||
}
|
||||
|
||||
44
engine/src/main/java/com/bartlomiejpluta/base/core/gc/DefaultOffHeapGarbageCollector.java
Executable file → Normal file
44
engine/src/main/java/com/bartlomiejpluta/base/core/gc/DefaultOffHeapGarbageCollector.java
Executable file → Normal file
@@ -1,22 +1,22 @@
|
||||
package com.bartlomiejpluta.base.core.gc;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultOffHeapGarbageCollector implements OffHeapGarbageCollector {
|
||||
private final List<Cleanable> cleanables;
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
cleanables.stream()
|
||||
.peek(cleanable -> log.info("Performing {} cleaning", cleanable.getClass().getSimpleName()))
|
||||
.forEach(Cleanable::cleanUp);
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gc;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultOffHeapGarbageCollector implements OffHeapGarbageCollector {
|
||||
private final List<Cleanable> cleanables;
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
cleanables.stream()
|
||||
.peek(cleanable -> log.info("Performing {} cleaning", cleanable.getClass().getSimpleName()))
|
||||
.forEach(Cleanable::cleanUp);
|
||||
}
|
||||
}
|
||||
|
||||
10
engine/src/main/java/com/bartlomiejpluta/base/core/gc/Disposable.java
Executable file → Normal file
10
engine/src/main/java/com/bartlomiejpluta/base/core/gc/Disposable.java
Executable file → Normal file
@@ -1,5 +1,5 @@
|
||||
package com.bartlomiejpluta.base.core.gc;
|
||||
|
||||
public interface Disposable {
|
||||
void dispose();
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gc;
|
||||
|
||||
public interface Disposable {
|
||||
void dispose();
|
||||
}
|
||||
|
||||
10
engine/src/main/java/com/bartlomiejpluta/base/core/gc/OffHeapGarbageCollector.java
Executable file → Normal file
10
engine/src/main/java/com/bartlomiejpluta/base/core/gc/OffHeapGarbageCollector.java
Executable file → Normal file
@@ -1,5 +1,5 @@
|
||||
package com.bartlomiejpluta.base.core.gc;
|
||||
|
||||
public interface OffHeapGarbageCollector {
|
||||
void cleanUp();
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gc;
|
||||
|
||||
public interface OffHeapGarbageCollector {
|
||||
void cleanUp();
|
||||
}
|
||||
|
||||
166
engine/src/main/java/com/bartlomiejpluta/base/core/gl/object/material/Material.java
Executable file → Normal file
166
engine/src/main/java/com/bartlomiejpluta/base/core/gl/object/material/Material.java
Executable file → Normal file
@@ -1,83 +1,83 @@
|
||||
package com.bartlomiejpluta.base.core.gl.object.material;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.object.texture.Texture;
|
||||
import lombok.Getter;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
@Getter
|
||||
public class Material {
|
||||
private final Vector4f color = new Vector4f();
|
||||
private final Vector2f spriteSize = new Vector2f(1, 1);
|
||||
private final Vector2f spritePosition = new Vector2f(0, 0);
|
||||
private final Texture texture;
|
||||
|
||||
private Material(Texture texture, float r, float g, float b, float alpha) {
|
||||
this.texture = texture;
|
||||
setColor(r, g, b, alpha);
|
||||
}
|
||||
|
||||
public void setAlpha(float alpha) {
|
||||
this.color.w = alpha;
|
||||
}
|
||||
|
||||
public void setColor(Vector4f color) {
|
||||
this.color.x = color.x;
|
||||
this.color.y = color.y;
|
||||
this.color.z = color.z;
|
||||
this.color.w = color.w;
|
||||
}
|
||||
|
||||
public void setColor(float r, float g, float b, float alpha) {
|
||||
color.x = r;
|
||||
color.y = g;
|
||||
color.z = b;
|
||||
color.w = alpha;
|
||||
}
|
||||
|
||||
public void setSpriteSize(Vector2f spriteSize) {
|
||||
this.spriteSize.x = spriteSize.x;
|
||||
this.spriteSize.y = spriteSize.y;
|
||||
}
|
||||
|
||||
public void setSpriteSize(float w, float h) {
|
||||
this.spriteSize.x = w;
|
||||
this.spriteSize.y = h;
|
||||
}
|
||||
|
||||
public void setSpritePosition(Vector2f spritePosition) {
|
||||
this.spritePosition.x = spritePosition.x;
|
||||
this.spritePosition.y = spritePosition.y;
|
||||
}
|
||||
|
||||
public void setSpritePosition(float x, float y) {
|
||||
this.spritePosition.x = x;
|
||||
this.spritePosition.y = y;
|
||||
}
|
||||
|
||||
public boolean hasTexture() {
|
||||
return texture != null;
|
||||
}
|
||||
|
||||
public void activateTextureIfExists() {
|
||||
if(hasTexture()) {
|
||||
texture.activate();
|
||||
}
|
||||
}
|
||||
|
||||
public static Material colored(float r, float g, float b, float alpha) {
|
||||
return new Material(null, r, g, b, alpha);
|
||||
}
|
||||
|
||||
public static Material textured(Texture texture) {
|
||||
return new Material(texture, 1, 1, 1, 1);
|
||||
}
|
||||
|
||||
public static Material textured(Texture texture, float r, float g, float b, float alpha) {
|
||||
return new Material(texture, r, g, b, alpha);
|
||||
}
|
||||
|
||||
public static Material textured(Texture texture, float alpha) {
|
||||
return new Material(texture, 1, 1, 1, alpha);
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.object.material;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.object.texture.Texture;
|
||||
import lombok.Getter;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
@Getter
|
||||
public class Material {
|
||||
private final Vector4f color = new Vector4f();
|
||||
private final Vector2f spriteSize = new Vector2f(1, 1);
|
||||
private final Vector2f spritePosition = new Vector2f(0, 0);
|
||||
private final Texture texture;
|
||||
|
||||
private Material(Texture texture, float r, float g, float b, float alpha) {
|
||||
this.texture = texture;
|
||||
setColor(r, g, b, alpha);
|
||||
}
|
||||
|
||||
public void setAlpha(float alpha) {
|
||||
this.color.w = alpha;
|
||||
}
|
||||
|
||||
public void setColor(Vector4f color) {
|
||||
this.color.x = color.x;
|
||||
this.color.y = color.y;
|
||||
this.color.z = color.z;
|
||||
this.color.w = color.w;
|
||||
}
|
||||
|
||||
public void setColor(float r, float g, float b, float alpha) {
|
||||
color.x = r;
|
||||
color.y = g;
|
||||
color.z = b;
|
||||
color.w = alpha;
|
||||
}
|
||||
|
||||
public void setSpriteSize(Vector2f spriteSize) {
|
||||
this.spriteSize.x = spriteSize.x;
|
||||
this.spriteSize.y = spriteSize.y;
|
||||
}
|
||||
|
||||
public void setSpriteSize(float w, float h) {
|
||||
this.spriteSize.x = w;
|
||||
this.spriteSize.y = h;
|
||||
}
|
||||
|
||||
public void setSpritePosition(Vector2f spritePosition) {
|
||||
this.spritePosition.x = spritePosition.x;
|
||||
this.spritePosition.y = spritePosition.y;
|
||||
}
|
||||
|
||||
public void setSpritePosition(float x, float y) {
|
||||
this.spritePosition.x = x;
|
||||
this.spritePosition.y = y;
|
||||
}
|
||||
|
||||
public boolean hasTexture() {
|
||||
return texture != null;
|
||||
}
|
||||
|
||||
public void activateTextureIfExists() {
|
||||
if(hasTexture()) {
|
||||
texture.activate();
|
||||
}
|
||||
}
|
||||
|
||||
public static Material colored(float r, float g, float b, float alpha) {
|
||||
return new Material(null, r, g, b, alpha);
|
||||
}
|
||||
|
||||
public static Material textured(Texture texture) {
|
||||
return new Material(texture, 1, 1, 1, 1);
|
||||
}
|
||||
|
||||
public static Material textured(Texture texture, float r, float g, float b, float alpha) {
|
||||
return new Material(texture, r, g, b, alpha);
|
||||
}
|
||||
|
||||
public static Material textured(Texture texture, float alpha) {
|
||||
return new Material(texture, 1, 1, 1, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
188
engine/src/main/java/com/bartlomiejpluta/base/core/gl/object/mesh/Mesh.java
Executable file → Normal file
188
engine/src/main/java/com/bartlomiejpluta/base/core/gl/object/mesh/Mesh.java
Executable file → Normal file
@@ -1,94 +1,94 @@
|
||||
package com.bartlomiejpluta.base.core.gl.object.mesh;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Disposable;
|
||||
import com.bartlomiejpluta.base.core.gl.render.Renderable;
|
||||
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.lwjgl.opengl.GL15.*;
|
||||
import static org.lwjgl.opengl.GL20.*;
|
||||
import static org.lwjgl.opengl.GL30.*;
|
||||
|
||||
public class Mesh implements Renderable, Disposable {
|
||||
private final int vaoId;
|
||||
private final List<Integer> vboIds = new ArrayList<>(2);
|
||||
private final int elementsCount;
|
||||
|
||||
public Mesh(float[] vertices, float[] texCoords, int[] elements) {
|
||||
try(var stack = MemoryStack.stackPush()) {
|
||||
elementsCount = elements.length;
|
||||
var verticesBuffer = stack.mallocFloat(vertices.length);
|
||||
var texCoordsBuffer = stack.mallocFloat(texCoords.length);
|
||||
var elementsBuffer = stack.mallocInt(elementsCount);
|
||||
verticesBuffer.put(vertices).flip();
|
||||
texCoordsBuffer.put(texCoords).flip();
|
||||
elementsBuffer.put(elements).flip();
|
||||
|
||||
vaoId = glGenVertexArrays();
|
||||
glBindVertexArray(vaoId);
|
||||
|
||||
int vboId = glGenBuffers();
|
||||
vboIds.add(vboId);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vboId);
|
||||
glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
|
||||
|
||||
vboId = glGenBuffers();
|
||||
vboIds.add(vboId);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vboId);
|
||||
glBufferData(GL_ARRAY_BUFFER, texCoordsBuffer, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
|
||||
|
||||
vboId = glGenBuffers();
|
||||
vboIds.add(vboId);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementsBuffer, GL_STATIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Window window, ShaderManager shaderManager) {
|
||||
glBindVertexArray(vaoId);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_INT, 0);
|
||||
glDisableVertexAttribArray(0);
|
||||
glDisableVertexAttribArray(1);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
glDisableVertexAttribArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
vboIds.forEach(GL15::glDeleteBuffers);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glDeleteVertexArrays(vaoId);
|
||||
}
|
||||
|
||||
public static Mesh quad(float width, float height, float originX, float originY) {
|
||||
var vertices = new float[] {
|
||||
-originX, -originY,
|
||||
-originX, height - originY,
|
||||
width - originX, height - originY,
|
||||
width - originX, - originY
|
||||
};
|
||||
|
||||
var texCoords = new float[] { 0, 0, 0, 1, 1, 1, 1, 0 };
|
||||
|
||||
var elements = new int[] { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
return new Mesh(vertices, texCoords, elements);
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.object.mesh;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Disposable;
|
||||
import com.bartlomiejpluta.base.core.gl.render.Renderable;
|
||||
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.lwjgl.opengl.GL15.*;
|
||||
import static org.lwjgl.opengl.GL20.*;
|
||||
import static org.lwjgl.opengl.GL30.*;
|
||||
|
||||
public class Mesh implements Renderable, Disposable {
|
||||
private final int vaoId;
|
||||
private final List<Integer> vboIds = new ArrayList<>(2);
|
||||
private final int elementsCount;
|
||||
|
||||
public Mesh(float[] vertices, float[] texCoords, int[] elements) {
|
||||
try(var stack = MemoryStack.stackPush()) {
|
||||
elementsCount = elements.length;
|
||||
var verticesBuffer = stack.mallocFloat(vertices.length);
|
||||
var texCoordsBuffer = stack.mallocFloat(texCoords.length);
|
||||
var elementsBuffer = stack.mallocInt(elementsCount);
|
||||
verticesBuffer.put(vertices).flip();
|
||||
texCoordsBuffer.put(texCoords).flip();
|
||||
elementsBuffer.put(elements).flip();
|
||||
|
||||
vaoId = glGenVertexArrays();
|
||||
glBindVertexArray(vaoId);
|
||||
|
||||
int vboId = glGenBuffers();
|
||||
vboIds.add(vboId);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vboId);
|
||||
glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
|
||||
|
||||
vboId = glGenBuffers();
|
||||
vboIds.add(vboId);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vboId);
|
||||
glBufferData(GL_ARRAY_BUFFER, texCoordsBuffer, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
|
||||
|
||||
vboId = glGenBuffers();
|
||||
vboIds.add(vboId);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementsBuffer, GL_STATIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Window window, ShaderManager shaderManager) {
|
||||
glBindVertexArray(vaoId);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_INT, 0);
|
||||
glDisableVertexAttribArray(0);
|
||||
glDisableVertexAttribArray(1);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
glDisableVertexAttribArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
vboIds.forEach(GL15::glDeleteBuffers);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glDeleteVertexArrays(vaoId);
|
||||
}
|
||||
|
||||
public static Mesh quad(float width, float height, float originX, float originY) {
|
||||
var vertices = new float[] {
|
||||
-originX, -originY,
|
||||
-originX, height - originY,
|
||||
width - originX, height - originY,
|
||||
width - originX, - originY
|
||||
};
|
||||
|
||||
var texCoords = new float[] { 0, 0, 0, 1, 1, 1, 1, 0 };
|
||||
|
||||
var elements = new int[] { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
return new Mesh(vertices, texCoords, elements);
|
||||
}
|
||||
}
|
||||
|
||||
78
engine/src/main/java/com/bartlomiejpluta/base/core/gl/object/texture/DefaultTextureManager.java
Executable file → Normal file
78
engine/src/main/java/com/bartlomiejpluta/base/core/gl/object/texture/DefaultTextureManager.java
Executable file → Normal file
@@ -1,39 +1,39 @@
|
||||
package com.bartlomiejpluta.base.core.gl.object.texture;
|
||||
|
||||
import com.bartlomiejpluta.base.core.util.res.ResourcesManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultTextureManager implements TextureManager {
|
||||
private final ResourcesManager resourcesManager;
|
||||
private final Map<String, Texture> loadedTextures = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Texture loadTexture(String textureFileName) {
|
||||
var texture = loadedTextures.get(textureFileName);
|
||||
|
||||
if (texture == null) {
|
||||
log.info("Loading [{}] texture to cache", textureFileName);
|
||||
var buffer = resourcesManager.loadResourceAsByteBuffer(textureFileName);
|
||||
texture = new Texture(textureFileName, buffer);
|
||||
loadedTextures.put(textureFileName, texture);
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
log.info("Disposing textures");
|
||||
loadedTextures.forEach((name, texture) -> texture.dispose());
|
||||
log.info("{} textures has been disposed", loadedTextures.size());
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.object.texture;
|
||||
|
||||
import com.bartlomiejpluta.base.core.util.res.ResourcesManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultTextureManager implements TextureManager {
|
||||
private final ResourcesManager resourcesManager;
|
||||
private final Map<String, Texture> loadedTextures = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Texture loadTexture(String textureFileName) {
|
||||
var texture = loadedTextures.get(textureFileName);
|
||||
|
||||
if (texture == null) {
|
||||
log.info("Loading [{}] texture to cache", textureFileName);
|
||||
var buffer = resourcesManager.loadResourceAsByteBuffer(textureFileName);
|
||||
texture = new Texture(textureFileName, buffer);
|
||||
loadedTextures.put(textureFileName, texture);
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
log.info("Disposing textures");
|
||||
loadedTextures.forEach((name, texture) -> texture.dispose());
|
||||
log.info("{} textures has been disposed", loadedTextures.size());
|
||||
}
|
||||
}
|
||||
|
||||
132
engine/src/main/java/com/bartlomiejpluta/base/core/gl/object/texture/Texture.java
Executable file → Normal file
132
engine/src/main/java/com/bartlomiejpluta/base/core/gl/object/texture/Texture.java
Executable file → Normal file
@@ -1,66 +1,66 @@
|
||||
package com.bartlomiejpluta.base.core.gl.object.texture;
|
||||
|
||||
import com.bartlomiejpluta.base.core.error.AppException;
|
||||
import com.bartlomiejpluta.base.core.gc.Disposable;
|
||||
import lombok.Getter;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL13.GL_TEXTURE0;
|
||||
import static org.lwjgl.opengl.GL13.glActiveTexture;
|
||||
import static org.lwjgl.stb.STBImage.stbi_failure_reason;
|
||||
import static org.lwjgl.stb.STBImage.stbi_load_from_memory;
|
||||
|
||||
public class Texture implements Disposable {
|
||||
private static final int DESIRED_CHANNELS = 4;
|
||||
|
||||
private final int textureId;
|
||||
|
||||
@Getter
|
||||
private final String fileName;
|
||||
|
||||
@Getter
|
||||
private final int width;
|
||||
|
||||
@Getter
|
||||
private final int height;
|
||||
|
||||
Texture(String textureFilename, ByteBuffer buffer) {
|
||||
try (var stack = MemoryStack.stackPush()) {
|
||||
var widthBuffer = stack.mallocInt(1);
|
||||
var heightBuffer = stack.mallocInt(1);
|
||||
var channelsBuffer = stack.mallocInt(1);
|
||||
|
||||
buffer = stbi_load_from_memory(buffer, widthBuffer, heightBuffer, channelsBuffer, DESIRED_CHANNELS);
|
||||
if (buffer == null) {
|
||||
throw new AppException("Image file [%s] could not be loaded: %s", textureFilename, stbi_failure_reason());
|
||||
}
|
||||
|
||||
width = widthBuffer.get();
|
||||
height = heightBuffer.get();
|
||||
|
||||
textureId = glGenTextures();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
fileName = textureFilename;
|
||||
}
|
||||
}
|
||||
|
||||
public void activate() {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
glDeleteTextures(textureId);
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.object.texture;
|
||||
|
||||
import com.bartlomiejpluta.base.core.error.AppException;
|
||||
import com.bartlomiejpluta.base.core.gc.Disposable;
|
||||
import lombok.Getter;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL13.GL_TEXTURE0;
|
||||
import static org.lwjgl.opengl.GL13.glActiveTexture;
|
||||
import static org.lwjgl.stb.STBImage.stbi_failure_reason;
|
||||
import static org.lwjgl.stb.STBImage.stbi_load_from_memory;
|
||||
|
||||
public class Texture implements Disposable {
|
||||
private static final int DESIRED_CHANNELS = 4;
|
||||
|
||||
private final int textureId;
|
||||
|
||||
@Getter
|
||||
private final String fileName;
|
||||
|
||||
@Getter
|
||||
private final int width;
|
||||
|
||||
@Getter
|
||||
private final int height;
|
||||
|
||||
Texture(String textureFilename, ByteBuffer buffer) {
|
||||
try (var stack = MemoryStack.stackPush()) {
|
||||
var widthBuffer = stack.mallocInt(1);
|
||||
var heightBuffer = stack.mallocInt(1);
|
||||
var channelsBuffer = stack.mallocInt(1);
|
||||
|
||||
buffer = stbi_load_from_memory(buffer, widthBuffer, heightBuffer, channelsBuffer, DESIRED_CHANNELS);
|
||||
if (buffer == null) {
|
||||
throw new AppException("Image file [%s] could not be loaded: %s", textureFilename, stbi_failure_reason());
|
||||
}
|
||||
|
||||
width = widthBuffer.get();
|
||||
height = heightBuffer.get();
|
||||
|
||||
textureId = glGenTextures();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
fileName = textureFilename;
|
||||
}
|
||||
}
|
||||
|
||||
public void activate() {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
glDeleteTextures(textureId);
|
||||
}
|
||||
}
|
||||
|
||||
14
engine/src/main/java/com/bartlomiejpluta/base/core/gl/object/texture/TextureManager.java
Executable file → Normal file
14
engine/src/main/java/com/bartlomiejpluta/base/core/gl/object/texture/TextureManager.java
Executable file → Normal file
@@ -1,7 +1,7 @@
|
||||
package com.bartlomiejpluta.base.core.gl.object.texture;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Cleanable;
|
||||
|
||||
public interface TextureManager extends Cleanable {
|
||||
Texture loadTexture(String textureFileName);
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.object.texture;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Cleanable;
|
||||
|
||||
public interface TextureManager extends Cleanable {
|
||||
Texture loadTexture(String textureFileName);
|
||||
}
|
||||
|
||||
124
engine/src/main/java/com/bartlomiejpluta/base/core/gl/render/DefaultRenderer.java
Executable file → Normal file
124
engine/src/main/java/com/bartlomiejpluta/base/core/gl/render/DefaultRenderer.java
Executable file → Normal file
@@ -1,62 +1,62 @@
|
||||
package com.bartlomiejpluta.base.core.gl.render;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.shader.constant.UniformName;
|
||||
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static org.lwjgl.opengl.GL15.*;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultRenderer implements Renderer {
|
||||
private final ShaderManager shaderManager;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
log.info("Initializing renderer");
|
||||
shaderManager
|
||||
.createShader("default", "/shaders/default.vs", "/shaders/default.fs")
|
||||
.selectShader("default")
|
||||
.createUniform(UniformName.UNI_MODEL_MATRIX)
|
||||
.createUniform(UniformName.UNI_VIEW_MATRIX)
|
||||
.createUniform(UniformName.UNI_PROJECTION_MATRIX)
|
||||
.createUniform(UniformName.UNI_OBJECT_COLOR)
|
||||
.createUniform(UniformName.UNI_HAS_OBJECT_TEXTURE)
|
||||
.createUniform(UniformName.UNI_TEXTURE_SAMPLER)
|
||||
.createUniform(UniformName.UNI_SPRITE_SIZE)
|
||||
.createUniform(UniformName.UNI_SPRITE_POSITION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Window window, Renderable renderable) {
|
||||
clear();
|
||||
updateViewport(window);
|
||||
|
||||
shaderManager.selectShader("default").useSelectedShader();
|
||||
|
||||
renderable.render(window, shaderManager);
|
||||
|
||||
shaderManager.detachCurrentShader();
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
private void updateViewport(Window window) {
|
||||
if (window.isResized()) {
|
||||
glViewport(0, 0, window.getWidth(), window.getHeight());
|
||||
window.setResized(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
log.info("There is nothing to clean up here");
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.render;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.shader.constant.UniformName;
|
||||
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static org.lwjgl.opengl.GL15.*;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultRenderer implements Renderer {
|
||||
private final ShaderManager shaderManager;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
log.info("Initializing renderer");
|
||||
shaderManager
|
||||
.createShader("default", "/shaders/default.vs", "/shaders/default.fs")
|
||||
.selectShader("default")
|
||||
.createUniform(UniformName.UNI_MODEL_MATRIX)
|
||||
.createUniform(UniformName.UNI_VIEW_MATRIX)
|
||||
.createUniform(UniformName.UNI_PROJECTION_MATRIX)
|
||||
.createUniform(UniformName.UNI_OBJECT_COLOR)
|
||||
.createUniform(UniformName.UNI_HAS_OBJECT_TEXTURE)
|
||||
.createUniform(UniformName.UNI_TEXTURE_SAMPLER)
|
||||
.createUniform(UniformName.UNI_SPRITE_SIZE)
|
||||
.createUniform(UniformName.UNI_SPRITE_POSITION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Window window, Renderable renderable) {
|
||||
clear();
|
||||
updateViewport(window);
|
||||
|
||||
shaderManager.selectShader("default").useSelectedShader();
|
||||
|
||||
renderable.render(window, shaderManager);
|
||||
|
||||
shaderManager.detachCurrentShader();
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
private void updateViewport(Window window) {
|
||||
if (window.isResized()) {
|
||||
glViewport(0, 0, window.getWidth(), window.getHeight());
|
||||
window.setResized(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
log.info("There is nothing to clean up here");
|
||||
}
|
||||
}
|
||||
|
||||
16
engine/src/main/java/com/bartlomiejpluta/base/core/gl/render/Renderable.java
Executable file → Normal file
16
engine/src/main/java/com/bartlomiejpluta/base/core/gl/render/Renderable.java
Executable file → Normal file
@@ -1,8 +1,8 @@
|
||||
package com.bartlomiejpluta.base.core.gl.render;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
|
||||
public interface Renderable {
|
||||
void render(Window window, ShaderManager shaderManager);
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.render;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
|
||||
public interface Renderable {
|
||||
void render(Window window, ShaderManager shaderManager);
|
||||
}
|
||||
|
||||
18
engine/src/main/java/com/bartlomiejpluta/base/core/gl/render/Renderer.java
Executable file → Normal file
18
engine/src/main/java/com/bartlomiejpluta/base/core/gl/render/Renderer.java
Executable file → Normal file
@@ -1,9 +1,9 @@
|
||||
package com.bartlomiejpluta.base.core.gl.render;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Cleanable;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
|
||||
public interface Renderer extends Cleanable {
|
||||
void init();
|
||||
void render(Window window, Renderable renderable);
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.render;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Cleanable;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
|
||||
public interface Renderer extends Cleanable {
|
||||
void init();
|
||||
void render(Window window, Renderable renderable);
|
||||
}
|
||||
|
||||
24
engine/src/main/java/com/bartlomiejpluta/base/core/gl/shader/constant/UniformName.java
Executable file → Normal file
24
engine/src/main/java/com/bartlomiejpluta/base/core/gl/shader/constant/UniformName.java
Executable file → Normal file
@@ -1,12 +1,12 @@
|
||||
package com.bartlomiejpluta.base.core.gl.shader.constant;
|
||||
|
||||
public interface UniformName {
|
||||
String UNI_MODEL_MATRIX = "modelMatrix";
|
||||
String UNI_VIEW_MATRIX = "viewMatrix";
|
||||
String UNI_PROJECTION_MATRIX = "projectionMatrix";
|
||||
String UNI_OBJECT_COLOR = "objectColor";
|
||||
String UNI_HAS_OBJECT_TEXTURE = "hasTexture";
|
||||
String UNI_TEXTURE_SAMPLER = "sampler";
|
||||
String UNI_SPRITE_SIZE = "spriteSize";
|
||||
String UNI_SPRITE_POSITION = "spritePosition";
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.shader.constant;
|
||||
|
||||
public interface UniformName {
|
||||
String UNI_MODEL_MATRIX = "modelMatrix";
|
||||
String UNI_VIEW_MATRIX = "viewMatrix";
|
||||
String UNI_PROJECTION_MATRIX = "projectionMatrix";
|
||||
String UNI_OBJECT_COLOR = "objectColor";
|
||||
String UNI_HAS_OBJECT_TEXTURE = "hasTexture";
|
||||
String UNI_TEXTURE_SAMPLER = "sampler";
|
||||
String UNI_SPRITE_SIZE = "spriteSize";
|
||||
String UNI_SPRITE_POSITION = "spritePosition";
|
||||
}
|
||||
|
||||
298
engine/src/main/java/com/bartlomiejpluta/base/core/gl/shader/manager/DefaultShaderManager.java
Executable file → Normal file
298
engine/src/main/java/com/bartlomiejpluta/base/core/gl/shader/manager/DefaultShaderManager.java
Executable file → Normal file
@@ -1,149 +1,149 @@
|
||||
package com.bartlomiejpluta.base.core.gl.shader.manager;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.shader.program.ShaderProgram;
|
||||
import com.bartlomiejpluta.base.core.gl.shader.uniform.Uniform;
|
||||
import com.bartlomiejpluta.base.core.util.res.ResourcesManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.joml.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultShaderManager implements ShaderManager {
|
||||
private final ResourcesManager resourcesManager;
|
||||
private final Map<String, ShaderProgram> shaders = new HashMap<>();
|
||||
private ShaderProgram current;
|
||||
|
||||
@Override
|
||||
public ShaderManager createShader(String programName, String vertexShaderFilename, String fragmentShaderFilename) {
|
||||
log.info("Creating {} shader", programName);
|
||||
var vertexShaderCode = resourcesManager.loadResourceAsString(vertexShaderFilename);
|
||||
var fragmentShaderCode = resourcesManager.loadResourceAsString(fragmentShaderFilename);
|
||||
var program = ShaderProgram.compile(vertexShaderCode, fragmentShaderCode);
|
||||
|
||||
shaders.put(programName, program);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager selectShader(String programName) {
|
||||
current = shaders.get(programName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager useSelectedShader() {
|
||||
current.use();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager detachCurrentShader() {
|
||||
current.detach();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager createUniform(String uniformName) {
|
||||
current.createUniform(uniformName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager createUniform(String uniformName, Uniform uniform) {
|
||||
current.createUniform(uniformName, uniform);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager createUniforms(String uniformName, int size) {
|
||||
current.createUniforms(uniformName, size);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager createUniforms(String uniformName, int size, Uniform uniform) {
|
||||
current.createUniforms(uniformName, size, uniform);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, int value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, boolean value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, float value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Vector2f value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Vector3f value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Vector4f value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Matrix3f value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Matrix4f value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Uniform uniform) {
|
||||
current.setUniform(uniformName, uniform);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, int index, Uniform uniform) {
|
||||
current.setUniform(uniformName, index, uniform);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniforms(String uniformName, Uniform[] uniforms) {
|
||||
current.setUniforms(uniformName, uniforms);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
log.info("Disposing shaders");
|
||||
shaders.forEach((name, program) -> program.dispose());
|
||||
log.info("{} shaders has been disposed", shaders.size());
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.shader.manager;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.shader.program.ShaderProgram;
|
||||
import com.bartlomiejpluta.base.core.gl.shader.uniform.Uniform;
|
||||
import com.bartlomiejpluta.base.core.util.res.ResourcesManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.joml.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultShaderManager implements ShaderManager {
|
||||
private final ResourcesManager resourcesManager;
|
||||
private final Map<String, ShaderProgram> shaders = new HashMap<>();
|
||||
private ShaderProgram current;
|
||||
|
||||
@Override
|
||||
public ShaderManager createShader(String programName, String vertexShaderFilename, String fragmentShaderFilename) {
|
||||
log.info("Creating {} shader", programName);
|
||||
var vertexShaderCode = resourcesManager.loadResourceAsString(vertexShaderFilename);
|
||||
var fragmentShaderCode = resourcesManager.loadResourceAsString(fragmentShaderFilename);
|
||||
var program = ShaderProgram.compile(vertexShaderCode, fragmentShaderCode);
|
||||
|
||||
shaders.put(programName, program);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager selectShader(String programName) {
|
||||
current = shaders.get(programName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager useSelectedShader() {
|
||||
current.use();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager detachCurrentShader() {
|
||||
current.detach();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager createUniform(String uniformName) {
|
||||
current.createUniform(uniformName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager createUniform(String uniformName, Uniform uniform) {
|
||||
current.createUniform(uniformName, uniform);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager createUniforms(String uniformName, int size) {
|
||||
current.createUniforms(uniformName, size);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager createUniforms(String uniformName, int size, Uniform uniform) {
|
||||
current.createUniforms(uniformName, size, uniform);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, int value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, boolean value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, float value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Vector2f value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Vector3f value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Vector4f value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Matrix3f value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Matrix4f value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, Uniform uniform) {
|
||||
current.setUniform(uniformName, uniform);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, int index, Uniform uniform) {
|
||||
current.setUniform(uniformName, index, uniform);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniforms(String uniformName, Uniform[] uniforms) {
|
||||
current.setUniforms(uniformName, uniforms);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
log.info("Disposing shaders");
|
||||
shaders.forEach((name, program) -> program.dispose());
|
||||
log.info("{} shaders has been disposed", shaders.size());
|
||||
}
|
||||
}
|
||||
|
||||
88
engine/src/main/java/com/bartlomiejpluta/base/core/gl/shader/manager/ShaderManager.java
Executable file → Normal file
88
engine/src/main/java/com/bartlomiejpluta/base/core/gl/shader/manager/ShaderManager.java
Executable file → Normal file
@@ -1,45 +1,45 @@
|
||||
package com.bartlomiejpluta.base.core.gl.shader.manager;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Cleanable;
|
||||
import com.bartlomiejpluta.base.core.gl.shader.uniform.Uniform;
|
||||
import org.joml.*;
|
||||
|
||||
public interface ShaderManager extends Cleanable {
|
||||
ShaderManager createShader(String programName, String vertexShaderFilename, String fragmentShaderFilename);
|
||||
|
||||
ShaderManager selectShader(String programName);
|
||||
|
||||
ShaderManager useSelectedShader();
|
||||
|
||||
ShaderManager detachCurrentShader();
|
||||
|
||||
ShaderManager createUniform(String uniformName);
|
||||
|
||||
ShaderManager createUniform(String uniformName, Uniform uniform);
|
||||
|
||||
ShaderManager createUniforms(String uniformName, int size);
|
||||
|
||||
ShaderManager createUniforms(String uniformName, int size, Uniform uniform);
|
||||
|
||||
ShaderManager setUniform(String uniformName, int value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, boolean value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, float value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Vector2f value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Vector3f value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Vector4f value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Matrix3f value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Matrix4f value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Uniform uniform);
|
||||
|
||||
ShaderManager setUniform(String uniformName, int index, Uniform uniform);
|
||||
|
||||
ShaderManager setUniforms(String uniformName, Uniform[] uniforms);
|
||||
package com.bartlomiejpluta.base.core.gl.shader.manager;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Cleanable;
|
||||
import com.bartlomiejpluta.base.core.gl.shader.uniform.Uniform;
|
||||
import org.joml.*;
|
||||
|
||||
public interface ShaderManager extends Cleanable {
|
||||
ShaderManager createShader(String programName, String vertexShaderFilename, String fragmentShaderFilename);
|
||||
|
||||
ShaderManager selectShader(String programName);
|
||||
|
||||
ShaderManager useSelectedShader();
|
||||
|
||||
ShaderManager detachCurrentShader();
|
||||
|
||||
ShaderManager createUniform(String uniformName);
|
||||
|
||||
ShaderManager createUniform(String uniformName, Uniform uniform);
|
||||
|
||||
ShaderManager createUniforms(String uniformName, int size);
|
||||
|
||||
ShaderManager createUniforms(String uniformName, int size, Uniform uniform);
|
||||
|
||||
ShaderManager setUniform(String uniformName, int value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, boolean value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, float value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Vector2f value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Vector3f value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Vector4f value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Matrix3f value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Matrix4f value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, Uniform uniform);
|
||||
|
||||
ShaderManager setUniform(String uniformName, int index, Uniform uniform);
|
||||
|
||||
ShaderManager setUniforms(String uniformName, Uniform[] uniforms);
|
||||
}
|
||||
350
engine/src/main/java/com/bartlomiejpluta/base/core/gl/shader/program/ShaderProgram.java
Executable file → Normal file
350
engine/src/main/java/com/bartlomiejpluta/base/core/gl/shader/program/ShaderProgram.java
Executable file → Normal file
@@ -1,175 +1,175 @@
|
||||
package com.bartlomiejpluta.base.core.gl.shader.program;
|
||||
|
||||
import com.bartlomiejpluta.base.core.error.AppException;
|
||||
import com.bartlomiejpluta.base.core.gc.Disposable;
|
||||
import com.bartlomiejpluta.base.core.gl.shader.uniform.Uniform;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.joml.*;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.lwjgl.opengl.GL20.*;
|
||||
|
||||
@Slf4j
|
||||
public class ShaderProgram implements Disposable {
|
||||
private final int programId;
|
||||
private final int vertexShaderId;
|
||||
private final int fragmentShaderId;
|
||||
private final Map<String, Integer> uniforms = new HashMap<>();
|
||||
|
||||
private ShaderProgram(String vertexShaderCode, String fragmentShaderCode) {
|
||||
this.programId = glCreateProgram();
|
||||
|
||||
if(this.programId == 0) {
|
||||
throw new AppException("Could not create shader program");
|
||||
}
|
||||
|
||||
this.vertexShaderId = createShader(vertexShaderCode, GL_VERTEX_SHADER);
|
||||
this.fragmentShaderId = createShader(fragmentShaderCode, GL_FRAGMENT_SHADER);
|
||||
|
||||
linkProgram();
|
||||
}
|
||||
|
||||
private int createShader(String shaderCode, int shaderType) {
|
||||
int shaderId = glCreateShader(shaderType);
|
||||
if(shaderId == 0) {
|
||||
throw new AppException("Could not create shader of type: %s", shaderType);
|
||||
}
|
||||
|
||||
glShaderSource(shaderId, shaderCode);
|
||||
glCompileShader(shaderId);
|
||||
|
||||
if(glGetShaderi(shaderId, GL_COMPILE_STATUS) == 0) {
|
||||
throw new AppException("Could not compile shader code: %s", glGetShaderInfoLog(shaderId, 1024));
|
||||
}
|
||||
|
||||
glAttachShader(programId, shaderId);
|
||||
|
||||
return shaderId;
|
||||
}
|
||||
|
||||
private void linkProgram() {
|
||||
glLinkProgram(programId);
|
||||
if(glGetProgrami(programId, GL_LINK_STATUS) == 0) {
|
||||
throw new AppException("Could not link shader program: %s", glGetProgramInfoLog(programId, 1024));
|
||||
}
|
||||
|
||||
if(vertexShaderId != 0) {
|
||||
glDetachShader(programId, vertexShaderId);
|
||||
}
|
||||
|
||||
if(fragmentShaderId != 0) {
|
||||
glDetachShader(programId, fragmentShaderId);
|
||||
}
|
||||
|
||||
glValidateProgram(programId);
|
||||
if(glGetProgrami(programId, GL_VALIDATE_STATUS) == 0) {
|
||||
log.warn("Program validation failed: {}", glGetProgramInfoLog(programId, 1024));
|
||||
}
|
||||
}
|
||||
|
||||
public void createUniform(String uniformName) {
|
||||
int location = glGetUniformLocation(programId, uniformName);
|
||||
|
||||
if(location < 0) {
|
||||
throw new AppException("Could not find uniform: %s", uniformName);
|
||||
}
|
||||
|
||||
uniforms.put(uniformName, location);
|
||||
}
|
||||
|
||||
public void createUniform(String uniformName, Uniform uniform) {
|
||||
uniform.createUniform(this, uniformName);
|
||||
}
|
||||
|
||||
public void createUniforms(String uniformName, int size) {
|
||||
for(int i=0; i<size; ++i) {
|
||||
createUniform(format("%s[%d]", uniformName, i));
|
||||
}
|
||||
}
|
||||
|
||||
public void createUniforms(String uniformName, int size, Uniform uniform) {
|
||||
for(int i=0; i<size; ++i) {
|
||||
createUniform(format("%s[%d]", uniformName, i), uniform);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, int value) {
|
||||
glUniform1i(uniforms.get(uniformName), value);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, boolean value) {
|
||||
glUniform1i(uniforms.get(uniformName), value ? 1 : 0);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, float value) {
|
||||
glUniform1f(uniforms.get(uniformName), value);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Vector2f value) {
|
||||
glUniform2f(uniforms.get(uniformName), value.x, value.y);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Vector3f value) {
|
||||
glUniform3f(uniforms.get(uniformName), value.x, value.y, value.z);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Vector4f value) {
|
||||
glUniform4f(uniforms.get(uniformName), value.x, value.y, value.z, value.w);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Matrix3f value) {
|
||||
try(var stack = MemoryStack.stackPush()) {
|
||||
var buffer = stack.mallocFloat(3 * 3);
|
||||
value.get(buffer);
|
||||
glUniformMatrix4fv(uniforms.get(uniformName), false, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Matrix4f value) {
|
||||
try(var stack = MemoryStack.stackPush()) {
|
||||
var buffer = stack.mallocFloat(4 * 4);
|
||||
value.get(buffer);
|
||||
glUniformMatrix4fv(uniforms.get(uniformName), false, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Uniform uniform) {
|
||||
uniform.setUniform(this, uniformName);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, int index, Uniform uniform) {
|
||||
setUniform(format("%s[%d]", uniformName, index), uniform);
|
||||
}
|
||||
|
||||
public void setUniforms(String uniformName, Uniform[] uniforms) {
|
||||
var size = uniforms != null ? uniforms.length : 0;
|
||||
for(int i=0; i<size; ++i) {
|
||||
setUniform(format("%s[%d]", uniformName, i), uniforms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void use() {
|
||||
glUseProgram(programId);
|
||||
}
|
||||
|
||||
public void detach() {
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
glUseProgram(0);
|
||||
|
||||
if(programId != 0) {
|
||||
glDeleteProgram(programId);
|
||||
}
|
||||
}
|
||||
|
||||
public static ShaderProgram compile(String vertexShaderCode, String fragmentShaderCode) {
|
||||
return new ShaderProgram(vertexShaderCode, fragmentShaderCode);
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.shader.program;
|
||||
|
||||
import com.bartlomiejpluta.base.core.error.AppException;
|
||||
import com.bartlomiejpluta.base.core.gc.Disposable;
|
||||
import com.bartlomiejpluta.base.core.gl.shader.uniform.Uniform;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.joml.*;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.lwjgl.opengl.GL20.*;
|
||||
|
||||
@Slf4j
|
||||
public class ShaderProgram implements Disposable {
|
||||
private final int programId;
|
||||
private final int vertexShaderId;
|
||||
private final int fragmentShaderId;
|
||||
private final Map<String, Integer> uniforms = new HashMap<>();
|
||||
|
||||
private ShaderProgram(String vertexShaderCode, String fragmentShaderCode) {
|
||||
this.programId = glCreateProgram();
|
||||
|
||||
if(this.programId == 0) {
|
||||
throw new AppException("Could not create shader program");
|
||||
}
|
||||
|
||||
this.vertexShaderId = createShader(vertexShaderCode, GL_VERTEX_SHADER);
|
||||
this.fragmentShaderId = createShader(fragmentShaderCode, GL_FRAGMENT_SHADER);
|
||||
|
||||
linkProgram();
|
||||
}
|
||||
|
||||
private int createShader(String shaderCode, int shaderType) {
|
||||
int shaderId = glCreateShader(shaderType);
|
||||
if(shaderId == 0) {
|
||||
throw new AppException("Could not create shader of type: %s", shaderType);
|
||||
}
|
||||
|
||||
glShaderSource(shaderId, shaderCode);
|
||||
glCompileShader(shaderId);
|
||||
|
||||
if(glGetShaderi(shaderId, GL_COMPILE_STATUS) == 0) {
|
||||
throw new AppException("Could not compile shader code: %s", glGetShaderInfoLog(shaderId, 1024));
|
||||
}
|
||||
|
||||
glAttachShader(programId, shaderId);
|
||||
|
||||
return shaderId;
|
||||
}
|
||||
|
||||
private void linkProgram() {
|
||||
glLinkProgram(programId);
|
||||
if(glGetProgrami(programId, GL_LINK_STATUS) == 0) {
|
||||
throw new AppException("Could not link shader program: %s", glGetProgramInfoLog(programId, 1024));
|
||||
}
|
||||
|
||||
if(vertexShaderId != 0) {
|
||||
glDetachShader(programId, vertexShaderId);
|
||||
}
|
||||
|
||||
if(fragmentShaderId != 0) {
|
||||
glDetachShader(programId, fragmentShaderId);
|
||||
}
|
||||
|
||||
glValidateProgram(programId);
|
||||
if(glGetProgrami(programId, GL_VALIDATE_STATUS) == 0) {
|
||||
log.warn("Program validation failed: {}", glGetProgramInfoLog(programId, 1024));
|
||||
}
|
||||
}
|
||||
|
||||
public void createUniform(String uniformName) {
|
||||
int location = glGetUniformLocation(programId, uniformName);
|
||||
|
||||
if(location < 0) {
|
||||
throw new AppException("Could not find uniform: %s", uniformName);
|
||||
}
|
||||
|
||||
uniforms.put(uniformName, location);
|
||||
}
|
||||
|
||||
public void createUniform(String uniformName, Uniform uniform) {
|
||||
uniform.createUniform(this, uniformName);
|
||||
}
|
||||
|
||||
public void createUniforms(String uniformName, int size) {
|
||||
for(int i=0; i<size; ++i) {
|
||||
createUniform(format("%s[%d]", uniformName, i));
|
||||
}
|
||||
}
|
||||
|
||||
public void createUniforms(String uniformName, int size, Uniform uniform) {
|
||||
for(int i=0; i<size; ++i) {
|
||||
createUniform(format("%s[%d]", uniformName, i), uniform);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, int value) {
|
||||
glUniform1i(uniforms.get(uniformName), value);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, boolean value) {
|
||||
glUniform1i(uniforms.get(uniformName), value ? 1 : 0);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, float value) {
|
||||
glUniform1f(uniforms.get(uniformName), value);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Vector2f value) {
|
||||
glUniform2f(uniforms.get(uniformName), value.x, value.y);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Vector3f value) {
|
||||
glUniform3f(uniforms.get(uniformName), value.x, value.y, value.z);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Vector4f value) {
|
||||
glUniform4f(uniforms.get(uniformName), value.x, value.y, value.z, value.w);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Matrix3f value) {
|
||||
try(var stack = MemoryStack.stackPush()) {
|
||||
var buffer = stack.mallocFloat(3 * 3);
|
||||
value.get(buffer);
|
||||
glUniformMatrix4fv(uniforms.get(uniformName), false, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Matrix4f value) {
|
||||
try(var stack = MemoryStack.stackPush()) {
|
||||
var buffer = stack.mallocFloat(4 * 4);
|
||||
value.get(buffer);
|
||||
glUniformMatrix4fv(uniforms.get(uniformName), false, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, Uniform uniform) {
|
||||
uniform.setUniform(this, uniformName);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, int index, Uniform uniform) {
|
||||
setUniform(format("%s[%d]", uniformName, index), uniform);
|
||||
}
|
||||
|
||||
public void setUniforms(String uniformName, Uniform[] uniforms) {
|
||||
var size = uniforms != null ? uniforms.length : 0;
|
||||
for(int i=0; i<size; ++i) {
|
||||
setUniform(format("%s[%d]", uniformName, i), uniforms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void use() {
|
||||
glUseProgram(programId);
|
||||
}
|
||||
|
||||
public void detach() {
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
glUseProgram(0);
|
||||
|
||||
if(programId != 0) {
|
||||
glDeleteProgram(programId);
|
||||
}
|
||||
}
|
||||
|
||||
public static ShaderProgram compile(String vertexShaderCode, String fragmentShaderCode) {
|
||||
return new ShaderProgram(vertexShaderCode, fragmentShaderCode);
|
||||
}
|
||||
}
|
||||
|
||||
18
engine/src/main/java/com/bartlomiejpluta/base/core/gl/shader/uniform/Uniform.java
Executable file → Normal file
18
engine/src/main/java/com/bartlomiejpluta/base/core/gl/shader/uniform/Uniform.java
Executable file → Normal file
@@ -1,9 +1,9 @@
|
||||
package com.bartlomiejpluta.base.core.gl.shader.uniform;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.shader.program.ShaderProgram;
|
||||
|
||||
public interface Uniform {
|
||||
void createUniform(ShaderProgram shaderProgram, String uniformName);
|
||||
|
||||
void setUniform(ShaderProgram shaderProgram, String uniformName);
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.gl.shader.uniform;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.shader.program.ShaderProgram;
|
||||
|
||||
public interface Uniform {
|
||||
void createUniform(ShaderProgram shaderProgram, String uniformName);
|
||||
|
||||
void setUniform(ShaderProgram shaderProgram, String uniformName);
|
||||
}
|
||||
|
||||
78
engine/src/main/java/com/bartlomiejpluta/base/core/image/DefaultImageManager.java
Executable file → Normal file
78
engine/src/main/java/com/bartlomiejpluta/base/core/image/DefaultImageManager.java
Executable file → Normal file
@@ -1,39 +1,39 @@
|
||||
package com.bartlomiejpluta.base.core.image;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.object.material.Material;
|
||||
import com.bartlomiejpluta.base.core.gl.object.texture.TextureManager;
|
||||
import com.bartlomiejpluta.base.core.util.math.MathUtil;
|
||||
import com.bartlomiejpluta.base.core.util.mesh.MeshManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultImageManager implements ImageManager {
|
||||
private final MeshManager meshManager;
|
||||
private final TextureManager textureManager;
|
||||
|
||||
@Override
|
||||
public Image createImage(String imageFileName) {
|
||||
var texture = textureManager.loadTexture(imageFileName);
|
||||
var width = texture.getWidth();
|
||||
var height = texture.getHeight();
|
||||
var gcd = MathUtil.gcdEuclidean(width, height);
|
||||
var initialWidth = width / gcd;
|
||||
var initialHeight = height / gcd;
|
||||
var mesh = meshManager.createQuad(initialWidth, initialHeight, 0, 0);
|
||||
|
||||
var image = new Image(mesh, Material.textured(texture), initialWidth, initialHeight);
|
||||
image.setScale(gcd);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
log.info("There is nothing to clean up here");
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.image;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.object.material.Material;
|
||||
import com.bartlomiejpluta.base.core.gl.object.texture.TextureManager;
|
||||
import com.bartlomiejpluta.base.core.util.math.MathUtil;
|
||||
import com.bartlomiejpluta.base.core.util.mesh.MeshManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultImageManager implements ImageManager {
|
||||
private final MeshManager meshManager;
|
||||
private final TextureManager textureManager;
|
||||
|
||||
@Override
|
||||
public Image createImage(String imageFileName) {
|
||||
var texture = textureManager.loadTexture(imageFileName);
|
||||
var width = texture.getWidth();
|
||||
var height = texture.getHeight();
|
||||
var gcd = MathUtil.gcdEuclidean(width, height);
|
||||
var initialWidth = width / gcd;
|
||||
var initialHeight = height / gcd;
|
||||
var mesh = meshManager.createQuad(initialWidth, initialHeight, 0, 0);
|
||||
|
||||
var image = new Image(mesh, Material.textured(texture), initialWidth, initialHeight);
|
||||
image.setScale(gcd);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
log.info("There is nothing to clean up here");
|
||||
}
|
||||
}
|
||||
|
||||
38
engine/src/main/java/com/bartlomiejpluta/base/core/image/Image.java
Executable file → Normal file
38
engine/src/main/java/com/bartlomiejpluta/base/core/image/Image.java
Executable file → Normal file
@@ -1,19 +1,19 @@
|
||||
package com.bartlomiejpluta.base.core.image;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.object.material.Material;
|
||||
import com.bartlomiejpluta.base.core.gl.object.mesh.Mesh;
|
||||
import com.bartlomiejpluta.base.core.world.object.RenderableObject;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class Image extends RenderableObject {
|
||||
private final int initialWidth;
|
||||
private final int initialHeight;
|
||||
|
||||
Image(Mesh mesh, Material texture, int initialWidth, int initialHeight) {
|
||||
super(mesh);
|
||||
this.initialWidth = initialWidth;
|
||||
this.initialHeight = initialHeight;
|
||||
setMaterial(texture);
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.image;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gl.object.material.Material;
|
||||
import com.bartlomiejpluta.base.core.gl.object.mesh.Mesh;
|
||||
import com.bartlomiejpluta.base.core.world.object.RenderableObject;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class Image extends RenderableObject {
|
||||
private final int initialWidth;
|
||||
private final int initialHeight;
|
||||
|
||||
Image(Mesh mesh, Material texture, int initialWidth, int initialHeight) {
|
||||
super(mesh);
|
||||
this.initialWidth = initialWidth;
|
||||
this.initialHeight = initialHeight;
|
||||
setMaterial(texture);
|
||||
}
|
||||
}
|
||||
|
||||
0
engine/src/main/java/com/bartlomiejpluta/base/core/image/ImageManager.java
Executable file → Normal file
0
engine/src/main/java/com/bartlomiejpluta/base/core/image/ImageManager.java
Executable file → Normal file
28
engine/src/main/java/com/bartlomiejpluta/base/core/logic/GameLogic.java
Executable file → Normal file
28
engine/src/main/java/com/bartlomiejpluta/base/core/logic/GameLogic.java
Executable file → Normal file
@@ -1,14 +1,14 @@
|
||||
package com.bartlomiejpluta.base.core.logic;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Cleanable;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
|
||||
public interface GameLogic extends Cleanable {
|
||||
void init(Window window);
|
||||
|
||||
void input(Window window);
|
||||
|
||||
void update(float dt);
|
||||
|
||||
void render(Window window);
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.logic;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Cleanable;
|
||||
import com.bartlomiejpluta.base.core.ui.Window;
|
||||
|
||||
public interface GameLogic extends Cleanable {
|
||||
void init(Window window);
|
||||
|
||||
void input(Window window);
|
||||
|
||||
void update(float dt);
|
||||
|
||||
void render(Window window);
|
||||
}
|
||||
|
||||
10
engine/src/main/java/com/bartlomiejpluta/base/core/logic/Updatable.java
Executable file → Normal file
10
engine/src/main/java/com/bartlomiejpluta/base/core/logic/Updatable.java
Executable file → Normal file
@@ -1,5 +1,5 @@
|
||||
package com.bartlomiejpluta.base.core.logic;
|
||||
|
||||
public interface Updatable {
|
||||
void update(float dt);
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.logic;
|
||||
|
||||
public interface Updatable {
|
||||
void update(float dt);
|
||||
}
|
||||
|
||||
14
engine/src/main/java/com/bartlomiejpluta/base/core/profiling/fps/FPSMonitor.java
Executable file → Normal file
14
engine/src/main/java/com/bartlomiejpluta/base/core/profiling/fps/FPSMonitor.java
Executable file → Normal file
@@ -1,7 +1,7 @@
|
||||
package com.bartlomiejpluta.base.core.profiling.fps;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Cleanable;
|
||||
import com.bartlomiejpluta.base.core.logic.Updatable;
|
||||
|
||||
public interface FPSMonitor extends Updatable, Cleanable {
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.profiling.fps;
|
||||
|
||||
import com.bartlomiejpluta.base.core.gc.Cleanable;
|
||||
import com.bartlomiejpluta.base.core.logic.Updatable;
|
||||
|
||||
public interface FPSMonitor extends Updatable, Cleanable {
|
||||
}
|
||||
|
||||
126
engine/src/main/java/com/bartlomiejpluta/base/core/profiling/fps/LogFPSMonitor.java
Executable file → Normal file
126
engine/src/main/java/com/bartlomiejpluta/base/core/profiling/fps/LogFPSMonitor.java
Executable file → Normal file
@@ -1,63 +1,63 @@
|
||||
package com.bartlomiejpluta.base.core.profiling.fps;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Comparator.comparingInt;
|
||||
import static java.util.function.Function.identity;
|
||||
import static java.util.stream.Collectors.counting;
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class LogFPSMonitor implements FPSMonitor {
|
||||
private static final int MOD = 30;
|
||||
private final List<Double> values = new LinkedList<>();
|
||||
private float fpsAccumulator = 0;
|
||||
private int pointer = 0;
|
||||
private double fps = 0;
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
fpsAccumulator += dt;
|
||||
|
||||
if (++pointer % MOD == 0) {
|
||||
fps = pointer / fpsAccumulator;
|
||||
fpsAccumulator = 0;
|
||||
pointer = 0;
|
||||
|
||||
values.add(fps);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
log.info("Min FPS: {}, max FPS: {}, avg FPS: {}",
|
||||
values.stream().min(Double::compareTo).orElse(-1.0),
|
||||
values.stream().max(Double::compareTo).orElse(-1.0),
|
||||
totalAverage()
|
||||
);
|
||||
|
||||
printHistogram();
|
||||
}
|
||||
|
||||
private double totalAverage() {
|
||||
return values.stream().reduce(0.0, Double::sum) / values.size();
|
||||
}
|
||||
|
||||
private void printHistogram() {
|
||||
values
|
||||
.stream()
|
||||
.mapToInt(Double::intValue)
|
||||
.boxed()
|
||||
.collect(groupingBy(identity(), counting()))
|
||||
.entrySet()
|
||||
.stream()
|
||||
.sorted(comparingInt(Map.Entry::getKey))
|
||||
.forEach(e -> log.info("{} FPS: {}% ({} occurrences)", e.getKey(), e.getValue() * 100.0f / values.size(), e.getValue()));
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.profiling.fps;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Comparator.comparingInt;
|
||||
import static java.util.function.Function.identity;
|
||||
import static java.util.stream.Collectors.counting;
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class LogFPSMonitor implements FPSMonitor {
|
||||
private static final int MOD = 30;
|
||||
private final List<Double> values = new LinkedList<>();
|
||||
private float fpsAccumulator = 0;
|
||||
private int pointer = 0;
|
||||
private double fps = 0;
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
fpsAccumulator += dt;
|
||||
|
||||
if (++pointer % MOD == 0) {
|
||||
fps = pointer / fpsAccumulator;
|
||||
fpsAccumulator = 0;
|
||||
pointer = 0;
|
||||
|
||||
values.add(fps);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
log.info("Min FPS: {}, max FPS: {}, avg FPS: {}",
|
||||
values.stream().min(Double::compareTo).orElse(-1.0),
|
||||
values.stream().max(Double::compareTo).orElse(-1.0),
|
||||
totalAverage()
|
||||
);
|
||||
|
||||
printHistogram();
|
||||
}
|
||||
|
||||
private double totalAverage() {
|
||||
return values.stream().reduce(0.0, Double::sum) / values.size();
|
||||
}
|
||||
|
||||
private void printHistogram() {
|
||||
values
|
||||
.stream()
|
||||
.mapToInt(Double::intValue)
|
||||
.boxed()
|
||||
.collect(groupingBy(identity(), counting()))
|
||||
.entrySet()
|
||||
.stream()
|
||||
.sorted(comparingInt(Map.Entry::getKey))
|
||||
.forEach(e -> log.info("{} FPS: {}% ({} occurrences)", e.getKey(), e.getValue() * 100.0f / values.size(), e.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
22
engine/src/main/java/com/bartlomiejpluta/base/core/profiling/time/annotation/MeasureExecutionTime.java
Executable file → Normal file
22
engine/src/main/java/com/bartlomiejpluta/base/core/profiling/time/annotation/MeasureExecutionTime.java
Executable file → Normal file
@@ -1,11 +1,11 @@
|
||||
package com.bartlomiejpluta.base.core.profiling.time.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface MeasureExecutionTime {
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.profiling.time.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface MeasureExecutionTime {
|
||||
}
|
||||
|
||||
76
engine/src/main/java/com/bartlomiejpluta/base/core/profiling/time/aspect/ExecutionTimeAspect.java
Executable file → Normal file
76
engine/src/main/java/com/bartlomiejpluta/base/core/profiling/time/aspect/ExecutionTimeAspect.java
Executable file → Normal file
@@ -1,38 +1,38 @@
|
||||
package com.bartlomiejpluta.base.core.profiling.time.aspect;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
@ConditionalOnExpression("${app.profiling.aspects:false}")
|
||||
public class ExecutionTimeAspect {
|
||||
|
||||
@Around("@annotation(com.bartlomiejpluta.base.core.stat.metrics.annotation.MeasureExecutionTime)")
|
||||
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
long start = 0;
|
||||
long end = 0;
|
||||
long elapsed;
|
||||
Object result;
|
||||
try {
|
||||
start = System.nanoTime();
|
||||
result = joinPoint.proceed();
|
||||
} finally {
|
||||
end = System.nanoTime();
|
||||
}
|
||||
|
||||
elapsed = end - start;
|
||||
log.debug("[{}.{}] = [{}s] [{}ms] [{}ns]",
|
||||
joinPoint.getTarget().getClass().getSimpleName(),
|
||||
joinPoint.getSignature().getName(),
|
||||
elapsed / 1000000.0, elapsed / 1000.0, elapsed
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.profiling.time.aspect;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
@ConditionalOnExpression("${app.profiling.aspects:false}")
|
||||
public class ExecutionTimeAspect {
|
||||
|
||||
@Around("@annotation(com.bartlomiejpluta.base.core.stat.metrics.annotation.MeasureExecutionTime)")
|
||||
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
long start = 0;
|
||||
long end = 0;
|
||||
long elapsed;
|
||||
Object result;
|
||||
try {
|
||||
start = System.nanoTime();
|
||||
result = joinPoint.proceed();
|
||||
} finally {
|
||||
end = System.nanoTime();
|
||||
}
|
||||
|
||||
elapsed = end - start;
|
||||
log.debug("[{}.{}] = [{}s] [{}ms] [{}ns]",
|
||||
joinPoint.getTarget().getClass().getSimpleName(),
|
||||
joinPoint.getSignature().getName(),
|
||||
elapsed / 1000000.0, elapsed / 1000.0, elapsed
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
20
engine/src/main/java/com/bartlomiejpluta/base/core/thread/ThreadManager.java
Executable file → Normal file
20
engine/src/main/java/com/bartlomiejpluta/base/core/thread/ThreadManager.java
Executable file → Normal file
@@ -1,10 +1,10 @@
|
||||
package com.bartlomiejpluta.base.core.thread;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ThreadManager {
|
||||
public Thread createThread(String name, Runnable runnable) {
|
||||
return new Thread(runnable, name);
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.thread;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ThreadManager {
|
||||
public Thread createThread(String name, Runnable runnable) {
|
||||
return new Thread(runnable, name);
|
||||
}
|
||||
}
|
||||
|
||||
40
engine/src/main/java/com/bartlomiejpluta/base/core/time/ChronoMeter.java
Executable file → Normal file
40
engine/src/main/java/com/bartlomiejpluta/base/core/time/ChronoMeter.java
Executable file → Normal file
@@ -1,20 +1,20 @@
|
||||
package com.bartlomiejpluta.base.core.time;
|
||||
|
||||
public class ChronoMeter {
|
||||
private double latchedTime;
|
||||
|
||||
public void init() {
|
||||
latchedTime = getTime();
|
||||
}
|
||||
|
||||
private double getTime() {
|
||||
return System.nanoTime() / 1_000_000_000.0;
|
||||
}
|
||||
|
||||
public float getElapsedTime() {
|
||||
double time = getTime();
|
||||
float elapsedTime = (float) (time - latchedTime);
|
||||
latchedTime = time;
|
||||
return elapsedTime;
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.time;
|
||||
|
||||
public class ChronoMeter {
|
||||
private double latchedTime;
|
||||
|
||||
public void init() {
|
||||
latchedTime = getTime();
|
||||
}
|
||||
|
||||
private double getTime() {
|
||||
return System.nanoTime() / 1_000_000_000.0;
|
||||
}
|
||||
|
||||
public float getElapsedTime() {
|
||||
double time = getTime();
|
||||
float elapsedTime = (float) (time - latchedTime);
|
||||
latchedTime = time;
|
||||
return elapsedTime;
|
||||
}
|
||||
}
|
||||
|
||||
238
engine/src/main/java/com/bartlomiejpluta/base/core/ui/Window.java
Executable file → Normal file
238
engine/src/main/java/com/bartlomiejpluta/base/core/ui/Window.java
Executable file → Normal file
@@ -1,119 +1,119 @@
|
||||
package com.bartlomiejpluta.base.core.ui;
|
||||
|
||||
import com.bartlomiejpluta.base.core.error.AppException;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.joml.Vector2f;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
import org.lwjgl.glfw.GLFWVidMode;
|
||||
import org.lwjgl.opengl.GL;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.system.MemoryUtil.NULL;
|
||||
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Window {
|
||||
private final String title;
|
||||
private long windowHandle;
|
||||
|
||||
@Getter
|
||||
private int width;
|
||||
|
||||
@Getter
|
||||
private int height;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean resized;
|
||||
|
||||
public void init() {
|
||||
// Setup an error callback. The default implementation
|
||||
// will print the error message in System.err.
|
||||
GLFWErrorCallback.createPrint(System.err).set();
|
||||
|
||||
// Initialize GLFW. Most GLFW functions will not work before doing this.
|
||||
if (!glfwInit()) {
|
||||
throw new AppException("Unable to initialize GLFW");
|
||||
}
|
||||
|
||||
glfwDefaultWindowHints(); // optional, the current window hints are already the default
|
||||
glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // the window will stay hidden after creation
|
||||
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // the window will be resizable
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
|
||||
// Create the window
|
||||
windowHandle = glfwCreateWindow(width, height, title, NULL, NULL);
|
||||
if (windowHandle == NULL) {
|
||||
throw new AppException("Failed to create the GLFW window");
|
||||
}
|
||||
|
||||
// Setup resize callback
|
||||
glfwSetFramebufferSizeCallback(windowHandle, (window, width, height) -> {
|
||||
Window.this.width = width;
|
||||
Window.this.height = height;
|
||||
Window.this.resized = true;
|
||||
});
|
||||
|
||||
// Setup a key callback. It will be called every time a key is pressed, repeated or released.
|
||||
glfwSetKeyCallback(windowHandle, (window, key, scancode, action, mods) -> {
|
||||
if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) {
|
||||
glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
|
||||
}
|
||||
});
|
||||
|
||||
// Get the resolution of the primary monitor
|
||||
GLFWVidMode videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||
|
||||
// Center our window
|
||||
glfwSetWindowPos(windowHandle, (videoMode.width() - width) / 2, (videoMode.height() - height) / 2);
|
||||
|
||||
// Make the OpenGL context current
|
||||
glfwMakeContextCurrent(windowHandle);
|
||||
|
||||
// Enable V-Sync
|
||||
// glfwSwapInterval(1);
|
||||
|
||||
// Make the window visible
|
||||
glfwShowWindow(windowHandle);
|
||||
|
||||
GL.createCapabilities();
|
||||
|
||||
// Support for transparencies
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Set the clear color
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
glfwSwapBuffers(windowHandle);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
public boolean isKeyPressed(int keyCode) {
|
||||
return glfwGetKey(windowHandle, keyCode) == GLFW_PRESS;
|
||||
}
|
||||
|
||||
public void clear(float r, float g, float b, float alpha) {
|
||||
glClearColor(r, g, b, alpha);
|
||||
}
|
||||
|
||||
public boolean shouldClose() {
|
||||
return glfwWindowShouldClose(windowHandle);
|
||||
}
|
||||
|
||||
public Vector2f getSize() {
|
||||
return new Vector2f(width, height);
|
||||
}
|
||||
|
||||
public static Window create(String title, int width, int height) {
|
||||
return new Window(title, -1, width, height, false);
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.ui;
|
||||
|
||||
import com.bartlomiejpluta.base.core.error.AppException;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.joml.Vector2f;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
import org.lwjgl.glfw.GLFWVidMode;
|
||||
import org.lwjgl.opengl.GL;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.system.MemoryUtil.NULL;
|
||||
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Window {
|
||||
private final String title;
|
||||
private long windowHandle;
|
||||
|
||||
@Getter
|
||||
private int width;
|
||||
|
||||
@Getter
|
||||
private int height;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean resized;
|
||||
|
||||
public void init() {
|
||||
// Setup an error callback. The default implementation
|
||||
// will print the error message in System.err.
|
||||
GLFWErrorCallback.createPrint(System.err).set();
|
||||
|
||||
// Initialize GLFW. Most GLFW functions will not work before doing this.
|
||||
if (!glfwInit()) {
|
||||
throw new AppException("Unable to initialize GLFW");
|
||||
}
|
||||
|
||||
glfwDefaultWindowHints(); // optional, the current window hints are already the default
|
||||
glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // the window will stay hidden after creation
|
||||
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // the window will be resizable
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
|
||||
// Create the window
|
||||
windowHandle = glfwCreateWindow(width, height, title, NULL, NULL);
|
||||
if (windowHandle == NULL) {
|
||||
throw new AppException("Failed to create the GLFW window");
|
||||
}
|
||||
|
||||
// Setup resize callback
|
||||
glfwSetFramebufferSizeCallback(windowHandle, (window, width, height) -> {
|
||||
Window.this.width = width;
|
||||
Window.this.height = height;
|
||||
Window.this.resized = true;
|
||||
});
|
||||
|
||||
// Setup a key callback. It will be called every time a key is pressed, repeated or released.
|
||||
glfwSetKeyCallback(windowHandle, (window, key, scancode, action, mods) -> {
|
||||
if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) {
|
||||
glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
|
||||
}
|
||||
});
|
||||
|
||||
// Get the resolution of the primary monitor
|
||||
GLFWVidMode videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||
|
||||
// Center our window
|
||||
glfwSetWindowPos(windowHandle, (videoMode.width() - width) / 2, (videoMode.height() - height) / 2);
|
||||
|
||||
// Make the OpenGL context current
|
||||
glfwMakeContextCurrent(windowHandle);
|
||||
|
||||
// Enable V-Sync
|
||||
// glfwSwapInterval(1);
|
||||
|
||||
// Make the window visible
|
||||
glfwShowWindow(windowHandle);
|
||||
|
||||
GL.createCapabilities();
|
||||
|
||||
// Support for transparencies
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Set the clear color
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
glfwSwapBuffers(windowHandle);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
public boolean isKeyPressed(int keyCode) {
|
||||
return glfwGetKey(windowHandle, keyCode) == GLFW_PRESS;
|
||||
}
|
||||
|
||||
public void clear(float r, float g, float b, float alpha) {
|
||||
glClearColor(r, g, b, alpha);
|
||||
}
|
||||
|
||||
public boolean shouldClose() {
|
||||
return glfwWindowShouldClose(windowHandle);
|
||||
}
|
||||
|
||||
public Vector2f getSize() {
|
||||
return new Vector2f(width, height);
|
||||
}
|
||||
|
||||
public static Window create(String title, int width, int height) {
|
||||
return new Window(title, -1, width, height, false);
|
||||
}
|
||||
}
|
||||
|
||||
20
engine/src/main/java/com/bartlomiejpluta/base/core/ui/WindowManager.java
Executable file → Normal file
20
engine/src/main/java/com/bartlomiejpluta/base/core/ui/WindowManager.java
Executable file → Normal file
@@ -1,10 +1,10 @@
|
||||
package com.bartlomiejpluta.base.core.ui;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WindowManager {
|
||||
public Window createWindow(String title, int width, int height) {
|
||||
return Window.create(title, width, height);
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.ui;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WindowManager {
|
||||
public Window createWindow(String title, int width, int height) {
|
||||
return Window.create(title, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
64
engine/src/main/java/com/bartlomiejpluta/base/core/util/math/MathUtil.java
Executable file → Normal file
64
engine/src/main/java/com/bartlomiejpluta/base/core/util/math/MathUtil.java
Executable file → Normal file
@@ -1,32 +1,32 @@
|
||||
package com.bartlomiejpluta.base.core.util.math;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
public class MathUtil {
|
||||
public static int gcdEuclidean(int a, int b) {
|
||||
int x = a;
|
||||
int y = b;
|
||||
int z;
|
||||
|
||||
while(y != 0) {
|
||||
z = x % y;
|
||||
x = y;
|
||||
y = z;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
public static int clamp(int value, int min, int max) {
|
||||
return min(max, max(value, min));
|
||||
}
|
||||
|
||||
public static float clamp(float value, float min, float max) {
|
||||
return min(max, max(value, min));
|
||||
}
|
||||
|
||||
public static double clamp(double value, double min, double max) {
|
||||
return min(max, max(value, min));
|
||||
}
|
||||
}
|
||||
package com.bartlomiejpluta.base.core.util.math;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
public class MathUtil {
|
||||
public static int gcdEuclidean(int a, int b) {
|
||||
int x = a;
|
||||
int y = b;
|
||||
int z;
|
||||
|
||||
while(y != 0) {
|
||||
z = x % y;
|
||||
x = y;
|
||||
y = z;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
public static int clamp(int value, int min, int max) {
|
||||
return min(max, max(value, min));
|
||||
}
|
||||
|
||||
public static float clamp(float value, float min, float max) {
|
||||
return min(max, max(value, min));
|
||||
}
|
||||
|
||||
public static double clamp(double value, double min, double max) {
|
||||
return min(max, max(value, min));
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user