macroScript mcr_WorldScaleUVTiling category:"ColinScripts" Icon:#("UVWUnwrapTools2",5) tooltip:"World Scale UV Tiling" (

global rlt_WorldScaleUVTiling

try ( DestroyDialog rlt_WorldScaleUVTiling ) catch()

version = "1.2"
	
rollout rlt_WorldScaleUVTiling ("World Scale UV Tiling v" + version) (
    label lbl_1 "This script will scale your UVs to match their real-world size" offset:[0,10]
    spinner spn_numUnits "Scale to: " range:[0,1e9,1] type:#float fieldWidth:50 offset:[-70,10]
    dropdownList ddl_units "" items:#("in", "f", "mm", "cm", "m", "km") selection:5 offset:[0,-23] width:50 align:#right
    spinner spn_channel "UV Channel: " range:[1,20,1] type:#integer fieldWidth:50 
    button btn_scaleuvs "Scale UVs" width:200 offset:[0,10]

    fn get_average_face_area obj = (
        total_area = 0

        num_faces = polyop.getNumFaces obj
        for i = 1 to num_faces do (
            total_area += polyop.getFaceArea obj i
        )
        
        total_area / num_faces
    )

    fn get_uv_area obj = (
        select obj

        max modify mode

		ttp = Turn_to_Poly limitPolySize:true maxPolySize:3
		unwrap = Unwrap_UVW()
		addModifier obj ttp
		addModifier obj unwrap
		
        num_uv_faces = unwrap.numberPolygons()
        faces = #{1..num_uv_faces}
		
		unwrap.getArea faces &x &y &width &height &areaUVW &areaGeom

		deleteModifier obj ttp
		deleteModifier obj unwrap
		areaUVW
    )
	
    /*
     * Thanks to ZeBoxx2 (https://zeboxx2.cgsociety.org/)!
     * For this snippet contribution
     */
    fn setApplyToEntireObject unwxformmod state = (
        -- windows codes
        local WM_COMMAND = 0x111 -- Windows Message: Command
        local BN_CLICKED = 0 -- clicky the button notification
        local BM_SETCHECK = 241 -- checkbutton toggle message ID

        -- change to modify panel, open the modifier
        if (GetCommandPanelTaskMode() != #modify) do (
            SetCommandPanelTaskMode #modify
        )
        if (modPanel.getCurrentObject() != unwxformmod) do (
            modpanel.setCurrentObject unwxformmod
        )

        -- get all the children of the desktop (in case command panel is floating)
        local desktopHWND = windows.getDesktopHWND()
        -- get its children
        local desktopChildren = windows.getChildrenHWND desktopHWND
        -- let's find that Apply to Entire Object checkbox
        local ateo_checkbox
        for child in desktopChildren do ( if (child[5] == "Apply to Entire Object") do ( ateo_checkbox = child ) )

        -- not found? uh oh
        if (ateo_checkbox == undefined) do ( return undefined )

        -- otherwise, let's proceed
        local ateo_checkbox_hwnd = ateo_checkbox[1]
        local ateo_checkbox_parent = UIAccessor.getParentWindow ateo_checkbox_hwnd
        local ateo_checkbox_id = UIAccessor.GetWindowResourceID ateo_checkbox_hwnd
        windows.sendMessage ateo_checkbox_hwnd BM_SETCHECK (if (state) then ( 1 ) else ( 0 )) 0
        windows.sendMessage ateo_checkbox_parent WM_COMMAND ((bit.shift BN_CLICKED 16) + ateo_checkbox_id) ateo_checkbox_hwnd

        -- done
        OK
    )

    fn scale_uvs obj uv_area_scale_factor map_channel = (
        uvxform = UVW_Xform()
        
        addModifier obj uvxform	
        
		local factor = sqrt(uv_area_scale_factor)
		
        uvxform.U_Tile = factor
        uvxform.V_Tile = factor
        uvxform.W_Tile = factor

        uvxform.Map_Channel = map_channel

        
        setApplyToEntireObject uvxform true
    )
    
    fn get_unit_scale = (
        -- Calculate the scale to a meter from current system units
        decode_string = (spn_numUnits.value as string)
        
        unit_type = ddl_units.items[ddl_units.selection]
    
        if unit_type == "in" then (
            decode_string += "\""
        ) else if unit_type == "f" then (
            decode_string += "'"
        ) else if unit_type == "mm" then (
            decode_string += "mm"
        ) else if unit_type == "cm" then (
            decode_string += "cm"
        ) else if unit_type == "m" then (
            decode_string += "m"
        ) else if unit_type == "km" then (
            decode_string += "km"
        ) else (
            messageBox "Unit type not found"
        )
        
        units.decodeValue decode_string
    )
    
    on btn_scaleuvs pressed do (
        undo "Scale UVs" on (
            selectedObjects = GetCurrentSelection()
       
            for obj in selectedObjects do (
                if classOf(obj) == Editable_Poly or classof(obj) == PolyMeshObject then (
                    
                    geo_average_area = get_average_face_area obj
                    uv_area = get_uv_area obj
                
                    print("Geo Area: " + geo_average_area as string)
                    print("UV Area : " + uv_area as string)
					
					uv_average_area = uv_area / (polyop.getNumFaces obj)
					print("UV average Area : " + uv_average_area as string)
                
                    geo_average_area_units = geo_average_area / (get_unit_scale() * get_unit_scale())
                
                    print("Average face area geo in unit2: " + geo_average_area_units as string)
                
                    uv_area_scale_factor = geo_average_area_units / uv_average_area
                
                    print("UV Scale Factor: " + uv_area_scale_factor as string)
                
                    scale_uvs obj uv_area_scale_factor spn_channel.value
                ) else (
                    messageBox (obj.name + " is not an Editable_Poly")
                )
            )
        )
    )
)

CreateDialog rlt_WorldScaleUVTiling 350 150
)