§
    àFÂj+  ã                   ó<  — d Z ddlZddlZddlZddlZddlmZ ddlmZm	Z	m
Z
mZ ddlZddlZddlmZ  ee¦  «                             ¦   «         j        j        j        Z G d„ d¦  «        Z G d„ d	¦  «        Zd
„ Zedk    r ej         e¦   «         ¦  «         dS dS )a  
FreeExile PBR Texture & Normal Map Synthesizer Engine (100% Free & Open-Source)
Converts 2D Diffuse Sprites into:
  1. Tangent-Space Normal Maps (Sobel gradient filter + Height reconstruction)
  2. Specular / Roughness Maps (Surface microfacet frequency analysis)
  3. Emissive / Glow Maps (Elemental hue isolation for Fire/Thunder/Poison visual FX)
  4. MaxRects Power-of-Two (2^n) Texture Atlas Packer with UV coordinate mapping

Designed specifically to feed Apple Metal 120 FPS Shaders (SpritePBRNormal.metal).
é    N)ÚPath)ÚDictÚListÚTupleÚOptional)ÚImagec                   óV  — e Zd ZdZeddej        dedej        fd„¦   «         Ze	 	 	 dd
ej        de	dede
ej                 dej        f
d„¦   «         Zedd
ej        de	dej        fd„¦   «         Zedd
ej        de	dej        fd„¦   «         Zededededeeef         fd„¦   «         Zd	S )ÚPBRTextureSynthesizerzSZero-cost, mathematically pure PBR material synthesizer for 2.5D Isometric Sprites.é   ÚrgbaÚ
iterationsÚreturnc                 óV  — |                       ¦   «         }|dd…dd…dd…f         }|dd…dd…df         }|dk                         t          j        ¦  «        }t	          j        t          j        d¦  «        }t          |¦  «        D ]ˆ}t	          j        ||¦  «        }|dk    |dk    z  }	t          j	        |	¦  «        s nOt          d¦  «        D ]<}
t	          j        |dd…dd…|
f         |¦  «        }||	         |dd…dd…|
f         |	<   Œ=|}Œ‰||dd…dd…dd…f<   ||dd…dd…df<   |S )zí
        Extends RGB colors of opaque pixels into transparent regions (alpha == 0)
        to completely eliminate black borders / alpha bleeding on dark backgrounds,
        while strictly preserving the original alpha channel.
        Né   r   )r   r   é   )
ÚcopyÚastypeÚnpÚuint8Úcv2ÚgetStructuringElementÚ
MORPH_RECTÚrangeÚdilateÚany)r   r   ÚresultÚrgbÚalphaÚmaskÚkernelÚ_Údilated_maskÚfringeÚcÚ	dilated_cs               úEC:\Projects\FreeExile\tools\asset_pipeline\pbr_texture_synthesizer.pyÚapply_dilation_paddingz,PBRTextureSynthesizer.apply_dilation_padding   s\  € ð —’‘”ˆØ�Q�Q�Q˜˜˜˜2˜A˜2�XÔˆØ�q�q�q˜!˜!˜!˜Q�w”ˆØ˜’	×!Ò!¥"¤(Ñ+Ô+ˆÝÔ*­3¬>¸6ÑBÔBˆå�zÑ"Ô"ð 	 ð 	 ˆAÝœ: d¨FÑ3Ô3ˆLØ" aÒ'¨D°AªIÑ6ˆFÝ”6˜&‘>”>ð Ø�Ý˜1‘X”Xð 9ð 9�ÝœJ s¨1¨1¨1¨a¨a¨a°¨7¤|°VÑ<Ô<�	Ø'0°Ô'8��A�A�A�q�q�q˜!�G”˜VÑ$Ð$ØˆDˆDàˆˆqˆqˆq�!�!�!�R�a�RˆxÑØˆˆqˆqˆq�!�!�!�Qˆw‰Øˆó    ç       @r   NÚdiffuse_bgrÚ	intensityÚsmooth_ksizer   c                 óü  — t          | j        ¦  «        dk    r3| j        d         dk    r"|€| dd…dd…df         }| dd…dd…dd…f         } t          j        | t          j        ¦  «                             t          j        ¦  «        dz  }|dk    rt          j        |||fd¦  «        }t          j	        |t          j
        ddd¬¦  «        }t          j	        |t          j
        ddd¬¦  «        }| |z  }| |z  }t          j        |¦  «        }	t          j        |dz  |dz  z   |	dz  z   ¦  «        }
