skeletal: change normal encoding, shade lighting interpolation

This commit is contained in:
Moritz Zwerger 2023-10-29 16:04:47 +01:00
parent b6b0a82ca9
commit 219a8085f8
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
3 changed files with 148 additions and 16 deletions

View File

@ -14,17 +14,21 @@
package de.bixilon.minosoft.gui.rendering.skeletal.mesh
import de.bixilon.kotlinglm.vec3.Vec3
import kotlin.math.abs
object SkeletalMeshUtil {
private fun encodePart(part: Float): Int {
val unsigned = (part + 1.0f) / 2.0f // remove negative sign
return (unsigned * 15.0f).toInt() and 0x0F
private fun encodeY(part: Float): Int {
if (part <= -1.0f) return 0
if (part >= 1.0f) return 0x0F
if (part < 0.0f) return ((part + 1.0f) * 8.0f).toInt() and 0x0F
return 8 + (part * 7.0f).toInt()
}
fun encodeNormal(normal: Vec3): Int {
val x = encodePart(normal.x)
val y = encodePart(normal.y)
val z = encodePart(normal.z)
val x = (abs(normal.x) * 15.0f).toInt()
val y = encodeY(normal.y)
val z = (abs(normal.z) * 15.0f).toInt()
return (y shl 8) or (z shl 4) or (x)
}

View File

@ -10,16 +10,18 @@
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
#define DEGREE_90 1.5707964f
float decodeNormalPart(uint data) {
return (data / 15.0f) * 2.0f - 1.0f;
if (data <= 8u) return (data / 8.0f) - 1.0f;
return (data - 8u) / 7.0f;
}
vec3 decodeNormal(uint normal) {
uint x = normal & 0x0Fu;
uint y = normal >> 8u & 0x0Fu;
uint z = normal >> 4u & 0x0Fu;
return vec3(decodeNormalPart(x), decodeNormalPart(y), decodeNormalPart(z));
return vec3(x / 15.0f, decodeNormalPart(y), z / 15.0f);
}
vec3 transformNormal(vec3 normal, mat4 transform) {
@ -27,12 +29,26 @@ vec3 transformNormal(vec3 normal, mat4 transform) {
return mat3(transform) * normal;
}
float getShade(vec3 normal) {
// TODO: interpolate between 3 sides
if (normal.y < -0.5f) return 0.5f;
if (normal.y > 0.5f) return 1.0f;
if (normal.x < -0.5f || normal.x > 0.5f) return 0.6f;
if (normal.z < -0.5f || normal.z > 0.5f) return 0.8f;
return 1.0f;
float interpolateShade(float delta, float max) {
if (delta < 0.0f) delta = -delta;
if (delta <= 0.0f) return 0.0f;
if (delta >= 1.0f) return max;
return delta * max;
}
float getShade(vec3 normal) {
float aX = asin(normal.x) / DEGREE_90;
float aY = asin(normal.y) / DEGREE_90;
float aZ = asin(normal.z) / DEGREE_90;
float x = interpolateShade(aX, 0.6f);
float y;
if (normal.y < 0.0f) {
y = interpolateShade(-aY, 0.5f);
} else {
y = interpolateShade(aY, 1.0f);
}
float z = interpolateShade(aZ, 0.8f);
return (x + y + z);
}

View File

@ -0,0 +1,112 @@
/*
* 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.gui.rendering.models.block.state.baked
import de.bixilon.kotlinglm.vec3.Vec3
import org.junit.jupiter.api.Test
import kotlin.math.abs
import kotlin.math.asin
class SkeletalShadeTest {
private val DEGREE_90 = 1.5707964f
fun interpolateShade(delta: Float, max: Float): Float {
var delta = delta
if (delta < 0.0f) delta = -delta
if (delta <= 0.0f) return 0.0f
if (delta >= 1.0f) return max
return delta * max
}
fun getShade(normal: Vec3): Float {
normal.normalizeAssign() // for testing purposes
// Take code from skeletal/shade.glsl
val aX = asin(normal.x) / DEGREE_90
val aY = asin(normal.y) / DEGREE_90
val aZ = asin(normal.z) / DEGREE_90
val x = interpolateShade(aX, 0.6f)
val y: Float
y = if (normal.y < 0.0f) {
interpolateShade(-aY, 0.5f)
} else {
interpolateShade(aY, 1.0f)
}
val z = interpolateShade(aZ, 0.8f)
return x + y + z
}
@Test
fun up() {
assertEquals(1.0f, getShade(Vec3(0, 1, 0)))
}
@Test
fun down() {
assertEquals(0.5f, getShade(Vec3(0, -1, 0)))
}
@Test
fun north() {
assertEquals(0.8f, getShade(Vec3(0, 0, 1)))
}
@Test
fun south() {
assertEquals(0.8f, getShade(Vec3(0, 0, -1)))
}
@Test
fun west() {
assertEquals(0.6f, getShade(Vec3(1, 0, 0)))
}
@Test
fun east() {
assertEquals(0.6f, getShade(Vec3(-1, 0, 0)))
}
@Test
fun northWest() {
assertEquals(0.7f, getShade(Vec3(1, 0, 1)))
}
@Test
fun eastSouth() {
assertEquals(0.7f, getShade(Vec3(-1, 0, -1)))
}
@Test
fun westUp() {
assertEquals(0.8f, getShade(Vec3(1, 1, 0)))
}
@Test
fun westDown() {
assertEquals(0.55f, getShade(Vec3(-1, -1, 0)))
}
@Test
fun westNorthUp() {
assertEquals(0.94f, getShade(Vec3(1, 1, 1)))
}
private fun assertEquals(expected: Float, actual: Float) {
if (abs(expected - actual) < 0.03f) return
throw AssertionError("Expected $expected but got $actual")
}
}