diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/BakedModelTestUtil.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/BakedModelTestUtil.kt index 6096ca4c9..9233e0407 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/BakedModelTestUtil.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/BakedModelTestUtil.kt @@ -43,11 +43,11 @@ object BakedModelTestUtil { return rendering.context.textureManager } - fun createFaces(from: Vec3 = Vec3(0.0f), to: Vec3 = Vec3(1.0f), texture: String = "#test"): Map { + fun createFaces(from: Vec3 = Vec3(0.0f), to: Vec3 = Vec3(1.0f), rotation: Int = 0, texture: String = "#test"): Map { val map: MutableMap = mutableMapOf() for (direction in Directions) { - map[direction] = ModelFace(texture, fallbackUV(direction, from, to), cull = direction, rotation = 0) + map[direction] = ModelFace(texture, fallbackUV(direction, from, to), cull = direction, rotation = rotation) } return map diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/FaceRotationTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/FaceRotationTest.kt new file mode 100644 index 000000000..8f7d6249c --- /dev/null +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/FaceRotationTest.kt @@ -0,0 +1,65 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.models.baked + +import de.bixilon.kotlinglm.vec3.Vec3 +import de.bixilon.minosoft.data.direction.Directions +import de.bixilon.minosoft.data.registries.identified.Namespaces.minecraft +import de.bixilon.minosoft.gui.rendering.models.baked.BakedModelTestUtil.assertFace +import de.bixilon.minosoft.gui.rendering.models.baked.BakedModelTestUtil.createFaces +import de.bixilon.minosoft.gui.rendering.models.baked.BakedModelTestUtil.createTextureManager +import de.bixilon.minosoft.gui.rendering.models.block.BlockModel +import de.bixilon.minosoft.gui.rendering.models.block.element.ModelElement +import de.bixilon.minosoft.gui.rendering.models.block.state.apply.SingleBlockStateApply +import de.bixilon.minosoft.gui.rendering.models.block.state.baked.BakingUtil.positions +import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture +import org.testng.annotations.Test + +@Test(groups = ["models"]) +class FaceRotationTest { + + fun rotation1() { + val from = Vec3(0.0f) + val to = Vec3(1.0f) + val model = SingleBlockStateApply(BlockModel(elements = listOf(ModelElement(from, to, faces = createFaces(rotation = 1))), textures = mapOf("test" to minecraft("block/test").texture()))) + + val baked = model.bake(createTextureManager("block/test"))!! + + + // we can use positions() here, because it is not rotated and already unit tested + baked.assertFace(Directions.DOWN, positions(Directions.DOWN, from, to), floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f), 0.5f) + baked.assertFace(Directions.UP, positions(Directions.UP, from, to), floatArrayOf(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f), 1.0f) + baked.assertFace(Directions.NORTH, positions(Directions.NORTH, from, to), floatArrayOf(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), 0.8f) + baked.assertFace(Directions.SOUTH, positions(Directions.SOUTH, from, to), floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f), 0.8f) + baked.assertFace(Directions.WEST, positions(Directions.WEST, from, to), floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f), 0.6f) + baked.assertFace(Directions.EAST, positions(Directions.EAST, from, to), floatArrayOf(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), 0.6f) + } + + fun rotation3() { + val from = Vec3(0.0f) + val to = Vec3(1.0f) + val model = SingleBlockStateApply(BlockModel(elements = listOf(ModelElement(from, to, faces = createFaces(rotation = 3))), textures = mapOf("test" to minecraft("block/test").texture()))) + + val baked = model.bake(createTextureManager("block/test"))!! + + + // we can use positions() here, because it is not rotated and already unit tested + baked.assertFace(Directions.DOWN, positions(Directions.DOWN, from, to), floatArrayOf(0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f), 0.5f) + baked.assertFace(Directions.UP, positions(Directions.UP, from, to), floatArrayOf(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f), 1.0f) + baked.assertFace(Directions.NORTH, positions(Directions.NORTH, from, to), floatArrayOf(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), 0.8f) + baked.assertFace(Directions.SOUTH, positions(Directions.SOUTH, from, to), floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f), 0.8f) + baked.assertFace(Directions.WEST, positions(Directions.WEST, from, to), floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f), 0.6f) + baked.assertFace(Directions.EAST, positions(Directions.EAST, from, to), floatArrayOf(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), 0.6f) + } +} diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/FullCubeBakeTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/FullCubeBakeTest.kt index af80d697e..439952fc6 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/FullCubeBakeTest.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/FullCubeBakeTest.kt @@ -63,6 +63,23 @@ class FullCubeBakeTest { baked.assertFace(Directions.EAST, positions(Directions.EAST, from, to), floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f), 0.6f) } + fun fullCubeY180() { + 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 = 2) + + val baked = model.bake(createTextureManager("block/test"))!! + + + // rotating 180° -> only top/bottom texture rotated, rest is the same + baked.assertFace(Directions.DOWN, positions(Directions.DOWN, from, to), floatArrayOf(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), 0.5f) + baked.assertFace(Directions.UP, positions(Directions.UP, from, to), floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f), 1.0f) + baked.assertFace(Directions.NORTH, positions(Directions.NORTH, from, to), floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f), 0.8f) + baked.assertFace(Directions.SOUTH, positions(Directions.SOUTH, from, to), floatArrayOf(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f), 0.8f) + baked.assertFace(Directions.WEST, positions(Directions.WEST, from, to), floatArrayOf(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f), 0.6f) + baked.assertFace(Directions.EAST, positions(Directions.EAST, from, to), floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f), 0.6f) + } + fun fullCubeX90() { val from = Vec3(0.0f) val to = Vec3(1.0f) @@ -79,6 +96,22 @@ class FullCubeBakeTest { baked.assertFace(Directions.EAST, positions(Directions.EAST, from, to), floatArrayOf(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), 0.6f) } + fun fullCubeX180() { + 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 = 2) + + val baked = model.bake(createTextureManager("block/test"))!! + + + baked.assertFace(Directions.DOWN, positions(Directions.DOWN, from, to), floatArrayOf(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f), 0.5f) + baked.assertFace(Directions.UP, positions(Directions.UP, from, to), floatArrayOf(0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f), 1.0f) + baked.assertFace(Directions.NORTH, positions(Directions.NORTH, from, to), floatArrayOf(0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f), 0.8f) + baked.assertFace(Directions.SOUTH, positions(Directions.SOUTH, from, to), floatArrayOf(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), 0.8f) + baked.assertFace(Directions.WEST, positions(Directions.WEST, from, to), floatArrayOf(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), 0.6f) + baked.assertFace(Directions.EAST, positions(Directions.EAST, from, to), floatArrayOf(0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f), 0.6f) + } + fun fullCubeX90Y90() { val from = Vec3(0.0f) val to = Vec3(1.0f) @@ -94,4 +127,20 @@ class FullCubeBakeTest { baked.assertFace(Directions.WEST, positions(Directions.WEST, from, to), floatArrayOf(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f), 0.6f) baked.assertFace(Directions.EAST, positions(Directions.EAST, from, to), floatArrayOf(0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f), 0.6f) } + + fun fullCubeX180Y180() { + 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 = 2, y = 2) + + val baked = model.bake(createTextureManager("block/test"))!! + + + baked.assertFace(Directions.DOWN, positions(Directions.DOWN, from, to), floatArrayOf(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), 0.5f) + baked.assertFace(Directions.UP, positions(Directions.UP, from, to), floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f), 1.0f) + baked.assertFace(Directions.NORTH, positions(Directions.NORTH, from, to), floatArrayOf(0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f), 0.8f) + baked.assertFace(Directions.SOUTH, positions(Directions.SOUTH, from, to), floatArrayOf(1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f), 0.8f) + baked.assertFace(Directions.WEST, positions(Directions.WEST, from, to), floatArrayOf(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), 0.6f) + baked.assertFace(Directions.EAST, positions(Directions.EAST, from, to), floatArrayOf(0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f), 0.6f) + } } diff --git a/src/main/java/de/bixilon/minosoft/data/direction/DirectionUtil.kt b/src/main/java/de/bixilon/minosoft/data/direction/DirectionUtil.kt index eb4e58549..f874dd560 100644 --- a/src/main/java/de/bixilon/minosoft/data/direction/DirectionUtil.kt +++ b/src/main/java/de/bixilon/minosoft/data/direction/DirectionUtil.kt @@ -27,7 +27,7 @@ object DirectionUtil { 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)] + return Directions.HORIZONTAL[ArrayUtil.modifyArrayIndex(horizontal + count, Directions.SIZE_SIDES)] } fun rotateMatrix(direction: Directions): Mat4 { diff --git a/src/main/java/de/bixilon/minosoft/data/direction/Directions.kt b/src/main/java/de/bixilon/minosoft/data/direction/Directions.kt index 6e7241eae..a9142f019 100644 --- a/src/main/java/de/bixilon/minosoft/data/direction/Directions.kt +++ b/src/main/java/de/bixilon/minosoft/data/direction/Directions.kt @@ -30,7 +30,7 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import kotlin.reflect.jvm.javaField enum class Directions( - @Deprecated("remove") val horizontalId: Int, + @Deprecated("remove") val campfireId: Int, val vector: Vec3i, val horizontal: Int, ) { diff --git a/src/main/java/de/bixilon/minosoft/data/entities/block/CampfireBlockEntity.kt b/src/main/java/de/bixilon/minosoft/data/entities/block/CampfireBlockEntity.kt index aea26a43e..89d70829b 100644 --- a/src/main/java/de/bixilon/minosoft/data/entities/block/CampfireBlockEntity.kt +++ b/src/main/java/de/bixilon/minosoft/data/entities/block/CampfireBlockEntity.kt @@ -73,7 +73,7 @@ class CampfireBlockEntity(connection: PlayConnection) : BlockEntity(connection) } } - val facing = blockState.getFacing().horizontalId + val facing = blockState.getFacing().campfireId for ((index, item) in items.withIndex()) { item ?: continue diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/face/FaceUV.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/face/FaceUV.kt index aa81706ad..7e9b1814f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/face/FaceUV.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/face/FaceUV.kt @@ -30,10 +30,10 @@ data class FaceUV( // @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.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) + Directions.EAST -> floatArrayOf(end.x, start.y, start.x, start.y, start.x, end.y, end.x, end.y ) // @formatter:on } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/face/ModelFace.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/face/ModelFace.kt index febc2baa7..e8080d49f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/face/ModelFace.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/face/ModelFace.kt @@ -21,6 +21,7 @@ import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.registries.identified.ResourceLocation import de.bixilon.minosoft.gui.rendering.models.block.BlockModel import de.bixilon.minosoft.gui.rendering.models.block.element.ModelElement.Companion.BLOCK_SIZE +import de.bixilon.minosoft.gui.rendering.models.block.state.apply.SingleBlockStateApply.Companion.rotation import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture import de.bixilon.minosoft.gui.rendering.tint.TintManager @@ -69,7 +70,7 @@ data class ModelFace( val uv = data["uv"]?.listCast()?.let { FaceUV(start = Vec2(it[0], it[1]) / BLOCK_SIZE, end = Vec2(it[2], it[3]) / BLOCK_SIZE) } ?: fallbackUV(direction, from, to) - val rotation = data["rotation"]?.toInt() ?: 0 + val rotation = data["rotation"]?.toInt()?.rotation() ?: 0 val cull = data["cullface"]?.toString()?.let { if (it == "none") null else Directions[it] } val tintIndex = data["tintindex"]?.toInt() ?: TintManager.DEFAULT_TINT_INDEX diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/apply/SingleBlockStateApply.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/apply/SingleBlockStateApply.kt index 073bb0da6..9cfab1248 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/apply/SingleBlockStateApply.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/apply/SingleBlockStateApply.kt @@ -73,17 +73,19 @@ data class SingleBlockStateApply( companion object { const val ROTATION_STEP = 90 + fun Int.rotation(): Int { + var rotation = this + + if (rotation % ROTATION_STEP != 0) throw IllegalArgumentException("Invalid rotation: $rotation") + rotation /= ROTATION_STEP + return rotation and 0x03 + } + fun deserialize(model: BlockModel, data: JsonObject): SingleBlockStateApply { val uvLock = data["uvlock"]?.toBoolean() ?: false val weight = data["weight"]?.toInt() ?: 1 - 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 + val x = data["x"]?.toInt()?.rotation() ?: 0 + val y = data["y"]?.toInt()?.rotation() ?: 0 return SingleBlockStateApply(model, uvLock, weight, x, y) }