t          j        |
d	¦  «        }
||
z  d
z  d
z   }||
z  d
z  d
z   }|	|
z  d
z  d
z   }|dz                       t          j        ¦  «        }|dz                       t          j        ¦  «        }|dz                       t          j        ¦  «        }|�|dk    }d||<   d||<   d||<   t          j        |||f¦  «        }|S )aZ  
        Generate Tangent-Space Normal Map (RGB where R=X, G=Y, B=Z) using Sobel gradients.
        Normal vector (Nx, Ny, Nz) mapped from [-1, 1] to [0, 255].
        OpenCV BGR layout: Channel 0=B(Nz), Channel 1=G(Ny), Channel 2=R(Nx).
        Transparent pixels default to flat normal (128, 128, 255 in RGB -> [255, 128, 128] in BGR).
        r   é   é   Nç     ào@r   r   ©Úksizeg�íµ ÷Æ°>g      à?éÿ   é€   )ÚlenÚshaper   ÚcvtColorÚCOLOR_BGR2GRAYr   r   Úfloat32ÚGaussianBlurÚSobelÚCV_32FÚ	ones_likeÚsqrtÚmaximumr   Údstack)r*   r+   r,   r   ÚgrayÚsobel_xÚsobel_yÚdxÚdyÚdzÚnormÚnxÚnyÚnzÚb_chÚg_chÚr_chÚ
trans_maskÚ
normal_bgrs                      r&   Úgenerate_normal_mapz)PBRTextureSynthesizer.generate_normal_map9   s  € õ ˆ{Ô Ñ!Ô! QÒ&Ð&¨;Ô+<¸QÔ+?À1Ò+DÐ+DØˆ}Ø# A A A q q q¨! GÔ,�Ø% a a a¨¨¨¨B¨Q¨B hÔ/ˆKåŒ|˜K­Ô);Ñ<Ô<×CÒCÅBÄJÑOÔOÐRWÑWˆà˜!ÒÐÝÔ# D¨<¸Ð*FÈÑJÔJˆDõ ”)˜D¥#¤*¨a°¸!Ð<Ñ<Ô<ˆÝ”)˜D¥#¤*¨a°¸!Ð<Ñ<Ô<ˆð ˆX˜	Ñ!ˆØˆX˜	Ñ!ˆÝŒ\˜"ÑÔˆõ Œw�r˜Q‘w  q¡Ñ(¨2°©7Ñ2Ñ3Ô3ˆÝŒz˜$ Ñ%Ô%ˆà�4‰i˜3Ñ Ñ$ˆØ�4‰i˜3Ñ Ñ$ˆØ�4‰i˜3Ñ Ñ$ˆð �U‘
×"Ò"¥2¤8Ñ,Ô,ˆØ�U‘
×"Ò"¥2¤8Ñ,Ô,ˆØ�U‘
×"Ò"¥2¤8Ñ,Ô,ˆàÐØ 1š*ˆJØ"ˆD�ÑØ"ˆD�ÑØ"ˆD�Ñå”Y  d¨DÐ1Ñ2Ô2ˆ
ØÐr(   ç333333ã?Úbase_roughnessc                 ón  — t          j        | t           j        ¦  «                             t          j        ¦  «        dz  }t	          j        t          j        |t           j        d¬¦  «        ¦  «        }t	          j	        ||dz  z   |dz  z
  dd¦  «        }|dz                       t          j
        ¦  «        S )z®
        Generate Roughness Map (Grayscale: 0=mirror glossy, 255=matte chalk).
        Metals/blades appear smoother (darker), cloth/stone appears rougher (lighter).
        r0   r   r1   gš™™™™™é?g333333Ó?gš™™™™™¹?gffffffî?)r   r7   r8   r   r   r9   ÚabsÚ	Laplacianr<   Úclipr   )r*   rR   rA   Ú	laplacianÚ	roughnesss        r&   Úgenerate_roughness_mapz,PBRTextureSynthesizer.generate_roughness_mapo   s‘   € õ Œ|˜K­Ô);Ñ<Ô<×CÒCÅBÄJÑOÔOÐRWÑWˆå”F�3œ=¨­s¬zÀÐCÑCÔCÑDÔDˆ	Ý”G˜N¨Y¸©_Ñ<ÀÀsÁ
ÑKÈSÐRVÑWÔWˆ	Ø˜EÑ!×)Ò)­"¬(Ñ3Ô3Ð3r(   ç      è?Ú	thresholdc                 óP  — t          j        | t           j        ¦  «        }|dd…dd…df                              t          j        ¦  «        dz  }|dd…dd…df                              t          j        ¦  «        dz  }||z  |k    }t	          j        | ¦  «        }| |         ||<   |S )up   
        Extract high-intensity elemental colors for glowing effects (ChÃ¢n VÃµ aura, fire, lightning).
        Nr.   r0   r   )r   r7   ÚCOLOR_BGR2HSVr   r   r9   Ú
zeros_like)r*   r[   ÚhsvÚ	v_channelÚ	s_channelÚ	glow_maskÚemissives          r&   Úgenerate_emissive_mapz+PBRTextureSynthesizer.generate_emissive_map{   s¤   € õ
 Œl˜;­Ô(9Ñ:Ô:ˆØ˜˜˜˜1˜1˜1˜a˜”L×'Ò'­¬
Ñ3Ô3°eÑ;ˆ	Ø˜˜˜˜1˜1˜1˜a˜”L×'Ò'­¬
Ñ3Ô3°eÑ;ˆ	ð  Ñ*¨iÒ7ˆ	Ý”= Ñ-Ô-ˆØ)¨)Ô4ˆ�ÑØˆr(   Úinput_image_pathÚ
output_dirÚ	sprite_idc                 óN  — |                      dd¬¦  «         t          j        t          |¦  «        t          j        ¦  «        }|€t          d|› �¦  «        ‚t          |j        ¦  «        dk    r|j        d         dk    nd}|r!|dd…dd…dd…f         }|dd…dd…df         }n;|}t          j	        |j        d	         |j        d
         fdt          j
        ¬¦  «        }|                      ||¬¦  «        }|                      |¦  «        }	|                      |¦  «        }
t          j        ||f¦  «        }t          j        |
|f¦  «        }t          j        |	|	|	|f¦  «        }t          ||› d�z  ¦  «        t          ||› d�z  ¦  «        t          ||› d�z  ¦  «        t          ||› d�z  ¦  «        dœ}t          j        |d         |¦  «         t          j        |d         |¦  «         t          j        |d         |¦  «         t          j        |d         |¦  «         |S )zAProcess a single sprite and generate its full PBR material suite.T©ÚparentsÚexist_okNzFailed to load image: r   r.   r/   Fr   r   r3   ©Údtype)r   z_diffuse.pngz_normal.pngz_roughness.pngz_emissive.png)ÚdiffuseÚnormalrX   rc   rn   ro   rX   rc   )Úmkdirr   ÚimreadÚstrÚIMREAD_UNCHANGEDÚFileNotFoundErrorr5   r6   r   Úfullr   rP   rY   rd   r@   Úimwrite)Úclsre   rf   rg   ÚimgÚ	has_alphaÚbgrr   ro   rX   rc   Únormal_bgraÚemissive_bgraÚroughness_bgraÚpathss                  r&   Úprocess_spritez$PBRTextureSynthesizer.process_spriteŠ   s@  € ð 	×Ò °ÐÑ5Ô5Ð5ÝŒj�Ð-Ñ.Ô.µÔ0DÑEÔEˆØˆ;Ý#Ð$OÐ=MÐ$OÐ$OÑPÔPÐPõ *-¨S¬Y©¬¸1Ò)<Ð)<�C”I˜a”L AÒ%Ð%À%ˆ	Øð 	OØ�a�a�a˜˜˜˜B˜Q˜B�h”-ˆCØ˜˜˜˜1˜1˜1˜a˜”LˆEˆEàˆCÝ”G˜SœY qœ\¨3¬9°Q¬<Ð8¸#ÅRÄXÐNÑNÔNˆEð ×(Ò(¨°EÐ(Ñ:Ô:ˆØ×.Ò.¨sÑ3Ô3ˆ	Ø×,Ò,¨SÑ1Ô1ˆõ ”i ¨ Ñ0Ô0ˆÝœ	 8¨UÐ"3Ñ4Ô4ˆÝœ I¨y¸)ÀUÐ#KÑLÔLˆõ ˜:¨9Ð(BÐ(BÐ(BÑBÑCÔCÝ˜*¨)Ð'@Ð'@Ð'@Ñ@ÑAÔAÝ˜Z¨YÐ*FÐ*FÐ*FÑFÑGÔGÝ˜J¨IÐ)DÐ)DÐ)DÑDÑEÔEð	
ð 
ˆõ 	Œ�E˜)Ô$ cÑ*Ô*Ð*ÝŒ�E˜(”O [Ñ1Ô1Ð1ÝŒ�E˜+Ô&¨Ñ7Ô7Ð7ÝŒ�E˜*Ô% }Ñ5Ô5Ð5àˆr(   )r   )r)   r   N)rQ   )rZ   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ústaticmethodr   ÚndarrayÚintr'   Úfloatr   rP   rY   rd   Úclassmethodr   rr   r   r   © r(   r&   r
   r
      sŠ  € € € € € Ø]Ð]àðð  R¤Zð ¸Sð ÈÌð ð ð ñ „\ðð4 ð ØØ&*ð	3ð 3Ø”Zð3àð3ð ð3ð ˜œ
Ô#ð	3ð
 
Œð3ð 3ð 3ñ „\ð3ðj ð	4ð 	4¨B¬Jð 	4Èð 	4ÐXZÔXbð 	4ð 	4ð 	4ñ „\ð	4ð ðð ¨2¬:ð À%ð ÐSUÔS]ð ð ð ñ „\ðð ð&¨dð &Àð &ÐQTð &ÐY]Ð^aÐcfÐ^fÔYgð &ð &ð &ñ „[ð&ð &ð &r(   r
   c                   ó|   — e Zd ZdZeddeeej        f         de	de
ej        eeef         f         fd„¦   «         ZdS )	ÚSpriteAtlasPackerzMPower-of-Two MaxRects Texture Atlas Packer for Metal & WebGL batch rendering.é   Úsprite_dictÚmax_atlas_sizer   c                 ó.  — t          |                      ¦   «         d„ d¬¦  «        }|}|}t          j        ||dft          j        ¬¦  «        }i }d}d}d}	d}
|D �]8\  }}|j        dd…         \  }}t          |j        ¦  «        d	k    r|j        d         nd
}||z   |
z   |k    rd}||	|
z   z  }d}	||z   |k    rt          d|› d|› d�¦  «        ‚|dk    r|||||z   …|||z   …f<   np|d	k    r|nt          j	        |t          j
        ¦  «        }t          j        ||d
fdt          j        ¬¦  «        }t          j        ||f¦  «        ||||z   …|||z   …f<   ||||g||z  ||z  ||z   |z  ||z   |z  gdœ||<   |||
z   z  }t          |	|¦  «        }	�Œ:||fS )z’
        Packs a dictionary of sprite images into a single power-of-two texture atlas.
        Returns: (atlas_image, uv_coordinates_map)
        c                 ó(   — | d         j         d         S )Nr   r   )r6   )Úitems    r&   ú<lambda>z0SpriteAtlasPacker.pack_sprites.<locals>.<lambda>¾   s   € ÀdÈ1ÄgÄmÐTUÔFV€ r(   T)ÚkeyÚreverser/   rl   r   r.   Nr   r   zTexture Atlas size Úxz
 exceeded!r3   )Ú
