def _export_textures_and_materials(blender_objects, saved_mod): textures = get_textures_from_blender_objects(blender_objects) blender_materials = get_materials_from_blender_objects(blender_objects) textures_array = ((ctypes.c_char * 64) * len(textures))() materials_data_array = (MaterialData * len(blender_materials))() for i, texture in enumerate(textures): file_name = os.path.basename(texture.image.filepath) try: file_path = ntpath.join(texture.albam_imported_texture_folder, file_name) except AttributeError: raise ExportError('Texture {0} was not imported from an Arc file'.format(texture.name)) try: file_path, _ = ntpath.splitext(file_path) textures_array[i] = (ctypes.c_char * 64)(*file_path.encode('ascii')) except UnicodeEncodeError: raise ExportError('Texture path {} is not in ascii'.format(file_path)) if len(file_path) > 64: # TODO: what if relative path are used? raise ExportError('File path to texture {} is longer than 64 characters' .format(file_path)) for i, mat in enumerate(blender_materials): material_data = MaterialData() try: # TODO: Should use data from actual blender material saved_mat = saved_mod.materials_data_array[i] except IndexError: raise ExportError('Exporting models with more materials than the original not supported yet') material_data.unk_01 = saved_mat.unk_01 material_data.unk_02 = saved_mat.unk_02 material_data.unk_03 = saved_mat.unk_03 material_data.unk_04 = saved_mat.unk_04 material_data.unk_05 = saved_mat.unk_05 material_data.unk_06 = saved_mat.unk_06 material_data.unk_07 = saved_mat.unk_07 for texture_slot in mat.texture_slots: if not texture_slot: continue texture = texture_slot.texture if not texture: # ? continue # texture_indices expects index-1 based try: texture_index = textures.index(texture) + 1 except ValueError: # TODO: logging print('error in textures') material_data.texture_indices[texture.albam_imported_texture_type] = texture_index materials_data_array[i] = material_data return textures_array, materials_data_array
def _export_textures_and_materials(blender_objects, saved_mod): textures = get_textures_from_blender_objects(blender_objects) blender_materials = get_materials_from_blender_objects(blender_objects) textures_array = ((ctypes.c_char * 64) * len(textures))() materials_data_array = (MaterialData * len(blender_materials))() for i, texture in enumerate(textures): file_name = os.path.basename(texture.image.filepath) try: file_path = ntpath.join(texture.albam_imported_texture_folder, file_name) except AttributeError: raise ExportError( 'Texture {0} was not imported from an Arc file'.format( texture.name)) try: file_path, _ = ntpath.splitext(file_path) textures_array[i] = (ctypes.c_char * 64)(*file_path.encode('ascii')) except UnicodeEncodeError: raise ExportError( 'Texture path {} is not in ascii'.format(file_path)) if len(file_path) > 64: # TODO: what if relative path are used? raise ExportError( 'File path to texture {} is longer than 64 characters'.format( file_path)) for i, mat in enumerate(blender_materials): material_data = MaterialData() try: # TODO: Should use data from actual blender material saved_mat = saved_mod.materials_data_array[i] except IndexError: raise ExportError( 'Exporting models with more materials than the original not supported yet' ) material_data.unk_01 = saved_mat.unk_01 material_data.unk_02 = saved_mat.unk_02 material_data.unk_03 = saved_mat.unk_03 material_data.unk_04 = saved_mat.unk_04 material_data.unk_05 = saved_mat.unk_05 material_data.unk_06 = saved_mat.unk_06 material_data.unk_07 = saved_mat.unk_07 for texture_slot in mat.texture_slots: if not texture_slot: continue texture = texture_slot.texture if not texture: # ? continue # texture_indices expects index-1 based try: texture_index = textures.index(texture) + 1 except ValueError: # TODO: logging print('error in textures') material_data.texture_indices[ texture.albam_imported_texture_type] = texture_index materials_data_array[i] = material_data return textures_array, materials_data_array
def _export_meshes(blender_objects, bounding_box, saved_mod): """ No weird optimization or sharing of offsets in the vertex buffer. All the same offsets, different positions like pl0200.mod from uPl01ShebaCos1.arc No time to investigate why and how those are decided. I suspect it might have to do with location of the meshes """ all_blender_meshes_objects = [ ob for ob in blender_objects if ob.type == 'MESH' or ob.type == 'EMPTY' ] passed_blender_meshes_objects = [ ob for ob in all_blender_meshes_objects if ob.type == 'MESH' ] meshes_156 = (Mesh156 * len(passed_blender_meshes_objects))() vertex_buffer = bytearray() index_buffer = bytearray() materials = get_materials_from_blender_objects(blender_objects) vertex_position = 0 face_position = 0 failed_count = 0 for i, blender_mesh_object in enumerate(all_blender_meshes_objects): # XXX: if a model with more meshes than the original is exported... boom # If somehow indices are changed... boom try: saved_mesh = saved_mod.meshes_array[i] except IndexError: raise ExportError( 'Exporting models with more meshes (parts) than the original not supported yet' ) # Failed meshes are imported as empties, since the only reason they fail is that they have out of bounds # array offsets (e.g. uEm41.arc em4000.mod), we can't retrieve any vertices since we'd mess up the vertex buffer if blender_mesh_object.type == 'EMPTY': failed_count += 1 continue blender_mesh = blender_mesh_object.data vertex_format, vertices_array = _export_vertices( blender_mesh_object, bounding_box, saved_mod, i) vertex_buffer.extend(vertices_array) # TODO: is all this format conversion necessary? triangle_strips_python = triangles_list_to_triangles_strip( blender_mesh) # mod156 use global indices for verts, in case one only mesh is needed, probably triangle_strips_python = [ e + vertex_position for e in triangle_strips_python ] triangle_strips_ctypes = (ctypes.c_ushort * len(triangle_strips_python))( *triangle_strips_python) index_buffer.extend(triangle_strips_ctypes) vertex_count = len(blender_mesh.vertices) index_count = len(triangle_strips_python) m156 = meshes_156[i - failed_count] try: m156.material_index = materials.index(blender_mesh.materials[0]) except IndexError: raise ExportError('Mesh {} has no materials'.format( blender_mesh.name)) m156.constant = 1 m156.level_of_detail = saved_mesh.level_of_detail # TODO m156.vertex_format = CLASSES_TO_VERTEX_FORMATS[vertex_format] m156.vertex_stride = 32 m156.vertex_count = vertex_count m156.vertex_index_end = vertex_position + vertex_count - 1 m156.vertex_index_start_1 = vertex_position m156.vertex_offset = 0 m156.face_position = face_position m156.face_count = index_count m156.face_offset = 0 m156.vertex_index_start_2 = vertex_position m156.vertex_group_count = saved_mesh.vertex_group_count # len(blender_mesh_object.vertex_groups) # XXX: improve, not guaranteed if the weighting is modified (e.g. replacing a model) m156.bone_palette_index = saved_mesh.bone_palette_index # TODO: not using saved_mesh since it seems these are optional. Needs research m156.group_index = saved_mesh.group_index m156.unk_01 = saved_mesh.unk_01 # m156.unk_02 = saved_mesh.unk_02 # crashes if set to saved_mesh.unk_02 in ChrisNormal m156.unk_03 = saved_mesh.unk_03 m156.unk_04 = saved_mesh.unk_04 m156.unk_05 = saved_mesh.unk_05 m156.unk_06 = saved_mesh.unk_06 m156.unk_07 = saved_mesh.unk_07 m156.unk_08 = saved_mesh.unk_08 m156.unk_09 = saved_mesh.unk_09 m156.unk_10 = saved_mesh.unk_10 m156.unk_11 = saved_mesh.unk_11 vertex_position += vertex_count face_position += index_count vertex_buffer = (ctypes.c_ubyte * len(vertex_buffer)).from_buffer(vertex_buffer) index_buffer = (ctypes.c_ushort * (len(index_buffer) // 2)).from_buffer(index_buffer) return meshes_156, vertex_buffer, index_buffer
def _export_meshes(blender_objects, bounding_box, saved_mod): """ No weird optimization or sharing of offsets in the vertex buffer. All the same offsets, different positions like pl0200.mod from uPl01ShebaCos1.arc No time to investigate why and how those are decided. I suspect it might have to do with location of the meshes """ all_blender_meshes_objects = [ob for ob in blender_objects if ob.type == 'MESH' or ob.type == 'EMPTY'] passed_blender_meshes_objects = [ob for ob in all_blender_meshes_objects if ob.type == 'MESH'] meshes_156 = (Mesh156 * len(passed_blender_meshes_objects))() vertex_buffer = bytearray() index_buffer = bytearray() materials = get_materials_from_blender_objects(blender_objects) vertex_position = 0 face_position = 0 failed_count = 0 for i, blender_mesh_object in enumerate(all_blender_meshes_objects): # XXX: if a model with more meshes than the original is exported... boom # If somehow indices are changed... boom try: saved_mesh = saved_mod.meshes_array[i] except IndexError: raise ExportError('Exporting models with more meshes (parts) than the original not supported yet') # Failed meshes are imported as empties, since the only reason they fail is that they have out of bounds # array offsets (e.g. uEm41.arc em4000.mod), we can't retrieve any vertices since we'd mess up the vertex buffer if blender_mesh_object.type == 'EMPTY': failed_count += 1 continue blender_mesh = blender_mesh_object.data vertex_format, vertices_array = _export_vertices(blender_mesh_object, bounding_box, saved_mod, i) vertex_buffer.extend(vertices_array) # TODO: is all this format conversion necessary? triangle_strips_python = triangles_list_to_triangles_strip(blender_mesh) # mod156 use global indices for verts, in case one only mesh is needed, probably triangle_strips_python = [e + vertex_position for e in triangle_strips_python] triangle_strips_ctypes = (ctypes.c_ushort * len(triangle_strips_python))(*triangle_strips_python) index_buffer.extend(triangle_strips_ctypes) vertex_count = len(blender_mesh.vertices) index_count = len(triangle_strips_python) m156 = meshes_156[i - failed_count] try: m156.material_index = materials.index(blender_mesh.materials[0]) except IndexError: raise ExportError('Mesh {} has no materials'.format(blender_mesh.name)) m156.constant = 1 m156.level_of_detail = saved_mesh.level_of_detail # TODO m156.vertex_format = CLASSES_TO_VERTEX_FORMATS[vertex_format] m156.vertex_stride = 32 m156.vertex_count = vertex_count m156.vertex_index_end = vertex_position + vertex_count - 1 m156.vertex_index_start_1 = vertex_position m156.vertex_offset = 0 m156.face_position = face_position m156.face_count = index_count m156.face_offset = 0 m156.vertex_index_start_2 = vertex_position m156.vertex_group_count = saved_mesh.vertex_group_count # len(blender_mesh_object.vertex_groups) # XXX: improve, not guaranteed if the weighting is modified (e.g. replacing a model) m156.bone_palette_index = saved_mesh.bone_palette_index # TODO: not using saved_mesh since it seems these are optional. Needs research m156.group_index = saved_mesh.group_index m156.unk_01 = saved_mesh.unk_01 # m156.unk_02 = saved_mesh.unk_02 # crashes if set to saved_mesh.unk_02 in ChrisNormal m156.unk_03 = saved_mesh.unk_03 m156.unk_04 = saved_mesh.unk_04 m156.unk_05 = saved_mesh.unk_05 m156.unk_06 = saved_mesh.unk_06 m156.unk_07 = saved_mesh.unk_07 m156.unk_08 = saved_mesh.unk_08 m156.unk_09 = saved_mesh.unk_09 m156.unk_10 = saved_mesh.unk_10 m156.unk_11 = saved_mesh.unk_11 vertex_position += vertex_count face_position += index_count vertex_buffer = (ctypes.c_ubyte * len(vertex_buffer)).from_buffer(vertex_buffer) index_buffer = (ctypes.c_ushort * (len(index_buffer) // 2)).from_buffer(index_buffer) return meshes_156, vertex_buffer, index_buffer