Skip to content

Instantly share code, notes, and snippets.

@rondreas
Last active February 22, 2023 06:54
Show Gist options
  • Select an option

  • Save rondreas/0cf11e0aa42c03de30f165bcd23c7559 to your computer and use it in GitHub Desktop.

Select an option

Save rondreas/0cf11e0aa42c03de30f165bcd23c7559 to your computer and use it in GitHub Desktop.
Functions for getting and setting texel density in Maya
"""
Functions for editing the texel density
https://80.lv/articles/textel-density-tutorial/
"""
import math
import maya.cmds as mc
import pymel.core as pm
# TODO: Handle non uniform texture sizes, ie: 512x1024 etc...
# TODO: Performance, it is currently quite slow,
def get_uv_isles(mesh):
""" Get each uv isle for the given mesh as a list of mesh faces.
:param mesh: the shape we want to get uv isles from.
:type mesh: pymel.core.nodetypes.Mesh
:returns: list of pymel.core.nodetypes.MeshFace lists
"""
result = []
shell_ids, number_of_shells = mesh.getUvShellsIds()
shells = tuple([] for _ in range(number_of_shells))
# Store shells as lists of UVs for each isle
for uv, shell in enumerate(shell_ids):
shells[shell].append(uv)
# For each uv shell, convert the UVs to faces,
# and store as final result.
for shell in shells:
# Get the faces as unicode
faces = pm.polyListComponentConversion(
mesh.map[shell],
fromUV=True,
toFace=True
)
# Append the pymel object list to result.
result.append(pm.ls(faces, flatten=True))
return result
def get_texel_density(faces, size=1024):
""" For the supplied faces, calculate the texel density value
:param faces: list of faces
:type faces: pymel.core.nodetypes.MeshFace list
:param size: the size in pixels for the texture
:type size: int
:returns: texel density value as px/unit
:rtype: float
"""
ws_area = 0.0
uv_area = 0.0
for face in faces:
ws_area += face.getArea(space='world')
uv_area += face.getUVArea() # By default I hope it picks the current uv set
# Get the square root of the area
ws_area = math.sqrt(ws_area)
uv_area = math.sqrt(uv_area)
px_area = uv_area * size
return px_area / ws_area
def get_uv_boundingbox(uvs):
""" Given a set of uvs return the 2d bounding box.
:param uvs:
:returns: boundingbox min max points in uv space.
"""
u_min = float('inf')
u_max = float('-inf')
v_min = float('inf')
v_max = float('-inf')
for uv in uvs:
# Bit odd how only querying one value returns both u and v values,
u, v = pm.polyEditUV(uv, query=True, uValue=True)
if u < u_min:
u_min = u
if v < v_min:
v_min = v
if u > u_max:
u_max = u
if v > v_max:
v_max = v
return ((u_min, v_min), (u_max, v_max))
def set_texel_density(faces, density, size=1024):
"""
:param faces: list of faces, assumed to be a uv shell
:type faces: pymel.core.nodetypes.MeshFace list
:param density:
:type density: float
:param size:
:type size: int
"""
# Calculate the uv scale required to set the correct texel density
scale = density / get_texel_density(faces, size)
# For given faces, get the uvs
uvs = pm.ls(
pm.polyListComponentConversion(faces, fromFace=True, toUV=True),
flatten=True
)
# Get the bounding box for the uvs,
uv_min, uv_max = get_uv_boundingbox(uvs)
# Get the center point from where we will scale, min + half(max-min)
pivot = (uv_min[0] + 0.5*(uv_max[0] - uv_min[0]), uv_min[1] + 0.5*(uv_max[1] - uv_min[1]))
pm.polyEditUV(faces, pivotU=pivot[0], pivotV=pivot[1], scaleU=scale, scaleV=scale)
def main():
pass
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment