oops
took changes from another branch, this should yeet
This commit is contained in:
@@ -1,153 +0,0 @@
|
|||||||
|
|
||||||
# thank you https://stackoverflow.com/a/71432759
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
|
|
||||||
from typing import Optional
|
|
||||||
from bpy.types import Image, Material
|
|
||||||
|
|
||||||
|
|
||||||
# Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jake Gordon and contributors
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
class Rectangle_Obj:
|
|
||||||
x: int = 0
|
|
||||||
y: int = 0
|
|
||||||
w: int = 0
|
|
||||||
h: int = 0
|
|
||||||
down: Rectangle_Obj = None
|
|
||||||
used: bool = False
|
|
||||||
right: Rectangle_Obj = None
|
|
||||||
|
|
||||||
def __init__(self, x:int, y:int, w:int, h:int, down=None, used =False, right=None):
|
|
||||||
self.x = x
|
|
||||||
self.y = y
|
|
||||||
self.w = w
|
|
||||||
self.h = h
|
|
||||||
self.down = down
|
|
||||||
self.used = used
|
|
||||||
self.right = right
|
|
||||||
|
|
||||||
def split(self, w, h) -> Rectangle_Obj:
|
|
||||||
self.used = True
|
|
||||||
self.down = Rectangle_Obj(x=self.x, y=self.y + h, w=self.w, h=self.h - h)
|
|
||||||
self.right = Rectangle_Obj(x=self.x + w, y=self.y, w=self.w - w, h=h)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def find(self, w, h) -> Optional[Rectangle_Obj]:
|
|
||||||
if self.used:
|
|
||||||
return self.right.find(w, h) or self.down.find(w, h)
|
|
||||||
elif (w <= self.w) and (h <= self.h):
|
|
||||||
return self
|
|
||||||
return None
|
|
||||||
|
|
||||||
class MaterialImageList:
|
|
||||||
albedo: Image
|
|
||||||
normal: Image
|
|
||||||
emission: Image
|
|
||||||
ambient_occlusion: Image
|
|
||||||
height: Image
|
|
||||||
roughness: Image
|
|
||||||
fit: Rectangle_Obj
|
|
||||||
material: Material
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
x: int = 0
|
|
||||||
y: int = 0
|
|
||||||
w: int = 0
|
|
||||||
h: int = 0
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BinPacker(object):
|
|
||||||
root: Rectangle_Obj
|
|
||||||
bin: list[MaterialImageList] = []
|
|
||||||
def __init__(self, structure: list[MaterialImageList]):
|
|
||||||
self.root = None
|
|
||||||
self.bin = structure
|
|
||||||
|
|
||||||
def fit(self):
|
|
||||||
structure = self.bin
|
|
||||||
structure_len = len(self.bin)
|
|
||||||
w: int = 0
|
|
||||||
h: int = 0
|
|
||||||
if structure_len > 0:
|
|
||||||
w = structure[0].w
|
|
||||||
h = structure[0].h
|
|
||||||
self.root = Rectangle_Obj(x=0, y=0, w=w, h=h)
|
|
||||||
for img in structure:
|
|
||||||
w = img.w
|
|
||||||
h = img.h
|
|
||||||
node = self.root.find(w, h)
|
|
||||||
if node:
|
|
||||||
img.fit = node.split(w, h)
|
|
||||||
else:
|
|
||||||
img.fit = self.grow_node(w, h)
|
|
||||||
return structure
|
|
||||||
|
|
||||||
def grow_node(self, w, h) -> Optional[Rectangle_Obj]:
|
|
||||||
can_grow_right = (h <= self.root.h)
|
|
||||||
can_grow_down = (w <= self.root.w)
|
|
||||||
|
|
||||||
should_grow_right = can_grow_right and (self.root.h >= (self.root.w + w))
|
|
||||||
should_grow_down = can_grow_down and (self.root.w >= (self.root.h + h))
|
|
||||||
|
|
||||||
if should_grow_right:
|
|
||||||
return self.grow_right(w, h)
|
|
||||||
elif should_grow_down:
|
|
||||||
return self.grow_down(w, h)
|
|
||||||
elif can_grow_right:
|
|
||||||
return self.grow_right(w, h)
|
|
||||||
elif can_grow_down:
|
|
||||||
return self.grow_down(w, h)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def grow_right(self, w, h) -> Optional[Rectangle_Obj]:
|
|
||||||
self.root = Rectangle_Obj(
|
|
||||||
used=True,
|
|
||||||
x=0,
|
|
||||||
y=0,
|
|
||||||
w=self.root.w + w,
|
|
||||||
h=self.root.h,
|
|
||||||
down=self.root,
|
|
||||||
right=Rectangle_Obj(x=self.root.w, y=0, w=w, h=self.root.h))
|
|
||||||
node = self.root.find(w, h)
|
|
||||||
if node:
|
|
||||||
return node.split(w, h)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def grow_down(self, w, h) -> Optional[Rectangle_Obj]:
|
|
||||||
self.root = Rectangle_Obj(
|
|
||||||
used=True,
|
|
||||||
x=0,
|
|
||||||
y=0,
|
|
||||||
w=self.root.w,
|
|
||||||
h=self.root.h + h,
|
|
||||||
down=Rectangle_Obj(x=0, y=self.root.h, w=self.root.w, h=h),
|
|
||||||
right=self.root
|
|
||||||
)
|
|
||||||
node = self.root.find(w, h)
|
|
||||||
if node:
|
|
||||||
return node.split(w, h)
|
|
||||||
return None
|
|
||||||
@@ -5,6 +5,8 @@ from typing import Tuple
|
|||||||
from bpy.types import Scene, PropertyGroup, Object, Material, TextureNode, Context, SceneObjects
|
from bpy.types import Scene, PropertyGroup, Object, Material, TextureNode, Context, SceneObjects
|
||||||
from bpy.props import BoolProperty, EnumProperty, FloatProperty, IntProperty, CollectionProperty, StringProperty, FloatVectorProperty, PointerProperty
|
from bpy.props import BoolProperty, EnumProperty, FloatProperty, IntProperty, CollectionProperty, StringProperty, FloatVectorProperty, PointerProperty
|
||||||
from bpy.utils import register_class
|
from bpy.utils import register_class
|
||||||
|
from ..functions.translations import t, get_languages_list, update_language
|
||||||
|
from ..core.addon_preferences import get_preference
|
||||||
|
|
||||||
|
|
||||||
class material_list_bool:
|
class material_list_bool:
|
||||||
|
|||||||
@@ -1,274 +0,0 @@
|
|||||||
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.properties import material_list_bool, SceneMatClass
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
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"}
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import bpy
|
|
||||||
from typing import List, Optional
|
|
||||||
from bpy.types import Operator, Context, Object, TextureNode
|
|
||||||
from ..core.register import register_wrap
|
|
||||||
from ..core.common import get_armature, simplify_bonename
|
|
||||||
|
|
||||||
@register_wrap
|
|
||||||
class Atlas_Textures(Operator):
|
|
||||||
bl_idname = "avatar_toolkit.atlas_textures"
|
|
||||||
bl_label = "Atlas Textures"
|
|
||||||
bl_description = """Combines materials and their textures to optimize the model.
|
|
||||||
Although this combines materials, it may not reduce your VRAM usage. Other tools
|
|
||||||
like Tuxedo can vastly reduce your VRAM usage as well as many other optimizations,
|
|
||||||
rather than just duct taping the textures together like material combiner and this tool.
|
|
||||||
"""
|
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
|
||||||
|
|
||||||
def execute(self, context: Context) -> set:
|
|
||||||
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ def load_translations() -> None:
|
|||||||
print("Default translation file 'en_US.json' not found.")
|
print("Default translation file 'en_US.json' not found.")
|
||||||
|
|
||||||
def t(phrase: str, *args, **kwargs) -> str:
|
def t(phrase: str, *args, **kwargs) -> str:
|
||||||
output: str = dictionary.get(phrase, None)
|
output: str = dictionary.get(phrase)
|
||||||
if output is None:
|
if output is None:
|
||||||
if verbose:
|
if verbose:
|
||||||
print('Warning: Unknown phrase: ' + phrase)
|
print('Warning: Unknown phrase: ' + phrase)
|
||||||
|
|||||||
Reference in New Issue
Block a user