Merge pull request #34 from 989onan/Texture-Atlasing
Texture Atlasing hot fix
This commit is contained in:
+5
-4
@@ -25,13 +25,14 @@ def register():
|
|||||||
|
|
||||||
# Order the classes before registration
|
# Order the classes before registration
|
||||||
core.register.order_classes()
|
core.register.order_classes()
|
||||||
# Register the properties
|
|
||||||
core.register.register_properties()
|
|
||||||
# Register the UI classes
|
# Register the UI classes
|
||||||
for cls in __bl_ordered_classes:
|
for cls in core.register.__bl_ordered_classes:
|
||||||
print("registering" + str(cls))
|
print("registering " + str(cls))
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
#finally register properties that may use some classes.
|
||||||
|
core.register.register_properties()
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
print("Unregistering Avatar Toolkit")
|
print("Unregistering Avatar Toolkit")
|
||||||
# Unregister the UI classes
|
# Unregister the UI classes
|
||||||
|
|||||||
+51
-1
@@ -7,8 +7,58 @@ import webbrowser
|
|||||||
import typing
|
import typing
|
||||||
|
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
from bpy.types import Object, ShapeKey, Mesh, Context
|
from bpy.types import Object, ShapeKey, Mesh, Context, Material, PropertyGroup
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
from bpy.props import PointerProperty
|
||||||
|
from bpy.utils import register_class
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SceneMatClass(PropertyGroup):
|
||||||
|
mat: PointerProperty(type=Material)
|
||||||
|
|
||||||
|
register_class(SceneMatClass)
|
||||||
|
|
||||||
|
class material_list_bool:
|
||||||
|
#For the love that is holy do not ever touch these. If this was java I would make these private
|
||||||
|
#They should only be accessed via context.scene.texture_atlas_Has_Mat_List_Shown
|
||||||
|
#This is so we know if the materials are up to date. messing with these variables directly will make the thing blow up.
|
||||||
|
|
||||||
|
#The only exception to this is the ExpandSection_Materials operator which populates this with new data once the materials have changed and need reloading.
|
||||||
|
old_list: dict[str,list[Material]] = {}
|
||||||
|
bool_material_list_expand: dict[str,bool] = {}
|
||||||
|
|
||||||
|
def set_bool(self, value: bool) -> None:
|
||||||
|
material_list_bool.bool_material_list_expand[bpy.context.scene.name] = value
|
||||||
|
if value == False:
|
||||||
|
material_list_bool.old_list[bpy.context.scene.name] = []
|
||||||
|
|
||||||
|
def get_bool(self) -> bool:
|
||||||
|
newlist: list[Material] = []
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if len(obj.material_slots)>0:
|
||||||
|
for mat_slot in obj.material_slots:
|
||||||
|
if mat_slot.material:
|
||||||
|
if mat_slot.material not in newlist:
|
||||||
|
newlist.append(mat_slot.material)
|
||||||
|
|
||||||
|
still_the_same: bool = True
|
||||||
|
if bpy.context.scene.name in material_list_bool.old_list:
|
||||||
|
for item in newlist:
|
||||||
|
if item not in material_list_bool.old_list[bpy.context.scene.name]:
|
||||||
|
still_the_same = False
|
||||||
|
break
|
||||||
|
for item in material_list_bool.old_list[bpy.context.scene.name]:
|
||||||
|
if item not in newlist:
|
||||||
|
still_the_same = False
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
still_the_same = False
|
||||||
|
material_list_bool.bool_material_list_expand[bpy.context.scene.name] = still_the_same
|
||||||
|
|
||||||
|
return material_list_bool.bool_material_list_expand[bpy.context.scene.name]
|
||||||
|
|
||||||
|
|
||||||
### Clean up material names in the given mesh by removing the '.001' suffix.
|
### Clean up material names in the given mesh by removing the '.001' suffix.
|
||||||
def clean_material_names(mesh: Mesh) -> None:
|
def clean_material_names(mesh: Mesh) -> None:
|
||||||
|
|||||||
+35
-4
@@ -1,12 +1,19 @@
|
|||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
from ..functions.translations import t, get_languages_list, update_language
|
from ..functions.translations import t, get_languages_list, update_language
|
||||||
|
from ..core.register import register_property
|
||||||
|
from bpy.types import Scene, Object, Material, TextureNode, Context, SceneObjects, PropertyGroup
|
||||||
|
from bpy.props import BoolProperty, EnumProperty, FloatProperty, IntProperty, CollectionProperty, StringProperty, FloatVectorProperty, PointerProperty
|
||||||
|
from bpy.utils import register_class
|
||||||
|
from ..core.register import register_wrap
|
||||||
from ..core.addon_preferences import get_preference
|
from ..core.addon_preferences import get_preference
|
||||||
from .common import get_armatures, get_mesh_items
|
from ..core.common import SceneMatClass, material_list_bool, get_armatures, get_mesh_items
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def register() -> None:
|
def register() -> None:
|
||||||
default_language = get_preference("language", 0)
|
default_language = get_preference("language", 0)
|
||||||
|
|
||||||
bpy.types.Scene.avatar_toolkit_language = bpy.props.EnumProperty(
|
bpy.types.Scene.avatar_toolkit_language = bpy.props.EnumProperty(
|
||||||
name=t("Settings.language.label", "Language"),
|
name=t("Settings.language.label", "Language"),
|
||||||
description=t("Settings.language.desc", "Select the language for the addon"),
|
description=t("Settings.language.desc", "Select the language for the addon"),
|
||||||
@@ -49,6 +56,31 @@ def register() -> None:
|
|||||||
description="The currently selected armature for Avatar Toolkit operations"
|
description="The currently selected armature for Avatar Toolkit operations"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#happy with how compressed this get_texture_node_list method is - @989onan
|
||||||
|
def get_texture_node_list(self: Material, context: Context) -> list[set[3]]:
|
||||||
|
if self.use_nodes:
|
||||||
|
Object.Enum = [((i.image.name if i.image else i.name+"_image"),(i.image.name if i.image else "node with no image..."),(i.image.name if i.image else i.name),index+1) for index,i in enumerate(self.node_tree.nodes) if i.bl_idname == "ShaderNodeTexImage"]
|
||||||
|
if not len(Object.Enum):
|
||||||
|
Object.Enum = [("ERROR", "THIS MATERIAL HAS NO IMAGES!", "ERROR", 0)]
|
||||||
|
else:
|
||||||
|
Object.Enum = [("ERROR", "THIS MATERIAL DOES NOT USE NODES!", "ERROR", 0)]
|
||||||
|
Object.Enum.append(("None", "None", "None", 0))
|
||||||
|
return Object.Enum
|
||||||
|
|
||||||
|
register_property((Material, "texture_atlas_albedo", EnumProperty(name="Albedo", description="The texture that will be used for the albedo map atlas", default=0, items=get_texture_node_list)))
|
||||||
|
register_property((Material, "texture_atlas_normal", EnumProperty(name="Normal", description="The texture that will be used for the normal map atlas", default=0, items=get_texture_node_list)))
|
||||||
|
register_property((Material, "texture_atlas_emission", EnumProperty(name="Emission", description="The texture that will be used for the emission map atlas", default=0, items=get_texture_node_list)))
|
||||||
|
register_property((Material, "texture_atlas_ambient_occlusion", EnumProperty(name="Ambient Occlusion", description="The texture that will be used for the ambient occlusion map atlas", default=0, items=get_texture_node_list)))
|
||||||
|
register_property((Material, "texture_atlas_height", EnumProperty(name="Height", description="The texture that will be used for the height map atlas", default=0, items=get_texture_node_list)))
|
||||||
|
register_property((Material, "texture_atlas_roughness", EnumProperty(name="Roughness", description="The texture that will be used for the roughness map atlas", default=0, items=get_texture_node_list)))
|
||||||
|
|
||||||
|
register_property((Scene, "texture_atlas_material_index", IntProperty(default=-1, get=(lambda self : -1), set=(lambda self,context : None))))
|
||||||
|
|
||||||
|
register_property((Scene, "materials", CollectionProperty(type=SceneMatClass)))
|
||||||
|
|
||||||
|
register_property((Scene, "texture_atlas_Has_Mat_List_Shown", BoolProperty(default=False, get=material_list_bool.get_bool, set=material_list_bool.set_bool)))
|
||||||
|
|
||||||
|
|
||||||
def unregister() -> None:
|
def unregister() -> None:
|
||||||
if hasattr(bpy.types.Scene, "avatar_toolkit_language"):
|
if hasattr(bpy.types.Scene, "avatar_toolkit_language"):
|
||||||
del bpy.types.Scene.avatar_toolkit_language
|
del bpy.types.Scene.avatar_toolkit_language
|
||||||
@@ -70,4 +102,3 @@ def unregister() -> None:
|
|||||||
|
|
||||||
if hasattr(bpy.types.Scene, "selected_armature"):
|
if hasattr(bpy.types.Scene, "selected_armature"):
|
||||||
del bpy.types.Scene.selected_armature
|
del bpy.types.Scene.selected_armature
|
||||||
|
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
import bpy
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
from typing import List, Tuple, Optional
|
||||||
|
from mathutils import Vector
|
||||||
|
from bpy.types import Material, Operator, Context, Object, Image, Mesh, MeshUVLoopLayer, Float2AttributeValue, ShaderNodeTexImage, ShaderNodeBsdfPrincipled, ShaderNodeNormalMap
|
||||||
|
from ..core.register import register_wrap
|
||||||
|
from ..core.common import SceneMatClass, material_list_bool
|
||||||
|
from ..core.packer.rectangle_packer import MaterialImageList, BinPacker
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def scale_images_to_largest(images:list[Image]) -> set:
|
||||||
|
print([image.name for image in images])
|
||||||
|
x: int=0
|
||||||
|
y: int=0
|
||||||
|
for image in images:
|
||||||
|
x = max(x,image.size[0])
|
||||||
|
y = max(y,image.size[1])
|
||||||
|
print(x,y)
|
||||||
|
|
||||||
|
for image in images:
|
||||||
|
image.scale(width=int(x), height=int(y))
|
||||||
|
|
||||||
|
return x,y
|
||||||
|
|
||||||
|
def MaterialImageList_to_Image_list(classitem: MaterialImageList) -> list[Image]:
|
||||||
|
list_of_images: list[Image] = []
|
||||||
|
|
||||||
|
list_of_images.append(classitem.albedo)
|
||||||
|
list_of_images.append(classitem.normal)
|
||||||
|
list_of_images.append(classitem.emission)
|
||||||
|
list_of_images.append(classitem.ambient_occlusion)
|
||||||
|
list_of_images.append(classitem.height)
|
||||||
|
list_of_images.append(classitem.roughness)
|
||||||
|
|
||||||
|
return list_of_images
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
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"
|
||||||
|
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)
|
||||||
|
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]
|
||||||
|
except Exception:
|
||||||
|
name: str = mat.mat.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)
|
||||||
|
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]
|
||||||
|
except Exception:
|
||||||
|
name: str = mat.mat.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)
|
||||||
|
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]
|
||||||
|
except Exception:
|
||||||
|
name: str = mat.mat.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)
|
||||||
|
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]
|
||||||
|
except Exception:
|
||||||
|
name: str = mat.mat.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)
|
||||||
|
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]
|
||||||
|
except Exception:
|
||||||
|
name: str = mat.mat.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)
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
def prep_images_in_scene(context: Context) -> list[MaterialImageList]:
|
||||||
|
preped_images: list[MaterialImageList] = get_material_images_from_scene(context)
|
||||||
|
for MaterialImageClass in preped_images:
|
||||||
|
ImageList: list[Image] = MaterialImageList_to_Image_list(MaterialImageClass)
|
||||||
|
|
||||||
|
MaterialImageClass.w, MaterialImageClass.h = scale_images_to_largest(ImageList)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return preped_images
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@register_wrap
|
||||||
|
class Atlas_Materials(Operator):
|
||||||
|
|
||||||
|
bl_idname = "avatar_toolkit.atlas_materials"
|
||||||
|
bl_label = "Atlas Materials"
|
||||||
|
bl_description = "Atlas materials to optimize the model"
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context: Context) -> bool:
|
||||||
|
return context.scene.texture_atlas_Has_Mat_List_Shown
|
||||||
|
|
||||||
|
def execute(self, context: Context) -> set:
|
||||||
|
try:
|
||||||
|
mat_images: list[MaterialImageList] = prep_images_in_scene(context)
|
||||||
|
|
||||||
|
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])
|
||||||
|
|
||||||
|
atlased_mat: MaterialImageList = MaterialImageList()
|
||||||
|
|
||||||
|
for mat in mat_images:
|
||||||
|
x: int = int(mat.fit.x)
|
||||||
|
y: int = int(mat.fit.y)
|
||||||
|
w: int = int(mat.albedo.size[0])
|
||||||
|
h: int = int(mat.albedo.size[1])
|
||||||
|
|
||||||
|
for obj in bpy.data.objects:
|
||||||
|
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:
|
||||||
|
for loop_idx in layer.loop_indices:
|
||||||
|
layer_loops: MeshUVLoopLayer
|
||||||
|
for layer_loops in mesh.uv_layers:
|
||||||
|
uv_item: Float2AttributeValue = layer_loops.uv[loop_idx]
|
||||||
|
uv_item.vector.x = (uv_item.vector.x*(w/size[0]))+(x/size[0])
|
||||||
|
uv_item.vector.y = (uv_item.vector.y*(h/size[1]))+(y/size[1])
|
||||||
|
|
||||||
|
for type in ["albedo","normal", "emission","ambient_occlusion","height", "roughness"]:
|
||||||
|
new_image_name: str= "Atlas_"+type+"_"+context.scene.name+"_"+Path(bpy.data.filepath).stem
|
||||||
|
|
||||||
|
print("Processing "+type+" atlas image")
|
||||||
|
|
||||||
|
if new_image_name in bpy.data.images:
|
||||||
|
bpy.data.images.remove(bpy.data.images[new_image_name])
|
||||||
|
|
||||||
|
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)
|
||||||
|
y: int = int(mat.fit.y)
|
||||||
|
w: int = int(mat.albedo.size[0])
|
||||||
|
h: int = int(mat.albedo.size[1])
|
||||||
|
|
||||||
|
image_var: Image = eval("mat."+type)
|
||||||
|
|
||||||
|
image_pixels: list[float] = list(image_var.pixels[:])
|
||||||
|
|
||||||
|
print("writing image \""+image_var.name+"\" to canvas.")
|
||||||
|
print("x: \""+str(x)+"\" "+"y: \""+str(y)+"\" "+"w: \""+str(w)+"\" "+"h: \""+str(h)+"\" ")
|
||||||
|
for k in range(0,h):
|
||||||
|
for i in range(0, w):
|
||||||
|
for channel in range(0,4):
|
||||||
|
canvas_pixels[
|
||||||
|
int((((k+y)*c_w)
|
||||||
|
+
|
||||||
|
(i+x))*4)
|
||||||
|
+int(channel)
|
||||||
|
] = image_pixels[
|
||||||
|
int((
|
||||||
|
(k*w)
|
||||||
|
+i)*4)
|
||||||
|
+int(channel)]
|
||||||
|
|
||||||
|
canvas.pixels[:] = canvas_pixels[:]
|
||||||
|
canvas.save(filepath=os.path.join(os.path.dirname(bpy.data.filepath),new_image_name+".png"))
|
||||||
|
exec("atlased_mat."+type+" = canvas")
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
output_node: ShaderNodeTexImage = atlased_mat.material.node_tree.nodes.new(type="ShaderNodeOutputMaterial")
|
||||||
|
output_node.location.x = 297.29705810546875
|
||||||
|
output_node.location.y = 298.918212890625
|
||||||
|
|
||||||
|
albedo_node: ShaderNodeTexImage = atlased_mat.material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
|
albedo_node.location.x = -588.6177978515625
|
||||||
|
albedo_node.location.y = 414.1948547363281
|
||||||
|
albedo_node.image = atlased_mat.albedo
|
||||||
|
|
||||||
|
emission_node: ShaderNodeTexImage = atlased_mat.material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
|
emission_node.location.x = -588.6177978515625
|
||||||
|
emission_node.location.y = -173.9259033203125
|
||||||
|
emission_node.image = atlased_mat.emission
|
||||||
|
|
||||||
|
normal_node: ShaderNodeTexImage = atlased_mat.material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
|
normal_node.location.x = -941.4189453125
|
||||||
|
normal_node.location.y = -20.8391780853271
|
||||||
|
normal_node.image = atlased_mat.normal
|
||||||
|
|
||||||
|
normal_map_node: ShaderNodeNormalMap = atlased_mat.material.node_tree.nodes.new(type="ShaderNodeNormalMap")
|
||||||
|
normal_map_node.location.x = -545.550537109375
|
||||||
|
normal_map_node.location.y = -0.7543716430664062
|
||||||
|
|
||||||
|
roughness_node: ShaderNodeTexImage = atlased_mat.material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
|
roughness_node.location.x = -592.1703491210938
|
||||||
|
roughness_node.location.y = 206.74075317382812
|
||||||
|
roughness_node.image = atlased_mat.roughness
|
||||||
|
|
||||||
|
ambient_occlusion_node: ShaderNodeTexImage = atlased_mat.material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
|
ambient_occlusion_node.location.x = -906.4371337890625
|
||||||
|
ambient_occlusion_node.location.y = -389.9602355957031
|
||||||
|
ambient_occlusion_node.image = atlased_mat.ambient_occlusion
|
||||||
|
|
||||||
|
height_node: ShaderNodeTexImage = atlased_mat.material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
|
height_node.location.x = -1222.383056640625
|
||||||
|
height_node.location.y = -375.48406982421875
|
||||||
|
height_node.image = atlased_mat.height
|
||||||
|
|
||||||
|
atlased_mat.material.node_tree.links.new(principled_node.inputs["Base Color"], albedo_node.outputs["Color"])
|
||||||
|
atlased_mat.material.node_tree.links.new(principled_node.inputs["Metallic"], roughness_node.outputs["Alpha"])
|
||||||
|
atlased_mat.material.node_tree.links.new(principled_node.inputs["Roughness"], roughness_node.outputs["Color"])
|
||||||
|
atlased_mat.material.node_tree.links.new(principled_node.inputs["Alpha"], albedo_node.outputs["Alpha"])
|
||||||
|
atlased_mat.material.node_tree.links.new(principled_node.inputs["Normal"], normal_map_node.outputs["Normal"])
|
||||||
|
atlased_mat.material.node_tree.links.new(principled_node.inputs["Emission Color"], emission_node.outputs["Color"])
|
||||||
|
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"])
|
||||||
|
|
||||||
|
|
||||||
|
for obj in context.scene.objects:
|
||||||
|
mesh: Mesh = obj.data
|
||||||
|
mesh.materials.clear()
|
||||||
|
|
||||||
|
mesh.materials.append(atlased_mat.material)
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
from bpy.types import UIList, Panel, UILayout, Object, Context,Material, Operator
|
||||||
|
import bpy
|
||||||
|
from ..core.register import register_wrap
|
||||||
|
from .panel import AvatarToolkitPanel
|
||||||
|
from ..core.common import SceneMatClass, material_list_bool
|
||||||
|
from ..functions.atlas_materials import Atlas_Materials
|
||||||
|
|
||||||
|
|
||||||
|
@register_wrap
|
||||||
|
class ExpandSection_Materials(Operator):
|
||||||
|
bl_idname = 'avatar_toolkit.expand_section_materials'
|
||||||
|
bl_label = ""
|
||||||
|
bl_description = ""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context: Context) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self, context: Context) -> set:
|
||||||
|
|
||||||
|
if not context.scene.texture_atlas_Has_Mat_List_Shown:
|
||||||
|
context.scene.materials.clear()
|
||||||
|
newlist: list[Material] = []
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
if len(obj.material_slots)>0:
|
||||||
|
for mat_slot in obj.material_slots:
|
||||||
|
if mat_slot.material:
|
||||||
|
if mat_slot.material not in newlist:
|
||||||
|
newlist.append(mat_slot.material)
|
||||||
|
newitem: SceneMatClass = context.scene.materials.add()
|
||||||
|
newitem.mat = mat_slot.material
|
||||||
|
material_list_bool.old_list[context.scene.name] = newlist
|
||||||
|
else:
|
||||||
|
context.scene.texture_atlas_Has_Mat_List_Shown = False
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
@register_wrap
|
||||||
|
class MaterialTextureAtlasProperties(UIList):
|
||||||
|
bl_label = "Texture Atlas Material List Material"
|
||||||
|
bl_idname = "Material_UL_avatar_toolkit_texture_atlas_mat_list_mat"
|
||||||
|
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):
|
||||||
|
|
||||||
|
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()
|
||||||
|
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
|
||||||
|
class TextureAtlasPanel(Panel):
|
||||||
|
bl_label = "Texture Atlasing"
|
||||||
|
bl_idname = "OBJECT_PT_avatar_toolkit_texture_atlas"
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_region_type = 'UI'
|
||||||
|
bl_category = "Avatar Toolkit"
|
||||||
|
bl_parent_id = "OBJECT_PT_avatar_toolkit"
|
||||||
|
|
||||||
|
def draw(self, context: Context):
|
||||||
|
layout = self.layout
|
||||||
|
row = layout.row()
|
||||||
|
boxoutter = row.box()
|
||||||
|
direction_icon = 'RIGHTARROW' if not context.scene.texture_atlas_Has_Mat_List_Shown else 'DOWNARROW_HLT'
|
||||||
|
row = boxoutter.row()
|
||||||
|
row.operator(ExpandSection_Materials.bl_idname, text=("Reload Texture Atlas Material List" if not context.scene.texture_atlas_Has_Mat_List_Shown else "Loaded Texture Atlas Material List"), icon=direction_icon)
|
||||||
|
if context.scene.texture_atlas_Has_Mat_List_Shown:
|
||||||
|
|
||||||
|
#get_texture_node_list(bpy.context)
|
||||||
|
|
||||||
|
row = boxoutter.row()
|
||||||
|
row.template_list(MaterialTextureAtlasProperties.bl_idname, 'material_list', context.scene, 'materials',
|
||||||
|
context.scene, 'texture_atlas_material_index', rows=12, type='DEFAULT')
|
||||||
|
row = layout.row()
|
||||||
|
row.operator(Atlas_Materials.bl_idname, text="Atlas Materials!")
|
||||||
Reference in New Issue
Block a user