direction rotating, tests, improve model baking

This commit is contained in:
Bixilon 2023-03-24 10:17:03 +01:00
parent 3fc0cf6751
commit 45c5b6b9c9
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
12 changed files with 236 additions and 30 deletions

View File

@ -220,7 +220,7 @@ testing {
options {
val options = this as TestNGOptions
options.preserveOrder = true
options.excludeGroups("version", "fluid", "world", "raycasting", "pixlyzer", "item", "block", "physics", "light", "packet", "container", "item_stack", "signature", "private_key", "interaction", "item_digging", "world_renderer", "rendering")
options.excludeGroups("command", "registry", "biome", "input", "version", "fluid", "world", "raycasting", "pixlyzer", "item", "block", "physics", "light", "packet", "container", "item_stack", "signature", "private_key", "interaction", "item_digging", "world_renderer", "rendering")
}
}
}

View File

@ -42,12 +42,13 @@ class CuboidBakeTest {
fun cuboidY90() {
val from = Vec3(1, 2, 3) / BLOCK_SIZE
val to = Vec3(16, 15, 14) / BLOCK_SIZE
val model = SingleBlockStateApply(BlockModel(elements = listOf(ModelElement(from, to, faces = createFaces(from, to))), textures = mapOf("test" to minecraft("block/test").texture())), y = 90)
val model = SingleBlockStateApply(BlockModel(elements = listOf(ModelElement(from, to, faces = createFaces(from, to))), textures = mapOf("test" to minecraft("block/test").texture())), y = 1)
val baked = model.bake(createTextureManager("block/test"))!!
baked.assertFace(Directions.DOWN, block(2, 2, 1, 2, 2, 16, 13, 2, 16, 13, 2, 1), block(1, 14, 16, 14, 16, 3, 1, 3), 0.5f)
baked.assertFace(Directions.UP, block(2, 15, 1, 13, 15, 1, 13, 15, 16, 2, 15, 16), block(1, 2, 1, 13, 16, 13, 16, 2), 1.0f)
// TODO: other sides
}
}

View File

@ -49,7 +49,7 @@ class FullCubeBakeTest {
fun fullCubeY90() {
val from = Vec3(0.0f)
val to = Vec3(1.0f)
val model = SingleBlockStateApply(BlockModel(elements = listOf(ModelElement(from, to, faces = createFaces())), textures = mapOf("test" to minecraft("block/test").texture())), y = 90)
val model = SingleBlockStateApply(BlockModel(elements = listOf(ModelElement(from, to, faces = createFaces())), textures = mapOf("test" to minecraft("block/test").texture())), y = 1)
val baked = model.bake(createTextureManager("block/test"))!!
@ -66,7 +66,7 @@ class FullCubeBakeTest {
fun fullCubeX90() {
val from = Vec3(0.0f)
val to = Vec3(1.0f)
val model = SingleBlockStateApply(BlockModel(elements = listOf(ModelElement(from, to, faces = createFaces())), textures = mapOf("test" to minecraft("block/test").texture())), x = 90)
val model = SingleBlockStateApply(BlockModel(elements = listOf(ModelElement(from, to, faces = createFaces())), textures = mapOf("test" to minecraft("block/test").texture())), x = 1)
val baked = model.bake(createTextureManager("block/test"))!!
@ -82,7 +82,7 @@ class FullCubeBakeTest {
fun fullCubeX90Y90() {
val from = Vec3(0.0f)
val to = Vec3(1.0f)
val model = SingleBlockStateApply(BlockModel(elements = listOf(ModelElement(from, to, faces = createFaces())), textures = mapOf("test" to minecraft("block/test").texture())), x = 90, y = 90)
val model = SingleBlockStateApply(BlockModel(elements = listOf(ModelElement(from, to, faces = createFaces())), textures = mapOf("test" to minecraft("block/test").texture())), x = 1, y = 1)
val baked = model.bake(createTextureManager("block/test"))!!

View File

@ -26,7 +26,7 @@ import de.bixilon.minosoft.protocol.packets.c2s.play.entity.interact.EntityAttac
import de.bixilon.minosoft.protocol.packets.c2s.play.move.SwingArmC2SP
import org.testng.annotations.Test
@Test(groups = ["integration"])
@Test(groups = ["interaction"])
class InteractionManagerTest {
fun attackNoTarget() {

View File

@ -16,9 +16,20 @@ package de.bixilon.minosoft.data.direction
import de.bixilon.kotlinglm.func.rad
import de.bixilon.kotlinglm.mat4x4.Mat4
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kutil.array.ArrayUtil
import de.bixilon.minosoft.data.Axes
object DirectionUtil {
fun Directions.rotateY(count: Int = 1): Directions {
if (count == 0) return this
if (axis == Axes.Y) return this
var count = count % Directions.SIZE_SIDES
if (count < 0) count += Directions.SIZE_SIDES
return Directions.HORIZONTAL[ArrayUtil.modifyArrayIndex((horizontalId - Directions.SIDE_OFFSET) + count, Directions.SIZE_SIDES)]
}
fun rotateMatrix(direction: Directions): Mat4 {
return when (direction) {
Directions.DOWN -> Mat4().translateAssign(Vec3(0.5f)).rotateAssign(180.0f.rad, Vec3(1, 0, 0)).translateAssign(Vec3(-0.5f))

View File

@ -20,7 +20,6 @@ import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.cast.CastUtil.unsafeNull
import de.bixilon.kutil.enums.EnumUtil
import de.bixilon.kutil.enums.ValuesEnum
import de.bixilon.kutil.exception.Broken
import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
import de.bixilon.minosoft.data.Axes
import de.bixilon.minosoft.data.registries.blocks.properties.serializer.BlockPropertiesSerializer
@ -33,13 +32,14 @@ import kotlin.reflect.jvm.javaField
enum class Directions(
@Deprecated("remove") val horizontalId: Int,
val vector: Vec3i,
val horizontal: Int,
) {
DOWN(-1, Vec3i(0, -1, 0)),
UP(-1, Vec3i(0, 1, 0)),
NORTH(2, Vec3i(0, 0, -1)),
SOUTH(0, Vec3i(0, 0, 1)),
WEST(1, Vec3i(-1, 0, 0)),
EAST(3, Vec3i(1, 0, 0));
DOWN(-1, Vec3i(0, -1, 0), -1),
UP(-1, Vec3i(0, 1, 0), -1),
NORTH(2, Vec3i(0, 0, -1), 0),
SOUTH(0, Vec3i(0, 0, 1), 2),
WEST(1, Vec3i(-1, 0, 0), 3),
EAST(3, Vec3i(1, 0, 0), 1);
val negative = ordinal % 2 == 0
@ -63,15 +63,6 @@ enum class Directions(
return vector[axis]
}
fun rotateYC(): Directions {
return when (this) {
NORTH -> EAST
SOUTH -> WEST
WEST -> NORTH
EAST -> SOUTH
else -> Broken("Rotation: $this")
}
}
@Deprecated("outsource")
fun getPositions(from: Vec3, to: Vec3): Array<Vec3> {
@ -153,6 +144,7 @@ enum class Directions(
override val VALUES = values()
override val NAME_MAP: Map<String, Directions> = EnumUtil.getEnumValues(VALUES)
val SIDES = arrayOf(NORTH, SOUTH, WEST, EAST)
val HORIZONTAL = arrayOf(NORTH, EAST, SOUTH, WEST)
val XYZ = arrayOf(WEST, EAST, DOWN, UP, NORTH, SOUTH)

View File

@ -20,6 +20,7 @@ import de.bixilon.kutil.primitive.IntUtil.toInt
import de.bixilon.kutil.random.RandomUtil.chance
import de.bixilon.minosoft.data.container.ItemStackUtil
import de.bixilon.minosoft.data.container.stack.ItemStack
import de.bixilon.minosoft.data.direction.DirectionUtil.rotateY
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.blocks.properties.BlockProperties.Companion.getFacing
import de.bixilon.minosoft.data.registries.blocks.properties.BlockProperties.Companion.isLit
@ -82,9 +83,9 @@ class CampfireBlockEntity(connection: PlayConnection) : BlockEntity(connection)
val direction = HORIZONTAL[Math.floorMod(index + facing, Directions.SIDES.size)]
val position = Vec3d(blockPosition) + Vec3d(
0.5f - direction.vector.x * DIRECTION_OFFSET + direction.rotateYC().vector.x * DIRECTION_OFFSET,
0.5f - direction.vector.x * DIRECTION_OFFSET + direction.rotateY().vector.x * DIRECTION_OFFSET,
0.5f,
0.5f - direction.vector.z * DIRECTION_OFFSET + direction.rotateYC().vector.z * DIRECTION_OFFSET,
0.5f - direction.vector.z * DIRECTION_OFFSET + direction.rotateY().vector.z * DIRECTION_OFFSET,
)
for (i in 0 until 4) {

View File

@ -14,6 +14,7 @@
package de.bixilon.minosoft.gui.rendering.models.block.element.face
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.gui.rendering.models.block.element.ModelElement.Companion.BLOCK_SIZE
data class FaceUV(
@ -22,4 +23,18 @@ data class FaceUV(
) {
constructor(u1: Float, v1: Float, u2: Float, v2: Float) : this(Vec2(u1, v1), Vec2(u2, v2))
constructor(u1: Int, v1: Int, u2: Int, v2: Int) : this(u1 / BLOCK_SIZE, v1 / BLOCK_SIZE, u2 / BLOCK_SIZE, v2 / BLOCK_SIZE)
fun toArray(direction: Directions): FloatArray {
return when (direction) {
// @formatter:off
Directions.DOWN,
Directions.SOUTH,
Directions.WEST -> floatArrayOf(start.x, start.y, start.x, end.y, end.x, end.y, end.x, start.y)
Directions.UP -> floatArrayOf(start.x, end.y, end.x, end.y, end.x, start.y, start.x, start.y)
Directions.NORTH,
Directions.EAST -> floatArrayOf(end.x, start.y, start.x, start.y, start.x, end.y, end.x, end.y)
// @formatter:on
}
}
}

View File

@ -16,11 +16,14 @@ package de.bixilon.minosoft.gui.rendering.models.block.state.apply
import de.bixilon.kutil.json.JsonObject
import de.bixilon.kutil.primitive.BooleanUtil.toBoolean
import de.bixilon.kutil.primitive.IntUtil.toInt
import de.bixilon.minosoft.data.direction.DirectionUtil.rotateY
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.gui.rendering.models.block.BlockModel
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.BakedFace
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.BakedModel
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.BakingUtil.compact
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.BakingUtil.positions
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.BakingUtil.pushRight
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.SideSize
import de.bixilon.minosoft.gui.rendering.models.loader.BlockLoader
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager
@ -39,27 +42,48 @@ data class SingleBlockStateApply(
override fun bake(textures: TextureManager): BakedModel? {
if (model.elements == null) return null
val bakedFaces: MutableMap<Directions, MutableList<BakedFace>> = EnumMap(Directions::class.java) // TODO: use array
val bakedFaces: Array<MutableList<BakedFace>> = Array(Directions.SIZE) { mutableListOf() }
val sizes: MutableMap<Directions, MutableList<SideSize.FaceSize>> = EnumMap(Directions::class.java)
for (element in model.elements) {
for ((direction, face) in element.faces) {
val texture = face.createTexture(model, textures)
var rotatedDirection = direction
if (y != 0) {
rotatedDirection = rotatedDirection.rotateY(this.y)
}
val positions = positions(rotatedDirection, element.from, element.to)
val positions = positions(direction, element.from, element.to)
var uv = face.uv.toArray(direction)
if (y != 0 && !uvLock) {
uv = uv.pushRight(2, if (rotatedDirection.negative) -y else y)
}
val shade = direction.shade
val bakedFace = BakedFace(positions, uv, shade, face.tintIndex, face.cull, texture)
bakedFaces[direction.ordinal] += bakedFace
}
}
TODO()
return BakedModel(bakedFaces.compact(), emptyArray())
}
companion object {
const val ROTATION_STEP = 90
fun deserialize(model: BlockModel, data: JsonObject): SingleBlockStateApply {
val uvLock = data["uvlock"]?.toBoolean() ?: false
val weight = data["weight"]?.toInt() ?: 1
val x = data["x"]?.toInt() ?: 0
val y = data["y"]?.toInt() ?: 0
var x = data["x"]?.toInt() ?: 0
var y = data["y"]?.toInt() ?: 0
if (x % ROTATION_STEP != 0) throw IllegalArgumentException("Invalid x rotation: $x")
x /= ROTATION_STEP
if (y % ROTATION_STEP != 0) throw IllegalArgumentException("Invalid x rotation: $y")
y /= ROTATION_STEP
return SingleBlockStateApply(model, uvLock, weight, x, y)
}
@ -69,5 +93,13 @@ data class SingleBlockStateApply(
return deserialize(model, data)
}
val Directions.shade: Float
get() = when (this) {
Directions.UP -> 1.0f
Directions.DOWN -> 0.5f
Directions.NORTH, Directions.SOUTH -> 0.8f
Directions.WEST, Directions.EAST -> 0.6f
}
}
}

View File

@ -14,6 +14,7 @@
package de.bixilon.minosoft.gui.rendering.models.block.state.baked
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kutil.array.ArrayUtil.cast
import de.bixilon.minosoft.data.direction.Directions
object BakingUtil {
@ -31,4 +32,33 @@ object BakingUtil {
}
}
inline fun <reified T> Array<MutableList<T>>.compact(): Array<Array<T>> {
val array: Array<Array<T>?> = arrayOfNulls(size)
for ((index, entries) in this.withIndex()) {
array[index] = entries.toTypedArray()
}
return array.cast()
}
fun FloatArray.pushRight(components: Int, steps: Int): FloatArray {
if (this.size % components != 0) throw IllegalArgumentException("Size mismatch!")
var steps = steps % (size / components)
if (steps < 0) steps += size * components
val target = FloatArray(size)
// push every value $components*steps right
val count = components * steps
for ((index, value) in this.withIndex()) {
val destination = (index + count) % this.size // TODO: allow negative steps
target[destination] = value
}
return target
}
}

View File

@ -0,0 +1,81 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.direction
import de.bixilon.minosoft.data.direction.DirectionUtil.rotateY
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class DirectionUtilTest {
@Test
fun rotateY1() {
assertEquals(Directions.UP.rotateY(1), Directions.UP)
assertEquals(Directions.DOWN.rotateY(1), Directions.DOWN)
assertEquals(Directions.NORTH.rotateY(1), Directions.EAST)
assertEquals(Directions.EAST.rotateY(1), Directions.SOUTH)
assertEquals(Directions.SOUTH.rotateY(1), Directions.WEST)
assertEquals(Directions.WEST.rotateY(1), Directions.NORTH)
}
@Test
fun rotateY5() {
assertEquals(Directions.UP.rotateY(5), Directions.UP)
assertEquals(Directions.DOWN.rotateY(5), Directions.DOWN)
assertEquals(Directions.NORTH.rotateY(5), Directions.EAST)
assertEquals(Directions.EAST.rotateY(5), Directions.SOUTH)
assertEquals(Directions.SOUTH.rotateY(5), Directions.WEST)
assertEquals(Directions.WEST.rotateY(5), Directions.NORTH)
}
@Test
fun rotateY2() {
assertEquals(Directions.UP.rotateY(2), Directions.UP)
assertEquals(Directions.DOWN.rotateY(2), Directions.DOWN)
assertEquals(Directions.NORTH.rotateY(2), Directions.SOUTH)
assertEquals(Directions.EAST.rotateY(2), Directions.WEST)
assertEquals(Directions.SOUTH.rotateY(2), Directions.NORTH)
assertEquals(Directions.WEST.rotateY(2), Directions.EAST)
}
@Test
fun rotateY3() {
assertEquals(Directions.UP.rotateY(3), Directions.UP)
assertEquals(Directions.DOWN.rotateY(3), Directions.DOWN)
assertEquals(Directions.NORTH.rotateY(3), Directions.WEST)
assertEquals(Directions.EAST.rotateY(3), Directions.NORTH)
assertEquals(Directions.SOUTH.rotateY(3), Directions.EAST)
assertEquals(Directions.WEST.rotateY(3), Directions.SOUTH)
}
@Test
fun `rotateY-1`() {
assertEquals(Directions.UP.rotateY(-1), Directions.UP)
assertEquals(Directions.DOWN.rotateY(-1), Directions.DOWN)
assertEquals(Directions.NORTH.rotateY(-1), Directions.WEST)
assertEquals(Directions.EAST.rotateY(-1), Directions.NORTH)
assertEquals(Directions.SOUTH.rotateY(-1), Directions.EAST)
assertEquals(Directions.WEST.rotateY(-1), Directions.SOUTH)
}
@Test
fun rotateY4() {
assertEquals(Directions.UP.rotateY(4), Directions.UP)
assertEquals(Directions.DOWN.rotateY(4), Directions.DOWN)
assertEquals(Directions.NORTH.rotateY(4), Directions.NORTH)
assertEquals(Directions.EAST.rotateY(4), Directions.EAST)
assertEquals(Directions.SOUTH.rotateY(4), Directions.SOUTH)
assertEquals(Directions.WEST.rotateY(4), Directions.WEST)
}
}

View File

@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.models.block.state.baked
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.BakingUtil.pushRight
import kotlin.test.Test
import kotlin.test.assertContentEquals
@ -70,4 +71,46 @@ class BakingUtilTest {
floatArrayOf(6.0f, 2.0f, 3.0f, 6.0f, 2.0f, 4.0f, 6.0f, 5.0f, 4.0f, 6.0f, 5.0f, 3.0f)
)
}
@Test
fun pushRight1() {
val array = floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f)
val expected = floatArrayOf(4f, 5f, 0f, 1f, 2f, 3f)
assertContentEquals(expected, array.pushRight(2, 1))
}
@Test
fun pushRight2() {
val array = floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f)
val expected = floatArrayOf(2f, 3f, 4f, 5f, 0f, 1f)
assertContentEquals(expected, array.pushRight(2, 2))
}
@Test
fun pushRight3() {
val array = floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f)
val expected = floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f)
assertContentEquals(expected, array.pushRight(2, 3))
}
@Test
fun pushLeft1() {
val array = floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f)
val expected = floatArrayOf(2f, 3f, 4f, 5f, 0f, 1f)
assertContentEquals(expected, array.pushRight(2, -1))
}
@Test
fun pushLeft2() {
val array = floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f)
val expected = floatArrayOf(4f, 5f, 0f, 1f, 2f, 3f)
assertContentEquals(expected, array.pushRight(2, -2))
}
@Test
fun pushLeft3() {
val array = floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f)
val expected = floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f)
assertContentEquals(expected, array.pushRight(2, -3))
}
}