TexTools is a UV and Texture tool set for 3dsMax created several years ago. This open repository will port in time several of the UV tools to Blender in python. http://renderhjs.net/textools/blender
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

195 lines
6.6 KiB

import bpy
import bmesh
import operator
import math
from mathutils import Vector
from collections import defaultdict
from . import utilities_texel
from . import utilities_uv
class op(bpy.types.Operator):
bl_idname = "uv.textools_texel_density_set"
bl_label = "Set Texel size"
bl_description = "Apply texel density by scaling the UV's to match the ratio"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
if not bpy.context.active_object:
return False
if len(bpy.context.selected_objects) == 0:
return False
if bpy.context.active_object.type != 'MESH':
return False
# Only in UV editor mode
if bpy.context.area.type != 'IMAGE_EDITOR':
return False
# Requires UV map
if not bpy.context.object.data.uv_layers:
return False
# if bpy.context.object.mode == 'EDIT':
# # In edit mode requires face select mode
# if bpy.context.scene.tool_settings.mesh_select_mode[2] == False:
# return False
return True
def execute(self, context):
set_texel_density(
self,
context,
bpy.context.scene.texToolsSettings.texel_mode_scale,
bpy.context.scene.texToolsSettings.texel_density
)
return {'FINISHED'}
def set_texel_density(self, context, mode, density):
print("Set texel density!")
is_edit = bpy.context.object.mode == 'EDIT'
is_sync = bpy.context.scene.tool_settings.use_uv_select_sync
object_faces = utilities_texel.get_selected_object_faces()
# Warning: No valid input objects
if len(object_faces) == 0:
self.report({'ERROR_INVALID_INPUT'}, "No valid meshes or UV maps")
return
# Collect Images / textures
object_images = {}
for obj in object_faces:
image = utilities_texel.get_object_texture_image(obj)
if image:
object_images[obj] = image
fallback_image = None
for area in bpy.context.screen.areas:
if area.type == 'IMAGE_EDITOR':
fallback_image = area.spaces[0].image
break
for obj in object_faces:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = obj
obj.select_set(state=True, view_layer=None)
# Find image of object
if obj in object_images:
image = object_images[obj]
else:
image = fallback_image
if image:
bpy.ops.object.mode_set(mode='EDIT')
bpy.context.scene.tool_settings.use_uv_select_sync = False
# Store selection
utilities_uv.selection_store()
bm = bmesh.from_edit_mesh(obj.data)
uv_layers = bm.loops.layers.uv.verify()
# Collect groups of faces to scale together
group_faces = []
if is_edit:
# Collect selected faces as islands
bm.faces.ensure_lookup_table()
bpy.ops.uv.select_all(action='SELECT')
group_faces = utilities_uv.getSelectionIslands()
elif mode == 'ALL':
# Scale all UV's together
group_faces = [bm.faces]
elif mode == 'ISLAND':
# Scale each UV idland centered
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.uv.select_all(action='SELECT')
group_faces = utilities_uv.getSelectionIslands()
print("group_faces {}x".format(len(group_faces)))
for group in group_faces:
# Get triangle areas
sum_area_vt = 0
sum_area_uv = 0
for face in group:
# Triangle Verts
triangle_uv = [loop[uv_layers].uv for loop in face.loops]
triangle_vt = [obj.matrix_world @
vert.co for vert in face.verts]
# Triangle Areas
face_area_vt = utilities_texel.get_area_triangle(
triangle_vt[0],
triangle_vt[1],
triangle_vt[2]
)
face_area_uv = utilities_texel.get_area_triangle_uv(
triangle_uv[0],
triangle_uv[1],
triangle_uv[2],
image.size[0],
image.size[1]
)
sum_area_vt += math.sqrt(face_area_vt)
sum_area_uv += math.sqrt(face_area_uv) * \
min(image.size[0], image.size[1])
# Apply scale to group
print("scale: {:.2f} {:.2f} {:.2f} ".format(
density, sum_area_uv, sum_area_vt))
scale = 0
if density > 0 and sum_area_uv > 0 and sum_area_vt > 0:
scale = density / (sum_area_uv / sum_area_vt)
# Set Scale Origin to Island or top left
if mode == 'ISLAND':
bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
elif mode == 'ALL':
bpy.context.tool_settings.transform_pivot_point = 'CURSOR'
bpy.ops.uv.cursor_set(location=(0, 1))
# Select Face loops and scale
bpy.ops.uv.select_all(action='DESELECT')
bpy.context.scene.tool_settings.uv_select_mode = 'VERTEX'
for face in group:
for loop in face.loops:
loop[uv_layers].select = True
print("Scale: {} {}x".format(scale, len(group)))
bpy.ops.transform.resize(
value=(scale, scale, 1), use_proportional_edit=False)
# Restore selection
utilities_uv.selection_restore()
# Restore selection
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
for obj in object_faces:
obj.select_set(state=True, view_layer=None)
bpy.context.view_layer.objects.active = list(object_faces.keys())[0]
# Restore edit mode
if is_edit:
bpy.ops.object.mode_set(mode='EDIT')
# Restore sync mode
if is_sync:
bpy.context.scene.tool_settings.use_uv_select_sync = True
bpy.utils.register_class(op)