mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-13 17:37:58 -04:00
wip: greedy meshing
This commit is contained in:
parent
ec35307a22
commit
2455359747
@ -14,16 +14,16 @@ package de.bixilon.minosoft.data.direction
|
|||||||
|
|
||||||
import de.bixilon.minosoft.data.Axes
|
import de.bixilon.minosoft.data.Axes
|
||||||
import de.bixilon.minosoft.data.registries.blocks.properties.serializer.BlockPropertiesSerializer
|
import de.bixilon.minosoft.data.registries.blocks.properties.serializer.BlockPropertiesSerializer
|
||||||
|
import de.bixilon.minosoft.data.text.ChatColors
|
||||||
import de.bixilon.minosoft.gui.rendering.models.FaceSize
|
import de.bixilon.minosoft.gui.rendering.models.FaceSize
|
||||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.get
|
import de.bixilon.minosoft.gui.rendering.util.VecUtil.get
|
||||||
import de.bixilon.minosoft.util.KUtil
|
import de.bixilon.minosoft.util.KUtil
|
||||||
import de.bixilon.minosoft.util.enum.ValuesEnum
|
import de.bixilon.minosoft.util.enum.ValuesEnum
|
||||||
|
import glm_.vec2.Vec2
|
||||||
import glm_.vec3.Vec3
|
import glm_.vec3.Vec3
|
||||||
import glm_.vec3.Vec3d
|
import glm_.vec3.Vec3d
|
||||||
import glm_.vec3.Vec3i
|
import glm_.vec3.Vec3i
|
||||||
import glm_.vec3.swizzle.xy
|
import glm_.vec3.swizzle.*
|
||||||
import glm_.vec3.swizzle.xz
|
|
||||||
import glm_.vec3.swizzle.yz
|
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
enum class Directions(
|
enum class Directions(
|
||||||
@ -41,6 +41,7 @@ enum class Directions(
|
|||||||
override val vectord = Vec3d(vector)
|
override val vectord = Vec3d(vector)
|
||||||
|
|
||||||
val axis: Axes get() = Axes[this] // ToDo
|
val axis: Axes get() = Axes[this] // ToDo
|
||||||
|
val debugColor = ChatColors[ordinal]
|
||||||
|
|
||||||
lateinit var inverted: Directions
|
lateinit var inverted: Directions
|
||||||
private set
|
private set
|
||||||
@ -87,6 +88,17 @@ enum class Directions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getUVMultiplier(from: Vec3, to: Vec3): Vec2 {
|
||||||
|
return when (this) {
|
||||||
|
DOWN -> from.zx - to.zx
|
||||||
|
UP -> from.xz - to.xz
|
||||||
|
NORTH -> from.xy - to.xy
|
||||||
|
SOUTH -> from.yx - to.yx
|
||||||
|
EAST -> from.zy - to.zy
|
||||||
|
WEST -> from.yz - to.yz
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object : BlockPropertiesSerializer, ValuesEnum<Directions> {
|
companion object : BlockPropertiesSerializer, ValuesEnum<Directions> {
|
||||||
override val VALUES = values()
|
override val VALUES = values()
|
||||||
|
@ -14,69 +14,176 @@
|
|||||||
package de.bixilon.minosoft.gui.rendering.block
|
package de.bixilon.minosoft.gui.rendering.block
|
||||||
|
|
||||||
import de.bixilon.minosoft.data.direction.Directions
|
import de.bixilon.minosoft.data.direction.Directions
|
||||||
import de.bixilon.minosoft.data.registries.blocks.BlockState
|
|
||||||
import de.bixilon.minosoft.data.world.ChunkSection
|
import de.bixilon.minosoft.data.world.ChunkSection
|
||||||
import de.bixilon.minosoft.gui.rendering.RenderWindow
|
import de.bixilon.minosoft.gui.rendering.RenderWindow
|
||||||
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
|
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
import de.bixilon.minosoft.gui.rendering.models.baked.block.GreedyBakedBlockModel
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition.SECTION_SIZE
|
||||||
|
import de.bixilon.minosoft.util.KUtil.decide
|
||||||
import de.bixilon.minosoft.util.logging.Log
|
import de.bixilon.minosoft.util.logging.Log
|
||||||
import de.bixilon.minosoft.util.logging.LogLevels
|
import de.bixilon.minosoft.util.logging.LogLevels
|
||||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||||
import glm_.vec3.Vec3i
|
import glm_.vec3.Vec3i
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
class SectionPreparer(
|
class SectionPreparer(
|
||||||
val renderWindow: RenderWindow,
|
val renderWindow: RenderWindow,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private fun renderNormal(position: Vec3i, section: ChunkSection, mesh: ChunkSectionMesh, random: Random) {
|
||||||
|
// ToDo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun prepare(section: ChunkSection): ChunkSectionMesh {
|
fun prepare(section: ChunkSection): ChunkSectionMesh {
|
||||||
val startTime = System.nanoTime()
|
val startTime = System.nanoTime()
|
||||||
val mesh = ChunkSectionMesh(renderWindow)
|
val mesh = ChunkSectionMesh(renderWindow)
|
||||||
|
|
||||||
val random = Random(0L)
|
val random = Random(0L)
|
||||||
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
|
|
||||||
for (y in 0 until ProtocolDefinition.SECTION_HEIGHT_Y) {
|
|
||||||
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
|
|
||||||
val block = section.blocks[ChunkSection.getIndex(x, y, z)] ?: continue
|
|
||||||
|
|
||||||
val neighbours: Array<BlockState?> = arrayOfNulls(Directions.VALUES.size)
|
|
||||||
|
|
||||||
// ToDo: Chunk borders
|
for (direction in Directions.VALUES) {
|
||||||
neighbours[Directions.DOWN.ordinal] = if (y == 0) {
|
// Sweep over each axis (X, Y and Z)
|
||||||
null
|
val backFace = direction.ordinal % 2 == 0
|
||||||
} else {
|
val axis = direction.axis.ordinal
|
||||||
section.blocks[ChunkSection.getIndex(x, y - 1, z)]
|
var i: Int
|
||||||
}
|
var j: Int
|
||||||
neighbours[Directions.UP.ordinal] = if (y == ProtocolDefinition.SECTION_MAX_Y) {
|
var k: Int
|
||||||
null
|
var l: Int
|
||||||
} else {
|
var w: Int
|
||||||
section.blocks[ChunkSection.getIndex(x, y + 1, z)]
|
var h: Int
|
||||||
}
|
val nextAxis = (axis + 1) % 3
|
||||||
neighbours[Directions.NORTH.ordinal] = if (z == 0) {
|
val nextNextAxis = (axis + 2) % 3
|
||||||
null
|
val position = IntArray(3)
|
||||||
} else {
|
val checkOffset = IntArray(3)
|
||||||
section.blocks[ChunkSection.getIndex(x, y, z - 1)]
|
val mask = BooleanArray(SECTION_SIZE * SECTION_SIZE)
|
||||||
}
|
checkOffset[axis] = 1
|
||||||
neighbours[Directions.SOUTH.ordinal] = if (z == ProtocolDefinition.SECTION_MAX_Z) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
section.blocks[ChunkSection.getIndex(x, y, z + 1)]
|
|
||||||
}
|
|
||||||
neighbours[Directions.WEST.ordinal] = if (x == 0) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
section.blocks[ChunkSection.getIndex(x - 1, y, z)]
|
|
||||||
}
|
|
||||||
neighbours[Directions.EAST.ordinal] = if (x == ProtocolDefinition.SECTION_MAX_X) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
section.blocks[ChunkSection.getIndex(x + 1, y, z)]
|
|
||||||
}
|
|
||||||
val model = block.model
|
|
||||||
|
|
||||||
random.setSeed(0L)
|
val offsetCheck = backFace.decide(-1, 1)
|
||||||
model?.singleRender(Vec3i(x, y, z), mesh, random, neighbours, 0xFF, intArrayOf(0xF, 0xF, 0xF, 0xF))
|
|
||||||
|
// Check each slice of the chunk one at a time
|
||||||
|
|
||||||
|
position[axis] = -1
|
||||||
|
while (position[axis] < SECTION_SIZE) {
|
||||||
|
|
||||||
|
// Compute the mask
|
||||||
|
var n = 0
|
||||||
|
position[nextNextAxis] = 0
|
||||||
|
while (position[nextNextAxis] < SECTION_SIZE) {
|
||||||
|
position[nextAxis] = 0
|
||||||
|
while (position[nextAxis] < SECTION_SIZE) {
|
||||||
|
// checkOffset determines the direction (X, Y or Z) that we are searching
|
||||||
|
// m.IsBlockAt(x,y,z) takes global map positions and returns true if a block exists there
|
||||||
|
if ((offsetCheck == 1 && position[axis] < 0) || (offsetCheck == -1 && position[axis] > SECTION_SIZE)) {
|
||||||
|
++position[nextAxis]
|
||||||
|
n++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val currentBlock = if (position[axis] >= 0) section.blocks[ChunkSection.getIndex(position[0], position[1], position[2])] else null
|
||||||
|
val compareBlock = if (position[axis] < SECTION_SIZE - 1) section.blocks[ChunkSection.getIndex(position[0] + checkOffset[0], position[1] + checkOffset[1], position[2] + checkOffset[2])] else null
|
||||||
|
|
||||||
|
// The mask is set to true if there is a visible face between two blocks,
|
||||||
|
// i.e. both aren't empty and both aren't blocks
|
||||||
|
val primaryBlock = if (backFace) {
|
||||||
|
compareBlock
|
||||||
|
} else {
|
||||||
|
currentBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
mask[n++] = primaryBlock != null && currentBlock != compareBlock
|
||||||
|
++position[nextAxis]
|
||||||
|
}
|
||||||
|
++position[nextNextAxis]
|
||||||
|
}
|
||||||
|
++position[axis]
|
||||||
|
n = 0
|
||||||
|
|
||||||
|
// Generate a mesh from the mask using lexicographic ordering,
|
||||||
|
// by looping over each block in this slice of the chunk
|
||||||
|
j = 0
|
||||||
|
while (j < SECTION_SIZE) {
|
||||||
|
i = 0
|
||||||
|
while (i < SECTION_SIZE) {
|
||||||
|
if (mask[n]) {
|
||||||
|
// Compute the width of this quad and store it in w
|
||||||
|
// This is done by searching along the current axis until mask[n + w] is false
|
||||||
|
w = 1
|
||||||
|
while (i + w < SECTION_SIZE && mask[n + w]) {
|
||||||
|
w++
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Compute the height of this quad and store it in h
|
||||||
|
// This is done by checking if every block next to this row (range 0 to w) is also part of the mask.
|
||||||
|
// For example, if w is 5 we currently have a quad of dimensions 1 x 5. To reduce triangle count,
|
||||||
|
// greedy meshing will attempt to expand this quad out to CHUNK_SIZE x 5, but will stop if it reaches a hole in the mask
|
||||||
|
var done = false
|
||||||
|
|
||||||
|
h = 1
|
||||||
|
while (j + h < SECTION_SIZE) {
|
||||||
|
k = 0
|
||||||
|
while (k < w) {
|
||||||
|
if (!mask[n + k + h * SECTION_SIZE]) {
|
||||||
|
done = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
if (done) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
h++
|
||||||
|
}
|
||||||
|
|
||||||
|
position[nextAxis] = i
|
||||||
|
position[nextNextAxis] = j
|
||||||
|
|
||||||
|
// du and dv determine the size and orientation of this face
|
||||||
|
val du = IntArray(3)
|
||||||
|
du[nextAxis] = w
|
||||||
|
val dv = IntArray(3)
|
||||||
|
dv[nextNextAxis] = h
|
||||||
|
|
||||||
|
|
||||||
|
if (!backFace) {
|
||||||
|
position[axis] -= offsetCheck
|
||||||
|
}
|
||||||
|
|
||||||
|
val start = Vec3i(position[0], position[1], position[2])
|
||||||
|
|
||||||
|
|
||||||
|
val end = Vec3i(position[0] + du[0] + dv[0], position[1] + du[1] + dv[1], position[2] + du[2] + dv[2])
|
||||||
|
|
||||||
|
|
||||||
|
val block = section.blocks[ChunkSection.getIndex(position[0], position[1], position[2])]!!
|
||||||
|
(block.model as GreedyBakedBlockModel).greedyRender(start, end, direction, mesh, 0xFF)
|
||||||
|
|
||||||
|
|
||||||
|
if (!backFace) {
|
||||||
|
position[axis] += offsetCheck
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear this part of the mask, so we don't add duplicate faces
|
||||||
|
l = 0
|
||||||
|
while (l < h) {
|
||||||
|
k = 0
|
||||||
|
while (k < w) {
|
||||||
|
mask[n + k + l * SECTION_SIZE] = false
|
||||||
|
++k
|
||||||
|
}
|
||||||
|
++l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment counters and continue
|
||||||
|
i += w
|
||||||
|
n += w
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++j
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import glm_.vec2.Vec2i
|
|||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.util.zip.GZIPInputStream
|
import java.util.zip.GZIPInputStream
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
class WorldRenderer(
|
class WorldRenderer(
|
||||||
private val connection: PlayConnection,
|
private val connection: PlayConnection,
|
||||||
@ -63,10 +64,11 @@ class WorldRenderer(
|
|||||||
lightMap.use(shader)
|
lightMap.use(shader)
|
||||||
|
|
||||||
|
|
||||||
|
val random = Random(0L)
|
||||||
val blockState = connection.registries.blockRegistry["diamond_block"]?.defaultState
|
val blockState = connection.registries.blockRegistry["diamond_block"]?.defaultState
|
||||||
val chunk = ChunkSection(Array(4096) { if (it < 4096) blockState else null })
|
val chunk = ChunkSection(Array(4096) { if (random.nextBoolean()) blockState else null })
|
||||||
// for(i in 0 until 100000)
|
for (i in 0 until 1000)
|
||||||
mesh = sectionPreparer.prepare(chunk)
|
mesh = sectionPreparer.prepare(chunk)
|
||||||
mesh.load()
|
mesh.load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import glm_.vec3.Vec3
|
|||||||
class ChunkSectionMesh(renderWindow: RenderWindow) : Mesh(renderWindow, SectionArrayMeshStruct, initialCacheSize = 100000) {
|
class ChunkSectionMesh(renderWindow: RenderWindow) : Mesh(renderWindow, SectionArrayMeshStruct, initialCacheSize = 100000) {
|
||||||
|
|
||||||
fun addVertex(position: Vec3, uv: Vec2, texture: AbstractTexture, tintColor: RGBColor?, light: Int) {
|
fun addVertex(position: Vec3, uv: Vec2, texture: AbstractTexture, tintColor: RGBColor?, light: Int) {
|
||||||
|
//val texture = renderWindow.WHITE_TEXTURE.texture
|
||||||
val textureLayer = if (RenderConstants.FORCE_DEBUG_TEXTURE) {
|
val textureLayer = if (RenderConstants.FORCE_DEBUG_TEXTURE) {
|
||||||
RenderConstants.DEBUG_TEXTURE_ID
|
RenderConstants.DEBUG_TEXTURE_ID
|
||||||
} else {
|
} else {
|
||||||
|
@ -24,7 +24,8 @@ import java.util.*
|
|||||||
|
|
||||||
class BakedBlockStateModel(
|
class BakedBlockStateModel(
|
||||||
val faces: Array<Array<BakedFace>>,
|
val faces: Array<Array<BakedFace>>,
|
||||||
) : BakedBlockModel { // ToDo: Greedy meshable
|
) : BakedBlockModel, GreedyBakedBlockModel { // ToDo: Greedy meshable
|
||||||
|
override val canGreedyMesh: Boolean = true
|
||||||
|
|
||||||
override fun getFaceSize(direction: Directions, random: Random): Array<FaceSize> {
|
override fun getFaceSize(direction: Directions, random: Random): Array<FaceSize> {
|
||||||
return arrayOf() // ToDo
|
return arrayOf() // ToDo
|
||||||
@ -43,6 +44,14 @@ class BakedBlockStateModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun greedyRender(start: Vec3i, end: Vec3i, side: Directions, mesh: ChunkSectionMesh, light: Int) {
|
||||||
|
val floatStart = start.toVec3()
|
||||||
|
val floatEnd = end.toVec3()
|
||||||
|
for (face in faces[side.ordinal]) {
|
||||||
|
face.greedyRender(floatStart, floatEnd, side, mesh, light)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getLight(position: Vec3i, random: Random, side: Directions, lightAccessor: LightAccessor): Int {
|
override fun getLight(position: Vec3i, random: Random, side: Directions, lightAccessor: LightAccessor): Int {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,13 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.gui.rendering.models.baked.block
|
package de.bixilon.minosoft.gui.rendering.models.baked.block
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.data.Axes
|
||||||
import de.bixilon.minosoft.data.direction.Directions
|
import de.bixilon.minosoft.data.direction.Directions
|
||||||
import de.bixilon.minosoft.data.registries.blocks.BlockState
|
import de.bixilon.minosoft.data.registries.blocks.BlockState
|
||||||
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
|
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
|
||||||
import de.bixilon.minosoft.gui.rendering.models.FaceSize
|
import de.bixilon.minosoft.gui.rendering.models.FaceSize
|
||||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
|
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
|
||||||
|
import de.bixilon.minosoft.gui.rendering.util.VecUtil.get
|
||||||
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
|
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
|
||||||
import glm_.vec2.Vec2
|
import glm_.vec2.Vec2
|
||||||
import glm_.vec3.Vec3
|
import glm_.vec3.Vec3
|
||||||
@ -37,4 +39,33 @@ class BakedFace(
|
|||||||
mesh.addVertex(positions[index] + position, uv[textureIndex], texture, null, light)
|
mesh.addVertex(positions[index] + position, uv[textureIndex], texture, null, light)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun greedyRender(start: Vec3, end: Vec3, side: Directions, mesh: ChunkSectionMesh, light: Int) {
|
||||||
|
val multiplier = end - start
|
||||||
|
val positions = arrayOf(
|
||||||
|
(positions[0] * multiplier) + start,
|
||||||
|
(positions[1] * multiplier) + start,
|
||||||
|
(positions[2] * multiplier) + start,
|
||||||
|
(positions[3] * multiplier) + start,
|
||||||
|
)
|
||||||
|
val fixPosition = this.positions[0][side.axis]
|
||||||
|
for (position in positions) {
|
||||||
|
when (side.axis) {
|
||||||
|
Axes.X -> position.x = start.x + fixPosition
|
||||||
|
Axes.Y -> position.y = start.y + fixPosition
|
||||||
|
Axes.Z -> position.z = start.z + fixPosition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val uvMultiplier = side.getUVMultiplier(start, end)
|
||||||
|
val uv = arrayOf(
|
||||||
|
uv[0] * uvMultiplier,
|
||||||
|
uv[1] * uvMultiplier,
|
||||||
|
uv[2] * uvMultiplier,
|
||||||
|
uv[3] * uvMultiplier,
|
||||||
|
)
|
||||||
|
for ((index, textureIndex) in Mesh.QUAD_DRAW_ODER) {
|
||||||
|
mesh.addVertex(positions[index], uv[textureIndex], texture, null, light)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ public final class ProtocolDefinition {
|
|||||||
public static final Pattern RESOURCE_LOCATION_PATTERN = Pattern.compile("([a-z_0-9]+:)?[a-zA-Z_0-9.]+");
|
public static final Pattern RESOURCE_LOCATION_PATTERN = Pattern.compile("([a-z_0-9]+:)?[a-zA-Z_0-9.]+");
|
||||||
public static final Pattern SCOREBOARD_OBJECTIVE_PATTERN = Pattern.compile("[a-zA-z-.+]{1,16}");
|
public static final Pattern SCOREBOARD_OBJECTIVE_PATTERN = Pattern.compile("[a-zA-z-.+]{1,16}");
|
||||||
|
|
||||||
|
public static final int SECTION_SIZE = 16;
|
||||||
public static final int SECTION_WIDTH_X = 16;
|
public static final int SECTION_WIDTH_X = 16;
|
||||||
public static final int SECTION_MAX_X = SECTION_WIDTH_X - 1;
|
public static final int SECTION_MAX_X = SECTION_WIDTH_X - 1;
|
||||||
public static final int SECTION_WIDTH_Z = 16;
|
public static final int SECTION_WIDTH_Z = 16;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user