KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt

310 lines
9.5 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package ru.dbotthepony.kstarbound.client
import org.lwjgl.opengl.GL46.*
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.gl.BlendFunc
import ru.dbotthepony.kstarbound.client.gl.GLFrameBuffer
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kstarbound.client.gl.vertex.quadZ
import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer
import ru.dbotthepony.kstarbound.client.render.renderLayeredList
import ru.dbotthepony.kstarbound.defs.ParallaxPrototype
import ru.dbotthepony.kstarbound.math.encasingChunkPosAABB
import ru.dbotthepony.kstarbound.util.DoubleEdgeProgression
import ru.dbotthepony.kstarbound.world.*
import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
import ru.dbotthepony.kvector.vector.nint.Vector2i
import java.nio.ByteBuffer
import java.nio.ByteOrder
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.roundToInt
import kotlin.math.sin
class ClientWorld(
val client: StarboundClient,
seed: Long,
widthInChunks: Int,
) : World<ClientWorld, ClientChunk>(seed, widthInChunks) {
init {
physics.debugDraw = client.gl.box2dRenderer
}
override fun chunkFactory(pos: ChunkPos): ClientChunk {
return ClientChunk(
world = this,
pos = pos,
)
}
var parallax: ParallaxPrototype? = null
/**
* Отрисовывает этот с обрезкой невидимой геометрии с точки зрения [size] в Starbound Units
*
* Все координаты "местности" сохраняются, поэтому, если отрисовывать слишком далеко от 0, 0
* то геометрия может начать искажаться из-за погрешности плавающей запятой
*/
fun render(
size: AABB,
isScreenspaceRender: Boolean = true
) {
val parallax = parallax
if (parallax != null) {
client.gl.matrixStack.push()
client.gl.matrixStack.translateWithMultiplication(y = parallax.verticalOrigin.toFloat() - 20f)
client.gl.shaderVertexTexture.use()
val builder = client.gl.flat2DTexturedQuads.small
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, QuadTransformers.uv(0f, 1f, 1f, 0f))
/*if (x1 < size.mins.x) {
break
}*/
if (xPos < -40) {
break
}
}
builder.upload()
builder.draw()
client.gl.matrixStack.pop()
}
client.gl.matrixStack.pop()
}
val determineRenderers = ArrayList<ILayeredRenderer>()
client.lightRenderer.begin()
for (chunk in collectPositionAware(size.encasingChunkPosAABB())) {
val renderer = chunk.second.OneShotRenderer(chunk.first)
determineRenderers.add(renderer)
chunk.second.bake()
//client.lightRenderer.addShadowGeometry(renderer)
}
renderLayeredList(client.gl.matrixStack, determineRenderers)
/*
for ((lightPosition, color) in listOf(
(client.screenToWorld(client.mouseCoordinatesF)) to Color.RED,
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(0.1f)) to Color.GREEN,
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(-0.1f)) to Color.BLUE,
)) {
if (isScreenspaceRender) {
val (x, y) = client.worldToScreen(lightPosition.x - 64f, lightPosition.y - 64f)
val (x2, y2) = client.worldToScreen(lightPosition.x + 64f, lightPosition.y + 64f)
client.pushScissorRect(x, client.viewportHeight - y, x2 - x, y - y2)
}
client.lightRenderer.renderSoftLight(lightPosition, color, radius = 64f, innerRadius = 2f)
if (isScreenspaceRender) {
client.popScissorRect()
}
}
val old = client.gl.blendFunc
client.gl.blendFunc = BlendFunc.MULTIPLY_BY_SRC
client.gl.activeTexture = 0
client.gl.texture2D = client.lightRenderer.outputTexture
client.gl.programs.viewTextureQuad.run()
client.gl.blendFunc = old
*/
val pos = client.screenToWorld(client.mouseCoordinatesF).toDoubleVector()
/*val lightsize = 16
val lightmap = floodLight(
Vector2i(pos.x.roundToInt(), pos.y.roundToInt()), lightsize
)
client.gl.quadWireframe {
for (column in 0 until lightmap.columns) {
for (row in 0 until lightmap.rows) {
if (lightmap[column, row] > 0) {
it.quad(pos.x.roundToInt() + column.toFloat() - lightsize, pos.y.roundToInt() + row.toFloat() - lightsize, pos.x.roundToInt() + column + 1f - lightsize, pos.y.roundToInt() + row + 1f - lightsize)
}
}
}
}*/
/*
val rayFan = ArrayList<Vector2d>()
for (i in 0 .. 359) {
rayFan.add(Vector2d(cos(i / 180.0 * PI), sin(i / 180.0 * PI)))
}
for (ray in rayFan) {
val trace = castRayNaive(pos, ray, 16.0)
client.gl.quadWireframe {
for ((tpos, tile) in trace.traversedTiles) {
if (tile.material != null)
it.quad(
tpos.x.toFloat(),
tpos.y.toFloat(),
tpos.x + 1f,
tpos.y + 1f
)
}
}
}
*/
//rayLightCircleNaive(pos, 48.0, falloffByTravel = 1.0, falloffByTile = 3.0)
/*
val result = rayLightCircleNaive(pos, 48.0, falloffByTravel = 1.0, falloffByTile = 3.0)
val result2 = rayLightCircleNaive(pos + Vector2d(-8.0), 24.0, falloffByTravel = 1.0, falloffByTile = 3.0)
val frame = GLFrameBuffer(client.gl)
frame.attachTexture(client.viewportWidth, client.viewportHeight)
frame.bind()
client.gl.clearColor = Color.BLACK
glClear(GL_COLOR_BUFFER_BIT)
client.gl.blendFunc = BlendFunc.ADDITIVE*/
/*client.gl.quadColor {
for (row in 0 until result.rows) {
for (column in 0 until result.columns) {
if (result[column, row] > 0.05) {
val color = result[column, row].toFloat() * 1.5f
it.quad(
pos.x.roundToInt() - result.rows.toFloat() / 2f + row.toFloat(),
pos.y.roundToInt() - result.columns.toFloat() / 2f + column.toFloat(),
pos.x.roundToInt() - result.rows.toFloat() / 2f + row + 1f,
pos.y.roundToInt() - result.columns.toFloat() / 2f + column + 1f
) { a, b -> a.pushVec4f(color, color, color, 1f) }
}
}
}
}
client.gl.quadColor {
for (row in 0 until result2.rows) {
for (column in 0 until result2.columns) {
if (result2[column, row] > 0.05) {
val color = result2[column, row].toFloat() * 1.5f
it.quad(
pos.x.roundToInt() - 8f - result2.rows.toFloat() / 2f + row.toFloat(),
pos.y.roundToInt() - result2.columns.toFloat() / 2f + column.toFloat(),
pos.x.roundToInt() - 8f - result2.rows.toFloat() / 2f + row + 1f,
pos.y.roundToInt() - result2.columns.toFloat() / 2f + column + 1f
) { a, b -> a.pushVec4f(color, 0f, 0f, 1f) }
}
}
}
}*/
/*val lightTextureWidth = (client.viewportWidth / PIXELS_IN_STARBOUND_UNIT).roundToInt()
val lightTextureHeight = (client.viewportHeight / PIXELS_IN_STARBOUND_UNIT).roundToInt()
val textureBuffer = ByteBuffer.allocateDirect(lightTextureWidth * lightTextureHeight * 3)
textureBuffer.order(ByteOrder.LITTLE_ENDIAN)
for (x in 0 until result.columns.coerceAtMost(lightTextureWidth)) {
for (y in 0 until result.rows.coerceAtMost(lightTextureHeight)) {
textureBuffer.position(x * 3 + y * lightTextureWidth * 3)
if (result[x, y] > 0.05) {
val color = result[x, y].toFloat() * 1.5f
textureBuffer.put((color * 255).toInt().coerceAtMost(255).toByte())
textureBuffer.put((color * 255).toInt().coerceAtMost(255).toByte())
textureBuffer.put((color * 255).toInt().coerceAtMost(255).toByte())
}
}
}*/
//frame.unbind()
// val texture = GLTexture2D(client.gl)
// textureBuffer.position(0)
// texture.upload(GL_RGB, lightTextureWidth, lightTextureHeight, GL_RGB, GL_UNSIGNED_BYTE, textureBuffer)
// texture.textureMinFilter = GL_LINEAR
// texture.textureMagFilter = GL_LINEAR
// client.gl.blendFunc = BlendFunc.MULTIPLY_BY_SRC
// client.gl.activeTexture = 0
// client.gl.texture2D = texture
// client.gl.programs.viewTextureQuad.run()
// client.gl.blendFunc = old
//frame.close()
//texture.close()
physics.debugDraw()
/*for (renderer in determineRenderers) {
renderer.renderDebug()
}*/
}
override fun thinkInner(delta: Double) {
val copy = arrayOfNulls<Entity>(entities.size)
var i = 0
for (ent in entities) {
copy[i++] = ent
}
for (ent in copy) {
ent!!.think(delta)
}
}
}