同步更改数据
This commit is contained in:
1 parent
ecb58b2f59
commit
33ba8bb357
21 files changed
+2019472
-63
No files matched your search
@@ -0,0 +1,273 @@
|
||||
import os, sys, time, argparse, requests, json, re, oss2
|
||||
import bpy, bmesh
|
||||
import open3d as o3d
|
||||
import numpy as np
|
||||
# import cupy as cp
|
||||
from mathutils import Matrix
|
||||
import math
|
||||
import logging
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
def remove_nan_verts(obj):
|
||||
'''删除模型中空坐标的顶点'''
|
||||
# obj = bpy.context.active_object
|
||||
if obj.mode == 'EDIT':
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
else:
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
|
||||
nans = [v for v in bm.verts if any(c != c for c in v.co)]
|
||||
bmesh.ops.delete(bm, geom=nans, context='VERTS')
|
||||
|
||||
degenerates = [f for f in bm.faces if len(f.verts) < 3]
|
||||
bmesh.ops.delete(bm, geom=degenerates, context='FACES')
|
||||
if obj.mode == 'EDIT':
|
||||
bmesh.update_edit_mesh(obj.data)
|
||||
else:
|
||||
bm.to_mesh(obj.data)
|
||||
obj.data.update()
|
||||
|
||||
print("NAN vertices and degenerate faces removed.")
|
||||
|
||||
def getHeadCount(pid):
|
||||
get_printinfo_url = 'https://mp.api.suwa3d.com/api/customerP3dLog/printInfo'
|
||||
res = requests.get(get_printinfo_url, params={'id': pid})
|
||||
print('get_printinfo_url:', res.url)
|
||||
print('res:', res.text)
|
||||
if res.status_code != 200:
|
||||
print('获取人数失败,程序退出')
|
||||
exit(1)
|
||||
res = json.loads(res.text)
|
||||
return res['data']['headcount']
|
||||
|
||||
def get_obj_bake_vertex_ply(obj_filepath, save_ply_path):
|
||||
start_time = time.time()
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Getting ply vertex from {obj_filepath}')
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.ops.object.select_by_type(type='MESH')
|
||||
bpy.ops.object.delete(use_global=False, confirm=False)
|
||||
bpy.ops.wm.obj_import(filepath=obj_filepath)
|
||||
|
||||
obj_original = [obj for obj in bpy.data.objects if obj.type == 'MESH'][0]
|
||||
bpy.context.view_layer.objects.active = obj_original
|
||||
obj_original.select_set(True)
|
||||
pid = obj_original.name
|
||||
|
||||
obj_num_vertices = len(obj_original.data.vertices)
|
||||
# print(f"模型 {pid} 顶点数量:", len(obj_original.data.vertices))
|
||||
|
||||
# 选择新分离出来的网格对象,并将该对象设为进活动对象
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = obj_original
|
||||
obj_original.select_set(True)
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Split foot time: ', time.time() - start_time)
|
||||
start_time = time.time()
|
||||
|
||||
# 表面细分Subdivision Surface,如果细分后定点数目大于目标数目,停止循环细分
|
||||
# target_verts = len(obj_original.data.vertices) * 8
|
||||
try:
|
||||
target_verts = getHeadCount(pid) * 2000000
|
||||
except TypeError as e:
|
||||
return 0
|
||||
|
||||
mesh_data = obj_original.data
|
||||
obj_original_name = obj_original.name
|
||||
|
||||
# Blender 网格对象的顶点数据转换为 Open3D 格式
|
||||
o3d_mesh = o3d.geometry.TriangleMesh()
|
||||
o3d_mesh.vertices = o3d.utility.Vector3dVector([v.co for v in mesh_data.vertices])
|
||||
o3d_mesh.triangles = o3d.utility.Vector3iVector([f.vertices for f in mesh_data.polygons])
|
||||
o3d_mesh.compute_vertex_normals() # 计算顶点法线
|
||||
|
||||
# open3d 细分操作
|
||||
for i in range(2, 8):
|
||||
mesh_subdivided = o3d_mesh.subdivide_loop(number_of_iterations=i)
|
||||
subdivision_vertex_count = len(mesh_subdivided.vertices)
|
||||
if subdivision_vertex_count > target_verts:
|
||||
break
|
||||
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Subdivision time: ', time.time() - start_time)
|
||||
start_time = time.time()
|
||||
|
||||
# open3d 减面操作
|
||||
# 官网文档介绍简化网格不能保证一定会达到这个数字
|
||||
target_triangles = target_verts * 2
|
||||
mesh_smp = mesh_subdivided.simplify_quadric_decimation(target_number_of_triangles=target_triangles)
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Decimation time: ', time.time() - start_time)
|
||||
start_time = time.time()
|
||||
|
||||
# 将open3d网格转换回blender网格
|
||||
o3d_to_blender_mesh = bpy.data.meshes.new(name=f"{obj_original_name}_decimation")
|
||||
o3d_to_blender_mesh.from_pydata(mesh_smp.vertices, [], mesh_smp.triangles)
|
||||
o3d_to_blender_mesh.update()
|
||||
obj_open3d = bpy.data.objects.new(f"{obj_original_name}_decimation", o3d_to_blender_mesh)
|
||||
# 设置新对象的位置和旋转信息,等于原始模型的信息
|
||||
obj_open3d.location = obj_original.location
|
||||
obj_open3d.rotation_euler = obj_original.rotation_euler
|
||||
obj_open3d.scale = obj_original.scale
|
||||
bpy.context.collection.objects.link(obj_open3d)
|
||||
# 开始将obj_mesh的贴图颜色烘焙到open3d模型上
|
||||
# 复制对象作为罩体
|
||||
obj_cage = obj_open3d.copy()
|
||||
obj_cage.data = obj_open3d.data.copy()
|
||||
bpy.context.collection.objects.link(obj_cage)
|
||||
obj_cage.name = f"{obj_original_name}_cage"
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = obj_open3d
|
||||
obj_open3d.select_set(True)
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.context.scene.tool_settings.use_uv_select_sync = True # 同步3D_View和UV编辑模式选中顶点部分
|
||||
bpy.ops.uv.smart_project()
|
||||
bpy.ops.uv.select_overlap()
|
||||
bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0)
|
||||
bpy.ops.uv.select_all(action='SELECT')
|
||||
bpy.ops.uv.average_islands_scale()
|
||||
bpy.ops.uv.pack_islands(margin=0)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
# 新建open3d模型的材质
|
||||
open3dMat = bpy.data.materials.new(name='Mat_open3d')
|
||||
obj_open3d.data.materials.append(open3dMat)
|
||||
open3dMat.use_nodes = True
|
||||
nodes = open3dMat.node_tree.nodes
|
||||
prin_bsdf_node = nodes.get("Principled BSDF")
|
||||
# 创建新的贴图对象
|
||||
tex_image_node = nodes.new("ShaderNodeTexImage")
|
||||
tex_image = bpy.data.images.new(name="o3d_teximage", width=8192, height=8192)
|
||||
tex_image_node.image = tex_image
|
||||
tex_image_node.location = (-400, 200)
|
||||
tex_image_node.select = True # 节点选中状态
|
||||
tex_image_node.select = True # 节点为活动状态
|
||||
links = open3dMat.node_tree.links
|
||||
bpy.context.object.active_material = open3dMat
|
||||
bpy.ops.object.material_slot_select()
|
||||
bpy.ops.object.material_slot_assign()
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Prepare bake data time: ', time.time() - start_time)
|
||||
start_time = time.time()
|
||||
# 烘焙操作
|
||||
bpy.context.scene.render.engine = 'CYCLES'
|
||||
bpy.context.scene.cycles.device = 'GPU'
|
||||
bpy.context.scene.cycles.preview_samples = 1
|
||||
bpy.context.scene.cycles.samples = 1
|
||||
bpy.context.scene.cycles.bake_type = 'DIFFUSE'
|
||||
bpy.context.scene.render.bake.use_pass_direct = False
|
||||
bpy.context.scene.render.bake.use_pass_indirect = False
|
||||
bpy.context.scene.render.bake.use_pass_color = True
|
||||
bpy.context.scene.render.bake.use_selected_to_active = True
|
||||
bpy.context.scene.render.bake.use_cage = True
|
||||
bpy.context.scene.render.bake.cage_object = bpy.data.objects[obj_cage.name]
|
||||
bpy.context.scene.render.bake.target = 'IMAGE_TEXTURES'
|
||||
# 添加置换修改器,不能应用
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = obj_cage
|
||||
obj_cage.select_set(True)
|
||||
bpy.ops.object.modifier_add(type='DISPLACE')
|
||||
bpy.context.object.modifiers["Displace"].mid_level = 0.995
|
||||
bpy.ops.object.modifier_apply(modifier="Displace")
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = obj_original
|
||||
obj_original.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj_open3d
|
||||
obj_open3d.select_set(True)
|
||||
bpy.ops.object.bake(type='DIFFUSE') # 开始 Bake
|
||||
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Bake DIFFUSE time: ', time.time() - start_time)
|
||||
start_time = time.time()
|
||||
|
||||
links.new(tex_image_node.outputs[0], prin_bsdf_node.inputs[0])
|
||||
|
||||
bpy.data.objects.remove(obj_original, do_unlink=True)
|
||||
bpy.data.objects.remove(obj_cage, do_unlink=True)
|
||||
|
||||
# 添加颜色属性
|
||||
bpy.ops.geometry.color_attribute_add()
|
||||
# 节点的创建:
|
||||
# nodes = bpy.context.active_object.active_material.node_tree.nodes
|
||||
# links = bpy.context.active_object.active_material.node_tree.links
|
||||
tex_coord = nodes.new("ShaderNodeTexCoord")
|
||||
Mapping = nodes.new('ShaderNodeMapping')
|
||||
vertex_color = nodes.new("ShaderNodeVertexColor")
|
||||
tex_coord.location = (-800, 600)
|
||||
Mapping.location = (-500, 200)
|
||||
vertex_color.location = (-200, 800)
|
||||
|
||||
# 节点之间的连接:
|
||||
# 纹理坐标的UV 连接 映射的矢量;映射的矢量 连接 纹理贴图的矢量;原理化BSDF删除;纹理贴图的矢量 连接 表(曲)面。
|
||||
links.new(tex_coord.outputs[2], nodes['Mapping'].inputs[0])
|
||||
links.new(Mapping.outputs[0], nodes['Image Texture'].inputs[0])
|
||||
principled_bsdf = nodes.get("Principled BSDF")
|
||||
nodes.remove(principled_bsdf)
|
||||
links.new(nodes['Image Texture'].outputs[0], nodes['Material Output'].inputs[0])
|
||||
|
||||
# 4.渲染烘焙相关操作
|
||||
bpy.context.scene.render.engine = 'CYCLES'
|
||||
bpy.context.scene.cycles.device = 'GPU'
|
||||
bpy.context.scene.cycles.preview_samples = 1
|
||||
bpy.context.scene.cycles.samples = 1
|
||||
bpy.context.scene.cycles.bake_type = 'EMIT'
|
||||
bpy.context.scene.render.bake.use_selected_to_active = False
|
||||
bpy.context.scene.render.bake.target = 'VERTEX_COLORS'
|
||||
bpy.ops.object.bake(type='EMIT') # 开始 Bake
|
||||
|
||||
# 连接节点 Color Attribute 和 节点 Material Output
|
||||
links.new(nodes['Color Attribute'].outputs[0], nodes['Material Output'].inputs[0])
|
||||
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Bake EMIT time: ', time.time() - start_time)
|
||||
start_time = time.time()
|
||||
|
||||
# 清除顶点为nan的无效数据
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = obj_open3d
|
||||
obj_open3d.select_set(True)
|
||||
remove_nan_verts(obj_open3d)
|
||||
|
||||
# 5.输出模型
|
||||
# bpy.ops.wm.save_as_mainfile(filepath='/data/datasets/plys/121614.bend')
|
||||
bpy.ops.wm.ply_export(filepath=save_ply_path, apply_modifiers=True, export_selected_objects=True, export_uv=False,
|
||||
export_normals=False, export_colors='SRGB', export_triangulated_mesh=False, ascii_format=True)
|
||||
bpy.data.objects.remove(obj_open3d, do_unlink=True)
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Save ply time: ', time.time() - start_time)
|
||||
|
||||
return obj_num_vertices
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# workdir = '/data/datasets/suwa_cut/data/auto/'
|
||||
# output = '/data/datasets/plys/auto/'
|
||||
workdir = 'G:\obj_fix\print'
|
||||
output = 'G:\obj_fix\ply\whole_obj_bake_vertex_high_ply'
|
||||
directory = f'G:\obj_fix/print/right'
|
||||
#遍历获取 G:\obj_fix\print\right 目录的文件,根据文件名获取 pid
|
||||
for file_name in os.listdir(directory):
|
||||
# 获取文件的完整路径
|
||||
file_path = os.path.join(directory, file_name)
|
||||
# 检查是否是文件
|
||||
if os.path.isfile(file_path):
|
||||
#
|
||||
arrFileName = file_name.split('_')
|
||||
pid = arrFileName[0]
|
||||
if int(pid) == 0:
|
||||
continue
|
||||
|
||||
print(f"File: {file_name}, PID: {pid}")
|
||||
|
||||
#判断文件是否存在
|
||||
file_path = os.path.join(output, f'{pid}.ply')
|
||||
if os.path.exists(file_path) and os.path.isfile(file_path):
|
||||
print("文件已存在,跳过循环!!!")
|
||||
continue
|
||||
|
||||
obj_filepath = os.path.join(workdir, f'{pid}/{pid}.obj')
|
||||
save_ply_path = os.path.join(output, f'{pid}.ply')
|
||||
if not os.path.exists(obj_filepath):
|
||||
print(f"{obj_filepath} 文件不存在,跳过循环!!!")
|
||||
continue
|
||||
|
||||
obj_num_vertices = get_obj_bake_vertex_ply(obj_filepath, save_ply_path)
|
||||
if obj_num_vertices == 0:
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: 模型 {pid} 程序getHeadCount()无法找到模型人数!总共运行时间: ', time.time() - start_time0)
|
||||
logging.info(f'模型 {pid} 顶点数量: {obj_num_vertices},程序getHeadCount()无法找到模型人数!,程序总共运行时间: {time.time() - start_time0:.2f} 秒')
|
||||
continue
|
||||
@@ -0,0 +1,296 @@
|
||||
import platform,sys,redis,time,requests,json,atexit
|
||||
sys.path.append('../libs/')
|
||||
|
||||
import config,libs,libs_db_temp,common
|
||||
|
||||
import os, sys, time, argparse, requests, json, re, oss2
|
||||
import bpy
|
||||
import open3d as o3d
|
||||
import shutil
|
||||
import psutil
|
||||
# 下载 obj mtl jpg 文件 修正obj 的贴地板 及身高尺寸 生成预览图
|
||||
|
||||
#文件存储lua路径
|
||||
diskName = "G:\\"
|
||||
filePath = "G://obj_fix/auto"
|
||||
#获取真实身高
|
||||
def get_real_height(input_path):
|
||||
get_real_height_url = 'https://mp.api.suwa3d.com/api/physical/infoByPid'
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Getting real height from {input_path}')
|
||||
pid = re.findall(r'(\d+)', input_path)[0]
|
||||
print(f'pid: {pid}')
|
||||
res = requests.get(get_real_height_url, params={'pid': pid})
|
||||
res = json.loads(res.text)
|
||||
if res['code'] == -1:
|
||||
print(f'Error: {res["message"]}, return default height 160')
|
||||
return 160
|
||||
height = res['data']['height']
|
||||
if height == 0:
|
||||
print(f'Error: height=0, return default height 160')
|
||||
return 160
|
||||
print(f'height: {height}')
|
||||
return height
|
||||
|
||||
|
||||
def base_fix(input_path):
|
||||
if not os.path.exists(input_path):
|
||||
print(f'Error: {input_path} does not exist.')
|
||||
return "error"
|
||||
|
||||
bpy.ops.wm.read_homefile()
|
||||
bpy.ops.object.delete(use_global=False, confirm=False)
|
||||
bpy.ops.wm.obj_import(filepath=input_path)
|
||||
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)
|
||||
|
||||
# 脚底贴地
|
||||
bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_1', align_axis={'Z'})
|
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||
|
||||
real_height = get_real_height(input_path)
|
||||
print(obj.dimensions)
|
||||
scale = real_height / obj.dimensions[2] / 100 # 除以100是因为单位是厘米
|
||||
bpy.context.object.scale = (scale, scale, scale)
|
||||
bpy.context.object.rotation_euler = (0, 0, 0)
|
||||
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN')
|
||||
bpy.context.object.location[0] = 0 # 移动到特定位置(1m, 1m)是为了让human3d可以正确识别
|
||||
bpy.context.object.location[1] = 0
|
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||
|
||||
# # bpy.ops.wm.save_mainfile(filepath=args.input_path.replace('.obj', '.blend'))
|
||||
# output_file = input_path.replace('.obj', 'test.obj')
|
||||
bpy.ops.wm.obj_export(filepath=input_path.replace('.obj', '_base_fix.obj'))
|
||||
return obj
|
||||
|
||||
#检测mtl文件最后一行是否 map_Kd 7126Tex1.jpg
|
||||
def check_mtl(tempMtlFilePath,pid):
|
||||
start_time = time.time()
|
||||
print(f"{pid}-开始检测mtl文件-开始时间-{start_time}")
|
||||
#读取mtl文件
|
||||
if not os.path.exists(tempMtlFilePath):
|
||||
return False
|
||||
with open(tempMtlFilePath, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
if len(lines) > 0:
|
||||
line = lines[-1]
|
||||
print(line)
|
||||
if "map_Kd" in line and f"{pid}Tex1.jpg" in line:
|
||||
print(f"{pid}-检测mtl文件-成功-耗时-{time.time()-start_time}")
|
||||
return True
|
||||
else:
|
||||
#删除最后一行
|
||||
if "map_Kd" in line and f"{pid}.jpg" in line:
|
||||
lines.pop()
|
||||
#最后一行插入 map_Kd 7126Tex1.jpg
|
||||
lines.append(f"map_Kd {pid}Tex1.jpg\n")
|
||||
with open(tempMtlFilePath, 'w', encoding='utf-8') as f:
|
||||
f.writelines(lines)
|
||||
print(f"{pid}-检测mtl文件-成功-耗时-{time.time()-start_time}")
|
||||
return True
|
||||
print(f"{pid}-检测mtl文件-失败-耗时-{time.time()-start_time}")
|
||||
return False
|
||||
|
||||
|
||||
#生成预览图
|
||||
def create_preview(obj_file_path,image_save_path,pid):
|
||||
|
||||
start_time = time.time()
|
||||
print(f"{pid}-开始生成预览图-开始时间-{start_time}")
|
||||
# 加载 OBJ 文件
|
||||
mesh = o3d.io.read_triangle_mesh(obj_file_path,enable_post_processing=True)
|
||||
mesh.compute_vertex_normals()
|
||||
# 创建视觉化窗口
|
||||
# o3d.visualization.MeshColorOption = o3d.visualization.MeshColorOption.Default
|
||||
vis = o3d.visualization.Visualizer()
|
||||
# render = o3d.visualization.RenderOption()
|
||||
width = 800
|
||||
height = 600
|
||||
vis.create_window(width=width, height=height,visible=False)
|
||||
# 添加几何体到视觉化窗口
|
||||
vis.add_geometry(mesh)
|
||||
vis.get_render_option().light_on = True
|
||||
# vis.get_render_option().mesh_color_option = o3d.visualization.MeshColorOption.Default
|
||||
vis.get_render_option().mesh_shade_option = o3d.visualization.MeshShadeOption.Default
|
||||
# render.background_color = [123,251,89] # 将光照强度设置为0.5或更低
|
||||
# vis.get_render_option().ambient_intensity = 0.9 # 设置环境光强度
|
||||
# vis.get_render_option().directional_light = False # 关闭定向光
|
||||
|
||||
# vis.get_render_option().line_width = 2.0
|
||||
# 更新渲染器并捕获屏幕图像
|
||||
vis.update_renderer()
|
||||
vis.poll_events()
|
||||
vis.capture_screen_image(image_save_path)
|
||||
# 关闭窗口
|
||||
vis.destroy_window()
|
||||
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: {pid} Run time: ', time.time() - start_time)
|
||||
|
||||
time.sleep(2)
|
||||
# 检测图片是否存在
|
||||
if not os.path.exists(image_save_path):
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: {pid} Error: {image_save_path} does not exist.')
|
||||
return False
|
||||
#判断文件夹是否存在
|
||||
if not os.path.exists(f"{filePath}/preview/"):
|
||||
os.makedirs(f"{filePath}/preview/")
|
||||
#图片存在则复制到指定的文件夹中
|
||||
shutil.copy(image_save_path, f"{filePath}/preview/")
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
#检测硬盘容量
|
||||
def check_disk():
|
||||
# 获取硬盘分区列表
|
||||
partitions = psutil.disk_partitions()
|
||||
|
||||
for partition in partitions:
|
||||
print("分区信息",partition)
|
||||
# 获取指定分区(比如'C:')的使用情况
|
||||
if partition.mountpoint == diskName: # 更改为您要检测的硬盘分区
|
||||
usage = psutil.disk_usage(partition.mountpoint)
|
||||
|
||||
free = f'{usage.free / (1024**3):.2f}'
|
||||
free = float(free)
|
||||
print(f"{diskName}剩余容量{free}")
|
||||
if free < 3:
|
||||
print("硬盘空间不足")
|
||||
return False
|
||||
|
||||
# print(f"Total: {usage.total / (1024**3):.2f} GB")
|
||||
# print(f"Used: {usage.used / (1024**3):.2f} GB")
|
||||
# print(f"Free: {usage.free / (1024**3):.2f} GB")
|
||||
# print(f"Percentage: {usage.percent}%")
|
||||
return True
|
||||
|
||||
|
||||
# def traverse_directory(directory):
|
||||
# for root, dirs, files in os.walk(directory):
|
||||
# print(f"当前目录: {root}")
|
||||
# print("子目录: ", dirs)
|
||||
# print("文件: ", files)
|
||||
# print()
|
||||
|
||||
def readTask():
|
||||
#遍历 G:\obj_fix\print\wrong
|
||||
folder_path = 'G:\\obj_fix\print\wrong'
|
||||
arrFiles = []
|
||||
for root, dirs, files in os.walk(folder_path):
|
||||
# print(f"当前目录: {root}")
|
||||
# print("子目录: ", dirs)
|
||||
arrFiles = files
|
||||
# print("文件: ", files)
|
||||
# print()
|
||||
for file in arrFiles:
|
||||
if file.endswith('_ok.png'):
|
||||
#提取pid
|
||||
pid = file.split('_')[0]
|
||||
print(pid)
|
||||
base_fix(f'{filePath}/{pid}/{pid}_new.obj')
|
||||
|
||||
|
||||
return
|
||||
#读取数据库的任务
|
||||
pid="164"
|
||||
base_fix(f'{filePath}/{pid}/{pid}_new.obj')
|
||||
|
||||
#生成预览图
|
||||
#res = create_preview(f'{filePath}/{pid}/{pid}_new.obj',f'{filePath}/{pid}/{pid}_preview.png',pid)
|
||||
|
||||
return
|
||||
|
||||
startTimeTask = time.time()
|
||||
|
||||
|
||||
pid = libs_db_temp.get_task_by_level()
|
||||
pid = str(pid)
|
||||
if pid == "":
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: 无任务,延时10s')
|
||||
time.sleep(10)
|
||||
return
|
||||
|
||||
if int(pid) == 0:
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: 无任务,延时10s')
|
||||
time.sleep(10)
|
||||
return
|
||||
#记录状态为处理中
|
||||
libs_db_temp.update_fix_status(pid,1)
|
||||
|
||||
# #判断对应的文件夹是否存在,存在则删除
|
||||
# if os.path.exists(f'{filePath}/{pid}'):
|
||||
# shutil.rmtree(f'{filePath}/{pid}')
|
||||
|
||||
|
||||
|
||||
# #下载pid的对应的oss端的文件
|
||||
# tempName = libs.down_obj_from_oss_for_fix(filePath, pid, "print")
|
||||
|
||||
# if tempName == "no_file":
|
||||
# #记录状态为异常 -3 文件不存在
|
||||
# libs_db_temp.update_fix_status(pid,-3)
|
||||
# return "error"
|
||||
|
||||
|
||||
# if tempName == "error":
|
||||
# #记录状态为异常 -1
|
||||
# libs_db_temp.update_fix_status(pid,-2)
|
||||
# return "error"
|
||||
|
||||
# if tempName == "":
|
||||
# print("找不到对应的obj文件")
|
||||
# #记录状态为异常 -1
|
||||
# libs_db_temp.update_fix_status(pid,-1)
|
||||
# return
|
||||
|
||||
|
||||
# #处理逻辑
|
||||
# print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())+"-"+pid+"-开始执行任务")
|
||||
# try:
|
||||
# res=base_fix(f'{filePath}/{pid}/{pid}.obj')
|
||||
# if res == "error":
|
||||
# #记录状态为异常 -2
|
||||
# libs_db_temp.update_fix_status(pid,-1)
|
||||
# return "error"
|
||||
# #记录状态为成功 2
|
||||
# # libs_db_temp.update_fix_status(pid,2)
|
||||
# except Exception as e:
|
||||
# #状态异常 -2
|
||||
# libs_db_temp.update_fix_status(pid,-1)
|
||||
# return "error"
|
||||
|
||||
# #检测 mtl文件是否有map_Kd 7126Tex1.jpg
|
||||
# res = check_mtl(f'{filePath}/{pid}/{pid}.mtl',pid)
|
||||
# if res == False:
|
||||
# #记录状态为异常 -2
|
||||
# libs_db_temp.update_fix_status(pid,-2)
|
||||
# return "error"
|
||||
# time.sleep(2)
|
||||
|
||||
#生成预览图
|
||||
res = create_preview(f'{filePath}/{pid}/{pid}.obj',f'{filePath}/{pid}/{pid}_preview.png',pid)
|
||||
if not res:
|
||||
#记录状态为异常 -2
|
||||
libs_db_temp.update_fix_status(pid,-2)
|
||||
return "error"
|
||||
libs_db_temp.update_fix_status(pid,2)
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: {pid} -任务完美结束总耗时: ', time.time() - startTimeTask)
|
||||
return True
|
||||
|
||||
#程序主入口
|
||||
if __name__ == '__main__':
|
||||
#atexit.register(common.notify,"处理数据校准任务已经停止")
|
||||
pid="90864"
|
||||
base_fix(f'{filePath}/{pid}/{pid}.obj')
|
||||
# readTask()
|
||||
# while True:
|
||||
# pid = readTask()
|
||||
# res = check_disk()
|
||||
# if res == False:
|
||||
# print("硬盘空间不足,无法执行任务")
|
||||
# break
|
||||
@@ -0,0 +1,157 @@
|
||||
import os, sys, time, argparse, requests, json, re, oss2
|
||||
import bpy, bmesh
|
||||
import open3d as o3d
|
||||
import numpy as np
|
||||
# import cupy as cp
|
||||
from mathutils import Matrix
|
||||
import math
|
||||
import logging
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
def get_auto_obj_bake_vertex_ply(obj_filepath, save_ply_path):
|
||||
start_time = time.time()
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Getting ply vertex from {obj_filepath}')
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.ops.object.select_by_type(type='MESH')
|
||||
bpy.ops.object.delete(use_global=False, confirm=False)
|
||||
bpy.ops.wm.obj_import(filepath=obj_filepath)
|
||||
|
||||
obj_original = [obj for obj in bpy.data.objects if obj.type == 'MESH'][0]
|
||||
bpy.context.view_layer.objects.active = obj_original
|
||||
obj_original.select_set(True)
|
||||
pid = obj_original.name
|
||||
|
||||
obj_num_vertices = len(obj_original.data.vertices)
|
||||
# print(f"模型 {pid} 顶点数量:", len(obj_original.data.vertices))
|
||||
|
||||
# 选择新分离出来的网格对象,并将该对象设为进活动对象
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = obj_original
|
||||
obj_original.select_set(True)
|
||||
|
||||
# 添加颜色属性
|
||||
bpy.ops.geometry.color_attribute_add()
|
||||
# 节点的创建:
|
||||
nodes = bpy.context.active_object.active_material.node_tree.nodes
|
||||
links = bpy.context.active_object.active_material.node_tree.links
|
||||
tex_coord = nodes.new("ShaderNodeTexCoord")
|
||||
Mapping = nodes.new('ShaderNodeMapping')
|
||||
vertex_color = nodes.new("ShaderNodeVertexColor")
|
||||
tex_coord.location = (-800, 600)
|
||||
Mapping.location = (-500, 200)
|
||||
vertex_color.location = (-200, 600)
|
||||
|
||||
# 节点之间的连接:
|
||||
# 纹理坐标的UV 连接 映射的矢量;映射的矢量 连接 纹理贴图的矢量;原理化BSDF删除;纹理贴图的矢量 连接 表(曲)面。
|
||||
links.new(tex_coord.outputs[2], nodes['Mapping'].inputs[0])
|
||||
links.new(Mapping.outputs[0], nodes['Image Texture'].inputs[0])
|
||||
principled_bsdf = nodes.get("Principled BSDF")
|
||||
nodes.remove(principled_bsdf)
|
||||
links.new(nodes['Image Texture'].outputs[0], nodes['Material Output'].inputs[0])
|
||||
|
||||
# 4.渲染烘焙相关操作
|
||||
bpy.context.scene.render.engine = 'CYCLES'
|
||||
bpy.context.scene.cycles.device = 'GPU'
|
||||
bpy.context.scene.cycles.preview_samples = 1
|
||||
bpy.context.scene.cycles.samples = 1
|
||||
bpy.context.scene.cycles.bake_type = 'EMIT'
|
||||
bpy.context.scene.render.bake.use_selected_to_active = False
|
||||
bpy.context.scene.render.bake.target = 'VERTEX_COLORS'
|
||||
bpy.ops.object.bake(type='EMIT') # 开始 Bake
|
||||
|
||||
# 连接节点 Color Attribute 和 节点 Material Output
|
||||
links.new(nodes['Color Attribute'].outputs[0], nodes['Material Output'].inputs[0])
|
||||
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Bake EMIT time: ', time.time() - start_time)
|
||||
start_time = time.time()
|
||||
|
||||
# 5.输出模型
|
||||
# bpy.ops.wm.save_as_mainfile(filepath='/data/datasets/plys/121614.bend')
|
||||
bpy.ops.wm.ply_export(filepath=save_ply_path, apply_modifiers=True, export_selected_objects=True, export_uv=False,
|
||||
export_normals=False, export_colors='SRGB', export_triangulated_mesh=False, ascii_format=True)
|
||||
bpy.data.objects.remove(obj_original, do_unlink=True)
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Save ply time: ', time.time() - start_time)
|
||||
|
||||
return obj_num_vertices
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# workdir = '/data/datasets/suwa_cut/data/auto/'
|
||||
# output = '/data/datasets/plys/auto/'
|
||||
workdir = 'G:\obj_fix\print'
|
||||
output = 'G:\obj_fix\ply\whole_obj_bake_vertex_high_ply'
|
||||
directory = f'G:\obj_fix/print/right'
|
||||
#遍历获取 G:\obj_fix\print\right 目录的文件,根据文件名获取 pid
|
||||
for file_name in os.listdir(directory):
|
||||
# 获取文件的完整路径
|
||||
file_path = os.path.join(directory, file_name)
|
||||
# 检查是否是文件
|
||||
if os.path.isfile(file_path):
|
||||
#
|
||||
arrFileName = file_name.split('_')
|
||||
pid = arrFileName[0]
|
||||
if int(pid) == 0:
|
||||
continue
|
||||
|
||||
print(f"File: {file_name}, PID: {pid}")
|
||||
|
||||
#判断文件是否存在
|
||||
file_path = os.path.join(output, f'{pid}.ply')
|
||||
if os.path.exists(file_path) and os.path.isfile(file_path):
|
||||
print("文件已存在,跳过循环!!!")
|
||||
continue
|
||||
|
||||
obj_filepath = os.path.join(workdir, f'{pid}/{pid}.obj')
|
||||
save_ply_path = os.path.join(output, f'{pid}.ply')
|
||||
if not os.path.exists(obj_filepath):
|
||||
print(f"{obj_filepath} 文件不存在,跳过循环!!!")
|
||||
continue
|
||||
|
||||
obj_num_vertices = get_auto_obj_bake_vertex_ply(obj_filepath, save_ply_path)
|
||||
if obj_num_vertices == 0:
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: 模型 {pid} 程序getHeadCount()无法找到模型人数!总共运行时间: ', time.time() - start_time0)
|
||||
logging.info(f'模型 {pid} 顶点数量: {obj_num_vertices},程序getHeadCount()无法找到模型人数!,程序总共运行时间: {time.time() - start_time0:.2f} 秒')
|
||||
continue
|
||||
# # 从文件名中提取 pid
|
||||
# pid = extract_pid(file_name)
|
||||
# if pid:
|
||||
# print(f"File: {file_name}, PID: {pid}")
|
||||
# else:
|
||||
# print(f"Unable to extract PID from file: {file_name}")
|
||||
|
||||
|
||||
# return
|
||||
# listdir_pids = os.listdir(workdir)
|
||||
# for pid in listdir_pids:
|
||||
# file_path = os.path.join(output, f'{pid}/{pid}.ply')
|
||||
# if os.path.exists(file_path) and os.path.isfile(file_path):
|
||||
# print("文件已存在,跳过循环!!!")
|
||||
# continue
|
||||
|
||||
# print(pid)
|
||||
# logging.basicConfig(filename=f'/data/datasets/plys/auto_vertex_time.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
# start_time0 = time.time()
|
||||
# print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: 模型 {pid} 开始运行时间: ', start_time0)
|
||||
# # 判断输出目录是否存在
|
||||
# if not os.path.exists(os.path.join(output, f'{pid}/')):
|
||||
# os.makedirs(os.path.join(output, f'{pid}/'))
|
||||
|
||||
# obj_filepath = os.path.join(workdir, f'{pid}/{pid}_cpd.obj')
|
||||
# save_ply_path = os.path.join(output, f'{pid}/{pid}.ply')
|
||||
# if not os.path.exists(obj_filepath):
|
||||
# print(f"{obj_filepath} 文件不存在,跳过循环!!!")
|
||||
# continue
|
||||
|
||||
# obj_num_vertices = get_auto_obj_bake_vertex_ply(obj_filepath, save_ply_path)
|
||||
# if obj_num_vertices == 0:
|
||||
# print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: 模型 {pid} 程序getHeadCount()无法找到模型人数!总共运行时间: ', time.time() - start_time0)
|
||||
# logging.info(f'模型 {pid} 顶点数量: {obj_num_vertices},程序getHeadCount()无法找到模型人数!,程序总共运行时间: {time.time() - start_time0:.2f} 秒')
|
||||
# continue
|
||||
|
||||
# print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: 模型 {pid} 顶点数量: {obj_num_vertices} 程序总共运行时间: ', time.time() - start_time0)
|
||||
# logging.info(f'模型 {pid} 顶点数量: {obj_num_vertices},程序总共运行时间: {time.time() - start_time0:.2f} 秒')
|
||||
# # 退出 Blender 并释放资源
|
||||
# bpy.ops.wm.quit_blender()
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import paramiko
|
||||
import os
|
||||
|
||||
# 使用示例
|
||||
sftp_host = "172.31.1.3" # SFTP 服务器地址
|
||||
sftp_port = 22 # SFTP 端口号
|
||||
sftp_user = 'gpu' # SFTP 登录用户名
|
||||
sftp_passwd = '2024' # SFTP 登录密码
|
||||
# 获取当前文件上一层目录,即项目目录 make2 底下
|
||||
local_root_path = "G:\\obj_fix\print"
|
||||
|
||||
# 定义函数来递归遍历文件夹下的所有文件
|
||||
def list_files(directory):
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
yield os.path.join(root, file)
|
||||
|
||||
def sftp_upload(sftp_host, sftp_port, sftp_user, sftp_passwd, local_file, remote_file):
|
||||
# 创建 SSH 客户端对象
|
||||
ssh_client = paramiko.SSHClient()
|
||||
|
||||
try:
|
||||
# 设置自动添加主机密钥
|
||||
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
# 连接到 SFTP 服务器
|
||||
ssh_client.connect(sftp_host, port=sftp_port, username=sftp_user, password=sftp_passwd)
|
||||
|
||||
# 创建 SFTP 客户端对象
|
||||
sftp_client = ssh_client.open_sftp()
|
||||
|
||||
# 判断 local_file 是文件还是目录
|
||||
if os.path.isdir(local_file):
|
||||
try:
|
||||
# 如果远程目录不存在,则创建
|
||||
sftp_client.mkdir(remote_file)
|
||||
except IOError:
|
||||
# 目录已存在,忽略
|
||||
pass
|
||||
|
||||
# 切换至远程目录
|
||||
sftp_client.chdir(remote_file)
|
||||
|
||||
# 遍历本地目录
|
||||
for file_name in os.listdir(local_file):
|
||||
local_path = os.path.join(local_file, file_name)
|
||||
|
||||
if os.path.isfile(local_path):
|
||||
# 如果是文件,上传文件
|
||||
sftp_client.put(local_path, file_name)
|
||||
else:
|
||||
# 如果是文件,直接上传文件
|
||||
sftp_client.put(local_file, remote_file)
|
||||
|
||||
print("上传成功")
|
||||
|
||||
except Exception as e:
|
||||
print(f"上传文件失败:{e}")
|
||||
|
||||
finally:
|
||||
# 关闭 SFTP 客户端和 SSH 连接
|
||||
sftp_client.close()
|
||||
ssh_client.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
nums = 0
|
||||
#遍历 local_root_path 下的所有文件
|
||||
for file_path in list_files(local_root_path):
|
||||
|
||||
if "around" in file_path:
|
||||
continue
|
||||
|
||||
|
||||
if "_old.obj" in file_path:
|
||||
|
||||
|
||||
# if ".obj" not in file_path:
|
||||
# continue
|
||||
|
||||
arrTemp = file_path.split('\\')
|
||||
pid = arrTemp[len(arrTemp)-2]
|
||||
nums += 1
|
||||
|
||||
# # pid = "164"
|
||||
# 本地文件路径
|
||||
dirPath = f'{local_root_path}/{pid}/{pid}.obj'
|
||||
# 远程服务的路径
|
||||
remote_file = f"/data/datasets/obj_fix/{pid}/{pid}.obj"
|
||||
sftp_upload(sftp_host, sftp_port, sftp_user, sftp_passwd, dirPath, remote_file)
|
||||
print(pid,file_path,"---剩余---",524 - nums)
|
||||
@@ -0,0 +1,92 @@
|
||||
import platform,sys,redis,time,requests,json,atexit
|
||||
import os, sys, time, argparse, requests, json, re, oss2
|
||||
import bpy
|
||||
import open3d as o3d
|
||||
import shutil
|
||||
import psutil
|
||||
|
||||
#生成预览图
|
||||
def create_preview(obj_file_path,image_save_path,pid):
|
||||
|
||||
start_time = time.time()
|
||||
print(f"{pid}-开始生成预览图-开始时间-{start_time}")
|
||||
# 加载 OBJ 文件
|
||||
mesh = o3d.io.read_triangle_mesh(obj_file_path,enable_post_processing=True)
|
||||
mesh.compute_vertex_normals()
|
||||
# 创建视觉化窗口
|
||||
# o3d.visualization.MeshColorOption = o3d.visualization.MeshColorOption.Default
|
||||
vis = o3d.visualization.Visualizer()
|
||||
# render = o3d.visualization.RenderOption()
|
||||
width = 50
|
||||
height = 100
|
||||
vis.create_window(width=width, height=height,visible=False)
|
||||
# 添加几何体到视觉化窗口
|
||||
vis.add_geometry(mesh)
|
||||
vis.get_render_option().light_on = True
|
||||
# vis.get_render_option().mesh_color_option = o3d.visualization.MeshColorOption.Default
|
||||
vis.get_render_option().mesh_shade_option = o3d.visualization.MeshShadeOption.Default
|
||||
# render.background_color = [123,251,89] # 将光照强度设置为0.5或更低
|
||||
# vis.get_render_option().ambient_intensity = 0.9 # 设置环境光强度
|
||||
# vis.get_render_option().directional_light = False # 关闭定向光
|
||||
|
||||
# vis.get_render_option().line_width = 2.0
|
||||
# 更新渲染器并捕获屏幕图像
|
||||
vis.update_renderer()
|
||||
vis.poll_events()
|
||||
vis.capture_screen_image(image_save_path)
|
||||
# 关闭窗口
|
||||
vis.destroy_window()
|
||||
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: {pid} Run time: ', time.time() - start_time)
|
||||
|
||||
time.sleep(2)
|
||||
# 检测图片是否存在
|
||||
if not os.path.exists(image_save_path):
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: {pid} Error: {image_save_path} does not exist.')
|
||||
return False
|
||||
# #判断文件夹是否存在
|
||||
# if not os.path.exists(f"{filePath}/preview/"):
|
||||
# os.makedirs(f"{filePath}/preview/")
|
||||
# #图片存在则复制到指定的文件夹中
|
||||
# shutil.copy(image_save_path, f"{filePath}/preview/")
|
||||
|
||||
|
||||
return True
|
||||
|
||||
# 定义函数来递归遍历文件夹下的所有文件
|
||||
def list_files(directory):
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
yield os.path.join(root, file)
|
||||
|
||||
|
||||
|
||||
#生成预览图
|
||||
|
||||
target_folder = 'G://obj_fix/print/'
|
||||
source_folder = 'G://obj_fix/print/preview'
|
||||
#遍历文件夹下的所有文件
|
||||
nums = 0
|
||||
for file_path in list_files(target_folder):
|
||||
|
||||
|
||||
if ".obj" not in file_path:
|
||||
continue
|
||||
print("文件路径:",file_path)
|
||||
#判断文件是否存在
|
||||
if not os.path.exists(file_path):
|
||||
continue
|
||||
|
||||
print(file_path)
|
||||
arrData = file_path.split('/')
|
||||
pid = arrData[len(arrData)-2]
|
||||
|
||||
|
||||
#判断封面图片是否存在
|
||||
# if os.path.exists(f'{target_folder}/{pid}_preview.png'):
|
||||
# print(f'{pid}_preview.png 已存在,跳过循环!!!')
|
||||
# continue
|
||||
nums += 1
|
||||
print(f"正在处理{pid}","--剩余--",1530 - nums)
|
||||
|
||||
create_preview(f'{file_path}',f'{source_folder}/{pid}_preview.png',pid)
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
import os
|
||||
import shutil
|
||||
#遍历 G:\obj_fix\print\wrong 文件 获取到pid
|
||||
|
||||
workdir = 'G:\obj_fix\print'
|
||||
# output = 'G:\obj_fix\ply\whole_obj_bake_vertex_high_ply'
|
||||
directory = f'G:\obj_fix/print/wrong'
|
||||
#遍历获取 G:\obj_fix\print\right 目录的文件,根据文件名获取 pid
|
||||
|
||||
arrWrongFiles = []
|
||||
for file_name in os.listdir(directory):
|
||||
# # 获取文件的完整路径
|
||||
# file_path = os.path.join(directory, file_name)
|
||||
# # 检查是否是文件
|
||||
# if os.path.isfile(file_path):
|
||||
# #
|
||||
|
||||
arrFileName = file_name.split('_')
|
||||
pid = arrFileName[0]
|
||||
if int(pid) == 0:
|
||||
continue
|
||||
|
||||
arrWrongFiles.append(pid)
|
||||
|
||||
#遍历 G:\obj_fix\print\preview 文件 获取到pid
|
||||
directory = f'G:\obj_fix/print/preview'
|
||||
for file_name in os.listdir(directory):
|
||||
arrFileName = file_name.split('_')
|
||||
pid = arrFileName[0]
|
||||
if int(pid) == 0:
|
||||
continue
|
||||
|
||||
if pid in arrWrongFiles:
|
||||
continue
|
||||
|
||||
#判断right目录中是否已经存在该pid的文件
|
||||
if os.path.exists(f'G:\obj_fix/print/right/{file_name}'):
|
||||
continue
|
||||
|
||||
print("new_pid",pid)
|
||||
#不在arrWrongFiles中的pid,就是正确的pid要复制到right目录
|
||||
filePath = os.path.join(directory, file_name)
|
||||
shutil.copy(filePath, f'G:\obj_fix/print/right/{file_name}')
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
31013
|
||||
78387
|
||||
40918
|
||||
71701
|
||||
27553
|
||||
58122
|
||||
55114
|
||||
25093
|
||||
9846
|
||||
70756
|
||||
128719
|
||||
17388
|
||||
49934
|
||||
56750
|
||||
71421
|
||||
55844
|
||||
136602
|
||||
136603
|
||||
136925
|
||||
31450
|
||||
71326
|
||||
30624
|
||||
56802
|
||||
4417
|
||||
55367
|
||||
119772
|
||||
75416
|
||||
71422
|
||||
66521
|
||||
26848
|
||||
55083
|
||||
77226
|
||||
42595
|
||||
8141
|
||||
115162
|
||||
66531
|
||||
11997
|
||||
77960
|
||||
115302
|
||||
68700
|
||||
124636
|
||||
79333
|
||||
54701
|
||||
70807
|
||||
65918
|
||||
64088
|
||||
127455
|
||||
71415
|
||||
11797
|
||||
68807
|
||||
115123
|
||||
53804
|
||||
24775
|
||||
75402
|
||||
30663
|
||||
68349
|
||||
44322
|
||||
14301
|
||||
55076
|
||||
14216
|
||||
68699
|
||||
56784
|
||||
55030
|
||||
70532
|
||||
13084
|
||||
48197
|
||||
79263
|
||||
14475
|
||||
44845
|
||||
14094
|
||||
15963
|
||||
29506
|
||||
117004
|
||||
88074
|
||||
49404
|
||||
62516
|
||||
57762
|
||||
76289
|
||||
25568
|
||||
120406
|
||||
9177
|
||||
13073
|
||||
70754
|
||||
55082
|
||||
83698
|
||||
77516
|
||||
125118
|
||||
25043
|
||||
78384
|
||||
71356
|
||||
56559
|
||||
70792
|
||||
68692
|
||||
115028
|
||||
26384
|
||||
8171
|
||||
23805
|
||||
14148
|
||||
82460
|
||||
24765
|
||||
82496
|
||||
115074
|
||||
120416
|
||||
39732
|
||||
45480
|
||||
67655
|
||||
22155
|
||||
77957
|
||||
29507
|
||||
83844
|
||||
56799
|
||||
31596
|
||||
40613
|
||||
24766
|
||||
100270
|
||||
104867
|
||||
21807
|
||||
56781
|
||||
21007
|
||||
136602
|
||||
55011
|
||||
22204
|
||||
77964
|
||||
16074
|
||||
56877
|
||||
68724
|
||||
13160
|
||||
136302
|
||||
83715
|
||||
121495
|
||||
77431
|
||||
12177
|
||||
68906
|
||||
24784
|
||||
30617
|
||||
87849
|
||||
56754
|
||||
55370
|
||||
42781
|
||||
12981
|
||||
70732
|
||||
121614
|
||||
30660
|
||||
8852
|
||||
31012
|
||||
78376
|
||||
75405
|
||||
9023
|
||||
136603
|
||||
69798
|
||||
55092
|
||||
8145
|
||||
71361
|
||||
81419
|
||||
55887
|
||||
83761
|
||||
74624
|
||||
82571
|
||||
15707
|
||||
115228
|
||||
70746
|
||||
83983
|
||||
29805
|
||||
71318
|
||||
135850
|
||||
83759
|
||||
29489
|
||||
63115
|
||||
19338
|
||||
16012
|
||||
29796
|
||||
15012
|
||||
48250
|
||||
8160
|
||||
75408
|
||||
48477
|
||||
68718
|
||||
21780
|
||||
55860
|
||||
77221
|
||||
20197
|
||||
115017
|
||||
86196
|
||||
55730
|
||||
82483
|
||||
64739
|
||||
13154
|
||||
8121
|
||||
22159
|
||||
55372
|
||||
86211
|
||||
44623
|
||||
11845
|
||||
68925
|
||||
106929
|
||||
31010
|
||||
31442
|
||||
71321
|
||||
68940
|
||||
25200
|
||||
50370
|
||||
70729
|
||||
49935
|
||||
68916
|
||||
24780
|
||||
39522
|
||||
42047
|
||||
20675
|
||||
78385
|
||||
68688
|
||||
13120
|
||||
8953
|
||||
33899
|
||||
13692
|
||||
79331
|
||||
76300
|
||||
30638
|
||||
71323
|
||||
19405
|
||||
48880
|
||||
56797
|
||||
56654
|
||||
9181
|
||||
115051
|
||||
46352
|
||||
10873
|
||||
27534
|
||||
55075
|
||||
136357
|
||||
55084
|
||||
11881
|
||||
68735
|
||||
77972
|
||||
8124
|
||||
13161
|
||||
70812
|
||||
30705
|
||||
55081
|
||||
123599
|
||||
31986
|
||||
36791
|
||||
55849
|
||||
47752
|
||||
55021
|
||||
128446
|
||||
30843
|
||||
71407
|
||||
41320
|
||||
57168
|
||||
71395
|
||||
16078
|
||||
20203
|
||||
65653
|
||||
27214
|
||||
52884
|
||||
71404
|
||||
24772
|
||||
115042
|
||||
55090
|
||||
78381
|
||||
43054
|
||||
125097
|
||||
136925
|
||||
68730
|
||||
68821
|
||||
68926
|
||||
61764
|
||||
11676
|
||||
43059
|
||||
98894
|
||||
80445
|
||||
84547
|
||||
83707
|
||||
82435
|
||||
83729
|
||||
82440
|
||||
83712
|
||||
80166
|
||||
80007
|
||||
82481
|
||||
83727
|
||||
85652
|
||||
86232
|
||||
86252
|
||||
83756
|
||||
82511
|
||||
87649
|
||||
82486
|
||||
@@ -0,0 +1,70 @@
|
||||
from flask import Flask, jsonify, request
|
||||
import os
|
||||
from flask_cors import CORS
|
||||
app = Flask(__name__)
|
||||
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
|
||||
|
||||
# 定义函数来递归遍历文件夹下的所有文件
|
||||
def list_files(directory):
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
yield os.path.join(root, file)
|
||||
|
||||
# 更新数据
|
||||
@app.route('/api/data/', methods=['GET'])
|
||||
def update_data():
|
||||
pid = request.args.get('pid')
|
||||
angle = request.args.get('angle')
|
||||
jump = request.args.get('jump')
|
||||
|
||||
if jump == "jump":
|
||||
file_path = f'G:\obj_fix\print\wrong\{pid}_preview.png'
|
||||
if not os.path.exists(file_path):
|
||||
return "file not exists"
|
||||
|
||||
#重命名文件
|
||||
new_file_path = f'G:\obj_fix\print\wrong\{pid}_preview_wait.png'
|
||||
os.rename(file_path, new_file_path)
|
||||
else:
|
||||
if pid == "0":
|
||||
return "pid is 0"
|
||||
|
||||
if str(angle[0]) == "0":
|
||||
return "angle is 0"
|
||||
|
||||
file_path = f'G:\obj_fix\print\wrong\{pid}_preview.png'
|
||||
if not os.path.exists(file_path):
|
||||
return "file not exists"
|
||||
|
||||
#重命名文件
|
||||
new_file_path = f'G:\obj_fix\print\wrong\{pid}_preview_{angle}.png'
|
||||
os.rename(file_path, new_file_path)
|
||||
|
||||
#遍历 G:\obj_fix\print\wrong\ 文件夹下的所有文件
|
||||
for file_path in list_files('G:\obj_fix\print\wrong'):
|
||||
if "_preview.png" not in file_path:
|
||||
continue
|
||||
#获取pid 从 "G:\obj_fix\print\wrong\76289_preview_117.png" 中获取 "76289
|
||||
pid = file_path.split('\\')[-1].split('_')[0]
|
||||
|
||||
return pid
|
||||
|
||||
# #组装链接
|
||||
# url = f'https://mp.suwa3d.com/preview.html?platform=1&id={pid}&size=18cm_x1&angle=1#/'
|
||||
|
||||
# #将 url 写入文件
|
||||
# with open('url.txt', 'a') as f:
|
||||
# f.write(url + '\n')
|
||||
|
||||
|
||||
#return jsonify({'pid': id1, 'angle': id2})
|
||||
# for item in data:
|
||||
# if item['id'] == data_id:
|
||||
# item.update(request.get_json())
|
||||
# return jsonify({'message': 'Data updated successfully'})
|
||||
#return jsonify({'message': 'Data not found'}), 404
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
+2017218
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,54 @@
|
||||
from pathlib import Path
|
||||
import os
|
||||
import shutil
|
||||
# 指定要遍历的文件夹路径
|
||||
folder_path = Path('G://obj_fix/print')
|
||||
|
||||
# 定义函数来递归遍历文件夹下的所有文件
|
||||
def list_files(directory):
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
yield os.path.join(root, file)
|
||||
|
||||
# 指定要遍历的文件夹路径
|
||||
# folder_path = folder_path
|
||||
|
||||
# 使用list_files函数来列出文件夹下的所有文件
|
||||
nums = 0
|
||||
for file_path in list_files(folder_path):
|
||||
|
||||
if "around" in file_path:
|
||||
continue
|
||||
|
||||
|
||||
if "_new.obj" not in file_path:
|
||||
continue
|
||||
|
||||
|
||||
|
||||
|
||||
#提取pid
|
||||
arrTemp = file_path.split('\\')
|
||||
pid = arrTemp[len(arrTemp)-2]
|
||||
|
||||
#判段是否存在 {pid}.obj 文件,存在的话就进行重命名
|
||||
initObjPath = f'G://obj_fix/print/{pid}/{pid}.obj'
|
||||
if not os.path.exists(initObjPath):
|
||||
continue
|
||||
|
||||
#存在的话就重命名文件
|
||||
old_file_path = f'G://obj_fix/print/{pid}/{pid}_old.obj'
|
||||
os.rename(initObjPath, old_file_path)
|
||||
|
||||
|
||||
newObjfilePath = f'G://obj_fix/print/{pid}/{pid}_new.obj'
|
||||
os.rename(newObjfilePath, initObjPath)
|
||||
|
||||
# # 将 new.obj 文件复制到指定的文件夹下 G:\obj_fix\print\{pid}\
|
||||
|
||||
# source_obj = file_path
|
||||
# target_obj = f'G://obj_fix/print/{pid}/{pid}_new.obj'
|
||||
# print(f"当前正在处理{pid}")
|
||||
# shutil.copy(source_obj, target_obj)
|
||||
nums += 1
|
||||
print(pid,file_path,nums)
|
||||
@@ -0,0 +1,33 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
folder_path = 'G:\\obj_fix\print'
|
||||
|
||||
# 遍历文件夹下的所有文件
|
||||
# 定义函数来递归遍历文件夹下的所有文件
|
||||
def list_files(directory):
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
yield os.path.join(root, file)
|
||||
|
||||
nums = 0
|
||||
for file_path in list_files(folder_path):
|
||||
if "_preview.png" not in file_path:
|
||||
continue
|
||||
print(file_path)
|
||||
nums += 1
|
||||
# #提取pid
|
||||
# arrTemp = file_path.split('\\')
|
||||
# pid = arrTemp[len(arrTemp)-2]
|
||||
|
||||
# #判断 G:\obj_fix\print\wrong\ 文件夹里 是否存在 {pid}_preview.png 文件
|
||||
|
||||
# filePath = f'G://obj_fix\print\wrong\{pid}_preview.png'
|
||||
# #不存在跳过
|
||||
# if not os.path.exists(filePath):
|
||||
# continue
|
||||
|
||||
# #存在的话就重命名文件
|
||||
# new_file_path = f'G://obj_fix\print\wrong\{pid}_preview_already_right.png'
|
||||
# os.rename(filePath, new_file_path)
|
||||
print(nums)
|
||||
@@ -0,0 +1,124 @@
|
||||
import numpy as np
|
||||
import open3d as o3d
|
||||
import math,os
|
||||
#矫正模型的方向,目前只支持水平旋转,
|
||||
#55084 55090 55092 55114 55367 55370 55372 55730 55844 55849 56559 56654 56750 75408
|
||||
|
||||
|
||||
|
||||
def rotate_vertex(x, y, z, axis, angle_degrees):
|
||||
"""
|
||||
围绕任意轴旋转顶点。
|
||||
axis是一个表示旋转轴的三元组 (ax, ay, az)。
|
||||
angle_degrees是旋转角度,以度为单位。
|
||||
"""
|
||||
# 将角度转换为弧度
|
||||
angle_radians = math.radians(angle_degrees)
|
||||
|
||||
# 将旋转轴转换为单位向量
|
||||
axis = np.array(axis)
|
||||
axis = axis / np.linalg.norm(axis)
|
||||
|
||||
# 将顶点位置转换为向量
|
||||
v = np.array([x, y, z])
|
||||
|
||||
# 应用罗德里格斯旋转公式
|
||||
v_rotated = (v * math.cos(angle_radians) +
|
||||
np.cross(axis, v) * math.sin(angle_radians) +
|
||||
axis * np.dot(axis, v) * (1 - math.cos(angle_radians)))
|
||||
|
||||
return v_rotated.tolist()
|
||||
|
||||
def transform_obj_file(source_file, target_file, axis, angle_degrees):
|
||||
"""
|
||||
读取 OBJ 文件,应用旋转,然后将结果保存到另一个文件。
|
||||
"""
|
||||
with open(source_file, 'r') as src:
|
||||
lines = src.readlines()
|
||||
|
||||
with open(target_file, 'w') as tgt:
|
||||
for line in lines:
|
||||
if line.startswith('v '):
|
||||
parts = line.split()
|
||||
x, y, z = map(float, parts[1:4])
|
||||
x_new, y_new, z_new = rotate_vertex(x, y, z, axis, angle_degrees)
|
||||
tgt.write(f"v {x_new} {y_new} {z_new}\n")
|
||||
else:
|
||||
tgt.write(line)
|
||||
|
||||
def list_files(directory):
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
yield os.path.join(root, file)
|
||||
|
||||
# # #遍历文件夹下的所有文件
|
||||
# folder_path = 'G://obj_fix/print/wrong'
|
||||
# nums = 0
|
||||
# rotate_axis = (0, 1, 0) # 比如绕X轴旋转
|
||||
# for file_path in list_files(folder_path):
|
||||
# #print(file_path) G://obj_fix/print/around/preview\9023_preview_117.png
|
||||
# if "_preview.png" in file_path:
|
||||
# continue
|
||||
# if "_right.png" in file_path:
|
||||
# continue
|
||||
# if "_wait.png" in file_path:
|
||||
# continue
|
||||
# #获取pid 从 "G://obj_fix/print/around/preview\9023_preview_117.png" 中获取 9023 he 117
|
||||
# arrData = file_path.split('\\')
|
||||
# lastData = arrData[-1]
|
||||
# arrLastData = lastData.split('_')
|
||||
# if len(arrLastData) < 3:
|
||||
# print(lastData)
|
||||
# continue
|
||||
# pid = arrLastData[0]
|
||||
# angle = arrLastData[2].split('.')[0]
|
||||
# #判断 angle 第一个数字是否为 0
|
||||
|
||||
# source_obj = f'G://obj_fix/print/{pid}/{pid}.obj'
|
||||
# target_obj = f'G://obj_fix/print/{pid}/{pid}_new.obj'
|
||||
|
||||
# nums +=1
|
||||
# print(f"当前正在处理{pid},已处理至-{nums}",)
|
||||
# if str(angle[0]) == "0":
|
||||
# rotate_angle = -int(angle)
|
||||
# transform_obj_file(source_obj, target_obj, rotate_axis, rotate_angle)
|
||||
# else:
|
||||
# rotate_angle = int(angle)
|
||||
# transform_obj_file(source_obj, target_obj, rotate_axis, rotate_angle)
|
||||
# print(int(angle))
|
||||
# print(angle,angle[0])
|
||||
|
||||
|
||||
|
||||
# # 加载模型
|
||||
# mesh = o3d.io.read_triangle_mesh(target_obj, enable_post_processing=True)
|
||||
|
||||
# # 创建渲染窗口并添加模型
|
||||
# vis = o3d.visualization.Visualizer()
|
||||
# vis.create_window()
|
||||
# vis.add_geometry(mesh)
|
||||
|
||||
# # 设置渲染参数
|
||||
# render_option = vis.get_render_option()
|
||||
# render_option.point_size = 1.0
|
||||
|
||||
# # 渲染模型
|
||||
# vis.run()
|
||||
# vis.destroy_window()
|
||||
|
||||
|
||||
pid = "90864" # 98894 115123
|
||||
source_obj = f'G://obj_fix/auto/{pid}/{pid}.obj'
|
||||
target_obj = f'G://obj_fix/auto/{pid}/{pid}_90.obj'
|
||||
|
||||
#遍历文件夹下的所有文件 /data/datasets/temp/auto_preview_need_around
|
||||
for file_path in list_files('G://obj_fix/auto'):
|
||||
pid = file_path.split('\\')[-1].split('_')[0]
|
||||
# 使用示例:
|
||||
rotate_axis = (1, 0, 0) # 比如绕X轴旋转 0 x 轴
|
||||
rotate_angle = -90 # 以度为单位
|
||||
transform_obj_file(source_obj, target_obj, rotate_axis, rotate_angle)
|
||||
|
||||
# rotate_axis = (0, 1, 0) # 比如绕X轴旋转 0 x 轴
|
||||
# rotate_angle = 60 # 以度为单位
|
||||
# transform_obj_file(target_obj, target_obj, rotate_axis, rotate_angle)
|
||||
@@ -0,0 +1,52 @@
|
||||
import platform,sys,redis,time,requests,json,atexit
|
||||
sys.path.append('../libs/')
|
||||
|
||||
import config,libs,libs_db_temp,common
|
||||
import oss2,os
|
||||
|
||||
|
||||
# 将本地文件上传到oss 中,并且清除 oss 上的文件
|
||||
|
||||
#文件存储lua路径
|
||||
typeV = "print"
|
||||
filePath = f"G://obj_fix/{typeV}"
|
||||
ossPrefix = f"objs/{typeV}"
|
||||
def readTask():
|
||||
startTimeTask = time.time()
|
||||
#读取数据库的任务
|
||||
# pid = libs_db_temp.get_task_by_status(100) #2 表示该文件已经处理好了
|
||||
# pid = str(pid)
|
||||
pid = "4417"
|
||||
if int(pid) == 0:
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: 无任务,延时10s')
|
||||
time.sleep(10)
|
||||
return
|
||||
#检测文件是否存在
|
||||
if not os.path.exists(f"{filePath}/{pid}/{pid}.obj"):
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: {pid} Error: {filePath}/{pid}/{pid}.obj does not exist.')
|
||||
return False
|
||||
|
||||
if not os.path.exists(f"{filePath}/{pid}/{pid}Tex1.jpg"):
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: {pid} Error: {filePath}/{pid}/{pid}.obj does not exist.')
|
||||
return False
|
||||
|
||||
if not os.path.exists(f"{filePath}/{pid}/{pid}.mtl"):
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: {pid} Error: {filePath}/{pid}/{pid}.obj does not exist.')
|
||||
return False
|
||||
|
||||
if not os.path.exists(f"{filePath}/{pid}/{pid}_preview.png"):
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: {pid} Error: {filePath}/{pid}/{pid}.obj does not exist.')
|
||||
return False
|
||||
|
||||
#清空oss上的文件
|
||||
libs.clear_oss_by_pid(f'{ossPrefix}/{pid}')
|
||||
#上传文件到oss
|
||||
libs.upload_obj_to_oss(filePath,typeV,pid)
|
||||
return
|
||||
|
||||
#程序主入口
|
||||
if __name__ == '__main__':
|
||||
#atexit.register(common.notify,"处理数据校准任务已经停止")
|
||||
readTask()
|
||||
# while True:
|
||||
# pid = readTask()
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=100270&size=18cm_x1&angle=1#
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=104867&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=106929&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=115017&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=115028&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=115042&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=115051&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=115074&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=115123&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=115162&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=115228&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=115302&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=117004&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=119772&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=120406&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=120416&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=121495&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=121614&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=123599&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=124636&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=125097&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=125118&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=127455&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=128446&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=128719&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=13154&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=13160&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=13161&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=135850&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=136302&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=136357&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=136602&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=136603&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=136925&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=13692&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=14094&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=14148&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=14216&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=14301&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=14475&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=15012&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=15707&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=15963&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=16012&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=16074&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=16078&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=17388&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=19338&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=19405&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=20197&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=20203&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=20675&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=21007&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=21780&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=21807&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=22155&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=22159&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=22204&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=23805&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=24765&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=24766&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=24772&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=24775&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=24780&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=24784&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=25043&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=25093&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=25200&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=25568&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=26384&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=26848&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=27214&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=27534&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=27553&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=29489&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=29506&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=29507&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=29796&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=29805&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=30617&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=30624&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=30638&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=30660&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=30663&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=30705&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=30843&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=31010&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=31012&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=31442&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=31450&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=31596&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=31986&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=33899&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=36791&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=39522&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=39732&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=40613&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=40918&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=41320&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=42047&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=42595&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=42781&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=43054&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=43059&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=44322&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=44623&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=44845&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=45480&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=46352&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=47752&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=48197&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=48250&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=48477&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=48880&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=49404&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=49934&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=49935&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=50370&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=52884&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=53804&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=54701&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55011&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55021&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55030&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55075&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55076&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55081&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55082&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55083&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55084&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55090&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55092&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55114&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55367&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55370&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55372&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55730&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55844&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55849&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55860&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=55887&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=56559&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=56654&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=56750&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=56754&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=56781&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=56784&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=56797&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=56799&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=56802&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=56877&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=57168&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=57762&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=58122&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=61764&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=62516&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=63115&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=64088&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=64739&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=65653&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=65918&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=66521&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=66531&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=67655&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68349&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68688&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68692&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68699&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68700&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68718&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68724&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68730&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68735&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68807&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68821&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68906&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68916&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68925&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68926&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=68940&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=69798&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=70532&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=70729&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=70732&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=70746&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=70754&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=70756&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=70792&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=70807&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=70812&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71318&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71321&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71323&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71326&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71356&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71361&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71395&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71404&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71407&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71415&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71421&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71422&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=71701&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=74624&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=75402&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=75405&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=75408&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=75416&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=76289&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=76300&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=77221&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=77226&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=77431&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=77516&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=77957&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=77960&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=77964&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=77972&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=78376&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=78381&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=78384&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=78385&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=79263&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=79331&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=79333&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=80007&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=80166&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=80445&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=81419&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=82435&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=82440&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=82460&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=82481&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=82483&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=82486&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=82496&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=82511&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=82571&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=83698&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=83707&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=83712&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=83715&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=83727&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=83729&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=83756&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=83759&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=83761&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=83844&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=83983&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=84547&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=85652&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=86196&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=86211&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=86232&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=86252&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=87649&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=87849&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=88074&size=18cm_x1&angle=1#/
|
||||
https://mp.suwa3d.com/preview.html?platform=1&id=98894&size=18cm_x1&angle=1#/
|
||||
Reference in new issue
Block a user