diff --git a/tools/convert-mesh/CMakeLists.txt b/tools/convert-mesh/CMakeLists.txt new file mode 100644 index 00000000..60253cd1 --- /dev/null +++ b/tools/convert-mesh/CMakeLists.txt @@ -0,0 +1,33 @@ +set(PSEMEK_CONVERT_MESH_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE PATH "Path to convert-mesh tool") + +function(psemek_add_model TARGET) + if(PSEMEK_PACKAGE_MODE) + if(NOT (TARGET ${TARGET})) + return() + endif() + endif() + + get_target_property(_OUT_DIR ${TARGET} BINARY_DIR) + + while(ARGN) + list(GET ARGN 0 _FILE) + list(GET ARGN 1 _NAME) + list(REMOVE_AT ARGN 0 1) + + get_filename_component(_NAME_PREFIX ${_NAME} DIRECTORY) + + file(MAKE_DIRECTORY "${_OUT_DIR}/meshes/${_NAME_PREFIX}") + + set(_OUT_FILE "${_OUT_DIR}/meshes/${_NAME}") + + set(_SCRIPT "${PSEMEK_CONVERT_MESH_DIR}/bin/convert-mesh.py") + + add_custom_command(OUTPUT "${_OUT_FILE}" + COMMAND blender --background "${_FILE}" --python "${_SCRIPT}" --python-exit-code 1 -- "${_OUT_FILE}" + DEPENDS "${_FILE}" "${_SCRIPT}" + WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" + ) + + psemek_add_resources(${TARGET} "${_OUT_FILE}" ${_NAME}) + endwhile() +endfunction() diff --git a/tools/convert-mesh/bin/convert-mesh.py b/tools/convert-mesh/bin/convert-mesh.py new file mode 100644 index 00000000..2c2667c9 --- /dev/null +++ b/tools/convert-mesh/bin/convert-mesh.py @@ -0,0 +1,151 @@ +import bpy +import math +import sys +import struct + +bpy.ops.object.mode_set(mode = 'OBJECT') + +mesh = None +flat = False +if 'mesh' in bpy.data.objects: + mesh = bpy.data.objects['mesh'].data + print('Using smooth normals') +if 'mesh_flat' in bpy.data.objects: + mesh = bpy.data.objects['mesh_flat'].data + print('Using flat normals') + flat = True +assert mesh + +colors = None +if len(mesh.vertex_colors) > 0: + colors = mesh.vertex_colors.active.data + print('Found vertex colors') + +texcoords = None +if len(mesh.uv_layers) > 0: + texcoords = mesh.uv_layers.active.data + print('Found texture coordinates') + +skeleton = None +if 'skeleton' in bpy.data.objects: + skeleton = bpy.data.objects['skeleton'].data + print('Found skeleton') + +POSITION_MASK = 1 +NORMAL_MASK = 2 +COLOR_MASK = 4 +TEXCOORD_MASK = 8 + +vertex_format = POSITION_MASK | NORMAL_MASK +if colors: + vertex_format |= COLOR_MASK +if texcoords: + vertex_format |= TEXCOORD_MASK +print("Using vertex format", format(vertex_format, '04b')) + +vertex_coords = [] +vertex_normals = [] +vertex_colors = [] +vertex_texcoords = [] + +indices = [] + +mesh.calc_loop_triangles() + +if flat: + for p in mesh.loop_triangles: + for li in p.loops: + i = len(vertex_coords) + v = mesh.vertices[mesh.loops[li].vertex_index] + vertex_coords.append((v.co.x, v.co.y, v.co.z)) + vertex_normals.append((p.normal.x, p.normal.y, p.normal.z)) + if colors: + vertex_colors.append(tuple(colors[li].color)) + if texcoords: + vertex_texcoords.append(tuple(texcoords[li].uv)) + indices.append(i) + +else: + for v in mesh.vertices: + vertex_coords.append((v.co.x, v.co.y, v.co.z)) + vertex_normals.append((v.normal.x, v.normal.y, v.normal.z)) + + if colors: + vertex_colors = [None] * len(vertex_coords) + if texcoords: + vertex_texcoords = [None] * len(vertex_coords) + + for p in mesh.loop_triangles: + for li in p.loops: + vi = mesh.loops[li].vertex_index + indices.append(vi) + v = mesh.vertices[vi] + if colors: + vertex_colors[vi] = tuple(colors[li].color) + if texcoords: + vertex_texcoords[vi] = tuple(texcoords[li].uv) + +assert (len(indices) % 3) == 0 +assert len(vertex_coords) == len(vertex_normals) +if colors: + assert len(vertex_coords) == len(vertex_colors) +if texcoords: + assert len(vertex_coords) == len(vertex_texcoords) + +if colors: + for i in range(len(vertex_colors)): + c = 0 + for k in (3, 2, 1, 0): + c = (c << 8) | int(max(0, min(255, vertex_colors[i][k] * 255))) + vertex_colors[i] = c + +vertices = [] +for i in range(len(vertex_coords)): + attribs = [vertex_coords[i], vertex_normals[i]] + if colors: + attribs.append(vertex_colors[i]) + if texcoords: + attribs.append(vertex_texcoords[i]) + vertices.append(tuple(attribs)) + +print(len(vertices), 'vertices') +print(len(indices), 'indices') + +if skeleton is not None: + bones = [] + for b in skeleton.data.bones: + if b.parent is None: + bones.append(-1) + else: + pi = list(skeleton.data.bones).index(b.parent) + bones.append(pi) + +def to_bytes(obj): + if type(obj) == int: + if obj < 0: + return struct.pack('