mirror of
https://github.com/drewcassidy/TexTools-Blender
synced 2024-09-01 14:54:44 +00:00
190 lines
5.2 KiB
Python
190 lines
5.2 KiB
Python
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
|
|
|
|
# Warning: No valid images
|
|
if len(object_images) == 0:
|
|
self.report({'ERROR_INVALID_INPUT'}, "No Texture found. Assign Checker map or texture." )
|
|
return
|
|
|
|
|
|
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
|
|
image = object_images[obj]
|
|
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)
|