Parallax definition loading
This commit is contained in:
parent
97e28ca0ee
commit
b1ee5bf66d
@ -4,11 +4,11 @@ import com.google.gson.*
|
|||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kstarbound.api.IVFS
|
import ru.dbotthepony.kstarbound.api.IVFS
|
||||||
import ru.dbotthepony.kstarbound.api.PhysicalFS
|
import ru.dbotthepony.kstarbound.api.PhysicalFS
|
||||||
|
import ru.dbotthepony.kstarbound.api.getPathFilename
|
||||||
import ru.dbotthepony.kstarbound.api.getPathFolder
|
import ru.dbotthepony.kstarbound.api.getPathFolder
|
||||||
import ru.dbotthepony.kstarbound.defs.*
|
import ru.dbotthepony.kstarbound.defs.*
|
||||||
import ru.dbotthepony.kstarbound.defs.projectile.*
|
import ru.dbotthepony.kstarbound.defs.projectile.*
|
||||||
import ru.dbotthepony.kstarbound.defs.world.SkyParameters
|
import ru.dbotthepony.kstarbound.defs.world.SkyParameters
|
||||||
import ru.dbotthepony.kstarbound.defs.world.SkyType
|
|
||||||
import ru.dbotthepony.kstarbound.defs.world.dungeon.DungeonWorldDef
|
import ru.dbotthepony.kstarbound.defs.world.dungeon.DungeonWorldDef
|
||||||
import ru.dbotthepony.kstarbound.io.*
|
import ru.dbotthepony.kstarbound.io.*
|
||||||
import ru.dbotthepony.kstarbound.math.*
|
import ru.dbotthepony.kstarbound.math.*
|
||||||
@ -25,8 +25,8 @@ import java.util.*
|
|||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
const val METRES_IN_STARBOUND_UNIT = 0.25
|
const val METRES_IN_STARBOUND_UNIT = 0.5
|
||||||
const val METRES_IN_STARBOUND_UNITf = 0.25f
|
const val METRES_IN_STARBOUND_UNITf = 0.5f
|
||||||
|
|
||||||
const val PIXELS_IN_STARBOUND_UNIT = 8.0
|
const val PIXELS_IN_STARBOUND_UNIT = 8.0
|
||||||
const val PIXELS_IN_STARBOUND_UNITf = 8.0f
|
const val PIXELS_IN_STARBOUND_UNITf = 8.0f
|
||||||
@ -36,12 +36,16 @@ class ProjectileDefLoadingException(message: String, cause: Throwable? = null) :
|
|||||||
|
|
||||||
object Starbound : IVFS {
|
object Starbound : IVFS {
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
|
||||||
private val tiles = HashMap<String, TileDefinition>()
|
private val tiles = HashMap<String, TileDefinition>()
|
||||||
private val projectiles = HashMap<String, ConfiguredProjectile>()
|
private val projectiles = HashMap<String, ConfiguredProjectile>()
|
||||||
val tilesAccess = Collections.unmodifiableMap(tiles)
|
private val parallax = HashMap<String, Parallax>()
|
||||||
val projectilesAccess = Collections.unmodifiableMap(projectiles)
|
|
||||||
|
|
||||||
val gson = GsonBuilder()
|
val tilesAccess: Map<String, TileDefinition> = Collections.unmodifiableMap(tiles)
|
||||||
|
val projectilesAccess: Map<String, ConfiguredProjectile> = Collections.unmodifiableMap(projectiles)
|
||||||
|
val parallaxAccess: Map<String, Parallax> = Collections.unmodifiableMap(parallax)
|
||||||
|
|
||||||
|
val gson: Gson = GsonBuilder()
|
||||||
.enableComplexMapKeySerialization()
|
.enableComplexMapKeySerialization()
|
||||||
.serializeNulls()
|
.serializeNulls()
|
||||||
.setDateFormat(DateFormat.LONG)
|
.setDateFormat(DateFormat.LONG)
|
||||||
@ -60,6 +64,7 @@ object Starbound : IVFS {
|
|||||||
.also(ConfigurableProjectile::registerGson)
|
.also(ConfigurableProjectile::registerGson)
|
||||||
.also(SkyParameters::registerGson)
|
.also(SkyParameters::registerGson)
|
||||||
.also(DungeonWorldDef::registerGson)
|
.also(DungeonWorldDef::registerGson)
|
||||||
|
.also(Parallax::registerGson)
|
||||||
|
|
||||||
.registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe())
|
.registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe())
|
||||||
|
|
||||||
@ -99,6 +104,25 @@ object Starbound : IVFS {
|
|||||||
fun getTileDefinition(name: String) = tiles[name]
|
fun getTileDefinition(name: String) = tiles[name]
|
||||||
private val initCallbacks = ArrayList<() -> Unit>()
|
private val initCallbacks = ArrayList<() -> Unit>()
|
||||||
|
|
||||||
|
private fun loadStage(
|
||||||
|
callback: (Boolean, Boolean, String) -> Unit,
|
||||||
|
loader: ((String) -> Unit) -> Unit,
|
||||||
|
name: String,
|
||||||
|
) {
|
||||||
|
val time = System.currentTimeMillis()
|
||||||
|
callback(false, false, "Loading $name...")
|
||||||
|
|
||||||
|
loader {
|
||||||
|
if (terminateLoading) {
|
||||||
|
throw InterruptedException("Game is terminating")
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(false, true, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(false, true, "Loaded $name in ${System.currentTimeMillis() - time}ms")
|
||||||
|
}
|
||||||
|
|
||||||
fun initializeGame(callback: (finished: Boolean, replaceStatus: Boolean, status: String) -> Unit) {
|
fun initializeGame(callback: (finished: Boolean, replaceStatus: Boolean, status: String) -> Unit) {
|
||||||
if (initializing) {
|
if (initializing) {
|
||||||
throw IllegalStateException("Already initializing!")
|
throw IllegalStateException("Already initializing!")
|
||||||
@ -125,36 +149,9 @@ object Starbound : IVFS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run {
|
loadStage(callback, this::loadTileMaterials, "materials")
|
||||||
val localTime = System.currentTimeMillis()
|
loadStage(callback, this::loadProjectiles, "projectiles")
|
||||||
|
loadStage(callback, this::loadParallax, "parallax definitions")
|
||||||
callback(false, false, "Loading materials...")
|
|
||||||
|
|
||||||
loadTileMaterials {
|
|
||||||
if (terminateLoading) {
|
|
||||||
throw InterruptedException("Game is terminating")
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(false, true, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(false, true, "Loaded materials in ${System.currentTimeMillis() - localTime}ms")
|
|
||||||
}
|
|
||||||
|
|
||||||
run {
|
|
||||||
val localTime = System.currentTimeMillis()
|
|
||||||
callback(false, false, "Loading projectiles...")
|
|
||||||
|
|
||||||
loadProjectiles {
|
|
||||||
if (terminateLoading) {
|
|
||||||
throw InterruptedException("Game is terminating")
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(false, true, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(false, true, "Loaded Projectiles in ${System.currentTimeMillis() - localTime}ms")
|
|
||||||
}
|
|
||||||
|
|
||||||
initializing = false
|
initializing = false
|
||||||
initialized = true
|
initialized = true
|
||||||
@ -257,4 +254,21 @@ object Starbound : IVFS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadParallax(callback: (String) -> Unit) {
|
||||||
|
for (fs in fileSystems) {
|
||||||
|
for (listedFile in fs.listAllFiles("parallax")) {
|
||||||
|
if (listedFile.endsWith(".parallax")) {
|
||||||
|
try {
|
||||||
|
callback("Loading $listedFile")
|
||||||
|
|
||||||
|
val def = gson.fromJson(getReader(listedFile), Parallax::class.java)
|
||||||
|
parallax[getPathFilename(listedFile).substringBefore('.')] = def
|
||||||
|
} catch(err: Throwable) {
|
||||||
|
LOGGER.error("Loading parallax file $listedFile", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,11 @@ interface IVFS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getPathFolder(path: String): String {
|
fun getPathFolder(path: String): String {
|
||||||
return path.substring(0, path.lastIndexOf('/'))
|
return path.substringBeforeLast('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPathFilename(path: String): String {
|
||||||
|
return path.substringAfterLast('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
class PhysicalFS(root: File) : IVFS {
|
class PhysicalFS(root: File) : IVFS {
|
||||||
|
@ -1,11 +1,39 @@
|
|||||||
package ru.dbotthepony.kstarbound.client
|
package ru.dbotthepony.kstarbound.client
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL46.*
|
||||||
|
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.VertexTransformers
|
||||||
import ru.dbotthepony.kstarbound.client.render.renderLayeredList
|
import ru.dbotthepony.kstarbound.client.render.renderLayeredList
|
||||||
|
import ru.dbotthepony.kstarbound.defs.Parallax
|
||||||
import ru.dbotthepony.kstarbound.math.encasingChunkPosAABB
|
import ru.dbotthepony.kstarbound.math.encasingChunkPosAABB
|
||||||
import ru.dbotthepony.kstarbound.world.*
|
import ru.dbotthepony.kstarbound.world.*
|
||||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||||
import ru.dbotthepony.kvector.util2d.AABB
|
import ru.dbotthepony.kvector.util2d.AABB
|
||||||
|
|
||||||
|
class DoubleEdgeProgression : Iterator<Int> {
|
||||||
|
var value = 0
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): Int {
|
||||||
|
return nextInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nextInt(): Int {
|
||||||
|
return if (value > 0) {
|
||||||
|
val ret = value
|
||||||
|
value = -value
|
||||||
|
ret
|
||||||
|
} else {
|
||||||
|
val ret = value
|
||||||
|
value = -value + 1
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ClientWorld(val client: StarboundClient, seed: Long = 0L) : World<ClientWorld, ClientChunk>(seed) {
|
class ClientWorld(val client: StarboundClient, seed: Long = 0L) : World<ClientWorld, ClientChunk>(seed) {
|
||||||
init {
|
init {
|
||||||
physics.debugDraw = client.gl.box2dRenderer
|
physics.debugDraw = client.gl.box2dRenderer
|
||||||
@ -18,17 +46,77 @@ class ClientWorld(val client: StarboundClient, seed: Long = 0L) : World<ClientWo
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parallax: Parallax? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Отрисовывает этот с обрезкой невидимой геометрии с точки зрения [size] в Starbound Units
|
* Отрисовывает этот с обрезкой невидимой геометрии с точки зрения [size] в Starbound Units
|
||||||
*
|
*
|
||||||
* Все координаты "местности" сохраняются, поэтому, если отрисовывать слишком далеко от 0, 0
|
* Все координаты "местности" сохраняются, поэтому, если отрисовывать слишком далеко от 0, 0
|
||||||
* то геометрия может начать искажаться из-за погрешности плавающей запятой
|
* то геометрия может начать искажаться из-за погрешности плавающей запятой
|
||||||
*
|
|
||||||
* Обрезает всю заведомо невидимую геометрию на основе аргументов mins и maxs (в пикселях)
|
|
||||||
*/
|
*/
|
||||||
fun render(
|
fun render(
|
||||||
size: AABB,
|
size: AABB,
|
||||||
) {
|
) {
|
||||||
|
val parallax = parallax
|
||||||
|
|
||||||
|
if (parallax != null) {
|
||||||
|
client.gl.matrixStack.push()
|
||||||
|
|
||||||
|
client.gl.matrixStack.translateWithMultiplication(y = parallax.verticalOrigin.toFloat() - 20f)
|
||||||
|
|
||||||
|
client.gl.shaderVertexTexture.use()
|
||||||
|
|
||||||
|
val stateful = client.gl.flat2DTexturedQuads.statefulSmall
|
||||||
|
val builder = stateful.builder
|
||||||
|
|
||||||
|
client.gl.activeTexture = 0
|
||||||
|
client.gl.shaderVertexTexture["_texture"] = 0
|
||||||
|
val centre = size.centre
|
||||||
|
|
||||||
|
for (layer in parallax.layers) {
|
||||||
|
client.gl.matrixStack.push()
|
||||||
|
client.gl.matrixStack.translateWithMultiplication(x = layer.offset.x.toFloat() / PIXELS_IN_STARBOUND_UNITf, y = layer.offset.y.toFloat() / PIXELS_IN_STARBOUND_UNITf)
|
||||||
|
|
||||||
|
client.gl.shaderVertexTexture.transform.set(client.gl.matrixStack.last)
|
||||||
|
|
||||||
|
val texture = client.gl.loadNamedTextureSafe("/parallax/images/${layer.kind}/base/1.png")
|
||||||
|
|
||||||
|
texture.bind()
|
||||||
|
texture.textureMagFilter = GL_NEAREST
|
||||||
|
texture.textureMinFilter = GL_NEAREST
|
||||||
|
|
||||||
|
builder.begin()
|
||||||
|
|
||||||
|
for (xPos in DoubleEdgeProgression()) {
|
||||||
|
var x0 = xPos.toFloat() * texture.width / PIXELS_IN_STARBOUND_UNITf
|
||||||
|
var x1 = (xPos + 1f) * texture.width / PIXELS_IN_STARBOUND_UNITf
|
||||||
|
|
||||||
|
val diffx = layer.parallax.x * centre.x - centre.x
|
||||||
|
val diffy = (layer.parallax.y * (centre.y + 20.0) - centre.y - 20.0).toFloat() / PIXELS_IN_STARBOUND_UNITf
|
||||||
|
|
||||||
|
x0 += diffx.toFloat() / PIXELS_IN_STARBOUND_UNITf
|
||||||
|
x1 += diffx.toFloat() / PIXELS_IN_STARBOUND_UNITf
|
||||||
|
|
||||||
|
builder.quadZ(x0, diffy, x1, diffy + texture.height.toFloat() / PIXELS_IN_STARBOUND_UNITf, 1f, VertexTransformers.uv(0f, 1f, 1f, 0f))
|
||||||
|
|
||||||
|
/*if (x1 < size.mins.x) {
|
||||||
|
break
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (xPos < -40) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stateful.upload()
|
||||||
|
stateful.draw()
|
||||||
|
|
||||||
|
client.gl.matrixStack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
client.gl.matrixStack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
val determineRenderers = ArrayList<ClientChunk>()
|
val determineRenderers = ArrayList<ClientChunk>()
|
||||||
|
|
||||||
for (chunk in collectInternal(size.encasingChunkPosAABB())) {
|
for (chunk in collectInternal(size.encasingChunkPosAABB())) {
|
||||||
|
@ -216,9 +216,9 @@ class StarboundClient : AutoCloseable {
|
|||||||
|
|
||||||
world?.render(
|
world?.render(
|
||||||
AABB.rectangle(
|
AABB.rectangle(
|
||||||
camera.pos.toDoubleVector(),
|
camera.pos.toDoubleVector(),
|
||||||
viewportWidth / settings.scale / PIXELS_IN_STARBOUND_UNIT,
|
viewportWidth / settings.scale / PIXELS_IN_STARBOUND_UNIT,
|
||||||
viewportHeight / settings.scale / PIXELS_IN_STARBOUND_UNIT))
|
viewportHeight / settings.scale / PIXELS_IN_STARBOUND_UNIT))
|
||||||
|
|
||||||
for (lambda in onPostDrawWorld) {
|
for (lambda in onPostDrawWorld) {
|
||||||
lambda.invoke()
|
lambda.invoke()
|
||||||
|
@ -252,7 +252,7 @@ class GLStateTracker {
|
|||||||
|
|
||||||
private val named2DTextures = HashMap<String, GLTexture2D>()
|
private val named2DTextures = HashMap<String, GLTexture2D>()
|
||||||
|
|
||||||
fun loadNamedTexture(path: String, memoryFormat: Int = GL_RGBA, fileFormat: Int = GL_RGBA): GLTexture2D {
|
fun loadNamedTexture(path: String, memoryFormat: Int, fileFormat: Int): GLTexture2D {
|
||||||
return named2DTextures.computeIfAbsent(path) {
|
return named2DTextures.computeIfAbsent(path) {
|
||||||
if (!Starbound.pathExists(path)) {
|
if (!Starbound.pathExists(path)) {
|
||||||
throw FileNotFoundException("Unable to locate $path")
|
throw FileNotFoundException("Unable to locate $path")
|
||||||
@ -262,10 +262,20 @@ class GLStateTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loadNamedTexture(path: String): GLTexture2D {
|
||||||
|
return named2DTextures.computeIfAbsent(path) {
|
||||||
|
if (!Starbound.pathExists(path)) {
|
||||||
|
throw FileNotFoundException("Unable to locate $path")
|
||||||
|
}
|
||||||
|
|
||||||
|
return@computeIfAbsent newTexture(path).upload(Starbound.readDirect(path)).generateMips()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var loadedEmptyTexture = false
|
private var loadedEmptyTexture = false
|
||||||
private val missingTexturePath = "/assetmissing.png"
|
private val missingTexturePath = "/assetmissing.png"
|
||||||
|
|
||||||
fun loadNamedTextureSafe(path: String, memoryFormat: Int = GL_RGBA, fileFormat: Int = GL_RGBA): GLTexture2D {
|
fun loadNamedTextureSafe(path: String, memoryFormat: Int, fileFormat: Int): GLTexture2D {
|
||||||
if (!loadedEmptyTexture) {
|
if (!loadedEmptyTexture) {
|
||||||
loadedEmptyTexture = true
|
loadedEmptyTexture = true
|
||||||
named2DTextures[missingTexturePath] = newTexture(missingTexturePath).upload(Starbound.readDirect(missingTexturePath), memoryFormat, fileFormat).generateMips()
|
named2DTextures[missingTexturePath] = newTexture(missingTexturePath).upload(Starbound.readDirect(missingTexturePath), memoryFormat, fileFormat).generateMips()
|
||||||
@ -281,6 +291,22 @@ class GLStateTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loadNamedTextureSafe(path: String): GLTexture2D {
|
||||||
|
if (!loadedEmptyTexture) {
|
||||||
|
loadedEmptyTexture = true
|
||||||
|
named2DTextures[missingTexturePath] = newTexture(missingTexturePath).upload(Starbound.readDirect(missingTexturePath), GL_RGBA, GL_RGBA).generateMips()
|
||||||
|
}
|
||||||
|
|
||||||
|
return named2DTextures.computeIfAbsent(path) {
|
||||||
|
if (!Starbound.pathExists(path)) {
|
||||||
|
LOGGER.error("Texture {} is missing! Falling back to {}", path, missingTexturePath)
|
||||||
|
return@computeIfAbsent named2DTextures[missingTexturePath]!!
|
||||||
|
}
|
||||||
|
|
||||||
|
return@computeIfAbsent newTexture(path).upload(Starbound.readDirect(path)).generateMips()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun bind(obj: GLVertexBufferObject): GLVertexBufferObject {
|
fun bind(obj: GLVertexBufferObject): GLVertexBufferObject {
|
||||||
if (obj.type == VBOType.ARRAY)
|
if (obj.type == VBOType.ARRAY)
|
||||||
VBO = obj
|
VBO = obj
|
||||||
|
@ -162,6 +162,40 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : A
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun upload(path: File): GLTexture2D {
|
||||||
|
state.ensureSameThread()
|
||||||
|
|
||||||
|
if (!path.exists()) {
|
||||||
|
throw FileNotFoundException("${path.absolutePath} does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path.isFile) {
|
||||||
|
throw FileNotFoundException("${path.absolutePath} is not a file")
|
||||||
|
}
|
||||||
|
|
||||||
|
val getwidth = intArrayOf(0)
|
||||||
|
val getheight = intArrayOf(0)
|
||||||
|
val getchannels = intArrayOf(0)
|
||||||
|
|
||||||
|
val bytes = STBImage.stbi_load(path.absolutePath, getwidth, getheight, getchannels, 0) ?: throw TextureLoadingException("Unable to load ${path.absolutePath}. Is it a valid image?")
|
||||||
|
|
||||||
|
require(getwidth[0] > 0) { "Image ${path.absolutePath} has bad width of ${getwidth[0]}" }
|
||||||
|
require(getheight[0] > 0) { "Image ${path.absolutePath} has bad height of ${getheight[0]}" }
|
||||||
|
|
||||||
|
val bufferFormat = when (val numChannels = getchannels[0]) {
|
||||||
|
1 -> GL_R
|
||||||
|
2 -> GL_RG
|
||||||
|
3 -> GL_RGB
|
||||||
|
4 -> GL_RGBA
|
||||||
|
else -> throw IllegalArgumentException("Weird amount of channels in file: $numChannels")
|
||||||
|
}
|
||||||
|
|
||||||
|
upload(bufferFormat, getwidth[0], getheight[0], bufferFormat, GL_UNSIGNED_BYTE, bytes)
|
||||||
|
STBImage.stbi_image_free(bytes)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
fun upload(buff: ByteBuffer, memoryFormat: Int, bufferFormat: Int): GLTexture2D {
|
fun upload(buff: ByteBuffer, memoryFormat: Int, bufferFormat: Int): GLTexture2D {
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
|
|
||||||
@ -180,6 +214,32 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : A
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun upload(buff: ByteBuffer): GLTexture2D {
|
||||||
|
state.ensureSameThread()
|
||||||
|
|
||||||
|
val getwidth = intArrayOf(0)
|
||||||
|
val getheight = intArrayOf(0)
|
||||||
|
val getchannels = intArrayOf(0)
|
||||||
|
|
||||||
|
val bytes = STBImage.stbi_load_from_memory(buff, getwidth, getheight, getchannels, 0) ?: throw TextureLoadingException("Unable to load ${buff}. Is it a valid image?")
|
||||||
|
|
||||||
|
require(getwidth[0] > 0) { "Image $name has bad width of ${getwidth[0]}" }
|
||||||
|
require(getheight[0] > 0) { "Image $name has bad height of ${getheight[0]}" }
|
||||||
|
|
||||||
|
val bufferFormat = when (val numChannels = getchannels[0]) {
|
||||||
|
1 -> GL_R
|
||||||
|
2 -> GL_RG
|
||||||
|
3 -> GL_RGB
|
||||||
|
4 -> GL_RGBA
|
||||||
|
else -> throw IllegalArgumentException("Weird amount of channels in file: $numChannels")
|
||||||
|
}
|
||||||
|
|
||||||
|
upload(bufferFormat, getwidth[0], getheight[0], bufferFormat, GL_UNSIGNED_BYTE, bytes)
|
||||||
|
STBImage.stbi_image_free(bytes)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
var isValid = true
|
var isValid = true
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
103
src/main/kotlin/ru/dbotthepony/kstarbound/defs/Parallax.kt
Normal file
103
src/main/kotlin/ru/dbotthepony/kstarbound/defs/Parallax.kt
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs
|
||||||
|
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.google.gson.JsonSyntaxException
|
||||||
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.stream.JsonReader
|
||||||
|
import com.google.gson.stream.JsonToken
|
||||||
|
import com.google.gson.stream.JsonWriter
|
||||||
|
import ru.dbotthepony.kstarbound.io.KTypeAdapter
|
||||||
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
|
class Parallax {
|
||||||
|
var verticalOrigin = 0.0
|
||||||
|
|
||||||
|
var layers = Array(0) { ParallaxLayer() }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val ADAPTER = KTypeAdapter(::Parallax,
|
||||||
|
Parallax::verticalOrigin,
|
||||||
|
Parallax::layers,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||||
|
gsonBuilder.registerTypeAdapter(Parallax::class.java, ADAPTER)
|
||||||
|
gsonBuilder.registerTypeAdapter(ParallaxLayer::class.java, ParallaxLayer.ADAPTER)
|
||||||
|
gsonBuilder.registerTypeAdapter(ParallaxLayer.Parallax::class.java, ParallaxLayer.LAYER_PARALLAX_ADAPTER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParallaxLayer {
|
||||||
|
class Parallax(val x: Double, val y: Double)
|
||||||
|
|
||||||
|
var timeOfDayCorrelation: String? = null
|
||||||
|
var offset = Vector2d.ZERO
|
||||||
|
var repeatY = false
|
||||||
|
var lightMapped = false
|
||||||
|
var tileLimitTop: Int? = null
|
||||||
|
var parallax by Delegates.notNull<Parallax>()
|
||||||
|
|
||||||
|
var unlit = false
|
||||||
|
var nohueshift = false
|
||||||
|
var minSpeed = 0
|
||||||
|
var maxSpeed = 0
|
||||||
|
var fadePercent = 0.0
|
||||||
|
|
||||||
|
var frequency = 1.0
|
||||||
|
|
||||||
|
var modCount = 0
|
||||||
|
|
||||||
|
var noRandomOffset = false
|
||||||
|
var directives: String? = null
|
||||||
|
|
||||||
|
var kind by Delegates.notNull<String>()
|
||||||
|
var baseCount = 1
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val LAYER_PARALLAX_ADAPTER = object : TypeAdapter<Parallax>() {
|
||||||
|
override fun write(out: JsonWriter?, value: Parallax?) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(reader: JsonReader): Parallax {
|
||||||
|
return when (val type = reader.peek()) {
|
||||||
|
JsonToken.BEGIN_ARRAY -> {
|
||||||
|
reader.beginArray()
|
||||||
|
val instance = Parallax(reader.nextDouble(), reader.nextDouble())
|
||||||
|
reader.endArray()
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonToken.NUMBER -> {
|
||||||
|
val num = reader.nextDouble()
|
||||||
|
Parallax(num, num)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw JsonSyntaxException("Unexpected token $type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val ADAPTER = KTypeAdapter(::ParallaxLayer,
|
||||||
|
ParallaxLayer::timeOfDayCorrelation,
|
||||||
|
ParallaxLayer::offset,
|
||||||
|
ParallaxLayer::repeatY,
|
||||||
|
ParallaxLayer::lightMapped,
|
||||||
|
ParallaxLayer::tileLimitTop,
|
||||||
|
ParallaxLayer::parallax,
|
||||||
|
ParallaxLayer::unlit,
|
||||||
|
ParallaxLayer::nohueshift,
|
||||||
|
ParallaxLayer::minSpeed,
|
||||||
|
ParallaxLayer::maxSpeed,
|
||||||
|
ParallaxLayer::fadePercent,
|
||||||
|
ParallaxLayer::kind,
|
||||||
|
ParallaxLayer::baseCount,
|
||||||
|
ParallaxLayer::noRandomOffset,
|
||||||
|
ParallaxLayer::directives,
|
||||||
|
ParallaxLayer::frequency,
|
||||||
|
ParallaxLayer::modCount,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user