More Fixes
- Fixes an other error in the updater due to urllib not being imported and used correctly. - Small Atlas Texturing fixes and UI Improvements.
This commit is contained in:
@@ -29,6 +29,24 @@ def register() -> None:
|
||||
description=t("MergeArmatures.selected_armature.label")
|
||||
)))
|
||||
|
||||
register_property((bpy.types.Object, "material_group_expanded", bpy.props.BoolProperty(
|
||||
name="Expand Material Group",
|
||||
description="Show/hide materials for this mesh",
|
||||
default=False
|
||||
)))
|
||||
|
||||
register_property((bpy.types.Material, "material_expanded", bpy.props.BoolProperty(
|
||||
name="Expand Material",
|
||||
description="Show/hide material properties",
|
||||
default=False
|
||||
)))
|
||||
|
||||
register_property((bpy.types.Material, "include_in_atlas", bpy.props.BoolProperty(
|
||||
name=t("TextureAtlas.include_in_atlas"),
|
||||
description=t("TextureAtlas.include_in_atlas_desc"),
|
||||
default=True
|
||||
)))
|
||||
|
||||
register_property((bpy.types.Scene, "merge_armature_apply_transforms", bpy.props.BoolProperty(
|
||||
default=False,
|
||||
name=t("MergeArmature.merge_armatures.apply_transforms.label"),
|
||||
|
||||
+4
-3
@@ -7,6 +7,7 @@ import shutil
|
||||
import pathlib
|
||||
import zipfile
|
||||
import time
|
||||
from urllib import request, error
|
||||
from threading import Thread
|
||||
from bpy.app.handlers import persistent
|
||||
from ..functions.translations import t
|
||||
@@ -140,9 +141,9 @@ def get_github_releases() -> bool:
|
||||
|
||||
try:
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
with urllib.request.urlopen(f'https://api.github.com/repos/{GITHUB_REPO}/releases') as url:
|
||||
with request.urlopen(f'https://api.github.com/repos/{GITHUB_REPO}/releases') as url:
|
||||
data = json.loads(url.read().decode())
|
||||
except urllib.error.URLError:
|
||||
except error.URLError:
|
||||
print('URL ERROR')
|
||||
return False
|
||||
|
||||
@@ -214,7 +215,7 @@ def download_file(update_url: str) -> None:
|
||||
try:
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
urllib.request.urlretrieve(update_url, update_zip_file)
|
||||
except urllib.error.URLError:
|
||||
except error.URLError:
|
||||
finish_update(error=t('download_file.cantConnect'))
|
||||
return
|
||||
|
||||
|
||||
@@ -10,6 +10,20 @@ from ..core.common import SceneMatClass, MaterialListBool
|
||||
from ..core.packer.rectangle_packer import MaterialImageList, BinPacker
|
||||
from ..functions.translations import t
|
||||
|
||||
class MaterialImageList:
|
||||
def __init__(self):
|
||||
self.albedo: Image = None
|
||||
self.normal: Image = None
|
||||
self.emission: Image = None
|
||||
self.ambient_occlusion: Image = None
|
||||
self.height: Image = None
|
||||
self.roughness: Image = None
|
||||
self.material: Material = None
|
||||
self.parent_mesh: Object = None
|
||||
self.w: int = 0
|
||||
self.h: int = 0
|
||||
self.fit = None
|
||||
|
||||
def scale_images_to_largest(images:list[Image]) -> set:
|
||||
print([image.name for image in images])
|
||||
x: int=0
|
||||
@@ -38,64 +52,67 @@ def MaterialImageList_to_Image_list(classitem: MaterialImageList) -> list[Image]
|
||||
|
||||
|
||||
def get_material_images_from_scene(context: Context) -> list[MaterialImageList]:
|
||||
mat: SceneMatClass = None
|
||||
material_image_list: list[MaterialImageList] = []
|
||||
for mat in context.scene.materials:
|
||||
new_mat_image_item: MaterialImageList = MaterialImageList()
|
||||
|
||||
for obj in context.scene.objects:
|
||||
if obj.type == 'MESH':
|
||||
for mat_slot in obj.material_slots:
|
||||
if mat_slot.material and mat_slot.material.include_in_atlas:
|
||||
new_mat_image_item = MaterialImageList()
|
||||
try:
|
||||
new_mat_image_item.albedo = bpy.data.images[mat.mat.texture_atlas_albedo]
|
||||
except Exception as e:
|
||||
name: str = mat.mat.name+"_albedo_replacement"
|
||||
new_mat_image_item.albedo = bpy.data.images[mat_slot.material.texture_atlas_albedo]
|
||||
except Exception:
|
||||
name = mat_slot.material.name + "_albedo_replacement"
|
||||
if name in bpy.data.images:
|
||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
||||
new_mat_image_item.albedo = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||
new_mat_image_item.albedo = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||
new_mat_image_item.albedo.pixels[:] = numpy.tile(numpy.array([0.0,0.0,0.0,1.0]), 32*32)
|
||||
try:
|
||||
new_mat_image_item.normal = bpy.data.images[mat.mat.texture_atlas_normal]
|
||||
new_mat_image_item.normal = bpy.data.images[mat_slot.material.texture_atlas_normal]
|
||||
except Exception:
|
||||
name: str = mat.mat.name+"_normal_replacement"
|
||||
name = mat_slot.material.name + "_normal_replacement"
|
||||
if name in bpy.data.images:
|
||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
||||
new_mat_image_item.normal = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||
new_mat_image_item.normal = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||
new_mat_image_item.normal.pixels[:] = numpy.tile(numpy.array([0.5,0.5,1.0,1.0]), 32*32)
|
||||
try:
|
||||
new_mat_image_item.emission = bpy.data.images[mat.mat.texture_atlas_emission]
|
||||
new_mat_image_item.emission = bpy.data.images[mat_slot.material.texture_atlas_emission]
|
||||
except Exception:
|
||||
name: str = mat.mat.name+"_emission_replacement"
|
||||
name = mat_slot.material.name + "_emission_replacement"
|
||||
if name in bpy.data.images:
|
||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
||||
new_mat_image_item.emission = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||
new_mat_image_item.emission = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||
new_mat_image_item.emission.pixels[:] = numpy.tile(numpy.array([0.0,0.0,0.0,1.0]), 32*32)
|
||||
|
||||
try:
|
||||
new_mat_image_item.ambient_occlusion = bpy.data.images[mat.mat.texture_atlas_ambient_occlusion]
|
||||
new_mat_image_item.ambient_occlusion = bpy.data.images[mat_slot.material.texture_atlas_ambient_occlusion]
|
||||
except Exception:
|
||||
name: str = mat.mat.name+"_ambient_occlusion_replacement"
|
||||
name = mat_slot.material.name + "_ambient_occlusion_replacement"
|
||||
if name in bpy.data.images:
|
||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
||||
new_mat_image_item.ambient_occlusion = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||
new_mat_image_item.ambient_occlusion = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||
new_mat_image_item.ambient_occlusion.pixels[:] = numpy.tile(numpy.array([1.0,1.0,1.0,1.0]), 32*32)
|
||||
try:
|
||||
new_mat_image_item.height = bpy.data.images[mat.mat.texture_atlas_height]
|
||||
new_mat_image_item.height = bpy.data.images[mat_slot.material.texture_atlas_height]
|
||||
except Exception:
|
||||
name: str = mat.mat.name+"_height_replacement"
|
||||
name = mat_slot.material.name + "_height_replacement"
|
||||
if name in bpy.data.images:
|
||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
||||
new_mat_image_item.height = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||
new_mat_image_item.height = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||
new_mat_image_item.height.pixels[:] = numpy.tile(numpy.array([0.5,0.5,0.5,1.0]), 32*32)
|
||||
|
||||
try:
|
||||
new_mat_image_item.roughness = bpy.data.images[mat.mat.texture_atlas_roughness]
|
||||
new_mat_image_item.roughness = bpy.data.images[mat_slot.material.texture_atlas_roughness]
|
||||
except Exception:
|
||||
name: str = mat.mat.name+"_roughness_replacement"
|
||||
name = mat_slot.material.name + "_roughness_replacement"
|
||||
if name in bpy.data.images:
|
||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
||||
new_mat_image_item.roughness = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||
new_mat_image_item.roughness = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||
new_mat_image_item.roughness.pixels[:] = numpy.tile(numpy.array([1.0,1.0,1.0,0.0]), 32*32)
|
||||
new_mat_image_item.material = mat.mat
|
||||
material_image_list.append(new_mat_image_item)
|
||||
return material_image_list
|
||||
|
||||
new_mat_image_item.material = mat_slot.material
|
||||
new_mat_image_item.parent_mesh = obj
|
||||
material_image_list.append(new_mat_image_item)
|
||||
|
||||
return material_image_list
|
||||
|
||||
def prep_images_in_scene(context: Context) -> list[MaterialImageList]:
|
||||
preped_images: list[MaterialImageList] = get_material_images_from_scene(context)
|
||||
@@ -124,13 +141,16 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
||||
|
||||
def execute(self, context: Context) -> set:
|
||||
try:
|
||||
mat_images: list[MaterialImageList] = prep_images_in_scene(context)
|
||||
# Only get materials marked for atlas creation
|
||||
mat_images: list[MaterialImageList] = [m for m in prep_images_in_scene(context) if m.material.include_in_atlas]
|
||||
|
||||
if not mat_images:
|
||||
self.report({'WARNING'}, t("TextureAtlas.no_materials_selected"))
|
||||
return {'CANCELLED'}
|
||||
|
||||
packer: BinPacker = BinPacker(mat_images)
|
||||
|
||||
mat_images = packer.fit()
|
||||
|
||||
|
||||
size: list[int] = [max([matimg.fit.w + matimg.albedo.size[0] for matimg in mat_images]),
|
||||
max([matimg.fit.h + matimg.albedo.size[1] for matimg in mat_images])]
|
||||
print([matimg.fit.w + matimg.albedo.size[1] for matimg in mat_images])
|
||||
@@ -144,9 +164,8 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
||||
h: int = int(mat.albedo.size[1])
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == 'MESH':
|
||||
mesh: Mesh = obj.data
|
||||
|
||||
|
||||
for layer in mesh.polygons:
|
||||
if obj.material_slots[layer.material_index].material:
|
||||
if obj.material_slots[layer.material_index].material == mat.material:
|
||||
@@ -167,7 +186,6 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
||||
|
||||
canvas: Image = bpy.data.images.new(name=new_image_name, width=int(size[0]),height=int(size[1]), alpha=True)
|
||||
c_w = canvas.size[0]
|
||||
#c_h = canvas.size[1]
|
||||
canvas_pixels: list[float] = list(canvas.pixels[:])
|
||||
for mat in mat_images:
|
||||
x: int = int(mat.fit.x)
|
||||
@@ -199,14 +217,12 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
||||
canvas.save(filepath=os.path.join(os.path.dirname(bpy.data.filepath),new_image_name+".png"))
|
||||
exec("atlased_mat."+type+" = canvas")
|
||||
|
||||
|
||||
#I am sorry for the amount of nodes I'm instanciating here and their values.
|
||||
#This is so that the nodes look pretty in the UI, which I think looks kinda nice. - @989onan
|
||||
atlased_mat.material = bpy.data.materials.new(name="Atlas_Final_"+bpy.context.scene.name+"_"+Path(bpy.data.filepath).stem)
|
||||
atlased_mat.material.use_nodes = True
|
||||
atlased_mat.material.node_tree.nodes.clear()
|
||||
|
||||
|
||||
#I am sorry for the amount of nodes I'm instanciating here and their values.
|
||||
#This is so that the nodes look pretty in the UI, which I think looks kinda nice. - @989onan
|
||||
principled_node: ShaderNodeBsdfPrincipled = atlased_mat.material.node_tree.nodes.new(type="ShaderNodeBsdfPrincipled")
|
||||
principled_node.location.x = 7.29706335067749
|
||||
principled_node.location.y = 298.918212890625
|
||||
@@ -258,11 +274,12 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
||||
atlased_mat.material.node_tree.links.new(output_node.inputs["Surface"], principled_node.outputs["BSDF"])
|
||||
atlased_mat.material.node_tree.links.new(normal_map_node.inputs["Color"], normal_node.outputs["Color"])
|
||||
|
||||
|
||||
# Only update materials for meshes that had materials included in the atlas
|
||||
for obj in context.scene.objects:
|
||||
if obj.type == 'MESH':
|
||||
if any(mat_slot.material and mat_slot.material.include_in_atlas for mat_slot in obj.material_slots):
|
||||
mesh: Mesh = obj.data
|
||||
mesh.materials.clear()
|
||||
|
||||
mesh.materials.append(atlased_mat.material)
|
||||
|
||||
self.report({'INFO'}, t("TextureAtlas.atlas_completed"))
|
||||
@@ -272,4 +289,3 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
||||
raise e
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
+22
-14
@@ -40,22 +40,25 @@ class AvatarToolKit_UL_MaterialTextureAtlasProperties(UIList):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
|
||||
def draw_item(self , context: Context, layout: UILayout, data: bpy.types.Object, item:SceneMatClass, icon, active_data, active_propname, index):
|
||||
def draw_item(self, context: Context, layout: UILayout, data: Object, item: SceneMatClass, icon, active_data, active_propname, index):
|
||||
if context.scene.texture_atlas_Has_Mat_List_Shown:
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text=item.mat.name, icon = "MATERIAL")
|
||||
col = box.row()
|
||||
|
||||
# Draw material entry
|
||||
row.prop(item.mat, "material_expanded",
|
||||
text=item.mat.name,
|
||||
icon='DOWNARROW_HLT' if item.mat.material_expanded else 'RIGHTARROW',
|
||||
emboss=False)
|
||||
row.prop(item.mat, "include_in_atlas", text="")
|
||||
|
||||
if item.mat.material_expanded and item.mat.include_in_atlas:
|
||||
col = box.column(align=True)
|
||||
col.prop(item.mat, "texture_atlas_albedo")
|
||||
col = box.row()
|
||||
col.prop(item.mat, "texture_atlas_normal")
|
||||
col = box.row()
|
||||
col.prop(item.mat, "texture_atlas_emission")
|
||||
col = box.row()
|
||||
col.prop(item.mat, "texture_atlas_ambient_occlusion")
|
||||
col = box.row()
|
||||
col.prop(item.mat, "texture_atlas_height")
|
||||
col = box.row()
|
||||
col.prop(item.mat, "texture_atlas_roughness")
|
||||
|
||||
@register_wrap
|
||||
@@ -74,7 +77,6 @@ class AvatarToolKit_PT_TextureAtlasPanel(Panel):
|
||||
|
||||
if armature:
|
||||
layout.label(text=t("TextureAtlas.label"), icon='TEXTURE')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
box = layout.box()
|
||||
@@ -86,16 +88,22 @@ class AvatarToolKit_PT_TextureAtlasPanel(Panel):
|
||||
|
||||
if context.scene.texture_atlas_Has_Mat_List_Shown:
|
||||
row = box.row()
|
||||
row.template_list(AvatarToolKit_UL_MaterialTextureAtlasProperties.bl_idname, 'material_list',
|
||||
context.scene, 'materials', context.scene, 'texture_atlas_material_index',
|
||||
rows=12, type='DEFAULT')
|
||||
row.template_list(AvatarToolKit_UL_MaterialTextureAtlasProperties.bl_idname,
|
||||
'material_list',
|
||||
context.scene,
|
||||
'materials',
|
||||
context.scene,
|
||||
'texture_atlas_material_index',
|
||||
rows=12,
|
||||
type='DEFAULT')
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
|
||||
row = layout.row()
|
||||
row.scale_y = 1.5
|
||||
row.operator(AvatarToolKit_OT_AtlasMaterials.bl_idname, text=t("TextureAtlas.atlas_materials"), icon='NODE_TEXTURE')
|
||||
|
||||
row.operator(AvatarToolKit_OT_AtlasMaterials.bl_idname,
|
||||
text=t("TextureAtlas.atlas_materials"),
|
||||
icon='NODE_TEXTURE')
|
||||
else:
|
||||
layout.label(text=t("Tools.select_armature"), icon='ERROR')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user