30
30
ModelElement ,
31
31
)
32
32
from hexdoc .minecraft .models .load import load_model
33
+ from hexdoc .utils .types import Vec3 , Vec4
33
34
34
35
# https://minecraft.wiki/w/Help:Isometric_renders#Preferences
35
36
LIGHT_UP = 0.98
40
41
@dataclass
41
42
class BlockRenderer :
42
43
loader : ModResourceLoader
44
+ debug : bool = False
43
45
44
46
def __post_init__ (self ):
45
47
self .window = HeadlessWindow (
46
48
size = (300 , 300 ),
47
49
)
50
+ mglw .activate_context (self .window )
48
51
49
52
self .config = BlockRendererConfig (ctx = self .window .ctx , wnd = self .window )
50
- self .window .config = self .config
51
53
54
+ self .window .config = self .config
52
55
self .window .swap_buffers ()
53
56
self .window .set_default_viewport ()
54
57
55
- mglw .activate_context (self .window )
56
-
57
58
@property
58
59
def ctx (self ):
59
60
return self .window .ctx
@@ -72,7 +73,7 @@ def render_block_model(self, model: BlockModel | ResourceLocation):
72
73
for name , texture_id in model .resolve_texture_variables ().items ()
73
74
}
74
75
75
- self .config .render_block (model , textures )
76
+ self .config .render_block (model , textures , self . debug )
76
77
77
78
def load_texture (self , texture_id : ResourceLocation ):
78
79
_ , path = self .loader .find_resource ("assets" , "textures" , texture_id + ".png" )
@@ -101,17 +102,7 @@ def __init__(self, ctx: Context, wnd: HeadlessWindow):
101
102
super ().__init__ (ctx , wnd )
102
103
103
104
# 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 )
115
106
116
107
view_size = 16 # TODO: value?
117
108
self .projection = Matrix44 .orthogonal_projection (
@@ -125,15 +116,49 @@ def __init__(self, ctx: Context, wnd: HeadlessWindow):
125
116
)
126
117
127
118
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
+ )
128
127
129
128
self .uniform ("m_proj" ).write (self .projection )
130
129
self .uniform ("m_camera" ).write (self .camera )
131
130
self .uniform ("layer" ).value = 0 # TODO: implement animations
132
131
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
+
133
157
def render_block (
134
158
self ,
135
159
model : BlockModel ,
136
160
texture_vars : dict [str , BlockTextureInfo ],
161
+ debug : bool = False ,
137
162
):
138
163
if not model .elements :
139
164
raise ValueError ("Unable to render model, no elements found" )
@@ -164,32 +189,41 @@ def render_block(
164
189
165
190
model_transform *= (
166
191
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 )
170
193
* Matrix44 .from_scale (gui .scale )
171
194
)
172
195
173
- model_transform *= Matrix44 . from_translation (( - 8 , - 8 , - 8 ))
196
+ # render elements
174
197
175
198
for element in model .elements :
176
- # transform element
177
-
178
199
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 ))
185
212
186
213
self .uniform ("m_model" ).write (element_transform )
187
214
188
215
# render each face of the element
189
216
for direction , face in element .faces .items ():
190
217
self .uniform ("light" ).value = get_face_light (direction )
191
218
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 )
193
227
194
228
self .ctx .finish ()
195
229
@@ -201,9 +235,14 @@ def render_block(
201
235
202
236
image .save ("out.png" , format = "png" )
203
237
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 )
207
246
return uniform
208
247
209
248
@@ -231,26 +270,80 @@ def layers(self):
231
270
return max_index + 1
232
271
233
272
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
242
276
243
277
# fmt: off
244
278
match direction :
245
279
case "south" :
246
- verts = [
280
+ return [
247
281
x2 , y1 , z2 ,
248
282
x2 , y2 , z2 ,
249
283
x1 , y1 , z2 ,
250
284
x2 , y2 , z2 ,
251
285
x1 , y2 , z2 ,
252
286
x1 , y1 , z2 ,
253
287
]
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" :
254
347
uvs = [
255
348
u2 , v1 ,
256
349
u2 , v2 ,
@@ -260,14 +353,6 @@ def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
260
353
u1 , v1 ,
261
354
]
262
355
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
- ]
271
356
uvs = [
272
357
u2 , v1 ,
273
358
u2 , v2 ,
@@ -277,14 +362,6 @@ def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
277
362
u1 , v1 ,
278
363
]
279
364
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
- ]
288
365
uvs = [
289
366
u2 , v2 ,
290
367
u1 , v2 ,
@@ -294,14 +371,6 @@ def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
294
371
u2 , v1 ,
295
372
]
296
373
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
- ]
305
374
uvs = [
306
375
u1 , v2 ,
307
376
u1 , v1 ,
@@ -311,14 +380,6 @@ def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
311
380
u2 , v2 ,
312
381
]
313
382
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
- ]
322
383
uvs = [
323
384
u2 , v1 ,
324
385
u2 , v2 ,
@@ -328,14 +389,6 @@ def get_face_vao(element: ModelElement, direction: FaceName, face: ElementFace):
328
389
u1 , v1 ,
329
390
]
330
391
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
- ]
339
392
uvs = [
340
393
u2 , v2 ,
341
394
u1 , v2 ,
@@ -422,6 +475,14 @@ def orbit_camera(pitch: float, yaw: float):
422
475
)
423
476
424
477
425
- def read_shader (path : str , type : Literal ["fragment" , "vertex" ]):
478
+ def read_shader (path : str , type : Literal ["fragment" , "vertex" , "geometry" ]):
426
479
file = resources .files (glsl ) / path / f"{ type } .glsl"
427
480
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