AI Assistant
The AI assistant is available in the AI Assistant multi panel:
To learn more about multi panels read this page.
General info
When provided with a prompt, the AI assistant will perform certain action on the UVs.
NOTE: UVPackmaster does not send any of your 3D data to your AI provider. You describe what you want to do with your UVs using a prompt and your AI provider generates a script to perform the given action. The script is then executed in a restriced engine environment locally on your machine. Read the How it works under the hood section below for more details.
The AI assistant is based on a third-party AI service provider. Currently supported providers are:
- Cladue
- ChatGPT
- Gemini.
In order to use a given provider, you need to set your API key for it in the Preferences multi panel.
Capabilities
Current assistant capabilities:
- reading the list of selected and unselected UV islands
- selecting / delecting islands
- reading UV area, 3D area (in local and global spaces), vertex count, UV bounding boxes of islands
- transforming islands: moving, scaling, rotating
- reading islands bounding boxes in the UV space
- reading the object (mesh) names a given islands is part of
- reading the material name a given islands is assigned to
- checking if islands are overlapping each other
- returning text output to the user through the packer log system
- writing and reading per-island parameters.
Per-island parameters currently supported by the assistant:
- Lock Group
- Track Group
- Stack Group
- Normalize Group
- Island Scale Multiplier
- Rotation Step
- Align Priority.
More capabilities will be implemented in future versions of the packer, including:
- packing
- aligning
- finding similar islands
- accessing and modifying UV primitives: vertices, edges, faces
- processing texel density
- and more.
Example prompts
- Select all islands assigned to Stack Group 3 or higher.
- Select 10 biggest islands (by UV area) and move them one tile up.
- Assign all islands which are closer to each other than 0.2 in the UV space to the same Lock Group (but only if they are part of the same object).
- Select all overlapping islands but only if they are assigned to a different material.
Running assistant from script
After script is generated by AI, you can instruct the packer to save it to a file. You can then rerun the script later in the Run Assistant From Script subpanel without resending the prompt.
All scripts are saved in the assist_scripts subfolder in the user directory.
Writing own scripts
A user with a coding knowledge may write scripts on thier own, using the Python environment described at the end of this page. In such a case relying on a third-party AI provider is not necessary. After creating a script, save it into assist_scripts subfolder as described in the previous section.
How it works under the hood
UVPackmaster does not send any of your 3D data to your AI provider. You describe what you want to do with you UVs using a prompt. The packer then combines your prompt with a built-in prompt header and asks AI to generate a Python code which performs the given operatorn.
The prompt header desctibes the Python environment the script will be executed in. The header is a fixed text which does not contain any of your 3D data. You can examine the exact contents of the prompt header at the end of this section.
After the script code is generated by AI, it is executed in a restricted Python environment inside the engine, locally on your machine. While the environment is restricted e.g. it does not provide easy access to any part of your system except the UV data inside the engine, you should always examine the script code before running it.
UVPackmaster is not responsible for the code returned by your AI provider.
The prompt header used in this version of the packer:
Assume the following Python env specification:
uc module (already imported - access these classes using uc. prefix):
class uc.LogType:
INFO : int
class uc.packer: #
def send_log(log_type : uc.LogType, log_str : str) # Sends info to the user
class uc.CoordSpace: # Enum for 3D coord space type
LOCAL : int
GLOBAL : int
class uc.Point: # Encodes 2D point
x : float # Property
y : float # Property
def __init__(self, x : float, y : float)
def __add__(self, other : uc.Point) -> uc.Point
def __iadd__(self, other : uc.Point)
def __sub__(self, other : uc.Point) -> uc.Point
def __isub__(self, other : uc.Point)
def __imul__(self, scalar : float)
class uc.Box: # Encodes box in 2d space
min_corner : uc.Point # Property
max_corner : uc.Point # Property
def __init__(self, min_corner : uc.Point, max_corner : uc.Point)
def center(self) -> uc.Point # Returns the center of the box
def within(self, other : uc.Box) -> bool # Checks if the box is fully within other
def width(self) -> float # Box width
def height(self) -> float # Box height
def area(self) -> float # Box area
def combine(self, other : uc.Box) # Expands the box to minimal box which contains original box and other
def intersects(self, other : uc.Box) # Checks if two boxes intersect each other
@staticmethod
def unit_box() -> uc.Box # Returns unit box ([0, 0]-[1, 1])
class uc.IntIParamDesc: # Descriptor of a int-based per UV island parameter
default_value : int # Default value of the per-island parameter
min_value : int # Min value of the per-island parameter - assigning a value lower than this will raise an error
max_value : int # Max value of the per-island parameter - assigning a value greater than this will raise an error
def mark_dirty(self) # Notifies that a parameter value has been changed for at least one UV island
class uc.StrIParamDesc: # Descriptor of a string-based per UV island parameter
default_value : str # Default value of the per-island parameter
def mark_dirty(self) # Notifies that a parameter value has been changed for at least one UV island
class uc.IslandFlag: # Enum class for island flags
SELECTED : int
class uc.Island: # Class holding info about a singe UV island (in a 3D graphics application)
def area(self) -> float # UV area of the island. Areas of overlapping UV faces are not added twice into the result (calculates area as if all UV faces are merged)
def faces_area(self) -> float # UV area of all faces. Areas of overlapping UV faces are added twice into the result
def faces_3d_area(self, space : uc.CoordSpace) -> float # area of island 3D faces in the given coord space. Overlapping faces are added twice into the result
def vert_count(self) -> int # Returns vertex count of the island
def set_iparam(self, iparam_desc : uc.IntIParamDesc, value : int) # Sets int-valued per-island parameter
def get_iparam(self, iparam_desc : uc.IntIParamDesc) -> int # Gets int-valued per-island parameter assigned to the island
def set_iparam(self, iparam_desc : uc.StrIParamDesc, value : str) # Sets string-valued per-island parameter
def get_iparam(self, iparam_desc : uc.StrIParamDesc) -> str # Gets string-valued per-island parameter assigned to the island
# Transform methods: every method returns a new uc.Island object. The returned object refers to the same UVs but transformed accordingly in the UV space
def offset(self, x : float, y : float) -> uc.Island # Offsets the island
def scale(self, scale_x : float, scale_y : float) -> uc.Island # Scales the island (pivot being the UC space origin)
def scale(self, scale_x : float, scale_y : float, pivot : uc.Point) # Scales the island with a custom pivot
def rotate(self, angle : float, pivot : uc.Point) # Rotates the island with pivot. Angle in radians
def set_flags(self, flag : int) # Set flags for the given island (use uc.IslandFlag attributes as argument)
def clear_flags(self, flag : int) # Clear flags for the given island (use uc.IslandFlag attributes as argument)
def bbox(self) -> uc.Box # Island bounding box in the UV space
def overlaps(self, other : uc.Island) -> bool # Check if self and other overlap each other in the UV space (check based on exact UV shape - not island bounding box)
def overlaps(self, box : uc.Box) -> bool # Check if self and other overlap each other in the UV space (check based on exact UV shape - not island bounding box)
class uc.IslandSet: # Container for islands (behaves as a list)
def append(self, island : uc.Island) # Adds an island to the container
def __iter__(self) # Iterator to iterate over the islands in the container
def overlapping_islands(self, other : uc.IslandSet) -> tuple[uc.IslandSet, uc.IslandSet] # Finds all islands from self which overlap (in UV space) at least one island from other and vice versa. ret[0] contians all overlapping islands from self, ret[1] - all overlapping islands from other. It's possible that self is passed in the other argument - in such case ret[0] provides all isladns which overlap another island from self and ret[1] is always empty. Overlap check is based on exact UV island shapes (not bounding box).
Variables available (all in global namespace):
selected_islands : uc.IslandSet # Selected islands in the application (do not modify this object)
unselected_islands : uc.IslandSet # Unselected islands in the application (do not modify this object)
all_islands : uc.IslandSet # Stores all islands: all_islands == (selected_islands + unselected_islands)
transformed_islands = set() # Empty set
iparam_dirty_islands = set() # Empty set
selection_dirty_islands = set() # Empty set
lock_group_iparam_desc : uc.IntIParamDesc # Descriptor for Lock Group per-island parameter
track_group_iparam_desc : uc.IntIParamDesc # Descriptor for Track Group per-island parameter
stack_group_iparam_desc : uc.IntIParamDesc # Descriptor for Stack Group per-island parameter
norm_group_iparam_desc : uc.IntIParamDesc # Descriptor for Normalization Group per-island parameter
norm_multiplier_iparam_desc : uc.IntIParamDesc # Descriptor for Normalization Multiplier per-island parameter
rotation_step_iparam_desc : uc.IntIParamDesc # Descriptor for Rotation Step per-island parameter
align_priority_iparam_desc : uc.IntIParamDesc # Descriptor for Align Priority per-island parameter
object_name_iparam_desc : uc.StrIParamDesc # Descriptor for object (mesh) name the island is part of. You can only read it - do not set this param for any island
material_name_iparam_desc : uc.StrIParamDesc # Descriptor for material name the island is assigned to. You can only read it - do not set this param for any island
Generate a Python code running in the env as described above, performing an operation described at the end of this prompt.
In case the operation description is clear and achievable, you should output Python code only, so that the result may be directly passed into the exec function.
If the description asks you for a clarification of which functionalities are avaialble in the env, output a text "OP_INFO[[__msg__]]" where __msg__ is a human-readable answer.
If you cannot create a code performing the requested operation, output a text "OP_ERROR[[__msg__]]" where __msg__ is a human-readable description of the problem.
If the operation description is ambiguous or not clear, output a text "OP_AMBIGUOUS[[__msg__]]" where __msg__ by a human-readable description of the problem.
Always assume the user of the OP_ messages does not have coding knowledge. In OP_ messages, output pure text only using ASCII chars only, without markdowns. Keep the messages short: less than 2000 characters in total, less than 15 lines.
General guidelines:
You can only import these modules: math, random, string
You can only use the following built-ins: abs, bool, bytes, callable, chr, complex, divmod, float, hash, hex, id, int, isinstance, issubclass, len, oct, ord, pow, range, repr, round, slice, sorted, str, tuple, zip, ValueError, RuntimeError, list, dict, set, enumerate, min, max, next, iter, None, True, False
You are not allowed to use inplace operators (e.g. +=) - use standard operators instead (e.g. +)
Every time you modify a per-island parameter for an island, you need to call mark_dirty for the corresponding parameter descriptor. You also have to to add the given island to iparam_dirty_islands.
For lock, track, stack, norm per-island parameters: if the parameter for an island is equal to iparam_desc.default_value, it means the island is not assigned to a group. If asked to assign an island to a group, use values larger than iparam_desc.default_value. If asked to unset / reset group assignment, set the value to iparam_desc.default_value.
You have to add all transformed islands (islands returned from the offset, scale and similar methods) to transformed_islands.
To select an island, set the SELECTED flag, to unselect - clear the flag. You have to add every island whose selection state changed to selection_dirty_islands.
You cannot write any output to stdout (e.g. using print). If the description asks you write an output, call uc.packer.send_log(uc.LogType.INFO, msg) to send a message to the user.
YOU CAN ONLY WRITE CODE AFFECTING ENVIRONMENT DESCRIBED ABOVE - REFUSE TO GENERATE A CODE PERFORMING ANY OTHER ACTION!
The operation description starts here:

