2025-07-01
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
import bpy
|
||||
import bmesh
|
||||
import math
|
||||
import re
|
||||
import os
|
||||
|
||||
image_material_prefix = "TT_checker_"
|
||||
|
||||
|
||||
|
||||
def get_object_texture_image(obj):
|
||||
# Search in material & texture slots
|
||||
for slot_mat in obj.material_slots:
|
||||
if slot_mat.material:
|
||||
|
||||
# Check for traditional texture slots in material
|
||||
for slot_tex in slot_mat.material.texture_paint_slots:
|
||||
if slot_tex and slot_tex.texture and hasattr(slot_tex.texture , 'image'):
|
||||
return slot_tex.texture.image
|
||||
|
||||
# Check if material uses Nodes
|
||||
if hasattr(slot_mat.material , 'node_tree'):
|
||||
if slot_mat.material.node_tree:
|
||||
for node in slot_mat.material.node_tree.nodes:
|
||||
if type(node) is bpy.types.ShaderNodeTexImage:
|
||||
if node.image:
|
||||
return node.image
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def image_resize(image, size_x, size_y):
|
||||
if image and image.source == 'FILE' or image.source == 'GENERATED':
|
||||
image.generated_width = int(size_x)
|
||||
image.generated_height = int(size_y)
|
||||
image.scale( int(size_x), int(size_y) )
|
||||
|
||||
|
||||
|
||||
def checker_images_cleanup():
|
||||
#Unneeded materials have to be deleted before unneeded images because images have them as users
|
||||
for material in bpy.data.materials:
|
||||
if material and image_material_prefix in material.name:
|
||||
if not material.users:
|
||||
bpy.data.materials.remove(material, do_unlink=True)
|
||||
|
||||
for image in bpy.data.images:
|
||||
if image and image_material_prefix in image.name:
|
||||
if not image.users:
|
||||
bpy.data.images.remove(image, do_unlink=True)
|
||||
|
||||
|
||||
|
||||
def get_checker_name(mode, size_x, size_y):
|
||||
return (image_material_prefix+"{1}x{2}_{0}").format(mode, size_x, size_y)
|
||||
|
||||
|
||||
|
||||
def get_area_triangle_uv(A,B,C, size_x, size_y):
|
||||
scale_x = size_x / max(size_x, size_y)
|
||||
scale_y = size_y / max(size_x, size_y)
|
||||
A.x/=scale_x
|
||||
B.x/=scale_x
|
||||
C.x/=scale_x
|
||||
|
||||
A.y/=scale_y
|
||||
B.y/=scale_y
|
||||
C.y/=scale_y
|
||||
|
||||
return get_area_triangle(A,B,C)
|
||||
|
||||
|
||||
def get_area_triangle(A,B,C):
|
||||
# Heron's formula: http://www.1728.org/triang.htm
|
||||
# area = square root (s • (s - a) • (s - b) • (s - c))
|
||||
a = (B-A).length
|
||||
b = (C-B).length
|
||||
c = (A-C).length
|
||||
s = (a+b+c)/2
|
||||
|
||||
# Use abs(s-a) for values that otherwise generate negative values e.g. pinched UV verts, otherwise math domain error
|
||||
return math.sqrt(s * abs(s-a) * abs(s-b) * abs(s-c))
|
||||
|
||||
|
||||
|
||||
stored_materials = {}
|
||||
stored_material_faces = {}
|
||||
def store_materials_clear():
|
||||
stored_materials.clear()
|
||||
stored_material_faces.clear()
|
||||
|
||||
|
||||
|
||||
def store_materials(obj):
|
||||
stored_materials[obj] = []
|
||||
stored_material_faces[obj] = []
|
||||
|
||||
# Enter edit mode
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set( state = True, view_layer = None)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
|
||||
# for each slot backup the material
|
||||
for s in range(len(obj.material_slots)):
|
||||
slot = obj.material_slots[s]
|
||||
|
||||
stored_materials[obj].append(slot.material)
|
||||
stored_material_faces[obj].append( [face.index for face in bm.faces if face.material_index == s] )
|
||||
|
||||
if slot and slot.material:
|
||||
slot.material.name = "backup_"+slot.material.name
|
||||
slot.material.use_fake_user = True
|
||||
|
||||
# Back to object mode
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
|
||||
|
||||
def restore_materials(objs):
|
||||
if len(objs) == 0 :
|
||||
return
|
||||
else:
|
||||
for obj in objs :
|
||||
if stored_materials.get(obj) == None :
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set( state = True, view_layer = None)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
count = len(obj.material_slots)
|
||||
for i in range(count):
|
||||
bpy.ops.object.material_slot_remove()
|
||||
objs = [obj for obj in objs if obj in stored_materials]
|
||||
|
||||
for obj in objs :
|
||||
# Enter edit mode
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
|
||||
# Restore slots
|
||||
for index in range(len(stored_materials[obj])):
|
||||
material = stored_materials[obj][index]
|
||||
faces = stored_material_faces[obj][index]
|
||||
|
||||
if material:
|
||||
material.name = material.name.replace("backup_","")
|
||||
obj.material_slots[index].material = material
|
||||
material.use_fake_user = False
|
||||
|
||||
# Face material indexies
|
||||
for face in bm.faces:
|
||||
if face.index in faces:
|
||||
face.material_index = index
|
||||
|
||||
# Back to object mode
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
# Remove material slots if none before
|
||||
if len(stored_materials[obj]) == 0 :
|
||||
for i in range(len(obj.material_slots)):
|
||||
bpy.ops.object.material_slot_remove()
|
||||
|
||||
|
||||
|
||||
def get_tile_size(self, image, udim_tile):
|
||||
tile_name = image.name+"."+str(udim_tile)+"."+image.file_format.lower()
|
||||
purge = False
|
||||
if tile_name not in bpy.data.images:
|
||||
base_image_location = bpy.path.abspath(image.filepath)
|
||||
base_tile = re.findall('\d{4}', base_image_location)[-1]
|
||||
image_location = base_image_location.replace(base_tile, str(udim_tile))
|
||||
if not os.path.isfile(image_location):
|
||||
self.report({'INFO'}, "Missing tile image "+tile_name)
|
||||
return 0
|
||||
else:
|
||||
bpy.data.images.load(image_location, check_existing=False)
|
||||
#bpy.ops.image.open(filepath=image_location, relative_path=False, use_udim_detecting=False)
|
||||
purge = True
|
||||
|
||||
tile = bpy.data.images[tile_name]
|
||||
size = min(tile.size[0], tile.size[1])
|
||||
if purge:
|
||||
#bpy.data.batch_remove([tile])
|
||||
bpy.data.images.remove(tile, do_unlink=True)
|
||||
|
||||
return size
|
||||
Reference in New Issue
Block a user