Pipeline: rewrite cad importer for freecadcmd, freecad part
This commit is contained in:
parent
64b80be028
commit
80e3c58913
22 changed files with 490 additions and 348 deletions
|
@ -1,9 +1,8 @@
|
||||||
## import-fcstd
|
## import_cad
|
||||||
### Импорт .FCStd сцены FreeCAD в Blender.
|
### Импорт CAD сцены в Blender.
|
||||||
|
|
||||||
Представлен в качестве модуля `import_cad_objects` c подмодулями.
|
Восстанавливает из JSON словаря Bledner сцену сохраняя иерархию и свойства объектов.
|
||||||
По умолчанию, импортирует из FreeCAD cцены все видимые solid объекты в Blender сцену.
|
Задает имена mesh объектов на основе solid объектов.
|
||||||
Задает имена mesh на основе solid объектов.
|
|
||||||
Импортирует локальные координаты и задает их mesh объектам.
|
Импортирует локальные координаты и задает их mesh объектам.
|
||||||
Импортирует FEM материалы (если они есть) и задает их mesh объектам.
|
Импортирует FEM материалы (если они есть) и задает их mesh объектам.
|
||||||
Может работать как в качестве модуля, так и внутри blender сцены.
|
Может работать как в качестве модуля, так и внутри blender сцены.
|
146
cg/blender/import_cad/build_blender_scene.py
Normal file
146
cg/blender/import_cad/build_blender_scene.py
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Original code by (C) 2019 yorikvanhavre <yorik@uncreated.net>
|
||||||
|
# Copyright (C) 2023 Ilia Kurochkin <brothermechanic@gmail.com>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
'''
|
||||||
|
DESCRIPTION.
|
||||||
|
- Build Blender scene from JSON data.
|
||||||
|
- Setup hierarchy.
|
||||||
|
- Setup materials.
|
||||||
|
- Setup LCS points.
|
||||||
|
- Apply Bledner scene transforms.
|
||||||
|
'''
|
||||||
|
__version__ = '0.1'
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
import bpy
|
||||||
|
from blender.utils.object_transforms import apply_transforms
|
||||||
|
from blender.import_cad.import_hierarchy import (fc_placement,
|
||||||
|
hierarchy)
|
||||||
|
from blender.import_cad.import_materials import (assign_materials,
|
||||||
|
assign_black)
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
# COLLECTIONS NAMIG CONVENTION
|
||||||
|
part_col_name = 'Part'
|
||||||
|
lcs_col_name = 'LCS'
|
||||||
|
hierarchy_col_name = 'Hierarchy'
|
||||||
|
lowpoly_col_name = 'Lowpoly Parts'
|
||||||
|
# LCS POINT'S SUFFIXES CONVENTION
|
||||||
|
inlet = '_in'
|
||||||
|
outlet = '_out'
|
||||||
|
root = '_root'
|
||||||
|
# CG ASSETS SUFFIXES CONVENTION
|
||||||
|
hightpoly = '_hp'
|
||||||
|
lowpoly = '_lp'
|
||||||
|
render = '_render'
|
||||||
|
|
||||||
|
scene_scale = 0.001
|
||||||
|
blackbody_mat_name = 'Robossembler_Black_Body'
|
||||||
|
|
||||||
|
|
||||||
|
def json_to_blend(js_data):
|
||||||
|
''' Reads JSON data and creates Blender scene '''
|
||||||
|
|
||||||
|
part_collection = bpy.data.collections.new(part_col_name)
|
||||||
|
bpy.context.scene.collection.children.link(part_collection)
|
||||||
|
|
||||||
|
lcs_collection = bpy.data.collections.new(lcs_col_name)
|
||||||
|
bpy.context.scene.collection.children.link(lcs_collection)
|
||||||
|
|
||||||
|
hierarchy_collection = bpy.data.collections.new(hierarchy_col_name)
|
||||||
|
bpy.context.scene.collection.children.link(hierarchy_collection)
|
||||||
|
|
||||||
|
fc_file = list(js_data.keys())[0]
|
||||||
|
|
||||||
|
bobjs = []
|
||||||
|
bobjs_for_render = []
|
||||||
|
|
||||||
|
for js_obj in js_data[fc_file]:
|
||||||
|
bobj = None
|
||||||
|
|
||||||
|
if js_data[fc_file][js_obj]['type'] == 'LCS':
|
||||||
|
bobj = bpy.data.objects.new(js_obj, None)
|
||||||
|
bobj.empty_display_type = 'ARROWS'
|
||||||
|
bobj.empty_display_size = round(random.uniform(0.05, 0.15), 3)
|
||||||
|
bobj.show_in_front = True
|
||||||
|
lcs_collection.objects.link(bobj)
|
||||||
|
|
||||||
|
elif js_data[fc_file][js_obj]['type'] == 'PART':
|
||||||
|
if js_data[fc_file][js_obj].get('mesh'):
|
||||||
|
verts = js_data[fc_file][js_obj]['mesh'][0]
|
||||||
|
edges = []
|
||||||
|
faces = js_data[fc_file][js_obj]['mesh'][1]
|
||||||
|
|
||||||
|
# create blender object data
|
||||||
|
bmesh = bpy.data.meshes.new(name=js_obj)
|
||||||
|
bmesh.from_pydata(verts, edges, faces)
|
||||||
|
bmesh.update()
|
||||||
|
bobj = bpy.data.objects.new(js_obj, bmesh)
|
||||||
|
part_collection.objects.link(bobj)
|
||||||
|
else:
|
||||||
|
logger.info('%s has not mesh data!', js_obj)
|
||||||
|
|
||||||
|
if bobj:
|
||||||
|
fc_placement(bobj,
|
||||||
|
js_data[fc_file][js_obj]['fc_location'],
|
||||||
|
js_data[fc_file][js_obj]['fc_rotation'],
|
||||||
|
scene_scale)
|
||||||
|
if bobj.type == 'MESH':
|
||||||
|
bobj.scale = (scene_scale, scene_scale, scene_scale)
|
||||||
|
apply_transforms(bobj, scale=True)
|
||||||
|
|
||||||
|
# construct assembly hierarchy
|
||||||
|
hierarchy_objs = hierarchy(bobj,
|
||||||
|
js_data[fc_file][js_obj]['hierarchy'],
|
||||||
|
scene_scale)
|
||||||
|
for hierarchy_obj in hierarchy_objs:
|
||||||
|
hierarchy_collection.objects.link(hierarchy_obj)
|
||||||
|
|
||||||
|
# one material for the whole object
|
||||||
|
if bobj.type == 'MESH':
|
||||||
|
if js_data[fc_file][js_obj].get('material'):
|
||||||
|
fem_mat = js_data[fc_file][js_obj]['material']
|
||||||
|
assign_materials(bobj, fem_mat)
|
||||||
|
bobjs_for_render.append(bobj)
|
||||||
|
else:
|
||||||
|
assign_black(bobj)
|
||||||
|
|
||||||
|
bobjs.append(bobj)
|
||||||
|
|
||||||
|
# losted root lcs inlet workaround
|
||||||
|
lcs_objects = lcs_collection.objects
|
||||||
|
if lcs_objects:
|
||||||
|
root_lcs = [lcs for lcs in lcs_objects if lcs.name.endswith(root)]
|
||||||
|
if root_lcs:
|
||||||
|
root_lcs = root_lcs[0]
|
||||||
|
root_inlet_name = '{}{}'.format(root_lcs.name.split(root)[0], inlet)
|
||||||
|
if not bpy.data.objects.get(root_inlet_name):
|
||||||
|
root_inlet = bpy.data.objects.new(root_inlet_name, None)
|
||||||
|
root_inlet.empty_display_type = 'ARROWS'
|
||||||
|
root_inlet.empty_display_size = 0.1
|
||||||
|
root_inlet.show_in_front = True
|
||||||
|
root_inlet.location = root_lcs.location
|
||||||
|
root_inlet.rotation_euler = root_lcs.rotation_euler
|
||||||
|
root_inlet.parent = root_lcs.parent
|
||||||
|
lcs_collection.objects.link(root_inlet)
|
||||||
|
else:
|
||||||
|
logger.info('Lost root LCS object!')
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
# update do not dork
|
||||||
|
logger.info('Imported %s objects without errors', len(bobjs))
|
||||||
|
return bobjs_for_render
|
|
@ -14,6 +14,9 @@
|
||||||
'''
|
'''
|
||||||
DESCRIPTION.
|
DESCRIPTION.
|
||||||
Import from json exported FreeCAD's asm4 coordinates as Blender's empty object.
|
Import from json exported FreeCAD's asm4 coordinates as Blender's empty object.
|
||||||
|
|
||||||
|
DEPRECATED
|
||||||
|
|
||||||
'''
|
'''
|
||||||
__version__ = '0.2'
|
__version__ = '0.2'
|
||||||
|
|
66
cg/blender/import_cad/import_hierarchy.py
Normal file
66
cg/blender/import_cad/import_hierarchy.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2023 Ilia Kurochkin <brothermechanic@gmail.com>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
'''
|
||||||
|
DESCRIPTION.
|
||||||
|
Collecting all parents and reconstruct this hierarhy in bledner.
|
||||||
|
'''
|
||||||
|
__version__ = '0.3'
|
||||||
|
import logging
|
||||||
|
import bpy
|
||||||
|
from mathutils import Vector
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
|
def fc_placement(bobj, fc_location, fc_rotation, scene_scale):
|
||||||
|
''' Prepare FreeCAD's Placement and Quaternion for Blender '''
|
||||||
|
bobj.location = Vector(fc_location) * scene_scale
|
||||||
|
m = bobj.rotation_mode
|
||||||
|
bobj.rotation_mode = 'QUATERNION'
|
||||||
|
# FreeCAD Quaternion is XYZW while Blender is WXYZ
|
||||||
|
fc_rotation.insert(0, fc_rotation.pop(3))
|
||||||
|
bobj.rotation_quaternion = (fc_rotation)
|
||||||
|
bobj.rotation_mode = m
|
||||||
|
return bobj
|
||||||
|
|
||||||
|
|
||||||
|
def hierarchy(bobj, hierarchy, scene_scale):
|
||||||
|
''' Blender object, dict, Blender World Scale factor. '''
|
||||||
|
hierarchy_objs = []
|
||||||
|
for parent_name in hierarchy.keys():
|
||||||
|
if bpy.data.objects.get(parent_name):
|
||||||
|
empty = bpy.data.objects[parent_name]
|
||||||
|
else:
|
||||||
|
empty = bpy.data.objects.new(parent_name, None)
|
||||||
|
empty.empty_display_type = 'CUBE'
|
||||||
|
empty.empty_display_size = 0.01
|
||||||
|
fc_placement(empty,
|
||||||
|
hierarchy[parent_name]['fc_location'],
|
||||||
|
hierarchy[parent_name]['fc_rotation'],
|
||||||
|
scene_scale)
|
||||||
|
empty.select_set(False)
|
||||||
|
hierarchy_objs.append(empty)
|
||||||
|
|
||||||
|
if hierarchy[parent_name]['deep_index'] == 0:
|
||||||
|
bobj.parent = empty
|
||||||
|
|
||||||
|
logger.debug('Add parent %s to object %s', bobj.parent.name, bobj.name)
|
||||||
|
|
||||||
|
for parent_name in hierarchy.keys():
|
||||||
|
parent_parenta_name = hierarchy[parent_name]['parent']
|
||||||
|
if parent_parenta_name:
|
||||||
|
bpy.data.objects[parent_name].parent = bpy.data.objects[
|
||||||
|
parent_parenta_name]
|
||||||
|
|
||||||
|
return hierarchy_objs
|
|
@ -10,20 +10,21 @@
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
__version__ = '0.2'
|
__version__ = '0.3'
|
||||||
import logging
|
import logging
|
||||||
import sys
|
|
||||||
import bpy
|
import bpy
|
||||||
from bpy_extras.node_shader_utils import PrincipledBSDFWrapper
|
from bpy_extras.node_shader_utils import PrincipledBSDFWrapper
|
||||||
from utils.shininess_to_roughness import shiny_to_rough
|
from blender.utils.shininess_to_roughness import shiny_to_rough
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
blackbody_mat_name = 'Robossembler_Black_Body'
|
||||||
|
|
||||||
|
|
||||||
def assign_materials(bobj, fem_mat):
|
def assign_materials(bobj, fem_mat):
|
||||||
''' Build Blender shader from FreeCAD's FEM material '''
|
''' Build Blender shader from FreeCAD's FEM material '''
|
||||||
fem_mat_name = fem_mat.Material['Name']
|
fem_mat_name = fem_mat['Name']
|
||||||
|
|
||||||
if fem_mat_name in bpy.data.materials:
|
if fem_mat_name in bpy.data.materials:
|
||||||
# prepare for reimport
|
# prepare for reimport
|
||||||
|
@ -32,37 +33,35 @@ def assign_materials(bobj, fem_mat):
|
||||||
else:
|
else:
|
||||||
bobj.material_slots[0].material = bpy.data.materials[fem_mat_name]
|
bobj.material_slots[0].material = bpy.data.materials[fem_mat_name]
|
||||||
else:
|
else:
|
||||||
if 'DiffuseColor' in fem_mat.Material.keys():
|
if 'DiffuseColor' in fem_mat.keys():
|
||||||
d_col_str = fem_mat.Material['DiffuseColor']
|
d_col_str = fem_mat['DiffuseColor']
|
||||||
d_col4 = tuple(
|
d_col4 = tuple(map(float, d_col_str[1:-1].split(', ')))
|
||||||
map(float, d_col_str[1:-1].split(', ')))
|
|
||||||
d_col = d_col4[:-1]
|
d_col = d_col4[:-1]
|
||||||
else:
|
else:
|
||||||
d_col = (0.5, 0.5, 0.5)
|
d_col = (0.5, 0.5, 0.5)
|
||||||
if 'Father' in fem_mat.Material.keys():
|
if 'Father' in fem_mat.keys():
|
||||||
if fem_mat.Material['Father'] == 'Metal':
|
if fem_mat['Father'] == 'Metal':
|
||||||
me = 1
|
me = 1
|
||||||
else:
|
else:
|
||||||
me = 0
|
me = 0
|
||||||
else:
|
else:
|
||||||
me = 0
|
me = 0
|
||||||
if 'Shininess' in fem_mat.Material.keys():
|
if 'Shininess' in fem_mat.keys():
|
||||||
shiny = float(fem_mat.Material['Shininess'])
|
shiny = float(fem_mat['Shininess'])
|
||||||
if shiny == 0:
|
if shiny == 0:
|
||||||
rg = 0.5
|
rg = 0.5
|
||||||
else:
|
else:
|
||||||
rg = shiny_to_rough(shiny)
|
rg = shiny_to_rough(shiny)
|
||||||
else:
|
else:
|
||||||
rg = 0.5
|
rg = 0.5
|
||||||
if 'EmissiveColor' in fem_mat.Material.keys():
|
if 'EmissiveColor' in fem_mat.keys():
|
||||||
e_col_str = fem_mat.Material['EmissiveColor']
|
e_col_str = fem_mat['EmissiveColor']
|
||||||
e_col4 = tuple(
|
e_col4 = tuple(map(float, e_col_str[1:-1].split(', ')))
|
||||||
map(float, e_col_str[1:-1].split(', ')))
|
|
||||||
e_col = e_col4[:-1]
|
e_col = e_col4[:-1]
|
||||||
else:
|
else:
|
||||||
e_col = (0.0, 0.0, 0.0)
|
e_col = (0.0, 0.0, 0.0)
|
||||||
if 'Transparency' in fem_mat.Material.keys():
|
if 'Transparency' in fem_mat.keys():
|
||||||
tr_str = fem_mat.Material['Transparency']
|
tr_str = fem_mat['Transparency']
|
||||||
alpha = 1.0 - float(tr_str)
|
alpha = 1.0 - float(tr_str)
|
||||||
else:
|
else:
|
||||||
alpha = 1.0
|
alpha = 1.0
|
||||||
|
@ -86,11 +85,11 @@ def assign_materials(bobj, fem_mat):
|
||||||
|
|
||||||
|
|
||||||
def assign_black(bobj):
|
def assign_black(bobj):
|
||||||
''' Set absolute black Blender shader '''
|
''' Set absolute black body shader '''
|
||||||
fem_mat_name = 'black_mat'
|
fem_mat_name = blackbody_mat_name
|
||||||
|
|
||||||
if fem_mat_name in bpy.data.materials:
|
if fem_mat_name in bpy.data.materials:
|
||||||
# prepare for reimport
|
# prepare for reimport TODO
|
||||||
if len(bobj.material_slots) < 1:
|
if len(bobj.material_slots) < 1:
|
||||||
bobj.data.materials.append(bpy.data.materials[fem_mat_name])
|
bobj.data.materials.append(bpy.data.materials[fem_mat_name])
|
||||||
else:
|
else:
|
||||||
|
@ -99,14 +98,10 @@ def assign_black(bobj):
|
||||||
bmat = bpy.data.materials.new(name=fem_mat_name)
|
bmat = bpy.data.materials.new(name=fem_mat_name)
|
||||||
bmat.use_nodes = True
|
bmat.use_nodes = True
|
||||||
bmat.diffuse_color = (0, 0, 0, 1)
|
bmat.diffuse_color = (0, 0, 0, 1)
|
||||||
bmat.node_tree.nodes.remove(bmat.node_tree.nodes['Principled BSDF'])
|
principled = bmat.node_tree.nodes['Principled BSDF']
|
||||||
emission = bmat.node_tree.nodes.new(type='ShaderNodeEmission')
|
principled.inputs['Base Color'].default_value = (0, 0, 0, 1)
|
||||||
emission.location = 0, 300
|
principled.inputs['Specular'].default_value = 0.0
|
||||||
emission.inputs['Color'].default_value = (0, 0, 0, 1)
|
principled.inputs['Roughness'].default_value = 1.0
|
||||||
emission.inputs['Strength'].default_value = 0
|
|
||||||
bmat.node_tree.links.new(
|
|
||||||
emission.outputs['Emission'],
|
|
||||||
bmat.node_tree.nodes['Material Output'].inputs['Surface'])
|
|
||||||
# prepare for reimport
|
# prepare for reimport
|
||||||
if len(bobj.material_slots) < 1:
|
if len(bobj.material_slots) < 1:
|
||||||
bobj.data.materials.append(bmat)
|
bobj.data.materials.append(bmat)
|
|
@ -22,9 +22,9 @@ import math
|
||||||
import bpy
|
import bpy
|
||||||
from mathutils import Matrix
|
from mathutils import Matrix
|
||||||
|
|
||||||
from utils.object_relations import (parenting,
|
from blender.utils.object_relations import (parenting,
|
||||||
unparenting)
|
unparenting)
|
||||||
from utils.object_transforms import round_transforms
|
from blender.utils.object_transforms import round_transforms
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
|
@ -1,217 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Original code by (C) 2019 yorikvanhavre <yorik@uncreated.net>
|
|
||||||
# Copyright (C) 2023 Ilia Kurochkin <brothermechanic@gmail.com>
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
'''
|
|
||||||
DESCRIPTION.
|
|
||||||
Main module:
|
|
||||||
- Reads a FreeCAD .FCStd file.
|
|
||||||
- Set tesselation parts to mesh.
|
|
||||||
- Inport meshes in Blender scene.
|
|
||||||
- Setup hierarchy.
|
|
||||||
- Setup materials.
|
|
||||||
- Setup LCS points.
|
|
||||||
- Apply FreeCAD to Bledner scene transforms.
|
|
||||||
'''
|
|
||||||
__version__ = '0.3'
|
|
||||||
import time
|
|
||||||
import FreeCAD
|
|
||||||
import Part, Mesh, MeshPart
|
|
||||||
import logging
|
|
||||||
import math
|
|
||||||
import xml
|
|
||||||
import sys
|
|
||||||
import xml.sax
|
|
||||||
import zipfile
|
|
||||||
import os
|
|
||||||
import random
|
|
||||||
import bpy
|
|
||||||
from import_fcstd.import_hierarchy import (hierarchy,
|
|
||||||
placement)
|
|
||||||
from import_fcstd.import_materials import (assign_materials,
|
|
||||||
assign_black)
|
|
||||||
from import_fcstd.is_object_solid import is_object_solid
|
|
||||||
from utils.object_transforms import apply_transforms
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
|
||||||
|
|
||||||
|
|
||||||
# COLLECTIONS NAMIG CONVENTION
|
|
||||||
parts_col_name = 'Import Parts'
|
|
||||||
lcs_col_name = 'Import LCS'
|
|
||||||
hierarchy_col_name = 'Import Hierarchy'
|
|
||||||
lowpoly_col_name = 'Lowpoly Parts'
|
|
||||||
# LCS POINT'S SUFFIXES CONVENTION
|
|
||||||
inlet = '_in'
|
|
||||||
outlet = '_out'
|
|
||||||
root = '_root'
|
|
||||||
# CG ASSETS SUFFIXES CONVENTION
|
|
||||||
hightpoly = '_hp'
|
|
||||||
lowpoly = '_lp'
|
|
||||||
render = '_render'
|
|
||||||
|
|
||||||
|
|
||||||
def obj_importer(filename,
|
|
||||||
tesselation_method='Standard',
|
|
||||||
linear_deflection=0.1,
|
|
||||||
angular_deflection=30.0,
|
|
||||||
fem_size=5.0,
|
|
||||||
update=False,
|
|
||||||
scene_placement=True,
|
|
||||||
skiphidden=True,
|
|
||||||
scale=0.001,
|
|
||||||
select=True,
|
|
||||||
nonsolid_property='Robossembler_NonSolid'):
|
|
||||||
|
|
||||||
''' Reads a FreeCAD .FCStd file and creates Blender objects '''
|
|
||||||
|
|
||||||
doc = FreeCAD.open(filename)
|
|
||||||
docname = doc.Name
|
|
||||||
|
|
||||||
if not update:
|
|
||||||
parts_collection = bpy.data.collections.new(parts_col_name)
|
|
||||||
bpy.context.scene.collection.children.link(parts_collection)
|
|
||||||
|
|
||||||
lcs_collection = bpy.data.collections.new(lcs_col_name)
|
|
||||||
bpy.context.scene.collection.children.link(lcs_collection)
|
|
||||||
|
|
||||||
hierarchy_collection = bpy.data.collections.new(hierarchy_col_name)
|
|
||||||
bpy.context.scene.collection.children.link(hierarchy_collection)
|
|
||||||
|
|
||||||
# collect all materials
|
|
||||||
fem_mats = []
|
|
||||||
for fem_mat in doc.Objects:
|
|
||||||
if fem_mat.isDerivedFrom('App::MaterialObjectPython'):
|
|
||||||
fem_mats.append(fem_mat)
|
|
||||||
bobjs = []
|
|
||||||
bobjs_for_render = []
|
|
||||||
for obj in doc.Objects:
|
|
||||||
bobj = None
|
|
||||||
if skiphidden:
|
|
||||||
if not obj.Visibility:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if obj.isDerivedFrom('PartDesign::CoordinateSystem'):
|
|
||||||
if update:
|
|
||||||
# locate existing object
|
|
||||||
for o in bpy.data.objects:
|
|
||||||
if o.name == obj.Label:
|
|
||||||
bobj = o
|
|
||||||
logger.debug('Replacing existing %s', obj.Label)
|
|
||||||
else:
|
|
||||||
bobj = bpy.data.objects.new(obj.Label, None)
|
|
||||||
bobj.empty_display_type = 'ARROWS'
|
|
||||||
bobj.empty_display_size = round(random.uniform(0.05, 0.15), 3)
|
|
||||||
bobj.show_in_front = True
|
|
||||||
lcs_collection.objects.link(bobj)
|
|
||||||
|
|
||||||
elif obj.isDerivedFrom('Part::Feature'):
|
|
||||||
# filter for nonsolids
|
|
||||||
if is_object_solid(obj) or hasattr(obj, nonsolid_property):
|
|
||||||
verts = []
|
|
||||||
edges = []
|
|
||||||
faces = []
|
|
||||||
# create mesh from shape
|
|
||||||
shape = obj.Shape
|
|
||||||
if scene_placement:
|
|
||||||
shape = obj.Shape.copy()
|
|
||||||
shape.Placement = obj.Placement.inverse().multiply(shape.Placement)
|
|
||||||
meshfromshape = doc.addObject('Mesh::Feature','Mesh')
|
|
||||||
if tesselation_method == 'Standard':
|
|
||||||
meshfromshape.Mesh = MeshPart.meshFromShape(
|
|
||||||
Shape=shape,
|
|
||||||
LinearDeflection=linear_deflection,
|
|
||||||
AngularDeflection=math.radians(angular_deflection),
|
|
||||||
Relative=False)
|
|
||||||
elif tesselation_method == 'FEM':
|
|
||||||
meshfromshape.Mesh = MeshPart.meshFromShape(
|
|
||||||
Shape=shape,
|
|
||||||
MaxLength=fem_size)
|
|
||||||
else:
|
|
||||||
raise TypeError('Wrong tesselation method! '
|
|
||||||
'Standard and FEM methods are supported only!')
|
|
||||||
break
|
|
||||||
t = meshfromshape.Mesh.Topology
|
|
||||||
verts = [[v.x,v.y,v.z] for v in t[0]]
|
|
||||||
faces = t[1]
|
|
||||||
|
|
||||||
if verts and faces:
|
|
||||||
# create or update object with mesh and material data
|
|
||||||
bmesh = bpy.data.meshes.new(name=obj.Label)
|
|
||||||
bmesh.from_pydata(verts, edges, faces)
|
|
||||||
bmesh.update()
|
|
||||||
if update:
|
|
||||||
# locate existing object
|
|
||||||
for o in bpy.data.objects:
|
|
||||||
if o.name == obj.Label:
|
|
||||||
bobj = o
|
|
||||||
bobj.data = bmesh
|
|
||||||
logger.debug('Replacing existing %s', obj.Label)
|
|
||||||
else:
|
|
||||||
bobj = bpy.data.objects.new(obj.Label, bmesh)
|
|
||||||
parts_collection.objects.link(bobj)
|
|
||||||
|
|
||||||
# skip for other object's types
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if bobj and scene_placement and not update:
|
|
||||||
placement(bobj, obj, scale)
|
|
||||||
if bobj.type == 'MESH':
|
|
||||||
bobj.scale = (scale, scale, scale)
|
|
||||||
apply_transforms(bobj, scale=True)
|
|
||||||
|
|
||||||
# construct assembly hierarchy
|
|
||||||
hierarchy_objs = hierarchy(bobj, obj, scale)
|
|
||||||
for hierarchy_obj in hierarchy_objs:
|
|
||||||
hierarchy_collection.objects.link(hierarchy_obj)
|
|
||||||
|
|
||||||
# one material for the whole object
|
|
||||||
if bobj.type == 'MESH':
|
|
||||||
for fem_mat in fem_mats:
|
|
||||||
for ref in fem_mat.References:
|
|
||||||
if ref[0].Label == bobj.name:
|
|
||||||
assign_materials(bobj, fem_mat)
|
|
||||||
bobjs_for_render.append(bobj)
|
|
||||||
continue
|
|
||||||
# looks like this is hidden internal object
|
|
||||||
if not bobj.material_slots:
|
|
||||||
assign_black(bobj)
|
|
||||||
|
|
||||||
# optional select object after importing
|
|
||||||
if bobj and select:
|
|
||||||
bpy.context.view_layer.objects.active = bobj
|
|
||||||
bobj.select_set(True)
|
|
||||||
|
|
||||||
bobjs.append(bobj)
|
|
||||||
|
|
||||||
# losted root lcs inlet workaround
|
|
||||||
lcs_objects = lcs_collection.objects
|
|
||||||
if lcs_objects:
|
|
||||||
root_lcs = [lcs for lcs in lcs_objects if lcs.name.endswith(root)][0]
|
|
||||||
root_inlet_name = ('{}{}'.format(root_lcs.name.split(root)[0], inlet))
|
|
||||||
if not bpy.data.objects.get(root_inlet_name):
|
|
||||||
root_inlet = bpy.data.objects.new(root_inlet_name, None)
|
|
||||||
root_inlet.empty_display_type = 'ARROWS'
|
|
||||||
root_inlet.empty_display_size = 0.1
|
|
||||||
root_inlet.show_in_front = True
|
|
||||||
root_inlet.location = root_lcs.location
|
|
||||||
root_inlet.rotation_euler = root_lcs.rotation_euler
|
|
||||||
root_inlet.parent = root_lcs.parent
|
|
||||||
lcs_collection.objects.link(root_inlet)
|
|
||||||
|
|
||||||
FreeCAD.closeDocument(docname)
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
# update do not dork
|
|
||||||
logger.info('Imported %s objects without errors', len(bobjs))
|
|
||||||
return bobjs_for_render
|
|
|
@ -1,60 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (C) 2023 Ilia Kurochkin <brothermechanic@gmail.com>
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
'''
|
|
||||||
DESCRIPTION.
|
|
||||||
Collecting all parents and reconstruct this hierarhy in bledner.
|
|
||||||
'''
|
|
||||||
__version__ = '0.2'
|
|
||||||
import logging
|
|
||||||
import bpy
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
|
||||||
|
|
||||||
|
|
||||||
def placement(bobj, obj, scale):
|
|
||||||
''' blender object, freecad object, scale factor '''
|
|
||||||
bobj.location = obj.Placement.Base.multiply(scale)
|
|
||||||
m = bobj.rotation_mode
|
|
||||||
bobj.rotation_mode = 'QUATERNION'
|
|
||||||
if obj.Placement.Rotation.Angle:
|
|
||||||
# FreeCAD Quaternion is XYZW while Blender is WXYZ
|
|
||||||
q = (obj.Placement.Rotation.Q[3],)+obj.Placement.Rotation.Q[:3]
|
|
||||||
bobj.rotation_quaternion = (q)
|
|
||||||
bobj.rotation_mode = m
|
|
||||||
return bobj
|
|
||||||
|
|
||||||
|
|
||||||
def hierarchy(bobj, obj, scale):
|
|
||||||
''' blender object, freecad object, scale factor '''
|
|
||||||
obj_parent = obj.getParentGeoFeatureGroup()
|
|
||||||
obj_child_name = None
|
|
||||||
parents = []
|
|
||||||
while obj_parent:
|
|
||||||
if bpy.data.objects.get(obj_parent.Label):
|
|
||||||
empty = bpy.data.objects[obj_parent.Label]
|
|
||||||
else:
|
|
||||||
empty = bpy.data.objects.new(obj_parent.Label, None)
|
|
||||||
empty.empty_display_type = 'CUBE'
|
|
||||||
empty.empty_display_size = 0.01
|
|
||||||
placement(empty, obj_parent, scale)
|
|
||||||
parents.append(empty)
|
|
||||||
if not bobj.parent:
|
|
||||||
bobj.parent = empty
|
|
||||||
else:
|
|
||||||
bpy.data.objects[obj_child_name].parent = empty
|
|
||||||
obj_child_name = obj_parent.Label
|
|
||||||
obj_parent = obj_parent.getParentGeoFeatureGroup()
|
|
||||||
empty.select_set(False)
|
|
||||||
logger.debug('Add parent %s to object %s', empty.name, bobj.name)
|
|
||||||
return parents
|
|
|
@ -21,9 +21,9 @@ import sys
|
||||||
import bpy
|
import bpy
|
||||||
import math
|
import math
|
||||||
|
|
||||||
from utils.generative_modifiers import shell_remesher
|
from blender.utils.generative_modifiers import shell_remesher
|
||||||
from utils.object_converter import convert_mesh_to_mesh
|
from blender.utils.object_converter import convert_mesh_to_mesh
|
||||||
from utils.object_relations import parenting
|
from blender.utils.object_relations import parenting
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
|
@ -29,20 +29,23 @@ def apply_transforms(obj, location=False, rotation=False, scale=False):
|
||||||
return rotation.to_matrix().to_4x4()
|
return rotation.to_matrix().to_4x4()
|
||||||
|
|
||||||
def get_sca_matrix(scale):
|
def get_sca_matrix(scale):
|
||||||
scale_martix = Matrix()
|
scene_scale_martix = Matrix()
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
scale_martix[i][i] = scale[i]
|
scene_scale_martix[i][i] = scale[i]
|
||||||
return scale_martix
|
return scene_scale_martix
|
||||||
|
|
||||||
if location and rotation and scale:
|
if location and rotation and scale:
|
||||||
loc, rot, sca = obj.matrix_world.decompose()
|
loc, rot, sca = obj.matrix_world.decompose()
|
||||||
mesh_martix = get_loc_matrix(loc) @ get_rot_matrix(rot) @ get_sca_matrix(sca)
|
mesh_martix = get_loc_matrix(loc) @ get_rot_matrix(
|
||||||
|
rot) @ get_sca_matrix(sca)
|
||||||
obj.data.transform(mesh_martix)
|
obj.data.transform(mesh_martix)
|
||||||
apply_matrix = get_loc_matrix(Vector.Fill(3, 0)) @ get_rot_matrix(Quaternion()) @ get_sca_matrix(Vector.Fill(3, 1))
|
apply_matrix = get_loc_matrix(Vector.Fill(3, 0)) @ get_rot_matrix(Quaternion()) @ get_sca_matrix(Vector.Fill(3, 1))
|
||||||
obj.matrix_world = apply_matrix
|
obj.matrix_world = apply_matrix
|
||||||
else:
|
else:
|
||||||
if location:
|
if location:
|
||||||
raise Exception('Location only applies with all transformations (rotate and scale) together!')
|
raise Exception(
|
||||||
|
'Location only applies with all transformations (rotate and scale) together!'
|
||||||
|
)
|
||||||
if rotation:
|
if rotation:
|
||||||
loc, rot, sca = obj.matrix_world.decompose()
|
loc, rot, sca = obj.matrix_world.decompose()
|
||||||
mesh_martix = get_rot_matrix(rot)
|
mesh_martix = get_rot_matrix(rot)
|
||||||
|
|
|
@ -4,6 +4,9 @@ DESCRIPTION.
|
||||||
Subassembly models setup from SDFormat file.
|
Subassembly models setup from SDFormat file.
|
||||||
Script selected subassembly models.
|
Script selected subassembly models.
|
||||||
Support Blender compiled as a Python Module only!
|
Support Blender compiled as a Python Module only!
|
||||||
|
|
||||||
|
DEPRECATED
|
||||||
|
|
||||||
"""
|
"""
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,6 @@ def shiny_to_rough(shininess):
|
||||||
''' convert shiny to roughness '''
|
''' convert shiny to roughness '''
|
||||||
a, b = -1.0, 2.0
|
a, b = -1.0, 2.0
|
||||||
c = (shininess / 100.0) - 1.0
|
c = (shininess / 100.0) - 1.0
|
||||||
D = math.pow(b,2) - (4 * a * c)
|
D = math.pow(b, 2) - (4 * a * c)
|
||||||
roughness = (-b + math.sqrt(D)) / (2 * a)
|
roughness = (-b + math.sqrt(D)) / (2 * a)
|
||||||
if roughness < 0:
|
return max(roughness, 0)
|
||||||
roughness = 0
|
|
||||||
return roughness
|
|
||||||
|
|
1
cg/freecad/README.md
Normal file
1
cg/freecad/README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
## Модули проекта Robossembler для FreeCAD
|
5
cg/freecad/__init__.py
Normal file
5
cg/freecad/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
DESCRIPTION.
|
||||||
|
FreeCAD modules for Robosembler project pipeline.
|
||||||
|
"""
|
3
cg/freecad/utils/README.md
Normal file
3
cg/freecad/utils/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
### export_freecad_scene - Экспорт .FCStd сцены FreeCAD в JSON словарь.
|
||||||
|
|
||||||
|
По умолчанию, импортирует из FreeCAD cцены все видимые solid объекты.
|
145
cg/freecad/utils/export_freecad_scene.py
Normal file
145
cg/freecad/utils/export_freecad_scene.py
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2023 Ilia Kurochkin <brothermechanic@gmail.com>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
'''
|
||||||
|
DESCRIPTION.
|
||||||
|
- Reads a FreeCAD .FCStd file.
|
||||||
|
- Set tesselation parts to mesh.
|
||||||
|
- Return scene as JSON dictionary.
|
||||||
|
'''
|
||||||
|
__version__ = '0.1'
|
||||||
|
import json
|
||||||
|
import FreeCAD
|
||||||
|
import Part
|
||||||
|
import Mesh
|
||||||
|
import MeshPart
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
import sys
|
||||||
|
from freecad.utils.is_object_solid import is_object_solid
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
|
def freecad_to_json(filename,
|
||||||
|
tesselation_method='Standard',
|
||||||
|
linear_deflection=0.1,
|
||||||
|
angular_deflection=30.0,
|
||||||
|
fem_size=50.0,
|
||||||
|
skiphidden=True,
|
||||||
|
nonsolid_property='Robossembler_NonSolid'):
|
||||||
|
''' Reads a FreeCAD .FCStd file and return json assembly. '''
|
||||||
|
|
||||||
|
scene = {}
|
||||||
|
js_objs = {}
|
||||||
|
|
||||||
|
doc = FreeCAD.open(filename)
|
||||||
|
docname = doc.Name
|
||||||
|
|
||||||
|
# collect all materials
|
||||||
|
fem_mats = []
|
||||||
|
for fem_mat in doc.Objects:
|
||||||
|
if fem_mat.isDerivedFrom('App::MaterialObjectPython'):
|
||||||
|
fem_mats.append(fem_mat)
|
||||||
|
|
||||||
|
for obj in doc.Objects:
|
||||||
|
js_obj = {}
|
||||||
|
|
||||||
|
if skiphidden:
|
||||||
|
if not obj.Visibility:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if obj.isDerivedFrom('PartDesign::CoordinateSystem'):
|
||||||
|
js_obj['type'] = 'LCS'
|
||||||
|
|
||||||
|
elif obj.isDerivedFrom('Part::Feature'):
|
||||||
|
js_obj['type'] = 'PART'
|
||||||
|
# filter for nonsolids
|
||||||
|
|
||||||
|
if is_object_solid(obj) or hasattr(obj, nonsolid_property):
|
||||||
|
# create mesh from shape
|
||||||
|
shape = obj.Shape
|
||||||
|
shape = obj.Shape.copy()
|
||||||
|
shape.Placement = obj.Placement.inverse().multiply(shape.Placement)
|
||||||
|
meshfromshape = doc.addObject('Mesh::Feature', 'Mesh')
|
||||||
|
if tesselation_method == 'Standard':
|
||||||
|
meshfromshape.Mesh = MeshPart.meshFromShape(
|
||||||
|
Shape=shape,
|
||||||
|
LinearDeflection=linear_deflection,
|
||||||
|
AngularDeflection=math.radians(angular_deflection),
|
||||||
|
Relative=False)
|
||||||
|
elif tesselation_method == 'FEM':
|
||||||
|
meshfromshape.Mesh = MeshPart.meshFromShape(
|
||||||
|
Shape=shape,
|
||||||
|
MaxLength=fem_size)
|
||||||
|
else:
|
||||||
|
raise TypeError('Wrong tesselation method! '
|
||||||
|
'Standard and FEM methods are supported only!')
|
||||||
|
break
|
||||||
|
t = meshfromshape.Mesh.Topology
|
||||||
|
verts = [[v.x, v.y, v.z] for v in t[0]]
|
||||||
|
faces = t[1]
|
||||||
|
js_obj['mesh'] = (verts, faces)
|
||||||
|
|
||||||
|
# one material for the whole object
|
||||||
|
for fem_mat in fem_mats:
|
||||||
|
for ref in fem_mat.References:
|
||||||
|
if ref[0].Label == obj.Label:
|
||||||
|
js_obj['material'] = fem_mat.Material
|
||||||
|
|
||||||
|
# skip for other object's types
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
js_obj['fc_location'] = tuple(obj.Placement.Base)
|
||||||
|
js_obj['fc_rotation'] = obj.Placement.Rotation.Q
|
||||||
|
|
||||||
|
# construct assembly hierarchy
|
||||||
|
obj_parent = obj.getParentGeoFeatureGroup()
|
||||||
|
obj_child_name = None
|
||||||
|
parents = {}
|
||||||
|
deep_index = 0
|
||||||
|
while obj_parent:
|
||||||
|
parent = {}
|
||||||
|
parent['fc_location'] = tuple(obj_parent.Placement.Base)
|
||||||
|
parent['fc_rotation'] = obj_parent.Placement.Rotation.Q
|
||||||
|
obj_child_name = obj_parent.Label
|
||||||
|
obj_parent = obj_parent.getParentGeoFeatureGroup()
|
||||||
|
if obj_parent:
|
||||||
|
parent['parent'] = obj_parent.Label
|
||||||
|
else:
|
||||||
|
parent['parent'] = None
|
||||||
|
parents[obj_child_name] = parent
|
||||||
|
parent['deep_index'] = deep_index
|
||||||
|
deep_index += 1
|
||||||
|
js_obj['hierarchy'] = parents
|
||||||
|
|
||||||
|
js_objs[obj.Label] = js_obj
|
||||||
|
|
||||||
|
FreeCAD.closeDocument(docname)
|
||||||
|
|
||||||
|
scene[filename] = js_objs
|
||||||
|
|
||||||
|
logger.info('Stored %s objects without errors', len(js_objs))
|
||||||
|
|
||||||
|
print(json.dumps(scene))
|
||||||
|
|
||||||
|
|
||||||
|
args = sys.argv[2:]
|
||||||
|
for arg in args[2:5]:
|
||||||
|
args[args.index(arg)] = float(arg)
|
||||||
|
for arg in args[5:6]:
|
||||||
|
args[args.index(arg)] = bool(arg)
|
||||||
|
#for num, item in enumerate(args):
|
||||||
|
# print(num, type(item))
|
||||||
|
freecad_to_json(*args)
|
56
cg/freecad/utils/freecad_cmd.py
Normal file
56
cg/freecad/utils/freecad_cmd.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# coding: utf-8
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# Copyright (C) 2023 Ilia Kurochkin <brothermechanic@gmail.com>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def freecad_proc(*args, **kwargs):
|
||||||
|
command = [
|
||||||
|
args[0],
|
||||||
|
args[1],
|
||||||
|
|
||||||
|
# general property
|
||||||
|
kwargs['filename'],
|
||||||
|
kwargs['tesselation_method'],
|
||||||
|
kwargs['linear_deflection'],
|
||||||
|
kwargs['angular_deflection'],
|
||||||
|
kwargs['fem_size'],
|
||||||
|
kwargs['skiphidden'],
|
||||||
|
kwargs['nonsolid_property'],
|
||||||
|
]
|
||||||
|
|
||||||
|
proc = subprocess.run(command,
|
||||||
|
check=True,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
encoding='utf-8')
|
||||||
|
|
||||||
|
return json.loads(proc.stdout.split('FreeCAD ')[0])
|
||||||
|
|
||||||
|
|
||||||
|
kwargs = {}
|
||||||
|
kwargs['filename'] = '/<path to .FCStd>'
|
||||||
|
kwargs['tesselation_method'] = 'Standard'
|
||||||
|
kwargs['linear_deflection'] = '0.1'
|
||||||
|
kwargs['angular_deflection'] = '30.0'
|
||||||
|
kwargs['fem_size'] = '10.0'
|
||||||
|
kwargs['skiphidden'] = 'True'
|
||||||
|
kwargs['nonsolid_property'] = 'Robossembler_NonSolid'
|
||||||
|
|
||||||
|
js_data = freecad_proc(
|
||||||
|
'freecadcmd',
|
||||||
|
'/<path to>/cg/freecad/utils/export_freecad_scene.py',
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
print(js_data)
|
|
@ -1,18 +1,15 @@
|
||||||
### freecad_to_asset.py
|
### freecad_to_asset.py
|
||||||
|
|
||||||
Пакетное производство 3д ассетов из объектов сцены Freecad
|
Пакетное производство 3д ассетов из объектов CAD сцены.
|
||||||
|
|
||||||
Поддерживается работа поверх Blender в качестве модуля!
|
Поддерживается работа поверх Blender в качестве модуля!
|
||||||
|
|
||||||
Сценарий производит:
|
Сценарий производит:
|
||||||
- импорт solid объектов в Blender сцену
|
- экспорт CAD сцены
|
||||||
|
- восстановление Blender сцены
|
||||||
- имена solid объектов в mesh объекты
|
- имена solid объектов в mesh объекты
|
||||||
- тесселяцию solid объектов с заданным уровнем
|
- тесселяцию solid объектов с заданным уровнем
|
||||||
- ретопологию mesh объектов с заданным уровнем
|
- ретопологию mesh объектов с заданным уровнем
|
||||||
- обработку mesh объектов для использования в качестве ассетов
|
- обработку mesh объектов для использования в качестве ассетов
|
||||||
- импорт FEM материалов и назначение их для mesh объектов
|
- импорт FEM материалов и назначение их для mesh объектов
|
||||||
- экспорт mesh объектов в требуемые форматы
|
- экспорт mesh объектов в требуемые форматы
|
||||||
|
|
||||||
### blender_opts.py
|
|
||||||
|
|
||||||
Различные операции в Bledner
|
|
||||||
|
|
|
@ -4,20 +4,22 @@ DESCRIPTION.
|
||||||
Convert and setup FreeCAD solid objects to 3d assets mesh files.
|
Convert and setup FreeCAD solid objects to 3d assets mesh files.
|
||||||
Support Blender compiled as a Python Module only!
|
Support Blender compiled as a Python Module only!
|
||||||
'''
|
'''
|
||||||
__version__ = '0.4'
|
__version__ = '0.5'
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
sys.path.append('../blender/')
|
sys.path.append('../')
|
||||||
from import_fcstd.import_cad_objects import obj_importer
|
from freecad.utils.export_freecad_scene import freecad_to_json
|
||||||
from import_fcstd.restruct_hierarchy_by_lcs import restruct_hierarchy
|
|
||||||
from import_fcstd.import_coordinate_point import lcs_json_importer
|
from blender.import_cad.build_blender_scene import json_to_blend
|
||||||
from utils.remove_collections import remove_collections
|
from blender.import_cad.restruct_hierarchy_by_lcs import restruct_hierarchy
|
||||||
from utils.cleanup_orphan_data import cleanup_orphan_data
|
from blender.import_cad.import_coordinate_point import lcs_json_importer
|
||||||
from utils.sdf_mesh_selector import sdf_mesh_selector
|
from blender.utils.remove_collections import remove_collections
|
||||||
from remesh.highpoly_setup import setup_meshes
|
from blender.utils.cleanup_orphan_data import cleanup_orphan_data
|
||||||
from remesh.lowpoly_setup import parts_to_shells
|
from blender.utils.sdf_mesh_selector import sdf_mesh_selector
|
||||||
|
from blender.remesh.highpoly_setup import setup_meshes
|
||||||
|
from blender.remesh.lowpoly_setup import parts_to_shells
|
||||||
|
|
||||||
from export.dae import export_dae
|
from export.dae import export_dae
|
||||||
from export.collision import export_col_stl
|
from export.collision import export_col_stl
|
||||||
|
@ -75,12 +77,9 @@ def freecad_asset_pipeline(fcstd_path,
|
||||||
remove_collections()
|
remove_collections()
|
||||||
cleanup_orphan_data()
|
cleanup_orphan_data()
|
||||||
|
|
||||||
# import objects
|
## convert FreeCAD scene to Blender scene
|
||||||
objs_for_render = obj_importer(fcstd_path,
|
# TODO
|
||||||
tesselation_method,
|
objs_for_render = json_to_blend(freecad_to_json(**kwargs))
|
||||||
linear_deflection,
|
|
||||||
angular_deflection,
|
|
||||||
fem_size)
|
|
||||||
|
|
||||||
# restructuring hierarchy by lcs points
|
# restructuring hierarchy by lcs points
|
||||||
lcs_objects = restruct_hierarchy()
|
lcs_objects = restruct_hierarchy()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue