├── .gitignore
├── LICENSE
├── README.md
├── build.gradle.kts
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── src
├── main
├── kotlin
│ └── me
│ │ └── hydos
│ │ └── rosella
│ │ ├── audio
│ │ └── SoundManager.kt
│ │ ├── render
│ │ ├── Vk.kt
│ │ ├── material
│ │ │ └── Material.kt
│ │ ├── model
│ │ │ ├── AssimpHelper.kt
│ │ │ ├── GuiRenderObject.kt
│ │ │ └── ModelLoader.kt
│ │ ├── resource
│ │ │ ├── CascadingResourceLoader.kt
│ │ │ ├── ClassLoaderResourceLoader.kt
│ │ │ ├── Global.kt
│ │ │ ├── Identifier.kt
│ │ │ ├── Resource.kt
│ │ │ └── ResourceLoader.kt
│ │ ├── shader
│ │ │ ├── RawShaderProgram.kt
│ │ │ ├── Shader.kt
│ │ │ ├── ShaderManager.kt
│ │ │ ├── ShaderProgram.kt
│ │ │ ├── pushconstant
│ │ │ │ └── ModelPushConstant.kt
│ │ │ └── ubo
│ │ │ │ └── Ubo.kt
│ │ ├── swapchain
│ │ │ ├── DepthBuffer.kt
│ │ │ └── Frame.kt
│ │ ├── texture
│ │ │ ├── StbiImage.kt
│ │ │ ├── Texture.kt
│ │ │ ├── TextureImage.kt
│ │ │ └── UploadableImage.kt
│ │ └── util
│ │ │ ├── SprirVUtils.kt
│ │ │ └── VkUtils.kt
│ │ └── scene
│ │ ├── Scene.kt
│ │ └── SceneModel.kt
└── resources
│ └── rosella
│ ├── editor
│ └── gui
│ │ ├── files
│ │ ├── class.png
│ │ ├── file.png
│ │ └── interface.png
│ │ └── folder.png
│ ├── fonts
│ └── DIN Bold.otf
│ └── shaders
│ ├── base.f.glsl
│ ├── base.v.glsl
│ ├── fonts.f.glsl
│ ├── fonts.v.glsl
│ ├── gui.colour.f.glsl
│ ├── gui.f.glsl
│ └── gui.v.glsl
└── test
└── java
└── me
└── hydos
└── rosella
└── example
└── PortalJava.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | run/
3 | out/
4 | target/
5 | .gradle/
6 | build/
7 | *.log
8 | *.iml
9 | *.class
10 | *.kotlin_module
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rosella
2 | an work in progress, vulkan, modern jvm render engine
3 |
4 | ## Goals
5 | 1. give people an opportunity to learn vulkan.
6 | 2. be efficient
7 | 3. be "just enough" abstraction
8 |
9 | ## Progress
10 | Currently im doing:
11 | #### Fonts :tm:
12 |
13 | ## Long Term goals
14 | 1. use "modern rendering techniques" such as gpu driven rendering (https://vkguide.dev/docs/gpudriven)
15 | 2. easy to use both in java and kotlin
16 | 3. document everything
17 |
18 | P.S: im also working on some portal 2 related stuff with the engine
19 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.gradle.internal.os.OperatingSystem
2 |
3 | plugins {
4 | java
5 | kotlin("jvm") version "1.5.10"
6 | id("com.github.johnrengelman.shadow") version "7.0.0"
7 | }
8 |
9 | group = "me.hydos"
10 | version = "1.0-SNAPSHOT"
11 |
12 | val lwjglVersion = "3.3.0-SNAPSHOT"
13 | val lwjglNatives = when (org.gradle.internal.os.OperatingSystem.current()) {
14 | org.gradle.internal.os.OperatingSystem.LINUX -> System.getProperty("os.arch").let {
15 | if (it.startsWith("arm") || it.startsWith("aarch64")) {
16 | val arch = if (it.contains("64") || it.startsWith("armv8")) {
17 | "arm64"
18 | } else {
19 | "arm32"
20 | }
21 |
22 | "natives-linux-$arch"
23 | } else {
24 | "natives-linux"
25 | }
26 | }
27 | org.gradle.internal.os.OperatingSystem.MAC_OS -> if (System.getProperty("os.arch")
28 | .startsWith("aarch64")
29 | ) "natives-macos-arm64" else "natives-macos"
30 | org.gradle.internal.os.OperatingSystem.WINDOWS -> "natives-windows"
31 | else -> error("Unrecognized or unsupported Operating system. Please set \"lwjglNatives\" manually")
32 | }
33 |
34 | repositories {
35 | mavenCentral()
36 |
37 | maven {
38 | name = "Sonatype Snapshots"
39 | url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
40 | }
41 | }
42 |
43 | dependencies {
44 | api(platform("org.lwjgl:lwjgl-bom:$lwjglVersion"))
45 |
46 | api("org.lwjgl", "lwjgl")
47 | api("org.lwjgl", "lwjgl-assimp")
48 | api("org.lwjgl", "lwjgl-glfw")
49 | api("org.lwjgl", "lwjgl-openal")
50 | api("org.lwjgl", "lwjgl-shaderc")
51 | api("org.lwjgl", "lwjgl-stb")
52 | api("org.lwjgl", "lwjgl-vma")
53 | api("org.lwjgl", "lwjgl-vulkan")
54 |
55 | api("org.joml", "joml", "1.10.1")
56 | api("it.unimi.dsi", "fastutil", "8.5.4")
57 | api("com.google.code.gson", "gson", "2.8.7")
58 | api("org.apache.logging.log4j", "log4j-core", "2.14.1")
59 |
60 | runtimeOnly("org.lwjgl", "lwjgl", classifier = lwjglNatives)
61 | runtimeOnly("org.lwjgl", "lwjgl-assimp", classifier = lwjglNatives)
62 | runtimeOnly("org.lwjgl", "lwjgl-glfw", classifier = lwjglNatives)
63 | runtimeOnly("org.lwjgl", "lwjgl-openal", classifier = lwjglNatives)
64 | runtimeOnly("org.lwjgl", "lwjgl-shaderc", classifier = lwjglNatives)
65 | runtimeOnly("org.lwjgl", "lwjgl-stb", classifier = lwjglNatives)
66 | runtimeOnly("org.lwjgl", "lwjgl-vma", classifier = lwjglNatives)
67 |
68 | if (lwjglNatives == "natives-macos" || lwjglNatives == "natives-macos-arm64") {
69 | runtimeOnly("org.lwjgl", "lwjgl-vulkan", classifier = lwjglNatives)
70 | }
71 |
72 | testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
73 | }
74 |
75 | tasks.test {
76 | useJUnitPlatform {
77 | }
78 | }
79 |
80 | tasks.register("fastCITest") {
81 | useJUnitPlatform {
82 | excludeTags("exclude_frequent_ci")
83 | }
84 | }
85 |
86 | tasks.register("slowCITest") {
87 | useJUnitPlatform {
88 | } // In the future we can add tags to exclude tests that require certain vulkan features which arent available on github
89 | }
90 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hYdos/Rosella/50a0ee2c5bdfe8255ceb1120f9de4ea7679da9a4/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "Rosella"
2 |
3 | pluginManagement {
4 | repositories {
5 | gradlePluginPortal()
6 | jcenter()
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/audio/SoundManager.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.audio
2 |
3 | import me.hydos.rosella.render.resource.Resource
4 | import org.apache.logging.log4j.LogManager
5 | import org.lwjgl.BufferUtils
6 | import org.lwjgl.openal.AL
7 | import org.lwjgl.openal.AL10.*
8 | import org.lwjgl.openal.ALC
9 | import org.lwjgl.openal.ALC10.alcMakeContextCurrent
10 | import org.lwjgl.openal.ALC11.alcCreateContext
11 | import org.lwjgl.openal.ALC11.alcOpenDevice
12 | import org.lwjgl.openal.EXTThreadLocalContext.alcSetThreadContext
13 | import org.lwjgl.stb.STBVorbis.*
14 | import org.lwjgl.stb.STBVorbisInfo
15 | import org.lwjgl.system.MemoryUtil
16 | import org.lwjgl.system.MemoryUtil.NULL
17 | import java.nio.IntBuffer
18 | import java.nio.ShortBuffer
19 |
20 | object SoundManager {
21 |
22 | @JvmStatic
23 | fun initialize() {
24 | try {
25 | val device = alcOpenDevice(BufferUtils.createByteBuffer(1))
26 | check(device != NULL) { "Failed to open an OpenAL device." }
27 |
28 | val deviceCaps = ALC.createCapabilities(device)
29 | check(deviceCaps.OpenALC10)
30 |
31 | val context = alcCreateContext(device, null as IntBuffer?)
32 | val useTLC = deviceCaps.ALC_EXT_thread_local_context && alcSetThreadContext(context)
33 |
34 | if (!useTLC) {
35 | check(alcMakeContextCurrent(context))
36 | }
37 |
38 | AL.createCapabilities(deviceCaps, MemoryUtil::memCallocPointer)
39 | } catch (e: Exception) {
40 | LogManager.getFormatterLogger("Rosella").error("Unable to initialize sound manager: " + e.message)
41 | }
42 | }
43 |
44 | @JvmStatic
45 | fun playback(file: Resource) {
46 | val buffer: Int = alGenBuffers()
47 | val source: Int = alGenSources()
48 | STBVorbisInfo.malloc().use { info ->
49 | val pcm = readVorbis(file, info)
50 |
51 | alBufferData(
52 | buffer,
53 | if (info.channels() == 1) AL_FORMAT_MONO16 else AL_FORMAT_STEREO16,
54 | pcm,
55 | info.sample_rate()
56 | )
57 | }
58 |
59 | //set up source input
60 | alSourcei(source, AL_BUFFER, buffer)
61 |
62 | //play source
63 | alSourcePlay(source)
64 |
65 | Thread {
66 | while (true) {
67 | try {
68 | Thread.sleep(1000)
69 | } catch (ignored: InterruptedException) {
70 | break
71 | }
72 |
73 | if (alGetSourcei(source, AL_SOURCE_STATE) == AL_STOPPED) {
74 | break
75 | }
76 | }
77 |
78 | alSourceStop(source)
79 | alDeleteSources(source)
80 | alDeleteBuffers(buffer)
81 | }.apply {
82 | isDaemon = true
83 | start()
84 | }
85 | }
86 |
87 | private fun readVorbis(resource: Resource, info: STBVorbisInfo): ShortBuffer {
88 | val vorbis = resource.readAllBytes(true)
89 | val error = BufferUtils.createIntBuffer(1)
90 | val decoder = stb_vorbis_open_memory(vorbis, error, null)
91 |
92 | if (decoder == NULL) {
93 | throw RuntimeException("Failed to open Ogg Vorbis file. Error: " + error[0])
94 | }
95 |
96 | stb_vorbis_get_info(decoder, info)
97 |
98 | val channels = info.channels()
99 | val pcm = BufferUtils.createShortBuffer(stb_vorbis_stream_length_in_samples(decoder) * channels)
100 |
101 | stb_vorbis_get_samples_short_interleaved(decoder, channels, pcm)
102 | stb_vorbis_close(decoder)
103 |
104 | return pcm
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/Vk.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is for accessing vulkan indirectly. it manages structs so engine code can look better.
3 | */
4 | @file:JvmName("VkKt")
5 |
6 | package me.hydos.rosella.render
7 |
8 | import me.hydos.rosella.device.QueueFamilyIndices
9 | import me.hydos.rosella.device.VulkanDevice
10 | import me.hydos.rosella.fbo.RenderPass
11 | import me.hydos.rosella.memory.Memory
12 | import me.hydos.rosella.render.renderer.Renderer
13 | import me.hydos.rosella.render.swapchain.DepthBuffer
14 | import me.hydos.rosella.render.swapchain.Swapchain
15 | import me.hydos.rosella.render.texture.ImageRegion
16 | import me.hydos.rosella.render.texture.Texture
17 | import me.hydos.rosella.render.texture.TextureImage
18 | import me.hydos.rosella.render.texture.UploadableImage
19 | import me.hydos.rosella.render.util.ok
20 | import org.lwjgl.PointerBuffer
21 | import org.lwjgl.system.MemoryStack
22 | import org.lwjgl.vulkan.*
23 | import org.lwjgl.vulkan.VK10.*
24 | import java.nio.LongBuffer
25 |
26 | fun allocateCmdBuffers(
27 | stack: MemoryStack,
28 | device: VulkanDevice,
29 | commandPool: Long,
30 | commandBuffersCount: Int,
31 | level: Int = VK_COMMAND_BUFFER_LEVEL_PRIMARY
32 | ): PointerBuffer {
33 | val allocInfo = VkCommandBufferAllocateInfo.callocStack(stack)
34 | .sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO)
35 | .commandPool(commandPool)
36 | .level(level)
37 | .commandBufferCount(commandBuffersCount)
38 | val pCommandBuffers = stack.callocPointer(commandBuffersCount)
39 | vkAllocateCommandBuffers(device.rawDevice, allocInfo, pCommandBuffers).ok()
40 | return pCommandBuffers
41 | }
42 |
43 | fun createBeginInfo(stack: MemoryStack): VkCommandBufferBeginInfo {
44 | return VkCommandBufferBeginInfo.callocStack(stack)
45 | .sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO)
46 | }
47 |
48 | fun createRenderPassInfo(stack: MemoryStack, renderPass: RenderPass): VkRenderPassBeginInfo {
49 | return VkRenderPassBeginInfo.callocStack(stack)
50 | .sType(VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO)
51 | .renderPass(renderPass.renderPass)
52 | }
53 |
54 | fun createRenderArea(stack: MemoryStack, x: Int = 0, y: Int = 0, swapchain: Swapchain): VkRect2D {
55 | return VkRect2D.callocStack(stack)
56 | .offset(VkOffset2D.callocStack(stack).set(x, y))
57 | .extent(swapchain.swapChainExtent)
58 | }
59 |
60 | fun createImageView(image: Long, format: Int, aspectFlags: Int, device: VulkanDevice): Long {
61 | MemoryStack.stackPush().use { stack ->
62 | val viewInfo = VkImageViewCreateInfo.callocStack(stack)
63 | .sType(VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO)
64 | .image(image)
65 | .viewType(VK_IMAGE_VIEW_TYPE_2D)
66 | .format(format)
67 | viewInfo.subresourceRange().aspectMask(aspectFlags)
68 | .baseMipLevel(0)
69 | .levelCount(1)
70 | .baseArrayLayer(0)
71 | .layerCount(1)
72 |
73 | val pImageView = stack.mallocLong(1)
74 | vkCreateImageView(device.rawDevice, viewInfo, null, pImageView).ok("Failed to create texture image view")
75 | return pImageView[0]
76 | }
77 | }
78 |
79 | fun createImgViews(swapchain: Swapchain, device: VulkanDevice) {
80 | swapchain.swapChainImageViews = ArrayList(swapchain.swapChainImages.size)
81 | for (swapChainImage in swapchain.swapChainImages) {
82 | swapchain.swapChainImageViews.add(
83 | createImageView(
84 | swapChainImage,
85 | swapchain.swapChainImageFormat,
86 | VK_IMAGE_ASPECT_COLOR_BIT,
87 | device
88 | )
89 | )
90 | }
91 | }
92 |
93 | fun createCmdPool(device: VulkanDevice, renderer: Renderer, surface: Long) {
94 | MemoryStack.stackPush().use { stack ->
95 | val queueFamilyIndices = findQueueFamilies(device, surface)
96 | val poolInfo = VkCommandPoolCreateInfo.callocStack(stack)
97 | .sType(VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO)
98 | .queueFamilyIndex(queueFamilyIndices.graphicsFamily)
99 | val pCommandPool = stack.mallocLong(1)
100 | vkCreateCommandPool(device.rawDevice, poolInfo, null, pCommandPool).ok()
101 | renderer.commandPool = pCommandPool[0]
102 | }
103 | }
104 |
105 | fun createClearValues(
106 | stack: MemoryStack,
107 | r: Float = 0f,
108 | g: Float = 0f,
109 | b: Float = 0f,
110 | depth: Float = 1.0f,
111 | stencil: Int = 0
112 | ): VkClearValue.Buffer {
113 | val clearValues = VkClearValue.callocStack(2, stack)
114 | clearValues[0].color().float32(stack.floats(r, g, b, 1.0f))
115 | clearValues[1].depthStencil().set(depth, stencil)
116 | return clearValues
117 | }
118 |
119 | fun beginSingleTimeCommands(renderer: Renderer, device: VulkanDevice): VkCommandBuffer {
120 | MemoryStack.stackPush().use { stack ->
121 | val pCommandBuffer = stack.mallocPointer(1)
122 | return renderer.beginCmdBuffer(stack, pCommandBuffer, device)
123 | }
124 | }
125 |
126 | fun endSingleTimeCommands(commandBuffer: VkCommandBuffer, device: VulkanDevice, renderer: Renderer) {
127 | MemoryStack.stackPush().use { stack ->
128 | vkEndCommandBuffer(commandBuffer)
129 | val submitInfo = VkSubmitInfo.callocStack(1, stack)
130 | .sType(VK_STRUCTURE_TYPE_SUBMIT_INFO)
131 | .pCommandBuffers(stack.pointers(commandBuffer))
132 | renderer.queues.graphicsQueue.vkQueueSubmit(submitInfo, VK_NULL_HANDLE)
133 | renderer.queues.graphicsQueue.vkQueueWaitIdle()
134 | vkFreeCommandBuffers(device.rawDevice, renderer.commandPool, commandBuffer)
135 | }
136 | }
137 |
138 | fun findQueueFamilies(device: VkDevice, surface: Long): QueueFamilyIndices {
139 | return findQueueFamilies(device.physicalDevice, surface)
140 | }
141 |
142 | fun findQueueFamilies(device: VulkanDevice, surface: Long): QueueFamilyIndices {
143 | return findQueueFamilies(device.physicalDevice, surface)
144 | }
145 |
146 | fun findQueueFamilies(device: VkPhysicalDevice, surface: Long): QueueFamilyIndices {
147 | MemoryStack.stackPush().use { stack ->
148 | val indices = QueueFamilyIndices()
149 |
150 | val queueFamilyCount = stack.ints(0)
151 | vkGetPhysicalDeviceQueueFamilyProperties(device, queueFamilyCount, null)
152 |
153 | val queueFamilies = VkQueueFamilyProperties.mallocStack(queueFamilyCount[0], stack)
154 | vkGetPhysicalDeviceQueueFamilyProperties(device, queueFamilyCount, queueFamilies)
155 |
156 | val presentSupport = stack.ints(VK_FALSE)
157 |
158 | var i = 0
159 | while (i < queueFamilies.capacity() || !indices.isComplete) {
160 | if (queueFamilies[i].queueFlags() and VK_QUEUE_GRAPHICS_BIT != 0) {
161 | indices.graphicsFamily = i
162 | }
163 | KHRSurface.vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, presentSupport)
164 | if (presentSupport.get(0) == VK_TRUE) {
165 | indices.presentFamily = i
166 | }
167 | i++
168 | }
169 | return indices
170 | }
171 | }
172 |
173 | fun findMemoryType(typeFilter: Int, properties: Int, device: VulkanDevice): Int {
174 | val memProperties = VkPhysicalDeviceMemoryProperties.mallocStack()
175 | vkGetPhysicalDeviceMemoryProperties(device.physicalDevice, memProperties)
176 | for (i in 0 until memProperties.memoryTypeCount()) {
177 | if (typeFilter and (1 shl i) != 0 && memProperties.memoryTypes(i)
178 | .propertyFlags() and properties == properties
179 | ) {
180 | return i
181 | }
182 | }
183 | error("Failed to find suitable memory type")
184 | }
185 |
186 | fun createTextureImageView(device: VulkanDevice, imgFormat: Int, textureImage: Long): Long {
187 | return createImageView(
188 | textureImage,
189 | imgFormat,
190 | VK_IMAGE_ASPECT_COLOR_BIT,
191 | device
192 | )
193 | }
194 |
195 | fun createImage(
196 | width: Int, height: Int, format: Int, tiling: Int, usage: Int, memProperties: Int,
197 | pTextureImage: LongBuffer, pTextureImageMemory: LongBuffer, device: VulkanDevice
198 | ) {
199 | MemoryStack.stackPush().use { stack ->
200 | val imageInfo = VkImageCreateInfo.callocStack(stack)
201 | .sType(VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO)
202 | .imageType(VK_IMAGE_TYPE_2D)
203 | imageInfo.extent()
204 | .width(width)
205 | .height(height)
206 | .depth(1)
207 | imageInfo
208 | .mipLevels(1)
209 | .arrayLayers(1)
210 | .format(format)
211 | .tiling(tiling)
212 | .initialLayout(VK_IMAGE_LAYOUT_UNDEFINED)
213 | .usage(usage)
214 | .samples(VK_SAMPLE_COUNT_1_BIT)
215 | .sharingMode(VK_SHARING_MODE_EXCLUSIVE)
216 | vkCreateImage(device.rawDevice, imageInfo, null, pTextureImage).ok("Failed to allocate image memory")
217 | val memRequirements = VkMemoryRequirements.mallocStack(stack)
218 | vkGetImageMemoryRequirements(device.rawDevice, pTextureImage[0], memRequirements)
219 | val allocInfo = VkMemoryAllocateInfo.callocStack(stack)
220 | .sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO)
221 | .allocationSize(memRequirements.size())
222 | .memoryTypeIndex(findMemoryType(memRequirements.memoryTypeBits(), memProperties, device))
223 | vkAllocateMemory(device.rawDevice, allocInfo, null, pTextureImageMemory).ok("Failed to allocate image memory")
224 | vkBindImageMemory(device.rawDevice, pTextureImage[0], pTextureImageMemory[0], 0)
225 | }
226 | }
227 |
228 | fun transitionImageLayout(
229 | renderer: Renderer,
230 | device: VulkanDevice,
231 | depthBuffer: DepthBuffer,
232 | image: Long,
233 | format: Int,
234 | oldLayout: Int,
235 | newLayout: Int
236 | ) {
237 | MemoryStack.stackPush().use { stack ->
238 | val barrier = VkImageMemoryBarrier.callocStack(1, stack)
239 | .sType(VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER)
240 | .oldLayout(oldLayout)
241 | .newLayout(newLayout)
242 | .srcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
243 | .dstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
244 | .image(image)
245 | barrier.subresourceRange().baseMipLevel(0)
246 | barrier.subresourceRange().levelCount(1)
247 | barrier.subresourceRange().baseArrayLayer(0)
248 | barrier.subresourceRange().layerCount(1)
249 |
250 |
251 | if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
252 | barrier.subresourceRange().aspectMask(VK_IMAGE_ASPECT_DEPTH_BIT)
253 | if (depthBuffer.hasStencilComponent(format)) {
254 | barrier.subresourceRange().aspectMask(
255 | barrier.subresourceRange().aspectMask() or VK_IMAGE_ASPECT_STENCIL_BIT
256 | )
257 | }
258 | } else {
259 | barrier.subresourceRange().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT)
260 | }
261 |
262 | val sourceStage: Int
263 | val destinationStage: Int
264 | if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
265 |
266 | barrier.srcAccessMask(0)
267 | .dstAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT)
268 |
269 | sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
270 | destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT
271 |
272 | } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
273 |
274 | barrier.srcAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT)
275 | .dstAccessMask(VK_ACCESS_SHADER_READ_BIT)
276 |
277 | sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT
278 | destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
279 |
280 | } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
281 |
282 | barrier.srcAccessMask(0)
283 | .dstAccessMask(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT or VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT)
284 |
285 | sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
286 | destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
287 |
288 | } else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
289 |
290 | barrier.srcAccessMask(VK_ACCESS_SHADER_READ_BIT)
291 | .dstAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT)
292 |
293 | sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
294 | destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT
295 |
296 | } else {
297 | throw IllegalArgumentException("Unsupported layout transition")
298 | }
299 | val commandBuffer: VkCommandBuffer = beginSingleTimeCommands(renderer, device)
300 | vkCmdPipelineBarrier(
301 | commandBuffer,
302 | sourceStage, destinationStage,
303 | 0,
304 | null,
305 | null,
306 | barrier
307 | )
308 | endSingleTimeCommands(commandBuffer, device, renderer)
309 | }
310 | }
311 |
312 | fun createTextureImage(
313 | renderer: Renderer,
314 | device: VulkanDevice,
315 | width: Int,
316 | height: Int,
317 | imgFormat: Int,
318 | textureImage: TextureImage
319 | ) {
320 | MemoryStack.stackPush().use { stack ->
321 |
322 | val pTextureImage = stack.mallocLong(1)
323 | val pTextureImageMemory = stack.mallocLong(1)
324 | createImage(
325 | width, height,
326 | imgFormat, VK_IMAGE_TILING_OPTIMAL,
327 | VK_IMAGE_USAGE_TRANSFER_DST_BIT or VK_IMAGE_USAGE_SAMPLED_BIT,
328 | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
329 | pTextureImage,
330 | pTextureImageMemory,
331 | device
332 | )
333 | textureImage.textureImage = pTextureImage[0]
334 | textureImage.textureImageMemory = pTextureImageMemory[0]
335 |
336 |
337 | transitionImageLayout(
338 | renderer,
339 | device,
340 | renderer.depthBuffer,
341 | textureImage.textureImage,
342 | imgFormat,
343 | VK_IMAGE_LAYOUT_UNDEFINED,
344 | VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
345 | )
346 | }
347 | }
348 |
349 | fun copyToTexture(
350 | renderer: Renderer,
351 | device: VulkanDevice,
352 | memory: Memory,
353 | image: UploadableImage,
354 | srcRegion: ImageRegion,
355 | dstRegion: ImageRegion,
356 | texture: Texture
357 | ) {
358 | MemoryStack.stackPush().use { stack ->
359 | val pBuffer = stack.mallocLong(1)
360 | val stagingBuf = memory.createStagingBuf(
361 | image.getSize(),
362 | pBuffer,
363 | stack
364 | ) { data ->
365 | val pixels = image.getPixels()!!
366 | val newData = data.getByteBuffer(0, pixels.limit())
367 | newData.put(0, pixels, 0, pixels.limit())
368 | }
369 |
370 | copyBufferToImage(
371 | renderer,
372 | device,
373 | stagingBuf.buffer(),
374 | texture.textureImage.textureImage,
375 | image.getWidth(),
376 | image.getHeight(),
377 | srcRegion.xOffset,
378 | srcRegion.yOffset,
379 | image.getFormat().pixelSize,
380 | dstRegion.width,
381 | dstRegion.height,
382 | dstRegion.xOffset,
383 | dstRegion.yOffset
384 | )
385 |
386 | stagingBuf.free(device, memory)
387 | }
388 | }
389 |
390 | fun copyBufferToImage(
391 | renderer: Renderer,
392 | device: VulkanDevice,
393 | buffer: Long,
394 | image: Long,
395 | srcImageWidth: Int,
396 | srcImageHeight: Int,
397 | srcXOffset: Int,
398 | srcYOffset: Int,
399 | srcPixelSize: Int,
400 | dstRegionWidth: Int,
401 | dstRegionHeight: Int,
402 | dstXOffset: Int,
403 | dstYOffset: Int
404 | ) {
405 | MemoryStack.stackPush().use { stack ->
406 | val region = VkBufferImageCopy.callocStack(1, stack)
407 | .bufferOffset((((srcYOffset * srcImageWidth) + srcXOffset) * srcPixelSize).toLong())
408 | .bufferRowLength(srcImageWidth)
409 | .bufferImageHeight(srcImageHeight)
410 | region.imageOffset().set(dstXOffset, dstYOffset, 0)
411 | region.imageSubresource()
412 | .aspectMask(VK_IMAGE_ASPECT_COLOR_BIT)
413 | .mipLevel(0)
414 | .baseArrayLayer(0)
415 | .layerCount(1)
416 | region.imageExtent().set(dstRegionWidth, dstRegionHeight, 1)
417 |
418 | val commandBuffer: VkCommandBuffer = beginSingleTimeCommands(renderer, device)
419 | vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region)
420 | endSingleTimeCommands(commandBuffer, device, renderer)
421 | }
422 |
423 |
424 | }
425 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/material/Material.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.material
2 |
3 | import me.hydos.rosella.Rosella
4 | import me.hydos.rosella.render.Topology
5 | import me.hydos.rosella.render.material.state.StateInfo
6 | import me.hydos.rosella.render.resource.Resource
7 | import me.hydos.rosella.render.shader.ShaderProgram
8 | import me.hydos.rosella.render.texture.*
9 | import me.hydos.rosella.render.vertex.VertexFormat
10 |
11 | /**
12 | * A Material is like texture information, normal information, and all of those things which give an object character wrapped into one class.
13 | * similar to how unity material's works
14 | * guaranteed to change in the future
15 | */
16 | open class Material(
17 | val resource: Resource,
18 | var shader: ShaderProgram,
19 | private val imgFormat: Int,
20 | val topology: Topology,
21 | val vertexFormat: VertexFormat,
22 | private val samplerCreateInfo: SamplerCreateInfo,
23 | val stateInfo: StateInfo
24 | ) {
25 | lateinit var pipeline: PipelineInfo
26 |
27 | lateinit var textures: Array
28 |
29 | open fun loadTextures(rosella: Rosella) { //FIXME this is also temporary
30 | if (resource != Resource.Empty) {
31 | val textureManager = rosella.common.textureManager
32 | val textureId = textureManager.generateTextureId() // FIXME this texture can't be removed
33 | val image: UploadableImage = StbiImage(resource, ImageFormat.fromVkFormat(imgFormat))
34 | textureManager.createTexture(
35 | rosella.renderer,
36 | textureId,
37 | image.getWidth(),
38 | image.getHeight(),
39 | imgFormat
40 | )
41 | textureManager.setTextureSampler(
42 | textureId,
43 | 0,
44 | samplerCreateInfo
45 | ) // 0 is the default texture no, but it's still gross
46 | textureManager.drawToExistingTexture(rosella.renderer, rosella.common.memory, textureId, image)
47 | val texture = textureManager.getTexture(textureId)!!
48 | textures = arrayOf(texture) //FIXME THIS SUCKS
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/model/AssimpHelper.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.model
2 |
3 | import me.hydos.rosella.render.resource.Identifier
4 | import me.hydos.rosella.render.resource.Resource
5 | import org.lwjgl.assimp.AIFile
6 | import org.lwjgl.assimp.AIFileIO
7 | import org.lwjgl.assimp.AIScene
8 | import org.lwjgl.assimp.Assimp.*
9 | import org.lwjgl.system.MemoryUtil
10 |
11 | fun loadScene(resource: Resource, flags: Int): AIScene? {
12 | val identifier = resource.identifier
13 |
14 | val context = identifier.path.run { substring(0, lastIndexOf('/')) }
15 | val name = identifier.path.run { substring(lastIndexOf('/') + 1) }
16 |
17 | val io = AIFileIO.create().apply {
18 | OpenProc { _, nFileName, _ ->
19 | val fileName = MemoryUtil.memASCII(nFileName)
20 | val id = Identifier(identifier.namespace, context + fileName)
21 | val data = resource.loader.ensureResource(id).readAllBytes(true)
22 |
23 | AIFile.create().apply {
24 | ReadProc { _, pBuffer, size, count ->
25 | val max = (data.remaining().toLong() / size).coerceAtMost(count)
26 | MemoryUtil.memCopy(MemoryUtil.memAddress(data), pBuffer, max * size)
27 | data.position(data.position() + (max * size).toInt())
28 | max
29 | }
30 |
31 | SeekProc { _, offset, origin ->
32 | when (origin) {
33 | aiOrigin_CUR -> {
34 | data.position(data.position() + offset.toInt())
35 | }
36 | aiOrigin_SET -> {
37 | data.position(offset.toInt())
38 | }
39 | aiOrigin_END -> {
40 | data.position(data.limit() + offset.toInt())
41 | }
42 | }
43 |
44 | 0
45 | }
46 |
47 | TellProc { data.position().toLong() }
48 | FileSizeProc { data.limit().toLong() }
49 |
50 | FlushProc {
51 | error("Cannot flush")
52 | }
53 |
54 | WriteProc { _, _, _, _ ->
55 | error("Cannot write")
56 | }
57 | }.address()
58 | }
59 | CloseProc { _, pFile ->
60 | val file = AIFile.create(pFile)
61 |
62 | file.FlushProc().free()
63 | file.SeekProc().free()
64 | file.FileSizeProc().free()
65 | file.TellProc().free()
66 | file.WriteProc().free()
67 | file.ReadProc().free()
68 | }
69 | }
70 |
71 | val scene = aiImportFileEx("/$name", flags, io)
72 |
73 | io.OpenProc().free()
74 | io.CloseProc().free()
75 |
76 | return scene
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/model/GuiRenderObject.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.model
2 |
3 | import it.unimi.dsi.fastutil.ints.IntArrayList
4 | import me.hydos.rosella.Rosella
5 | import me.hydos.rosella.render.material.Material
6 | import me.hydos.rosella.render.resource.Resource
7 | import me.hydos.rosella.render.vertex.BufferVertexConsumer
8 | import me.hydos.rosella.scene.`object`.RenderObject
9 | import org.joml.Matrix4f
10 | import org.joml.Vector3f
11 |
12 | open class GuiRenderObject(
13 | material: Material,
14 | private var z: Float = -1f,
15 | private var colour: Vector3f = Vector3f(0f, 0f, 0f),
16 | viewMatrix: Matrix4f,
17 | projectionMatrix: Matrix4f
18 | ) : RenderObject(Resource.Empty, material, viewMatrix, projectionMatrix) {
19 |
20 | constructor(
21 | material: Material,
22 | z: Float,
23 | colour: Vector3f,
24 | scaleX: Float,
25 | scaleZ: Float,
26 | viewMatrix: Matrix4f,
27 | projectionMatrix: Matrix4f
28 | ) : this(material, z, colour, viewMatrix, projectionMatrix) {
29 | scale(scaleX, scaleZ)
30 | }
31 |
32 | constructor(
33 | material: Material,
34 | z: Float,
35 | colour: Vector3f,
36 | scaleX: Float,
37 | scaleZ: Float,
38 | translateX: Float,
39 | translateZ: Float,
40 | viewMatrix: Matrix4f,
41 | projectionMatrix: Matrix4f
42 | ) : this(material, z, colour, scaleX, scaleZ, viewMatrix, projectionMatrix) {
43 | translate(translateX, translateZ)
44 | }
45 |
46 | override fun loadModelInfo() {
47 | // FIXME FIXME FIXME FIXME this is bad, don't force cast here
48 | val vertexConsumer: BufferVertexConsumer = renderInfo.bufferProvider as BufferVertexConsumer
49 |
50 | vertexConsumer.clear()
51 | renderInfo.indices = IntArrayList()
52 |
53 | colour = Vector3f(0f, 0f, 0f)
54 |
55 | // TODO: is this conversion doing what it should be? should convert int representing unsigned byte to signed byte through wrapping
56 | vertexConsumer
57 | .pos(-0.5f, -0.5f, 0f)
58 | .color(colour.x().toInt().toByte(), colour.y().toInt().toByte(), colour.z().toInt().toByte())
59 | .uv(0f, 0f)
60 | .nextVertex()
61 |
62 | vertexConsumer
63 | .pos(0.5f, -0.5f, 0f)
64 | .color(colour.x().toInt().toByte(), colour.y().toInt().toByte(), colour.z().toInt().toByte())
65 | .uv(1f, 0f)
66 | .nextVertex()
67 |
68 | vertexConsumer
69 | .pos(0.5f, 0.5f, 0f)
70 | .color(colour.x().toInt().toByte(), colour.y().toInt().toByte(), colour.z().toInt().toByte())
71 | .uv(1f, 1f)
72 | .nextVertex()
73 |
74 | vertexConsumer
75 | .pos(-0.5f, 0.5f, 0f)
76 | .color(colour.x().toInt().toByte(), colour.y().toInt().toByte(), colour.z().toInt().toByte())
77 | .uv(0f, 1f)
78 | .nextVertex()
79 |
80 | renderInfo.indices.add(0)
81 | renderInfo.indices.add(1)
82 | renderInfo.indices.add(2)
83 | renderInfo.indices.add(2)
84 | renderInfo.indices.add(3)
85 | renderInfo.indices.add(0)
86 | }
87 |
88 | override fun onAddedToScene(rosella: Rosella) {
89 | super.onAddedToScene(rosella)
90 | modelMatrix.translate(0f, 0f, z)
91 | }
92 |
93 | private fun scale(x: Float, y: Float) {
94 | modelMatrix.scale(x, y, 1f)
95 | }
96 |
97 | private fun translate(x: Float, y: Float) {
98 | modelMatrix.translate(x, -y, 0f)
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/model/ModelLoader.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.model
2 |
3 | import me.hydos.rosella.render.resource.Resource
4 | import org.joml.Vector2f
5 | import org.joml.Vector2fc
6 | import org.joml.Vector3f
7 | import org.joml.Vector3fc
8 | import org.lwjgl.assimp.AIMesh
9 | import org.lwjgl.assimp.AINode
10 | import org.lwjgl.assimp.AIScene
11 | import org.lwjgl.assimp.AIVector3D
12 | import org.lwjgl.assimp.Assimp.aiGetErrorString
13 | import java.util.Objects.requireNonNull
14 | import java.util.logging.Logger
15 |
16 | object ModelLoader {
17 | @JvmStatic
18 | fun loadModel(resource: Resource, flags: Int): SimpleModel {
19 | loadScene(resource, flags).use { scene ->
20 | Logger.getLogger(ModelLoader::class.java.simpleName)
21 | println("Loading model " + resource.identifier)
22 |
23 | if (scene?.mRootNode() == null) {
24 | throw RuntimeException("Could not load model " + aiGetErrorString())
25 | }
26 |
27 | val model = SimpleModel()
28 | val startTime = System.nanoTime()
29 | processNode(scene.mRootNode()!!, scene, model)
30 | println("mdl loaded in " + (System.nanoTime() - startTime) / 1e6 + "ms")
31 | return model
32 | }
33 | }
34 |
35 | private fun processNode(node: AINode, scene: AIScene, model: SimpleModel) {
36 | if (node.mMeshes() != null) {
37 | processNodeMeshes(scene, node, model)
38 | }
39 | if (node.mChildren() != null) {
40 | val children = node.mChildren()
41 | for (i in 0 until node.mNumChildren()) {
42 | processNode(AINode.create(children!![i]), scene, model)
43 | }
44 | }
45 | }
46 |
47 | private fun processNodeMeshes(scene: AIScene, node: AINode, model: SimpleModel) {
48 | val pMeshes = scene.mMeshes()
49 | val meshIndices = node.mMeshes()
50 | for (i in 0 until meshIndices!!.capacity()) {
51 | val mesh = AIMesh.create(pMeshes!![meshIndices[i]])
52 | processMesh(scene, mesh, model)
53 | }
54 | }
55 |
56 | private fun processMesh(scene: AIScene, mesh: AIMesh, model: SimpleModel) {
57 | processPositions(mesh, model.positions)
58 | processTexCoords(mesh, model.texCoords)
59 | processIndices(mesh, model.indices)
60 | }
61 |
62 | private fun processPositions(mesh: AIMesh, positions: MutableList) {
63 | val vertices: AIVector3D.Buffer = requireNonNull(mesh.mVertices())
64 | for (i in 0 until vertices.capacity()) {
65 | val position = vertices[i]
66 | positions.add(Vector3f(position.x(), position.y(), position.z()))
67 | }
68 | }
69 |
70 | private fun processTexCoords(mesh: AIMesh, texCoords: MutableList) {
71 | val aiTexCoords: AIVector3D.Buffer = mesh.mTextureCoords(0)!!
72 | for (i in 0 until aiTexCoords.capacity()) {
73 | val coords = aiTexCoords[i]
74 | texCoords.add(Vector2f(coords.x(), coords.y()))
75 | }
76 | }
77 |
78 | private fun processIndices(mesh: AIMesh, indices: MutableList) {
79 | val aiFaces = mesh.mFaces()
80 | for (i in 0 until mesh.mNumFaces()) {
81 | val face = aiFaces[i]
82 | val pIndices = face.mIndices()
83 | for (j in 0 until face.mNumIndices()) {
84 | indices.add(pIndices[j])
85 | }
86 | }
87 | }
88 |
89 | class SimpleModel {
90 | val positions: MutableList = ArrayList()
91 | val texCoords: MutableList = ArrayList()
92 | val indices: MutableList = ArrayList()
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/resource/CascadingResourceLoader.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.resource
2 |
3 | class CascadingResourceLoader(private val loaders: Collection) : ResourceLoader {
4 |
5 | override fun loadResource(id: Identifier): Resource? {
6 | for (loader in loaders) {
7 | val resource = loader.loadResource(id)
8 |
9 | if (resource != null) {
10 | return object : Resource by resource {
11 | override val loader: ResourceLoader
12 | get() = this@CascadingResourceLoader
13 | }
14 | }
15 | }
16 |
17 | return null
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/resource/ClassLoaderResourceLoader.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.resource
2 |
3 | import java.io.InputStream
4 |
5 | class ClassLoaderResourceLoader(private val loader: ClassLoader) : ResourceLoader {
6 |
7 | override fun loadResource(id: Identifier): Resource? {
8 | val url = loader.getResource(id.file)
9 |
10 | return if (url == null) {
11 | null
12 | } else object : Resource {
13 | override val identifier: Identifier
14 | get() = id
15 |
16 | override val loader: ResourceLoader
17 | get() = this@ClassLoaderResourceLoader
18 |
19 | override fun openStream(): InputStream = url.openStream()
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/resource/Global.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.resource
2 |
3 | import me.hydos.rosella.Rosella
4 | import java.awt.image.BufferedImage
5 | import java.io.ByteArrayInputStream
6 | import java.io.ByteArrayOutputStream
7 | import java.io.InputStream
8 | import java.nio.ByteBuffer
9 | import javax.imageio.ImageIO
10 |
11 | /**
12 | * Don't use this once [Rosella] get its own ResourceLoader field
13 | */
14 | object Global : ResourceLoader by ClassLoaderResourceLoader(ClassLoader.getSystemClassLoader()) {
15 |
16 | fun fromBufferedImage(image: BufferedImage, id: Identifier): Resource {
17 | return object : Resource {
18 | override val identifier: Identifier
19 | get() = id
20 |
21 | override val loader: ResourceLoader
22 | get() = this@Global
23 |
24 | override fun openStream(): InputStream {
25 | val out = ByteArrayOutputStream()
26 | ImageIO.write(
27 | image,
28 | "png",
29 | out
30 | )
31 | return ByteArrayInputStream(out.toByteArray())
32 | }
33 | }
34 | }
35 |
36 | fun fromByteBuffer(bb: ByteBuffer, id: Identifier): Resource {
37 | return object : Resource {
38 | override val identifier: Identifier
39 | get() = id
40 |
41 | override val loader: ResourceLoader
42 | get() = this@Global
43 |
44 | override fun openStream(): InputStream {
45 | val byteArray = ByteArray(bb.remaining())
46 | bb.get(byteArray)
47 | return ByteArrayInputStream(byteArray)
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/resource/Identifier.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.resource
2 |
3 | data class Identifier(val namespace: String, val path: String) {
4 |
5 | val file: String = "$namespace/$path"
6 |
7 | override fun toString(): String {
8 | return "$namespace:$path"
9 | }
10 |
11 | companion object {
12 | @JvmStatic
13 | val EMPTY = Identifier("rosella", "empty")
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/resource/Resource.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.resource
2 |
3 | import java.io.InputStream
4 | import java.nio.ByteBuffer
5 |
6 | interface Resource {
7 |
8 | val identifier: Identifier
9 |
10 | val loader: ResourceLoader
11 |
12 | fun openStream(): InputStream
13 |
14 | fun readAllBytes(native: Boolean = false): ByteBuffer {
15 | val bytes = openStream().readBytes()
16 |
17 | if (native) {
18 | val buffer = ByteBuffer.allocateDirect(bytes.size)
19 | buffer.put(bytes)
20 | buffer.rewind()
21 | return buffer
22 | }
23 |
24 | return ByteBuffer.wrap(bytes)
25 | }
26 |
27 | object Empty : Resource {
28 | override val identifier: Identifier
29 | get() = TODO("Not yet implemented")
30 | override val loader: ResourceLoader
31 | get() = TODO("Not yet implemented")
32 |
33 | override fun openStream(): InputStream {
34 | TODO("Not yet implemented")
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/resource/ResourceLoader.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.resource
2 |
3 | interface ResourceLoader {
4 |
5 | fun loadResource(id: Identifier): Resource?
6 |
7 | fun ensureResource(id: Identifier): Resource {
8 | return loadResource(id) ?: error("Could not open $id")
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/shader/RawShaderProgram.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.shader
2 |
3 | import it.unimi.dsi.fastutil.Hash.VERY_FAST_LOAD_FACTOR
4 | import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
5 | import me.hydos.rosella.device.VulkanDevice
6 | import me.hydos.rosella.memory.Memory
7 | import me.hydos.rosella.render.descriptorsets.DescriptorSets
8 | import me.hydos.rosella.render.renderer.Renderer
9 | import me.hydos.rosella.render.resource.Resource
10 | import me.hydos.rosella.render.shader.ubo.Ubo
11 | import me.hydos.rosella.render.swapchain.Swapchain
12 | import me.hydos.rosella.render.texture.Texture
13 | import me.hydos.rosella.render.texture.TextureManager
14 | import me.hydos.rosella.render.util.ok
15 | import me.hydos.rosella.scene.`object`.impl.SimpleFramebufferObjectManager
16 | import org.lwjgl.system.MemoryStack
17 | import org.lwjgl.vulkan.*
18 | import org.lwjgl.vulkan.VK10.*
19 |
20 | open class RawShaderProgram(
21 | var vertexShader: Resource?,
22 | var fragmentShader: Resource?,
23 | val device: VulkanDevice,
24 | val memory: Memory,
25 | var maxObjCount: Int,
26 | private vararg var poolObjects: PoolObjectInfo
27 | ) {
28 | var descriptorPool: Long = 0
29 | var descriptorSetLayout: Long = 0
30 |
31 | private val preparableTextures = ReferenceOpenHashSet(3, VERY_FAST_LOAD_FACTOR)
32 |
33 | fun updateUbos(currentImage: Int, swapchain: Swapchain, objectManager: SimpleFramebufferObjectManager) {
34 | for (instances in objectManager.renderObjects.values) {
35 | for (instance in instances) {
36 | instance.ubo.update(
37 | currentImage,
38 | swapchain
39 | )
40 | }
41 | }
42 | }
43 |
44 | fun prepareTexturesForRender(
45 | renderer: Renderer,
46 | textureManager: TextureManager
47 | ) { // TODO: should we move this?
48 | preparableTextures.forEach {
49 | if (it != null) {
50 | textureManager.prepareTexture(renderer, it)
51 | }
52 | }
53 | preparableTextures.clear()
54 | }
55 |
56 | private fun createPool(swapchain: Swapchain) {
57 | if (descriptorPool != 0L) {
58 | vkDestroyDescriptorPool(device.rawDevice, descriptorPool, null)
59 | }
60 | MemoryStack.stackPush().use { stack ->
61 | val poolSizes = VkDescriptorPoolSize.callocStack(poolObjects.size, stack)
62 |
63 | poolObjects.forEachIndexed { i, poolObj ->
64 | poolSizes[i]
65 | .type(poolObj.getVkType())
66 | .descriptorCount(swapchain.swapChainImages.size * maxObjCount)
67 | }
68 |
69 | val poolInfo = VkDescriptorPoolCreateInfo.callocStack(stack)
70 | .sType(VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO)
71 | .pPoolSizes(poolSizes)
72 | .maxSets(swapchain.swapChainImages.size * maxObjCount)
73 | .flags(VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT)
74 |
75 | val pDescriptorPool = stack.mallocLong(1)
76 | vkCreateDescriptorPool(
77 | device.rawDevice,
78 | poolInfo,
79 | null,
80 | pDescriptorPool
81 | ).ok("Failed to create descriptor pool")
82 |
83 | descriptorPool = pDescriptorPool[0]
84 | }
85 | }
86 |
87 | fun createDescriptorSetLayout() {
88 | MemoryStack.stackPush().use {
89 | val bindings = VkDescriptorSetLayoutBinding.callocStack(poolObjects.size, it)
90 |
91 | poolObjects.forEachIndexed { i, poolObj ->
92 | bindings[i]
93 | .binding(if (poolObj.getBindingLocation() == -1) i else poolObj.getBindingLocation())
94 | .descriptorCount(1)
95 | .descriptorType(poolObj.getVkType())
96 | .pImmutableSamplers(null)
97 | .stageFlags(poolObj.getShaderStage())
98 | }
99 |
100 | val layoutInfo = VkDescriptorSetLayoutCreateInfo.callocStack(it)
101 | layoutInfo.sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO)
102 | layoutInfo.pBindings(bindings)
103 | val pDescriptorSetLayout = it.mallocLong(1)
104 | vkCreateDescriptorSetLayout(
105 | device.rawDevice,
106 | layoutInfo,
107 | null,
108 | pDescriptorSetLayout
109 | ).ok("Failed to create descriptor set layout")
110 | descriptorSetLayout = pDescriptorSetLayout[0]
111 | }
112 | }
113 |
114 | fun createDescriptorSets(
115 | swapchain: Swapchain,
116 | logger: org.apache.logging.log4j.Logger,
117 | currentTextures: Array,
118 | ubo: Ubo
119 | ) {
120 | this.preparableTextures.addAll(currentTextures)
121 |
122 | if (descriptorPool == 0L) {
123 | createPool(swapchain)
124 | }
125 | if (descriptorSetLayout == 0L) {
126 | logger.warn("Descriptor Set Layouts are invalid! rebuilding... (THIS IS NOT FAST)")
127 | createDescriptorSetLayout()
128 | }
129 | MemoryStack.stackPush().use { stack ->
130 | val layouts = stack.mallocLong(swapchain.swapChainImages.size)
131 | for (i in 0 until layouts.capacity()) {
132 | layouts.put(i, descriptorSetLayout)
133 | }
134 | val allocInfo = VkDescriptorSetAllocateInfo.callocStack(stack)
135 | .sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO)
136 | .descriptorPool(descriptorPool)
137 | .pSetLayouts(layouts)
138 | val pDescriptorSets = stack.mallocLong(swapchain.swapChainImages.size)
139 |
140 | vkAllocateDescriptorSets(device.rawDevice, allocInfo, pDescriptorSets)
141 | .ok("Failed to allocate descriptor sets")
142 |
143 | val descriptorSets = DescriptorSets(descriptorPool, pDescriptorSets.capacity())
144 | val bufferInfo = VkDescriptorBufferInfo.callocStack(1, stack)
145 | .offset(0)
146 | .range(ubo.getSize().toLong())
147 |
148 | val descriptorWrites = VkWriteDescriptorSet.callocStack(poolObjects.size, stack)
149 |
150 | for (i in 0 until pDescriptorSets.capacity()) {
151 | val descriptorSet = pDescriptorSets[i]
152 | bufferInfo.buffer(ubo.getUniformBuffers()[i].buffer())
153 | poolObjects.forEachIndexed { index, poolObj ->
154 | // TODO OPT: maybe group descriptors up by type if that's faster than defining each one by itself
155 | val descriptorWrite = descriptorWrites[index]
156 | .sType(VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET)
157 | .dstBinding(if (poolObj.getBindingLocation() == -1) index else poolObj.getBindingLocation())
158 | .dstArrayElement(0)
159 | .descriptorType(poolObj.getVkType())
160 | .descriptorCount(1)
161 |
162 | when (poolObj.getVkType()) {
163 | VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER -> {
164 | descriptorWrite.pBufferInfo(bufferInfo)
165 | }
166 |
167 | VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER -> {
168 | if (poolObj is PoolSamplerInfo) {
169 | val texture = if (poolObj.samplerIndex == -1) {
170 | TextureManager.BLANK_TEXTURE
171 | } else {
172 | currentTextures[poolObj.samplerIndex] ?: TextureManager.BLANK_TEXTURE
173 | }
174 |
175 | val imageInfo = VkDescriptorImageInfo.callocStack(1, stack)
176 | .imageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
177 | .imageView(texture.textureImage.view)
178 | .sampler(texture.textureSampler!!)
179 |
180 | descriptorWrite.pImageInfo(imageInfo)
181 | }
182 | }
183 | }
184 | descriptorWrite.dstSet(descriptorSet)
185 | }
186 | vkUpdateDescriptorSets(device.rawDevice, descriptorWrites, null)
187 | descriptorSets.setDescriptorPool(descriptorPool)
188 | descriptorSets.add(descriptorSet)
189 | }
190 |
191 | ubo.setDescriptors(descriptorSets)
192 | }
193 | }
194 |
195 | fun free() {
196 | vkDestroyDescriptorSetLayout(device.rawDevice, descriptorSetLayout, null)
197 | vkDestroyDescriptorPool(device.rawDevice, descriptorPool, null)
198 | }
199 |
200 | interface PoolObjectInfo {
201 | /**
202 | * If -1, the object will use the current index in the list when iterating
203 | * TODO: when converting this to java, make a static variable for -1 and use that
204 | */
205 | fun getBindingLocation(): Int
206 | fun getVkType(): Int
207 | fun getShaderStage(): Int
208 | }
209 |
210 | enum class PoolUboInfo : PoolObjectInfo {
211 | INSTANCE;
212 |
213 | override fun getBindingLocation(): Int {
214 | return -1
215 | }
216 |
217 | override fun getVkType(): Int {
218 | return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
219 | }
220 |
221 | override fun getShaderStage(): Int {
222 | return VK_SHADER_STAGE_ALL
223 | }
224 | }
225 |
226 | data class PoolSamplerInfo(private val bindingLocation: Int, val samplerIndex: Int) : PoolObjectInfo {
227 |
228 | override fun getBindingLocation(): Int {
229 | return bindingLocation
230 | }
231 |
232 | override fun getVkType(): Int {
233 | return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
234 | }
235 |
236 | override fun getShaderStage(): Int {
237 | return VK_SHADER_STAGE_ALL
238 | }
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/shader/Shader.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.shader
2 |
3 | import me.hydos.rosella.render.resource.Resource
4 |
5 | class Shader(val shaderLocation: Resource)
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/shader/ShaderManager.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.shader
2 |
3 | import me.hydos.rosella.Rosella
4 |
5 | class ShaderManager(val rosella: Rosella) {
6 |
7 | var cachedShaders = HashMap()
8 |
9 | fun getOrCreateShader(rawShader: RawShaderProgram): ShaderProgram? {
10 | if (!cachedShaders.containsKey(rawShader)) {
11 | cachedShaders[rawShader] = ShaderProgram(rawShader, rosella, rawShader.maxObjCount)
12 | }
13 |
14 | return cachedShaders[rawShader]
15 | }
16 |
17 | fun free() {
18 | for (program in cachedShaders.values) {
19 | program.free()
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/shader/ShaderProgram.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.shader
2 |
3 | import me.hydos.rosella.Rosella
4 | import me.hydos.rosella.device.VulkanDevice
5 | import me.hydos.rosella.render.util.compileShaderFile
6 | import me.hydos.rosella.render.util.ok
7 | import me.hydos.rosella.ubo.DescriptorManager
8 | import org.lwjgl.system.MemoryStack
9 | import org.lwjgl.vulkan.VK10
10 | import org.lwjgl.vulkan.VkShaderModuleCreateInfo
11 | import java.nio.ByteBuffer
12 |
13 | class ShaderProgram(val raw: RawShaderProgram, val rosella: Rosella, maxObjects: Int) {
14 |
15 | private val fragmentShader by lazy {
16 | compileShaderFile(raw.fragmentShader!!, ShaderType.FRAGMENT_SHADER).also {
17 | fragmentShaderCompiled = true
18 | }
19 | }
20 | private val vertexShader by lazy {
21 | compileShaderFile(raw.vertexShader!!, ShaderType.VERTEX_SHADER).also {
22 | vertexShaderCompiled = true
23 | }
24 | }
25 | val descriptorManager = DescriptorManager(maxObjects, this, rosella.renderer.swapchain, rosella.common.device, rosella.common.memory)
26 |
27 | private var fragmentShaderCompiled: Boolean = false
28 | private var vertexShaderCompiled: Boolean = false
29 |
30 | /**
31 | * Create a Vulkan shader module. used during pipeline creation.
32 | */
33 | private fun createShader(spirvCode: ByteBuffer, device: VulkanDevice): Long {
34 | MemoryStack.stackPush().use { stack ->
35 | val createInfo = VkShaderModuleCreateInfo.callocStack(stack)
36 | .sType(VK10.VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO)
37 | .pCode(spirvCode)
38 | val pShaderModule = stack.mallocLong(1)
39 | VK10.vkCreateShaderModule(device.rawDevice, createInfo, null, pShaderModule).ok()
40 | return pShaderModule[0]
41 | }
42 | }
43 |
44 | fun getVertShaderModule(): Long {
45 | return createShader(vertexShader.bytecode(), rosella.common.device)
46 | }
47 |
48 | fun getFragShaderModule(): Long {
49 | return createShader(fragmentShader.bytecode(), rosella.common.device)
50 | }
51 |
52 | /**
53 | * Free Shaders
54 | */
55 | fun free() {
56 | if (vertexShaderCompiled) vertexShader.free()
57 | if (fragmentShaderCompiled) fragmentShader.free()
58 | raw.free()
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/shader/pushconstant/ModelPushConstant.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.shader.pushconstant
2 |
3 | import org.joml.Vector3f
4 |
5 | class ModelPushConstant {
6 | val position: Vector3f = Vector3f()
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/shader/ubo/Ubo.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.shader.ubo
2 |
3 | import me.hydos.rosella.device.VulkanDevice
4 | import me.hydos.rosella.memory.BufferInfo
5 | import me.hydos.rosella.memory.Memory
6 | import me.hydos.rosella.memory.MemoryCloseable
7 | import me.hydos.rosella.render.descriptorsets.DescriptorSets
8 | import me.hydos.rosella.render.swapchain.Swapchain
9 |
10 | /**
11 | * A Uniform Buffer Object (ubo) is an object used to do things such as sending transformation matrices to the shader, sending lighting values to the shader, etc
12 | */
13 | abstract class Ubo : MemoryCloseable {
14 |
15 | /**
16 | * Called when the uniform buffers should be created
17 | */
18 | abstract fun create(swapchain: Swapchain)
19 |
20 | /**
21 | * Called before each frame to update the ubo
22 | */
23 | abstract fun update(currentImg: Int, swapchain: Swapchain)
24 |
25 | /**
26 | * Called when the program is closing and free's memory
27 | */
28 | abstract fun free()
29 |
30 | /**
31 | * Gets the size of the ubo
32 | */
33 | abstract fun getSize(): Int
34 |
35 | /**
36 | * Gets an list of pointers to the ubo frames
37 | */
38 | abstract fun getUniformBuffers(): List
39 |
40 | /**
41 | * Gets the descriptor sets used with this ubo
42 | */
43 | abstract fun getDescriptors(): DescriptorSets
44 |
45 | /**
46 | * Called when the program is closing and free's memory
47 | */
48 | override fun free(device: VulkanDevice?, memory: Memory?) {
49 | free()
50 | }
51 |
52 | abstract fun setDescriptors(descriptorSets: DescriptorSets)
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/swapchain/DepthBuffer.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.swapchain
2 |
3 | import me.hydos.rosella.device.VulkanDevice
4 | import me.hydos.rosella.render.createImage
5 | import me.hydos.rosella.render.createImageView
6 | import me.hydos.rosella.render.renderer.Renderer
7 | import me.hydos.rosella.render.transitionImageLayout
8 | import org.lwjgl.system.MemoryStack
9 | import org.lwjgl.vulkan.VK10.*
10 | import org.lwjgl.vulkan.VkFormatProperties
11 | import java.nio.IntBuffer
12 |
13 | /**
14 | * Since vulkan gives us so much control, we must make our own depth buffer instead of relying on the driver to create one for us.
15 | */
16 | class DepthBuffer {
17 |
18 | private var depthImage: Long = 0
19 | private var depthImageMemory: Long = 0
20 | var depthImageView: Long = 0
21 |
22 | fun createDepthResources(device: VulkanDevice, swapchain: Swapchain, renderer: Renderer) {
23 | MemoryStack.stackPush().use { stack ->
24 | val depthFormat: Int = findDepthFormat(device)
25 | val pDepthImage = stack.mallocLong(1)
26 | val pDepthImageMemory = stack.mallocLong(1)
27 | createImage(
28 | swapchain.swapChainExtent.width(),
29 | swapchain.swapChainExtent.height(),
30 | depthFormat,
31 | VK_IMAGE_TILING_OPTIMAL,
32 | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
33 | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
34 | pDepthImage,
35 | pDepthImageMemory,
36 | device
37 | )
38 | depthImage = pDepthImage[0]
39 | depthImageMemory = pDepthImageMemory[0]
40 | depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, device)
41 |
42 | // Explicitly transitioning the depth image
43 | transitionImageLayout(
44 | renderer,
45 | device,
46 | renderer.depthBuffer,
47 | depthImage,
48 | depthFormat,
49 | VK_IMAGE_LAYOUT_UNDEFINED,
50 | VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
51 | )
52 | }
53 | }
54 |
55 | fun findDepthFormat(device: VulkanDevice): Int {
56 | return findSupportedFormat(
57 | MemoryStack.stackGet()
58 | .ints(VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT),
59 | VK_IMAGE_TILING_OPTIMAL,
60 | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT,
61 | device
62 | )
63 | }
64 |
65 | fun hasStencilComponent(format: Int): Boolean {
66 | return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT
67 | }
68 |
69 | private fun findSupportedFormat(
70 | formatCandidates: IntBuffer,
71 | tiling: Int,
72 | features: Int,
73 | device: VulkanDevice
74 | ): Int {
75 | MemoryStack.stackPush().use { stack ->
76 | val props = VkFormatProperties.callocStack(stack)
77 | for (i in 0 until formatCandidates.capacity()) {
78 | val format = formatCandidates[i]
79 | vkGetPhysicalDeviceFormatProperties(device.physicalDevice, format, props)
80 | if (tiling == VK_IMAGE_TILING_LINEAR && props.linearTilingFeatures() and features == features) {
81 | return format
82 | } else if (tiling == VK_IMAGE_TILING_OPTIMAL && props.optimalTilingFeatures() and features == features) {
83 | return format
84 | }
85 | }
86 | }
87 | error("Failed to find supported format")
88 | }
89 |
90 | fun free(device: VulkanDevice) {
91 | vkDestroyImageView(device.rawDevice, depthImageView, null)
92 | vkDestroyImage(device.rawDevice, depthImage, null)
93 | vkFreeMemory(device.rawDevice, depthImageMemory, null)
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/swapchain/Frame.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.swapchain
2 |
3 | import org.lwjgl.system.MemoryStack.stackGet
4 | import java.nio.LongBuffer
5 |
6 | /**
7 | * Represents a frame that will be rendered to the window
8 | */
9 | class Frame(
10 | private val imageAvailableSemaphore: Long,
11 | private val renderFinishedSemaphore: Long,
12 | private val fence: Long
13 | ) {
14 | fun imageAvailableSemaphore(): Long {
15 | return imageAvailableSemaphore
16 | }
17 |
18 | fun pImageAvailableSemaphore(): LongBuffer {
19 | return stackGet().longs(imageAvailableSemaphore)
20 | }
21 |
22 | fun renderFinishedSemaphore(): Long {
23 | return renderFinishedSemaphore
24 | }
25 |
26 | fun pRenderFinishedSemaphore(): LongBuffer {
27 | return stackGet().longs(renderFinishedSemaphore)
28 | }
29 |
30 | fun fence(): Long {
31 | return fence
32 | }
33 |
34 | fun pFence(): LongBuffer {
35 | return stackGet().longs(fence)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/texture/StbiImage.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.texture
2 |
3 | import me.hydos.rosella.render.resource.Resource
4 | import org.lwjgl.stb.STBImage
5 | import org.lwjgl.system.MemoryStack
6 | import java.nio.ByteBuffer
7 |
8 | class StbiImage(resource: Resource, private val format: ImageFormat) : UploadableImage {
9 |
10 | private var width: Int
11 | private var height: Int
12 | private var size: Int
13 | private var pixels: ByteBuffer
14 |
15 | init {
16 | MemoryStack.stackPush().use { stack ->
17 | val file = resource.readAllBytes(true)
18 | val pWidth = stack.mallocInt(1)
19 | val pHeight = stack.mallocInt(1)
20 | val pChannels = stack.mallocInt(1)
21 | var pixels: ByteBuffer? = STBImage.stbi_load_from_memory(file, pWidth, pHeight, pChannels, format.channels)
22 | if (pixels != null) {
23 | if (pChannels[0] != format.channels) {
24 | throw RuntimeException("Failed to load texture image ${resource.identifier}: Expected channel count (${format.channels}) did not match returned channel count (${pChannels[0]})")
25 | }
26 | } else {
27 | pixels = ByteBuffer.wrap(resource.openStream().readAllBytes())
28 | if (pixels == null) {
29 | throw RuntimeException("Failed to load texture image ${resource.identifier}")
30 | }
31 | }
32 |
33 | this.width = pWidth[0]
34 | this.height = pHeight[0]
35 | this.size = width * height * format.pixelSize
36 | this.pixels = pixels
37 | }
38 | }
39 |
40 |
41 | override fun getWidth(): Int {
42 | return width
43 | }
44 |
45 | override fun getHeight(): Int {
46 | return height
47 | }
48 |
49 | override fun getFormat(): ImageFormat {
50 | return format
51 | }
52 |
53 | override fun getSize(): Int {
54 | return size
55 | }
56 |
57 | override fun getPixels(): ByteBuffer {
58 | return pixels
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/texture/Texture.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.texture
2 |
3 | data class Texture(
4 | val imgFormat: Int,
5 | val width: Int,
6 | val height: Int,
7 | val textureImage: TextureImage,
8 | var textureSampler: Long?
9 | )
10 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/texture/TextureImage.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.texture
2 |
3 | data class TextureImage(
4 | var textureImage: Long,
5 | var textureImageMemory: Long,
6 | var view: Long
7 | )
8 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/texture/UploadableImage.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.texture
2 |
3 | import java.nio.ByteBuffer
4 |
5 | /**
6 | * Allows the ability for the software to load the image their own way. especially handy when you generate images
7 | */
8 | interface UploadableImage {
9 |
10 | fun getWidth(): Int
11 | fun getHeight(): Int
12 | fun getFormat(): ImageFormat
13 | fun getSize(): Int
14 | fun getPixels(): ByteBuffer?
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/util/SprirVUtils.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.util
2 |
3 | import me.hydos.rosella.render.resource.Resource
4 | import me.hydos.rosella.render.shader.ShaderType
5 | import org.lwjgl.system.MemoryUtil.NULL
6 | import org.lwjgl.system.NativeResource
7 | import org.lwjgl.util.shaderc.Shaderc.*
8 | import java.nio.ByteBuffer
9 |
10 | fun compileShaderFile(shader: Resource, shaderType: ShaderType): SpirV {
11 | val source = shader.openStream().readBytes().decodeToString()
12 | return compileShader(shader.identifier.file, source, shaderType)
13 | }
14 |
15 | fun compileShader(filename: String, source: String, shaderType: ShaderType): SpirV {
16 | val compiler = shaderc_compiler_initialize()
17 | if (compiler == NULL) {
18 | throw RuntimeException("Failed to create shader compiler")
19 | }
20 |
21 | val result: Long = shaderc_compile_into_spv(compiler, source, shaderType.shaderCType, filename, "main", NULL)
22 | if (result == NULL) {
23 | throw RuntimeException("Failed to compile shader $filename into SPIR-V")
24 | }
25 |
26 | if (shaderc_result_get_compilation_status(result) != shaderc_compilation_status_success) {
27 | error("Failed to compile shader $filename into SPIR-V: ${shaderc_result_get_error_message(result)}")
28 | }
29 | shaderc_compiler_release(compiler)
30 |
31 | return SpirV(result, shaderc_result_get_bytes(result))
32 | }
33 |
34 | class SpirV(private val handle: Long, private var bytecode: ByteBuffer?) : NativeResource {
35 | fun bytecode(): ByteBuffer {
36 | return bytecode!!
37 | }
38 |
39 | override fun free() {
40 | shaderc_result_release(handle)
41 | bytecode = null
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/render/util/VkUtils.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.render.util
2 |
3 | import org.joml.Matrix4f
4 | import org.joml.Vector2f
5 | import org.joml.Vector3f
6 | import org.joml.Vector4f
7 | import org.lwjgl.vulkan.EXTDebugReport.VK_ERROR_VALIDATION_FAILED_EXT
8 | import org.lwjgl.vulkan.KHRSurface
9 | import org.lwjgl.vulkan.VK10
10 | import org.lwjgl.vulkan.VK11
11 | import kotlin.reflect.KClass
12 |
13 | private val errorMap = mutableMapOf().apply {
14 | this[VK10.VK_NOT_READY] = "VK_NOT_READY"
15 | this[VK10.VK_TIMEOUT] = "VK_TIMEOUT"
16 | this[VK10.VK_EVENT_SET] = "VK_EVENT_SET"
17 | this[VK10.VK_EVENT_RESET] = "VK_EVENT_RESET"
18 | this[VK10.VK_INCOMPLETE] = "VK_INCOMPLETE"
19 | this[VK10.VK_ERROR_OUT_OF_HOST_MEMORY] = "VK_ERROR_OUT_OF_HOST_MEMORY"
20 | this[VK11.VK_ERROR_OUT_OF_POOL_MEMORY] = "VK_ERROR_OUT_OF_POOL_MEMORY"
21 | this[VK10.VK_ERROR_OUT_OF_DEVICE_MEMORY] = "VK_ERROR_OUT_OF_DEVICE_MEMORY"
22 | this[VK10.VK_ERROR_INITIALIZATION_FAILED] = "VK_ERROR_INITIALIZATION_FAILED"
23 | this[VK10.VK_ERROR_DEVICE_LOST] = "VK_ERROR_DEVICE_LOST"
24 | this[VK10.VK_ERROR_MEMORY_MAP_FAILED] = "VK_ERROR_MEMORY_MAP_FAILED"
25 | this[VK10.VK_ERROR_LAYER_NOT_PRESENT] = "VK_ERROR_LAYER_NOT_PRESENT"
26 | this[VK10.VK_ERROR_EXTENSION_NOT_PRESENT] = "VK_ERROR_EXTENSION_NOT_PRESENT"
27 | this[VK10.VK_ERROR_FEATURE_NOT_PRESENT] = "VK_ERROR_FEATURE_NOT_PRESENT"
28 | this[VK10.VK_ERROR_INCOMPATIBLE_DRIVER] = "VK_ERROR_INCOMPATIBLE_DRIVER"
29 | this[VK10.VK_ERROR_TOO_MANY_OBJECTS] = "VK_ERROR_TOO_MANY_OBJECTS"
30 | this[VK10.VK_ERROR_FORMAT_NOT_SUPPORTED] = "VK_ERROR_FORMAT_NOT_SUPPORTED"
31 | this[VK10.VK_ERROR_FRAGMENTED_POOL] = "VK_ERROR_FRAGMENTED_POOL"
32 | this[VK10.VK_ERROR_UNKNOWN] = "VK_ERROR_UNKNOWN"
33 | this[KHRSurface.VK_ERROR_NATIVE_WINDOW_IN_USE_KHR] = "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR"
34 | this[VK_ERROR_VALIDATION_FAILED_EXT] = "VK_ERROR_VALIDATION_FAILED_EXT"
35 | }
36 |
37 | private val SIZEOF_CACHE = mutableMapOf, Int>().apply {
38 | this[Byte::class.java] = Byte.SIZE_BYTES
39 | this[Character::class.java] = Character.BYTES
40 | this[Short::class.java] = Short.SIZE_BYTES
41 | this[Integer::class.java] = Integer.BYTES
42 | this[Float::class.java] = Float.SIZE_BYTES
43 | this[Long::class.java] = Long.SIZE_BYTES
44 | this[Double::class.java] = Double.SIZE_BYTES
45 |
46 | this[Vector2f::class.java] = 2 * Float.SIZE_BYTES
47 | this[Vector3f::class.java] = 3 * Float.SIZE_BYTES
48 | this[Vector4f::class.java] = 4 * Float.SIZE_BYTES
49 |
50 | this[Matrix4f::class.java] = 16 * java.lang.Float.BYTES
51 | }
52 |
53 | fun sizeof(kClass: KClass<*>): Int {
54 | return SIZEOF_CACHE[kClass.java] ?: 0
55 | }
56 |
57 | fun alignof(obj: Any?): Int {
58 | return if (obj == null) 0 else SIZEOF_CACHE[obj.javaClass] ?: Integer.BYTES
59 | }
60 |
61 | fun alignas(offset: Int, alignment: Int): Int {
62 | return if (offset % alignment == 0) offset else (offset - 1 or alignment - 1) + 1
63 | }
64 |
65 | fun Int.ok(): Int {
66 | if (this != VK10.VK_SUCCESS) {
67 | throw RuntimeException(errorMap[this] ?: toString())
68 | }
69 | return this
70 | }
71 |
72 | fun Int.ok(message: String): Int {
73 | if (this != VK10.VK_SUCCESS) {
74 | throw RuntimeException(message + " Caused by: " + errorMap[this] + " (" + this + ")")
75 | }
76 | return this
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/scene/Scene.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.scene
2 |
3 | import me.hydos.rosella.render.resource.Identifier
4 |
5 | class Scene {
6 | var id: Identifier = Identifier("rosella", "empty")
7 | var models: MutableList = ArrayList()
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/hydos/rosella/scene/SceneModel.kt:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.scene
2 |
3 | import me.hydos.rosella.render.resource.Identifier
4 |
5 | class SceneModel {
6 | var id: Identifier? = null
7 | var location: Identifier? = null
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/resources/rosella/editor/gui/files/class.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hYdos/Rosella/50a0ee2c5bdfe8255ceb1120f9de4ea7679da9a4/src/main/resources/rosella/editor/gui/files/class.png
--------------------------------------------------------------------------------
/src/main/resources/rosella/editor/gui/files/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hYdos/Rosella/50a0ee2c5bdfe8255ceb1120f9de4ea7679da9a4/src/main/resources/rosella/editor/gui/files/file.png
--------------------------------------------------------------------------------
/src/main/resources/rosella/editor/gui/files/interface.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hYdos/Rosella/50a0ee2c5bdfe8255ceb1120f9de4ea7679da9a4/src/main/resources/rosella/editor/gui/files/interface.png
--------------------------------------------------------------------------------
/src/main/resources/rosella/editor/gui/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hYdos/Rosella/50a0ee2c5bdfe8255ceb1120f9de4ea7679da9a4/src/main/resources/rosella/editor/gui/folder.png
--------------------------------------------------------------------------------
/src/main/resources/rosella/fonts/DIN Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hYdos/Rosella/50a0ee2c5bdfe8255ceb1120f9de4ea7679da9a4/src/main/resources/rosella/fonts/DIN Bold.otf
--------------------------------------------------------------------------------
/src/main/resources/rosella/shaders/base.f.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 |
4 | layout(binding = 1) uniform sampler2D texSampler;
5 |
6 | layout(location = 0) in vec3 fragColor;
7 | layout(location = 1) in vec2 fragTexCoord;
8 |
9 | layout(location = 0) out vec4 outColor;
10 |
11 | void main() {
12 | outColor = texture(texSampler, fragTexCoord);
13 | }
--------------------------------------------------------------------------------
/src/main/resources/rosella/shaders/base.v.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 |
4 | layout(binding = 0) uniform UniformBufferObject {
5 | mat4 model;
6 | mat4 view;
7 | mat4 proj;
8 | } ubo;
9 |
10 | layout(location = 0) in vec3 inPosition;
11 | layout(location = 1) in vec3 inColor;
12 | layout(location = 2) in vec2 inTexCoord;
13 |
14 | layout(location = 0) out vec3 fragColor;
15 | layout(location = 1) out vec2 fragTexCoord;
16 |
17 | void main() {
18 | vec4 worldPosition = ubo.model * vec4(inPosition, 1.0);
19 |
20 | gl_Position = ubo.proj * ubo.view * worldPosition;
21 | fragColor = inColor;
22 | fragTexCoord = inTexCoord;
23 | }
--------------------------------------------------------------------------------
/src/main/resources/rosella/shaders/fonts.f.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 |
4 | layout(binding = 1) uniform sampler2D texSampler;
5 |
6 | layout(location = 0) in vec3 fragColor;
7 | layout(location = 1) in vec2 fragTexCoord;
8 |
9 | layout(location = 0) out vec4 outColor;
10 |
11 | void main() {
12 | bool wireframe = false;
13 |
14 | outColor = texture(texSampler, fragTexCoord);
15 |
16 | if (outColor.a < 0.05 && !wireframe) {
17 | discard;
18 | } else {
19 | outColor.r = fragColor.r;
20 | outColor.g = fragColor.g;
21 | outColor.b = fragColor.b;
22 | }
23 |
24 | if (wireframe) {
25 | outColor = vec4(1, 1, 1, 1);
26 | }
27 | }
--------------------------------------------------------------------------------
/src/main/resources/rosella/shaders/fonts.v.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 |
4 | layout(binding = 0) uniform UniformBufferObject {
5 | mat4 model;
6 | mat4 view;
7 | mat4 proj;
8 | } ubo;
9 |
10 | layout(location = 0) in vec3 inPosition;
11 | layout(location = 1) in vec3 inColor;
12 | layout(location = 2) in vec2 inTexCoord;
13 |
14 | layout(location = 0) out vec3 fragColor;
15 | layout(location = 1) out vec2 fragTexCoord;
16 |
17 | void main() {
18 | gl_Position = ubo.proj * ubo.model * vec4(inPosition.xy, 0.0, 1.0);
19 | fragColor = inColor;
20 | fragTexCoord = vec2(inTexCoord.x, 1.0 - inTexCoord.y);
21 | }
--------------------------------------------------------------------------------
/src/main/resources/rosella/shaders/gui.colour.f.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 |
4 | layout(binding = 1) uniform sampler2D texSampler;
5 |
6 | layout(location = 0) in vec3 fragColor;
7 | layout(location = 1) in vec2 fragTexCoord;
8 |
9 | layout(location = 0) out vec4 outColor;
10 |
11 | void main() {
12 | outColor = vec4(fragColor, 1.0);
13 | }
--------------------------------------------------------------------------------
/src/main/resources/rosella/shaders/gui.f.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 |
4 | layout(binding = 1) uniform sampler2D texSampler;
5 |
6 | layout(location = 0) in vec3 fragColor;
7 | layout(location = 1) in vec2 fragTexCoord;
8 |
9 | layout(location = 0) out vec4 outColor;
10 |
11 | void main() {
12 | bool wireframe = false;
13 |
14 | outColor = texture(texSampler, fragTexCoord);
15 |
16 | if(outColor.a < 0.05 && !wireframe) {
17 | discard;
18 | }
19 |
20 | if(wireframe) {
21 | outColor = vec4(1, 1, 1, 1);
22 | }
23 | }
--------------------------------------------------------------------------------
/src/main/resources/rosella/shaders/gui.v.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 |
4 | layout(binding = 0) uniform UniformBufferObject {
5 | mat4 model;
6 | mat4 view;
7 | mat4 proj;
8 | } ubo;
9 |
10 | layout(location = 0) in vec3 inPosition;
11 | layout(location = 1) in vec3 inColor;
12 | layout(location = 2) in vec2 inTexCoord;
13 |
14 | layout(location = 0) out vec3 fragColor;
15 | layout(location = 1) out vec2 fragTexCoord;
16 |
17 | void main() {
18 | gl_Position = ubo.proj * ubo.model * vec4(inPosition.xy, 0.0, 1.0);
19 | fragColor = inColor;
20 | fragTexCoord = vec2(inTexCoord.x, 1.0 - inTexCoord.y);
21 | }
--------------------------------------------------------------------------------
/src/test/java/me/hydos/rosella/example/PortalJava.java:
--------------------------------------------------------------------------------
1 | package me.hydos.rosella.example;
2 |
3 | import me.hydos.rosella.Rosella;
4 | import me.hydos.rosella.display.GlfwWindow;
5 | import me.hydos.rosella.render.Topology;
6 | import me.hydos.rosella.render.material.Material;
7 | import me.hydos.rosella.render.material.state.StateInfo;
8 | import me.hydos.rosella.render.model.GuiRenderObject;
9 | import me.hydos.rosella.render.resource.Global;
10 | import me.hydos.rosella.render.resource.Identifier;
11 | import me.hydos.rosella.render.shader.RawShaderProgram;
12 | import me.hydos.rosella.render.shader.ShaderProgram;
13 | import me.hydos.rosella.render.texture.SamplerCreateInfo;
14 | import me.hydos.rosella.render.texture.TextureFilter;
15 | import me.hydos.rosella.render.texture.WrapMode;
16 | import me.hydos.rosella.render.vertex.VertexFormats;
17 | import me.hydos.rosella.scene.object.impl.SimpleFramebufferObjectManager;
18 | import org.joml.Matrix4f;
19 | import org.joml.Vector3f;
20 | import org.lwjgl.vulkan.VK10;
21 |
22 | public class PortalJava {
23 |
24 | public static final GlfwWindow window = new GlfwWindow(1280, 720, "Portal 3: Java Edition", true);
25 | public static final Rosella rosella = new Rosella(window, "portal 3", true);
26 |
27 | public static final Matrix4f viewMatrix = new Matrix4f().lookAt(2.0f, -40.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
28 | public static final Matrix4f projectionMatrix = new Matrix4f().perspective(
29 | (float) Math.toRadians(45.0),
30 | 1280 / 720f,
31 | 0.1f,
32 | 1000.0f
33 | );
34 |
35 | public static Material menuBackground;
36 | public static Material portalLogo;
37 |
38 | public static ShaderProgram basicShader;
39 | public static ShaderProgram guiShader;
40 |
41 | public static StateInfo defaultStateInfo = new StateInfo(
42 | VK10.VK_COLOR_COMPONENT_R_BIT | VK10.VK_COLOR_COMPONENT_G_BIT | VK10.VK_COLOR_COMPONENT_B_BIT | VK10.VK_COLOR_COMPONENT_A_BIT,
43 | true,
44 | false,
45 | 0, 0, 0, 0,
46 | false,
47 | true,
48 | VK10.VK_BLEND_FACTOR_ONE, VK10.VK_BLEND_FACTOR_ZERO, VK10.VK_BLEND_FACTOR_ONE, VK10.VK_BLEND_FACTOR_ZERO,
49 | VK10.VK_BLEND_OP_ADD,
50 | true,
51 | false,
52 | VK10.VK_COMPARE_OP_LESS,
53 | false,
54 | VK10.VK_LOGIC_OP_COPY,
55 | 1.0f
56 | );
57 |
58 | public static void main(String[] args) {
59 | System.loadLibrary("renderdoc");
60 | loadShaders();
61 | loadMaterials();
62 | setupMainMenuScene();
63 | rosella.renderer.rebuildCommandBuffers(rosella.renderer.renderPass, (SimpleFramebufferObjectManager) rosella.objectManager);
64 | window.startAutomaticLoop(rosella);
65 | }
66 |
67 | private static void setupMainMenuScene() {
68 | rosella.getMainFboObjManager().addObject(
69 | new GuiRenderObject(menuBackground, -1f, new Vector3f(0, 0, 0), 1.5f, 1f, viewMatrix, projectionMatrix)
70 | );
71 |
72 | rosella.getMainFboObjManager().addObject(
73 | new GuiRenderObject(portalLogo, -0.9f, new Vector3f(0, 0, 0), 0.4f, 0.1f, -1f, -2.6f, viewMatrix, projectionMatrix)
74 | );
75 | }
76 |
77 | private static void loadMaterials() {
78 | menuBackground = rosella.objectManager.registerMaterial(
79 | new Material(
80 | Global.INSTANCE.ensureResource(new Identifier("example", "textures/background/background01.png")),
81 | guiShader,
82 | VK10.VK_FORMAT_R8G8B8A8_UNORM,
83 | Topology.TRIANGLES,
84 | VertexFormats.POSITION_COLOR3_UV0,
85 | new SamplerCreateInfo(TextureFilter.NEAREST, WrapMode.REPEAT),
86 | defaultStateInfo
87 | )
88 | );
89 |
90 | portalLogo = rosella.objectManager.registerMaterial(
91 | new Material(
92 | Global.INSTANCE.ensureResource(new Identifier("example", "textures/gui/portal2logo.png")),
93 | guiShader,
94 | VK10.VK_FORMAT_R8G8B8A8_SRGB,
95 | Topology.TRIANGLES,
96 | VertexFormats.POSITION_COLOR3_UV0,
97 | new SamplerCreateInfo(TextureFilter.NEAREST, WrapMode.REPEAT),
98 | defaultStateInfo
99 | )
100 | );
101 |
102 | rosella.objectManager.submitMaterials();
103 | }
104 |
105 | private static void loadShaders() {
106 | basicShader = rosella.objectManager.addShader(
107 | new RawShaderProgram(
108 | Global.INSTANCE.ensureResource(new Identifier("rosella", "shaders/base.v.glsl")),
109 | Global.INSTANCE.ensureResource(new Identifier("rosella", "shaders/base.f.glsl")),
110 | rosella.common.device,
111 | rosella.common.memory,
112 | 10,
113 | RawShaderProgram.PoolUboInfo.INSTANCE,
114 | new RawShaderProgram.PoolSamplerInfo(-1, 0)
115 | )
116 | );
117 |
118 | guiShader = rosella.objectManager.addShader(
119 | new RawShaderProgram(
120 | Global.INSTANCE.ensureResource(new Identifier("rosella", "shaders/gui.v.glsl")),
121 | Global.INSTANCE.ensureResource(new Identifier("rosella", "shaders/gui.f.glsl")),
122 | rosella.common.device,
123 | rosella.common.memory,
124 | 10,
125 | RawShaderProgram.PoolUboInfo.INSTANCE,
126 | new RawShaderProgram.PoolSamplerInfo(-1, 0)
127 | )
128 | );
129 | }
130 | }
131 |
--------------------------------------------------------------------------------