def test_unpack_pack_half_float(): # FIXME: Research half float and find out if these are actual limitations or # a bug in the function. # if it's not an actual limitation, raise an exception # https://en.wikipedia.org/wiki/Half-precision_floating-point_format expected_fail = 0 for short_input in range(0, 65535): if (short_input in range(31745, 33792) or short_input in range(1, 1024) or short_input in range(64512, 65535)): expected_fail += 1 else: float_output = unpack_half_float(short_input) short_again = pack_half_float(float_output) assert short_input == short_again assert expected_fail == 4093
def _export_vertices(blender_mesh_object, bounding_box, saved_mod, mesh_index): saved_mesh = saved_mod.meshes_array[mesh_index] if saved_mod.bone_palette_array: bone_palette = saved_mod.bone_palette_array[ saved_mesh.bone_palette_index].values[:] else: bone_palette = [] blender_mesh = blender_mesh_object.data vertex_count = len(blender_mesh.vertices) weights_per_vertex = get_bone_indices_and_weights_per_vertex( blender_mesh_object) # TODO: check the number of uv layers uvs_per_vertex = get_uvs_per_vertex(blender_mesh_object.data, blender_mesh_object.data.uv_layers[0]) VF = VERTEX_FORMATS_TO_CLASSES[saved_mesh.vertex_format] ''' # Unfortunately this fails in some cases and could crash the game; until some mesh unknowns are figured out, # relying on saved_mesh data # e.g. pl0000.mod from uPl00ChrisNormal.arc, meshes_array[30] has max bones per vertex = 4, but the # original file has 5 if weights_per_vertex: max_bones_per_vertex = max({len(data) for data in weights_per_vertex.values()}) if max_bones_per_vertex > 8: raise RuntimeError("The mesh '{}' contains some vertex that are weighted by " "more than 8 bones, which is not supported. Fix it and try again" .format(blender_mesh.name)) VF = VERTEX_FORMATS_TO_CLASSES[max_bones_per_vertex] ''' for vertex_index, (uv_x, uv_y) in uvs_per_vertex.items(): # flipping for dds textures uv_y *= -1 uv_x = pack_half_float(uv_x) uv_y = pack_half_float(uv_y) uvs_per_vertex[vertex_index] = (uv_x, uv_y) if uvs_per_vertex and len(uvs_per_vertex) != vertex_count: # TODO: logging print('There are some vertices with no uvs in mesh in {}.' 'Vertex count: {} UVs per vertex: {}'.format( blender_mesh.name, vertex_count, len(uvs_per_vertex))) box_width = abs(bounding_box.min_x * 100) + abs(bounding_box.max_x * 100) box_height = abs(bounding_box.min_y * 100) + abs(bounding_box.max_y * 100) box_length = abs(bounding_box.min_z * 100) + abs(bounding_box.max_z * 100) vertices_array = (VF * vertex_count)() has_bones = hasattr(VF, 'bone_indices') has_tangents = hasattr(VF, 'tangent_x') has_second_uv_layer = hasattr(VF, 'uv2_x') original_vertices_array = get_vertices_array( saved_mod, saved_mod.meshes_array[mesh_index]) for vertex_index, vertex in enumerate(blender_mesh.vertices): vertex_struct = vertices_array[vertex_index] original_vertex_struct = original_vertices_array[vertex_index] if weights_per_vertex: weights_data = weights_per_vertex[ vertex_index] # list of (bone_index, value) bone_indices = [ bone_palette.index(bone_index) for bone_index, _ in weights_data ] weight_values = [ round(weight_value * 255) for _, weight_value in weights_data ] else: bone_indices = [] weight_values = [] xyz = (vertex.co[0] * 100, vertex.co[1] * 100, vertex.co[2] * 100) xyz = z_up_to_y_up(xyz) if has_bones: # applying bounding box constraints xyz = vertices_export_locations(xyz, box_width, box_length, box_height) vertex_struct.position_x = xyz[0] vertex_struct.position_y = xyz[1] vertex_struct.position_z = xyz[2] vertex_struct.position_w = 32767 if has_bones: array_size = ctypes.sizeof(vertex_struct.bone_indices) vertex_struct.bone_indices = (ctypes.c_ubyte * array_size)(*bone_indices) vertex_struct.weight_values = (ctypes.c_ubyte * array_size)(*weight_values) ''' vertex_struct.normal_x = round(vertex.normal[0] * 127) vertex_struct.normal_y = round(vertex.normal[2] * 127) * -1 vertex_struct.normal_z = round(vertex.normal[1] * 127) vertex_struct.normal_w = -1 ''' # XXX quick hack until normals can be exported ok in blender (gotta check fbx export or ask Bastien Montagne) vertex_struct.normal_x = original_vertex_struct.normal_x vertex_struct.normal_y = original_vertex_struct.normal_y vertex_struct.normal_z = original_vertex_struct.normal_z vertex_struct.normal_w = original_vertex_struct.normal_w if has_tangents: vertex_struct.tangent_x = original_vertex_struct.tangent_x vertex_struct.tangent_y = original_vertex_struct.tangent_x vertex_struct.tangent_z = original_vertex_struct.tangent_x vertex_struct.tangent_w = original_vertex_struct.tangent_x ''' vertex_struct.tangent_x = -1 vertex_struct.tangent_y = -1 vertex_struct.tangent_z = -1 vertex_struct.tangent_w = -1 ''' vertex_struct.uv_x = uvs_per_vertex.get( vertex_index, (0, 0))[0] if uvs_per_vertex else 0 vertex_struct.uv_y = uvs_per_vertex.get( vertex_index, (0, 0))[1] if uvs_per_vertex else 0 if has_second_uv_layer: vertex_struct.uv2_x = 0 vertex_struct.uv2_y = 0 return VF, vertices_array
def _export_vertices(blender_mesh_object, bounding_box, saved_mod, mesh_index): saved_mesh = saved_mod.meshes_array[mesh_index] if saved_mod.bone_palette_array: bone_palette = saved_mod.bone_palette_array[saved_mesh.bone_palette_index].values[:] else: bone_palette = [] blender_mesh = blender_mesh_object.data vertex_count = len(blender_mesh.vertices) weights_per_vertex = get_bone_indices_and_weights_per_vertex(blender_mesh_object) # TODO: check the number of uv layers uvs_per_vertex = get_uvs_per_vertex(blender_mesh_object.data, blender_mesh_object.data.uv_layers[0]) VF = VERTEX_FORMATS_TO_CLASSES[saved_mesh.vertex_format] ''' # Unfortunately this fails in some cases and could crash the game; until some mesh unknowns are figured out, # relying on saved_mesh data # e.g. pl0000.mod from uPl00ChrisNormal.arc, meshes_array[30] has max bones per vertex = 4, but the # original file has 5 if weights_per_vertex: max_bones_per_vertex = max({len(data) for data in weights_per_vertex.values()}) if max_bones_per_vertex > 8: raise RuntimeError("The mesh '{}' contains some vertex that are weighted by " "more than 8 bones, which is not supported. Fix it and try again" .format(blender_mesh.name)) VF = VERTEX_FORMATS_TO_CLASSES[max_bones_per_vertex] ''' for vertex_index, (uv_x, uv_y) in uvs_per_vertex.items(): # flipping for dds textures uv_y *= -1 uv_x = pack_half_float(uv_x) uv_y = pack_half_float(uv_y) uvs_per_vertex[vertex_index] = (uv_x, uv_y) if uvs_per_vertex and len(uvs_per_vertex) != vertex_count: # TODO: logging print('There are some vertices with no uvs in mesh in {}.' 'Vertex count: {} UVs per vertex: {}'.format(blender_mesh.name, vertex_count, len(uvs_per_vertex))) box_width = abs(bounding_box.min_x * 100) + abs(bounding_box.max_x * 100) box_height = abs(bounding_box.min_y * 100) + abs(bounding_box.max_y * 100) box_length = abs(bounding_box.min_z * 100) + abs(bounding_box.max_z * 100) vertices_array = (VF * vertex_count)() has_bones = hasattr(VF, 'bone_indices') has_tangents = hasattr(VF, 'tangent_x') has_second_uv_layer = hasattr(VF, 'uv2_x') original_vertices_array = get_vertices_array(saved_mod, saved_mod.meshes_array[mesh_index]) for vertex_index, vertex in enumerate(blender_mesh.vertices): vertex_struct = vertices_array[vertex_index] original_vertex_struct = original_vertices_array[vertex_index] if weights_per_vertex: weights_data = weights_per_vertex[vertex_index] # list of (bone_index, value) bone_indices = [bone_palette.index(bone_index) for bone_index, _ in weights_data] weight_values = [round(weight_value * 255) for _, weight_value in weights_data] else: bone_indices = [] weight_values = [] xyz = (vertex.co[0] * 100, vertex.co[1] * 100, vertex.co[2] * 100) xyz = z_up_to_y_up(xyz) if has_bones: # applying bounding box constraints xyz = vertices_export_locations(xyz, box_width, box_length, box_height) vertex_struct.position_x = xyz[0] vertex_struct.position_y = xyz[1] vertex_struct.position_z = xyz[2] vertex_struct.position_w = 32767 if has_bones: array_size = ctypes.sizeof(vertex_struct.bone_indices) vertex_struct.bone_indices = (ctypes.c_ubyte * array_size)(*bone_indices) vertex_struct.weight_values = (ctypes.c_ubyte * array_size)(*weight_values) ''' vertex_struct.normal_x = round(vertex.normal[0] * 127) vertex_struct.normal_y = round(vertex.normal[2] * 127) * -1 vertex_struct.normal_z = round(vertex.normal[1] * 127) vertex_struct.normal_w = -1 ''' # XXX quick hack until normals can be exported ok in blender (gotta check fbx export or ask Bastien Montagne) vertex_struct.normal_x = original_vertex_struct.normal_x vertex_struct.normal_y = original_vertex_struct.normal_y vertex_struct.normal_z = original_vertex_struct.normal_z vertex_struct.normal_w = original_vertex_struct.normal_w if has_tangents: vertex_struct.tangent_x = original_vertex_struct.tangent_x vertex_struct.tangent_y = original_vertex_struct.tangent_x vertex_struct.tangent_z = original_vertex_struct.tangent_x vertex_struct.tangent_w = original_vertex_struct.tangent_x ''' vertex_struct.tangent_x = -1 vertex_struct.tangent_y = -1 vertex_struct.tangent_z = -1 vertex_struct.tangent_w = -1 ''' vertex_struct.uv_x = uvs_per_vertex.get(vertex_index, (0, 0))[0] if uvs_per_vertex else 0 vertex_struct.uv_y = uvs_per_vertex.get(vertex_index, (0, 0))[1] if uvs_per_vertex else 0 if has_second_uv_layer: vertex_struct.uv2_x = 0 vertex_struct.uv2_y = 0 return VF, vertices_array