Импорт фрейма (объекта локальных осей) в качестве локальной матрицы
This commit is contained in:
parent
16f6f375b5
commit
5208577d86
9 changed files with 122 additions and 76 deletions
|
@ -1,13 +1,14 @@
|
|||
## import-fcstd
|
||||
### Импорт .FCStd сцены FreeCAD в Blender.
|
||||
|
||||
Автор оригинальной версии сценария yorikvanhavre: [github](https://gist.github.com/yorikvanhavre/680156f59e2b42df8f5f5391cae2660b)
|
||||
|
||||
Представлен в качестве модуля `importer.py` c подмодулями.
|
||||
Импортирует все видимые solid объекты в качестве mesh в Blender сцену.
|
||||
Представлен в качестве модуля `import_cad_objects` c подмодулями.
|
||||
По умолчанию, импортирует из FreeCAD cцены все видимые solid объекты в Blender сцену.
|
||||
Задает имена mesh на основе solid объектов.
|
||||
Импортирует локальные координаты и задает их mesh объектам.
|
||||
Импортирует FEM материалы (если они есть) и задает их mesh объектам.
|
||||
Может работать как в качестве модуля, так и внутри blender сцены.
|
||||
|
||||
Так же пресдставлен модуль импорта точек координат `import_coordinate_point`.
|
||||
Модуль может использоваться для передачи осей координат и позиций из FreeCAD.
|
||||
Например локальные координаты подобъекта плоскости, точки или задание локального нуля.
|
||||
|
||||
|
|
|
@ -1,14 +1,2 @@
|
|||
# -*- 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.
|
||||
__version__ = "0.1"
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
# 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 time
|
||||
import FreeCAD
|
||||
import logging
|
||||
import xml
|
||||
|
@ -29,10 +29,10 @@ logger = logging.getLogger(__name__)
|
|||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
def importer(filename,
|
||||
def obj_importer(filename,
|
||||
tessellation,
|
||||
update=False,
|
||||
placement=True,
|
||||
tessellation=10.0,
|
||||
skiphidden=True,
|
||||
scale=0.001,
|
||||
select=True,
|
||||
|
@ -40,33 +40,8 @@ def importer(filename,
|
|||
|
||||
"""Reads a FreeCAD .FCStd file and creates Blender objects"""
|
||||
|
||||
#path = '/usr/lib64/freecad/lib64'
|
||||
TRIANGULATE = False # set to True to triangulate all faces (will loose multimaterial info)
|
||||
TRIANGULATE = True # set to True to triangulate all faces (will loose multimaterial info)
|
||||
|
||||
'''
|
||||
try:
|
||||
# append the FreeCAD path specified in addon preferences
|
||||
user_preferences = bpy.context.preferences
|
||||
addon_prefs = user_preferences.addons[__name__].preferences
|
||||
path = addon_prefs.filepath
|
||||
if path:
|
||||
if os.path.isfile(path):
|
||||
path = os.path.dirname(path)
|
||||
logger.debug("Configured FreeCAD path:", path)
|
||||
sys.path.append(path)
|
||||
else:
|
||||
logger.debug("FreeCAD path is not configured in preferences")
|
||||
import FreeCAD
|
||||
except:
|
||||
logger.debug("Unable to import the FreeCAD Python module. Make sure it is installed on your system")
|
||||
logger.debug("and compiled with Python3 (same version as Blender).")
|
||||
logger.debug("It must also be found by Python, you might need to set its path in this Addon preferences")
|
||||
logger.debug("(User preferences->Addons->expand this addon).")
|
||||
if report:
|
||||
report({'ERROR'},"Unable to import the FreeCAD Python module. Check Addon preferences.")
|
||||
return {'CANCELLED'}
|
||||
# check if we have a GUI document
|
||||
'''
|
||||
guidata = {}
|
||||
zdoc = zipfile.ZipFile(filename)
|
||||
if zdoc:
|
||||
|
@ -91,7 +66,7 @@ def importer(filename,
|
|||
cols.append((buf[i*4+3],buf[i*4+2],buf[i*4+1],buf[i*4]))
|
||||
guidata[key]["DiffuseColor"] = cols
|
||||
zdoc.close()
|
||||
#logger.debug ("guidata:",guidata)
|
||||
|
||||
doc = FreeCAD.open(filename)
|
||||
docname = doc.Name
|
||||
if not doc:
|
||||
|
@ -99,7 +74,6 @@ def importer(filename,
|
|||
if report:
|
||||
report({'ERROR'},"Unable to open the given FreeCAD file")
|
||||
return {'CANCELLED'}
|
||||
#logger.debug ("Transferring",len(doc.Objects),"objects to Blender")
|
||||
|
||||
# import some FreeCAD modules needed below. After "import FreeCAD" these modules become available
|
||||
import Part
|
||||
|
@ -131,17 +105,20 @@ def importer(filename,
|
|||
verts = []
|
||||
edges = []
|
||||
faces = []
|
||||
matindex = [] # face to material relationship
|
||||
faceedges = [] # a placeholder to store edges that belong to a face
|
||||
matindex = [] # face to material relationship
|
||||
faceedges = [] # a placeholder to store edges that belong to a face
|
||||
|
||||
if obj.isDerivedFrom("Part::Feature"):
|
||||
# !!!
|
||||
# create mesh from shape
|
||||
shape = obj.Shape
|
||||
if placement:
|
||||
# !!!
|
||||
placement = obj.Placement
|
||||
shape = obj.Shape.copy()
|
||||
shape.Placement = placement.inverse().multiply(shape.Placement)
|
||||
if shape.Faces:
|
||||
# !!!
|
||||
if TRIANGULATE:
|
||||
# triangulate and make faces
|
||||
rawdata = shape.tessellate(tessellation)
|
||||
|
@ -153,9 +130,12 @@ def importer(filename,
|
|||
for e in face.Edges:
|
||||
faceedges.append(e.hashCode())
|
||||
else:
|
||||
# !!!
|
||||
# write FreeCAD faces as polygons when possible
|
||||
time_start = time.time()
|
||||
for face in shape.Faces:
|
||||
if (len(face.Wires) > 1) or (not isinstance(face.Surface,Part.Plane)) or hascurves(face):
|
||||
# !!!
|
||||
# face has holes or is curved, so we need to triangulate it
|
||||
rawdata = face.tessellate(tessellation)
|
||||
for v in rawdata[0]:
|
||||
|
@ -170,6 +150,7 @@ def importer(filename,
|
|||
faces.append(nf)
|
||||
matindex.append(len(rawdata[1]))
|
||||
else:
|
||||
# !!!
|
||||
f = []
|
||||
ov = face.OuterWire.OrderedVertexes
|
||||
for v in ov:
|
||||
|
@ -188,11 +169,13 @@ def importer(filename,
|
|||
matindex.append(1)
|
||||
for e in face.Edges:
|
||||
faceedges.append(e.hashCode())
|
||||
logger.debug('faces time is %s', (time.time() - time_start))
|
||||
for edge in shape.Edges:
|
||||
# !!!
|
||||
# Treat remaining edges (that are not in faces)
|
||||
if not (edge.hashCode() in faceedges):
|
||||
if hascurves(edge):
|
||||
dv = edge.discretize(9) #TODO use tessellation value
|
||||
dv = edge.discretize(9) # TODO use tessellation value
|
||||
for i in range(len(dv)-1):
|
||||
dv1 = [dv[i].x,dv[i].y,dv[i].z]
|
||||
dv2 = [dv[i+1].x,dv[i+1].y,dv[i+1].z]
|
||||
|
@ -216,12 +199,13 @@ def importer(filename,
|
|||
mesh = obj.Mesh
|
||||
if placement:
|
||||
placement = obj.Placement
|
||||
mesh = obj.Mesh.copy() # in meshes, this zeroes the placement
|
||||
mesh = obj.Mesh.copy() # in meshes, this zeroes the placement
|
||||
t = mesh.Topology
|
||||
verts = [[v.x,v.y,v.z] for v in t[0]]
|
||||
faces = t[1]
|
||||
|
||||
if verts and (faces or edges):
|
||||
# !!!
|
||||
# create or update object with mesh and material data
|
||||
bobj = None
|
||||
bmat = None
|
||||
|
@ -230,14 +214,16 @@ def importer(filename,
|
|||
for o in bpy.data.objects:
|
||||
if o.data.name == obj.Name:
|
||||
bobj = o
|
||||
logger.debug("Replacing existing object:",obj.Label)
|
||||
logger.debug('Replacing existing %s', obj.Label)
|
||||
bmesh = bpy.data.meshes.new(name=obj.Name)
|
||||
bmesh.from_pydata(verts, edges, faces)
|
||||
bmesh.update()
|
||||
if bobj:
|
||||
logger.debug('Updating the mesh of existing %s', obj.Label)
|
||||
# update only the mesh of existing object. Don't touch materials
|
||||
bobj.data = bmesh
|
||||
else:
|
||||
# !!!
|
||||
# create new object
|
||||
bobj = bpy.data.objects.new(obj.Label, bmesh)
|
||||
if placement:
|
||||
|
@ -251,6 +237,7 @@ def importer(filename,
|
|||
bobj.rotation_mode = m
|
||||
bobj.scale = (scale, scale, scale)
|
||||
if obj.Name in guidata:
|
||||
# !!!
|
||||
# one material for the whole object
|
||||
for fem_mat in doc.Objects:
|
||||
set_fem_mat(obj, bobj, fem_mat)
|
||||
|
@ -267,7 +254,3 @@ def importer(filename,
|
|||
|
||||
logger.info("Import freecad scene finished without errors")
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
importer()
|
46
cg/blender/import_fcstd/import_coordinate_point.py
Normal file
46
cg/blender/import_fcstd/import_coordinate_point.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
# -*- 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.
|
||||
# Import from json exported FreeCAD's asm4 coordinates as Blender's empty object.
|
||||
__version__ = "0.1"
|
||||
|
||||
import logging
|
||||
import bpy
|
||||
import json
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
def empty_importer(path_json):
|
||||
with open(path_json) as f:
|
||||
data = json.load(f)
|
||||
|
||||
pivot_name = data['label']
|
||||
pivot_pose = data['placement']
|
||||
loc = tuple(pivot_pose['position'].values())
|
||||
fori = tuple(pivot_pose['orientation'].values())
|
||||
bori = (fori[3],)+fori[:3]
|
||||
|
||||
bpy.ops.object.empty_add(
|
||||
type='ARROWS', radius=0.01, align='WORLD',
|
||||
location=(0, 0, 0), rotation=(0, 0, 0))
|
||||
pivot_obj = bpy.context.active_object # or bpy.context.object
|
||||
pivot_obj.name = pivot_name
|
||||
pivot_obj.rotation_mode = 'QUATERNION'
|
||||
pivot_obj.location = loc
|
||||
pivot_obj.rotation_quaternion = bori
|
||||
|
||||
f.close()
|
||||
logger.info('Point %s imported without errors', pivot_name)
|
|
@ -1,5 +1,4 @@
|
|||
# -*- 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
|
||||
|
@ -11,6 +10,9 @@
|
|||
# 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.
|
||||
# Simple FreeCAD's object test for manifold mawater-tight surface.
|
||||
import FreeCAD
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# -*- 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
|
||||
|
|
|
@ -25,12 +25,10 @@ def asset_setup(transforms=True, sharpness=True, shading=True):
|
|||
bpy.context.view_layer.objects.active = ob
|
||||
|
||||
if transforms:
|
||||
# apply scale
|
||||
apply_transforms(ob, location=False, rotation=False, scale=True)
|
||||
# remove doubles
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
bpy.ops.mesh.remove_doubles(threshold = 0.0001)
|
||||
bpy.ops.mesh.remove_doubles(threshold = 0.00001)
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
bpy.ops.mesh.select_mode(type = 'FACE')
|
||||
bpy.ops.mesh.select_interior_faces()
|
||||
|
|
|
@ -12,12 +12,7 @@ import sys
|
|||
import xml.etree.ElementTree as ET
|
||||
import os
|
||||
sys.path.append('../')
|
||||
from import_fcstd.importer import importer
|
||||
from mathutils import Matrix
|
||||
from utils.remove_collections import remove_collections
|
||||
from utils.cleanup_orphan_data import cleanup_orphan_data
|
||||
from remesh import asset_setup
|
||||
from export.stl import export_stl
|
||||
import bpy
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
@ -4,12 +4,15 @@ DESCRIPTION.
|
|||
Convert and setup FreeCAD solid objects to 3d assets mesh files.
|
||||
Support Blender compiled as a Python Module only!
|
||||
"""
|
||||
__version__ = "0.1"
|
||||
__version__ = "0.2"
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
sys.path.append('../blender/')
|
||||
from import_fcstd.importer import importer
|
||||
from import_fcstd.import_cad_objects import obj_importer
|
||||
from import_fcstd.import_coordinate_point import empty_importer
|
||||
from utils.apply_transforms import apply_transforms
|
||||
from utils.remove_collections import remove_collections
|
||||
from utils.cleanup_orphan_data import cleanup_orphan_data
|
||||
from utils.sdf_mesh_selector import sdf_mesh_selector
|
||||
|
@ -17,27 +20,55 @@ from remesh import asset_setup
|
|||
from export.dae import export_dae
|
||||
from export.collision import export_col_stl
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
def freecad_asset_pipeline(fcstd_path,
|
||||
mesh_export_path,
|
||||
tessellation=10,
|
||||
tessellation,
|
||||
mesh_export_path=None,
|
||||
json_path=None,
|
||||
blend_path=None,
|
||||
sdf_path=None):
|
||||
""" Setup FreeCAD scene to CG asset """
|
||||
remove_collections()
|
||||
cleanup_orphan_data()
|
||||
importer(fcstd_path, tessellation)
|
||||
obj_importer(fcstd_path, tessellation)
|
||||
|
||||
# apply scale to all objects
|
||||
obs = bpy.context.selected_objects
|
||||
for ob in obs:
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
ob.select_set(state=True)
|
||||
bpy.context.view_layer.objects.active = ob
|
||||
apply_transforms(ob, location=False, rotation=False, scale=True)
|
||||
for ob in obs:
|
||||
ob.select_set(state=True)
|
||||
|
||||
if json_path is not None:
|
||||
for point in os.listdir(json_path):
|
||||
if point.endswith('.json'):
|
||||
empty_importer(point)
|
||||
|
||||
if sdf_path is not None:
|
||||
sdf_mesh_selector(sdf_path)
|
||||
|
||||
asset_setup()
|
||||
|
||||
if blend_path is not None:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=blend_path)
|
||||
export_dae(mesh_export_path)
|
||||
export_col_stl(mesh_export_path)
|
||||
|
||||
# export all objects
|
||||
if mesh_export_path is not None:
|
||||
obs = bpy.context.selected_objects
|
||||
for ob in obs:
|
||||
ob.matrix_world = mathutils.Matrix()
|
||||
for ob in obs:
|
||||
ob.select_set(state=True)
|
||||
export_dae(mesh_export_path)
|
||||
export_col_stl(mesh_export_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -47,10 +78,12 @@ if __name__ == '__main__':
|
|||
description='Convert and setup FreeCAD solid objects to 3d assets mesh files.')
|
||||
parser.add_argument(
|
||||
'--fcstd_path', type=str, help='Path to source FreeCAD scene', required=True)
|
||||
parser.add_argument(
|
||||
'--mesh_export_path', type=str, help='Path for export meshes', required=True)
|
||||
parser.add_argument(
|
||||
'--tessellation', type=int, help='Tessellation number', default=10, required=False)
|
||||
parser.add_argument(
|
||||
'--mesh_export_path', type=str, help='Path for export meshes', required=False)
|
||||
parser.add_argument(
|
||||
'--json_path', type=str, help='Path to DIR with coordinate points jsons', required=False)
|
||||
parser.add_argument(
|
||||
'--blend_path', type=str, help='Path for export blend assembly file', required=False)
|
||||
parser.add_argument(
|
||||
|
@ -58,8 +91,9 @@ if __name__ == '__main__':
|
|||
args = parser.parse_args()
|
||||
|
||||
freecad_asset_pipeline(args.fcstd_path,
|
||||
args.mesh_export_path,
|
||||
args.tessellation,
|
||||
args.mesh_export_path,
|
||||
args.json_path,
|
||||
args.blend_path,
|
||||
args.sdf_path)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue