def main(): """ Generate .dae file. """ mesh = Collada() effect = material.Effect('effect0', [], 'phong', diffuse=(1, 0, 0), specular=(0, 1, 0)) mat = material.Material('material0', 'mymaterial', effect) mesh.effects.append(effect) mesh.materials.append(mat) vert_floats = numpy.array([ -50, 50, 50, 50, 50, 50, -50, -50, 50, 50, -50, 50, -50, 50, -50, 50, 50, -50, -50, -50, -50, 50, -50, -50 ]) normal_floats = numpy.array([ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1 ]) componests = ('X', 'Y', 'Z') vert_src = source.FloatSource('cubeverts-array', vert_floats, componests) normal_src = source.FloatSource('cubenormals-array', normal_floats, componests) input_list = source.InputList() input_list.addInput(0, 'VERTEX', '#cubeverts-array') input_list.addInput(1, 'NORMAL', '#cubenormals-array') indices = numpy.array([ 0, 0, 2, 1, 3, 2, 0, 0, 3, 2, 1, 3, 0, 4, 1, 5, 5, 6, 0, 4, 5, 6, 4, 7, 6, 8, 7, 9, 3, 10, 6, 8, 3, 10, 2, 11, 0, 12, 4, 13, 6, 14, 0, 12, 6, 14, 2, 15, 3, 16, 7, 17, 5, 18, 3, 16, 5, 18, 1, 19, 5, 20, 7, 21, 6, 22, 5, 20, 6, 22, 4, 23 ]) geom = geometry.Geometry(mesh, 'geometry0', 'mycube', [vert_src, normal_src]) triset = geom.createTriangleSet(indices, input_list, 'materialref') geom.primitives.append(triset) mesh.geometries.append(geom) matnode = scene.MaterialNode('materialref', mat, inputs=[]) geomnode = scene.GeometryNode(geom, [matnode]) node = scene.Node('node0', children=[geomnode]) myscene = scene.Scene('myscene', [node]) mesh.scenes.append(myscene) mesh.scene = myscene mesh.write('collada_sample.dae')
def build_scene(env, outfilename): #architecture is : #-env.name #--exterior #----ingredients #--compartments #----surface #------ingredients #----interior #------ingredients #create the document and a node for rootenv collada_xml = Collada() collada_xml.assetInfo.unitname = "centimeter" collada_xml.assetInfo.unitmeter = 0.01 collada_xml.assetInfo.upaxis = "Y_UP" root_env = scene.Node(env.name) myscene = scene.Scene(env.name + "_Scene", [root_env]) collada_xml.scenes.append(myscene) collada_xml.scene = myscene # bbsurface = None r = env.exteriorRecipe if r: collada_xml, root_env = buildRecipe(r, r.name, collada_xml, root_env) for o in env.compartments: rs = o.surfaceRecipe if rs: #p,s,bb=up (767.0) #used for lipids #pp,ss,bbsurface = up (700.0) collada_xml, root_env = buildRecipe(rs, str(o.name) + "_surface", collada_xml, root_env, mask=bbsurface) ri = o.innerRecipe if ri: collada_xml, root_env = buildRecipe(ri, str(o.name) + "_interior", collada_xml, root_env, mask=bbsurface) collada_xml, root_env = buildCompartmentsGeom(env.compartments[0], collada_xml, root_env) collada_xml.write(outfilename) return collada_xml
def export_dae(vertices, indices, fname): mesh = Collada() effect = material.Effect("effect0", [], "phong", diffuse=(1, 0, 0), specular=(0, 1, 0)) mat = material.Material("material0", "mymaterial", effect) mesh.effects.append(effect) mesh.materials.append(mat) vert_src = source.FloatSource('verts-array', numpy.array(vertices), ('X', 'Y', 'Z')) inlist = source.InputList() inlist.addInput(0, 'VERTEX', '#verts-array') indices = numpy.array(indices) geom = geometry.Geometry(mesh, 'geometry0', 'linac', [vert_src]) triset = geom.createTriangleSet(indices, inlist, "materialref") geom.primitives.append(triset) mesh.geometries.append(geom) matnode = scene.MaterialNode("materialref", mat, inputs=[]) geomnode = scene.GeometryNode(geom, [matnode]) node = scene.Node("node0", children=[geomnode]) myscene = scene.Scene("myscene", [node]) mesh.scenes.append(myscene) mesh.scene = myscene mesh.write(fname)
def _save_dae(self, name): """\ Save the model as a collada file. """ mesh = Collada() effect = material.Effect("effect0", [], "phong", diffuse=(0.9,0.9,0.9), \ specular=(0.1,0.1,0.1)) mat = material.Material("material0", "mymaterial", effect) mesh.effects.append(effect) mesh.materials.append(mat) vert_floats = [] norm_floats = [] for vertex in self.vertices: vert_floats.append(vertex.x) vert_floats.append(vertex.y) vert_floats.append(vertex.z) for normal in self.normals: norm_floats.append(normal.x) norm_floats.append(normal.y) norm_floats.append(normal.z) indices = array(self.collada_indices) vert_src = source.FloatSource("vert-array", array(vert_floats), \ ('X', 'Y', 'Z')) norm_src = source.FloatSource("norm-array", array(norm_floats), \ ('X', 'Y', 'Z')) geom = geometry.Geometry(mesh, "geometry0", "solid", [vert_src, norm_src]) input_list = source.InputList() input_list.addInput(0, 'VERTEX', "#vert-array") input_list.addInput(1, 'NORMAL', "#norm-array") triset = geom.createTriangleSet(indices, input_list, "materialref") geom.primitives.append(triset) mesh.geometries.append(geom) matnode = scene.MaterialNode("materialref", mat, inputs=[]) geomnode = scene.GeometryNode(geom, [matnode]) node = scene.Node("node0", children=[geomnode]) myscene = scene.Scene("myscene", [node]) mesh.scenes.append(myscene) mesh.scene = myscene mesh.write(name+'.dae')
# print "pfff" isoc = isocontour.getContour3d(ndata, 0, 0, isovalue, isocontour.NO_COLOR_VARIABLE) vert = np.zeros((isoc.nvert, 3)).astype('f') norm = np.zeros((isoc.nvert, 3)).astype('f') col = np.zeros((isoc.nvert)).astype('f') tri = np.zeros((isoc.ntri, 3)).astype('i') isocontour.getContour3dData(isoc, vert, norm, col, tri, 0) #print vert if maskGrid.crystal: vert = maskGrid.crystal.toCartesian(vert) return vert, norm, tri collada_xml = Collada() collada_xml.assetInfo.unitname = "centimeter" collada_xml.assetInfo.unitmeter = 0.01 collada_xml.assetInfo.upaxis = "Y_UP" root_env = scene.Node(env.name) myscene = scene.Scene(env.name + "_Scene", [root_env]) collada_xml.scenes.append(myscene) collada_xml.scene = myscene name = "myMolecule" matnode = oneMaterial(name, collada_xml) master_node = buildMeshGeom(name, v, f, collada_xml, matnode) collada_xml.nodes.append(master_node) collada_xml.write("test.dae")
class ColladaExport(object): def __init__(self, directory, export_as='dae_only'): self._dir = directory self._export_as = export_as self._geometries = {} self._materials = {} self._collada = Collada() self._scene = Scene('main', []) self._collada.scenes.append(self._scene) self._collada.scene = self._scene def save(self, fp): self._collada.write(fp) def object(self, b_obj, parent=None, children=True): b_matrix = b_obj.matrix_world if parent: if children: b_matrix = b_obj.matrix_local else: b_matrix = Matrix() node = self.node(b_obj.name, b_matrix) if any(b_obj.children) and children: self.object(b_obj, parent=node, children=False) for child in b_obj.children: self.object(child, parent=node) if parent: parent.children.append(node) else: self._scene.nodes.append(node) inode_meth = getattr(self, 'obj_' + b_obj.type, None) if inode_meth: node.children.extend(inode_meth(b_obj)) def node(self, b_name, b_matrix=None): tf = [] if b_matrix: tf.append(self.matrix(b_matrix)) node = Node(b_name, transforms=tf) node.save() return node def obj_MESH(self, b_obj): geom = self._geometries.get(b_obj.data.name, None) if not geom: geom = self.mesh(b_obj.data) self._geometries[b_obj.data.name] = geom matnodes = [] for slot in b_obj.material_slots: sname = slot.material.name if sname not in self._materials: self._materials[sname] = self.material(slot.material) matnodes.append(MaterialNode('none', self._materials[sname], inputs=[])) return [GeometryNode(geom, matnodes)] def mesh(self, b_mesh): vert_srcid = b_mesh.name + '-vertary' vert_f = [c for v in b_mesh.vertices for c in v.co] vert_src = FloatSource(vert_srcid, np.array(vert_f), ('X', 'Y', 'Z')) sources = [vert_src] smooth = list(filter(lambda f: f.use_smooth, b_mesh.faces)) if any(smooth): vnorm_srcid = b_mesh.name + '-vnormary' norm_f = [c for v in b_mesh.vertices for c in v.normal] norm_src = FloatSource(vnorm_srcid, np.array(norm_f), ('X', 'Y', 'Z')) sources.append(norm_src) flat = list(filter(lambda f: not f.use_smooth, b_mesh.faces)) if any(flat): fnorm_srcid = b_mesh.name + '-fnormary' norm_f = [c for f in flat for c in f.normal] norm_src = FloatSource(fnorm_srcid, np.array(norm_f), ('X', 'Y', 'Z')) sources.append(norm_src) name = b_mesh.name + '-geom' geom = Geometry(self._collada, name, name, sources) if any(smooth): ilist = InputList() ilist.addInput(0, 'VERTEX', _url(vert_srcid)) ilist.addInput(1, 'NORMAL', _url(vnorm_srcid)) # per vertex normals indices = np.array([ i for v in [ (v, v) for f in smooth for v in f.vertices ] for i in v]) if _is_trimesh(smooth): p = geom.createTriangleSet(indices, ilist, 'none') else: vcount = [len(f.vertices) for f in smooth] p = geom.createPolylist(indices, vcount, ilist, 'none') geom.primitives.append(p) if any(flat): ilist = InputList() ilist.addInput(0, 'VERTEX', _url(vert_srcid)) ilist.addInput(1, 'NORMAL', _url(fnorm_srcid)) indices = [] # per face normals for i, f in enumerate(flat): for v in f.vertices: indices.extend([v, i]) indices = np.array(indices) if _is_trimesh(flat): p = geom.createTriangleSet(indices, ilist, 'none') else: vcount = [len(f.vertices) for f in flat] p = geom.createPolylist(indices, vcount, ilist, 'none') geom.primitives.append(p) self._collada.geometries.append(geom) return geom def material(self, b_mat): shader = 'lambert' if b_mat.specular_shader == 'PHONG': shader = 'phong' elif b_mat.specular_shader == 'BLINN': shader = 'blinn' if b_mat.use_shadeless: shader = 'constant' child = { 'ambient': (b_mat.ambient,) * 3, 'emission': (b_mat.emit,) * 3, 'diffuse': tuple(b_mat.diffuse_color), } if b_mat.use_transparency: child.update({ 'transparent': (0.,0.,0.), 'transparency': b_mat.alpha, }) if b_mat.raytrace_mirror.use: child.update({ 'reflective': tuple(b_mat.mirror_color), 'reflectivity': b_mat.raytrace_mirror.reflect_factor, }) effect = Effect(b_mat.name + '-fx', [], shader, **child) mat = Material(b_mat.name, b_mat.name, effect) self._collada.effects.append(effect) self._collada.materials.append(mat) return mat def matrix(self, b_matrix): f = tuple(map(tuple, b_matrix.transposed())) return MatrixTransform(np.array( [e for r in f for e in r], dtype=np.float32))
class ColladaExport: def __init__(self, objects, filepath, directory, kwargs): self._is_zae = kwargs["export_as"] == "zae" self._export_textures = kwargs["export_textures"] self._add_blender_extensions = kwargs["add_blender_extensions"] self._filepath = filepath self._dir = directory self._ext_files_map = {} self._ext_files_revmap = {} if self._is_zae: self._zip = zipfile.ZipFile(self._filepath, "w") class ZipAttr: compress_type = zipfile.ZIP_DEFLATED file_attr = 0o100644 << 16 date_time = time.gmtime()[:6] scene_name = "scene.dae" @classmethod def new_item(celf, filename): item = zipfile.ZipInfo() item.compress_type = celf.compress_type item.external_attr = celf.file_attr item.date_time = celf.date_time item.filename = filename return item #end new_item #end ZipAttr self._zipattr = ZipAttr # First item in archive is uncompressed and named “mimetype”, and # contents is MIME type for archive. This way it ends up at a fixed # offset (filename at 30 bytes from start, contents at 38 bytes) for # easy detection by format-sniffing tools. This convention is used # by ODF and other formats similarly based on Zip archives. mimetype = zipfile.ZipInfo() mimetype.filename = "mimetype" mimetype.compress_type = zipfile.ZIP_STORED mimetype.external_attr = ZipAttr.file_attr mimetype.date_time = (2020, 8, 23, 1, 33, 52) # about when I started getting .zae export working self._zip.writestr(mimetype, b"model/vnd.collada+xml+zip") # extrapolating from the fact that the official type for .dae files # is “model/vnd.collada+xml” else: self._zip = None self._zipattr = None #end if self._up_axis = kwargs["up_axis"] if self._up_axis == "Z_UP": self._orient = Matrix.Identity(4) elif self._up_axis == "X_UP": self._orient = Matrix.Rotation(-120 * DEG, 4, Vector(1, -1, 1)) else: # "Y_UP" or unspecified self._orient = Matrix.Rotation(-90 * DEG, 4, "X") #end if obj_children = {} for obj in objects: parent = obj.parent if parent == None: parentname = None else: parentname = parent.name #end if if parentname not in obj_children: obj_children[parentname] = set() #end if obj_children[parentname].add(obj.name) #end for self._obj_children = obj_children self._selected_only = kwargs["use_selection"] self._geometries = {} self._materials = {} self._collada = Collada() self._collada.xmlnode.getroot().set("version", kwargs["collada_version"]) self._collada.assetInfo.unitmeter = 1 self._collada.assetInfo.unitname = "metre" self._collada.assetInfo.upaxis = self._up_axis self._collada.assetInfo.save() root_technique = self.blender_technique( True, self._collada.xmlnode.getroot()) if root_technique != None: prefixes = E.id_prefixes() for k in sorted(DATABLOCK.__members__.keys()): v = DATABLOCK[k] if not v.internal_only: prefix = E.prefix(name=k, value=v.nameid("")) prefixes.append(prefix) #end if #end for root_technique.append(prefixes) #end if self._scene = Scene(DATABLOCK.SCENE.nameid("main"), []) self._collada.scenes.append(self._scene) self._collada.scene = self._scene self._id_seq = 0 #end __init__ def write_ext_file(self, category, obj_name, filename, contents): if category not in self._ext_files_map: self._ext_files_map[category] = {} self._ext_files_revmap[category] = {} #end if ext_files_map = self._ext_files_map[category] ext_files_revmap = self._ext_files_revmap[category] if obj_name in ext_files_map: # already encountered this external file out_filename = ext_files_map[obj_name] else: if not self._is_zae: outdir = os.path.join(self._dir, category.subdir) os.makedirs(outdir, exist_ok=True) #end if base_out_filename = os.path.join(category.subdir, filename) out_filename = base_out_filename seq = 0 while out_filename in ext_files_revmap: if seq == 0: base_parts = os.path.splitext(base_out_filename) #end if seq += 1 assert seq < 1000000 # impose some ridiculous but finite upper limit out_filename = "%s-%0.3d%s" % (base_parts[0], seq, base_parts[1]) #end while ext_files_map[obj_name] = out_filename ext_files_revmap[out_filename] = obj_name if self._is_zae: item = self._zipattr.new_item(out_filename) self._zip.writestr(item, contents) else: out = open(os.path.join(self._dir, out_filename), "wb") out.write(contents) out.close() #end if #end if return out_filename #end write_ext_file def save(self): if self._is_zae: item = self._zipattr.new_item(self._zipattr.scene_name) dae = io.BytesIO() self._collada.write(dae) self._zip.writestr(item, dae.getvalue()) manifest = ElementTree.Element("dae_root") manifest.text = self._zipattr.scene_name item = self._zipattr.new_item("manifest.xml") self._zip.writestr(item, ElementTree.tostring(manifest)) # all done self._zip.close() else: self._collada.write(self._filepath) #end if #end save def blender_technique(self, as_extra, obj): # experimental: add Blender-specific attributes via a custom <technique>. if self._add_blender_extensions: if isinstance(obj, DaeObject): obj = obj.xmlnode #end if blendstuff = E.technique(profile="BLENDER028") if as_extra: parent = E.extra() else: parent = obj #end if parent.append(blendstuff) if as_extra: obj.append(parent) #end if else: blendstuff = None #end if return blendstuff #end blender_technique def obj_blender_technique(self, as_extra, obj, b_data, attribs): # save any custom technique settings for this object. blendstuff = self.blender_technique(as_extra, obj) if blendstuff != None: for tagname, attrname in attribs: if hasattr(b_data, attrname): subtag = getattr(E, tagname)(str(getattr(b_data, attrname))) blendstuff.append(subtag) #end if #end for #end if #end obj_blender_technique def next_internal_id(self): self._id_seq += 1 return DATABLOCK.INTERNAL_ID.nameid("%0.5d" % self._id_seq) #end next_internal_id def node(self, b_matrix=None): node = Node(id=None, xmlnode=E.node()) # construct my own xmlnode to avoid setting an id or name # (should be optional according to Collada spec) if b_matrix != None: node.transforms.append(self.matrix(b_matrix)) #end if node.save() return node #end node def obj_camera(self, b_obj): result = [] b_cam = b_obj.data if b_cam.type == "PERSP": cam_class = PerspectiveCamera args = \ { "xfov" : b_cam.angle_x / DEG, "yfov" : b_cam.angle_y / DEG, } elif b_cam.type == "ORTHO": cam_class = OrthographicCamera args = \ { "xmag" : b_cam.ortho_scale, "ymag" : b_cam.ortho_scale, } else: cam_class = None #end if if cam_class != None: # todo: shared datablock cam = cam_class \ ( id = DATABLOCK.CAMERA.nameid(b_obj.name), znear = b_cam.clip_start, zfar = b_cam.clip_end, **args ) self._collada.cameras.append(cam) result.append(CameraNode(cam)) #end if return result #end obj_camera def obj_light(self, b_obj): result = [] b_light = b_obj.data light_type = tuple \ ( t for t in ( ("POINT", PointLight), ("SPOT", SpotLight), ("SUN", DirectionalLight), ) if b_light.type == t[0] ) if len(light_type) != 0: light_type = light_type[0][1] else: light_type = None #end if if light_type != None: # todo: shared datablock light = light_type \ ( DATABLOCK.LAMP.nameid(b_obj.name), color = tuple(b_light.color) + (1,) ) for attr, battr, conv in \ ( # conversions are inverses of those done in importer ("falloff_ang", "spot_size", lambda ang : ang / DEG), ("falloff_exp", "spot_blend", lambda blend : 1 / max(blend, 0.00001) - 1), # some very small-magnitude positive value to avoid division by zero ) \ : if hasattr(b_light, battr) and hasattr(light, attr): setattr(light, attr, conv(getattr(b_light, battr))) #end if #end for if b_light.use_nodes: node_graph = b_light.node_tree the_node = list(n for n in node_graph.nodes if n.type == "OUTPUT_LIGHT")[0] trace_path = iter \ ( ( ("Surface", "EMISSION"), ("Strength", "LIGHT_FALLOFF"), ) ) found = False while True: trace = next(trace_path, None) if trace == None: if not the_node.inputs["Strength"].is_linked: found = True #end if break #end if input_name, want_shader_type = trace input = the_node.inputs[input_name] if not input.is_linked: break links = input.links # note docs say this takes O(N) in total nr links in node graph to compute if len(links) == 0: break the_node = links[0].from_node if the_node.type != want_shader_type: break output_name = links[0].from_socket.name #end while if found: strength = the_node.inputs["Strength"].default_value if strength != 0: atten = \ { "Constant" : "constant_att", "Linear" : "linear_att", "Quadratic" : "quad_att", }.get(output_name) if atten != None and hasattr(light, atten): setattr(light, atten, 1 / strength) #end if #end if #end if #end if self.obj_blender_technique \ ( True, light, b_light, [ ("angle", "angle"), ("power", "energy"), ("shadow_soft_size", "shadow_soft_size"), ("spot_blend", "spot_blend"), ("spot_size", "spot_size"), ] ) self._collada.lights.append(light) result.append(LightNode(light)) #end if return result #end obj_light def obj_empty(self, b_obj): result = Node(id=DATABLOCK.EMPTY.nameid(b_obj.name)) return [result] #end obj_empty def obj_mesh(self, b_obj): def make_slotname(slotindex): # Blender doesn’t name material slots, but Collada does return "slot%.3d" % slotindex #end make_slotname def encode_mesh(b_mesh, b_mesh_name, b_material_slots): def is_trimesh(faces): return all([len(f.verts) == 3 for f in faces]) #end is_trimesh #begin encode_mesh mesh_name = DATABLOCK.MESH.nameid(b_mesh_name) sources = [] vert_srcid = self.next_internal_id() sources.append \ ( FloatSource ( id = vert_srcid, data = np.array([c for v in b_mesh.verts for c in v.co]), components = ("X", "Y", "Z") ) ) vnorm_srcid = self.next_internal_id() sources.append \ ( FloatSource ( id = vnorm_srcid, data = np.array([c for v in b_mesh.verts for c in v.normal]), components = ("X", "Y", "Z") ) ) # todo: face normal might be different for flat shading uv_ids = [] if b_mesh.loops.layers.uv.active != None: active_uv_name = b_mesh.loops.layers.uv.active.name else: active_uv_name = None #end if for i, (b_uvname, uvlayer) in enumerate(b_mesh.loops.layers.uv.items()): uv_name = self.next_internal_id() uv_ids.append((uv_name, b_uvname)) sources.append \ ( FloatSource ( id = uv_name, data = np.array ( [ x for f in b_mesh.faces for l in f.loops for x in l[uvlayer].uv ] ), components = ("S", "T") ) ) #end for geom = Geometry(self._collada, mesh_name, mesh_name, sources) blendstuff = self.blender_technique(True, geom) if blendstuff != None: names = E.layer_names() for u in uv_ids: names.append(E.name(name=u[1], refid=u[0], type="UV")) #end for blendstuff.append(names) #end if for slotindex in range(max(len(b_material_slots), 1)): slotname = make_slotname(slotindex) assigned = \ [ f for f in b_mesh.faces if f.material_index == slotindex ] if any(assigned): ilist = InputList() ilist.addInput(0, "VERTEX", idurl(vert_srcid)) ilist.addInput(0, "NORMAL", idurl(vnorm_srcid)) setnr = 0 for u in uv_ids: setnr += 1 ilist.addInput(1, "TEXCOORD", idurl(u[0]), (setnr, 0)[u[1] == active_uv_name]) # always assign set 0 to active UV layer #end for indices = [] for face in b_mesh.faces: for face_loop in face.loops: this_face = [face_loop.vert.index, face_loop.index] indices.extend(this_face) #end for #end for indices = np.array(indices) if is_trimesh(assigned): p = geom.createTriangleSet(indices, ilist, slotname) else: vcounts = [len(f.verts) for f in assigned] p = geom.createPolylist(indices, vcounts, ilist, slotname) #end if geom.primitives.append(p) #end if #end for self._collada.geometries.append(geom) return geom #end encode_mesh #begin obj_mesh b_mesh_name = b_obj.data.name b_material_slots = b_obj.material_slots b_mesh = bmesh.new() geom = self._geometries.get(b_mesh_name, None) if not geom: b_mesh.from_mesh(b_obj.data) geom = encode_mesh(b_mesh, b_mesh_name, b_material_slots) self._geometries[b_mesh_name] = geom #end if matnodes = [] for slotindex, slot in enumerate(b_material_slots): sname = slot.material.name if sname not in self._materials: self._materials[sname] = self.material(slot.material) #end if matnodes.append \ ( MaterialNode ( make_slotname(slotindex), self._materials[sname], inputs = [("ACTIVE_UV", "TEXCOORD", "0")] # always assign set 0 to active UV layer ) ) #end for b_mesh.free() return [GeometryNode(geom, matnodes)] #end obj_mesh obj_type_handlers = \ { "CAMERA" : (obj_camera, DATABLOCK.CAMERA), "EMPTY" : (obj_empty, DATABLOCK.EMPTY), "LIGHT" : (obj_light, DATABLOCK.LAMP), "MESH" : (obj_mesh, DATABLOCK.MESH), } def object(self, b_obj, parent=None): handle_type = self.obj_type_handlers.get(b_obj.type) if handle_type != None: if parent != None: b_matrix = b_obj.matrix_local else: b_matrix = self._orient @ b_obj.matrix_world #end if obj = handle_type[0](self, b_obj) is_node = len(obj) == 1 and isinstance(obj[0], Node) if is_node: obj = obj[0] assert b_matrix != None obj.transforms.append(self.matrix(b_matrix)) node = obj else: node = self.node(b_matrix) #end if children = self._obj_children.get(b_obj.name) if children != None: for childname in children: self.object(bpy.data.objects[childname], parent=node) #end for #end if if parent != None: parent.children.append(node) else: self._scene.nodes.append(node) #end if if not is_node: node.children.extend(obj) #end if #end if #end object def material(self, b_mat): shader = "lambert" effect_kwargs = \ { "diffuse" : tuple(b_mat.diffuse_color[:3]), "double_sided" : not b_mat.use_backface_culling, } effect_params = [] if b_mat.diffuse_color[3] != 1.0: effect_kwargs["transparency"] = b_mat.diffuse_color[3] #end if if b_mat.use_nodes: b_shader = list(n for n in b_mat.node_tree.nodes if n.type == "BSDF_PRINCIPLED") if len(b_shader) == 1: # assume node setup somewhat resembles what importer creates b_shader = b_shader[0] else: b_shader = None #end if if b_shader != None: def get_input(name): input = b_shader.inputs[name] if not input.is_linked: value = input.default_value else: value = None #end if return value #end get_input def get_input_map(name): input = b_shader.inputs[name] map = None # to begin with if input.is_linked: links = input.links # note docs say this takes O(N) in total nr links in node graph to compute teximage = list \ ( l.from_node for l in links if isinstance(l.from_node, bpy.types.ShaderNodeTexImage) and l.from_socket.name == "Color" ) if len(teximage) != 0: teximage = teximage[0].image if teximage.packed_file != None: contents = teximage.packed_file.data else: contents = open( bpy.path.abspath(teximage.filepath), "rb").read() #end if out_filepath = self.write_ext_file \ ( category = EXT_FILE.TEXTURE, obj_name = teximage.name, filename = os.path.basename(teximage.filepath), contents = contents ) image = CImage(id=self.next_internal_id(), path=out_filepath) surface = Surface(id=self.next_internal_id(), img=image) sampler = Sampler2D(id=self.next_internal_id(), surface=surface) map = Map(sampler=sampler, texcoord="ACTIVE_UV") effect_params.extend([image, surface, sampler]) #end if #end if return map #end get_input_map value = get_input("Base Color") if value != None: effect_kwargs["diffuse"] = value[:3] elif self._export_textures: map = get_input_map("Base Color") if map != None: effect_kwargs["diffuse"] = map #end if #end if # todo: support maps for more inputs value = get_input("Metallic") metallic = True if value == None or value == 0: value = get_input("Specular") metallic = False #end if if value != None and value != 0: shader = "phong" # do I care about “blinn”? if metallic: effect_kwargs["reflective"] = effect_kwargs["diffuse"] else: effect_kwargs["reflective"] = (1, 1, 1) #end if effect_kwargs["reflectivity"] = value #end if value = get_input("Alpha") if value != None and value != 1.0: effect_kwargs["transparency"] = value # overridden by Transmission (below) if any #end if value = get_input("Transmission") if value != None and value != 0: effect_kwargs["transparency"] = value effect_kwargs["transparent"] = effect_kwargs["diffuse"] value = get_input("IOR") if value != None: effect_kwargs["index_of_refraction"] = value #end if #end if else: pass # give up for now #end if else: # quick fudge based only on Viewport Display settings if b_mat.metallic > 0 or b_mat.roughness < 1: shader = "phong" # do I care about “blinn”? try: shininess = 1 / b_mat.roughness - 1 # inverse of formula used in importer except ZeroDivisionError: shininess = math.inf #end try shininess = min( shininess, 10000) # just some arbitrary finite upper limit effect_kwargs["reflectivity"] = b_mat.specular_intensity effect_kwargs["shininess"] = shininess if b_mat.metallic > 0: # not paying attention to actual value of b_mat.metallic! effect_kwargs["reflective"] = b_mat.specular_color[:3] else: effect_kwargs["reflective"] = (1, 1, 1) #end if #end if #end if effect = Effect(self.next_internal_id(), effect_params, shader, **effect_kwargs) mat = Material(DATABLOCK.MATERIAL.nameid(b_mat.name), b_mat.name, effect) self._collada.effects.append(effect) self._collada.materials.append(mat) return mat #end material @staticmethod def matrix(b_matrix): return \ MatrixTransform \ ( np.array ( [e for r in tuple(map(tuple, b_matrix)) for e in r], dtype = np.float32 ) )