310 lines
9.5 KiB
Kotlin
310 lines
9.5 KiB
Kotlin
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)
|
||
}
|
||
}
|
||
}
|