From e858e6a1d9f24aba286ccc3802ce8114b0783ba5 Mon Sep 17 00:00:00 2001 From: Moritz Zwerger Date: Tue, 1 Aug 2023 03:14:53 +0200 Subject: [PATCH] wip element rotation --- .../models/baked/BakedModelTestUtil.kt | 12 +++- .../baked/rotation/ElementRotationTest.kt | 60 +++++++++++++++++++ .../block/state/baked/cull/FaceCullingTest.kt | 12 ++-- .../models/block/element/ElementRotation.kt | 13 +++- .../models/block/state/baked/BakedModel.kt | 2 +- .../block/state/baked/cull/FaceCulling.kt | 4 -- .../gui/rendering/util/vec/vec3/Vec3Util.kt | 15 +---- 7 files changed, 87 insertions(+), 31 deletions(-) create mode 100644 src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/rotation/ElementRotationTest.kt 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 8337f9635..6da87de9c 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 @@ -25,6 +25,7 @@ import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture import de.bixilon.minosoft.protocol.network.connection.play.ConnectionTestUtil import de.bixilon.minosoft.util.KUtil.toResourceLocation import org.testng.Assert +import kotlin.math.abs object BakedModelTestUtil { private val texture = Minosoft::class.java.getResourceAsStream("/assets/minosoft/textures/debug.png")!!.readAllBytes() @@ -56,9 +57,18 @@ object BakedModelTestUtil { if (faces.size != 1) throw IllegalArgumentException("Model has more/less than once face: ${faces.size}!") val face = faces.first() - vertices?.let { Assert.assertEquals(face.positions, it, "Vertices mismatch") } + vertices?.let { assertMatches(face.positions, it, "Vertices mismatch") } uv?.let { if (!face.uv.contentEquals(it)) throw AssertionError("UV mismatch, expected [${uv[0]}|${uv[1]}], but got [${face.uv[0]}|${face.uv[1]}]") } // printing the first element is fine, it is always clockwise shade?.let { Assert.assertEquals(face.shade, it, "Shade mismatch") } texture?.toResourceLocation()?.texture()?.let { Assert.assertEquals(face.texture, it, "Texture mismatch") } } + + private fun assertMatches(actual: FloatArray, expected: FloatArray, message: String) { + if (actual.size != expected.size) throw AssertionError("Size mismatch!") + + for (i in actual.indices) { + val delta = abs(actual[i] - expected[i]) + if (delta > 0.01f) throw AssertionError("$message: Delta is too high at index $i: ${actual[i]}, expected ${expected[i]}") + } + } } diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/rotation/ElementRotationTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/rotation/ElementRotationTest.kt new file mode 100644 index 000000000..4adafcc82 --- /dev/null +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/baked/rotation/ElementRotationTest.kt @@ -0,0 +1,60 @@ +/* + * 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.rotation + +import de.bixilon.kotlinglm.vec3.Vec3 +import de.bixilon.minosoft.data.Axes +import de.bixilon.minosoft.data.direction.Directions +import de.bixilon.minosoft.data.registries.identified.Namespaces.minecraft +import de.bixilon.minosoft.gui.rendering.models.ModelTestUtil.bake +import de.bixilon.minosoft.gui.rendering.models.ModelTestUtil.rbgy +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.ElementRotation +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.textures.TextureUtil.texture +import org.testng.annotations.Test + +@Test(groups = ["models"]) +class ElementRotationTest { + + private fun block(rotation: ElementRotation): SingleBlockStateApply { + val from = Vec3(0.0f) + val to = Vec3(1.0f) + return SingleBlockStateApply(BlockModel(elements = listOf(ModelElement(from, to, faces = createFaces(), rotation = rotation)), textures = mapOf("test" to minecraft("block/test").texture()))) + } + + fun `rotate block around origin 45 degree on the y axis`() { + val model = block(ElementRotation(axis = Axes.Y, angle = 45.0f)) + + val baked = model.bake(createTextureManager("block/test"))!! + + + baked.assertFace(Directions.UP, floatArrayOf(-0.2f, 1.0f, 0.5f, 0.5f, 1.0f, -0.2f, 1.2f, 1.0f, 0.5f, 0.5f, 1.0f, 1.2f), rbgy, 1.0f) + } + + fun `rotate block around origin -45 degree on the y axis`() { + val model = block(ElementRotation(axis = Axes.Y, angle = -45.0f)) + + val baked = model.bake(createTextureManager("block/test"))!! + + + baked.assertFace(Directions.UP, floatArrayOf(0.5f, 1.0f, -0.2f, 1.2f, 1.0f, 0.5f, 0.5f, 1.0f, 1.2f, -0.2f, 1.0f, 0.5f), rbgy, 1.0f) + } + + // TODO: a,z axis +} diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/block/state/baked/cull/FaceCullingTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/block/state/baked/cull/FaceCullingTest.kt index a5d72dd92..610bf9256 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/block/state/baked/cull/FaceCullingTest.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/block/state/baked/cull/FaceCullingTest.kt @@ -14,20 +14,16 @@ package de.bixilon.minosoft.gui.rendering.models.block.state.baked.cull import de.bixilon.kotlinglm.vec2.Vec2 -import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kutil.exception.Broken import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.registries.blocks.settings.BlockSettings import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.blocks.types.Block import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft -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.cull.side.FaceProperties import de.bixilon.minosoft.gui.rendering.models.block.state.baked.cull.side.SideProperties import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies -import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.memory.MemoryTexture -import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY import org.testng.Assert.assertFalse import org.testng.Assert.assertTrue import org.testng.annotations.Test @@ -35,8 +31,8 @@ import org.testng.annotations.Test @Test(groups = ["models", "culling"]) class FaceCullingTest { - private fun createFace(transparency: TextureTransparencies = TextureTransparencies.OPAQUE, properties: FaceProperties? = FaceProperties(Vec2(0), Vec2(1), transparency)): BakedFace { - return BakedFace(floatArrayOf(), floatArrayOf(), 1.0f, -1, null, MemoryTexture(Vec2i.EMPTY), properties) + private fun createFace(transparency: TextureTransparencies = TextureTransparencies.OPAQUE, properties: FaceProperties? = FaceProperties(Vec2(0), Vec2(1), transparency)): FaceProperties? { + return properties } private fun createBlock(transparency: TextureTransparencies = TextureTransparencies.OPAQUE, properties: SideProperties? = SideProperties(arrayOf(FaceProperties(Vec2(0), Vec2(1), transparency)), transparency), type: Int = 0): BlockState { @@ -228,7 +224,7 @@ class FaceCullingTest { val block = object : Block(minosoft("dummy"), BlockSettings()), CustomBlockCulling { override val hardness get() = Broken() - override fun shouldCull(state: BlockState, face: BakedFace, directions: Directions, neighbour: BlockState): Boolean { + override fun shouldCull(state: BlockState, properties: FaceProperties, directions: Directions, neighbour: BlockState): Boolean { throw AssertionError("shouldCall invoked!") } } @@ -244,7 +240,7 @@ class FaceCullingTest { private fun forceNoCull() = object : Block(minosoft("dummy"), BlockSettings()), CustomBlockCulling { override val hardness get() = Broken() - override fun shouldCull(state: BlockState, face: BakedFace, directions: Directions, neighbour: BlockState): Boolean { + override fun shouldCull(state: BlockState, properties: FaceProperties, directions: Directions, neighbour: BlockState): Boolean { return false } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/ElementRotation.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/ElementRotation.kt index ce03ed74e..9c90dc8c2 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/ElementRotation.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/element/ElementRotation.kt @@ -13,23 +13,30 @@ package de.bixilon.minosoft.gui.rendering.models.block.element +import de.bixilon.kotlinglm.func.rad import de.bixilon.kotlinglm.vec3.Vec3 import de.bixilon.kutil.json.JsonObject import de.bixilon.kutil.primitive.BooleanUtil.toBoolean import de.bixilon.kutil.primitive.FloatUtil.toFloat import de.bixilon.minosoft.data.Axes +import de.bixilon.minosoft.data.entities.EntityRotation.Companion.CIRCLE_DEGREE import de.bixilon.minosoft.gui.rendering.models.block.element.ModelElement.Companion.BLOCK_SIZE +import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.rotateAssign import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.toVec3 data class ElementRotation( - val origin: Vec3, + val origin: Vec3 = ORIGIN, val axis: Axes, val angle: Float, val rescale: Boolean = false, ) { fun apply(positions: FloatArray) { -// TODO("Can not rotate yet!") + val angle = -angle.rad + Vec3(0, positions).rotateAssign(angle, axis, origin, rescale) + Vec3(3, positions).rotateAssign(angle, axis, origin, rescale) + Vec3(6, positions).rotateAssign(angle, axis, origin, rescale) + Vec3(9, positions).rotateAssign(angle, axis, origin, rescale) } @@ -37,7 +44,7 @@ data class ElementRotation( private val ORIGIN = Vec3(0.5f) fun deserialize(data: JsonObject): ElementRotation? { - val angle = data["angle"]?.toFloat() ?: return null + val angle = data["angle"]?.toFloat()?.mod(CIRCLE_DEGREE.toFloat()) ?: return null if (angle == 0.0f) return null val rescale = data["rescale"]?.toBoolean() ?: false diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/baked/BakedModel.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/baked/BakedModel.kt index b12cd1e99..72bd88bba 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/baked/BakedModel.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/baked/BakedModel.kt @@ -47,7 +47,7 @@ class BakedModel( val direction = Directions.VALUES[directionIndex].inverted for (face in faces) { - if (FaceCulling.canCull(state, face, direction, neighbour)) { + if (FaceCulling.canCull(state, face.properties, direction, neighbour)) { continue } face.render(offset, mesh, light, tints) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/baked/cull/FaceCulling.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/baked/cull/FaceCulling.kt index ad070e4a6..c2b7c0904 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/baked/cull/FaceCulling.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/baked/cull/FaceCulling.kt @@ -22,10 +22,6 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparenci object FaceCulling { - inline fun canCull(state: BlockState, face: BakedFace, direction: Directions, neighbour: BlockState?): Boolean { - return canCull(state, face.properties, direction, neighbour) - } - fun canCull(state: BlockState, properties: FaceProperties?, direction: Directions, neighbour: BlockState?): Boolean { if (neighbour == null) return false if (properties == null) return false diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3Util.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3Util.kt index 914a15ff4..ae9ff5437 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3Util.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3Util.kt @@ -61,7 +61,7 @@ object Vec3Util { fun rotate(x: Float, y: Float, sin: Float, cos: Float, rescale: Boolean): Vec2 { val result = Vec2(x * cos - y * sin, x * sin + y * cos) if (rescale) { - return result / cos + result /= cos } return result } @@ -78,19 +78,6 @@ object Vec3Util { } } - fun Vec3.rotateAssign(rotation: Vec2, centerBlock: Boolean = false) { - if (centerBlock) { - this -= 0.5f - } - - rotateAssign(-rotation.x, Axes.X) - rotateAssign(rotation.y, Axes.Y) - - if (centerBlock) { - this += 0.5f - } - } - fun Vec3.rotateAssign(angle: Float, axis: Axes, origin: Vec3, rescale: Boolean) { this -= origin rotateAssign(angle, axis, rescale)