Skip to content

Commit 5989a04

Browse files
committed
Add debug axis rendering, fix element rotation
1 parent 68175f8 commit 5989a04

File tree

5 files changed

+163
-83
lines changed

5 files changed

+163
-83
lines changed

out.png

-767 Bytes
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#version 330
2+
3+
out vec4 outColor;
4+
uniform vec4 color;
5+
6+
void main() {
7+
outColor = color;
8+
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#version 330
2+
3+
uniform mat4 m_proj;
4+
uniform mat4 m_camera;
5+
uniform mat4 m_model;
6+
7+
in vec3 in_position;
8+
9+
void main() {
10+
gl_Position = m_proj * m_camera * m_model * vec4(in_position, 1.0);
11+
}

src/hexdoc/graphics/render.py

+142-81
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
ModelElement,
3131
)
3232
from hexdoc.minecraft.models.load import load_model
33+
from hexdoc.utils.types import Vec3, Vec4
3334

3435
# https://minecraft.wiki/w/Help:Isometric_renders#Preferences
3536
LIGHT_UP = 0.98
@@ -40,20 +41,20 @@
4041
@dataclass
4142
class BlockRenderer:
4243
loader: ModResourceLoader
44+
debug: bool = False
4345

4446
def __post_init__(self):
4547
self.window = HeadlessWindow(
4648
size=(300, 300),
4749
)
50+
mglw.activate_context(self.window)
4851

4952
self.config = BlockRendererConfig(ctx=self.window.ctx, wnd=self.window)
50-
self.window.config = self.config
5153

54+
self.window.config = self.config
5255
self.window.swap_buffers()
5356
self.window.set_default_viewport()
5457

55-
mglw.activate_context(self.window)
56-
5758
@property
5859
def ctx(self):
5960
return self.window.ctx
@@ -72,7 +73,7 @@ def render_block_model(self, model: BlockModel | ResourceLocation):
7273
for name, texture_id in model.resolve_texture_variables().items()
7374
}
7475

75-
self.config.render_block(model, textures)
76+
self.config.render_block(model, textures, self.debug)
7677

7778
def load_texture(self, texture_id: ResourceLocation):
7879
_, path = self.loader.find_resource("assets", "textures", texture_id + ".png")
@@ -101,17 +102,7 @@ def __init__(self, ctx: Context, wnd: HeadlessWindow):
101102
super().__init__(ctx, wnd)
102103

103104
# ensure faces are displayed in the correct order
104-
self.ctx.enable(mgl.DEPTH_TEST)
105-
106-
self.face_prog = self.ctx.program(
107-
vertex_shader=read_shader("block_face", "vertex"),
108-
fragment_shader=read_shader("block_face", "fragment"),
109-
)
110-
111-
# self.debug_prog = self.ctx.program(
112-
# vertex_shader=read_shader("debug", "vertex"),
113-
# fragment_shader=read_shader("debug", "fragment"),
114-
# )
105+
self.ctx.enable(mgl.DEPTH_TEST | mgl.BLEND)
115106

116107
view_size = 16 # TODO: value?
117108
self.projection = Matrix44.orthogonal_projection(
@@ -125,15 +116,49 @@ def __init__(self, ctx: Context, wnd: HeadlessWindow):
125116
)
126117

127118
self.camera = orbit_camera(yaw=90, pitch=0)
119+
# self.camera = orbit_camera(yaw=180, pitch=0)
120+
121+
# block faces
122+
123+
self.face_prog = self.ctx.program(
124+
vertex_shader=read_shader("block_face", "vertex"),
125+
fragment_shader=read_shader("block_face", "fragment"),
126+
)
128127

129128
self.uniform("m_proj").write(self.projection)
130129
self.uniform("m_camera").write(self.camera)
131130
self.uniform("layer").value = 0 # TODO: implement animations
132131

132+
# debugging, eg. axis planes
133+
134+
self.debug_prog = self.ctx.program(
135+
vertex_shader=read_shader("debug", "vertex"),
136+
fragment_shader=read_shader("debug", "fragment"),
137+
)
138+
139+
self.uniform("m_proj", "debug").write(self.projection)
140+
self.uniform("m_camera", "debug").write(self.camera)
141+
self.uniform("m_model", "debug").write(Matrix44.identity(dtype="f4"))
142+
143+
self.debug_axes = list[tuple[VAO, Vec4]]()
144+
145+
pos = 8
146+
neg = 0
147+
for from_, to, color, direction in [
148+
((0, neg, neg), (0, pos, pos), (1, 0, 0, 0.75), "east"),
149+
((neg, 0, neg), (pos, 0, pos), (0, 1, 0, 0.75), "up"),
150+
((neg, neg, 0), (pos, pos, 0), (0, 0, 1, 0.75), "south"),
151+
]:
152+
vao = VAO()
153+
verts = get_face_verts(from_, to, direction)
154+
vao.buffer(np.array(verts, dtype=np.float32), "3f", ["in_position"])
155+
self.debug_axes.append((vao, color))
156+
133157
def render_block(
134158
self,
135159
model: BlockModel,
136160
texture_vars: dict[str, BlockTextureInfo],
161+
debug: bool = False,
137162
):
138163
if not model.elements:
139164
raise ValueError("Unable to render model, no elements found")
@@ -164,32 +189,41 @@ def render_block(
164189

165190
model_transform *= (
166191
Matrix44.from_translation(gui.translation)
167-
* Matrix44.from_x_rotation(gui.eulers[0])
168-
* Matrix44.from_y_rotation(gui.eulers[1])
169-
* Matrix44.from_z_rotation(gui.eulers[2])
192+
* get_rotation_matrix(gui.eulers)
170193
* Matrix44.from_scale(gui.scale)
171194
)
172195

173-
model_transform *= Matrix44.from_translation((-8, -8, -8))
196+
# render elements
174197

175198
for element in model.elements:
176-
# transform element
177-
178199
element_transform = model_transform.copy()
179-
# if rotation := element.rotation:
180-
# # TODO: rescale??
181-
# origin = np.array(rotation.origin)
182-
# element_transform *= Matrix44.from_translation(-origin)
183-
# element_transform *= Matrix44.from_eulers(rotation.eulers)
184-
# element_transform *= Matrix44.from_translation(origin)
200+
201+
# TODO: rescale??
202+
if rotation := element.rotation:
203+
center = np.add(element.to, element.from_) / 2
204+
origin = np.array(rotation.origin)
205+
element_transform *= (
206+
Matrix44.from_translation(center - origin)
207+
* get_rotation_matrix(rotation.eulers)
208+
* Matrix44.from_translation(origin - center)
209+
)
210+
211+
element_transform *= Matrix44.from_translation((-8, -8, -8))
185212

186213
self.uniform("m_model").write(element_transform)
187214

188215
# render each face of the element
189216
for direction, face in element.faces.items():
190217
self.uniform("light").value = get_face_light(direction)
191218
self.uniform("texture0").value = texture_locs[face.texture.lstrip("#")]
192-
get_face_vao(element, direction, face).render(self.face_prog)
219+
220+
vao = get_face_vao(element, direction, face)
221+
vao.render(self.face_prog)
222+
223+
if debug:
224+
for axis, color in self.debug_axes:
225+
self.uniform("color", "debug").value = color
226+
axis.render(self.debug_prog)
193227

194228
self.ctx.finish()
195229

@@ -201,9 +235,14 @@ def render_block(
201235

202236
image.save("out.png", format="png")
203237

204-
def uniform(self, name: str):
205-
uniform = self.face_prog[name]
206-
assert isinstance(uniform, Uniform)
238+
def uniform(self, name: str, prog_name: Literal["face", "debug"] = "face"):
239+
match prog_name:
240+
case "face":
241+
prog = self.face_prog
242+
case "debug":
243+
prog = self.debug_prog
244+
245+
assert isinstance(uniform := prog[name], Uniform)
207246
return uniform
208247

209248

@@ -231,26 +270,80 @@ def layers(self):
231270
return max_index + 1
232271

233272

234-
def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
235-
x1, y1, z1 = element.from_
236-
x2, y2, z2 = element.to
237-
238-
if face.uv:
239-
u1, v1, u2, v2 = face.uv
240-
else:
241-
u1, v1, u2, v2 = get_default_uv(element, direction)
273+
def get_face_verts(from_: Vec3, to: Vec3, direction: FaceName):
274+
x1, y1, z1 = from_
275+
x2, y2, z2 = to
242276

243277
# fmt: off
244278
match direction:
245279
case "south":
246-
verts = [
280+
return [
247281
x2, y1, z2,
248282
x2, y2, z2,
249283
x1, y1, z2,
250284
x2, y2, z2,
251285
x1, y2, z2,
252286
x1, y1, z2,
253287
]
288+
case "east":
289+
return [
290+
x2, y1, z1,
291+
x2, y2, z1,
292+
x2, y1, z2,
293+
x2, y2, z1,
294+
x2, y2, z2,
295+
x2, y1, z2,
296+
]
297+
case "down":
298+
return [
299+
x2, y1, z1,
300+
x2, y1, z2,
301+
x1, y1, z2,
302+
x2, y1, z1,
303+
x1, y1, z2,
304+
x1, y1, z1,
305+
]
306+
case "west":
307+
return [
308+
x1, y1, z2,
309+
x1, y2, z2,
310+
x1, y2, z1,
311+
x1, y1, z2,
312+
x1, y2, z1,
313+
x1, y1, z1,
314+
]
315+
case "north":
316+
return [
317+
x2, y2, z1,
318+
x2, y1, z1,
319+
x1, y1, z1,
320+
x2, y2, z1,
321+
x1, y1, z1,
322+
x1, y2, z1,
323+
]
324+
case "up":
325+
return [
326+
x2, y2, z1,
327+
x1, y2, z1,
328+
x2, y2, z2,
329+
x1, y2, z1,
330+
x1, y2, z2,
331+
x2, y2, z2,
332+
]
333+
# fmt: on
334+
335+
336+
def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
337+
verts = get_face_verts(element.from_, element.to, direction)
338+
339+
if face.uv:
340+
u1, v1, u2, v2 = face.uv
341+
else:
342+
u1, v1, u2, v2 = get_default_uv(element, direction)
343+
344+
# fmt: off
345+
match direction:
346+
case "south":
254347
uvs = [
255348
u2, v1,
256349
u2, v2,
@@ -260,14 +353,6 @@ def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
260353
u1, v1,
261354
]
262355
case "east":
263-
verts = [
264-
x2, y1, z1,
265-
x2, y2, z1,
266-
x2, y1, z2,
267-
x2, y2, z1,
268-
x2, y2, z2,
269-
x2, y1, z2,
270-
]
271356
uvs = [
272357
u2, v1,
273358
u2, v2,
@@ -277,14 +362,6 @@ def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
277362
u1, v1,
278363
]
279364
case "down":
280-
verts = [
281-
x2, y1, z1,
282-
x2, y1, z2,
283-
x1, y1, z2,
284-
x2, y1, z1,
285-
x1, y1, z2,
286-
x1, y1, z1,
287-
]
288365
uvs = [
289366
u2, v2,
290367
u1, v2,
@@ -294,14 +371,6 @@ def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
294371
u2, v1,
295372
]
296373
case "west":
297-
verts = [
298-
x1, y1, z2,
299-
x1, y2, z2,
300-
x1, y2, z1,
301-
x1, y1, z2,
302-
x1, y2, z1,
303-
x1, y1, z1,
304-
]
305374
uvs = [
306375
u1, v2,
307376
u1, v1,
@@ -311,14 +380,6 @@ def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
311380
u2, v2,
312381
]
313382
case "north":
314-
verts = [
315-
x2, y2, z1,
316-
x2, y1, z1,
317-
x1, y1, z1,
318-
x2, y2, z1,
319-
x1, y1, z1,
320-
x1, y2, z1,
321-
]
322383
uvs = [
323384
u2, v1,
324385
u2, v2,
@@ -328,14 +389,6 @@ def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
328389
u1, v1,
329390
]
330391
case "up":
331-
verts = [
332-
x2, y2, z1,
333-
x1, y2, z1,
334-
x2, y2, z2,
335-
x1, y2, z1,
336-
x1, y2, z2,
337-
x2, y2, z2,
338-
]
339392
uvs = [
340393
u2, v2,
341394
u1, v2,
@@ -422,6 +475,14 @@ def orbit_camera(pitch: float, yaw: float):
422475
)
423476

424477

425-
def read_shader(path: str, type: Literal["fragment", "vertex"]):
478+
def read_shader(path: str, type: Literal["fragment", "vertex", "geometry"]):
426479
file = resources.files(glsl) / path / f"{type}.glsl"
427480
return file.read_text("utf-8")
481+
482+
483+
def get_rotation_matrix(eulers: Vec3):
484+
return (
485+
Matrix44.from_x_rotation(eulers[0])
486+
* Matrix44.from_y_rotation(eulers[1])
487+
* Matrix44.from_z_rotation(eulers[2])
488+
)

0 commit comments

Comments
 (0)