Added back texture Atlas
- Now working with Alpha 2. - Did some changed but it should still work, did some basic testing. - Do want to make further changes and make the system better where possible.
This commit is contained in:
+45
-1
@@ -10,7 +10,7 @@ import numpy.typing as npt
|
||||
|
||||
from typing import Optional, Tuple, List, Set, Dict, Any, Generator, Callable, Union, Type
|
||||
from mathutils import Vector, Matrix
|
||||
from bpy.types import (Context, Object, Modifier, EditBone, Operator,
|
||||
from bpy.types import (Context, Object, Modifier, EditBone, Operator, Material,
|
||||
VertexGroup, ShapeKey, Bone, Mesh, Armature, PropertyGroup)
|
||||
from functools import lru_cache
|
||||
from bpy.props import PointerProperty, IntProperty, StringProperty
|
||||
@@ -19,6 +19,50 @@ from ..core.logging_setup import logger
|
||||
from ..core.translations import t
|
||||
from ..core.dictionaries import bone_names
|
||||
|
||||
class SceneMatClass(PropertyGroup):
|
||||
mat: PointerProperty(type=Material)
|
||||
|
||||
register_class(SceneMatClass)
|
||||
|
||||
class MaterialListBool:
|
||||
#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:
|
||||
MaterialListBool.bool_material_list_expand[bpy.context.scene.name] = value
|
||||
if value == False:
|
||||
MaterialListBool.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 MaterialListBool.old_list:
|
||||
for item in newlist:
|
||||
if item not in MaterialListBool.old_list[bpy.context.scene.name]:
|
||||
still_the_same = False
|
||||
break
|
||||
for item in MaterialListBool.old_list[bpy.context.scene.name]:
|
||||
if item not in newlist:
|
||||
still_the_same = False
|
||||
break
|
||||
else:
|
||||
still_the_same = False
|
||||
MaterialListBool.bool_material_list_expand[bpy.context.scene.name] = still_the_same
|
||||
|
||||
return MaterialListBool.bool_material_list_expand[bpy.context.scene.name]
|
||||
|
||||
class ProgressTracker:
|
||||
"""Universal progress tracking for Avatar Toolkit operations"""
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
# 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
|
||||
+99
-1
@@ -14,7 +14,7 @@ from .logging_setup import logger
|
||||
from .translations import t, get_languages_list, update_language
|
||||
from .addon_preferences import get_preference, save_preference
|
||||
from .updater import get_version_list
|
||||
from .common import get_armature_list, get_active_armature, get_all_meshes
|
||||
from .common import get_armature_list, get_active_armature, get_all_meshes, SceneMatClass
|
||||
from ..functions.visemes import VisemePreview
|
||||
from ..functions.eye_tracking import set_rotation
|
||||
|
||||
@@ -367,6 +367,104 @@ class AvatarToolkitSceneProperties(PropertyGroup):
|
||||
default=True
|
||||
)
|
||||
|
||||
material_search_filter: StringProperty(
|
||||
name=t("TextureAtlas.search_materials"),
|
||||
description=t("TextureAtlas.search_materials_desc"),
|
||||
default=""
|
||||
)
|
||||
|
||||
def get_texture_node_list(self: Material, context: Context) -> list[tuple]:
|
||||
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 = [(t("TextureAtlas.error.label"),
|
||||
t("TextureAtlas.no_images_error.desc"),
|
||||
t("TextureAtlas.error.label"), 0)]
|
||||
else:
|
||||
Object.Enum = [(t("TextureAtlas.error.label"),
|
||||
t("TextureAtlas.no_nodes_error.desc"),
|
||||
t("TextureAtlas.error.label"), 0)]
|
||||
Object.Enum.append((t("TextureAtlas.none.label"),
|
||||
t("TextureAtlas.none.label"),
|
||||
t("TextureAtlas.none.label"), 0))
|
||||
return Object.Enum
|
||||
|
||||
Material.texture_atlas_albedo = EnumProperty(
|
||||
name=t("TextureAtlas.albedo"),
|
||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.albedo").lower()),
|
||||
default=0,
|
||||
items=get_texture_node_list
|
||||
)
|
||||
|
||||
Material.texture_atlas_normal = EnumProperty(
|
||||
name=t("TextureAtlas.normal"),
|
||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.normal").lower()),
|
||||
default=0,
|
||||
items=get_texture_node_list
|
||||
)
|
||||
|
||||
Material.texture_atlas_emission = EnumProperty(
|
||||
name=t("TextureAtlas.emission"),
|
||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.emission").lower()),
|
||||
default=0,
|
||||
items=get_texture_node_list
|
||||
)
|
||||
|
||||
Material.texture_atlas_ambient_occlusion = EnumProperty(
|
||||
name=t("TextureAtlas.ambient_occlusion"),
|
||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.ambient_occlusion").lower()),
|
||||
default=0,
|
||||
items=get_texture_node_list
|
||||
)
|
||||
|
||||
Material.texture_atlas_height = EnumProperty(
|
||||
name=t("TextureAtlas.height"),
|
||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.height").lower()),
|
||||
default=0,
|
||||
items=get_texture_node_list
|
||||
)
|
||||
|
||||
Material.texture_atlas_roughness = EnumProperty(
|
||||
name=t("TextureAtlas.roughness"),
|
||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.roughness").lower()),
|
||||
default=0,
|
||||
items=get_texture_node_list
|
||||
)
|
||||
|
||||
Material.include_in_atlas = BoolProperty(
|
||||
name=t("TextureAtlas.include_in_atlas"),
|
||||
description=t("TextureAtlas.include_in_atlas_desc"),
|
||||
default=False
|
||||
)
|
||||
|
||||
Material.material_expanded = BoolProperty(
|
||||
name=t("TextureAtlas.material_expanded"),
|
||||
description=t("TextureAtlas.material_expanded_desc"),
|
||||
default=False
|
||||
)
|
||||
|
||||
texture_atlas_Has_Mat_List_Shown: BoolProperty(
|
||||
name=t("TextureAtlas.list_shown"),
|
||||
description=t("TextureAtlas.list_shown_desc"),
|
||||
default=False
|
||||
)
|
||||
|
||||
texture_atlas_material_index: IntProperty(
|
||||
default=-1,
|
||||
get=lambda self: -1,
|
||||
set=lambda self, context: None
|
||||
)
|
||||
|
||||
materials: CollectionProperty(
|
||||
type=SceneMatClass
|
||||
)
|
||||
|
||||
|
||||
|
||||
def register() -> None:
|
||||
"""Register the Avatar Toolkit property group"""
|
||||
logger.info("Registering Avatar Toolkit properties")
|
||||
|
||||
Reference in New Issue
Block a user