├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── core ├── assets │ ├── fonts │ │ ├── Exo-Regular.otf │ │ ├── LeagueGothic-Regular.otf │ │ └── LiberationMono-Regular.ttf │ ├── generated-fonts │ │ ├── exo14.fnt │ │ └── exo14 │ │ │ └── exo14.png │ ├── shader │ │ ├── custom.fragment.glsl │ │ ├── custom.vertex.glsl │ │ └── water.fragment.glsl~ │ ├── texture │ │ └── dirt.png │ └── ui │ │ ├── league64.fnt │ │ ├── ubuntu-medium20.fnt │ │ ├── ui.atlas │ │ ├── ui.json │ │ └── ui.png ├── build.gradle └── src │ └── main │ ├── java │ ├── META-INF │ │ └── MANIFEST.MF │ └── org │ │ └── jrenner │ │ ├── RenStringBuilder.java │ │ ├── learngl │ │ ├── SimplexNoise.java │ │ └── cube │ │ │ ├── CubeDataGrid.java │ │ │ └── WorldChunkData.java │ │ └── smartfont │ │ ├── SmartFontGenerator.java │ │ └── writer │ │ └── BitmapFontWriter.java │ └── kotlin │ └── org │ └── jrenner │ └── learngl │ ├── Assets.kt │ ├── DebugPool.kt │ ├── Direction.kt │ ├── Fonts.kt │ ├── FramePool.kt │ ├── HUD.kt │ ├── Main.kt │ ├── Physics.kt │ ├── TimedIntervalTask.kt │ ├── View.kt │ ├── gameworld │ ├── Chunk.kt │ ├── ChunkMesh.kt │ ├── CubeData.kt │ ├── CubeTypes.kt │ ├── World.kt │ └── WorldUpdater.kt │ ├── input │ └── GameInput.kt │ ├── light │ ├── DirectionalLight.kt │ ├── Lights.kt │ └── PointLight.kt │ └── utils │ ├── Extensions.kt │ ├── SimpleTimer.kt │ └── Tools.kt ├── countlines.py ├── desktop ├── build.gradle └── src │ ├── META-INF │ └── MANIFEST.MF │ └── org │ └── jrenner │ └── learngl │ └── desktop │ └── DesktopLauncher.java ├── generated-fonts ├── exo14.fnt └── exo14 │ └── exo14.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── test ├── build.gradle └── src │ └── kotlin │ └── org │ └── jrenner │ └── learngl │ └── test │ └── CubeDataGridTest.kt └── voxel1.png /.gitignore: -------------------------------------------------------------------------------- 1 | ## Java 2 | 3 | generated-fonts 4 | kotlin-voxel.jar 5 | 6 | *.class 7 | *.war 8 | *.ear 9 | hs_err_pid* 10 | 11 | ## GWT 12 | war/ 13 | html/war/gwt_bree/ 14 | html/gwt-unitCache/ 15 | .apt_generated/ 16 | html/war/WEB-INF/deploy/ 17 | html/war/WEB-INF/classes/ 18 | .gwt/ 19 | gwt-unitCache/ 20 | www-test/ 21 | .gwt-tmp/ 22 | 23 | ## Android Studio and Intellij and Android in general 24 | android/libs/armeabi/ 25 | android/libs/armeabi-v7a/ 26 | android/libs/x86/ 27 | android/gen/ 28 | .idea/ 29 | *.ipr 30 | *.iws 31 | *.iml 32 | out/ 33 | com_crashlytics_export_strings.xml 34 | 35 | ## Eclipse 36 | .classpath 37 | .project 38 | .metadata 39 | **/bin/ 40 | tmp/ 41 | *.tmp 42 | *.bak 43 | *.swp 44 | *~.nib 45 | local.properties 46 | .settings/ 47 | .loadpath 48 | .externalToolBuilders/ 49 | *.launch 50 | 51 | ## NetBeans 52 | **/nbproject/private/ 53 | build/ 54 | nbbuild/ 55 | dist/ 56 | nbdist/ 57 | nbactions.xml 58 | nb-configuration.xml 59 | 60 | ## Gradle 61 | 62 | .gradle 63 | gradle-app.setting 64 | build/ 65 | 66 | ## OS Specific 67 | .DS_Store 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kotlin-voxel 2 | A voxel engine (like Minecraft) written in Kotlin 3 | 4 | ------ 5 | 6 | This voxel engine was created as a programming exercise. It was created with the Kotlin programming language and libgdx, a Java game library. 7 | 8 | ![screenshot](/voxel1.png) 9 | 10 | Youtube Video: https://t.co/77OObtUcxR 11 | 12 | To run, you must use the gradle task 13 | 14 | Linux/OSX: 15 | ``` 16 | ./gradlew desktop:run 17 | ``` 18 | Windows: 19 | ``` 20 | gradlew.bat desktop:run 21 | ``` 22 | 23 | If you want to edit the code with IntelliJ IDEA indepedent from gradle, you can create idea project files with the task: 24 | ``` 25 | ./gradlew idea (windows: gradlew.bat idea) 26 | ``` 27 | Then open the .ipr file with IntelliJ 28 | 29 | Controls are explained in the in-game HUD 30 | 31 | additionally, you may press 'G' to see OpenGL debugging info, or 'V' to reset the view. 32 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlinVersion = "1.1.50" 3 | repositories { 4 | mavenLocal() 5 | mavenCentral() 6 | jcenter() 7 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } 8 | } 9 | dependencies { 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" 11 | } 12 | } 13 | 14 | allprojects { 15 | apply plugin: "eclipse" 16 | apply plugin: "idea" 17 | 18 | version = '1.0' 19 | ext { 20 | appName = 'Terra' 21 | gdxVersion = '1.9.6' 22 | roboVMVersion = '2.3.1' 23 | box2DLightsVersion = '1.4' 24 | ashleyVersion = '1.7.0' 25 | aiVersion = '1.8.0' 26 | } 27 | 28 | repositories { 29 | mavenLocal() 30 | mavenCentral() 31 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } 32 | maven { url "https://oss.sonatype.org/content/repositories/releases/" } 33 | } 34 | } 35 | 36 | project(":desktop") { 37 | apply plugin: "java" 38 | 39 | dependencies { 40 | compile project(":core") 41 | compile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion" 42 | compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" 43 | compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop" 44 | } 45 | } 46 | 47 | project(":core") { 48 | apply plugin: "java" 49 | apply plugin: "kotlin" 50 | 51 | dependencies { 52 | compile "com.badlogicgames.gdx:gdx:$gdxVersion" 53 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" 54 | compile "com.badlogicgames.gdx:gdx-freetype:$gdxVersion" 55 | } 56 | } 57 | 58 | project(":test") { 59 | apply plugin: "java" 60 | apply plugin: "kotlin" 61 | 62 | dependencies { 63 | compile project(":core") 64 | testCompile group: 'junit', name: 'junit', version: '4.11' 65 | } 66 | } 67 | 68 | tasks.eclipse.doLast { 69 | delete ".project" 70 | } 71 | -------------------------------------------------------------------------------- /core/assets/fonts/Exo-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrenner/kotlin-voxel/1266cc7366fea7a069f3a545ac54cc0e753d457a/core/assets/fonts/Exo-Regular.otf -------------------------------------------------------------------------------- /core/assets/fonts/LeagueGothic-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrenner/kotlin-voxel/1266cc7366fea7a069f3a545ac54cc0e753d457a/core/assets/fonts/LeagueGothic-Regular.otf -------------------------------------------------------------------------------- /core/assets/fonts/LiberationMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrenner/kotlin-voxel/1266cc7366fea7a069f3a545ac54cc0e753d457a/core/assets/fonts/LiberationMono-Regular.ttf -------------------------------------------------------------------------------- /core/assets/generated-fonts/exo14/exo14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrenner/kotlin-voxel/1266cc7366fea7a069f3a545ac54cc0e753d457a/core/assets/generated-fonts/exo14/exo14.png -------------------------------------------------------------------------------- /core/assets/shader/custom.fragment.glsl: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision mediump float; 3 | #endif 4 | 5 | //input from vertex shader 6 | varying vec4 v_lighting; 7 | 8 | uniform sampler2D u_diffuseTexture; 9 | uniform float u_maxViewDist; 10 | uniform vec4 u_fogColor; 11 | 12 | varying vec2 v_diffuseUV; 13 | varying float v_distFromCamera; 14 | 15 | 16 | void main() { 17 | float distRatio = clamp(v_distFromCamera / u_maxViewDist, 0.0, 1.0); 18 | float fogIntensity = u_fogColor.a * (distRatio * distRatio); 19 | vec3 fogBaseColor = u_fogColor.rgb; 20 | vec4 litDiffuse = v_lighting * texture2D(u_diffuseTexture, v_diffuseUV.st); 21 | gl_FragColor.rgb = (fogBaseColor * fogIntensity) + (litDiffuse.rgb * (1.0 - fogIntensity)); 22 | gl_FragColor.a = litDiffuse.a; 23 | } -------------------------------------------------------------------------------- /core/assets/shader/custom.vertex.glsl: -------------------------------------------------------------------------------- 1 | //our attributes 2 | attribute vec3 a_position; 3 | attribute vec3 a_normal; 4 | 5 | struct PointLight 6 | { 7 | vec3 pos; 8 | vec3 color; 9 | float intensity; 10 | }; 11 | 12 | struct DirLight 13 | { 14 | vec3 direction; 15 | vec3 color; 16 | }; 17 | 18 | attribute vec2 a_textureCoords; 19 | 20 | varying vec2 v_diffuseUV; 21 | 22 | uniform mat4 u_projTrans; 23 | uniform mat4 u_normalMatrix; 24 | uniform vec4 u_ambientLight; 25 | uniform vec3 u_cameraPos; 26 | 27 | #define NUM_POINTLIGHTS 11 28 | uniform PointLight u_pointLights[NUM_POINTLIGHTS]; 29 | 30 | uniform DirLight u_dirLight; 31 | 32 | varying vec4 v_lighting; 33 | varying float v_distFromCamera; 34 | 35 | vec4 getPointLightColor(const PointLight ptLight) { 36 | vec3 pos_to_light = a_position - ptLight.pos; 37 | float dist = length(pos_to_light); 38 | pos_to_light = normalize(pos_to_light); 39 | 40 | float diffuse = max(0.0, dot(a_normal, -pos_to_light)); 41 | 42 | float amb = 0.2; 43 | float constAtt = 0.8; 44 | float linearAtt = 0.04 * dist; 45 | float expAtt = 0.04 * dist * dist; 46 | 47 | float attTotal = constAtt + linearAtt + (expAtt) / ptLight.intensity; 48 | 49 | vec4 result = vec4(ptLight.color, 1.0) * (amb + diffuse) / attTotal; 50 | result.a = 1.0; 51 | return result; 52 | } 53 | 54 | vec4 applyPointLight(const PointLight ptLight, vec4 color) { 55 | vec4 lightCol = getPointLightColor(ptLight); 56 | return color + lightCol; 57 | } 58 | 59 | vec4 applyDirectionalLight(const DirLight dirLight, vec4 color) { 60 | //vec4 normalBase = normalize(u_normalMatrix * vec4(a_normal, 0.0)); 61 | //vec3 normal = normalBase.xyz; 62 | vec3 lightDir = -dirLight.direction; 63 | float NdotL = clamp(dot(a_normal, lightDir), 0.0, 1.0); 64 | vec4 value = vec4(dirLight.color, 1.0) * NdotL; 65 | return color += value; 66 | } 67 | 68 | void main() { 69 | v_lighting = vec4(u_ambientLight.rgb, 1.0); 70 | 71 | for(int i = 0; i < NUM_POINTLIGHTS; i++) { 72 | PointLight pLight = u_pointLights[i]; 73 | v_lighting = applyPointLight(pLight, v_lighting); 74 | } 75 | 76 | v_lighting = applyDirectionalLight(u_dirLight, v_lighting); 77 | 78 | gl_Position = u_projTrans * vec4(a_position.xyz, 1.0); 79 | v_diffuseUV = a_textureCoords; 80 | v_distFromCamera = abs(length(u_cameraPos - a_position.xyz)); 81 | } 82 | -------------------------------------------------------------------------------- /core/assets/shader/water.fragment.glsl~: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | #define LOWP lowp 3 | #define MED mediump 4 | #define HIGH highp 5 | precision mediump float; 6 | #else 7 | #define MED 8 | #define LOWP 9 | #define HIGH 10 | #endif 11 | 12 | #if defined(specularTextureFlag) || defined(specularColorFlag) 13 | #define specularFlag 14 | #endif 15 | 16 | #ifdef normalFlag 17 | varying vec3 v_normal; 18 | #endif //normalFlag 19 | 20 | #if defined(colorFlag) 21 | varying vec4 v_color; 22 | #endif 23 | 24 | #ifdef blendedFlag 25 | varying float v_opacity; 26 | #ifdef alphaTestFlag 27 | varying float v_alphaTest; 28 | #endif //alphaTestFlag 29 | #endif //blendedFlag 30 | 31 | #if defined(diffuseTextureFlag) || defined(specularTextureFlag) 32 | #define textureFlag 33 | #endif 34 | 35 | #ifdef diffuseTextureFlag 36 | varying MED vec2 v_diffuseUV; 37 | #endif 38 | 39 | #ifdef specularTextureFlag 40 | varying MED vec2 v_specularUV; 41 | #endif 42 | 43 | #ifdef diffuseColorFlag 44 | uniform vec4 u_diffuseColor; 45 | #endif 46 | 47 | #ifdef diffuseTextureFlag 48 | uniform sampler2D u_diffuseTexture; 49 | #endif 50 | 51 | #ifdef specularColorFlag 52 | uniform vec4 u_specularColor; 53 | #endif 54 | 55 | #ifdef specularTextureFlag 56 | uniform sampler2D u_specularTexture; 57 | #endif 58 | 59 | #ifdef normalTextureFlag 60 | uniform sampler2D u_normalTexture; 61 | #endif 62 | 63 | #ifdef lightingFlag 64 | varying vec3 v_lightDiffuse; 65 | 66 | #if defined(ambientLightFlag) || defined(ambientCubemapFlag) || defined(sphericalHarmonicsFlag) 67 | #define ambientFlag 68 | #endif //ambientFlag 69 | 70 | #ifdef specularFlag 71 | varying vec3 v_lightSpecular; 72 | #endif //specularFlag 73 | 74 | #ifdef shadowMapFlag 75 | uniform sampler2D u_shadowTexture; 76 | uniform float u_shadowPCFOffset; 77 | varying vec3 v_shadowMapUv; 78 | #define separateAmbientFlag 79 | 80 | float getShadowness(vec2 offset) 81 | { 82 | const vec4 bitShifts = vec4(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 160581375.0); 83 | return step(v_shadowMapUv.z, dot(texture2D(u_shadowTexture, v_shadowMapUv.xy + offset), bitShifts));//+(1.0/255.0)); 84 | } 85 | 86 | float getShadow() 87 | { 88 | return (//getShadowness(vec2(0,0)) + 89 | getShadowness(vec2(u_shadowPCFOffset, u_shadowPCFOffset)) + 90 | getShadowness(vec2(-u_shadowPCFOffset, u_shadowPCFOffset)) + 91 | getShadowness(vec2(u_shadowPCFOffset, -u_shadowPCFOffset)) + 92 | getShadowness(vec2(-u_shadowPCFOffset, -u_shadowPCFOffset))) * 0.25; 93 | } 94 | #endif //shadowMapFlag 95 | 96 | #if defined(ambientFlag) && defined(separateAmbientFlag) 97 | varying vec3 v_ambientLight; 98 | #endif //separateAmbientFlag 99 | 100 | #endif //lightingFlag 101 | 102 | #ifdef fogFlag 103 | uniform vec4 u_fogColor; 104 | varying float v_fog; 105 | #endif // fogFlag 106 | 107 | uniform float u_time; 108 | 109 | #define TAU 6.28318530718 110 | #define MAX_ITER 3 111 | 112 | void main() { 113 | #if defined(normalFlag) 114 | vec3 normal = v_normal; 115 | #endif // normalFlag 116 | // WATER RIPPLE EFFECT 117 | float time = u_time * .1; 118 | // uv should be the 0-1 uv of texture... 119 | vec2 uv = 0.1 * v_diffuseUV; 120 | 121 | vec2 p = mod(uv*TAU, TAU)-250.0; 122 | // vec2 p = uv -250.; 123 | 124 | vec2 i = vec2(p); 125 | float c = 1.0; 126 | float inten = .005; 127 | 128 | for (int n = 0; n < MAX_ITER; n++) 129 | { 130 | float t = time * (1.0 - (3.5 / float(n+1))); 131 | i = p + vec2(cos(t - i.x) + sin(t + i.y), sin(t - i.y) + cos(t + i.x)); 132 | c += 1.0/length(vec2(p.x / (sin(i.x+t)/inten),p.y / (cos(i.y+t)/inten))); 133 | } 134 | c /= float(MAX_ITER); 135 | c = 1.17-pow(c, 1.4); 136 | vec3 color = vec3(pow(abs(c), 8.0)); 137 | color = clamp(color + vec3(0.0, 0.35, 0.5), 0.0, 1.0); 138 | 139 | vec4 diffuse = vec4(color, 1.0); 140 | 141 | #if (!defined(lightingFlag)) 142 | gl_FragColor.rgb = diffuse.rgb; 143 | #elif (!defined(specularFlag)) 144 | #if defined(ambientFlag) && defined(separateAmbientFlag) 145 | #ifdef shadowMapFlag 146 | gl_FragColor.rgb = (diffuse.rgb * (v_ambientLight + getShadow() * v_lightDiffuse)); 147 | //gl_FragColor.rgb = texture2D(u_shadowTexture, v_shadowMapUv.xy); 148 | #else 149 | gl_FragColor.rgb = (diffuse.rgb * (v_ambientLight + v_lightDiffuse)); 150 | #endif //shadowMapFlag 151 | #else 152 | #ifdef shadowMapFlag 153 | gl_FragColor.rgb = getShadow() * (diffuse.rgb * v_lightDiffuse); 154 | #else 155 | gl_FragColor.rgb = (diffuse.rgb * v_lightDiffuse); 156 | #endif //shadowMapFlag 157 | #endif 158 | #else 159 | #if defined(specularTextureFlag) && defined(specularColorFlag) 160 | vec3 specular = texture2D(u_specularTexture, v_specularUV).rgb * u_specularColor.rgb * v_lightSpecular; 161 | #elif defined(specularTextureFlag) 162 | vec3 specular = texture2D(u_specularTexture, v_specularUV).rgb * v_lightSpecular; 163 | #elif defined(specularColorFlag) 164 | vec3 specular = u_specularColor.rgb * v_lightSpecular; 165 | #else 166 | vec3 specular = v_lightSpecular; 167 | #endif 168 | 169 | #if defined(ambientFlag) && defined(separateAmbientFlag) 170 | #ifdef shadowMapFlag 171 | gl_FragColor.rgb = (diffuse.rgb * (getShadow() * v_lightDiffuse + v_ambientLight)) + specular; 172 | //gl_FragColor.rgb = texture2D(u_shadowTexture, v_shadowMapUv.xy); 173 | #else 174 | gl_FragColor.rgb = (diffuse.rgb * (v_lightDiffuse + v_ambientLight)) + specular; 175 | #endif //shadowMapFlag 176 | #else 177 | #ifdef shadowMapFlag 178 | gl_FragColor.rgb = getShadow() * ((diffuse.rgb * v_lightDiffuse) + specular); 179 | #else 180 | gl_FragColor.rgb = (diffuse.rgb * v_lightDiffuse) + specular; 181 | #endif //shadowMapFlag 182 | #endif 183 | #endif //lightingFlag 184 | 185 | #ifdef fogFlag 186 | gl_FragColor.rgb = mix(gl_FragColor.rgb, u_fogColor.rgb, v_fog); 187 | #endif // end fogFlag 188 | 189 | #ifdef blendedFlag 190 | gl_FragColor.a = diffuse.a * v_opacity; 191 | #ifdef alphaTestFlag 192 | if (gl_FragColor.a <= v_alphaTest) 193 | discard; 194 | #endif 195 | #else 196 | gl_FragColor.a = 1.0; 197 | #endif 198 | 199 | } 200 | -------------------------------------------------------------------------------- /core/assets/texture/dirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrenner/kotlin-voxel/1266cc7366fea7a069f3a545ac54cc0e753d457a/core/assets/texture/dirt.png -------------------------------------------------------------------------------- /core/assets/ui/league64.fnt: -------------------------------------------------------------------------------- 1 | info face="LeagueGothic-Regular" size=64 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=0,0 2 | common lineHeight=65 base=48 scaleW=512 scaleH=512 pages=1 packed=0 3 | page id=0 file="league64.png" 4 | chars count=94 5 | char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=48 xadvance=10 page=0 chnl=0 6 | char id=124 x=0 y=0 width=8 height=71 xoffset=3 yoffset=-6 xadvance=12 page=0 chnl=0 7 | char id=125 x=8 y=0 width=20 height=64 xoffset=2 yoffset=-1 xadvance=22 page=0 chnl=0 8 | char id=123 x=28 y=0 width=20 height=64 xoffset=2 yoffset=-1 xadvance=22 page=0 chnl=0 9 | char id=106 x=48 y=0 width=13 height=62 xoffset=-1 yoffset=0 xadvance=12 page=0 chnl=0 10 | char id=36 x=61 y=0 width=20 height=60 xoffset=2 yoffset=-4 xadvance=22 page=0 chnl=0 11 | char id=92 x=81 y=0 width=25 height=60 xoffset=0 yoffset=-6 xadvance=24 page=0 chnl=0 12 | char id=41 x=106 y=0 width=16 height=60 xoffset=1 yoffset=-2 xadvance=17 page=0 chnl=0 13 | char id=40 x=122 y=0 width=15 height=60 xoffset=2 yoffset=-2 xadvance=17 page=0 chnl=0 14 | char id=93 x=137 y=0 width=14 height=56 xoffset=2 yoffset=0 xadvance=17 page=0 chnl=0 15 | char id=91 x=151 y=0 width=14 height=56 xoffset=3 yoffset=0 xadvance=17 page=0 chnl=0 16 | char id=47 x=165 y=0 width=23 height=54 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=0 17 | char id=81 x=188 y=0 width=22 height=53 xoffset=2 yoffset=0 xadvance=23 page=0 chnl=0 18 | char id=57 x=210 y=0 width=19 height=51 xoffset=2 yoffset=0 xadvance=22 page=0 chnl=0 19 | char id=51 x=229 y=0 width=20 height=51 xoffset=1 yoffset=0 xadvance=21 page=0 chnl=0 20 | char id=38 x=249 y=0 width=26 height=50 xoffset=1 yoffset=-1 xadvance=26 page=0 chnl=0 21 | char id=37 x=275 y=0 width=29 height=50 xoffset=2 yoffset=-1 xadvance=32 page=0 chnl=0 22 | char id=64 x=304 y=0 width=25 height=50 xoffset=2 yoffset=0 xadvance=28 page=0 chnl=0 23 | char id=63 x=329 y=0 width=20 height=50 xoffset=1 yoffset=0 xadvance=20 page=0 chnl=0 24 | char id=33 x=349 y=0 width=8 height=50 xoffset=3 yoffset=0 xadvance=12 page=0 chnl=0 25 | char id=48 x=357 y=0 width=20 height=50 xoffset=2 yoffset=-1 xadvance=23 page=0 chnl=0 26 | char id=56 x=377 y=0 width=21 height=50 xoffset=1 yoffset=-1 xadvance=22 page=0 chnl=0 27 | char id=54 x=398 y=0 width=19 height=50 xoffset=2 yoffset=0 xadvance=22 page=0 chnl=0 28 | char id=53 x=417 y=0 width=19 height=50 xoffset=2 yoffset=1 xadvance=22 page=0 chnl=0 29 | char id=50 x=436 y=0 width=22 height=50 xoffset=1 yoffset=-1 xadvance=23 page=0 chnl=0 30 | char id=121 x=458 y=0 width=19 height=50 xoffset=1 yoffset=12 xadvance=20 page=0 chnl=0 31 | char id=113 x=477 y=0 width=19 height=50 xoffset=2 yoffset=11 xadvance=22 page=0 chnl=0 32 | char id=112 x=0 y=71 width=19 height=50 xoffset=2 yoffset=11 xadvance=22 page=0 chnl=0 33 | char id=103 x=19 y=71 width=23 height=50 xoffset=0 yoffset=11 xadvance=22 page=0 chnl=0 34 | char id=100 x=42 y=71 width=19 height=50 xoffset=2 yoffset=0 xadvance=22 page=0 chnl=0 35 | char id=98 x=61 y=71 width=19 height=50 xoffset=2 yoffset=0 xadvance=22 page=0 chnl=0 36 | char id=85 x=80 y=71 width=20 height=50 xoffset=2 yoffset=0 xadvance=23 page=0 chnl=0 37 | char id=83 x=100 y=71 width=21 height=50 xoffset=1 yoffset=-1 xadvance=22 page=0 chnl=0 38 | char id=79 x=121 y=71 width=20 height=50 xoffset=2 yoffset=-1 xadvance=23 page=0 chnl=0 39 | char id=74 x=141 y=71 width=13 height=50 xoffset=0 yoffset=0 xadvance=13 page=0 chnl=0 40 | char id=71 x=154 y=71 width=20 height=50 xoffset=2 yoffset=0 xadvance=23 page=0 chnl=0 41 | char id=69 x=174 y=71 width=18 height=50 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=0 42 | char id=67 x=192 y=71 width=20 height=50 xoffset=2 yoffset=0 xadvance=22 page=0 chnl=0 43 | char id=35 x=212 y=71 width=36 height=49 xoffset=2 yoffset=0 xadvance=39 page=0 chnl=0 44 | char id=55 x=248 y=71 width=19 height=49 xoffset=0 yoffset=0 xadvance=18 page=0 chnl=0 45 | char id=52 x=267 y=71 width=22 height=49 xoffset=1 yoffset=0 xadvance=22 page=0 chnl=0 46 | char id=49 x=289 y=71 width=14 height=49 xoffset=1 yoffset=0 xadvance=16 page=0 chnl=0 47 | char id=108 x=303 y=71 width=8 height=49 xoffset=3 yoffset=0 xadvance=12 page=0 chnl=0 48 | char id=107 x=311 y=71 width=20 height=49 xoffset=2 yoffset=0 xadvance=20 page=0 chnl=0 49 | char id=105 x=331 y=71 width=8 height=49 xoffset=3 yoffset=0 xadvance=12 page=0 chnl=0 50 | char id=104 x=339 y=71 width=19 height=49 xoffset=2 yoffset=0 xadvance=22 page=0 chnl=0 51 | char id=102 x=358 y=71 width=16 height=49 xoffset=0 yoffset=0 xadvance=15 page=0 chnl=0 52 | char id=90 x=374 y=71 width=20 height=49 xoffset=1 yoffset=0 xadvance=20 page=0 chnl=0 53 | char id=89 x=394 y=71 width=22 height=49 xoffset=0 yoffset=0 xadvance=21 page=0 chnl=0 54 | char id=88 x=416 y=71 width=23 height=49 xoffset=1 yoffset=0 xadvance=23 page=0 chnl=0 55 | char id=87 x=439 y=71 width=33 height=49 xoffset=0 yoffset=0 xadvance=32 page=0 chnl=0 56 | char id=86 x=472 y=71 width=24 height=49 xoffset=0 yoffset=0 xadvance=23 page=0 chnl=0 57 | char id=84 x=0 y=121 width=22 height=49 xoffset=0 yoffset=0 xadvance=20 page=0 chnl=0 58 | char id=82 x=22 y=121 width=21 height=49 xoffset=2 yoffset=0 xadvance=23 page=0 chnl=0 59 | char id=80 x=43 y=121 width=20 height=49 xoffset=3 yoffset=0 xadvance=23 page=0 chnl=0 60 | char id=78 x=63 y=121 width=22 height=49 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=0 61 | char id=77 x=85 y=121 width=28 height=49 xoffset=2 yoffset=0 xadvance=31 page=0 chnl=0 62 | char id=76 x=113 y=121 width=18 height=49 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=0 63 | char id=75 x=131 y=121 width=22 height=49 xoffset=3 yoffset=0 xadvance=24 page=0 chnl=0 64 | char id=73 x=153 y=121 width=8 height=49 xoffset=3 yoffset=0 xadvance=12 page=0 chnl=0 65 | char id=72 x=161 y=121 width=20 height=49 xoffset=3 yoffset=0 xadvance=24 page=0 chnl=0 66 | char id=70 x=181 y=121 width=18 height=49 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=0 67 | char id=68 x=199 y=121 width=21 height=49 xoffset=3 yoffset=0 xadvance=24 page=0 chnl=0 68 | char id=66 x=220 y=121 width=21 height=49 xoffset=3 yoffset=0 xadvance=24 page=0 chnl=0 69 | char id=65 x=241 y=121 width=25 height=49 xoffset=0 yoffset=0 xadvance=24 page=0 chnl=0 70 | char id=116 x=266 y=121 width=16 height=46 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=0 71 | char id=59 x=282 y=121 width=8 height=45 xoffset=3 yoffset=12 xadvance=11 page=0 chnl=0 72 | char id=101 x=290 y=121 width=20 height=39 xoffset=1 yoffset=12 xadvance=21 page=0 chnl=0 73 | char id=58 x=310 y=121 width=8 height=38 xoffset=3 yoffset=12 xadvance=11 page=0 chnl=0 74 | char id=117 x=318 y=121 width=19 height=38 xoffset=2 yoffset=12 xadvance=22 page=0 chnl=0 75 | char id=115 x=337 y=121 width=19 height=38 xoffset=1 yoffset=12 xadvance=19 page=0 chnl=0 76 | char id=111 x=356 y=121 width=19 height=38 xoffset=1 yoffset=11 xadvance=20 page=0 chnl=0 77 | char id=109 x=375 y=121 width=29 height=38 xoffset=2 yoffset=11 xadvance=32 page=0 chnl=0 78 | char id=99 x=404 y=121 width=19 height=38 xoffset=1 yoffset=12 xadvance=20 page=0 chnl=0 79 | char id=97 x=423 y=121 width=20 height=38 xoffset=1 yoffset=11 xadvance=21 page=0 chnl=0 80 | char id=122 x=443 y=121 width=18 height=37 xoffset=0 yoffset=12 xadvance=17 page=0 chnl=0 81 | char id=120 x=461 y=121 width=19 height=37 xoffset=1 yoffset=12 xadvance=20 page=0 chnl=0 82 | char id=119 x=480 y=121 width=30 height=37 xoffset=0 yoffset=12 xadvance=29 page=0 chnl=0 83 | char id=118 x=0 y=170 width=20 height=37 xoffset=0 yoffset=12 xadvance=19 page=0 chnl=0 84 | char id=114 x=20 y=170 width=15 height=37 xoffset=3 yoffset=12 xadvance=16 page=0 chnl=0 85 | char id=110 x=35 y=170 width=19 height=37 xoffset=2 yoffset=12 xadvance=22 page=0 chnl=0 86 | char id=62 x=54 y=170 width=30 height=36 xoffset=3 yoffset=8 xadvance=33 page=0 chnl=0 87 | char id=60 x=84 y=170 width=30 height=36 xoffset=3 yoffset=8 xadvance=33 page=0 chnl=0 88 | char id=43 x=114 y=170 width=31 height=29 xoffset=2 yoffset=11 xadvance=33 page=0 chnl=0 89 | char id=94 x=145 y=170 width=32 height=24 xoffset=2 yoffset=0 xadvance=35 page=0 chnl=0 90 | char id=61 x=177 y=170 width=30 height=20 xoffset=3 yoffset=15 xadvance=33 page=0 chnl=0 91 | char id=39 x=207 y=170 width=7 height=19 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=0 92 | char id=34 x=214 y=170 width=17 height=19 xoffset=2 yoffset=0 xadvance=17 page=0 chnl=0 93 | char id=42 x=231 y=170 width=18 height=18 xoffset=2 yoffset=0 xadvance=21 page=0 chnl=0 94 | char id=44 x=249 y=170 width=8 height=17 xoffset=2 yoffset=40 xadvance=10 page=0 chnl=0 95 | char id=126 x=257 y=170 width=36 height=13 xoffset=3 yoffset=-4 xadvance=40 page=0 chnl=0 96 | char id=46 x=293 y=170 width=8 height=9 xoffset=2 yoffset=40 xadvance=10 page=0 chnl=0 97 | char id=45 x=301 y=170 width=11 height=8 xoffset=2 yoffset=27 xadvance=13 page=0 chnl=0 98 | char id=96 x=312 y=170 width=11 height=8 xoffset=9 yoffset=0 xadvance=32 page=0 chnl=0 99 | char id=95 x=323 y=170 width=35 height=7 xoffset=2 yoffset=52 xadvance=37 page=0 chnl=0 100 | -------------------------------------------------------------------------------- /core/assets/ui/ubuntu-medium20.fnt: -------------------------------------------------------------------------------- 1 | info face="Ubuntu Medium" size=20 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=0,0 2 | common lineHeight=24 base=19 scaleW=512 scaleH=512 pages=1 packed=0 3 | page id=0 file="ubuntu-medium20.png" 4 | chars count=95 5 | char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=19 xadvance=5 page=0 chnl=0 6 | char id=127 x=0 y=0 width=11 height=25 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=0 7 | char id=42 x=11 y=0 width=11 height=25 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=0 8 | char id=126 x=22 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 9 | char id=38 x=34 y=0 width=15 height=25 xoffset=1 yoffset=0 xadvance=14 page=0 chnl=0 10 | char id=95 x=49 y=0 width=11 height=25 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=0 11 | char id=35 x=60 y=0 width=14 height=25 xoffset=1 yoffset=0 xadvance=13 page=0 chnl=0 12 | char id=61 x=74 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 13 | char id=43 x=86 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 14 | char id=37 x=98 y=0 width=19 height=25 xoffset=1 yoffset=0 xadvance=18 page=0 chnl=0 15 | char id=45 x=117 y=0 width=7 height=25 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=0 16 | char id=36 x=124 y=0 width=12 height=25 xoffset=2 yoffset=0 xadvance=11 page=0 chnl=0 17 | char id=94 x=136 y=0 width=12 height=25 xoffset=0 yoffset=0 xadvance=11 page=0 chnl=0 18 | char id=92 x=148 y=0 width=9 height=25 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0 19 | char id=64 x=157 y=0 width=20 height=25 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=0 20 | char id=47 x=177 y=0 width=9 height=25 xoffset=-1 yoffset=0 xadvance=8 page=0 chnl=0 21 | char id=124 x=186 y=0 width=7 height=25 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=0 22 | char id=62 x=193 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 23 | char id=60 x=205 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 24 | char id=125 x=217 y=0 width=8 height=25 xoffset=0 yoffset=0 xadvance=7 page=0 chnl=0 25 | char id=123 x=225 y=0 width=8 height=25 xoffset=1 yoffset=0 xadvance=7 page=0 chnl=0 26 | char id=93 x=233 y=0 width=8 height=25 xoffset=0 yoffset=0 xadvance=7 page=0 chnl=0 27 | char id=91 x=241 y=0 width=8 height=25 xoffset=2 yoffset=0 xadvance=7 page=0 chnl=0 28 | char id=41 x=249 y=0 width=8 height=25 xoffset=0 yoffset=0 xadvance=7 page=0 chnl=0 29 | char id=40 x=257 y=0 width=8 height=25 xoffset=2 yoffset=0 xadvance=7 page=0 chnl=0 30 | char id=58 x=265 y=0 width=6 height=25 xoffset=2 yoffset=0 xadvance=5 page=0 chnl=0 31 | char id=59 x=271 y=0 width=6 height=25 xoffset=1 yoffset=0 xadvance=5 page=0 chnl=0 32 | char id=44 x=277 y=0 width=6 height=25 xoffset=1 yoffset=0 xadvance=5 page=0 chnl=0 33 | char id=46 x=283 y=0 width=6 height=25 xoffset=2 yoffset=0 xadvance=5 page=0 chnl=0 34 | char id=39 x=289 y=0 width=6 height=25 xoffset=2 yoffset=0 xadvance=5 page=0 chnl=0 35 | char id=63 x=295 y=0 width=9 height=25 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0 36 | char id=96 x=304 y=0 width=9 height=25 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=0 37 | char id=33 x=313 y=0 width=7 height=25 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=0 38 | char id=34 x=320 y=0 width=10 height=25 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=0 39 | char id=48 x=330 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 40 | char id=57 x=342 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 41 | char id=56 x=354 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 42 | char id=55 x=366 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 43 | char id=54 x=378 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 44 | char id=53 x=390 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 45 | char id=52 x=402 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 46 | char id=51 x=414 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 47 | char id=50 x=426 y=0 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 48 | char id=49 x=438 y=0 width=12 height=25 xoffset=2 yoffset=0 xadvance=11 page=0 chnl=0 49 | char id=122 x=450 y=0 width=11 height=25 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=0 50 | char id=121 x=461 y=0 width=11 height=25 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=0 51 | char id=120 x=472 y=0 width=12 height=25 xoffset=0 yoffset=0 xadvance=11 page=0 chnl=0 52 | char id=119 x=484 y=0 width=17 height=25 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=0 53 | char id=118 x=0 y=25 width=12 height=25 xoffset=0 yoffset=0 xadvance=11 page=0 chnl=0 54 | char id=117 x=12 y=25 width=13 height=25 xoffset=1 yoffset=0 xadvance=12 page=0 chnl=0 55 | char id=116 x=25 y=25 width=9 height=25 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=0 56 | char id=115 x=34 y=25 width=10 height=25 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=0 57 | char id=114 x=44 y=25 width=9 height=25 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=0 58 | char id=113 x=53 y=25 width=13 height=25 xoffset=1 yoffset=0 xadvance=12 page=0 chnl=0 59 | char id=112 x=66 y=25 width=13 height=25 xoffset=2 yoffset=0 xadvance=12 page=0 chnl=0 60 | char id=111 x=79 y=25 width=13 height=25 xoffset=1 yoffset=0 xadvance=12 page=0 chnl=0 61 | char id=110 x=92 y=25 width=13 height=25 xoffset=2 yoffset=0 xadvance=12 page=0 chnl=0 62 | char id=109 x=105 y=25 width=18 height=25 xoffset=2 yoffset=0 xadvance=17 page=0 chnl=0 63 | char id=108 x=123 y=25 width=7 height=25 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=0 64 | char id=107 x=130 y=25 width=12 height=25 xoffset=2 yoffset=0 xadvance=11 page=0 chnl=0 65 | char id=106 x=142 y=25 width=8 height=25 xoffset=-1 yoffset=0 xadvance=6 page=0 chnl=0 66 | char id=105 x=150 y=25 width=7 height=25 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=0 67 | char id=104 x=157 y=25 width=13 height=25 xoffset=2 yoffset=0 xadvance=12 page=0 chnl=0 68 | char id=103 x=170 y=25 width=13 height=25 xoffset=1 yoffset=0 xadvance=12 page=0 chnl=0 69 | char id=102 x=183 y=25 width=9 height=25 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=0 70 | char id=101 x=192 y=25 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 71 | char id=100 x=204 y=25 width=13 height=25 xoffset=1 yoffset=0 xadvance=12 page=0 chnl=0 72 | char id=99 x=217 y=25 width=10 height=25 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=0 73 | char id=98 x=227 y=25 width=13 height=25 xoffset=2 yoffset=0 xadvance=12 page=0 chnl=0 74 | char id=97 x=240 y=25 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 75 | char id=90 x=252 y=25 width=13 height=25 xoffset=1 yoffset=0 xadvance=12 page=0 chnl=0 76 | char id=89 x=265 y=25 width=14 height=25 xoffset=1 yoffset=0 xadvance=13 page=0 chnl=0 77 | char id=88 x=279 y=25 width=14 height=25 xoffset=0 yoffset=0 xadvance=13 page=0 chnl=0 78 | char id=87 x=293 y=25 width=20 height=25 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=0 79 | char id=86 x=313 y=25 width=15 height=25 xoffset=1 yoffset=0 xadvance=14 page=0 chnl=0 80 | char id=85 x=328 y=25 width=15 height=25 xoffset=2 yoffset=0 xadvance=14 page=0 chnl=0 81 | char id=84 x=343 y=25 width=13 height=25 xoffset=1 yoffset=0 xadvance=12 page=0 chnl=0 82 | char id=83 x=356 y=25 width=12 height=25 xoffset=1 yoffset=0 xadvance=11 page=0 chnl=0 83 | char id=82 x=368 y=25 width=14 height=25 xoffset=2 yoffset=0 xadvance=13 page=0 chnl=0 84 | char id=81 x=382 y=25 width=17 height=25 xoffset=1 yoffset=0 xadvance=16 page=0 chnl=0 85 | char id=80 x=399 y=25 width=13 height=25 xoffset=2 yoffset=0 xadvance=12 page=0 chnl=0 86 | char id=79 x=412 y=25 width=17 height=25 xoffset=1 yoffset=0 xadvance=16 page=0 chnl=0 87 | char id=78 x=429 y=25 width=16 height=25 xoffset=2 yoffset=0 xadvance=15 page=0 chnl=0 88 | char id=77 x=445 y=25 width=19 height=25 xoffset=1 yoffset=0 xadvance=18 page=0 chnl=0 89 | char id=76 x=464 y=25 width=12 height=25 xoffset=2 yoffset=0 xadvance=11 page=0 chnl=0 90 | char id=75 x=476 y=25 width=14 height=25 xoffset=2 yoffset=0 xadvance=13 page=0 chnl=0 91 | char id=74 x=490 y=25 width=11 height=25 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=0 92 | char id=73 x=501 y=25 width=7 height=25 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=0 93 | char id=72 x=0 y=50 width=15 height=25 xoffset=2 yoffset=0 xadvance=14 page=0 chnl=0 94 | char id=71 x=15 y=50 width=15 height=25 xoffset=1 yoffset=0 xadvance=14 page=0 chnl=0 95 | char id=70 x=30 y=50 width=12 height=25 xoffset=2 yoffset=0 xadvance=11 page=0 chnl=0 96 | char id=69 x=42 y=50 width=13 height=25 xoffset=2 yoffset=0 xadvance=12 page=0 chnl=0 97 | char id=68 x=55 y=50 width=16 height=25 xoffset=2 yoffset=0 xadvance=15 page=0 chnl=0 98 | char id=67 x=71 y=50 width=14 height=25 xoffset=1 yoffset=0 xadvance=13 page=0 chnl=0 99 | char id=66 x=85 y=50 width=14 height=25 xoffset=2 yoffset=0 xadvance=13 page=0 chnl=0 100 | char id=65 x=99 y=50 width=15 height=25 xoffset=-1 yoffset=0 xadvance=14 page=0 chnl=0 101 | -------------------------------------------------------------------------------- /core/assets/ui/ui.atlas: -------------------------------------------------------------------------------- 1 | 2 | ui.png 3 | size: 1024,256 4 | format: RGBA8888 5 | filter: Nearest,Nearest 6 | repeat: none 7 | button-checked 8 | rotate: false 9 | xy: 927, 204 10 | size: 39, 48 11 | split: 18, 18, 21, 21 12 | orig: 39, 48 13 | offset: 0, 0 14 | index: -1 15 | button-checked-over 16 | rotate: false 17 | xy: 814, 140 18 | size: 39, 48 19 | split: 18, 18, 21, 21 20 | orig: 39, 48 21 | offset: 0, 0 22 | index: -1 23 | button-disabled 24 | rotate: false 25 | xy: 968, 204 26 | size: 39, 48 27 | split: 18, 18, 21, 21 28 | orig: 39, 48 29 | offset: 0, 0 30 | index: -1 31 | button-down 32 | rotate: false 33 | xy: 750, 190 34 | size: 62, 62 35 | split: 14, 14, 15, 13 36 | orig: 62, 62 37 | offset: 0, 0 38 | index: -1 39 | button-over 40 | rotate: false 41 | xy: 750, 126 42 | size: 62, 62 43 | split: 14, 14, 15, 13 44 | orig: 62, 62 45 | offset: 0, 0 46 | index: -1 47 | button-up 48 | rotate: false 49 | xy: 814, 190 50 | size: 62, 62 51 | split: 14, 14, 15, 13 52 | orig: 62, 62 53 | offset: 0, 0 54 | index: -1 55 | check-checked 56 | rotate: false 57 | xy: 791, 4 58 | size: 39, 48 59 | split: 18, 18, 21, 21 60 | orig: 39, 48 61 | offset: 0, 0 62 | index: -1 63 | check-up 64 | rotate: false 65 | xy: 791, 4 66 | size: 39, 48 67 | split: 18, 18, 21, 21 68 | orig: 39, 48 69 | offset: 0, 0 70 | index: -1 71 | check-checked-over 72 | rotate: false 73 | xy: 750, 4 74 | size: 39, 48 75 | split: 18, 18, 21, 21 76 | orig: 39, 48 77 | offset: 0, 0 78 | index: -1 79 | check-disabled 80 | rotate: false 81 | xy: 855, 140 82 | size: 39, 48 83 | split: 18, 18, 21, 21 84 | orig: 39, 48 85 | offset: 0, 0 86 | index: -1 87 | check-down 88 | rotate: false 89 | xy: 853, 90 90 | size: 39, 48 91 | split: 18, 18, 21, 21 92 | orig: 39, 48 93 | offset: 0, 0 94 | index: -1 95 | check-over 96 | rotate: false 97 | xy: 913, 154 98 | size: 39, 48 99 | split: 18, 18, 21, 21 100 | orig: 39, 48 101 | offset: 0, 0 102 | index: -1 103 | checkbox-off 104 | rotate: false 105 | xy: 818, 57 106 | size: 26, 26 107 | orig: 48, 48 108 | offset: 11, 11 109 | index: -1 110 | checkbox-on 111 | rotate: false 112 | xy: 141, 12 113 | size: 35, 32 114 | orig: 48, 48 115 | offset: 11, 11 116 | index: -1 117 | checkbox-over 118 | rotate: false 119 | xy: 373, 12 120 | size: 32, 32 121 | orig: 48, 48 122 | offset: 8, 8 123 | index: -1 124 | icon-blitz 125 | rotate: false 126 | xy: 750, 54 127 | size: 32, 70 128 | orig: 49, 80 129 | offset: 8, 5 130 | index: -1 131 | icon-blitz-dark 132 | rotate: false 133 | xy: 784, 54 134 | size: 32, 70 135 | orig: 49, 80 136 | offset: 8, 5 137 | index: -1 138 | label 139 | rotate: false 140 | xy: 81, 7 141 | size: 15, 14 142 | split: 7, 7, 7, 6 143 | orig: 15, 14 144 | offset: 0, 0 145 | index: -1 146 | league64 147 | rotate: false 148 | xy: 0, 46 149 | size: 508, 206 150 | orig: 512, 512 151 | offset: 0, 306 152 | index: -1 153 | list 154 | rotate: false 155 | xy: 245, 2 156 | size: 30, 42 157 | split: 14, 15, 21, 20 158 | orig: 30, 42 159 | offset: 0, 0 160 | index: -1 161 | list-selection 162 | rotate: false 163 | xy: 213, 2 164 | size: 30, 42 165 | split: 14, 15, 21, 20 166 | orig: 30, 42 167 | offset: 0, 0 168 | index: -1 169 | tree-selection 170 | rotate: false 171 | xy: 213, 2 172 | size: 30, 42 173 | split: 14, 15, 21, 20 174 | orig: 30, 42 175 | offset: 0, 0 176 | index: -1 177 | tree-over 178 | rotate: false 179 | xy: 213, 2 180 | size: 30, 42 181 | split: 14, 15, 21, 20 182 | orig: 30, 42 183 | offset: 0, 0 184 | index: -1 185 | scroll 186 | rotate: false 187 | xy: 0, 0 188 | size: 3, 3 189 | split: 1, 1, 1, 1 190 | orig: 3, 3 191 | offset: 0, 0 192 | index: -1 193 | scroll-corner 194 | rotate: false 195 | xy: 0, 5 196 | size: 39, 39 197 | split: 19, 19, 19, 19 198 | orig: 39, 39 199 | offset: 0, 0 200 | index: -1 201 | scroll-horizontal 202 | rotate: false 203 | xy: 867, 9 204 | size: 24, 24 205 | split: 11, 11, 11, 11 206 | orig: 24, 24 207 | offset: 0, 0 208 | index: -1 209 | scroll-vertical 210 | rotate: false 211 | xy: 867, 9 212 | size: 24, 24 213 | split: 11, 11, 11, 11 214 | orig: 24, 24 215 | offset: 0, 0 216 | index: -1 217 | scroll-horizontal-knob 218 | rotate: false 219 | xy: 475, 20 220 | size: 24, 24 221 | split: 11, 11, 11, 11 222 | orig: 24, 24 223 | offset: 0, 0 224 | index: -1 225 | scroll-vertical-knob 226 | rotate: false 227 | xy: 475, 20 228 | size: 24, 24 229 | split: 11, 11, 11, 11 230 | orig: 24, 24 231 | offset: 0, 0 232 | index: -1 233 | select 234 | rotate: false 235 | xy: 867, 35 236 | size: 33, 53 237 | split: 6, 24, 25, 25 238 | orig: 33, 53 239 | offset: 0, 0 240 | index: -1 241 | select-list-opaque 242 | rotate: false 243 | xy: 277, 2 244 | size: 30, 42 245 | split: 14, 15, 21, 20 246 | orig: 30, 42 247 | offset: 0, 0 248 | index: -1 249 | select-list-selection 250 | rotate: false 251 | xy: 309, 2 252 | size: 30, 42 253 | split: 14, 15, 21, 20 254 | orig: 30, 42 255 | offset: 0, 0 256 | index: -1 257 | select-open 258 | rotate: false 259 | xy: 818, 85 260 | size: 33, 53 261 | split: 6, 24, 25, 25 262 | orig: 33, 53 263 | offset: 0, 0 264 | index: -1 265 | select-over 266 | rotate: false 267 | xy: 832, 2 268 | size: 33, 53 269 | split: 6, 24, 25, 25 270 | orig: 33, 53 271 | offset: 0, 0 272 | index: -1 273 | slider-horizontal 274 | rotate: false 275 | xy: 492, 0 276 | size: 15, 18 277 | split: 0, 0, 0, 15 278 | orig: 15, 18 279 | offset: 0, 0 280 | index: -1 281 | slider-horizontal-empty 282 | rotate: false 283 | xy: 475, 0 284 | size: 15, 18 285 | split: 0, 0, 0, 15 286 | orig: 15, 18 287 | offset: 0, 0 288 | index: -1 289 | slider-horizontal-filled 290 | rotate: false 291 | xy: 41, 23 292 | size: 57, 21 293 | split: 8, 8, 8, 8 294 | orig: 57, 21 295 | offset: 0, 0 296 | index: -1 297 | slider-horizontal-knob 298 | rotate: false 299 | xy: 878, 205 300 | size: 47, 47 301 | orig: 48, 48 302 | offset: 0, 1 303 | index: -1 304 | slider-vertical-knob 305 | rotate: false 306 | xy: 878, 205 307 | size: 47, 47 308 | orig: 48, 48 309 | offset: 0, 1 310 | index: -1 311 | slider-vertical 312 | rotate: false 313 | xy: 995, 187 314 | size: 18, 15 315 | split: 0, 15, 0, 0 316 | orig: 18, 15 317 | offset: 0, 0 318 | index: -1 319 | slider-vertical-empty 320 | rotate: false 321 | xy: 41, 6 322 | size: 18, 15 323 | split: 0, 15, 0, 0 324 | orig: 18, 15 325 | offset: 0, 0 326 | index: -1 327 | slider-vertical-filled 328 | rotate: false 329 | xy: 61, 6 330 | size: 18, 15 331 | split: 0, 15, 0, 0 332 | orig: 18, 15 333 | offset: 0, 0 334 | index: -1 335 | splitpane-horizontal 336 | rotate: false 337 | xy: 100, 11 338 | size: 39, 33 339 | split: 19, 19, 6, 24 340 | orig: 39, 33 341 | offset: 0, 0 342 | index: -1 343 | splitpane-vertical 344 | rotate: false 345 | xy: 178, 5 346 | size: 33, 39 347 | split: 6, 24, 19, 19 348 | orig: 33, 39 349 | offset: 0, 0 350 | index: -1 351 | table-background 352 | rotate: false 353 | xy: 510, 13 354 | size: 238, 239 355 | orig: 240, 240 356 | offset: 0, 1 357 | index: -1 358 | textfield 359 | rotate: false 360 | xy: 954, 104 361 | size: 39, 48 362 | split: 18, 18, 30, 15 363 | pad: 18, 18, 10, 12 364 | orig: 39, 48 365 | offset: 0, 0 366 | index: -1 367 | textfield-cursor 368 | rotate: false 369 | xy: 5, 0 370 | size: 3, 3 371 | split: 1, 1, 1, 1 372 | orig: 3, 3 373 | offset: 0, 0 374 | index: -1 375 | textfield-disabled 376 | rotate: false 377 | xy: 954, 154 378 | size: 39, 48 379 | split: 18, 18, 30, 15 380 | pad: 18, 18, 10, 12 381 | orig: 39, 48 382 | offset: 0, 0 383 | index: -1 384 | textfield-focused 385 | rotate: false 386 | xy: 913, 104 387 | size: 39, 48 388 | split: 18, 17, 30, 14 389 | pad: 18, 17, 10, 11 390 | orig: 39, 48 391 | offset: 0, 0 392 | index: -1 393 | textfield-selection 394 | rotate: false 395 | xy: 818, 54 396 | size: 1, 1 397 | orig: 1, 1 398 | offset: 0, 0 399 | index: -1 400 | tree 401 | rotate: false 402 | xy: 341, 2 403 | size: 30, 42 404 | split: 14, 15, 21, 20 405 | orig: 30, 42 406 | offset: 0, 0 407 | index: -1 408 | tree-minus 409 | rotate: false 410 | xy: 407, 12 411 | size: 32, 32 412 | orig: 48, 48 413 | offset: 8, 8 414 | index: -1 415 | tree-plus 416 | rotate: false 417 | xy: 441, 12 418 | size: 32, 32 419 | orig: 48, 48 420 | offset: 8, 8 421 | index: -1 422 | white 423 | rotate: false 424 | xy: 821, 54 425 | size: 1, 1 426 | orig: 1, 1 427 | offset: 0, 0 428 | index: -1 429 | window 430 | rotate: false 431 | xy: 896, 133 432 | size: 15, 70 433 | split: 7, 7, 63, 6 434 | orig: 15, 70 435 | offset: 0, 0 436 | index: -1 437 | -------------------------------------------------------------------------------- /core/assets/ui/ui.json: -------------------------------------------------------------------------------- 1 | { 2 | com.badlogic.gdx.graphics.g2d.BitmapFont: { 3 | league64: {file: league64.fnt}, 4 | default-font: league64, 5 | thin-font: default-font 6 | }, 7 | 8 | com.badlogic.gdx.graphics.Color: { 9 | green: { a: 1, b: 0, g: 1, r: 0 }, 10 | white: { a: 1, b: 1, g: 1, r: 1 }, 11 | red: { a: 1, b: 0, g: 0, r: 1 }, 12 | black: { a: 1, b: 0, g: 0, r: 0 } 13 | }, 14 | 15 | com.badlogic.gdx.scenes.scene2d.ui.Skin$TintedDrawable: { 16 | dialogDim: { name: white, color: { r: 0, g: 0, b: 0, a: 1.0 } } 17 | }, 18 | com.badlogic.gdx.scenes.scene2d.ui.Button$ButtonStyle: { 19 | default: { up: button-up, down: button-down, over: button-over, disabled: button-disabled}, 20 | toggle: { up: button-up, down: button-down, over: button-over, checked: button-checked, checkedOver: button-checked-over, disabled: button-disabled } 21 | }, 22 | com.badlogic.gdx.scenes.scene2d.ui.TextButton$TextButtonStyle: { 23 | default: { up: button-up, down: button-down, over: button-over, disabled: button-disabled, font: default-font, fontColor: white , downFontColor: white, overFontColor: white, disabledFontColor: white}, 24 | toggle: { up: button-up, down: button-down, over: button-over, checked: button-checked, checkedOver: button-checked-over, disabled: button-disabled, font: default-font, fontColor: white , downFontColor: white, overFontColor: white, checkedFontColor: white, checkedOverFontColor: white, disabledFontColor: white }, 25 | default-thin: { up: button-up, down: button-down, over: button-over, disabled: button-disabled, font: thin-font, fontColor: white , downFontColor: white, overFontColor: white, disabledFontColor: white}, 26 | toggle-thin: { up: button-up, down: button-down, over: button-over, checked: button-checked, checkedOver: button-checked-over, disabled: button-disabled, font: thin-font, fontColor: white , downFontColor: white, overFontColor: white, checkedFontColor: white, checkedOverFontColor: white, disabledFontColor: white } 27 | }, 28 | com.badlogic.gdx.scenes.scene2d.ui.ImageButton$ImageButtonStyle: { 29 | default: { up: button-up, down: button-down, over: button-over, disabled: button-disabled, imageUp: icon-blitz, imageDown: icon-blitz, imageOver: icon-blitz, imageDisabled: icon-blitz}, 30 | toggle: { up: button-up, down: button-down, over: button-over, checked: button-checked, checkedOver: button-checked-over, disabled: button-disabled,imageUp: icon-blitz, imageDown: icon-blitz, imageOver: icon-blitz, imageChecked: icon-blitz-dark, imageCheckedOver: icon-blitz-dark, imageDisabled: icon-blitz } 31 | }, 32 | com.badlogic.gdx.scenes.scene2d.ui.ScrollPane$ScrollPaneStyle: { 33 | default: { vScroll: scroll-vertical, hScrollKnob: scroll-horizontal-knob, background: select-list-opaque, hScroll: scroll-horizontal, vScrollKnob: scroll-vertical-knob, corner: scroll-corner} 34 | }, 35 | com.badlogic.gdx.scenes.scene2d.ui.List$ListStyle: { 36 | default: { selection: select-list-selection, font: default-font}, 37 | default-thin: { selection: select-list-selection, font: default-font} 38 | }, 39 | com.badlogic.gdx.scenes.scene2d.ui.SelectBox$SelectBoxStyle: { 40 | default: { background: select, backgroundOver: select-over, backgroundOpen: select-open, font: thin-font, fontColor: white , 41 | scrollStyle: default, 42 | listStyle: default 43 | }, 44 | default-thin: { background: select, backgroundOver: select-over, backgroundOpen: select-open, font: thin-font, fontColor: white } 45 | }, 46 | com.badlogic.gdx.scenes.scene2d.ui.SplitPane$SplitPaneStyle: { 47 | default-vertical: { handle: splitpane-vertical }, 48 | default-horizontal: { handle: splitpane-horizontal } 49 | }, 50 | com.badlogic.gdx.scenes.scene2d.ui.Window$WindowStyle: { 51 | default: { titleFont: default-font, background: window, titleFontColor: white }, 52 | dialog: { titleFont: default-font, background: window, titleFontColor: white, stageBackground: dialogDim }, 53 | default-thin: { titleFont: thin-font, background: window, titleFontColor: white }, 54 | dialog-thin: { titleFont: thin-font, background: window, titleFontColor: white, stageBackground: dialogDim } 55 | }, 56 | com.badlogic.gdx.scenes.scene2d.ui.Slider$SliderStyle: { 57 | default-horizontal: { background: slider-horizontal, knob: slider-horizontal-knob}, 58 | default-vertical: { background: slider-vertical, knob: slider-vertical-knob}, 59 | left-horizontal: { background: slider-horizontal, knob: slider-horizontal-knob, knobBefore: slider-horizontal-filled, knobAfter: slider-horizontal-empty}, 60 | right-horizontal: { background: slider-horizontal, knob: slider-horizontal-knob, knobBefore: slider-horizontal-empty, knobAfter: slider-horizontal-filled}, 61 | up-vertical: { background: slider-vertical, knob: slider-vertical-knob, knobBefore: slider-vertical-filled, knobAfter: slider-vertical-empty}, 62 | down-vertical: { background: slider-vertical, knob: slider-vertical-knob, knobBefore: slider-vertical-empty, knobAfter: slider-vertical-filled}, 63 | }, 64 | com.badlogic.gdx.scenes.scene2d.ui.Label$LabelStyle: { 65 | default: { font: default-font, fontColor: white , background: label}, 66 | default-thin: { font: thin-font, fontColor: white , background: label} 67 | }, 68 | com.badlogic.gdx.scenes.scene2d.ui.TextField$TextFieldStyle: { 69 | default: { selection: textfield-selection, background: textfield, focusedBackground: textfield-focused, disabledBackground: textfield-disabled, font: default-font, fontColor: white, focusedFontColor: white, disabledFontColor: white, cursor: textfield-cursor, messageFont: default-font, messageFontColor: white }, 70 | default-thin: { selection: textfield-selection, background: textfield, focusedBackground: textfield-focused, disabledBackground: textfield-disabled, font: thin-font, fontColor: white, focusedFontColor: white, disabledFontColor: white, cursor: textfield-cursor, messageFont: default-font, messageFontColor: white } 71 | }, 72 | com.badlogic.gdx.scenes.scene2d.ui.CheckBox$CheckBoxStyle: { 73 | default: { up: check-up, down: check-down, over: check-over, checked: check-checked, checkedOver: check-checked-over, disabled: check-disabled, font: default-font, fontColor: white , downFontColor: white, overFontColor: white, checkedFontColor: white, checkedOverFontColor: white, disabledFontColor: white, checkboxOn: checkbox-on, checkboxOff: checkbox-off, checkboxOver: checkbox-over}, 74 | default-thin: { up: check-up, down: check-down, over: check-over, checked: check-checked, checkedOver: check-checked-over, disabled: check-disabled, font: thin-font, fontColor: white , downFontColor: white, overFontColor: white, checkedFontColor: white, checkedOverFontColor: white, disabledFontColor: white, checkboxOn: checkbox-on, checkboxOff: checkbox-off, checkboxOver: checkbox-over} 75 | }, 76 | com.badlogic.gdx.scenes.scene2d.ui.List$ListStyle: { 77 | default: { fontColorUnselected: white, selection: list-selection, fontColorSelected: white, font: default-font }, 78 | default-thin: { fontColorUnselected: white, selection: list-selection, fontColorSelected: white, font: thin-font } 79 | }, 80 | com.badlogic.gdx.scenes.scene2d.ui.Touchpad$TouchpadStyle: { 81 | default: { background: label, knob: slider-horizontal-knob } 82 | }, 83 | com.badlogic.gdx.scenes.scene2d.ui.Tree$TreeStyle: { 84 | default: { minus: tree-minus, plus: tree-plus, over: tree-over, selection: tree-selection , background: scroll} 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /core/assets/ui/ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrenner/kotlin-voxel/1266cc7366fea7a069f3a545ac54cc0e753d457a/core/assets/ui/ui.png -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "java" 2 | 3 | sourceCompatibility = 1.7 4 | [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' 5 | 6 | sourceSets.main.java.srcDirs = [ "src/main/java", "src/main/kotlin" ] 7 | 8 | 9 | eclipse.project { 10 | name = appName + "-core" 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: org.jrenner.learngl.desktop.DesktopLauncher 3 | 4 | -------------------------------------------------------------------------------- /core/src/main/java/org/jrenner/RenStringBuilder.java: -------------------------------------------------------------------------------- 1 | package org.jrenner; 2 | 3 | import com.badlogic.gdx.utils.StringBuilder; 4 | 5 | public class RenStringBuilder extends StringBuilder { 6 | 7 | /*** 8 | * Kotlin has trouble with property 'length' and function 'length()', so we wrap it with this function and class 9 | */ 10 | public int sbLength() { 11 | return this.length(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/org/jrenner/learngl/SimplexNoise.java: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl;/* 2 | * A speed-improved simplex noise algorithm for 2D, 3D and 4D in Java. 3 | * 4 | * Based on example code by Stefan Gustavson (stegu@itn.liu.se). 5 | * Optimisations by Peter Eastman (peastman@drizzle.stanford.edu). 6 | * Better rank ordering method by Stefan Gustavson in 2012. 7 | * 8 | * This could be speeded up even further, but it's useful as it is. 9 | * 10 | * Version 2012-03-09 11 | * 12 | * This code was placed in the public domain by its original author, 13 | * Stefan Gustavson. You may use it as you see fit, but 14 | * attribution is appreciated. 15 | * 16 | * 3D+4D implementations removed and other methods added by github.com/jrenner 2015-03-08 17 | * 18 | */ 19 | 20 | public class SimplexNoise { // Simplex noise in 2D, 3D and 4D 21 | private static Grad grad3[] = {new Grad(1,1,0),new Grad(-1,1,0),new Grad(1,-1,0),new Grad(-1,-1,0), 22 | new Grad(1,0,1),new Grad(-1,0,1),new Grad(1,0,-1),new Grad(-1,0,-1), 23 | new Grad(0,1,1),new Grad(0,-1,1),new Grad(0,1,-1),new Grad(0,-1,-1)}; 24 | 25 | /*private static Grad grad4[]= {new Grad(0,1,1,1),new Grad(0,1,1,-1),new Grad(0,1,-1,1),new Grad(0,1,-1,-1), 26 | new Grad(0,-1,1,1),new Grad(0,-1,1,-1),new Grad(0,-1,-1,1),new Grad(0,-1,-1,-1), 27 | new Grad(1,0,1,1),new Grad(1,0,1,-1),new Grad(1,0,-1,1),new Grad(1,0,-1,-1), 28 | new Grad(-1,0,1,1),new Grad(-1,0,1,-1),new Grad(-1,0,-1,1),new Grad(-1,0,-1,-1), 29 | new Grad(1,1,0,1),new Grad(1,1,0,-1),new Grad(1,-1,0,1),new Grad(1,-1,0,-1), 30 | new Grad(-1,1,0,1),new Grad(-1,1,0,-1),new Grad(-1,-1,0,1),new Grad(-1,-1,0,-1), 31 | new Grad(1,1,1,0),new Grad(1,1,-1,0),new Grad(1,-1,1,0),new Grad(1,-1,-1,0), 32 | new Grad(-1,1,1,0),new Grad(-1,1,-1,0),new Grad(-1,-1,1,0),new Grad(-1,-1,-1,0)};*/ 33 | 34 | private static short p[] = {151,160,137,91,90,15, 35 | 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 36 | 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 37 | 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 38 | 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 39 | 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 40 | 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 41 | 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 42 | 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 43 | 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 44 | 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 45 | 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 46 | 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180}; 47 | // To remove the need for index wrapping, double the permutation table length 48 | private static short perm[] = new short[512]; 49 | private static short permMod12[] = new short[512]; 50 | static { 51 | for(int i=0; i<512; i++) 52 | { 53 | perm[i]=p[i & 255]; 54 | permMod12[i] = (short)(perm[i] % 12); 55 | } 56 | } 57 | 58 | // Skewing and unskewing factors for 2, 3, and 4 dimensions 59 | private static final double F2 = 0.5*(Math.sqrt(3.0)-1.0); 60 | private static final double G2 = (3.0-Math.sqrt(3.0))/6.0; 61 | /* private static final double F3 = 1.0/3.0; 62 | private static final double G3 = 1.0/6.0; 63 | private static final double F4 = (Math.sqrt(5.0)-1.0)/4.0; 64 | private static final double G4 = (5.0-Math.sqrt(5.0))/20.0;*/ 65 | 66 | // This method is a *lot* faster than using (int)Math.floor(x) 67 | private static int fastfloor(double x) { 68 | int xi = (int)x; 69 | return xy0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) 98 | else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) 99 | // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and 100 | // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where 101 | // c = (3-sqrt(3))/6 102 | double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords 103 | double y1 = y0 - j1 + G2; 104 | double x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords 105 | double y2 = y0 - 1.0 + 2.0 * G2; 106 | // Work out the hashed gradient indices of the three simplex corners 107 | int ii = i & 255; 108 | int jj = j & 255; 109 | int gi0 = permMod12[ii+perm[jj]]; 110 | int gi1 = permMod12[ii+i1+perm[jj+j1]]; 111 | int gi2 = permMod12[ii+1+perm[jj+1]]; 112 | // Calculate the contribution from the three corners 113 | double t0 = 0.5 - x0*x0-y0*y0; 114 | if(t0<0) n0 = 0.0; 115 | else { 116 | t0 *= t0; 117 | n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient 118 | } 119 | double t1 = 0.5 - x1*x1-y1*y1; 120 | if(t1<0) n1 = 0.0; 121 | else { 122 | t1 *= t1; 123 | n1 = t1 * t1 * dot(grad3[gi1], x1, y1); 124 | } 125 | double t2 = 0.5 - x2*x2-y2*y2; 126 | if(t2<0) n2 = 0.0; 127 | else { 128 | t2 *= t2; 129 | n2 = t2 * t2 * dot(grad3[gi2], x2, y2); 130 | } 131 | // Add contributions from each corner to get the final noise value. 132 | // The result is scaled to return values in the interval [-1,1]. 133 | return 70.0 * (n0 + n1 + n2); 134 | } 135 | 136 | // Inner class to speed upp gradient computations 137 | // (array access is a lot slower than member access) 138 | private static class Grad 139 | { 140 | //double x, y, z, w; 141 | double x, y, z; 142 | 143 | Grad(double x, double y, double z) 144 | { 145 | this.x = x; 146 | this.y = y; 147 | this.z = z; 148 | } 149 | 150 | /* Grad(double x, double y, double z, double w) 151 | { 152 | this.x = x; 153 | this.y = y; 154 | this.z = z; 155 | this.w = w; 156 | }*/ 157 | } 158 | 159 | public static void generateSimplexNoise(int startX, int startY, int width, int height, float frequency, float[][] storage){ 160 | for(int i = 0, x = startX; x < startX + width; i++, x++){ 161 | for(int j = 0, y = startY; y < startY + height; j++, y++){ 162 | storage[i][j] = (float) noise(x * frequency,y * frequency); 163 | storage[i][j] = (storage[i][j] + 1) / 2; //generate values between 0 and 1 164 | } 165 | } 166 | } 167 | } 168 | 169 | -------------------------------------------------------------------------------- /core/src/main/java/org/jrenner/learngl/cube/CubeDataGrid.java: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.cube; 2 | 3 | import com.badlogic.gdx.math.MathUtils; 4 | import com.badlogic.gdx.math.Vector3; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jrenner.learngl.Main; 7 | import org.jrenner.learngl.gameworld.Chunk; 8 | import org.jrenner.learngl.gameworld.CubeData; 9 | import org.jrenner.learngl.gameworld.CubeType; 10 | 11 | import java.util.Iterator; 12 | 13 | import static org.jrenner.learngl.utils.ToolsKt.threeIntegerHashCode; 14 | 15 | public class CubeDataGrid implements Iterable { 16 | private static final int chunkSize = Chunk.Companion.getChunkSize(); 17 | public static int width = chunkSize; 18 | public static int height = chunkSize; 19 | public static int depth = chunkSize; 20 | public boolean dirty = true; 21 | public int numElements; 22 | public CubeData[][][] grid = new CubeData[chunkSize][chunkSize][chunkSize]; 23 | 24 | /** the world coordinates projection of local coords (0f,0f,0f) 25 | * i.e. the 'left', 'bottom', 'front' corner of the box in world space */ 26 | public Vector3 origin = new Vector3(); 27 | 28 | /** center of the box the CDG represents in world coordinates */ 29 | public Vector3 center = new Vector3(); 30 | 31 | /** (width, height, depth) of the box in woorld coords 32 | * i.e. the 'right', 'top', 'back' corner of the box in world space */ 33 | public Vector3 boundary = new Vector3(); 34 | 35 | // cache int values of the origin, probably pre-mature optimization 36 | public int x, y, z; 37 | 38 | public CubeDataGrid() { 39 | for (int y = 0; y < height; y++) { 40 | for (int x = 0; x < width; x++) { 41 | for (int z = 0; z < width; z++) { 42 | grid[y][x][z] = new CubeData(); 43 | } 44 | } 45 | } 46 | } 47 | 48 | public static CubeDataGrid create(float originX, float originY, float originZ) { 49 | //CubeDataGrid cdg = Pools.obtain(CubeDataGrid.class); 50 | CubeDataGrid cdg = Main.Companion.getMainCDGPool().obtain(); 51 | cdg.init(originX, originY, originZ); 52 | return cdg; 53 | } 54 | 55 | public void init(float originX, float originY, float originZ) { 56 | this.origin.set(originX, originY, originZ); 57 | this.center.set(origin).add(width / 2, height / 2, depth / 2); 58 | this.boundary.set(origin).add(width, height, depth); 59 | this.x = MathUtils.round(origin.x); 60 | this.y = MathUtils.round(origin.y); 61 | this.z = MathUtils.round(origin.z); 62 | numElements = width * height * depth; 63 | for (int y = 0; y < height; y++) { 64 | for (int x = 0; x < width; x++) { 65 | for (int z = 0; z < depth; z++) { 66 | //CubeData cubeData = Pools.obtain(CubeData.class); 67 | CubeData cubeData = grid[y][x][z]; 68 | //cubeData.getPosition().set(x + origin.x, y + origin.y, z + origin.z); 69 | cubeData.setX((short) (x + origin.x)); 70 | cubeData.setY((short) (y + origin.y)); 71 | cubeData.setZ((short) (z + origin.z)); 72 | } 73 | } 74 | } 75 | } 76 | 77 | public boolean hasCubeAt(Vector3 vec) { 78 | return hasCubeAt(vec.x, vec.y, vec.z); 79 | } 80 | 81 | 82 | public boolean hasCubeAt(float worldX, float worldY, float worldZ) { 83 | return (worldX >= origin.x && worldX < boundary.x && 84 | worldY >= origin.y && worldY < boundary.y && 85 | worldZ >= origin.z && worldZ < boundary.z); 86 | 87 | /*int y = MathUtils.floor(worldY - origin.y); 88 | int x = MathUtils.floor(worldX - origin.x); 89 | int z = MathUtils.floor(worldZ - origin.z); 90 | boolean result = y >= 0 && y < height && x >= 0 && x < width && z >= 0 && z < depth; 91 | //System.out.printf("check for cube at, xyz: %d, %d, %d -- %s\n", x, y, z, result); 92 | return result;*/ 93 | } 94 | 95 | public CubeData getCubeAt(Vector3 vec) { 96 | return getCubeAt(vec.x, vec.y, vec.z); 97 | } 98 | 99 | public CubeData getCubeAt(float worldX, float worldY, float worldZ) { 100 | int y = MathUtils.floor(worldY - origin.y); 101 | int x = MathUtils.floor(worldX - origin.x); 102 | int z = MathUtils.floor(worldZ - origin.z); 103 | return grid[y][x][z]; 104 | } 105 | 106 | public int numberOfHiddenFaces() { 107 | int total = 0; 108 | for (CubeData cubeData : this) { 109 | total += cubeData.getHiddenFacesCount(); 110 | } 111 | return total; 112 | } 113 | 114 | public int getElevation(float xf, float zf) { 115 | int worldX = MathUtils.floor(xf); 116 | int worldZ = MathUtils.floor(zf); 117 | for (int worldY = this.y + height-1; worldY >= this.y; worldY--) { 118 | CubeData cube = getCubeAt(worldX, worldY, worldZ); 119 | if (cube.getCubeType() != CubeType.Void) { 120 | return worldY; 121 | } 122 | } 123 | return -1; 124 | } 125 | 126 | // ITERATOR SECTION ------------------------------- 127 | 128 | private CubeDataGridIterator iterator = null; 129 | 130 | @NotNull 131 | @Override 132 | public Iterator iterator() { 133 | /*if (iterator == null) { 134 | iterator = new CubeDataGridIterator(this); 135 | } 136 | iterator.reset();*/ 137 | if (iterator == null) { 138 | iterator = new CubeDataGridIterator(); 139 | iterator.setParentCDG(this); 140 | } 141 | iterator.reset(); 142 | return iterator; 143 | } 144 | 145 | private class CubeDataGridIterator implements Iterator { 146 | int y; 147 | int x; 148 | int z; 149 | int maxY; 150 | int maxX; 151 | int maxZ; 152 | 153 | public void reset() { 154 | y = 0; 155 | x = 0; 156 | z = 0; 157 | } 158 | 159 | 160 | public CubeDataGridIterator() { 161 | 162 | } 163 | 164 | public void setParentCDG(CubeDataGrid cdg) { 165 | maxY = height - 1; 166 | maxX = width - 1; 167 | maxZ = depth - 1; 168 | } 169 | 170 | @Override 171 | public boolean hasNext() { 172 | return y <= maxY && x <= maxX && z <= maxZ; 173 | } 174 | 175 | @Override 176 | public CubeData next() { 177 | CubeData cubeData = grid[y][x][z]; 178 | // increment index 179 | if (z >= maxZ) { 180 | if (x >= maxX) { 181 | // this will go past array bounds, which call hasNext to return false 182 | y++; 183 | x = 0; 184 | z = 0; 185 | } else { 186 | x++; 187 | z = 0; 188 | } 189 | } else { 190 | z++; 191 | } 192 | return cubeData; 193 | } 194 | 195 | @Override 196 | public void remove() { 197 | 198 | } 199 | } 200 | 201 | public int numberOfNonVoidCubes() { 202 | int total = 0; 203 | for (CubeData cube : this) { 204 | if (cube.getCubeType() != CubeType.Void) { 205 | total++; 206 | } 207 | } 208 | return total; 209 | } 210 | 211 | public void free() { 212 | /*for (CubeData cube : this) { 213 | Pools.free(cube); 214 | }*/ 215 | //Pools.free(this); 216 | Main.Companion.getMainCDGPool().free(this); 217 | } 218 | 219 | @Override 220 | public int hashCode() { 221 | return threeIntegerHashCode(x, y, z); 222 | } 223 | 224 | @Override 225 | public String toString() { 226 | return String.format("%.2f, %.2f, %.2f", origin.x, origin.y, origin.z); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /core/src/main/java/org/jrenner/learngl/cube/WorldChunkData.java: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.cube; 2 | 3 | import com.badlogic.gdx.math.Vector3; 4 | import org.jrenner.learngl.Main; 5 | import org.jrenner.learngl.gameworld.Chunk; 6 | import org.jrenner.learngl.gameworld.World; 7 | 8 | import static org.jrenner.learngl.MainKt.getWorld; 9 | 10 | /** a 3d array of the all chunks in the game */ 11 | public class WorldChunkData { 12 | public int width, height, depth; 13 | 14 | public WorldChunkData(int width, int height, int depth, World world) { 15 | this.width = width; 16 | this.height = height; 17 | this.depth = depth; 18 | //this.data = new CubeDataGrid[height][width][depth]; 19 | // CODE DISABLED, now done lazily 20 | /* for (int y = 0; y <= height/chunkSize(); y++) { 21 | for (int x = 0; x <= width/chunkSize(); x++) { 22 | for (int z = 0; z <= depth/chunkSize(); z++) { 23 | origin.set(x, y, z); 24 | origin.scl(chunkSize()); 25 | System.out.println("make cdg at origin: " + origin); 26 | CubeDataGrid cdg = new CubeDataGrid(chunkSize(), origin); 27 | world.applyWorldData(cdg); 28 | cdg.dirty = true; 29 | data[y][x][z] = cdg; 30 | } 31 | } 32 | }*/ 33 | } 34 | 35 | /** workaround for bug in kotlin compiler 36 | * https://youtrack.jetbrains.com/issue/KT-6586 37 | */ 38 | private int chunkSize() { 39 | return Chunk.Companion.getChunkSize(); 40 | } 41 | 42 | private static Vector3 tmp = new Vector3(); 43 | 44 | // TODO test this method 45 | public CubeDataGrid getCDGByWorldPos(float xf, float yf, float zf) { 46 | int sz = chunkSize(); 47 | int x = (int) xf / sz; 48 | int y = (int) yf / sz; 49 | int z = (int) zf / sz; 50 | Vector3 origin = tmp.set(x, y, z); 51 | origin.scl(sz); 52 | //System.out.println("make cdg at origin: " + origin + ", size: " + sz); 53 | CubeDataGrid cdg = CubeDataGrid.create(origin.x, origin.y, origin.z); 54 | Main.Companion.getMainWorld().applyWorldData(cdg, getWorld()); 55 | cdg.dirty = true; 56 | return cdg; 57 | } 58 | 59 | 60 | /*private WorldChunkDataIterator iterator; 61 | 62 | @NotNull 63 | @Override 64 | public Iterator iterator() { 65 | if (iterator == null) { 66 | iterator = new WorldChunkDataIterator(this); 67 | } 68 | iterator.reset(); 69 | return iterator; 70 | }*/ 71 | 72 | /*class WorldChunkDataIterator implements Iterator { 73 | private WorldChunkData wcd; 74 | int y; 75 | int x; 76 | int z; 77 | int maxY; 78 | int maxX; 79 | int maxZ; 80 | 81 | public void reset() { 82 | y = 0; 83 | x = 0; 84 | z = 0; 85 | } 86 | 87 | 88 | public WorldChunkDataIterator(WorldChunkData wcd) { 89 | this.wcd = wcd; 90 | maxY = wcd.height - 1; 91 | maxX = wcd.width - 1; 92 | maxZ = wcd.depth - 1; 93 | } 94 | 95 | @Override 96 | public boolean hasNext() { 97 | return y <= maxY && x <= maxX && z <= maxZ; 98 | } 99 | 100 | @Override 101 | public CubeDataGrid next() { 102 | CubeDataGrid cdg = wcd.data[y][x][z]; 103 | // increment index 104 | if (z >= maxZ) { 105 | if (x >= maxX) { 106 | // this will go past array bounds, which call hasNext to return false 107 | y++; 108 | x = 0; 109 | z = 0; 110 | } else { 111 | x++; 112 | z = 0; 113 | } 114 | } else { 115 | z++; 116 | } 117 | return cdg; 118 | } 119 | 120 | @Override 121 | public void remove() { 122 | 123 | } 124 | }*/ 125 | } 126 | -------------------------------------------------------------------------------- /core/src/main/java/org/jrenner/smartfont/SmartFontGenerator.java: -------------------------------------------------------------------------------- 1 | package org.jrenner.smartfont; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.Preferences; 5 | import com.badlogic.gdx.files.FileHandle; 6 | import com.badlogic.gdx.graphics.Pixmap; 7 | import com.badlogic.gdx.graphics.Texture; 8 | import com.badlogic.gdx.graphics.g2d.BitmapFont; 9 | import com.badlogic.gdx.graphics.g2d.PixmapPacker; 10 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 11 | import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; 12 | import com.badlogic.gdx.graphics.glutils.PixmapTextureData; 13 | import com.badlogic.gdx.utils.Array; 14 | import com.badlogic.gdx.utils.GdxRuntimeException; 15 | import org.jrenner.smartfont.writer.BitmapFontWriter; 16 | 17 | /*import com.badlogic.gdx.graphics.g2d.freetype.FreeType; 18 | import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;*/ 19 | 20 | /** Generate and cache freetype fonts dynamically. 21 | * see: https://github.com/jrenner/gdx-smart-font */ 22 | public class SmartFontGenerator { 23 | private static final String TAG = "SmartFontGenerator"; 24 | private boolean forceGeneration; 25 | private String generatedFontDir; 26 | private int referenceScreenWidth; 27 | // TODO figure out optimal page size automatically 28 | private int pageSize; 29 | 30 | public SmartFontGenerator() { 31 | forceGeneration = false; 32 | generatedFontDir = "generated-fonts/"; 33 | referenceScreenWidth = 1080; 34 | pageSize = 512; // size of atlas pages for font pngs 35 | } 36 | 37 | 38 | /** Will load font from file. If that fails, font will be generated and saved to file. 39 | * @param fontFile the actual font (.otf, .ttf) 40 | * @param fontName the name of the font, i.e. "arial-small", "arial-large", "monospace-10" 41 | * This will be used for creating the font file names 42 | * @param fontSize size of font when screen width equals referenceScreenWidth */ 43 | public BitmapFont createFont(FileHandle fontFile, String fontName, int fontSize, String fontChars) { 44 | BitmapFont font = null; 45 | // if fonts are already generated, just load from file 46 | Preferences fontPrefs = Gdx.app.getPreferences("org.jrenner.smartfont"); 47 | int displayWidth = fontPrefs.getInteger("display-width", 0); 48 | int displayHeight = fontPrefs.getInteger("display-height", 0); 49 | boolean loaded = false; 50 | if (displayWidth != Gdx.graphics.getWidth() || displayHeight != Gdx.graphics.getHeight()) { 51 | Gdx.app.debug(TAG, "Screen size change detected, regenerating fonts"); 52 | } else { 53 | try { 54 | // try to load from file 55 | Gdx.app.debug(TAG, "Loading generated font from file cache"); 56 | font = new BitmapFont(getFontFile(fontName + ".fnt")); 57 | loaded = true; 58 | } catch (GdxRuntimeException e) { 59 | Gdx.app.error(TAG, e.getMessage()); 60 | Gdx.app.debug(TAG, "Couldn't load pre-generated fonts. Will generate fonts."); 61 | } 62 | } 63 | if (!loaded || forceGeneration) { 64 | forceGeneration = false; 65 | float width = Gdx.graphics.getWidth(); 66 | 67 | // store screen width for detecting screen size change 68 | // on later startups, which will require font regeneration 69 | fontPrefs.putInteger("display-width", Gdx.graphics.getWidth()); 70 | fontPrefs.putInteger("display-height", Gdx.graphics.getHeight()); 71 | fontPrefs.flush(); 72 | 73 | font = generateFontWriteFiles(fontName, fontFile, fontSize, pageSize, pageSize, fontChars); 74 | } 75 | return font; 76 | } 77 | 78 | /** Convenience method for generating a font, and then writing the fnt and png files. 79 | * Writing a generated font to files allows the possibility of only generating the fonts when they are missing, otherwise 80 | * loading from a previously generated file. 81 | * @param fontFile 82 | * @param fontSize 83 | */ 84 | private BitmapFont generateFontWriteFiles(String fontName, FileHandle fontFile, int fontSize, int pageWidth, int pageHeight, String fontCharacters) { 85 | if (fontCharacters == null) { 86 | fontCharacters = FreeTypeFontGenerator.DEFAULT_CHARS; 87 | } 88 | FreeTypeFontGenerator generator = new FreeTypeFontGenerator(fontFile); 89 | 90 | PixmapPacker packer = new PixmapPacker(pageWidth, pageHeight, Pixmap.Format.RGBA8888, 2, false); 91 | FreeTypeFontGenerator.FreeTypeFontParameter params = new FreeTypeFontGenerator.FreeTypeFontParameter(); 92 | params.size = fontSize; 93 | params.characters = fontCharacters; 94 | params.packer = packer; 95 | //FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = generator.generateData(fontSize, fontCharacters, false, packer); 96 | FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = generator.generateData(params); 97 | Array pages = packer.getPages(); 98 | TextureRegion[] texRegions = new TextureRegion[pages.size]; 99 | for (int i=0; i texRegArr = new Array<>(); 112 | texRegArr.addAll(texRegions); 113 | BitmapFont font = new BitmapFont(fontData, texRegArr, false); 114 | saveFontToFile(font, fontSize, fontName, packer); 115 | generator.dispose(); 116 | packer.dispose(); 117 | return font; 118 | } 119 | 120 | private void saveFontToFile(BitmapFont font, int fontSize, String fontName, PixmapPacker packer) { 121 | FileHandle fontFile = getFontFile(fontName + ".fnt"); // .fnt path 122 | FileHandle pixmapDir = getFontFile(fontName); // png dir path 123 | BitmapFontWriter.setOutputFormat(BitmapFontWriter.OutputFormat.Text); 124 | 125 | String[] pageRefs = BitmapFontWriter.writePixmaps(packer.getPages(), pixmapDir, fontName); 126 | Gdx.app.debug(TAG, String.format("Saving font [%s]: fontfile: %s, pixmapDir: %s\n", fontName, fontFile, pixmapDir)); 127 | // here we must add the png dir to the page refs 128 | for (int i = 0; i < pageRefs.length; i++) { 129 | pageRefs[i] = fontName + "/" + pageRefs[i]; 130 | } 131 | BitmapFontWriter.writeFont(font.getData(), pageRefs, fontFile, new BitmapFontWriter.FontInfo(fontName, fontSize), 1, 1); 132 | } 133 | 134 | private FileHandle getFontFile(String filename) { 135 | return Gdx.files.local(generatedFontDir + filename); 136 | } 137 | 138 | // GETTERS, SETTERS ----------------------- 139 | 140 | public void setForceGeneration(boolean force) { 141 | forceGeneration = force; 142 | } 143 | 144 | public boolean getForceGeneration() { 145 | return forceGeneration; 146 | } 147 | 148 | /** Set directory for storing generated fonts */ 149 | public void setGeneratedFontDir(String dir) { 150 | generatedFontDir = dir; 151 | } 152 | 153 | /** @see org.jrenner.smartfont.SmartFontGenerator#setGeneratedFontDir(String) */ 154 | public String getGeneratedFontDir() { 155 | return generatedFontDir; 156 | } 157 | 158 | /** Set the reference screen width for computing sizes. If reference width is 1280, and screen width is 1280 159 | * Then the fontSize paramater will be unaltered when creating a font. If the screen width is 720, the font size 160 | * will by scaled down to (720 / 1280) of original size. */ 161 | public void setReferenceScreenWidth(int width) { 162 | referenceScreenWidth = width; 163 | } 164 | 165 | /** @see org.jrenner.smartfont.SmartFontGenerator#setReferenceScreenWidth(int) */ 166 | public int getReferenceScreenWidth() { 167 | return referenceScreenWidth; 168 | } 169 | 170 | /** Set the width and height of the png files to which the fonts will be saved. 171 | * In the future it would be nice for page size to be automatically set to the optimal size 172 | * by the font generator. In the mean time it must be set manually. */ 173 | public void setPageSize(int size) { 174 | pageSize = size; 175 | } 176 | 177 | /** @see org.jrenner.smartfont.SmartFontGenerator#setPageSize(int) */ 178 | public int getPageSize() { 179 | return pageSize; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /core/src/main/java/org/jrenner/smartfont/writer/BitmapFontWriter.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011 See AUTHORS file. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | 17 | package org.jrenner.smartfont.writer; 18 | 19 | 20 | import com.badlogic.gdx.files.FileHandle; 21 | import com.badlogic.gdx.graphics.Pixmap; 22 | import com.badlogic.gdx.graphics.PixmapIO; 23 | import com.badlogic.gdx.graphics.g2d.BitmapFont.BitmapFontData; 24 | import com.badlogic.gdx.graphics.g2d.BitmapFont.Glyph; 25 | import com.badlogic.gdx.graphics.g2d.PixmapPacker.Page; 26 | import com.badlogic.gdx.utils.Array; 27 | 28 | /** A utility to output BitmapFontData to a FNT file. This can be useful for caching the result from TrueTypeFont, for faster load 29 | * times. 30 | * 31 | * The font format is from the AngelCodeFont BMFont tool. 32 | * 33 | * @author mattdesl AKA davedes */ 34 | 35 | 36 | /** 37 | * This file is 'borrowed' from gdx-tools in the libgdx source 38 | */ 39 | 40 | public class BitmapFontWriter { 41 | 42 | /** The output format. */ 43 | public static enum OutputFormat { 44 | 45 | /** AngelCodeFont text format */ 46 | Text, 47 | /** AngelCodeFont XML format */ 48 | XML; 49 | } 50 | 51 | /** The output format */ 52 | private static OutputFormat format = OutputFormat.Text; 53 | 54 | /** Sets the AngelCodeFont output format for subsequent writes; can be text (for LibGDX) or XML (for other engines, like 55 | * Pixi.js). 56 | * 57 | * @param fmt the output format to use */ 58 | public static void setOutputFormat(OutputFormat fmt) { 59 | if (fmt==null) 60 | throw new NullPointerException("format cannot be null"); 61 | format = fmt; 62 | } 63 | 64 | /** Returns the currently used output format. 65 | * @return the output format */ 66 | public static OutputFormat getOutputFormat() { 67 | return format; 68 | } 69 | 70 | /** The Padding parameter for FontInfo. */ 71 | public static class Padding { 72 | public int up, down, left, right; 73 | 74 | public Padding() { 75 | } 76 | 77 | public Padding(int up, int down, int left, int right) { 78 | this.up = up; 79 | this.down = down; 80 | this.left = left; 81 | this.right = right; 82 | } 83 | } 84 | 85 | /** The spacing parameter for FontInfo. */ 86 | public static class Spacing { 87 | public int horizontal, vertical; 88 | } 89 | 90 | /** The font "info" line; this will be ignored by LibGDX's BitmapFont reader, 91 | * but useful for clean and organized output. */ 92 | public static class FontInfo { 93 | /** Face name */ 94 | public String face; 95 | /** Font size (pt) */ 96 | public int size = 12; 97 | /** Whether the font is bold */ 98 | public boolean bold; 99 | /** Whether the font is italic */ 100 | public boolean italic; 101 | /** The charset; or null/empty for default */ 102 | public String charset; 103 | /** Whether the font uses unicode glyphs */ 104 | public boolean unicode = true; 105 | /** Stretch for height; default to 100% */ 106 | public int stretchH = 100; 107 | /** Whether smoothing is applied */ 108 | public boolean smooth = true; 109 | /** Amount of anti-aliasing that was applied to the font */ 110 | public int aa = 2; 111 | /** Padding that was applied to the font */ 112 | public Padding padding = new Padding(); 113 | /** Horizontal/vertical spacing that was applied to font */ 114 | public Spacing spacing = new Spacing(); 115 | public int outline = 0; 116 | 117 | public FontInfo() { 118 | } 119 | 120 | public FontInfo(String face, int size) { 121 | this.face = face; 122 | this.size = size; 123 | } 124 | } 125 | 126 | private static String quote(Object params) { 127 | return quote(params, false); 128 | } 129 | 130 | private static String quote(Object params, boolean spaceAfter) { 131 | if (BitmapFontWriter.getOutputFormat() == OutputFormat.XML) 132 | return "\"" + params.toString().trim() + "\"" + (spaceAfter ? " " : ""); 133 | else 134 | return params.toString(); 135 | } 136 | 137 | /** Writes the given BitmapFontData to a file, using the specified pageRefs strings as the image paths for each texture 138 | * page. The glyphs in BitmapFontData have a "page" id, which references the index of the pageRef you specify here. 139 | * 140 | * The FontInfo parameter is useful for cleaner output; such as including a size and font face name hint. However, it can be 141 | * null to use default values. Ultimately, LibGDX ignores the "info" line when reading back fonts. 142 | * 143 | * Likewise, the scaleW and scaleH are only for cleaner output. They are currently ignored by LibGDX's reader. For maximum 144 | * compatibility with other BMFont tools, you should use the width and height of your texture pages (each page should be the 145 | * same size). 146 | * 147 | * @param fontData the bitmap font 148 | * @param pageRefs the references to each texture page image file, generally in the same folder as outFntFile 149 | * @param outFntFile the font file to save to (typically ends with '.fnt') 150 | * @param info the optional info for the file header; can be null 151 | * @param scaleW the width of your texture pages 152 | * @param scaleH the height of your texture pages */ 153 | public static void writeFont (BitmapFontData fontData, String[] pageRefs, FileHandle outFntFile, FontInfo info, int scaleW, int scaleH) { 154 | if (info==null) { 155 | info = new FontInfo(); 156 | info.face = outFntFile.nameWithoutExtension(); 157 | } 158 | 159 | int lineHeight = (int)fontData.lineHeight; 160 | int pages = pageRefs.length; 161 | int packed = 0; 162 | int base = (int)((fontData.capHeight) + (fontData.flipped ? -fontData.ascent : fontData.ascent)); 163 | OutputFormat fmt = BitmapFontWriter.getOutputFormat(); 164 | boolean xml = fmt == OutputFormat.XML; 165 | 166 | StringBuilder buf = new StringBuilder(); 167 | 168 | if (xml) { 169 | buf.append("\n"); 170 | } 171 | String xmlOpen = xml ? "\t<" : ""; 172 | String xmlCloseSelf = xml ? "/>" : ""; 173 | String xmlTab = xml ? "\t" : ""; 174 | String xmlClose = xml ? ">" : ""; 175 | 176 | String xmlQuote = xml ? "\"" : ""; 177 | String alphaChnlParams = 178 | xml ? " alphaChnl=\"0\" redChnl=\"0\" greenChnl=\"0\" blueChnl=\"0\"" 179 | : " alphaChnl=0 redChnl=0 greenChnl=0 blueChnl=0"; 180 | //INFO LINE 181 | 182 | buf.append(xmlOpen) 183 | .append("info face=\"") 184 | .append(info.face==null ? "" : info.face.replaceAll("\"", "'")) 185 | .append("\" size=").append( quote(info.size) ) 186 | .append(" bold=").append( quote(info.bold ? 1 : 0) ) 187 | .append(" italic=").append( quote(info.italic ? 1 : 0) ) 188 | .append(" charset=\"").append(info.charset==null ? "" : info.charset) 189 | .append("\" unicode=").append( quote(info.unicode ? 1 : 0) ) 190 | .append(" stretchH=").append( quote(info.stretchH) ) 191 | .append(" smooth=").append( quote(info.smooth ? 1 : 0) ) 192 | .append(" aa=").append( quote(info.aa) ) 193 | .append(" padding=") 194 | .append(xmlQuote) 195 | .append(info.padding.up).append(",") 196 | .append(info.padding.down).append(",") 197 | .append(info.padding.left).append(",") 198 | .append(info.padding.right) 199 | .append(xmlQuote) 200 | .append(" spacing=") 201 | .append(xmlQuote) 202 | .append(info.spacing.horizontal).append(",") 203 | .append(info.spacing.vertical) 204 | .append(xmlQuote) 205 | .append(xmlCloseSelf) 206 | .append("\n"); 207 | 208 | //COMMON line 209 | buf.append(xmlOpen) 210 | .append("common lineHeight=").append( quote(lineHeight) ) 211 | .append(" base=").append( quote(base) ) 212 | .append(" scaleW=").append( quote(scaleW) ) 213 | .append(" scaleH=").append( quote(scaleH) ) 214 | .append(" pages=").append( quote(pages) ) 215 | .append(" packed=").append( quote(packed) ) 216 | .append(alphaChnlParams) 217 | .append(xmlCloseSelf) 218 | .append("\n"); 219 | 220 | if (xml) 221 | buf.append("\t\n"); 222 | 223 | //PAGES 224 | for (int i=0; i\n"); 238 | 239 | //CHARS 240 | Array glyphs = new Array(256); 241 | for (int i=0; i\n"); 279 | 280 | //KERNINGS 281 | int kernCount = 0; 282 | StringBuilder kernBuf = new StringBuilder(); 283 | for (int i = 0; i < glyphs.size; i++) { 284 | for (int j = 0; j < glyphs.size; j++) { 285 | Glyph first = glyphs.get(i); 286 | Glyph second = glyphs.get(j); 287 | int kern = first.getKerning((char)second.id); 288 | if (kern!=0) { 289 | kernCount++; 290 | kernBuf.append(xmlTab) 291 | .append(xmlOpen) 292 | .append("kerning first=").append(quote(first.id)) 293 | .append(" second=").append(quote(second.id)) 294 | .append(" amount=").append(quote(kern, true)) 295 | .append(xmlCloseSelf) 296 | .append("\n"); 297 | } 298 | } 299 | } 300 | 301 | //KERN info 302 | buf.append(xmlOpen) 303 | .append("kernings count=").append(quote(kernCount)) 304 | .append(xmlClose) 305 | .append("\n"); 306 | buf.append(kernBuf); 307 | 308 | if (xml) { 309 | buf.append("\t\n"); 310 | buf.append(""); 311 | } 312 | 313 | String charset = info.charset; 314 | if (charset!=null&&charset.length()==0) 315 | charset = null; 316 | 317 | outFntFile.writeString(buf.toString(), false, charset); 318 | } 319 | 320 | 321 | /** A utility method which writes the given font data to a file. 322 | * 323 | * The specified pixmaps are written to the parent directory of outFntFile, using that file's name without an 324 | * extension for the PNG file name(s). 325 | * 326 | * The specified FontInfo is optional, and can be null. 327 | * 328 | * Typical usage looks like this: 329 | * 330 | *
331 | 	 * BitmapFontWriter.writeFont(myFontData, myFontPixmaps, Gdx.files.external("fonts/output.fnt"), new FontInfo("Arial", 16));
332 | 	 * 
333 | * 334 | * @param fontData the font data 335 | * @param pages the pixmaps to write as PNGs 336 | * @param outFntFile the output file for the font definition 337 | * @param info the optional font info for the header file, can be null */ 338 | public static void writeFont (BitmapFontData fontData, Pixmap[] pages, FileHandle outFntFile, FontInfo info) { 339 | String[] pageRefs = writePixmaps(pages, outFntFile.parent(), outFntFile.nameWithoutExtension()); 340 | 341 | //write the font data 342 | writeFont(fontData, pageRefs, outFntFile, info, pages[0].getWidth(), pages[0].getHeight()); 343 | } 344 | 345 | /** A utility method to write the given array of pixmaps to the given output directory, with the specified file name. If the 346 | * pages array is of length 1, then the resulting file ref will look like: "fileName.png". 347 | * 348 | * If the pages array is greater than length 1, the resulting file refs will be appended with "_N", such as "fileName_0.png", 349 | * "fileName_1.png", "fileName_2.png" etc. 350 | * 351 | * The returned string array can then be passed to the writeFont method. 352 | * 353 | * Note: None of the pixmaps will be disposed. 354 | * 355 | * @param pages the pages of pixmap data to write 356 | * @param outputDir the output directory 357 | * @param fileName the file names for the output images 358 | * @return the array of string references to be used with writeFont */ 359 | public static String[] writePixmaps (Pixmap[] pages, FileHandle outputDir, String fileName) { 360 | if (pages==null || pages.length==0) 361 | throw new IllegalArgumentException("no pixmaps supplied to BitmapFontWriter.write"); 362 | 363 | String[] pageRefs = new String[pages.length]; 364 | 365 | for (int i=0; i pages, FileHandle outputDir, String fileName) { 385 | Pixmap[] pix = new Pixmap[pages.size]; 386 | for (int i=0; i(val name: String, val newObjFunc: () -> T, initialCapacity: Int = 16) : Pool(initialCapacity) { 11 | companion object { 12 | val list = Arr>() 13 | val sb : RenStringBuilder by lazy { 14 | RenStringBuilder() 15 | } 16 | fun allDebugInfo(): String { 17 | sb.delete(0, sb.sbLength()) 18 | for (pool in list) { 19 | sb.append(pool.debugInfo()).append("\n") 20 | } 21 | return sb.toString() 22 | } 23 | 24 | } 25 | init { 26 | list.add(this) 27 | } 28 | var objectsCreated = 0 29 | private set 30 | 31 | var objectsFreed = 0 32 | 33 | var objectsObtained = 0 34 | 35 | override fun obtain(): T { 36 | objectsObtained++ 37 | synchronized(this) { 38 | return super.obtain() 39 | } 40 | } 41 | 42 | override fun newObject(): T { 43 | objectsCreated++ 44 | return newObjFunc() 45 | } 46 | 47 | override fun free(obj: T) { 48 | synchronized(this) { 49 | super.free(obj) 50 | } 51 | objectsFreed++ 52 | } 53 | 54 | override fun freeAll(objects: Arr?) { 55 | throw GdxRuntimeException("DebugPool does not support freeAll method") 56 | } 57 | 58 | fun debugInfo(): String { 59 | return "[DebugPool: $name] created: $objectsCreated, obtained: $objectsObtained, freed: $objectsFreed, currently free: ${getFree()}" 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/Direction.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl 2 | 3 | object Direction { 4 | val Up = 0x01 5 | val Down = 0x02 6 | val North = 0x04 7 | val South = 0x08 8 | val East = 0x10 9 | val West = 0x20 10 | 11 | val all = arrayOf(Up, Down, North, South, East, West) 12 | val allSize = 6 13 | val ALL_FACES: Int = 0xFF 14 | 15 | fun toString(n: Int): String { 16 | return when (n) { 17 | Up -> "Up" 18 | Down -> "Down" 19 | North -> "North" 20 | South -> "South" 21 | East -> "East" 22 | West -> "West" 23 | else -> "Non-direction integer: $n" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/Fonts.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl 2 | 3 | import org.jrenner.smartfont.SmartFontGenerator 4 | import com.badlogic.gdx.Gdx 5 | import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator 6 | import kotlin.properties.Delegates 7 | import com.badlogic.gdx.graphics.g2d.BitmapFont 8 | 9 | class Fonts { 10 | 11 | var normal: BitmapFont by Delegates.notNull() 12 | 13 | init { 14 | val gen = SmartFontGenerator() 15 | val fileHandle = Gdx.files.local("fonts/Exo-Regular.otf") 16 | val size: Int = 18 17 | val fontName = "exo" + size 18 | println("$fontName") 19 | val chars = FreeTypeFontGenerator.DEFAULT_CHARS 20 | normal = gen.createFont(fileHandle, fontName, size, chars) 21 | } 22 | 23 | fun dispose() { 24 | normal.dispose() 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/FramePool.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl 2 | 3 | import com.badlogic.gdx.utils.Array as Arr 4 | 5 | /** a pool of re-usable objects, where the re-usable objects are only borrowed until 6 | * the start of the next render frame, when they are automatically reset and added back 7 | * into the pool 8 | */ 9 | abstract class FramePool() { 10 | companion object { 11 | val framePools = Arr>() 12 | fun reset() { 13 | for (fp in framePools) { 14 | fp.reset() 15 | } 16 | } 17 | } 18 | 19 | private val pool = Arr() 20 | private val borrowed = Arr() 21 | 22 | abstract fun newObject(): T 23 | 24 | fun obtain(): T { 25 | val item: T = if (pool.size == 0) { 26 | newObject() 27 | } else { 28 | pool.pop() 29 | } 30 | borrowed.add(item) 31 | return item 32 | } 33 | 34 | fun reset() { 35 | for (i in 0..borrowed.size-1) { 36 | val item = borrowed[i] 37 | pool.add(item) 38 | } 39 | borrowed.clear() 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/HUD.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Stage 4 | import com.badlogic.gdx.scenes.scene2d.ui.Table 5 | import com.badlogic.gdx.scenes.scene2d.ui.Label 6 | import com.badlogic.gdx.Gdx 7 | import com.badlogic.gdx.scenes.scene2d.Actor 8 | import com.badlogic.gdx.utils.Align 9 | import com.badlogic.gdx.utils.StringBuilder 10 | import org.jrenner.RenStringBuilder 11 | import org.jrenner.learngl.utils.plus 12 | 13 | class HUD { 14 | val stage = Stage() 15 | val table = Table() 16 | val info: Label = Label("Info", skin) 17 | 18 | init { 19 | refresh() 20 | } 21 | 22 | fun refresh() { 23 | table.clear() 24 | table.setFillParent(true) 25 | table.align(Align.left or Align.top) 26 | fun label(act: Actor) = table.add(act).align(Align.left or Align.top).row() 27 | label(info) 28 | stage.addActor(table) 29 | } 30 | 31 | var accumulatedTime = 0f 32 | 33 | fun isReadyForUpdate(): Boolean { 34 | val interval = 0.1f 35 | if (accumulatedTime >= interval) { 36 | accumulatedTime -= interval 37 | return true 38 | } 39 | return false 40 | } 41 | 42 | val sb = RenStringBuilder() 43 | 44 | fun update(dt: Float) { 45 | if (isReadyForUpdate()) { 46 | sb.delete(0, sb.sbLength()) 47 | sb + "FPS: " + Gdx.graphics.getFramesPerSecond().toString() 48 | sb + "\nMemory\n\tJava: ${Gdx.app.getJavaHeap() / 1000000} MB\n\tNative: ${Gdx.app.getNativeHeap() / 1000000} MB" 49 | sb + "\nChunks: ${world.chunks.size}, Rendered: ${view.chunksRendered}" 50 | sb + "\nChunkQueue: ${world.chunkCreationQueue.size}" 51 | val c = view.camera.position 52 | sb + "\nChunks, created: ${world.chunksCreated}, removed: ${world.chunksRemoved}" 53 | val camElev = world.getElevation(c.x, c.z) 54 | sb + "\nCamera: %.1f, %.1f %.1f\n\tAltitude above ground: $camElev".format(c.x, c.y, c.z) 55 | val moveMode = if (view.walkingEnabled) "Walking" else "Flying" 56 | sb + "\nMovement mode: ${moveMode}" 57 | sb + "\nView Distance: ${View.maxViewDist}" 58 | sb + "\nWARNING: \nHigh view distances require\nexponentially large amounts of memory" 59 | sb + "\n\nCONTROLS:\n\tW, A, S, D to move\n\tClick and hold mouse to look around\n\t- and + to change view distance" 60 | sb + "\nSpace to switch flying/walking" 61 | info.setText(sb.toString()) 62 | } 63 | } 64 | 65 | var enabled = true 66 | 67 | fun render(dt: Float) { 68 | accumulatedTime += dt 69 | if (enabled) { 70 | update(dt) 71 | stage.act(dt) 72 | stage.draw() 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/Main.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl 2 | 3 | import com.badlogic.gdx.ApplicationAdapter 4 | import com.badlogic.gdx.utils.TimeUtils 5 | import com.badlogic.gdx.Gdx 6 | import com.badlogic.gdx.graphics.FPSLogger 7 | import kotlin.properties.Delegates 8 | import com.badlogic.gdx.math.MathUtils 9 | import com.badlogic.gdx.utils.Array as Arr 10 | import com.badlogic.gdx.graphics.profiling.GLProfiler 11 | import com.badlogic.gdx.scenes.scene2d.ui.Skin 12 | import com.badlogic.gdx.graphics.Color 13 | import org.jrenner.learngl.gameworld.Chunk 14 | import com.badlogic.gdx.math.Vector3 15 | import org.jrenner.learngl.light.Lights 16 | import org.jrenner.learngl.cube.CubeDataGrid 17 | import org.jrenner.learngl.input.GameInput 18 | import org.jrenner.learngl.gameworld.World 19 | 20 | class Main : ApplicationAdapter() { 21 | companion object { 22 | public val mainWorld: World get() = world // for easier Java interop 23 | public val mainChunkPool: DebugPool get() = chunkPool 24 | public val mainCDGPool: DebugPool get() = cdgPool 25 | 26 | } 27 | 28 | val fpsLogger = FPSLogger() 29 | 30 | 31 | override fun create() { 32 | main = this 33 | fonts = Fonts() 34 | assets = Assets() 35 | assets.load() 36 | view = View() 37 | hud = HUD() 38 | gameInput = GameInput() 39 | GLProfiler.enable() 40 | world = World(8192, 64, 8192) 41 | view.camera.position.set(world.width / 2f, 50f, world.depth / 2f) 42 | view.camera.lookAt(world.width.toFloat(), 0f, world.depth / 2f) 43 | println("world size: ${world.width * world.height * world.depth}") 44 | lights = Lights() 45 | } 46 | 47 | val viewResetter = TimedIntervalTask(1.0f, { 48 | resetViewRequested = true 49 | }) 50 | 51 | override fun render() { 52 | /*if (frame % 120 == 0L) { 53 | //println(DebugPool.allDebugInfo()) 54 | //world.elevTimer.report() 55 | }*/ 56 | FramePool.reset() 57 | if (resetViewRequested) { 58 | resetViewRequested = false 59 | val pos = Vector3(view.camera.position) 60 | val direction = Vector3(view.camera.direction) 61 | fonts.dispose() 62 | fonts = Fonts() 63 | assets.load() 64 | view = View() 65 | hud = HUD() 66 | lights = Lights() 67 | gameInput.resetProcessors() 68 | view.camera.position.set(pos) 69 | view.camera.direction.set(direction) 70 | for (light in lights.pointLights) { 71 | light.pos.set(view.camera.position) 72 | light.setNextDest() 73 | } 74 | println("view reset") 75 | } 76 | val dt = Gdx.graphics.getDeltaTime() 77 | frameTimes[frameTimeIdx++] = dt 78 | world.update(dt) 79 | frame++ 80 | profileGL() 81 | //fpsLogger.log() 82 | view.render(dt) 83 | hud.render(dt) 84 | gameInput.update(dt) 85 | /*if (frame % 120 == 0L) { 86 | resetViewRequested = true 87 | }*/ 88 | Physics.update() 89 | } 90 | 91 | var profileRequested = false 92 | var resetViewRequested = false 93 | 94 | fun profileGL() { 95 | if (profileRequested) { 96 | profileRequested = false 97 | println("calls: ${GLProfiler.calls}") 98 | println("draw calls: ${GLProfiler.drawCalls}") 99 | val min = GLProfiler.vertexCount.min 100 | val max = GLProfiler.vertexCount.max 101 | val avg = GLProfiler.vertexCount.average 102 | val total = GLProfiler.vertexCount.total 103 | println("vertices: min,boundary: $min, $max - average: $avg - total: $total") 104 | println("----------------------------------------------") 105 | } 106 | GLProfiler.reset() 107 | } 108 | 109 | override fun resize(width: Int, height: Int) { 110 | //super.resize(width, height) 111 | resetViewRequested = true 112 | } 113 | } 114 | 115 | var fonts: Fonts by Delegates.notNull() 116 | var main: Main by Delegates.notNull() 117 | var view: View by Delegates.notNull() 118 | var hud: HUD by Delegates.notNull() 119 | var gameInput: GameInput by Delegates.notNull() 120 | var skin: Skin by Delegates.notNull() 121 | var assets: Assets by Delegates.notNull() 122 | var world: World by Delegates.notNull() 123 | var lights: Lights by Delegates.notNull() 124 | 125 | var frame = 0L 126 | 127 | val screenWidth: Int get() = Gdx.graphics.getWidth() 128 | val screenHeight: Int get() = Gdx.graphics.getHeight() 129 | val screenRatio: Float get() = screenWidth / 1080f 130 | 131 | val startTime = TimeUtils.millis() 132 | val millisSinceStart: Long get() = TimeUtils.millis() - startTime 133 | val secondsSinceStart: Float get() = millisSinceStart / 1000f 134 | var lastSecond = 0f 135 | 136 | var hiddenFacesEnabled = true 137 | 138 | val initialChunkPoolSize = 2048 139 | val chunkPool = DebugPool("Chunk", { Chunk() }, initialChunkPoolSize) 140 | val cdgPool = DebugPool("CDG", { CubeDataGrid() }, initialChunkPoolSize) 141 | 142 | fun rand(f: Float): Float { 143 | return MathUtils.random(f) 144 | } 145 | 146 | fun randColor(): Color { 147 | return Color(rand(1f), rand(1f), rand(1f), 1f) 148 | } 149 | 150 | val frameTimes : FloatArray by lazy { FloatArray(screenHeight) } 151 | var frameTimeIdx: Int = 0 152 | get() { 153 | if (field >= frameTimes.size) { 154 | field = 0 155 | for (i in 0..frameTimes.size - 1) { 156 | frameTimes[i] = 0f 157 | } 158 | } 159 | return field 160 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/Physics.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl 2 | 3 | import com.badlogic.gdx.math.Vector3 4 | import org.jrenner.learngl.gameworld.CubeData 5 | import com.badlogic.gdx.math.collision.Ray 6 | import com.badlogic.gdx.math.Intersector 7 | import com.badlogic.gdx.math.collision.BoundingBox 8 | 9 | /** returns position correction needed */ 10 | object Physics { 11 | 12 | private val tmp = Vector3() 13 | private val tmp2 = Vector3() 14 | private val tmp3 = Vector3() 15 | private val tmp4 = Vector3() 16 | 17 | private val intersect = Vector3() 18 | 19 | private val ray = Ray(tmp, tmp) 20 | private val bbox = BoundingBox() 21 | 22 | val rayStart = Vector3() 23 | val rayDir = Vector3() 24 | val rayEnd = Vector3() 25 | 26 | fun collision(pos: Vector3): Vector3 { 27 | tmp.setZero() 28 | if (world.hasChunkAt(pos.x, pos.y, pos.z)) { 29 | val center = tmp2.set(world.getCubeAt(pos.x, pos.y, pos.z).getPositionTempVec()).add(0.5f, 0.5f, 0.5f) 30 | val diff = tmp.set(center).sub(pos) 31 | rayStart.set(diff.scl(2f)).add(pos) 32 | rayDir.set(diff) 33 | rayEnd.set(rayDir).scl(10f).add(rayStart) 34 | ray.set(rayStart, rayDir) 35 | bbox.set(tmp3.set(center.x - 0.5f, center.y - 0.5f, center.z - 0.5f), 36 | tmp4.set(center.x + 0.5f, center.y + 0.5f, center.z + 0.5f)) 37 | 38 | val didHit = Intersector.intersectRayBounds(ray, bbox, intersect) 39 | 40 | if (didHit) { 41 | //println("hit: $didHit \t intersection: $intersect") 42 | } 43 | } 44 | return intersect 45 | } 46 | 47 | fun update() { 48 | collision(view.camera.position) 49 | } 50 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/TimedIntervalTask.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl 2 | 3 | class TimedIntervalTask(val intervalSeconds: Float, val task: () -> Unit) { 4 | private var accumulated = 0f 5 | 6 | fun update(dt: Float) { 7 | accumulated += dt 8 | if (accumulated >= intervalSeconds) { 9 | accumulated -= intervalSeconds 10 | task() 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/View.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl 2 | 3 | import com.badlogic.gdx.graphics.PerspectiveCamera 4 | import com.badlogic.gdx.Gdx 5 | import com.badlogic.gdx.graphics.GL20 6 | import com.badlogic.gdx.graphics.Color 7 | import com.badlogic.gdx.graphics.g3d.ModelBatch 8 | import com.badlogic.gdx.utils.Array as Arr 9 | import com.badlogic.gdx.math.Quaternion 10 | import com.badlogic.gdx.math.Vector3 11 | import kotlin.properties.Delegates 12 | import com.badlogic.gdx.graphics.g3d.utils.FirstPersonCameraController 13 | import com.badlogic.gdx.utils.GdxRuntimeException 14 | import com.badlogic.gdx.graphics.glutils.ShaderProgram 15 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer 16 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType 17 | import com.badlogic.gdx.math.Matrix4 18 | import org.jrenner.learngl.cube.CubeDataGrid 19 | import com.badlogic.gdx.math.MathUtils 20 | import org.jrenner.learngl.gameworld.Chunk 21 | import com.badlogic.gdx.math.Frustum 22 | import org.jrenner.learngl.gameworld.World 23 | 24 | class View { 25 | companion object { 26 | var maxViewDist = 200f 27 | set(d) { 28 | field = MathUtils.clamp(d, 20f, 1000f) 29 | } 30 | val WALKING_MAX_VELOCITY = 5f 31 | val FLYING_MAX_VELOCITY = 30f 32 | 33 | } 34 | val gl = Gdx.gl!! 35 | val camera = PerspectiveCamera(67f, screenWidth.toFloat(), screenHeight.toFloat()) 36 | var walkingEnabled = false 37 | val fogColor = Color(0.4f, 0.4f, 0.45f, 1.0f) // alpha is fog intensity 38 | //val fogColor = Color.valueOf("9CD2FF") 39 | val camControl: FirstPersonCameraController 40 | init { 41 | gl.glClearColor(fogColor.r, fogColor.g, fogColor.b, 1.0f) 42 | camera.near = 0.1f 43 | camera.far = 1500f 44 | camControl = FirstPersonCameraController(camera) 45 | camControl.setVelocity(FLYING_MAX_VELOCITY) 46 | } 47 | 48 | // begin debug section 49 | val modelBatch: ModelBatch by lazy { 50 | ModelBatch() 51 | } 52 | // end debug 53 | 54 | val shapes = ShapeRenderer() 55 | 56 | val shader: ShaderProgram 57 | val normalMatrixLocation: Int 58 | val projTransLocation: Int 59 | val diffuseTextureLocation: Int 60 | val diffuseUVLocation: Int 61 | val maxViewDistLocation: Int 62 | val camPosLocation: Int 63 | val fogColorLocation: Int 64 | init { 65 | val getShader = { path: String -> Gdx.files.local(path)!! } 66 | val vert = getShader("shader/custom.vertex.glsl") 67 | val frag = getShader("shader/custom.fragment.glsl") 68 | shader = ShaderProgram(vert, frag) 69 | val log = shader.getLog() 70 | if (!shader.isCompiled()) { 71 | println("SHADER ERROR:\n$log}") 72 | throw GdxRuntimeException("SHADER DID NOT COMPILE, SEE SHADER LOG ABOVE") 73 | } else { 74 | println("SHADER COMPILED OK:\n$log") 75 | } 76 | val loc = { name: String -> shader.getUniformLocation(name) } 77 | projTransLocation = loc("u_projTrans") 78 | normalMatrixLocation = loc("u_normalMatrix") 79 | diffuseTextureLocation = loc("u_diffuseTexture") 80 | diffuseUVLocation = loc("u_diffuseUV") 81 | maxViewDistLocation = loc("u_maxViewDist") 82 | camPosLocation = loc("u_cameraPos") 83 | fogColorLocation = loc("u_fogColor") 84 | } 85 | 86 | val q = Quaternion() 87 | 88 | val debug = true 89 | 90 | var fallSpeed = 0f 91 | 92 | fun simulateWalking() { 93 | if (!walkingEnabled) return 94 | // some quick and dirty gravity 95 | val pos = camera.position 96 | //val elev = world.getElevation(pos.x, pos.z).toFloat() + 2f 97 | val sz = 1f 98 | val elev = world.getBoundingBoxElevation(pos.x - sz/2f, pos.z - sz/2f, sz, sz).toFloat() + 2f 99 | val elevDiff = elev - pos.y 100 | if (Math.abs(elevDiff) <= 0.1f) { 101 | pos.y = elev 102 | fallSpeed = 0f 103 | } else if (elevDiff > 0) { 104 | pos.y += 0.2f 105 | } else { 106 | fallSpeed += 0.01f 107 | pos.y -= fallSpeed 108 | } 109 | } 110 | 111 | fun render(dt: Float) { 112 | gl.glClear(GL20.GL_COLOR_BUFFER_BIT or GL20.GL_DEPTH_BUFFER_BIT) 113 | 114 | if(debug) { 115 | modelBatch.begin(camera) 116 | for (pl in lights.pointLights) { 117 | if (!pl.attachedToCamera) { 118 | modelBatch.render(pl.debugInstance) 119 | } 120 | } 121 | modelBatch.end() 122 | } 123 | 124 | gl.glEnable(GL20.GL_DEPTH_TEST) 125 | gl.glEnable(GL20.GL_BLEND); 126 | gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); 127 | 128 | gl.glEnable(GL20.GL_CULL_FACE) 129 | gl.glCullFace(GL20.GL_BACK) 130 | 131 | //gl.glEnable(GL20.GL_TEXTURE_2D); 132 | simulateWalking() 133 | camera.up.set(Vector3.Y) 134 | camera.update() 135 | lights.update(dt) 136 | 137 | draw() 138 | 139 | 140 | gl.glDisable(GL20.GL_DEPTH_TEST) 141 | gl.glDisable(GL20.GL_CULL_FACE) 142 | 143 | // we don't need this to be exact, let's optimize 144 | if (frame % 5 == 0L) { 145 | synchronized(world) { 146 | world.updater?.tempCamPos?.set(camera.position) 147 | world.updater?.tempFrustum?.update(camera.invProjectionView) 148 | world.updater?.maxDist = View.maxViewDist 149 | } 150 | } 151 | 152 | /*shapes.begin(ShapeType.Line) 153 | shapes.setColor(Color.RED) 154 | println("ray: ${Physics.rayStart} ------ ${Physics.rayEnd}") 155 | shapes.line(Physics.rayStart, Physics.rayEnd) 156 | shapes.end()*/ 157 | 158 | 159 | } 160 | 161 | var chunksRendered = 0 162 | 163 | val normalMatrix = Matrix4() 164 | 165 | fun draw() { 166 | try { 167 | shader.begin() 168 | lights.setUniforms() 169 | //assets.grassTexture.bind() 170 | assets.dirtTexture.bind() 171 | shader.setUniformMatrix(projTransLocation, camera.combined) 172 | //shader.setUniformMatrix(viewMatrixLocation, camera.view) 173 | normalMatrix.set(camera.combined.inv().tra()) 174 | shader.setUniformMatrix(normalMatrixLocation, normalMatrix) 175 | shader.setUniformi(diffuseTextureLocation, 0) 176 | // subtract by chunkSize to hide popping in/out of chunks 177 | shader.setUniformf(maxViewDistLocation, maxViewDist - Chunk.chunkSize) 178 | shader.setUniformf(camPosLocation, camera.position) 179 | shader.setUniformf(fogColorLocation, fogColor) 180 | 181 | // Grass 182 | //assets.grassTexture.bind() 183 | chunksRendered = 0 184 | for (chunk in world.chunks) { 185 | if (chunk.chunkMesh.vertexCount != 0 && chunk.inFrustum()) { 186 | chunksRendered++ 187 | chunk.chunkMesh.mesh.render(shader, GL20.GL_TRIANGLES, 0, chunk.chunkMesh.vertexCount) 188 | } 189 | } 190 | 191 | shader.end() 192 | } catch (e: GdxRuntimeException) { 193 | e.printStackTrace() 194 | main.resetViewRequested = true 195 | println("sleep 3 seconds") 196 | Thread.sleep(3000) 197 | return 198 | } 199 | 200 | //drawXYZCoords() 201 | drawFrameTimes() 202 | } 203 | 204 | val tmp = Vector3() 205 | val tmp2 = Vector3() 206 | 207 | fun drawChunkBoundingBoxes() { 208 | shapes.begin(ShapeType.Line) 209 | shapes.setProjectionMatrix(camera.combined) 210 | shapes.setColor(Color.GREEN) 211 | for (chunk in world.chunks) { 212 | val o = chunk.dataGrid.origin 213 | val w = CubeDataGrid.width.toFloat() 214 | val h = CubeDataGrid.height.toFloat() 215 | val d = CubeDataGrid.depth.toFloat() 216 | shapes.box(o.x, o.y, o.z + d, w, h, d) 217 | } 218 | shapes.end() 219 | } 220 | 221 | fun drawXYZCoords() { 222 | val o = tmp2.set(0f, -10f, 0f) 223 | val n = 5f 224 | shapes.setProjectionMatrix(camera.combined) 225 | shapes.begin(ShapeType.Line) 226 | // x 227 | shapes.setColor(Color.BLUE) 228 | shapes.line(o, tmp.set(n, 0f, 0f).add(o)) 229 | // y 230 | shapes.setColor(Color.GREEN) 231 | shapes.line(o, tmp.set(0f, n, 0f).add(o)) 232 | // z 233 | shapes.setColor(Color.RED) 234 | shapes.line(o, tmp.set(0f, 0f, n).add(o)) 235 | shapes.end() 236 | 237 | } 238 | 239 | val mtx = Matrix4() 240 | init { 241 | mtx.setToOrtho2D(0f, 0f, screenWidth.toFloat(), screenHeight.toFloat()) 242 | } 243 | 244 | fun drawFrameTimes() { 245 | shapes.setProjectionMatrix(mtx) 246 | shapes.begin(ShapeType.Line) 247 | shapes.setColor(Color.GREEN) 248 | val mult = 300f 249 | val slow = 1 / 45f 250 | val verySlow = 1 / 30f 251 | val tooSlow = 1 / 10f 252 | 253 | for (i in 0..frameTimes.size - 1) { 254 | val time = frameTimes[i] 255 | val col = when { 256 | time >= tooSlow -> Color.RED 257 | time >= verySlow -> Color.ORANGE 258 | time >= slow -> Color.YELLOW 259 | else -> Color.GREEN 260 | } 261 | shapes.setColor(col) 262 | shapes.line(0f, i.toFloat(), time * mult, i.toFloat()) 263 | } 264 | shapes.end() 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/gameworld/Chunk.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.gameworld 2 | 3 | import kotlin.properties.Delegates 4 | import org.jrenner.learngl.cube.CubeDataGrid 5 | import org.jrenner.learngl.utils.refresh 6 | import org.jrenner.learngl.chunkPool 7 | import org.jrenner.learngl.view 8 | 9 | class Chunk() { 10 | companion object { 11 | var chunkSize = 16 12 | var chunkSizef = chunkSize.toFloat() 13 | val maxIndex = chunkSize - 1 14 | fun obtain(cdg: CubeDataGrid): Chunk { 15 | //val chunk = Pools.obtain(javaClass()) 16 | val chunk = chunkPool.obtain() 17 | chunk.dataGrid = cdg 18 | chunk.dirty = true 19 | /*val p = Pools.get(javaClass()) 20 | val peakFree = p.peak 21 | val freeCount = p.getFree() 22 | println("chunk pool, peak: $peakFree, free count: $freeCount")*/ 23 | 24 | return chunk 25 | } 26 | } 27 | 28 | var dirty = false 29 | 30 | var dataGrid: CubeDataGrid by Delegates.notNull() 31 | 32 | val chunkMesh: ChunkMesh by lazy { 33 | ChunkMesh() 34 | } 35 | 36 | fun update() { 37 | if (dirty) { 38 | refresh() 39 | dirty = false 40 | } 41 | } 42 | 43 | 44 | 45 | fun inFrustum(): Boolean { 46 | val x = dataGrid.center.x 47 | val y = dataGrid.center.y 48 | val z = dataGrid.center.z 49 | val w = CubeDataGrid.width 50 | val h = CubeDataGrid.height 51 | val d = CubeDataGrid.depth 52 | return view.camera.frustum.boundsInFrustum(x, y, z, w/2f, h/2f, d/2f) 53 | } 54 | 55 | fun refresh() { 56 | dataGrid.refresh() 57 | chunkMesh.reset(dataGrid) 58 | chunkMesh.buildMesh() 59 | } 60 | 61 | 62 | fun dispose() { 63 | dataGrid.free() 64 | //chunkMesh.dispose() 65 | //Pools.free(this) 66 | chunkPool.free(this) 67 | } 68 | 69 | override fun hashCode(): Int { 70 | return dataGrid.hashCode() 71 | } 72 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/gameworld/ChunkMesh.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.gameworld 2 | 3 | import com.badlogic.gdx.graphics.Mesh 4 | import com.badlogic.gdx.graphics.VertexAttributes.Usage 5 | import com.badlogic.gdx.graphics.VertexAttribute 6 | import com.badlogic.gdx.math.Vector3 7 | import com.badlogic.gdx.utils.GdxRuntimeException 8 | import org.jrenner.learngl.cube.CubeDataGrid 9 | import com.badlogic.gdx.math.Vector2 10 | import kotlin.properties.Delegates 11 | import org.jrenner.learngl.Direction 12 | 13 | class ChunkMesh() { 14 | companion object { 15 | private val cubeRectData = RectData() 16 | 17 | private val POSITION_COMPONENTS = 3 18 | //private val COLOR_COMPONENTS = 4 19 | private val TEXTURE_COORDS = 2 20 | private val NORMAL_COMPONENTS = 3 21 | private val NUM_COMPONENTS = POSITION_COMPONENTS + TEXTURE_COORDS + NORMAL_COMPONENTS 22 | 23 | private val VERTS_PER_TRI = 3 24 | private val TRIS_PER_FACE = 2 25 | private val FACES_PER_CUBE = 6 26 | private val CUBE_SIZE = 1f 27 | val MAX_VERTS = VERTS_PER_TRI * TRIS_PER_FACE * FACES_PER_CUBE * (Chunk.chunkSize * Chunk.chunkSize * Chunk.chunkSize) * NUM_COMPONENTS 28 | 29 | val verts = FloatArray(MAX_VERTS) 30 | } 31 | 32 | //private val PRIMITIVE_SIZE = 3 * NUM_COMPONENTS 33 | 34 | private var cdg: CubeDataGrid by Delegates.notNull() 35 | 36 | var numCubes = 0 37 | var numFaces = 0 38 | var numTris = 0 39 | var numVerts = 0 40 | var numFloats = 0 41 | 42 | fun reset(cdg: CubeDataGrid) { 43 | this.cdg = cdg 44 | numCubes = cdg.numberOfNonVoidCubes() 45 | numFaces = FACES_PER_CUBE * numCubes - cdg.numberOfHiddenFaces() 46 | numTris = numFaces * TRIS_PER_FACE 47 | numVerts = numTris * VERTS_PER_TRI 48 | numFloats = numVerts * NUM_COMPONENTS 49 | } 50 | 51 | private var cubesCreated = 0 52 | private var idx = 0 53 | private var triangles = 0 54 | var vertexCount = 0 55 | 56 | var hasMesh = false 57 | 58 | fun resetMesh() { 59 | if (hasMesh) { 60 | mesh.dispose() 61 | } 62 | mesh = Mesh(true, numVerts, 0, 63 | VertexAttribute(Usage.Position, POSITION_COMPONENTS, "a_position"), 64 | //VertexAttribute(Usage.ColorUnpacked, COLOR_COMPONENTS, "a_color"), 65 | VertexAttribute(Usage.TextureCoordinates, TEXTURE_COORDS, "a_textureCoords"), 66 | VertexAttribute(Usage.Normal, NORMAL_COMPONENTS, "a_normal")) 67 | hasMesh = true 68 | } 69 | 70 | var mesh: Mesh by Delegates.notNull() 71 | 72 | // EXPERIMENTAL: re-use the same mesh to avoid diposal peformance cost 73 | // the downside is that every mesh will be of max size = MAX_VERTS, ballooning native memory 74 | /*val mesh = Mesh(true, MAX_VERTS, 0, 75 | VertexAttribute(Usage.Position, POSITION_COMPONENTS, "a_position"), 76 | //VertexAttribute(Usage.ColorUnpacked, COLOR_COMPONENTS, "a_color"), 77 | VertexAttribute(Usage.TextureCoordinates, TEXTURE_COORDS, "a_textureCoords"), 78 | VertexAttribute(Usage.Normal, NORMAL_COMPONENTS, "a_normal")) 79 | 80 | fun resetMesh() { 81 | // do nothing 82 | }*/ 83 | 84 | var started = false 85 | 86 | private fun start() { 87 | if (started) throw GdxRuntimeException("call end() first!") 88 | started = true 89 | idx = 0 90 | triangles = 0 91 | cubesCreated = 0 92 | vertexCount = 0 93 | } 94 | 95 | fun buildMesh() { 96 | resetMesh() 97 | start() 98 | for (cubeData in cdg) { 99 | //println("create cube at: ${cubeData.position.fmt}") 100 | //println("cube: ${cubeData.cubeType}") 101 | if (cubeData.cubeType != CubeType.Void) { 102 | addCube(cubeData.getPositionTempVec(), CUBE_SIZE, cubeData.hiddenFaces) 103 | } 104 | } 105 | end() 106 | } 107 | 108 | 109 | private fun end() { 110 | if (!started) throw GdxRuntimeException("call start() first!") 111 | /* if (cubesCreated != NUM_CUBES) { 112 | throw GdxRuntimeException("cubes created (${cubesCreated}) is not equal to NUM_CUBES (${NUM_CUBES})") 113 | }*/ 114 | /* if (vertexCount != NUM_VERTS) { 115 | throw GdxRuntimeException("vertexCount ($vertexCount) != NUM_VERTS ($NUM_VERTS)") 116 | }*/ 117 | started = false 118 | mesh.setVertices(verts, 0, numFloats); 119 | //mesh.updateVertices(0, verts, 0, numFloats) 120 | } 121 | 122 | private fun addRect(rv: RectData) { 123 | triangle(rv.v00, rv.v10, rv.v11, rv.normal, rv.uv00, rv.uv10, rv.uv11) 124 | triangle(rv.v00, rv.v11, rv.v01, rv.normal, rv.uv00, rv.uv11, rv.uv01) 125 | } 126 | 127 | private fun addCube(origin: Vector3, sz: Float, hiddenBitwise: Int) { 128 | cubesCreated++ 129 | val r = cubeRectData 130 | 131 | val n = 1f 132 | 133 | r.uv00.set(0f, n) 134 | r.uv10.set(n, n) 135 | r.uv11.set(n, 0f) 136 | r.uv01.set(0f, 0f) 137 | 138 | /*var hiddenFaces = 0 139 | for (dir in array(NORTH, SOUTH, EAST, WEST, UP, DOWN)) { 140 | if (hiddenBitwise and dir != 0) { 141 | hiddenFaces++ 142 | } 143 | } 144 | println("hidden faces for cube: $hiddenFaces")*/ 145 | 146 | if (hiddenBitwise and Direction.North == 0) { 147 | r.v00.set(0f, 0f, sz).add(origin) 148 | r.v10.set(sz, 0f, sz).add(origin) 149 | r.v11.set(sz, sz, sz).add(origin) 150 | r.v01.set(0f, sz, sz).add(origin) 151 | r.normal.set(0f, 0f, 1f) 152 | addRect(r) 153 | } 154 | 155 | if (hiddenBitwise and Direction.South == 0) { 156 | r.v00.set(sz, 0f, 0f).add(origin) 157 | r.v10.set(0f, 0f, 0f).add(origin) 158 | r.v11.set(0f, sz, 0f).add(origin) 159 | r.v01.set(sz, sz, 0f).add(origin) 160 | r.normal.set(0f, 0f, -1f) 161 | addRect(r) 162 | } 163 | 164 | if (hiddenBitwise and Direction.Down == 0) { 165 | r.v00.set(0f, 0f, 0f).add(origin) 166 | r.v10.set(sz, 0f, 0f).add(origin) 167 | r.v11.set(sz, 0f, sz).add(origin) 168 | r.v01.set(0f, 0f, sz).add(origin) 169 | r.normal.set(0f, -1f, 0f) 170 | addRect(r) 171 | } 172 | 173 | if (hiddenBitwise and Direction.Up == 0) { 174 | r.v00.set(sz, sz, 0f).add(origin) 175 | r.v10.set(0f, sz, 0f).add(origin) 176 | r.v11.set(0f, sz, sz).add(origin) 177 | r.v01.set(sz, sz, sz).add(origin) 178 | r.normal.set(0f, 1f, 0f) 179 | addRect(r) 180 | } 181 | 182 | 183 | if (hiddenBitwise and Direction.West == 0) { 184 | r.v00.set(0f, 0f, 0f).add(origin) 185 | r.v10.set(0f, 0f, sz).add(origin) 186 | r.v11.set(0f, sz, sz).add(origin) 187 | r.v01.set(0f, sz, 0f).add(origin) 188 | r.normal.set(-1f, 0f, 0f) 189 | addRect(r) 190 | } 191 | 192 | if (hiddenBitwise and Direction.East == 0) { 193 | r.v00.set(sz, 0f, sz).add(origin) 194 | r.v10.set(sz, 0f, 0f).add(origin) 195 | r.v11.set(sz, sz, 0f).add(origin) 196 | r.v01.set(sz, sz, sz).add(origin) 197 | r.normal.set(1f, 0f, 0f) 198 | addRect(r) 199 | } 200 | } 201 | 202 | private fun triangle(a: Vector3, b: Vector3, c: Vector3, nor: Vector3, uvA: Vector2, uvB: Vector2, uvC: Vector2) { 203 | triangles++ 204 | vertex(a, nor, uvA) 205 | vertex(b, nor, uvB) 206 | vertex(c, nor, uvC) 207 | } 208 | 209 | private fun vertex(v: Vector3, nor: Vector3, uv: Vector2) { 210 | vertexCount++ 211 | 212 | // POSITION 213 | verts[idx++] = v.x 214 | verts[idx++] = v.y 215 | verts[idx++] = v.z 216 | 217 | // TEXTURE_UV 218 | verts[idx++] = uv.x 219 | verts[idx++] = uv.y 220 | 221 | // NORMAL 222 | verts[idx++] = nor.x 223 | verts[idx++] = nor.y 224 | verts[idx++] = nor.z 225 | } 226 | 227 | fun dispose() { 228 | mesh.dispose() 229 | } 230 | } 231 | 232 | class RectData() { 233 | val v00 = Vector3() 234 | val v10 = Vector3() 235 | val v01 = Vector3() 236 | val v11 = Vector3() 237 | val normal = Vector3() 238 | val uv00 = Vector2() 239 | val uv10 = Vector2() 240 | val uv01 = Vector2() 241 | val uv11 = Vector2() 242 | } 243 | -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/gameworld/CubeData.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.gameworld 2 | 3 | import com.badlogic.gdx.utils.Array as Arr 4 | import com.badlogic.gdx.math.Vector3 5 | import org.jrenner.learngl.Direction 6 | 7 | 8 | class CubeData { 9 | companion object { 10 | private val tmp = Vector3() 11 | } 12 | public val xf: Float get() = x.toFloat() 13 | public val yf: Float get() = y.toFloat() 14 | public val zf: Float get() = z.toFloat() 15 | public var x: Short = 0 16 | public var y: Short = 0 17 | public var z: Short = 0 18 | var cubeType = CubeType.Void 19 | var hiddenFaces: Int = 0 20 | val hiddenFacesCount: Int 21 | get() { 22 | var c = 0 23 | for (i in 0 until Direction.allSize) { 24 | if (hiddenFaces and Direction.all[i] != 0) { 25 | c++ 26 | } 27 | } 28 | return c 29 | } 30 | 31 | val debugHiddenFaces: String 32 | get() { 33 | var hidden = "" 34 | for (i in 0 until Direction.allSize) { 35 | val dir = Direction.all[i] 36 | if (hiddenFaces and dir != 0) { 37 | hidden += ", ${Direction.toString(dir)}" 38 | } 39 | } 40 | return hidden 41 | } 42 | 43 | fun getPositionTempVec(): Vector3 { 44 | return tmp.set(xf, yf, zf) 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/gameworld/CubeTypes.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.gameworld 2 | 3 | enum class CubeType { 4 | Void, 5 | Grass, 6 | Water 7 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/gameworld/World.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.gameworld 2 | 3 | import org.jrenner.learngl.cube.CubeDataGrid 4 | import com.badlogic.gdx.math.Vector3 5 | import com.badlogic.gdx.utils.Array as Arr 6 | import com.badlogic.gdx.math.MathUtils 7 | import com.badlogic.gdx.utils.ObjectSet 8 | import org.jrenner.learngl.cube.WorldChunkData 9 | import com.badlogic.gdx.utils.DelayedRemovalArray 10 | import com.badlogic.gdx.utils.GdxRuntimeException 11 | import com.badlogic.gdx.utils.ObjectMap 12 | import org.jrenner.learngl.* 13 | import org.jrenner.learngl.utils.calculateHiddenFaces 14 | import org.jrenner.learngl.utils.threeIntegerHashCode 15 | 16 | class World(val width: Int, val height: Int, val depth: Int) { 17 | 18 | var updater: WorldUpdater? = null 19 | 20 | var updatesEnabled = true 21 | 22 | private fun chunkCount(dimen: Int): Int { 23 | val chunkSize = Chunk.chunkSize 24 | return (if (dimen % chunkSize > 0) 1 else 0) + dimen / chunkSize 25 | } 26 | 27 | var chunksCreated = 0L 28 | var chunksRemoved = 0L 29 | 30 | val numChunksX = chunkCount(width) 31 | val numChunksY = chunkCount(height) 32 | val numChunksZ = chunkCount(depth) 33 | /** Array of existing chunks for fast iteration */ 34 | val chunks = DelayedRemovalArray(200) 35 | /** map of chunk hashCodes to Chunks, mostly for fast checking of chunk existence in the world*/ 36 | val chunkHashCodeMap = ObjectMap(200) 37 | /** keeps track of which chunks are queued for creation */ 38 | val chunkCreationQueue = ObjectSet() 39 | val worldData = WorldChunkData(width, height, depth, this) 40 | 41 | fun processCreationQueue() { 42 | synchronized(this) { 43 | if (chunkCreationQueue.size == 0) return 44 | val cdg = chunkCreationQueue.minBy { it.center.dst2(view.camera.position) }!! 45 | //val cdg = chunkCreationQueue.first() 46 | chunkCreationQueue.remove(cdg) 47 | val chunk = Chunk.obtain(cdg) 48 | addChunk(chunk) 49 | } 50 | } 51 | 52 | fun removeChunksOutOfViewRange() { 53 | synchronized(this) { 54 | chunks.begin() 55 | for (i in 0 until chunks.size) { 56 | val chunk = chunks[i] 57 | val dist2 = chunk.dataGrid.center.dst2(view.camera.position) 58 | if (dist2 > View.maxViewDist * View.maxViewDist) { 59 | val hash: Int = chunk.hashCode() 60 | chunks.removeIndex(i) 61 | chunkHashCodeMap.remove(hash) 62 | chunk.dispose() 63 | chunksRemoved++ 64 | } 65 | } 66 | chunks.end() 67 | } 68 | } 69 | 70 | //var updatedOnce = false 71 | 72 | fun update(dt: Float) { 73 | initUpdater() 74 | // the bigger the backlog, the harder we work to catch up 75 | val chunksPerFrame = 1 + chunkCreationQueue.size / 10 76 | for (n in 1..chunksPerFrame) { 77 | processCreationQueue() 78 | } 79 | if (frame % 30 == 0L) { 80 | removeChunksOutOfViewRange() 81 | } 82 | for (chunk in chunks) { 83 | chunk.update() 84 | } 85 | } 86 | 87 | fun initUpdater() { 88 | if (!updatesEnabled) return 89 | if (updater == null) { 90 | updater =WorldUpdater(this) 91 | val t = Thread(updater) 92 | t.setName("WorldUpdater") 93 | t.start() 94 | } 95 | } 96 | 97 | /** @see CubeDataGrid.origin */ 98 | fun snapToChunkOrigin(value: Float): Float { 99 | //val centerOffset = Chunk.chunkSize / 2f 100 | val div = MathUtils.floor(value) / Chunk.chunkSize 101 | return (div * Chunk.chunkSize).toFloat() 102 | } 103 | 104 | /** @see CubeDataGrid.center */ 105 | fun snapToChunkCenter(value: Float): Float { 106 | return snapToChunkOrigin(value) + Chunk.chunkSize / 2 107 | } 108 | 109 | /** for use in tests mostly */ 110 | fun calculateHiddenFaces() { 111 | for (chunk in chunks) { 112 | chunk.dataGrid.calculateHiddenFaces(this) 113 | } 114 | } 115 | 116 | fun hasCubeAt(x: Float, y: Float, z: Float): Boolean { 117 | if (!hasChunkAt(x, y, z)) { 118 | return false 119 | } else { 120 | val chunk = getChunkAt(x, y, z) 121 | if (chunk.dataGrid.hasCubeAt(x, y, z)) { 122 | return true 123 | } else { 124 | throw GdxRuntimeException("world.hasCubeAt($x, $y, $z), error: world has cube, but chunk doesn't!") 125 | } 126 | } 127 | } 128 | 129 | fun getCubeAt(x: Float, y: Float, z: Float):CubeData { 130 | val chunk = getChunkAt(x, y, z) 131 | return chunk.dataGrid.getCubeAt(x, y, z) 132 | } 133 | 134 | fun hasChunkAt(x: Float, y: Float, z: Float): Boolean { 135 | val sx = snapToChunkOrigin(x).toInt() 136 | val sy = snapToChunkOrigin(y).toInt() 137 | val sz = snapToChunkOrigin(z).toInt() 138 | return chunkHashCodeMap.containsKey(threeIntegerHashCode(sx, sy, sz)) 139 | /* for (i in 0..chunks.size - 1) { 140 | val chunk = chunks[i] 141 | if (chunk.dataGrid.hasCubeAt(x, y, z)) { 142 | return true 143 | } 144 | } 145 | return false*/ 146 | } 147 | 148 | fun addChunk(chunk: Chunk) { 149 | chunks.add(chunk) 150 | chunkHashCodeMap.put(chunk.hashCode(), chunk) 151 | chunksCreated++ 152 | } 153 | 154 | fun getChunkAt(x: Float, y: Float, z: Float): Chunk { 155 | val sx = snapToChunkOrigin(x).toInt() 156 | val sy = snapToChunkOrigin(y).toInt() 157 | val sz = snapToChunkOrigin(z).toInt() 158 | return chunkHashCodeMap.get(threeIntegerHashCode(sx, sy, sz), null)!! 159 | /*for (i in 0..chunks.size - 1) { 160 | val chunk = chunks[i] 161 | if(chunk.dataGrid.hasCubeAt(x, y, z)) return chunk 162 | }*/ 163 | //throw GdxRuntimeException("no chunk contains cube at: $x, $y, $z") 164 | } 165 | 166 | //val elevTimer = SimpleTimer("Elevations") 167 | 168 | // for finding elevation 169 | private val verticalChunks = Arr() 170 | 171 | fun getElevation(x: Float, z: Float): Int { 172 | //elevTimer.start() 173 | verticalChunks.clear() 174 | var y = world.height.toFloat() 175 | while(y >= 0f) { 176 | if (hasChunkAt(x, y, z)) { 177 | verticalChunks.add(getChunkAt(x, y, z)) 178 | } 179 | y -= CubeDataGrid.height 180 | } 181 | var result = -1 182 | for (chunk in verticalChunks) { 183 | val elev = chunk.dataGrid.getElevation(x, z) 184 | if (elev > -1) { 185 | result = elev 186 | break 187 | } 188 | } 189 | //elevTimer.stop() 190 | return result 191 | } 192 | 193 | private val bboxElevations = IntArray(4) 194 | 195 | fun getBoundingBoxElevation(x: Float, z: Float, width: Float, depth: Float): Int { 196 | val elevs = bboxElevations 197 | elevs[0] = getElevation(x, z) 198 | elevs[1] = getElevation(x+width, z) 199 | elevs[2] = getElevation(x+width, z+depth) 200 | elevs[3] = getElevation(x, z+depth) 201 | return elevs.max()!! 202 | } 203 | 204 | /** should only be used by test package */ 205 | fun createAllChunks() { 206 | chunks.clear() 207 | val chunkSize = Chunk.chunkSize 208 | var x: Float 209 | var y = 0f 210 | var z: Float 211 | var chunkCount = 0 212 | val origin = Vector3() 213 | while (y < height) { 214 | x = 0f 215 | while (x < width) { 216 | z = 0f 217 | while (z < depth) { 218 | chunkCount++ 219 | /*var chunkWidth = Math.min(chunkSize, (width - x).toInt()) 220 | var chunkHeight = Math.min(chunkSize, (height - y).toInt()) 221 | var chunkDepth = Math.min(chunkSize, (depth - z).toInt())*/ 222 | //println("[$chunkCount]chunk divide w,h,d: $width, $height, $depth") 223 | origin.set(x, y, z) 224 | val cdg = CubeDataGrid.create(origin.x, origin.y, origin.z) 225 | cdg.init(origin.x, origin.y, origin.z) 226 | for (chunk in cdg) { 227 | chunk.cubeType = CubeType.Grass 228 | } 229 | val chunk = Chunk.obtain(cdg) 230 | addChunk(chunk) 231 | z += chunkSize 232 | } 233 | x += chunkSize 234 | } 235 | y += chunkSize 236 | } 237 | } 238 | 239 | class NoiseLayer(val freq: Float, val weight: Float) { 240 | val elevations = Array(Chunk.chunkSize, { FloatArray(Chunk.chunkSize) }) 241 | fun generateNoise(originX: Float, originZ: Float) { 242 | SimplexNoise.generateSimplexNoise(originX.toInt(), originZ.toInt(), CubeDataGrid.width, CubeDataGrid.depth, freq, elevations) 243 | } 244 | } 245 | 246 | object NoiseLayerManager { 247 | val allLayers = Arr() 248 | 249 | fun addLayer(freq: Float, weight: Float) { 250 | allLayers.add(NoiseLayer(freq, weight)) 251 | } 252 | 253 | init { 254 | addLayer(0.003f, 1.0f) 255 | addLayer(0.01f, 0.2f) 256 | addLayer(0.03f, 0.1f) 257 | } 258 | 259 | fun generateAllNoise(cdg: CubeDataGrid) { 260 | for (layer in allLayers) { 261 | layer.generateNoise(cdg.origin.x, cdg.origin.z) 262 | } 263 | } 264 | 265 | fun getNoise(x: Int, z: Int): Float { 266 | var noise = 0f 267 | for (i in 0..allLayers.size-1) { 268 | val layer = allLayers[i] 269 | noise += layer.elevations[x][z] * layer.weight 270 | } 271 | return noise 272 | } 273 | } 274 | 275 | fun applyWorldData(cdg: CubeDataGrid, wor: World) { 276 | //val start = TimeUtils.nanoTime() 277 | NoiseLayerManager.generateAllNoise(cdg) 278 | for (cube in cdg) { 279 | val x = (cube.xf - cdg.origin.x).toInt() 280 | val z = (cube.zf - cdg.origin.z).toInt() 281 | val elev = NoiseLayerManager.getNoise(x, z) * wor.height 282 | if (cube.yf > elev) { 283 | cube.cubeType = CubeType.Void 284 | } else { 285 | cube.cubeType = CubeType.Grass 286 | } 287 | } 288 | //val elapsed = TimeUtils.nanoTime() - start 289 | //println("applyWorldData: ${TimeUtils.nanosToMillis(elapsed)}") 290 | } 291 | 292 | fun processUpdateFromWorldUpdater(upd:WorldUpdater) { 293 | for (cdg in upd.tempCreationQueue) { 294 | chunkCreationQueue.add(cdg) 295 | } 296 | } 297 | 298 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/gameworld/WorldUpdater.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.gameworld 2 | 3 | import com.badlogic.gdx.math.MathUtils 4 | import com.badlogic.gdx.math.Vector3 5 | import com.badlogic.gdx.utils.Array as Arr 6 | import org.jrenner.learngl.cube.CubeDataGrid 7 | import org.jrenner.learngl.View 8 | import org.jrenner.learngl.utils.threeIntegerHashCode 9 | import com.badlogic.gdx.math.Frustum 10 | import com.badlogic.gdx.utils.IntSet 11 | import org.jrenner.learngl.utils.inFrustum 12 | import org.jrenner.learngl.world 13 | 14 | /** updates chunks in the world on a separate Thread */ 15 | class WorldUpdater(val wor: World): Runnable { 16 | 17 | /** hold here to pass to world thread when finished updating */ 18 | val tempCreationQueue = Arr() 19 | /** this temporarily stores data from the World's map of chunk hash codes -> chunks 20 | * for thread safety. 21 | * It is used to check if the chunks is either already created 22 | * or already queued for creation 23 | * the world updater 24 | */ 25 | val tempChunkHashCodeSet = IntSet() 26 | val tempChunks = Arr() 27 | 28 | /** camPos and frustum will be set by the View in View.render */ 29 | val tempCamPos = Vector3() 30 | val tempFrustum = Frustum() 31 | 32 | val updateIntervalMillis = 250L 33 | var maxDist = View.maxViewDist 34 | val queueLimit = 10 35 | var worldQueueSize = 0 36 | 37 | override fun run() { 38 | try { 39 | while (true) { 40 | synchronized(wor) { 41 | retrieveDataFromWorld() 42 | } 43 | createChunksInViewRange() 44 | synchronized(wor) { 45 | communicateUpdateToWorld() 46 | } 47 | Thread.sleep(updateIntervalMillis) 48 | } 49 | } catch (e: Exception) { 50 | // if we have failed at thread-safety, just crash with a message 51 | println("ERROR in WorldUpdater thread:\n") 52 | e.printStackTrace() 53 | System.exit(1) 54 | } 55 | } 56 | 57 | fun retrieveDataFromWorld() { 58 | tempChunkHashCodeSet.clear() 59 | for (item in wor.chunkHashCodeMap.keys()) { 60 | tempChunkHashCodeSet.add(item) 61 | } 62 | for (item in wor.chunkCreationQueue) { 63 | val hash = item.hashCode() 64 | tempChunkHashCodeSet.add(hash) 65 | } 66 | worldQueueSize = wor.chunkCreationQueue.size 67 | } 68 | 69 | 70 | fun communicateUpdateToWorld() { 71 | wor.processUpdateFromWorldUpdater(this) 72 | tempCreationQueue.clear() 73 | } 74 | 75 | /** see: World.hasChunkAt */ 76 | fun worldUpdaterHasChunkAt(x: Float, y: Float, z: Float): Boolean { 77 | val sx = wor.snapToChunkOrigin(x).toInt() 78 | val sy = wor.snapToChunkOrigin(y).toInt() 79 | val sz = wor.snapToChunkOrigin(z).toInt() 80 | return tempChunkHashCodeSet.contains(threeIntegerHashCode(sx, sy, sz)) 81 | } 82 | 83 | fun createChunksInViewRange() { 84 | if (worldQueueSize > queueLimit) return 85 | /*if (chunkCreationQueue.size >= queueSizeLimit) { 86 | return 87 | }*/ 88 | val camPos = tempCamPos 89 | val loX = MathUtils.clamp(camPos.x - maxDist, 0f, world.width.toFloat()); 90 | val loY = MathUtils.clamp(camPos.y - maxDist, 0f, world.height.toFloat()); 91 | val loZ = MathUtils.clamp(camPos.z - maxDist, 0f, world.depth.toFloat()); 92 | val hiX = MathUtils.clamp(camPos.x + maxDist, 0f, world.width.toFloat()); 93 | val hiY = MathUtils.clamp(camPos.y + maxDist, 0f, world.height.toFloat()); 94 | val hiZ = MathUtils.clamp(camPos.z + maxDist, 0f, world.depth.toFloat()); 95 | val sz: Float = Chunk.chunkSizef 96 | 97 | fun createChunkIfNeeded(x: Float, y: Float, z: Float) { 98 | val chunkX = wor.snapToChunkCenter(x) 99 | val chunkY = wor.snapToChunkCenter(y) 100 | val chunkZ = wor.snapToChunkCenter(z) 101 | // does this chunk already exist? 102 | val hasChunk = worldUpdaterHasChunkAt(chunkX, chunkY, chunkZ) 103 | // lazily create chunks only when the camera looks at them 104 | val inView = inFrustum(chunkX, chunkY, chunkZ, Chunk.chunkSizef, tempFrustum) 105 | if (!hasChunk && inView) { 106 | val dist2 = camPos.dst2(chunkX, chunkY, chunkZ) 107 | // IN RANGE 108 | if (dist2 <= maxDist * maxDist) { 109 | val origin = Vector3(wor.snapToChunkOrigin(x), wor.snapToChunkOrigin(y), wor.snapToChunkOrigin(z)) 110 | val cdg = wor.worldData.getCDGByWorldPos(origin.x, origin.y, origin.z) 111 | //println("WORLD UPDATER: added to temp queue (${origin.x}, ${origin.y}, ${origin.z})") 112 | tempCreationQueue.add(cdg) 113 | } 114 | } 115 | } 116 | var x = loX 117 | var y: Float 118 | var z: Float 119 | while(x <= hiX) { 120 | y = loY 121 | while(y <= hiY) { 122 | z = loZ 123 | while (z <= hiZ) { 124 | createChunkIfNeeded(x, y, z) 125 | z +=sz 126 | } 127 | y += sz 128 | } 129 | x += sz 130 | } 131 | /* 132 | Original code used for iteration: 133 | for (x in loX..hiX step sz) { 134 | for (y in loY..hiY step sz) { 135 | for (z in loZ..hiZ step sz) { 136 | ... 137 | Kotlin converts the float primitives into boxed Float objects in order to use Float method invocations 138 | Current iteration method avoids object allocation (GC optimization) 139 | */ 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/input/GameInput.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.input 2 | 3 | import com.badlogic.gdx.InputAdapter 4 | import com.badlogic.gdx.graphics.g3d.utils.FirstPersonCameraController 5 | import com.badlogic.gdx.Gdx 6 | import com.badlogic.gdx.InputMultiplexer 7 | import kotlin.properties.Delegates 8 | import org.jrenner.learngl.View 9 | import com.badlogic.gdx.Input.Keys 10 | import org.jrenner.learngl.main 11 | import org.jrenner.learngl.view 12 | 13 | class GameInput { 14 | val input = Gdx.input!! 15 | val multi = InputMultiplexer() 16 | var proc: InputAdapter by Delegates.notNull() 17 | val camControl: FirstPersonCameraController get() = view.camControl 18 | 19 | init { 20 | proc = createMainProcessor() 21 | resetProcessors() 22 | } 23 | 24 | fun resetProcessors() { 25 | multi.clear() 26 | input.setInputProcessor(multi) 27 | multi.addProcessor(proc) 28 | multi.addProcessor(camControl) 29 | } 30 | 31 | fun update(dt: Float) { 32 | camControl.update(dt) 33 | } 34 | 35 | 36 | fun createMainProcessor(): InputAdapter { 37 | return object : InputAdapter() { 38 | 39 | val viewDistChangeDelta: Float get() = if (Gdx.input.isKeyPressed(Keys.SHIFT_LEFT)) 100f else 10f 40 | 41 | override fun keyDown(keycode: Int): Boolean { 42 | when (keycode) { 43 | Keys.G -> main.profileRequested = true // profile OpenGL 44 | Keys.V -> { 45 | main.resetViewRequested = true 46 | } 47 | /*Keys.H -> { 48 | learngl.hud.enabled = !learngl.hud.enabled 49 | println("HUD enabled: ${learngl.hud.enabled}") 50 | }*/ 51 | /* Keys.F -> { 52 | learngl.hiddenFacesEnabled = !learngl.hiddenFacesEnabled 53 | println("hideFaces: $org.jrenner.learngl.hiddenFacesEnabled") 54 | }*/ 55 | Keys.PLUS -> { 56 | View.maxViewDist += viewDistChangeDelta 57 | println("MAX VIEW DIST: ${View.maxViewDist}") 58 | } 59 | Keys.MINUS -> { 60 | 61 | View.maxViewDist -= viewDistChangeDelta 62 | println("MAX VIEW DIST: ${View.maxViewDist}") 63 | } 64 | Keys.SPACE -> { 65 | view.walkingEnabled = !view.walkingEnabled 66 | println("walking mode: ${view.walkingEnabled}") 67 | when (view.walkingEnabled) { 68 | true -> view.camControl.setVelocity(View.WALKING_MAX_VELOCITY) 69 | false -> view.camControl.setVelocity(View.FLYING_MAX_VELOCITY) 70 | } 71 | 72 | } 73 | else -> Unit 74 | } 75 | return false 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/light/DirectionalLight.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.light 2 | 3 | import com.badlogic.gdx.graphics.Color 4 | import com.badlogic.gdx.math.Vector3 5 | import org.jrenner.learngl.view 6 | 7 | class DirectionalLight { 8 | val color = Color() 9 | val direction = Vector3() 10 | 11 | var colorLoc = -1 12 | var directionLoc = -1 13 | 14 | fun setUniforms() { 15 | val shader = view.shader 16 | shader.setUniformf(colorLoc, color.r, color.g, color.b) 17 | shader.setUniformf(directionLoc, direction.x, direction.y, direction.z) 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/light/Lights.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.light 2 | import com.badlogic.gdx.utils.Array as Arr 3 | import org.jrenner.learngl.view 4 | import com.badlogic.gdx.graphics.Color 5 | import com.badlogic.gdx.utils.GdxRuntimeException 6 | import org.jrenner.learngl.utils.randomizeColor 7 | 8 | class Lights { 9 | val pointLights = Arr() 10 | val dirLight = DirectionalLight() 11 | val uniformAmbient = 0f 12 | val ambientLight = Color(uniformAmbient, uniformAmbient, uniformAmbient, 1.0f) 13 | var ambientLoc = -1 14 | 15 | fun update(dt: Float) { 16 | for (pl in pointLights) { 17 | pl.update(dt) 18 | } 19 | } 20 | 21 | fun setLocations() { 22 | val shader = view.shader 23 | ambientLoc = shader.getUniformLocation("u_ambientLight") 24 | if (ambientLoc == -1) throw GdxRuntimeException("couldn't get location for u_ambientLight from shader") 25 | for (i in 0..pointLights.size - 1) { 26 | val pl = pointLights[i] 27 | pl.posLoc = shader.getUniformLocation("u_pointLights[$i].pos") 28 | pl.colorLoc = shader.getUniformLocation("u_pointLights[$i].color") 29 | pl.intensityLoc = shader.getUniformLocation("u_pointLights[$i].intensity") 30 | //println("PointLight locations, pos: ${pl.posLoc}, color: ${pl.colorLoc}") 31 | if (pl.posLoc == -1 || pl.colorLoc == -1 || pl.intensityLoc == -1) throw GdxRuntimeException("bad shader location(s) for PointLight") 32 | } 33 | dirLight.colorLoc = shader.getUniformLocation("u_dirLight.color") 34 | dirLight.directionLoc = shader.getUniformLocation("u_dirLight.direction") 35 | if (dirLight.colorLoc == -1 || dirLight.directionLoc == -1) throw GdxRuntimeException("bad shader location(s), dirlight") 36 | } 37 | 38 | fun setUniforms() { 39 | view.shader.setUniformf(ambientLoc, ambientLight.r, ambientLight.g, ambientLight.b, ambientLight.a) 40 | for (pl in pointLights) { 41 | pl.setUniforms() 42 | } 43 | dirLight.setUniforms() 44 | } 45 | 46 | // CREATE LIGHTS 47 | init { 48 | val camLight = PointLight() 49 | pointLights.add(camLight) 50 | //pl.pos.set(0f, 0f, 0f) 51 | camLight.intensity = 10f 52 | camLight.attachedToCamera = true 53 | 54 | val intensity = 0.15f 55 | dirLight.color.set(intensity, intensity, intensity, 1.0f) 56 | dirLight.direction.set(-.5f, -1f, -.5f).nor() 57 | 58 | for (n in 1..10) { 59 | val pl = PointLight() 60 | randomizeColor(pl.color, min = 0.3f, max = 1.0f) 61 | pointLights.add(pl) 62 | } 63 | 64 | setLocations() 65 | } 66 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/light/PointLight.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.light 2 | 3 | import com.badlogic.gdx.math.Vector3 4 | import com.badlogic.gdx.graphics.Color 5 | import kotlin.properties.Delegates 6 | import org.jrenner.learngl.view 7 | import com.badlogic.gdx.utils.GdxRuntimeException 8 | import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder 9 | import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute 10 | import com.badlogic.gdx.graphics.g3d.Material 11 | import com.badlogic.gdx.graphics.VertexAttributes.Usage 12 | import com.badlogic.gdx.graphics.g3d.ModelInstance 13 | import com.badlogic.gdx.graphics.g3d.Model 14 | import org.jrenner.learngl.utils.r 15 | import org.jrenner.learngl.world 16 | import org.jrenner.learngl.TimedIntervalTask 17 | import com.badlogic.gdx.math.MathUtils 18 | import com.badlogic.gdx.math.Vector2 19 | import org.jrenner.learngl.lights 20 | import org.jrenner.learngl.utils.fmt 21 | 22 | class PointLight { 23 | 24 | val pos = Vector3() 25 | val color = Color(Color.WHITE) 26 | var intensity = 5.0f 27 | 28 | val accel = 0.005f 29 | val maxSpeed = 2.0f 30 | val velocity = Vector3() 31 | 32 | val debugModel: Model by lazy { 33 | val mb = ModelBuilder() 34 | val sz = 0.5f 35 | val div = 12 36 | val mat = Material(ColorAttribute.createDiffuse(color)) 37 | val attr = (Usage.Position or Usage.ColorPacked).toLong() 38 | mb.createSphere(sz, sz, sz, div, div, mat, attr) 39 | } 40 | 41 | val debugInstance: ModelInstance by lazy { 42 | ModelInstance(debugModel) 43 | } 44 | 45 | var destX = 0f 46 | var destZ = 0f 47 | init { 48 | setNextDest() 49 | } 50 | 51 | val xOffset = -2f 52 | 53 | fun setNextDest() { 54 | //dest.set(xOffset, r(world.height.toFloat()), r(world.depth.toFloat())) 55 | val cam = view.camera.position 56 | val limit = 80f 57 | destX = r(cam.x - limit, cam.x + limit) 58 | destZ = r(cam.z - limit, cam.z + limit) 59 | } 60 | 61 | var posLoc = -1 62 | var colorLoc = -1 63 | var intensityLoc = -1 64 | 65 | var attachedToCamera = false 66 | 67 | val tmp = Vector3() 68 | var elevation = 0f 69 | var elevationOffset = r(5f, 20f) 70 | 71 | val elevationUpdater = TimedIntervalTask(intervalSeconds = 0.25f) { 72 | elevation = world.getElevation(pos.x, pos.z).toFloat() 73 | } 74 | 75 | fun update(dt: Float) { 76 | elevationUpdater.update(dt) 77 | if (view.debug) { 78 | debugInstance.transform.setToTranslation(pos) 79 | } 80 | if (attachedToCamera) { 81 | pos.set(view.camera.position) 82 | return 83 | } 84 | val diff = tmp.set(destX, 0f, destZ).sub(pos.x, 0f, pos.z) 85 | val dist = diff.len() 86 | velocity.y = 0f 87 | if (dist < 1f) { 88 | setNextDest() 89 | } else { 90 | velocity.add(diff.nor().scl(accel)) 91 | } 92 | velocity.scl(0.98f) 93 | pos.add(velocity.x, 0f, velocity.z) 94 | pos.y = MathUtils.lerp(pos.y, elevation + elevationOffset, 0.05f) 95 | } 96 | 97 | fun setUniforms() { 98 | val shader = view.shader 99 | shader.setUniformf(posLoc, pos.x, pos.y, pos.z) 100 | shader.setUniformf(colorLoc, color.r, color.g, color.b) 101 | shader.setUniformf(intensityLoc, intensity) 102 | } 103 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/utils/Extensions.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.utils 2 | 3 | import com.badlogic.gdx.math.Vector3 4 | import org.jrenner.learngl.cube.CubeDataGrid 5 | import org.jrenner.learngl.Direction 6 | import com.badlogic.gdx.utils.StringBuilder 7 | import org.jrenner.learngl.gameworld.World 8 | import org.jrenner.learngl.gameworld.CubeData 9 | import org.jrenner.learngl.gameworld.CubeType 10 | import org.jrenner.learngl.hiddenFacesEnabled 11 | import org.jrenner.learngl.world 12 | 13 | class IntVector3 { 14 | var x: Int = 0 15 | var y: Int = 0 16 | var z: Int = 0 17 | 18 | fun set(x: Int, y: Int, z: Int): IntVector3 { 19 | this.x = x 20 | this.y = y 21 | this.z = z 22 | return this 23 | } 24 | 25 | fun set(v: IntVector3): IntVector3 { 26 | return set(v.x, v.y, v.z) 27 | } 28 | 29 | fun set(v: Vector3): IntVector3 { 30 | return set(v.x.toInt(), v.y.toInt(), v.z.toInt()) 31 | } 32 | 33 | fun toVector3(v: Vector3) { 34 | v.set(x.toFloat(), y.toFloat(), z.toFloat()) 35 | } 36 | } 37 | 38 | fun World.get(v3: Vector3): CubeData { 39 | return this.getCubeAt(v3.x, v3.y, v3.z) 40 | } 41 | 42 | fun CubeDataGrid.refresh() { 43 | if (dirty) { 44 | dirty = false 45 | calculateHiddenFaces(world) 46 | } 47 | } 48 | 49 | fun CubeDataGrid.calculateHiddenFaces(wor: World = world) { 50 | for (cubeData: CubeData in this) { 51 | cubeData.hiddenFaces = 0 52 | if (!hiddenFacesEnabled) continue 53 | if (cubeData.cubeType == CubeType.Void) { 54 | continue 55 | //cubeData.hiddenFaces = Direction.ALL_FACES 56 | } else { 57 | for (i in 0..Direction.allSize - 1) { 58 | var x = cubeData.xf 59 | var y = cubeData.yf 60 | var z = cubeData.zf 61 | val dir = Direction.all[i] 62 | when (dir) { 63 | Direction.Up -> y++ 64 | Direction.Down -> y-- 65 | Direction.North -> z++ 66 | Direction.South -> z-- 67 | Direction.West -> x-- 68 | Direction.East -> x++ 69 | } 70 | // only care about hidden faces local to this cube 71 | if (this.hasCubeAt(x, y, z)) { 72 | val cube = getCubeAt(x, y, z) 73 | if (cube.cubeType != CubeType.Void) { 74 | // HIDDEN 75 | cubeData.hiddenFaces = cubeData.hiddenFaces or dir 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } 82 | 83 | operator fun StringBuilder.plus(any: Any?): StringBuilder { 84 | return this.append(any) 85 | } 86 | 87 | val Float.fmt: String get() = "%.2f".format(this) 88 | val Vector3.fmt: String get() = "%.2f, %.2f, %.2f".format(x, y, z) -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/utils/SimpleTimer.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.utils 2 | 3 | import com.badlogic.gdx.utils.TimeUtils 4 | import com.badlogic.gdx.utils.GdxRuntimeException 5 | 6 | class SimpleTimer(val name: String) { 7 | var total = 0L 8 | var count = 0 9 | 10 | var active = false 11 | 12 | private var startTime = 0L 13 | 14 | fun start() { 15 | if (active) throw GdxRuntimeException("already started!") 16 | active = true 17 | startTime = TimeUtils.nanoTime() 18 | } 19 | 20 | fun stop() { 21 | if (!active) throw GdxRuntimeException("not started!") 22 | active = false 23 | val elapsed = TimeUtils.nanoTime() - startTime 24 | total += elapsed 25 | count++ 26 | } 27 | 28 | private fun Long.fmt(): String { 29 | return "%.2f ms".format(this.toDouble() / 1000000.0) 30 | } 31 | 32 | 33 | fun report() { 34 | val avg: Long 35 | if (count == 0) { 36 | avg = -1L 37 | } else { 38 | avg = total / count 39 | } 40 | println("Timer [$name]: count: $count, total: ${total.fmt()}, average: ${avg.fmt()}") 41 | } 42 | 43 | fun reset() { 44 | total = 0L 45 | count = 0 46 | active = false 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/kotlin/org/jrenner/learngl/utils/Tools.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.utils 2 | 3 | import com.badlogic.gdx.math.MathUtils 4 | import com.badlogic.gdx.graphics.Color 5 | import com.badlogic.gdx.math.Frustum 6 | import com.badlogic.gdx.math.Vector2 7 | import com.badlogic.gdx.math.Vector3 8 | import com.badlogic.gdx.math.Plane 9 | import com.badlogic.gdx.math.Intersector 10 | 11 | fun r(f: Float) = MathUtils.random(f) 12 | fun r(f1: Float, f2: Float) = MathUtils.random(f1, f2) 13 | fun r(n: Int) = MathUtils.random(n) 14 | fun r(n1: Int, n2: Int) = MathUtils.random(n1, n2) 15 | 16 | fun randomizeColor(color: Color, min: Float = 0.0f, max: Float = 1.0f): Color { 17 | color.r = r(min, max) 18 | color.g = r(min, max) 19 | color.b = r(min, max) 20 | return color 21 | } 22 | 23 | public fun threeIntegerHashCode(a: Int, b: Int, c: Int): Int { 24 | // h = (a*P1 + b)*P2 + c 25 | val prime1 = 1013 26 | val prime2 = 7499 27 | return (a * prime1 + b) * prime2 + c 28 | } 29 | 30 | fun inFrustum(x: Float, y: Float, z: Float, bboxRadius: Float, frustum: Frustum): Boolean { 31 | val w = bboxRadius 32 | val h = bboxRadius 33 | val d = bboxRadius 34 | return frustum.boundsInFrustum(x, y, z, w, h, d) 35 | } 36 | -------------------------------------------------------------------------------- /countlines.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os, string 3 | 4 | print "Counting lines in all Kotlin (*.kt) files:" 5 | codefiles = [] 6 | 7 | dirinfo = os.walk(os.getcwd()) 8 | for item in dirinfo: 9 | for f in item[2]: 10 | if f[-2:] == 'kt': 11 | codefiles.append([item[0],"/",f]) 12 | 13 | def is_whitespace_only(line): 14 | for char in line: 15 | if char not in string.whitespace: 16 | return False 17 | return True 18 | 19 | lines = 0 20 | code_lines = 0 21 | for f in codefiles: 22 | this_lines = 0 23 | this_code_lines = 0 24 | with open("".join(f)) as fin: 25 | for line in fin: 26 | if is_whitespace_only(line): 27 | this_lines += 1 28 | else: 29 | this_lines += 1 30 | this_code_lines += 1 31 | lines += this_lines 32 | code_lines += this_code_lines 33 | gap = " " * (20 - len(f[2])) 34 | gap2 = " " * (8 - len(str(this_code_lines))) 35 | out ="%s%s - SLOC: %d%s(%d)" % (f[2], gap, this_code_lines, gap2, this_lines) 36 | gap3 = " " * (48 - len(out)) 37 | out = out + "%s%s.kt" % (gap3, f[0]) 38 | print out 39 | 40 | output = "Total lines of code: %d Not counting whitespace lines: %d" % (lines, code_lines) 41 | output_len = len(output) 42 | print "="*output_len 43 | print output 44 | print "="*output_len 45 | -------------------------------------------------------------------------------- /desktop/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "java" 2 | 3 | sourceCompatibility = 1.6 4 | sourceSets.main.java.srcDirs = [ "src/" ] 5 | 6 | project.ext.mainClassName = "org.jrenner.learngl.desktop.DesktopLauncher" 7 | project.ext.assetsDir = new File("../core/assets"); 8 | 9 | task run(dependsOn: classes, type: JavaExec) { 10 | main = project.mainClassName 11 | classpath = sourceSets.main.runtimeClasspath 12 | standardInput = System.in 13 | workingDir = project.assetsDir 14 | ignoreExitValue = true 15 | minHeapSize = "512m" 16 | } 17 | 18 | task dist(type: Jar) { 19 | from files(sourceSets.main.output.classesDir) 20 | from files(sourceSets.main.output.resourcesDir) 21 | from {configurations.compile.collect {zipTree(it)}} 22 | from files(project.assetsDir); 23 | 24 | manifest { 25 | attributes 'Main-Class': project.mainClassName 26 | } 27 | } 28 | 29 | dist.dependsOn classes 30 | 31 | eclipse { 32 | project { 33 | name = appName + "-desktop" 34 | linkedResource name: 'assets', type: '2', location: 'PARENT-1-PROJECT_LOC/core/assets' 35 | } 36 | } 37 | 38 | task afterEclipseImport(description: "Post processing after project generation", group: "IDE") { 39 | doLast { 40 | def classpath = new XmlParser().parse(file(".classpath")) 41 | new Node(classpath, "classpathentry", [ kind: 'src', path: 'assets' ]); 42 | def writer = new FileWriter(file(".classpath")) 43 | def printer = new XmlNodePrinter(new PrintWriter(writer)) 44 | printer.setPreserveWhitespace(true) 45 | printer.print(classpath) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /desktop/src/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: org.jrenner.learngl.desktop.DesktopLauncher 3 | 4 | -------------------------------------------------------------------------------- /desktop/src/org/jrenner/learngl/desktop/DesktopLauncher.java: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.desktop; 2 | 3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication; 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; 5 | import org.jrenner.learngl.Main; 6 | import org.jrenner.learngl.View; 7 | 8 | 9 | public class DesktopLauncher { 10 | 11 | public static void main (String[] args) { 12 | try { 13 | for (String arg : args) { 14 | processArg(arg); 15 | } 16 | } catch (Exception e) { 17 | System.err.println("ERROR PROCESSING COMMAND LINE ARGUMENTS:\n"); 18 | e.printStackTrace(); 19 | System.exit(0); 20 | } 21 | LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); 22 | config.backgroundFPS = 60; 23 | config.foregroundFPS = 60; 24 | config.vSyncEnabled = true; 25 | //config.width = 1024; 26 | //config.height = 768; 27 | config.width = 1880; 28 | config.height = 1000; 29 | config.fullscreen = false; 30 | config.samples = 0; 31 | new LwjglApplication(new Main(), config); 32 | } 33 | 34 | private static void processArg(String arg) throws Exception { 35 | String[] pieces = arg.split("="); 36 | if (pieces[0].equals("-dist")) { 37 | int viewDist = Integer.parseInt(pieces[1]); 38 | int max = 250; 39 | if (viewDist > max) { 40 | System.out.println("VIEW DIST CANNOT BE OVER " + max); 41 | System.exit(0); 42 | } else { 43 | View.Companion.setMaxViewDist(viewDist); 44 | System.out.println("COMMAND LINE ARG VIEW DIST SET: " + viewDist); 45 | } 46 | 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /generated-fonts/exo14/exo14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrenner/kotlin-voxel/1266cc7366fea7a069f3a545ac54cc0e753d457a/generated-fonts/exo14/exo14.png -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.daemon=true 2 | org.gradle.jvmargs=-Xms128m -Xmx512m 3 | org.gradle.configureondemand=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrenner/kotlin-voxel/1266cc7366fea7a069f3a545ac54cc0e753d457a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 18 19:05:26 CDT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-4.2.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'desktop', 'core', 'test' 2 | -------------------------------------------------------------------------------- /test/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "java" 2 | apply plugin: "kotlin" 3 | 4 | sourceCompatibility = 1.7 5 | [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' 6 | 7 | //sourceSets.main.java.srcDirs = [ "src/java", "src/kotlin" ] 8 | //testSourceSets.main.java.srcDirs = [ "src/kotlin" ] 9 | 10 | idea { 11 | module { 12 | //sourceDirs += file('src/main/java') 13 | 14 | testSourceDirs += file('src/kotlin') 15 | testSourceDirs += file('src/java') 16 | } 17 | } 18 | 19 | 20 | eclipse.project { 21 | name = appName + "-test" 22 | } 23 | -------------------------------------------------------------------------------- /test/src/kotlin/org/jrenner/learngl/test/CubeDataGridTest.kt: -------------------------------------------------------------------------------- 1 | package org.jrenner.learngl.test 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.* 5 | import org.jrenner.learngl.cube.CubeDataGrid 6 | import com.badlogic.gdx.utils.Array as Arr 7 | import com.badlogic.gdx.math.Vector3 8 | import org.jrenner.learngl.utils.IntVector3 9 | import com.badlogic.gdx.math.MathUtils 10 | import com.badlogic.gdx.utils.GdxRuntimeException 11 | import com.badlogic.gdx.utils.ObjectSet 12 | import org.jrenner.learngl.gameworld.Chunk 13 | import org.jrenner.learngl.gameworld.CubeData 14 | import org.jrenner.learngl.gameworld.CubeType 15 | import org.jrenner.learngl.gameworld.World 16 | import org.jrenner.learngl.utils.calculateHiddenFaces 17 | import org.jrenner.learngl.utils.threeIntegerHashCode 18 | import org.jrenner.learngl.world 19 | 20 | 21 | class CubeDataGridTest { 22 | 23 | fun basicCDG(): CubeDataGrid { 24 | val origin = Vector3(0f, 0f, 0f) 25 | return CubeDataGrid.create(origin.x, origin.y, origin.z) 26 | } 27 | 28 | val expectedNumElements = CubeDataGrid.width * CubeDataGrid.height * CubeDataGrid.depth 29 | 30 | 31 | @Test 32 | fun constructor() { 33 | val cdg = basicCDG() 34 | assertEquals(expectedNumElements, cdg.numElements) 35 | } 36 | 37 | @Test fun hashCodeTest() { 38 | val set = ObjectSet() 39 | val size = 100 40 | for (x in 0..size) { 41 | //println("hashCodeTest, x: $x") 42 | for (y in 0..size) { 43 | for (z in 0..size) { 44 | //val cdg = CubeDataGrid() 45 | //cdg.init(x.toFloat(), y.toFloat(), z.toFloat()) 46 | //val hash = cdg.hashCode() 47 | val hash = threeIntegerHashCode(x, y, z) 48 | assertFalse("duplicate hash codes must not exist for CubeDataGrid\n(xyz: $x, $y, $z) hash: $hash", 49 | set.contains(hash)) 50 | set.add(hash) 51 | } 52 | } 53 | } 54 | } 55 | 56 | @Test fun iteration() { 57 | // test multiple times to test reset() method 58 | val cdg = basicCDG() 59 | for (n in 1..3) { 60 | var count = 0 61 | for (item in cdg) { 62 | count++ 63 | } 64 | assertEquals(expectedNumElements, count) 65 | } 66 | } 67 | 68 | /** test iteration order of Y, then X, then Z */ 69 | @Test fun iterationOrder() { 70 | val cdg = basicCDG() 71 | val grid = cdg.grid 72 | val manualCollection = Arr() 73 | for (y in grid.indices) { 74 | for (x in grid[y].indices) { 75 | for (z in grid[y][x].indices) { 76 | val cubeData = grid[y][x][z] 77 | manualCollection.add(cubeData) 78 | } 79 | } 80 | } 81 | val iteratorCollection = Arr() 82 | for (cubeData in cdg) { 83 | iteratorCollection.add(cubeData) 84 | } 85 | assertEquals(manualCollection.size, iteratorCollection.size) 86 | val manualPos = Vector3() 87 | val iteratorPos = Vector3() 88 | for (i in 0 until manualCollection.size) { 89 | manualPos.set(manualCollection.get(i).getPositionTempVec()) 90 | iteratorPos.set(iteratorCollection.get(i).getPositionTempVec()) 91 | assertEquals(manualPos, iteratorPos) 92 | } 93 | } 94 | 95 | @Test fun hasCube() { 96 | val width = CubeDataGrid.width 97 | val height = CubeDataGrid.height 98 | val depth = CubeDataGrid.depth 99 | 100 | val cdg = basicCDG() 101 | val under = 0.99f 102 | for (x in 0 until width) { 103 | for (y in 0 until height) { 104 | for (z in 0 until depth) { 105 | assertTrue(cdg.hasCubeAt(x.toFloat(), y.toFloat(), z.toFloat())) 106 | assertTrue(cdg.hasCubeAt(x + under, y + under, z + under)) 107 | } 108 | } 109 | } 110 | assertTrue(cdg.hasCubeAt(0f, 0f, 0f)) 111 | assertFalse(cdg.hasCubeAt(-0.01f, -0.01f, -0.01f)) 112 | assertFalse(cdg.hasCubeAt(-0.01f, 1f, 1f)) 113 | assertFalse(cdg.hasCubeAt(width.toFloat(), height.toFloat(), depth.toFloat())) 114 | assertFalse(cdg.hasCubeAt(width + 0.01f, height + 0.01f, depth + 0.01f)) 115 | } 116 | 117 | @Test fun getNeighbor() { 118 | val width = CubeDataGrid.width 119 | val height = CubeDataGrid.height 120 | val depth = CubeDataGrid.depth 121 | val origin = Vector3(50f, 25f, 12f) 122 | val grid = CubeDataGrid.create(origin.x, origin.y, origin.z) 123 | assertTrue(grid.hasCubeAt(origin)) 124 | assertTrue(grid.hasCubeAt(origin.x + width-1, origin.y + height-1, origin.z + depth-1)) 125 | 126 | assertFalse(grid.hasCubeAt(-100f, -100f, -100f)) 127 | assertFalse(grid.hasCubeAt(0f, 0f, 0f)) 128 | assertFalse(grid.hasCubeAt(origin.x + width, origin.y + height, origin.z + depth)) 129 | assertFalse(grid.hasCubeAt(origin.x - 1, origin.y - 1, origin.z - 1)) 130 | 131 | val iv = IntVector3().set(origin) 132 | 133 | for (x in iv.x..iv.x+width-1) { 134 | for (y in iv.y..iv.y+height-1) { 135 | for (z in iv.z..iv.z+depth-1) { 136 | assertTrue(grid.hasCubeAt(x.toFloat(), y.toFloat(), z.toFloat())) 137 | assertNotNull(grid.getCubeAt(x.toFloat(), y.toFloat(), z.toFloat())) 138 | } 139 | } 140 | } 141 | } 142 | 143 | @Test fun chunkElevation() { 144 | val cdg = basicCDG() 145 | val maxElevation = 5 146 | for (cube in cdg) { 147 | if(cube.yf > maxElevation) { 148 | cube.cubeType = CubeType.Void 149 | } else { 150 | cube.cubeType = CubeType.Grass 151 | } 152 | } 153 | fun CubeDataGrid.test(x: Float, z: Float, expected: Int) { 154 | assertEquals(expected, this.getElevation(x, z)) 155 | } 156 | val sz = Chunk.chunkSize 157 | println("chunk size: $sz") 158 | cdg.test(0f, 0f, maxElevation) 159 | cdg.test(0f, sz-1f, maxElevation) 160 | cdg.test(sz-1f, 0f, maxElevation) 161 | cdg.test(sz/2f, sz/2f, maxElevation) 162 | for (x in 0..sz-1) { 163 | for (z in 0..sz-1) { 164 | cdg.test(x.toFloat(), z.toFloat(), expected = maxElevation) 165 | } 166 | } 167 | 168 | val cdg2 = basicCDG() 169 | cdg2.forEach { it.cubeType = CubeType.Grass } 170 | cdg2.getCubeAt(0f, 5f, 0f).cubeType = CubeType.Void 171 | cdg2.test(0f, 0f, expected = sz-1) 172 | cdg2.getCubeAt(0f, 3f, 0f).cubeType = CubeType.Void 173 | cdg2.test(0f, 0f, expected = sz-1) 174 | 175 | } 176 | } 177 | 178 | class WorldTest { 179 | 180 | fun createWorld(w: Int, h: Int, d: Int): World { 181 | val w = World(w, h, d) 182 | world = w 183 | w.updatesEnabled = false 184 | w.createAllChunks() 185 | return w 186 | } 187 | 188 | @Test fun chunkDivisionCubeCount() { 189 | fun testCubeCount(szMult: Int) { 190 | val sz = szMult * Chunk.chunkSize 191 | if (sz % Chunk.chunkSize != 0) { 192 | throw GdxRuntimeException("Invalid world size, world size must be divisible by chunk size") 193 | } 194 | val world = createWorld(sz, sz, sz) 195 | val expectedTotalCubes = sz * sz * sz 196 | var ct = 0 197 | world.chunks.forEach { 198 | ct += it.dataGrid.numElements 199 | } 200 | val totalCubes = ct 201 | //val totalCubes = world.chunks.fold(0, {(value: Int, chunk: Chunk!) -> value + chunk.dataGrid.numElements }) 202 | assertEquals(expectedTotalCubes, totalCubes) 203 | } 204 | for (worldSize in 1..4) { 205 | testCubeCount(worldSize) 206 | } 207 | } 208 | 209 | @Test fun chunkDivisionChunkCount() { 210 | fun assertCounts(world: World, xCount: Int, yCount: Int, zCount: Int) { 211 | //println("world: ${world.width}, ${world.height}, ${world.depth}") 212 | assertEquals(world.numChunksX, xCount) 213 | assertEquals(world.numChunksY, yCount) 214 | assertEquals(world.numChunksZ, zCount) 215 | } 216 | 217 | val sz = Chunk.chunkSize 218 | 219 | var w = createWorld(sz, sz, sz) 220 | assertCounts(w, 1, 1, 1) 221 | 222 | w = createWorld(sz*2, sz, sz) 223 | assertCounts(w, 2, 1, 1) 224 | w = createWorld(sz, sz*2, sz) 225 | assertCounts(w, 1, 2, 1) 226 | w = createWorld(sz, sz, sz*2) 227 | assertCounts(w, 1, 1, 2) 228 | 229 | w = createWorld(sz*10, sz*5, sz*3) 230 | assertCounts(w, 10, 5, 3) 231 | 232 | } 233 | 234 | @Test fun crossChunkGetNeighbor() { 235 | val sz = Chunk.chunkSize 236 | var world = createWorld(sz, sz, sz) 237 | 238 | fun testNeighbor(x: Float, y: Float, z: Float) { 239 | assertTrue(world.hasCubeAt(x, y, z)) 240 | assertNotNull(world.getCubeAt(x, y, z)) 241 | } 242 | testNeighbor(0f, 0f, 0f) 243 | testNeighbor(0.1f, 0.1f, 0.1f) 244 | testNeighbor(7f, 7f, 7f) 245 | val lo = sz-0.01f 246 | testNeighbor(lo, lo, lo) 247 | assertFalse(world.hasCubeAt(sz+0.1f, 0f, 0f)) 248 | assertFalse(world.hasCubeAt(0f, sz+0.1f, 0f)) 249 | assertFalse(world.hasCubeAt(0f, 0f, sz+0.1f)) 250 | assertFalse(world.hasCubeAt(-0.1f, -0.1f, -0.1f)) 251 | 252 | world = createWorld(17, 17, 17) 253 | for (y in 0..16) { 254 | for (x in 0..16) { 255 | for (z in 0..16) { 256 | testNeighbor(x.toFloat(), y.toFloat(), z.toFloat()) 257 | } 258 | } 259 | } 260 | } 261 | 262 | @Test fun hiddenFaces() { 263 | fun createTestWorld(w: Int, h: Int, d: Int): World { 264 | val wor = createWorld(w, h, d) 265 | wor.calculateHiddenFaces() 266 | return wor 267 | } 268 | fun World.testHiddenFaces(x: Float, y: Float, z: Float, expected: Int) { 269 | val cube = this.getCubeAt(x, y, z) 270 | //println("CUBE AT ${cube.getPositionTempVec()}") 271 | //println(cube.debugHiddenFaces) 272 | assertEquals(expected, this.getCubeAt(x, y, z).hiddenFacesCount) 273 | } 274 | val sz = 32 275 | val max = sz-1.toFloat() 276 | val testWorld = createTestWorld(sz, sz, sz) 277 | //println("world chunks: ${testWorld.chunks.size}") 278 | var total = 0 279 | //print("hidden faces per chunk: ") 280 | for (chunk in testWorld.chunks) { 281 | chunk.dataGrid.calculateHiddenFaces(testWorld) 282 | var subTotal = chunk.dataGrid.numberOfHiddenFaces() 283 | //print(", $subTotal") 284 | total += subTotal 285 | } 286 | //println("\ntotal hidden faces in world: $total") 287 | testWorld.testHiddenFaces(0f, 0f, 0f, expected = 3) 288 | testWorld.testHiddenFaces(1f, 0f, 0f, expected = 4) 289 | testWorld.testHiddenFaces(0f, 1f, 0f, expected = 4) 290 | testWorld.testHiddenFaces(0f, 0f, 1f, expected = 4) 291 | testWorld.testHiddenFaces(1f, 1f, 0f, expected = 5) 292 | testWorld.testHiddenFaces(1f, 1f, 1f, expected = 6) 293 | 294 | testWorld.testHiddenFaces(max-1, max-1, max-1, expected = 6) 295 | testWorld.testHiddenFaces(max, max, max, expected = 3) 296 | 297 | val world2 = createTestWorld(128, 16, 16) 298 | world2.testHiddenFaces(0f, 0f, 0f, expected = 3) 299 | world2.testHiddenFaces(127.5f, 0f, 0f, expected = 3) 300 | world2.testHiddenFaces(127.5f, 15.5f, 15.5f, expected = 3) 301 | world2.testHiddenFaces(15.5f, 15.5f, 15.5f, expected = 3) 302 | world2.testHiddenFaces(15.0f, 15.0f, 8.0f, expected = 4) 303 | } 304 | 305 | @Test fun testChunkSnapping() { 306 | // world size is actually irrelvant to this test, see world.snapToChunkCenter method contents for details 307 | val w = createWorld(8, 8, 8) 308 | val v = Vector3() 309 | val sz = Chunk.chunkSize 310 | val szf = sz.toFloat() 311 | val origin = Vector3() 312 | val delta = 0.00001f 313 | fun test() { 314 | assertEquals(origin.x, w.snapToChunkOrigin(v.x), delta) 315 | assertEquals(origin.y, w.snapToChunkOrigin(v.y), delta) 316 | assertEquals(origin.z, w.snapToChunkOrigin(v.z), delta) 317 | 318 | val centerOffset = Chunk.chunkSize / 2f 319 | assertEquals(centerOffset + origin.x, w.snapToChunkCenter(v.x), delta) 320 | assertEquals(centerOffset + origin.y, w.snapToChunkCenter(v.y), delta) 321 | assertEquals(centerOffset + origin.z, w.snapToChunkCenter(v.z), delta) 322 | } 323 | 324 | if (sz < 4) { 325 | throw GdxRuntimeException("this test depends on chunk size being >= 8") 326 | } 327 | origin.set(0f, 0f, 0f) 328 | v.set(1.12f, 2.98f, 3.84f) 329 | test() 330 | 331 | origin.set(0f, 0f, 0f) 332 | v.set(sz-0.1f, sz-0.1f, sz-0.1f) 333 | test() 334 | 335 | origin.set(szf, szf, szf) 336 | v.set(szf, szf, szf) 337 | test() 338 | 339 | origin.set(szf, szf, szf) 340 | v.set(szf+0.01f, szf+0.01f, szf+0.01f) 341 | test() 342 | 343 | origin.set(0f, 0f, 0f) 344 | v.set(szf-0.01f, szf-0.01f, szf-0.01f) 345 | test() 346 | 347 | origin.set(128f, 128f, 128f) 348 | v.set(129f, 129f, 130f) 349 | test() 350 | } 351 | 352 | @Test fun hasCube_and_hasChunk() { 353 | val sz = 32 354 | val w = createWorld(sz, sz, sz) 355 | val max = sz-1 356 | fun testHas(x: Float, y: Float, z: Float) { 357 | assertTrue("xyz: $x, $y, $z", w.hasChunkAt(x, y, z)) 358 | assertTrue("xyz: $x, $y, $z", w.hasCubeAt(x, y, z)) 359 | } 360 | fun testHasNot(x: Float, y: Float, z: Float) { 361 | assertFalse("xyz: $x, $y, $z", w.hasChunkAt(x, y, z)) 362 | assertFalse("xyz: $x, $y, $z", w.hasCubeAt(x, y, z)) 363 | } 364 | for (xi in 0..max) { 365 | for (yi in 0..max) { 366 | for (zi in 0..max) { 367 | val x = xi.toFloat() 368 | val y = yi.toFloat() 369 | val z = zi.toFloat() 370 | testHas(x, y, z) 371 | } 372 | } 373 | } 374 | testHas(0f, 0f, 0f) 375 | testHasNot(-0.01f, -0.01f, -0.01f) 376 | testHas(0.05f, 0.05f, 0.05f) 377 | testHasNot(max + 1.1f, max + 1.1f, max + 1.1f) 378 | val fsz = sz.toFloat() 379 | testHasNot(fsz, fsz, fsz) 380 | } 381 | 382 | @Test fun getCube() { 383 | val sz = 32 384 | val max = sz-1 385 | val w = createWorld(sz, sz, sz) 386 | val tmp = Vector3() 387 | fun Float.i(): Int = MathUtils.floor(this) 388 | // snap float values to integer before comparing 389 | fun equivalentAsIntegers(v1: Vector3, v2: Vector3) { 390 | assertTrue(v1.x.i() == v2.x.i()) 391 | assertTrue(v1.y.i() == v2.y.i()) 392 | assertTrue(v1.z.i() == v2.z.i()) 393 | } 394 | fun test(x: Float, y: Float, z: Float) { 395 | val cube = w.getCubeAt(x, y, z) 396 | tmp.set(x, y, z) 397 | equivalentAsIntegers(tmp, cube.getPositionTempVec()) 398 | } 399 | for (x in 0..max) { 400 | for (y in 0..max) { 401 | for (z in 0..max) { 402 | test(x + 0f, y + 0f, z + 0f) 403 | test(x + 0.95f, y + 0.95f, z + 0.95f) 404 | test(x + 0.01f, y + 0.01f, z + 0.01f) 405 | } 406 | } 407 | } 408 | } 409 | 410 | @Test fun getChunk() { 411 | val sz = 32 412 | val max = sz-1 413 | val w = createWorld(sz, sz, sz) 414 | 415 | fun test(x: Float, y: Float, z: Float) { 416 | val chunk = w.getChunkAt(x, y, z) 417 | val worldCube = w.getCubeAt(x, y, z) 418 | val chunkCube = chunk.dataGrid.getCubeAt(x, y, z) 419 | assertEquals(worldCube, chunkCube) 420 | assertTrue(worldCube == chunkCube) 421 | } 422 | 423 | for (x in 0..max) { 424 | for (y in 0..max) { 425 | for (z in 0..max) { 426 | test(x + 0f, y + 0f, z + 0f) 427 | test(x + 0.95f, y + 0.95f, z + 0.95f) 428 | test(x + 0.01f, y + 0.01f, z + 0.01f) 429 | } 430 | } 431 | } 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /voxel1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrenner/kotlin-voxel/1266cc7366fea7a069f3a545ac54cc0e753d457a/voxel1.png --------------------------------------------------------------------------------