842 lines
31 KiB
Python
842 lines
31 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
SUW Core - Material Manager Module
|
||
拆分自: suw_impl.py (Line 880-1200, 6470-6950)
|
||
用途: Blender材质管理、纹理处理、材质应用
|
||
版本: 1.0.0
|
||
作者: SUWood Team
|
||
"""
|
||
|
||
from .memory_manager import memory_manager
|
||
import time
|
||
import logging
|
||
from typing import Dict, Any, Optional
|
||
|
||
# 设置日志
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 检查Blender可用性
|
||
try:
|
||
import bpy
|
||
BLENDER_AVAILABLE = True
|
||
except ImportError:
|
||
BLENDER_AVAILABLE = False
|
||
|
||
# 【新增】材质类型常量 - 按照Ruby代码定义
|
||
MAT_TYPE_NORMAL = 0
|
||
MAT_TYPE_OBVERSE = 1
|
||
MAT_TYPE_NATURE = 2
|
||
|
||
# 导入内存管理器
|
||
|
||
# ==================== 材质管理器类 ====================
|
||
|
||
|
||
class MaterialManager:
|
||
"""材质管理器 - 负责所有材质相关操作"""
|
||
|
||
def __init__(self):
|
||
"""
|
||
初始化材质管理器 - 完全独立,不依赖suw_impl
|
||
"""
|
||
self.textures = {} # 材质缓存
|
||
self.material_cache = {} # 【修复】添加缺少的材质缓存字典
|
||
self.material_stats = {
|
||
"materials_created": 0,
|
||
"textures_loaded": 0,
|
||
"creation_errors": 0
|
||
}
|
||
|
||
# 材质类型配置
|
||
self.mat_type = MAT_TYPE_NORMAL # 当前材质类型
|
||
self.back_material = True # 是否应用背面材质
|
||
|
||
logger.info("MaterialManager 初始化完成")
|
||
|
||
def init_materials(self):
|
||
"""初始化材质 - 减少注册调用"""
|
||
try:
|
||
if not BLENDER_AVAILABLE:
|
||
return
|
||
|
||
logger.debug("初始化材质...")
|
||
|
||
# 创建基础材质
|
||
materials_to_create = [
|
||
("mat_default", (0.8, 0.8, 0.8, 1.0)),
|
||
("mat_select", (1.0, 0.5, 0.0, 1.0)),
|
||
("mat_normal", (0.7, 0.7, 0.7, 1.0)),
|
||
("mat_obverse", (0.9, 0.9, 0.9, 1.0)),
|
||
("mat_reverse", (0.6, 0.6, 0.6, 1.0)),
|
||
("mat_thin", (0.5, 0.5, 0.5, 1.0)),
|
||
# 【新增】加工相关材质
|
||
("mat_machining", (0.0, 0.5, 1.0, 1.0)), # 蓝色 - 有效加工
|
||
("mat_cancelled", (0.5, 0.5, 0.5, 1.0)), # 灰色 - 取消的加工
|
||
]
|
||
|
||
for mat_name, color in materials_to_create:
|
||
if mat_name not in bpy.data.materials:
|
||
material = bpy.data.materials.new(name=mat_name)
|
||
material.use_nodes = True
|
||
|
||
# 设置基础颜色
|
||
if material.node_tree:
|
||
principled = material.node_tree.nodes.get(
|
||
"Principled BSDF")
|
||
if principled:
|
||
principled.inputs['Base Color'].default_value = color
|
||
|
||
# 只注册一次
|
||
memory_manager.register_object(material)
|
||
self.textures[mat_name] = material
|
||
else:
|
||
# 如果材质已存在,直接使用
|
||
self.textures[mat_name] = bpy.data.materials[mat_name]
|
||
|
||
logger.info("材质初始化完成")
|
||
|
||
except Exception as e:
|
||
logger.error(f"初始化材质失败: {e}")
|
||
|
||
def add_mat_rgb(self, mat_id: str, alpha: float, r: int, g: int, b: int):
|
||
"""添加RGB材质"""
|
||
try:
|
||
if not BLENDER_AVAILABLE:
|
||
return None
|
||
|
||
# 检查材质是否已存在
|
||
if mat_id in self.material_cache:
|
||
material_name = self.material_cache[mat_id]
|
||
if material_name in bpy.data.materials:
|
||
return bpy.data.materials[material_name]
|
||
|
||
# 创建新材质
|
||
material = bpy.data.materials.new(mat_id)
|
||
material.use_nodes = True
|
||
|
||
# 设置颜色
|
||
if material.node_tree:
|
||
principled = material.node_tree.nodes.get("Principled BSDF")
|
||
if principled:
|
||
color = (r/255.0, g/255.0, b/255.0, alpha)
|
||
principled.inputs[0].default_value = color
|
||
|
||
# 设置透明度
|
||
if alpha < 1.0:
|
||
material.blend_method = 'BLEND'
|
||
# Alpha input
|
||
principled.inputs[21].default_value = alpha
|
||
|
||
# 缓存材质
|
||
self.material_cache[mat_id] = material.name
|
||
self.textures[mat_id] = material
|
||
memory_manager.register_object(material)
|
||
|
||
logger.info(f"创建RGB材质: {mat_id}")
|
||
return material
|
||
|
||
except Exception as e:
|
||
logger.error(f"创建RGB材质失败: {e}")
|
||
return None
|
||
|
||
def get_texture(self, key: str):
|
||
"""获取纹理材质 - 修复版本,支持Default_前缀查找"""
|
||
if not BLENDER_AVAILABLE:
|
||
return None
|
||
|
||
try:
|
||
# 检查键是否有效
|
||
if not key:
|
||
return self.textures.get("mat_default")
|
||
|
||
# 【修复1】从缓存中获取
|
||
if key in self.textures:
|
||
material = self.textures[key]
|
||
# 验证材质是否仍然有效
|
||
if material and material.name in bpy.data.materials:
|
||
return material
|
||
else:
|
||
# 清理无效的缓存
|
||
del self.textures[key]
|
||
|
||
# 【修复2】在现有材质中查找 - 支持多种匹配方式
|
||
for material in bpy.data.materials:
|
||
material_name = material.name
|
||
|
||
# 精确匹配
|
||
if key == material_name:
|
||
self.textures[key] = material
|
||
logger.info(f"✅ 找到精确匹配材质: {key}")
|
||
return material
|
||
|
||
# 包含匹配(处理Default_前缀)
|
||
if key in material_name:
|
||
self.textures[key] = material
|
||
logger.info(f"✅ 找到包含匹配材质: {key} -> {material_name}")
|
||
return material
|
||
|
||
# Default_前缀匹配
|
||
if material_name.startswith(f"Default_{key}"):
|
||
self.textures[key] = material
|
||
logger.info(f"✅ 找到Default_前缀材质: {key} -> {material_name}")
|
||
return material
|
||
|
||
# 【修复3】如果没找到,尝试创建默认材质
|
||
logger.warning(f"未找到纹理: {key},尝试创建默认材质")
|
||
try:
|
||
# 创建默认材质
|
||
default_material = bpy.data.materials.new(
|
||
name=f"Default_{key}")
|
||
default_material.use_nodes = True
|
||
|
||
# 设置基础颜色
|
||
if default_material.node_tree:
|
||
principled = default_material.node_tree.nodes.get(
|
||
"Principled BSDF")
|
||
if principled:
|
||
# 使用灰色作为默认颜色
|
||
principled.inputs['Base Color'].default_value = (
|
||
0.7, 0.7, 0.7, 1.0)
|
||
|
||
# 缓存材质
|
||
self.textures[key] = default_material
|
||
if memory_manager:
|
||
memory_manager.register_object(default_material)
|
||
|
||
logger.info(f"✅ 创建默认材质: Default_{key}")
|
||
return default_material
|
||
|
||
except Exception as create_error:
|
||
logger.error(f"创建默认材质失败: {create_error}")
|
||
|
||
# 【修复4】返回默认材质
|
||
default_material = self.textures.get("mat_default")
|
||
if default_material and default_material.name in bpy.data.materials:
|
||
logger.warning(f"使用系统默认材质: {key}")
|
||
return default_material
|
||
|
||
logger.warning(f"未找到纹理: {key}")
|
||
return None
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取纹理失败: {e}")
|
||
return None
|
||
|
||
def apply_material_to_face(self, face, material):
|
||
"""为面应用材质"""
|
||
try:
|
||
if not face or not material or not BLENDER_AVAILABLE:
|
||
return
|
||
|
||
if hasattr(face, 'data') and face.data:
|
||
if not face.data.materials:
|
||
face.data.materials.append(material)
|
||
else:
|
||
face.data.materials[0] = material
|
||
|
||
except Exception as e:
|
||
logger.error(f"为面应用材质失败: {e}")
|
||
|
||
def create_transparent_material(self):
|
||
"""创建透明材质"""
|
||
try:
|
||
if not BLENDER_AVAILABLE:
|
||
return None
|
||
|
||
# 检查是否已存在透明材质
|
||
transparent_mat_name = "mat_transparent"
|
||
if transparent_mat_name in self.textures:
|
||
return self.textures[transparent_mat_name]
|
||
|
||
# 创建透明材质
|
||
material = bpy.data.materials.new(name=transparent_mat_name)
|
||
material.use_nodes = True
|
||
material.blend_method = 'BLEND'
|
||
|
||
# 设置透明属性
|
||
if material.node_tree:
|
||
principled = material.node_tree.nodes.get("Principled BSDF")
|
||
if principled:
|
||
# 设置基础颜色为半透明白色
|
||
principled.inputs['Base Color'].default_value = (
|
||
1.0, 1.0, 1.0, 0.5)
|
||
# 设置Alpha
|
||
principled.inputs['Alpha'].default_value = 0.5
|
||
|
||
# 缓存材质
|
||
self.textures[transparent_mat_name] = material
|
||
memory_manager.register_object(material)
|
||
|
||
logger.info("创建透明材质完成")
|
||
return material
|
||
|
||
except Exception as e:
|
||
logger.error(f"创建透明材质失败: {e}")
|
||
return None
|
||
|
||
# ==================== 【修复】添加缺少的c02方法 ====================
|
||
|
||
def c02(self, data: Dict[str, Any]):
|
||
"""add_texture - 添加纹理"""
|
||
try:
|
||
logger.info(
|
||
f"🎨 MaterialManager.c02: 处理纹理 {data.get('ckey', 'unknown')}")
|
||
|
||
if not BLENDER_AVAILABLE:
|
||
logger.warning("Blender不可用,跳过纹理创建")
|
||
return None
|
||
|
||
ckey = data.get("ckey")
|
||
if not ckey:
|
||
logger.warning("纹理键为空,跳过创建")
|
||
return None
|
||
|
||
# 检查纹理是否已存在
|
||
if ckey in self.textures:
|
||
existing_material = self.textures[ckey]
|
||
if existing_material and existing_material.name in bpy.data.materials:
|
||
logger.info(f"✅ 纹理 {ckey} 已存在")
|
||
return existing_material
|
||
else:
|
||
# 清理无效缓存
|
||
del self.textures[ckey]
|
||
|
||
# 创建新材质
|
||
material = bpy.data.materials.new(name=ckey)
|
||
material.use_nodes = True
|
||
|
||
# 获取材质节点
|
||
nodes = material.node_tree.nodes
|
||
links = material.node_tree.links
|
||
|
||
# 清理默认节点
|
||
nodes.clear()
|
||
|
||
# 创建基础节点
|
||
principled = nodes.new(type='ShaderNodeBsdfPrincipled')
|
||
principled.location = (0, 0)
|
||
|
||
output = nodes.new(type='ShaderNodeOutputMaterial')
|
||
output.location = (300, 0)
|
||
|
||
# 连接基础节点
|
||
links.new(principled.outputs['BSDF'], output.inputs['Surface'])
|
||
|
||
# 设置纹理图像
|
||
src_path = data.get("src")
|
||
if src_path:
|
||
try:
|
||
import os
|
||
if os.path.exists(src_path):
|
||
# 加载图像
|
||
image_name = os.path.basename(src_path)
|
||
image = bpy.data.images.get(image_name)
|
||
|
||
if not image:
|
||
image = bpy.data.images.load(src_path)
|
||
if memory_manager:
|
||
memory_manager.register_image(image)
|
||
|
||
# 创建纹理节点
|
||
tex_coord = nodes.new(type='ShaderNodeTexCoord')
|
||
tex_coord.location = (-600, 0)
|
||
|
||
tex_image = nodes.new(type='ShaderNodeTexImage')
|
||
tex_image.image = image
|
||
tex_image.location = (-300, 0)
|
||
|
||
# 连接节点
|
||
links.new(
|
||
tex_coord.outputs['UV'], tex_image.inputs['Vector'])
|
||
links.new(
|
||
tex_image.outputs['Color'], principled.inputs['Base Color'])
|
||
|
||
# 透明度
|
||
alpha_value = data.get("alpha", 1.0)
|
||
if alpha_value < 1.0:
|
||
links.new(
|
||
tex_image.outputs['Alpha'], tex_image.inputs['Alpha'])
|
||
material.blend_method = 'BLEND'
|
||
else:
|
||
# 文件不存在,使用纯色
|
||
principled.inputs['Base Color'].default_value = (
|
||
0.5, 0.5, 0.5, 1.0)
|
||
logger.warning(f"纹理文件不存在: {src_path}")
|
||
|
||
except Exception as img_error:
|
||
logger.error(f"加载图像失败: {img_error}")
|
||
# 红色表示错误
|
||
principled.inputs['Base Color'].default_value = (
|
||
1.0, 0.0, 0.0, 1.0)
|
||
else:
|
||
# 没有图片路径,使用RGB数据
|
||
r = data.get("r", 128) / 255.0
|
||
g = data.get("g", 128) / 255.0
|
||
b = data.get("b", 128) / 255.0
|
||
principled.inputs['Base Color'].default_value = (r, g, b, 1.0)
|
||
|
||
# 设置透明度
|
||
alpha_value = data.get("alpha", 1.0)
|
||
principled.inputs['Alpha'].default_value = alpha_value
|
||
if alpha_value < 1.0:
|
||
material.blend_method = 'BLEND'
|
||
|
||
# 设置其他属性
|
||
if "reflection" in data:
|
||
metallic_value = data["reflection"]
|
||
principled.inputs['Metallic'].default_value = metallic_value
|
||
|
||
if "reflection_glossiness" in data:
|
||
roughness_value = 1.0 - data["reflection_glossiness"]
|
||
principled.inputs['Roughness'].default_value = roughness_value
|
||
|
||
# 缓存材质
|
||
self.textures[ckey] = material
|
||
if memory_manager:
|
||
memory_manager.register_object(material)
|
||
|
||
# 更新统计
|
||
if hasattr(self, 'material_stats'):
|
||
self.material_stats["materials_created"] += 1
|
||
|
||
logger.info(f"✅ 创建纹理材质成功: {ckey}")
|
||
return material
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ MaterialManager.c02 执行失败: {e}")
|
||
self.material_stats["creation_errors"] += 1
|
||
return None
|
||
|
||
# ==================== 其他方法继续 ====================
|
||
|
||
def textured_surf(self, face, back_material, color, saved_color=None, scale_a=None, angle_a=None):
|
||
"""为表面应用纹理 - 保持原始方法名和参数"""
|
||
try:
|
||
if not face or not BLENDER_AVAILABLE:
|
||
return
|
||
|
||
# 获取材质
|
||
material = None
|
||
if color:
|
||
material = self.get_texture(color)
|
||
|
||
if not material and saved_color:
|
||
material = self.get_texture(saved_color)
|
||
|
||
if not material:
|
||
material = self.get_texture("mat_default")
|
||
|
||
# 应用材质
|
||
if material:
|
||
self.apply_material_to_face(face, material)
|
||
|
||
# 应用纹理变换
|
||
if scale_a or angle_a:
|
||
self.apply_texture_transform(face, material, scale_a, angle_a)
|
||
|
||
except Exception as e:
|
||
logger.error(f"应用表面纹理失败: {e}")
|
||
|
||
def apply_texture_transform(self, face, material, scale=None, angle=None):
|
||
"""应用纹理变换 - 保持原始方法名和参数"""
|
||
try:
|
||
if not face or not material or not BLENDER_AVAILABLE:
|
||
return
|
||
|
||
if not hasattr(face, 'data') or not face.data:
|
||
return
|
||
|
||
mesh = face.data
|
||
|
||
# 确保有UV层
|
||
if not mesh.uv_layers:
|
||
mesh.uv_layers.new(name="UVMap")
|
||
|
||
uv_layer = mesh.uv_layers.active
|
||
|
||
if uv_layer:
|
||
self.apply_uv_transform(uv_layer, scale, angle)
|
||
|
||
except Exception as e:
|
||
logger.error(f"应用纹理变换失败: {e}")
|
||
|
||
def apply_uv_transform(self, uv_layer, scale, angle):
|
||
"""应用UV变换 - 保持原始方法名和参数"""
|
||
try:
|
||
if not uv_layer:
|
||
return
|
||
|
||
import math
|
||
|
||
# 应用缩放和旋转
|
||
if scale or angle:
|
||
for loop in uv_layer.data:
|
||
u, v = loop.uv
|
||
|
||
# 应用缩放
|
||
if scale:
|
||
u *= scale
|
||
v *= scale
|
||
|
||
# 应用旋转
|
||
if angle:
|
||
angle_rad = math.radians(angle)
|
||
cos_a = math.cos(angle_rad)
|
||
sin_a = math.sin(angle_rad)
|
||
|
||
# 绕中心点旋转
|
||
u_centered = u - 0.5
|
||
v_centered = v - 0.5
|
||
|
||
u_new = u_centered * cos_a - v_centered * sin_a + 0.5
|
||
v_new = u_centered * sin_a + v_centered * cos_a + 0.5
|
||
|
||
u, v = u_new, v_new
|
||
|
||
loop.uv = (u, v)
|
||
|
||
except Exception as e:
|
||
logger.error(f"应用UV变换失败: {e}")
|
||
|
||
def rotate_texture(self, face, scale, angle):
|
||
"""旋转纹理 - 保持原始方法名和参数"""
|
||
try:
|
||
if not face or not BLENDER_AVAILABLE:
|
||
return
|
||
|
||
if not hasattr(face, 'data') or not face.data:
|
||
return
|
||
|
||
mesh = face.data
|
||
if not mesh.uv_layers:
|
||
return
|
||
|
||
uv_layer = mesh.uv_layers.active
|
||
if uv_layer:
|
||
self.apply_uv_transform(uv_layer, scale, angle)
|
||
|
||
except Exception as e:
|
||
logger.error(f"旋转纹理失败: {e}")
|
||
|
||
def set_mat_type(self, mat_type: int):
|
||
"""设置材质类型"""
|
||
self.mat_type = mat_type
|
||
logger.info(f"设置材质类型: {mat_type}")
|
||
|
||
def get_mat_type(self) -> int:
|
||
"""获取当前材质类型"""
|
||
return self.mat_type
|
||
|
||
def clear_material_cache(self):
|
||
"""清理材质缓存"""
|
||
try:
|
||
if hasattr(self, 'material_cache'):
|
||
self.material_cache.clear()
|
||
# 保留基础材质,清理其他缓存
|
||
base_materials = ["mat_default", "mat_select",
|
||
"mat_normal", "mat_obverse", "mat_reverse", "mat_thin"]
|
||
filtered_textures = {
|
||
k: v for k, v in self.textures.items() if k in base_materials}
|
||
self.textures = filtered_textures
|
||
logger.info("材质缓存清理完成")
|
||
except Exception as e:
|
||
logger.error(f"清理材质缓存失败: {e}")
|
||
|
||
# ==================== 【新增】c11和c30命令方法 ====================
|
||
|
||
def c11(self, data: Dict[str, Any]):
|
||
"""part_obverse - 设置零件正面显示 - 按照Ruby逻辑实现"""
|
||
try:
|
||
if not BLENDER_AVAILABLE:
|
||
logger.warning("Blender不可用,跳过零件正面显示设置")
|
||
return 0
|
||
|
||
uid = data.get("uid")
|
||
v = data.get("v", False)
|
||
|
||
# 【按照Ruby逻辑】设置材质类型
|
||
if v:
|
||
self.mat_type = MAT_TYPE_OBVERSE # MAT_TYPE_OBVERSE = 1
|
||
logger.info("设置材质类型为正面显示")
|
||
else:
|
||
self.mat_type = MAT_TYPE_NORMAL # MAT_TYPE_NORMAL = 0
|
||
logger.info("设置材质类型为正常显示")
|
||
|
||
# 获取零件数据
|
||
from .data_manager import get_data_manager
|
||
data_manager = get_data_manager()
|
||
parts_data = data_manager.get_parts({"uid": uid})
|
||
|
||
processed_count = 0
|
||
for root, part in parts_data.items():
|
||
if part and hasattr(part, 'data'):
|
||
try:
|
||
self._textured_part(part, False)
|
||
processed_count += 1
|
||
except Exception as e:
|
||
logger.warning(f"处理零件失败: {root}, {e}")
|
||
|
||
logger.info(f"✅ 设置零件正面显示: {processed_count}")
|
||
return processed_count
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 设置零件正面显示失败: {e}")
|
||
return 0
|
||
|
||
def c30(self, data: Dict[str, Any]):
|
||
"""part_nature - 设置零件自然显示 - 按照Ruby逻辑实现"""
|
||
try:
|
||
if not BLENDER_AVAILABLE:
|
||
logger.warning("Blender不可用,跳过零件自然显示设置")
|
||
return 0
|
||
|
||
uid = data.get("uid")
|
||
v = data.get("v", False)
|
||
|
||
# 【按照Ruby逻辑】设置材质类型
|
||
if v:
|
||
self.mat_type = MAT_TYPE_NATURE # MAT_TYPE_NATURE = 2
|
||
logger.info("设置材质类型为自然显示")
|
||
else:
|
||
self.mat_type = MAT_TYPE_NORMAL # MAT_TYPE_NORMAL = 0
|
||
logger.info("设置材质类型为正常显示")
|
||
|
||
# 获取零件数据
|
||
from .data_manager import get_data_manager
|
||
data_manager = get_data_manager()
|
||
parts_data = data_manager.get_parts({"uid": uid})
|
||
|
||
processed_count = 0
|
||
for root, part in parts_data.items():
|
||
if part and hasattr(part, 'data'):
|
||
try:
|
||
self._textured_part(part, False)
|
||
processed_count += 1
|
||
except Exception as e:
|
||
logger.warning(f"处理零件失败: {root}, {e}")
|
||
|
||
logger.info(f"✅ 设置零件自然显示: {processed_count}")
|
||
return processed_count
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 设置零件自然显示失败: {e}")
|
||
return 0
|
||
|
||
def _textured_part(self, part, selected: bool):
|
||
"""为零件应用纹理 - 按照Ruby逻辑实现"""
|
||
try:
|
||
if not part or not hasattr(part, 'data'):
|
||
return
|
||
|
||
# 【按照Ruby逻辑】处理零件的每个子对象
|
||
for child in part.children:
|
||
if not child:
|
||
continue
|
||
|
||
# 跳过非模型部件
|
||
child_type = self._get_part_attribute(child, "typ", "")
|
||
if child_type != "cp":
|
||
continue
|
||
|
||
# 跳过加工和拉手
|
||
if child_type in ["work", "pull"]:
|
||
continue
|
||
|
||
# 【按照Ruby逻辑】处理可见性
|
||
if self.mat_type == MAT_TYPE_NATURE:
|
||
# 自然模式下,模型部件隐藏,虚拟部件显示
|
||
if hasattr(child, 'type') and child.type == 'MESH':
|
||
child.hide_viewport = True
|
||
child.hide_render = True
|
||
elif self._get_part_attribute(child, "virtual", False):
|
||
child.hide_viewport = False
|
||
child.hide_render = False
|
||
else:
|
||
# 其他模式下,模型部件显示,虚拟部件隐藏
|
||
if hasattr(child, 'type') and child.type == 'MESH':
|
||
child.hide_viewport = False
|
||
child.hide_render = False
|
||
elif self._get_part_attribute(child, "virtual", False):
|
||
child.hide_viewport = True
|
||
child.hide_render = True
|
||
|
||
# 【按照Ruby逻辑】为面应用材质
|
||
self._apply_part_materials(child, selected)
|
||
|
||
except Exception as e:
|
||
logger.error(f"为零件应用纹理失败: {e}")
|
||
|
||
def _apply_part_materials(self, obj, selected: bool):
|
||
"""为对象应用材质 - 按照Ruby逻辑实现"""
|
||
try:
|
||
if not obj:
|
||
return
|
||
|
||
# 确定材质类型
|
||
material_key = None
|
||
if selected:
|
||
material_key = "mat_select"
|
||
elif self.mat_type == MAT_TYPE_NATURE:
|
||
# 自然模式下根据材质编号选择材质
|
||
mn = self._get_part_attribute(obj, "mn", 0)
|
||
if mn == 1:
|
||
material_key = "mat_obverse" # 门板
|
||
elif mn == 2:
|
||
material_key = "mat_reverse" # 柜体
|
||
elif mn == 3:
|
||
material_key = "mat_thin" # 背板
|
||
else:
|
||
# 正常模式或正面模式,使用原始材质
|
||
material_key = self._get_part_attribute(
|
||
obj, "ckey", "mat_default")
|
||
|
||
# 获取材质
|
||
material = self.get_texture(material_key)
|
||
if not material:
|
||
material = self.get_texture("mat_default")
|
||
|
||
# 应用材质到对象
|
||
if hasattr(obj, 'data') and obj.data:
|
||
if not obj.data.materials:
|
||
obj.data.materials.append(material)
|
||
else:
|
||
obj.data.materials[0] = material
|
||
|
||
except Exception as e:
|
||
logger.error(f"为对象应用材质失败: {e}")
|
||
|
||
def _get_part_attribute(self, obj, attr_name: str, default_value=None):
|
||
"""获取零件属性 - 支持多种对象类型"""
|
||
try:
|
||
if hasattr(obj, 'get'):
|
||
# 如果是字典或类似对象
|
||
return obj.get(attr_name, default_value)
|
||
elif hasattr(obj, 'sw'):
|
||
# 如果有sw属性
|
||
return obj.sw.get(attr_name, default_value)
|
||
elif isinstance(obj, dict):
|
||
# 如果是字典
|
||
return obj.get(attr_name, default_value)
|
||
else:
|
||
# 尝试从Blender对象的自定义属性获取
|
||
try:
|
||
if hasattr(obj, attr_name):
|
||
return getattr(obj, attr_name)
|
||
elif hasattr(obj, 'sw'):
|
||
return obj.sw.get(attr_name, default_value)
|
||
elif hasattr(obj, 'get'):
|
||
return obj.get(attr_name, default_value)
|
||
except:
|
||
pass
|
||
|
||
return default_value
|
||
|
||
except Exception as e:
|
||
logger.debug(f"获取零件属性失败: {e}")
|
||
return default_value
|
||
|
||
def get_material_stats(self) -> Dict[str, Any]:
|
||
"""获取材质管理器统计信息"""
|
||
try:
|
||
stats = {
|
||
"manager_type": "MaterialManager",
|
||
"cached_textures": len(self.textures),
|
||
"cached_materials": len(getattr(self, 'material_cache', {})),
|
||
"current_mat_type": self.mat_type,
|
||
"back_material": self.back_material,
|
||
"blender_available": BLENDER_AVAILABLE
|
||
}
|
||
|
||
if BLENDER_AVAILABLE:
|
||
stats["total_blender_materials"] = len(bpy.data.materials)
|
||
|
||
return stats
|
||
except Exception as e:
|
||
logger.error(f"获取材质统计失败: {e}")
|
||
return {"error": str(e)}
|
||
|
||
# 【新增】加工材质应用方法
|
||
def apply_machining_material(self, obj):
|
||
"""应用加工材质 - 蓝色,表示有效加工"""
|
||
try:
|
||
if not BLENDER_AVAILABLE or not obj:
|
||
return
|
||
|
||
material = self.get_texture("mat_machining")
|
||
if not material:
|
||
# 如果材质不存在,创建一个
|
||
material = bpy.data.materials.new(name="mat_machining")
|
||
material.use_nodes = True
|
||
if material.node_tree:
|
||
principled = material.node_tree.nodes.get(
|
||
"Principled BSDF")
|
||
if principled:
|
||
principled.inputs['Base Color'].default_value = (
|
||
0.0, 0.5, 1.0, 1.0) # 蓝色
|
||
self.textures["mat_machining"] = material
|
||
|
||
# 应用材质到对象
|
||
if hasattr(obj, 'data') and obj.data:
|
||
if not obj.data.materials:
|
||
obj.data.materials.append(material)
|
||
else:
|
||
obj.data.materials[0] = material
|
||
|
||
except Exception as e:
|
||
logger.error(f"应用加工材质失败: {e}")
|
||
|
||
def apply_cancelled_machining_material(self, obj):
|
||
"""应用取消加工材质 - 灰色,表示取消的加工"""
|
||
try:
|
||
if not BLENDER_AVAILABLE or not obj:
|
||
return
|
||
|
||
material = self.get_texture("mat_cancelled")
|
||
if not material:
|
||
# 如果材质不存在,创建一个
|
||
material = bpy.data.materials.new(name="mat_cancelled")
|
||
material.use_nodes = True
|
||
if material.node_tree:
|
||
principled = material.node_tree.nodes.get(
|
||
"Principled BSDF")
|
||
if principled:
|
||
principled.inputs['Base Color'].default_value = (
|
||
0.5, 0.5, 0.5, 1.0) # 灰色
|
||
self.textures["mat_cancelled"] = material
|
||
|
||
# 应用材质到对象
|
||
if hasattr(obj, 'data') and obj.data:
|
||
if not obj.data.materials:
|
||
obj.data.materials.append(material)
|
||
else:
|
||
obj.data.materials[0] = material
|
||
|
||
except Exception as e:
|
||
logger.error(f"应用取消加工材质失败: {e}")
|
||
|
||
# ==================== 模块实例 ====================
|
||
|
||
|
||
# 全局实例,将由SUWImpl初始化时设置
|
||
material_manager = None
|
||
|
||
|
||
def init_material_manager():
|
||
"""初始化材质管理器 - 不再需要suw_impl参数"""
|
||
global material_manager
|
||
material_manager = MaterialManager()
|
||
return material_manager
|
||
|
||
|
||
def get_material_manager():
|
||
"""获取全局材质管理器实例"""
|
||
global material_manager
|
||
if material_manager is None:
|
||
material_manager = init_material_manager()
|
||
return material_manager
|
||
|
||
|
||
# 自动初始化
|
||
material_manager = init_material_manager()
|