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()
|