Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions objmc/assets/minecraft/shaders/include/objmc_main.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -196,11 +196,18 @@ if (marker == ivec4(12,34,56,78)) {
vec3 vPos0 = subgroupQuadBroadcast(Pos, 0);
vec3 vPos1 = subgroupQuadBroadcast(Pos, 1);
vec3 vPos2 = subgroupQuadBroadcast(Pos, 3);
float scale = distance(vPos0, vPos1);
float entityScale = distance(vPos0, vPos1);
vPos1 = normalize(vPos0 - vPos1);
vPos2 = normalize(vPos0 - vPos2);
mat3 fullRotation = mat3(vPos2, vPos1, cross(vPos2, vPos1));
posoffset = scale * fullRotation * posoffset;
vec3 facing = cross(vPos2, vPos1);
float yaw = -atan(facing.x, facing.z);
float pitch = -atan(facing.y, length(facing.xz));
vec3 trackedRotation = vec3(
pitch * autorotate.x,
yaw * autorotate.y,
0
);
posoffset = entityScale * rotate(trackedRotation + rotation) * posoffset;
}
//pure color rotation
else {
Expand All @@ -223,4 +230,4 @@ if (marker == ivec4(12,34,56,78)) {
// posoffset = vec3(gl_VertexID % 4 - 2, gl_VertexID % 4 / 2 * 2, -(gl_VertexID % 4) + 2 * 2);
// Pos += posoffset;
// vertexColor = vec4(1.0,0.0,0.0,1.0);
//}
//}
106 changes: 106 additions & 0 deletions tests/test_autorotate_shader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import re
import unittest
from pathlib import Path


SHADER_PATH = (
Path(__file__).parents[1]
/ "objmc"
/ "assets"
/ "minecraft"
/ "shaders"
/ "include"
/ "objmc_main.glsl"
)
GENERATOR_PATH = Path(__file__).parents[1] / "objmc.py"


class AutorotateShaderTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.shader = SHADER_PATH.read_text(encoding="utf-8")
cls.generator = GENERATOR_PATH.read_text(encoding="utf-8")
match = re.search(
r"if \(any\(greaterThan\(autorotate,vec2\(0\)\)\)\) \{(?P<body>.*?)\n\s*\}",
cls.shader,
re.DOTALL,
)
if match is None:
raise AssertionError("autorotate shader block was not found")
cls.autorotate_body = match.group("body")

decoder = re.search(
r"vec2 autorotate = vec2\(getb\(t\[6\]\.r, (\d), 1\), "
r"getb\(t\[6\]\.r, (\d), 1\)\);",
cls.shader,
)
if decoder is None:
raise AssertionError("autorotate metadata decoder was not found")
cls.pitch_bit, cls.yaw_bit = map(int, decoder.groups())

encoder = re.search(r"autorotate << (\d)", cls.generator)
if encoder is None:
raise AssertionError("autorotate metadata encoder was not found")
cls.encoder_shift = int(encoder.group(1))

@classmethod
def decode_mode(cls, mode):
metadata = mode << cls.encoder_shift
return (
(metadata >> cls.pitch_bit) & 1,
(metadata >> cls.yaw_bit) & 1,
)

def test_metadata_modes_decode_to_expected_axis_masks(self):
expected = {
0: (0, 0),
1: (0, 1),
2: (1, 0),
3: (1, 1),
}
self.assertEqual(
{mode: self.decode_mode(mode) for mode in expected},
expected,
)

def test_axis_masks_lock_the_unselected_angle(self):
def tracked_angles(mode, pitch, yaw):
pitch_mask, yaw_mask = self.decode_mode(mode)
return pitch * pitch_mask, yaw * yaw_mask

self.assertEqual(tracked_angles(1, 0.25, 1.0), tracked_angles(1, 0.75, 1.0))
self.assertEqual(tracked_angles(2, 0.5, 0.25), tracked_angles(2, 0.5, 1.25))
self.assertNotEqual(tracked_angles(3, 0.25, 1.0), tracked_angles(3, 0.75, 1.0))

def test_autorotate_derives_facing_from_the_quad_basis(self):
self.assertRegex(
self.autorotate_body,
r"vec3 facing\s*=\s*cross\(vPos2,\s*vPos1\)",
)
self.assertNotRegex(self.autorotate_body, r"\bNormal\b")

def test_autorotate_masks_pitch_and_yaw_independently(self):
self.assertRegex(
self.autorotate_body,
r"pitch\s*\*\s*autorotate\.x",
)
self.assertRegex(
self.autorotate_body,
r"yaw\s*\*\s*autorotate\.y",
)

def test_autorotate_preserves_color_rotation(self):
self.assertRegex(
self.autorotate_body,
r"rotate\(trackedRotation\s*\+\s*rotation\)",
)

def test_autorotate_preserves_entity_scale(self):
self.assertRegex(
self.autorotate_body,
r"entityScale\s*\*\s*rotate\(",
)


if __name__ == "__main__":
unittest.main()