pixel_rectÚuv_rect)ÚsortedÚitemsr   Úzerosr   r6   r5   ÚRuntimeErrorr   r7   ÚCOLOR_GRAY2BGRru   r@   Úmax)r�   rŽ   Úsorted_spritesÚatlas_wÚatlas_hÚatlasÚuv_mapÚcurr_xÚcurr_yÚ
row_heightÚpaddingÚnamerx   ÚhÚwÚchannelsrz   r   s                     r&   Úpack_spriteszSpriteAtlasPacker.pack_sprites·   s   € õ   × 1Ò 1Ñ 3Ô 3Ð9VÐ9VÐ`dÐeÑeÔeˆà ˆØ ˆÝ”˜' 7¨AÐ.µb´hÐ?Ñ?Ô?ˆØˆàˆØˆØˆ
Øˆà'ð !	,ñ !	,‰IˆD�#Ø”9˜R˜a˜R”=‰DˆAˆqÝ'*¨3¬9¡~¤~¸Ò':Ð':�s”y ”|�|ÀˆHà˜‰z˜GÑ# gÒ-Ð-à�Ø˜* wÑ.Ñ.�Ø�
à˜‰z˜GÒ#Ð#Ý"Ð#d¸Ð#dÐ#dÈ.Ð#dÐ#dÐ#dÑeÔeÐeð ˜1Š}ˆ}Ø>A��f˜V a™ZÐ'¨°¸±
Ð):Ð:Ñ;Ð;à%¨š]˜]�c�cµ´¸SÅ#ÔBTÑ0UÔ0U�Ýœ  A q 	¨3µb´hÐ?Ñ?Ô?�Ý>@¼iÈÈeÈÑ>UÔ>U��f˜V a™ZÐ'¨°¸±
Ð):Ð:Ñ;ð  & v¨q°!Ð4à˜WÑ$Ø˜WÑ$Ø˜a‘Z 7Ñ*Ø˜a‘Z 7Ñ*ð	ðð ˆF�4‰Lð �a˜'‘kÑ!ˆFÝ˜Z¨Ñ+Ô+ˆJ‰Jà�fˆ}Ðr(   N)rŒ   )r€   r�   r‚   rƒ   r„   r   rr   r   r…   r†   r   Údictr«   r‰   r(   r&   r‹   r‹   ´   sy   € € € € € ØWÐWàð5ð 5 $ s¨B¬J Ô"7ð 5Èð 5ÐX]Ð^`Ô^hÐjnÐorÐtxÐoxÔjyÐ^yÔXzð 5ð 5ð 5ñ „\ð5ð 5ð 5r(   r‹   c                  ó*  — t          d¦  «         t          d¦  «         t          d¦  «         t          dz  dz  dz  } |                      dd¬¦  «         d}t          j        ||d	ft          j        ¬
¦  «        }t          j        |dddd¦  «         t          j        |dddd¦  «         t          j        |dddd¦  «         | dz  }t          j	        t          |¦  «        |¦  «         t          d¦  «         t                               || d¦  «        }|                     ¦   «         D ]-\  }}t          d|                     ¦   «         d›d|› �¦  «         Œ.t          d¦  «         t          j        |d         t          j        ¦  «        t          j        |d         t          j        ¦  «        t          j        |d         t          j        ¦  «        dœ}t"                               |d ¬!¦  «        \  }}	| d"z  }
| d#z  }t          j	        t          |
¦  «        |¦  «         t'          |d$d%¬&¦  «        5 }t)          j        |	|d'¬(¦  «         d d d ¦  «         n# 1 swxY w Y   t          d)|
› �¦  «         t          d*|› �¦  «         t          d+¦  «         d,S )-NzA=================================================================z7  FreeExile PBR Texture & Normal Map Synthesizer EngineÚclientÚassetsÚgenerated_pbrTri   r4   r/   rl   )é@   r±   é0   )é-   éŒ   éð   r3   éÿÿÿÿ)é:   é
   )éF   r¹   )éÜ   rº   rº   r3   )r±   é(   é   )r   rµ   r3   r3   zdemo_martial_hero.pngz&Generating PBR maps for demo sprite...Úhero_azure_bladez  * z<10z: z2
