Export blender object to simple file format
Update 2015:
The following script generates a C/C++ header that you can include in your project. By default we will include the glad.h (see the glad repository on github) header to make sure we have the openGL defines/functions.
""" Exports the selected object in blender to class with the same name as the selected object. We create a vao and vbo the first time the draw function is called. To use this script: - Copy and paste the contents in to a new blender script - Execute/Run the script with the select object. - Save the header file. """ import bpy import math import mathutils import os import bmesh from mathutils import Vector, Matrix from bpy_extras.io_utils import ExportHelper from bpy.props import StringProperty, BoolProperty, EnumProperty from bpy.types import Operator o = bpy.context.active_object print("="*40) output = [] mat_x90 = mathutils.Matrix.Rotation(-math.pi/2, 4, 'X') conv_mat = o.matrix_world # Build the output list. # -------------------------------------------------- def add_vertex(v, n): v = conv_mat * v v = mat_x90 * v output.append(v.x) output.append(v.y) output.append(v.z) output.append(n.x) output.append(n.y) output.append(n.z) bm = bmesh.new() bm.from_mesh(o.data) bmesh.ops.triangulate(bm, faces=bm.faces) for face in bm.faces: for v in face.verts: add_vertex(v.co, face.normal) bm.free() del bm # -------------------------------------------------- def create_opengl_header(context, filepath): out_floats = [ '%.3f' % elem for elem in output ] out_floats_str = "float vertices[] = {" +",".join(out_floats) +"};" out_header = ("" "#ifndef " +o.name.upper() +"_H\n" "#define " +o.name.upper() +"_H\n" "\n" "#include <glad/glad.h>\n" "\n" "class " +o.name +" {\n" " public: \n" " void draw();\n" "};" "\n" "\n" "/* --------------------------------------------------------------- */\n" "\n" "inline void " +o.name +"::draw() {\n" "\n" " static GLuint vao = 0;\n" " static GLuint vbo = 0;\n" "\n" " int nvertices = " +str(len(output)) +";\n" " " +out_floats_str +"\n" "\n" " if (0 == vao) {\n" " glGenVertexArrays(1, &vao);\n" " glBindVertexArray(vao);\n" " glGenBuffers(1, &vbo);\n" " glBindBuffer(GL_ARRAY_BUFFER, vbo);\n" " glBufferData(GL_ARRAY_BUFFER, nvertices * sizeof(float), vertices, GL_STATIC_DRAW);\n" " glEnableVertexAttribArray(0); /* positions */\n" " glEnableVertexAttribArray(1); /* normals */\n" " glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (GLvoid*)0);\n" " glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (GLvoid*)12);\n" " }\n" "\n" " glBindVertexArray(vao);\n" " glDrawArrays(GL_TRIANGLES, 0, " +str(len(output)) +");\n" "}\n" "\n" "#endif\n" "") f = open(filepath, 'w', encoding='utf-8') f.write(out_header) f.close() return {'FINISHED'} # This snippet is necessary to popup the file save window. # Also adds this operator to the 'space bar menu' class ExportSomeData(Operator, ExportHelper): bl_idname = "export_opengl_header.exp" bl_label = "Export OpenGL Header(ROXLU)" filename_ext = ".h" filter_glob = StringProperty( default="*.h", options={'HIDDEN'}, ) def invoke(self, context, event): self.filepath = o.name +".h" context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} def execute(self, context): return create_opengl_header(context, self.filepath) def menu_func_export(self, context): self.layout.operator(ExportSomeData.bl_idname, text="Export OpenGL Header") def register(): bpy.utils.register_class(ExportSomeData) bpy.types.INFO_MT_file_export.append(menu_func_export) def unregister(): bpy.utils.unregister_class(ExportSomeData) bpy.types.INFO_MT_file_export.remove(menu_func_export) if __name__ == "__main__": register() bpy.ops.export_opengl_header.exp('INVOKE_DEFAULT')
Scripts from 2012, 2013, 2014:
Tiny script to export a blender object to a OBJ like of file. This format is very simple to parse. I use this sometimes when working on small simulations where I want to have a 3D model.
import bpy import math import mathutils from mathutils import Vector, Matrix o = bpy.context.active_object print("="*40) output = [] mat_x90 = mathutils.Matrix.Rotation(-math.pi/2, 4, 'X') conv_mat = o.matrix_world def add_vert(face_index, n): v = o.data.vertices[face_index].co v = conv_mat * v v = mat_x90 * v output.append("v %f %f %f %f %f %f" % (v.x, v.y, v.z, n.x, n.y, n.z)) def add_face(face, norm): if len(face) == 4: add_vert(face[0], norm) add_vert(face[1], norm) add_vert(face[2], norm) add_vert(face[0], norm) add_vert(face[2], norm) add_vert(face[3], norm) elif len(face) == 3: add_vert(face[0], norm) add_vert(face[1], norm) add_vert(face[2], norm) for face in o.data.polygons: verts = face.vertices[:] n = face.normal add_face(verts, n) out_str = "\n".join(map(str, output))
Another example which lets the user select the destination path of the exported file:
import bpy import math import mathutils from mathutils import Vector, Matrix from bpy_extras.io_utils import ExportHelper from bpy.props import StringProperty, BoolProperty, EnumProperty from bpy.types import Operator # Get the active object, create rotation matrix for Blender > GL, coordinate system o = bpy.context.active_object print("="*40) output = [] mat_x90 = mathutils.Matrix.Rotation(-math.pi/2, 4, 'X') conv_mat = o.matrix_world # Collect all vertices / normals def add_vert(face_index, n): v = o.data.vertices[face_index].co v = conv_mat * v v = mat_x90 * v output.append("v %f %f %f %f %f %f" % (v.x, v.y, v.z, n.x, n.y, n.z)) # Export the given face def add_face(face, norm): if len(face) == 4: add_vert(face[0], norm) add_vert(face[1], norm) add_vert(face[2], norm) add_vert(face[0], norm) add_vert(face[2], norm) add_vert(face[3], norm) elif len(face) == 3: add_vert(face[0], norm) add_vert(face[1], norm) add_vert(face[2], norm) # Called when user selected a file, saved the file def write_some_data(context, filepath): for face in o.data.polygons: verts = face.vertices[:] n = face.normal add_face(verts, n) out_str = "\n".join(map(str, output)) f = open(filepath, 'w', encoding='utf-8') f.write(out_str) f.close() return {'FINISHED'} # This snippet is necessary to popup the file save window. # Also adds this operator to the 'space bar menu' class ExportSomeData(Operator, ExportHelper): bl_idname = "export_simple_obj.exp" bl_label = "Export Simple Object (ROXLU)" filename_ext = ".txt" filter_glob = StringProperty( default="*.txt", options={'HIDDEN'}, ) def execute(self, context): return write_some_data(context, self.filepath) def menu_func_export(self, context): self.layout.operator(ExportSomeData.bl_idname, text="Simple Roxlu exporter") def register(): bpy.utils.register_class(ExportSomeData) bpy.types.INFO_MT_file_export.append(menu_func_export) def unregister(): bpy.utils.unregister_class(ExportSomeData) bpy.types.INFO_MT_file_export.remove(menu_func_export) if __name__ == "__main__": register() bpy.ops.export_simple_obj.exp('INVOKE_DEFAULT')
We transform from the default Blender orientation to the default openGL orientation (z pointing into the screen).
""" Exports the selected object in blender to an float array with vertices and normals. We do not use vertex indices but rather duplicate each triangle. """ import bpy import math import mathutils from mathutils import Vector, Matrix o = bpy.context.active_object print("="*40) output = [] mat_x90 = mathutils.Matrix.Rotation(-math.pi/2, 4, 'X') conv_mat = o.matrix_world def add_vert(face_index, n): v = o.data.vertices[face_index].co v = conv_mat * v v = mat_x90 * v output.append(v.x) output.append(v.y) output.append(v.z) output.append(n.x) output.append(n.y) output.append(n.z) def add_face(face, norm): if len(face) == 4: add_vert(face[0], norm) add_vert(face[1], norm) add_vert(face[2], norm) add_vert(face[0], norm) add_vert(face[2], norm) add_vert(face[3], norm) elif len(face) == 3: add_vert(face[0], norm) add_vert(face[1], norm) add_vert(face[2], norm) for face in o.data.polygons: verts = face.vertices[:] n = face.normal add_face(verts, n) out_str = "int nvertices = " +str(len(output)) +";\n" out_floats = [ '%.3f' % elem for elem in output ] out_str += "float vertices[] = {" +",".join(out_floats) +"};" dest = "path_to_your_header_file/bone.h" f = open(dest, 'w+') f.write(out_str) f.close() print("Created.")
And another updated (May 2017) version which exports the vertices into a .h and .cpp file in the same directory as the .blend file and correctly sets some external variables.
""" Exports the selected object in blender to an float array with vertices and normals. The vertices are rotated into the OpenGL axis system. We do not use vertex indices but rather duplicate each triangle. We create a .h and .cpp file called "Model[ObjectName].h/cpp" and save it into the same location as the .blend file. """ import bpy import math import mathutils from mathutils import Vector, Matrix o = bpy.context.active_object print("="*40) output = [] mat_x90 = mathutils.Matrix.Rotation(-math.pi/2, 4, 'X') conv_mat = o.matrix_world def add_vert(face_index, n): v = o.data.vertices[face_index].co v = conv_mat * v v = mat_x90 * v output.append(v.x) output.append(v.y) output.append(v.z) output.append(n.x) output.append(n.y) output.append(n.z) def add_face(face, norm): if len(face) == 4: add_vert(face[0], norm) add_vert(face[1], norm) add_vert(face[2], norm) add_vert(face[0], norm) add_vert(face[2], norm) add_vert(face[3], norm) elif len(face) == 3: add_vert(face[0], norm) add_vert(face[1], norm) add_vert(face[2], norm) for face in o.data.polygons: verts = face.vertices[:] n = face.normal add_face(verts, n) model_name = o.name; out_header_str = "" \ +"#ifndef MODEL_" +model_name.upper() +"_H\n" \ +"#define MODEL_" +model_name.upper() +"_H\n" \ +"extern int nfloats_" +model_name.lower() +";\n" \ +"extern int nvertices_" +model_name.lower() +";\n" \ +"extern float vertices_" +model_name.lower() +"[];\n" \ +"#endif" out_cpp_str = "" \ +"#include \"Model" +model_name +".h\"\n" \ +"int nfloats_" +model_name.lower() +" = " +str(len(output)) +";\n" \ +"int nvertices_" +model_name.lower() +" = " +str((int(len(output) / 6))) +";\n" out_cpp_floats = [ '%.3f' % elem for elem in output ] out_cpp_str += "float vertices_" +model_name.lower() +"[] = {" +",".join(out_cpp_floats) +"};" header_filepath = bpy.path.abspath("//") +"Model" +model_name +".h" cpp_filepath = bpy.path.abspath("//") +"Model" +model_name +".cpp" f = open(cpp_filepath, 'w+') f.write(out_cpp_str) f.close() f = open(header_filepath, "w+") f.write(out_header_str) f.close() print("Created.")