582 lines
21 KiB
Python
582 lines
21 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
|
|||
|
|
|||
|
# 导入内存管理器
|
|||
|
|
|||
|
# ==================== 材质管理器类 ====================
|
|||
|
|
|||
|
|
|||
|
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 = "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)),
|
|||
|
]
|
|||
|
|
|||
|
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: str):
|
|||
|
"""设置材质类型"""
|
|||
|
self.mat_type = mat_type
|
|||
|
logger.info(f"设置材质类型: {mat_type}")
|
|||
|
|
|||
|
def get_mat_type(self) -> str:
|
|||
|
"""获取当前材质类型"""
|
|||
|
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}")
|
|||
|
|
|||
|
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)}
|
|||
|
|
|||
|
# ==================== 模块实例 ====================
|
|||
|
|
|||
|
|
|||
|
# 全局实例,将由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()
|