Packing into 512x512 Power-of-Two Sprite Atlas...rn   ro   rc   )Úhero_diffuseÚhero_normalÚhero_emissivei   )rŽ   zatlas_pbr_hero.pngzatlas_pbr_hero.jsonr©   zutf-8)Úencodingr.   )Úindentz  OK Atlas image generated: z  OK UV mapping JSON saved: z.
PBR Synthesis pipeline verified successfully!r   )ÚprintÚPROJECT_ROOTrp   r   rš   r   r   ÚcircleÚ	rectanglerv   rr   r
   r   r™   Úupperrq   rs   r‹   r«   ÚopenÚjsonÚdump)Útest_outÚsizeÚdemo_spriteÚ
demo_inputÚ	pbr_pathsÚkÚvÚsprites_to_packr¡   Ú	uv_coordsÚ
atlas_pathÚ	json_pathÚfs                r&   Úmainr×   ð   sÅ  € Ý	ˆ(�O„O€OÝ	Ð
CÑDÔDÐDÝ	ˆ(�O„O€Oå˜hÑ&¨Ñ1°OÑC€HØ‡N‚N˜4¨$€NÑ/Ô/Ð/ð €DÝ”(˜D $¨˜?µ"´(Ð;Ñ;Ô;€Kå„Jˆ{˜H bÐ*=¸rÑBÔBÐBå„M�+˜x¨Ð3GÈÑLÔLÐLå„Jˆ{˜H bÐ*<¸bÑAÔAÐAàÐ3Ñ3€JÝ„K•�J‘” Ñ-Ô-Ð-å	Ð
2Ñ3Ô3Ð3Ý%×4Ò4°ZÀÐK]Ñ^Ô^€IØ—’Ñ!Ô!ð +ð +‰ˆˆ1ÝÐ)�Q—W’W‘Y”YÐ)Ð)Ð) aÐ)Ð)Ñ*Ô*Ð*Ð*õ 
Ð
?Ñ@Ô@Ð@åœ
 9¨YÔ#7½Ô9MÑNÔNÝ”z )¨HÔ"5µsÔ7KÑLÔLÝœ I¨jÔ$9½3Ô;OÑPÔPðð €Oõ
 )×5Ò5°oÐVYÐ5ÑZÔZÑ€Eˆ9ØÐ0Ñ0€JØÐ0Ñ0€IÝ„K•�J‘” Ñ'Ô'Ð'Ý	ˆi˜ wÐ	/Ñ	/Ô	/ð *°1ÝŒ	�)˜Q qÐ)Ñ)Ô)Ð)ð*ð *ð *ñ *ô *ð *ð *ð *ð *ð *ð *øøøð *ð *ð *ð *õ 
Ð
5¨Ð
5Ð
5Ñ6Ô6Ð6Ý	Ð
4¨Ð
4Ð
4Ñ5Ô5Ð5Ý	Ð
;Ñ<Ô<Ð<Øˆ1s   È1IÉIÉIÚ__main__)rƒ   ÚosÚsysrÉ   ÚmathÚpathlibr   Útypingr   r   r   r   r   Únumpyr   ÚPILr   Ú__file__ÚresolveÚparentrÄ   r
   r‹   r×   r€   Úexitr‰   r(   r&   ú<module>rä      sQ  ðð	ð 	ð 
€	€	€	Ø 
€
€
€
Ø €€€Ø €€€Ø Ð Ð Ð Ð Ð Ø .Ð .Ð .Ð .Ð .Ð .Ð .Ð .Ð .Ð .Ð .Ð .à 
€
€
€
Ø Ð Ð Ð Ø Ð Ð Ð Ð Ð àˆt�H‰~Œ~×%Ò%Ñ'Ô'Ô.Ô5Ô<€ðVð Vð Vð Vð Vñ Vô Vð Vðr9ð 9ð 9ð 9ð 9ñ 9ô 9ð 9ðx+ð +ð +ð\ ˆzÒÐØ€C„HˆTˆT‰VŒVÑÔÐÐÐð Ðr(   