@@ -225,7 +225,7 @@ def __init__(self):
225225 self .diffuse = [1.0 , 1.0 , 1.0 , 1.0 ]
226226 self .specular = [1.0 , 1.0 , 1.0 , 1.0 ]
227227 self .setup_done = False
228-
228+
229229 def apply (self ):
230230 if not self .setup_done :
231231 glLightfv (GL_LIGHT0 , GL_AMBIENT , self .ambient )
@@ -244,41 +244,58 @@ def __init__(self):
244244 self .light_color = [0.0 , 0.0 , 0.0 ]
245245 self .brightness = 2
246246 self .display_list = None
247-
247+ self .texture = None
248+
248249 def create_display_list (self ):
249250 if self .display_list is not None :
250251 return
251-
252+
253+ self ._create_blurred_texture ()
254+
252255 try :
253256 self .display_list = glGenLists (1 )
254257 if self .display_list == 0 :
255258 return
256-
259+
257260 glNewList (self .display_list , GL_COMPILE )
258-
261+
259262 size = self .size
260- dark_r = self .dark_color [0 ] * self .brightness
261- dark_g = self .dark_color [1 ] * self .brightness
262- dark_b = self .dark_color [2 ] * self .brightness
263- light_r = self .light_color [0 ] * self .brightness
264- light_g = self .light_color [1 ] * self .brightness
265- light_b = self .light_color [2 ] * self .brightness
266-
267- glNormal3f (0 , 1 , 0 )
268- glBegin (GL_QUADS )
269- for x in range (- size , size ):
270- for z in range (- size , size ):
271- if (x + z ) & 1 :
272- glColor3f (light_r , light_g , light_b )
273- else :
274- glColor3f (dark_r , dark_g , dark_b )
275-
276- glVertex3f (x , 0 , z )
277- glVertex3f (x , 0 , z + 1 )
278- glVertex3f (x + 1 , 0 , z + 1 )
279- glVertex3f (x + 1 , 0 , z )
280- glEnd ()
281-
263+
264+ if self .texture :
265+ glEnable (GL_TEXTURE_2D )
266+ glBindTexture (GL_TEXTURE_2D , self .texture )
267+ glNormal3f (0 , 1 , 0 )
268+ glColor3f (1 , 1 , 1 )
269+ glBegin (GL_QUADS )
270+ glTexCoord2f (0 , 0 ); glVertex3f (- size , 0 , - size )
271+ glTexCoord2f (1 , 0 ); glVertex3f (size , 0 , - size )
272+ glTexCoord2f (1 , 1 ); glVertex3f (size , 0 , size )
273+ glTexCoord2f (0 , 1 ); glVertex3f (- size , 0 , size )
274+ glEnd ()
275+ glDisable (GL_TEXTURE_2D )
276+ else :
277+ dark_r = self .dark_color [0 ] * self .brightness
278+ dark_g = self .dark_color [1 ] * self .brightness
279+ dark_b = self .dark_color [2 ] * self .brightness
280+ light_r = self .light_color [0 ] * self .brightness
281+ light_g = self .light_color [1 ] * self .brightness
282+ light_b = self .light_color [2 ] * self .brightness
283+
284+ glNormal3f (0 , 1 , 0 )
285+ glBegin (GL_QUADS )
286+ for x in range (- size , size ):
287+ for z in range (- size , size ):
288+ if (x + z ) & 1 :
289+ glColor3f (light_r , light_g , light_b )
290+ else :
291+ glColor3f (dark_r , dark_g , dark_b )
292+
293+ glVertex3f (x , 0 , z )
294+ glVertex3f (x , 0 , z + 1 )
295+ glVertex3f (x + 1 , 0 , z + 1 )
296+ glVertex3f (x + 1 , 0 , z )
297+ glEnd ()
298+
282299 glEndList ()
283300 except Exception as e :
284301 print (f"Error creating checkerboard display list: { e } " )
@@ -288,11 +305,69 @@ def create_display_list(self):
288305 except :
289306 pass
290307 self .display_list = None
291-
292- def draw (self ):
308+
309+ def _create_blurred_texture (self ):
310+ """generate a pre-blurred checkerboard texture"""
311+ try :
312+ tex_size = 1024
313+ tile_count = self .size * 2
314+ pixels_per_tile = tex_size / tile_count
315+
316+ dark_val = int (self .dark_color [0 ] * self .brightness * 255 )
317+ light_val = int (self .light_color [0 ] * self .brightness * 255 )
318+
319+ # generate sharp checkerboard
320+ data = bytearray (tex_size * tex_size * 3 )
321+ for y in range (tex_size ):
322+ for x in range (tex_size ):
323+ tx = int (x / pixels_per_tile )
324+ ty = int (y / pixels_per_tile )
325+ val = light_val if (tx + ty ) & 1 else dark_val
326+ idx = (y * tex_size + x ) * 3
327+ data [idx ] = val
328+ data [idx + 1 ] = val
329+ data [idx + 2 ] = val
330+
331+ # gentle blur: blend 20% blurred with 80% sharp
332+ sharp = bytes (data )
333+ blurred = bytearray (tex_size * tex_size * 3 )
334+ radius = 1
335+ for y in range (tex_size ):
336+ for x in range (tex_size ):
337+ r_sum = 0
338+ count = 0
339+ for dy in range (- radius , radius + 1 ):
340+ for dx in range (- radius , radius + 1 ):
341+ nx = min (max (x + dx , 0 ), tex_size - 1 )
342+ ny = min (max (y + dy , 0 ), tex_size - 1 )
343+ r_sum += sharp [(ny * tex_size + nx ) * 3 ]
344+ count += 1
345+ idx = (y * tex_size + x ) * 3
346+ blur_val = r_sum // count
347+ sharp_val = sharp [idx ]
348+ val = int (sharp_val * 0.9 + blur_val * 0.1 )
349+ blurred [idx ] = val
350+ blurred [idx + 1 ] = val
351+ blurred [idx + 2 ] = val
352+ data = blurred
353+
354+ self .texture = glGenTextures (1 )
355+ glBindTexture (GL_TEXTURE_2D , self .texture )
356+ glTexImage2D (GL_TEXTURE_2D , 0 , GL_RGB , tex_size , tex_size , 0 ,
357+ GL_RGB , GL_UNSIGNED_BYTE , bytes (data ))
358+ glTexParameteri (GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR )
359+ glTexParameteri (GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR )
360+ glTexParameteri (GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE )
361+ glTexParameteri (GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE )
362+ glBindTexture (GL_TEXTURE_2D , 0 )
363+ except Exception as e :
364+ print (f"Blur texture creation failed ({ e } ), using sharp checkerboard" )
365+ self .texture = None
366+
367+ def draw (self , display_width = None , display_height = None ):
293368 if self .display_list is None :
294369 return
295-
370+
296371 glPushMatrix ()
297372 glTranslatef (* self .position )
298373 if self .rotation [0 ] or self .rotation [1 ] or self .rotation [2 ]:
@@ -302,14 +377,20 @@ def draw(self):
302377 glScalef (self .scale [0 ], self .scale [1 ], self .scale [2 ])
303378 glCallList (self .display_list )
304379 glPopMatrix ()
305-
380+
306381 def cleanup (self ):
307382 if self .display_list :
308383 try :
309384 glDeleteLists (self .display_list , 1 )
310385 except :
311386 pass
312387 self .display_list = None
388+ if self .texture :
389+ try :
390+ glDeleteTextures ([self .texture ])
391+ except :
392+ pass
393+ self .texture = None
313394
314395class MissingTextureScene :
315396 def __init__ (self , sound_manager = None , display_scale = 1.0 ):
@@ -1033,9 +1114,9 @@ def draw_object(obj):
10331114 brightness_map = {"cube" : 0.6 , "sphere" : 0.7 , "cone" : 0.65 }
10341115 b = brightness_map .get (obj .type , 0.6 ) * obj .brightness
10351116 glColor3f (b , b , b )
1036-
1117+
10371118 glCallList (obj .display_list )
1038-
1119+
10391120 if lighting_disabled :
10401121 glEnable (GL_LIGHTING )
10411122
@@ -1295,12 +1376,54 @@ def main():
12951376
12961377 elif clicked_obj and clicked_obj .type == "cone" :
12971378 sound_manager .play_sound ('cone_click' )
1298-
1379+
1380+ # clear hover so cone shows normal color between flashes
1381+ clicked_obj .is_hovered = False
1382+
1383+ # red -> normal -> red -> normal -> red (fast)
1384+ flash_pattern = [True , False , True , False , True ]
1385+ for is_red in flash_pattern :
1386+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )
1387+ camera .apply ()
1388+ light .apply ()
1389+ board .draw (display [0 ], display [1 ])
1390+ glDisable (GL_TEXTURE_2D )
1391+ glBindTexture (GL_TEXTURE_2D , 0 )
1392+ glEnable (GL_LIGHTING )
1393+ glEnable (GL_DEPTH_TEST )
1394+ glEnable (GL_COLOR_MATERIAL )
1395+ glColorMaterial (GL_FRONT_AND_BACK , GL_AMBIENT_AND_DIFFUSE )
1396+ for o in objects :
1397+ if o == clicked_obj :
1398+ glPushMatrix ()
1399+ glTranslatef (* o .position )
1400+ if o .base_rotation [0 ]: glRotatef (o .base_rotation [0 ], 1 , 0 , 0 )
1401+ if o .base_rotation [1 ]: glRotatef (o .base_rotation [1 ], 0 , 1 , 0 )
1402+ if o .base_rotation [2 ]: glRotatef (o .base_rotation [2 ], 0 , 0 , 1 )
1403+ sx = o .scale * o .scale_xyz [0 ]
1404+ sy = o .scale * o .scale_xyz [1 ]
1405+ sz = o .scale * o .scale_xyz [2 ]
1406+ glScalef (sx , sy , sz )
1407+ if is_red :
1408+ glDisable (GL_LIGHTING )
1409+ glColor3f (1.0 , 0.0 , 0.0 )
1410+ glCallList (o .display_list )
1411+ glEnable (GL_LIGHTING )
1412+ else :
1413+ b = 0.65 * o .brightness
1414+ glColor3f (b , b , b )
1415+ glCallList (o .display_list )
1416+ glPopMatrix ()
1417+ else :
1418+ draw_object (o )
1419+ cursor_renderer .draw (mouse_pos , display [0 ], display [1 ])
1420+ pygame .display .flip ()
1421+ pygame .time .wait (80 )
1422+
12991423 cone_duration = sound_manager .get_sound_duration ('cone_click' )
1300- if cone_duration > 0 :
1301- pygame .time .wait (int (cone_duration * 1000 ))
1302- else :
1303- pygame .time .wait (500 )
1424+ remaining = int (cone_duration * 1000 ) - 400 if cone_duration > 0 else 100
1425+ if remaining > 0 :
1426+ pygame .time .wait (remaining )
13041427
13051428 if hasattr (pygame .display , 'get_desktop_sizes' ):
13061429 desktop_sizes = pygame .display .get_desktop_sizes ()
@@ -1359,9 +1482,23 @@ def main():
13591482 # check triangle click in cone scene (LEFT-CLICK ONLY)
13601483 if cone_scene .check_triangle_click (mouse_pos , display [0 ], display [1 ]):
13611484 sound_manager .play_sound ('cone_back' )
1362-
1363- # 3 second delay
1364- pygame .time .wait (3000 )
1485+
1486+ # clear hover so triangle shows normal color between flashes
1487+ cone_scene .triangle_hovered = False
1488+
1489+ # red -> grey -> red -> grey -> red
1490+ flash_pattern = ["red" , "grey" , "red" , "grey" , "red" ]
1491+ for state in flash_pattern :
1492+ cone_scene .triangle_flash_red = state
1493+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )
1494+ cone_scene .draw (display [0 ], display [1 ])
1495+ cursor_renderer .draw (mouse_pos , display [0 ], display [1 ])
1496+ pygame .display .flip ()
1497+ pygame .time .wait (80 )
1498+ cone_scene .triangle_flash_red = False
1499+
1500+ # 3 second delay (minus flash time)
1501+ pygame .time .wait (2600 )
13651502
13661503 # return to main menu
13671504 current_scene = "main"
@@ -1410,12 +1547,14 @@ def main():
14101547 ray_caster .update_matrices ()
14111548
14121549 check_object_hover (mouse_pos , ray_caster , objects , sound_manager )
1413-
1414- board .draw ()
1415-
1550+
1551+ board .draw (display [0 ], display [1 ])
1552+ glDisable (GL_TEXTURE_2D )
1553+ glBindTexture (GL_TEXTURE_2D , 0 )
1554+
14161555 for obj in objects :
14171556 draw_object (obj )
1418-
1557+
14191558 cursor_renderer .draw (mouse_pos , display [0 ], display [1 ])
14201559
14211560 elif current_scene == "error" :
0 commit comments