import os, sys, time, bpy, math, requests, bmesh, json, shutil,oss2 from PIL import Image import platform,redis if platform.system() == 'Windows': sys.path.append('e:\\libs\\') #sys.path.append('libs') else: sys.path.append('/data/deploy/make3d/make2/libs/') import config, libs, libs_db,main_service_db,common,foot_mark_seam,libs_db_gpu def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False): """Returns a transformed, triangulated copy of the mesh""" assert obj.type == 'MESH' if apply_modifiers and obj.modifiers: import bpy depsgraph = bpy.context.evaluated_depsgraph_get() obj_eval = obj.evaluated_get(depsgraph) me = obj_eval.to_mesh() bm = bmesh.new() bm.from_mesh(me) obj_eval.to_mesh_clear() else: me = obj.data if obj.mode == 'EDIT': bm_orig = bmesh.from_edit_mesh(me) bm = bm_orig.copy() else: bm = bmesh.new() bm.from_mesh(me) if transform: matrix = obj.matrix_world.copy() if not matrix.is_identity: bm.transform(matrix) matrix.translation.zero() if not matrix.is_identity: bm.normal_update() if triangulate: bmesh.ops.triangulate(bm, faces=bm.faces) return bm def find_pid_objname(pid): for obj in bpy.data.objects: if obj.name.startswith(str(pid)): return obj.name def down_obj_from_oss(workdir, pid, action): if os.path.exists(os.path.join(workdir, action, pid)): print(f'目录{os.path.join(workdir, action, pid)}已存在,跳过') return else: os.makedirs(os.path.join(workdir, action, pid)) # 根据前缀获取文件列表 prefix = f'objs/print/{pid}/base/model/' filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix) print('正在下载:', prefix) obj_filename = "" for file in filelist: filename = file.key.split('/')[-1] if filename.endswith(".exr"): continue if filename.endswith("_1.jpg") or filename.endswith("_8.jpg"): continue if filename.endswith('.obj'): obj_filename = filename # print('正在下载:', file.key) localfile = os.path.join(workdir, action, pid, filename) config.oss_bucket.get_object_to_file(file.key, localfile) return obj_filename #加载obj文件 def reload_obj(pid): #下载obj文件 down_obj_from_oss(config.workdir, pid, "print") obj_filename = os.path.join(config.workdir,'print',pid,f'{pid}.obj') bpy.ops.wm.read_homefile() bpy.ops.object.delete(use_global=False, confirm=False) bpy.ops.import_scene.obj(filepath=obj_filename) bpy.context.scene.unit_settings.scale_length = 1 bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS' bpy.context.scene.unit_settings.mass_unit = 'GRAMS' obj = bpy.context.selected_objects[0] bpy.context.view_layer.objects.active = obj obj.select_set(True) def get_p3d_info(pid): url = "https://mp.api.suwa3d.com/api/customerP3dLog/info?id="+pid res = requests.get(url) res = res.json() if res["code"] == 1000: #https://www.suwa3d.com/external_order_cover/2025/01/09/1x4pzvxolk6d6xflb8p8r779e0oo2myn.png imagePath = res["data"]["texture_cover_img"] #字符串替换 imagePath = imagePath.replace("https://www.suwa3d.com/","") imagePath = imagePath.replace(".jpg",".png") return imagePath else: return 0 def base_fix(pid): # 统一blender环境 reload_obj(pid) # 统一模型方向、位置、大小... pid_objname = find_pid_objname(pid) obj = bpy.data.objects[pid_objname] bpy.data.objects[pid_objname].rotation_euler = (0, 0, 0) # 检查当前模型是否正确使用 z 轴作为高度轴 if obj.dimensions.z < obj.dimensions.y: # 如果 z 轴比 y 轴小,说明高度错误,应该旋转调整模型 print(f"警告:模型的高度轴(z)似乎不正确,使用 y 轴作为高度。正在修正模型方向...") bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) # 应用当前变换 obj.rotation_euler[0] = math.radians(90) # 旋转模型 90 度,交换 z 和 y 轴 bpy.ops.object.transform_apply(rotation=True) # 应用旋转 bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN') bpy.context.object.location[0] = 0 bpy.context.object.location[1] = 0 bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 模型基础校正完成') def export_and_update_glbs(pid): print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始导出并上传审核模型和3D相册模型glb文件...') start_time = time.time() #headcount = libs.getHeadCount(pid) headcount = 1 pid_objname = find_pid_objname(pid) obj = bpy.data.objects[pid_objname] obj.select_set(True) model_info = {} model_info['headcount'] = headcount model_info['faces'] = round(len(obj.data.polygons) / 10000) model_info['height'] = round(obj.dimensions.y * 100) # bpy.ops.wm.save_as_mainfile(filepath=os.path.join(config.workdir, pid, f'{pid}.blend')) # 统一缩放到9cm标准尺寸 scale = 90 / bpy.data.objects[pid_objname].dimensions.y bpy.data.objects[pid_objname].scale = (scale, scale, scale) bpy.ops.object.transform_apply(scale=True) # bpy.ops.wm.save_as_mainfile(filepath=os.path.join(config.workdir, pid, f'{pid}-9cm.blend')) bm = bmesh_copy_from_object(obj) model_info['volume'] = round(bm.calc_volume(), 2) model_info['weight'] = round(model_info['volume'] * 1.226, 2) print(f'{pid}的模型数据:{model_info}') res = requests.get(f'{config.urls["upload_model_info_url"]}?pid={pid}&headcount={headcount}&faces={model_info["faces"]}&volume={model_info["volume"]}&weight={model_info["weight"]}&height={model_info["height"]}') print('上传模型数据:', res.text) # with open(os.path.join(config.sharedir, 'model_info', f'{pid}.json'), 'w') as f: # json.dump(model_info, f) # f.close() # 先生成审核模型 faces_dest = 500000 * headcount # 减面 faces_current = len(bpy.data.objects[pid_objname].data.polygons) print(f'当前面数:{faces_current},目标面数:{faces_dest}') bpy.ops.object.modifier_add(type='DECIMATE') bpy.context.object.modifiers["Decimate"].ratio = faces_dest / faces_current bpy.ops.object.modifier_apply(modifier="Decimate") glb_filename = os.path.join(config.workdir, 'print',pid, f'{pid}.glb') bpy.ops.export_scene.gltf(filepath=glb_filename, export_format='GLB', export_apply=True, export_jpeg_quality=75, export_draco_mesh_compression_enable=False) # glb_filename = os.path.join(config.workdir, pid, 'output', f'{pid}.glb') # bpy.ops.export_scene.gltf(filepath=glb_filename, export_format='GLB', export_apply=True, export_jpeg_quality=75, export_draco_mesh_compression_enable=False) # 再生成数字模型 faces_dest = 120000 * headcount # 减面 faces_current = len(bpy.data.objects[pid_objname].data.polygons) print(f'当前面数:{faces_current},目标面数:{faces_dest}') bpy.ops.object.modifier_add(type='DECIMATE') bpy.context.object.modifiers["Decimate"].ratio = faces_dest / faces_current bpy.ops.object.modifier_apply(modifier="Decimate") glb_filename = os.path.join(config.workdir,'print',pid,f'{pid}-3d.glb') bpy.ops.export_scene.gltf(filepath=glb_filename, export_format='GLB', export_apply=True, export_jpeg_quality=75, export_draco_mesh_compression_enable=False) os.system(f'gltfpack -c -i {os.path.join(config.workdir, "print",pid, f"{pid}.glb")} -o {os.path.join(config.workdir, "print",pid, f"{pid}-pack.glb")}') config.oss_bucket.put_object_from_file(f'glbs/auto/{pid}.glb', os.path.join(config.workdir, 'print',pid, f'{pid}-pack.glb')) config.oss_bucket.put_object_from_file(f'glbs/print/{pid}.glb', os.path.join(config.workdir, 'print',pid, f'{pid}-pack.glb')) config.oss_bucket.put_object_from_file(f'glbs/3d/{pid}.glb', os.path.join(config.workdir,'print',pid, f'{pid}-3d.glb')) print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} glb文件导出并上传完成,共费时{libs.diff_time(start_time)}') #移除文件夹 #shutil.rmtree(os.path.join(config.workdir,'print',pid)) #更新该笔订单的状态为 3000 requests.post("https://shop.api.suwa3d.com/api/printOrder/updateExternalOrderStatus", data={'pid': pid}) def createGlb(pid): base_fix(pid) export_and_update_glbs(pid) #移除文件夹 # try: # shutil.rmtree(os.path.join(config.workdir,'print',pid)) # except: # pass def create_redis_connection(): """创建 Redis 连接,若连接失败则重试""" while True: try: r = redis.Redis(host="106.14.158.208",password="kcV2000",port=6379,db=6) # 尝试进行一次操作,检查连接是否有效 r.ping() # ping 操作是一个简单的连接测试 print("Redis连接成功!") return r except ConnectionError: print("Redis连接失败,正在重试...") time.sleep(5) if __name__ == '__main__': arrArgs = sys.argv if len(arrArgs) == 2: pid = arrArgs[1] #判断是否是数字 if pid.isdigit(): print('请输入正确的pid') createGlb(str(pid)) else: r = create_redis_connection() while True: try: if r.llen('model:3dglb') == 0: print('队列为空,等待10秒') time.sleep(10) continue info = r.lpop('model:3dglb') if info is None: print('队列为空,等待10秒') time.sleep(10) pid = info.decode('utf-8') if not pid.isdigit(): continue createGlb(pid) except Exception as e: print(f'错误:{e}') time.sleep(10) r = create_redis_connection() continue