Back to posts.

Blender 2.8 OpenGL Buffer Exporter

Recently I needed to create a test case while integrating the Google Filament library into my engine. For this test case I wanted to get the vertices from the selected object in Blender, into a buffer that I could render with OpenGL.

The following Blender script creates a [name].h and [name].cpp for the selected object and stores the positions, normals and UVs. Note that this is a very basic script with a minimal amount of error checking.

# roxlu - 2019-08-04 - Blender 2.8
# ------------------------------------------------
# Very basic script that exports the vertices of the selected object
# to a .h and .cpp that you can use into your graphics app directly.
# Vertices are rotated to Y-up, -Z forward, following common OpenGL.
#
#
# Version:
# ------------------------------------------------
# v0.0.0  - 2019-08-04:   First release 
#
# Reference:
# ------------------------------------------------
# http://roxlu.com/2019/061/blender-2-8-opengl-buffer-exporter
# http://roxlu.com/2012/001/export-blender-object-to-simple-file-format
 
import bpy
import math
import mathutils
from mathutils import Vector, Matrix
 
o = bpy.context.active_object
output_vertices = []
output_indices = []
mat_x90 = mathutils.Matrix.Rotation(-math.pi/2, 4, 'X')
conv_mat = o.matrix_world
depsgraph = bpy.context.evaluated_depsgraph_get()
 
print("="*40)
 
def store_vertex(mesh, uvLayer, loopTriangle, loopIndex):
    loop = mesh.loops[loopIndex]
    dx = loop.vertex_index
    vert = mesh.vertices[dx]
    if loopTriangle.use_smooth:
        norm = vert.normal
    else:
        norm = loopTriangle.normal
    uv = uvLayer[loopIndex].uv
    pos = o.matrix_world @ vert.co
    pos = mat_x90 @ pos
    norm = o.matrix_world @ norm
    norm = mat_x90 @ norm       
    output_vertices.extend(pos)
    output_vertices.extend(norm)
    output_vertices.extend(uv)
 
for mesh in bpy.data.meshes:
 
    # we only export the selected object.
    if mesh.name != o.name:
        continue
 
    # get uv layer
    uv_layer = mesh.uv_layers.active and mesh.uv_layers.active.data
    if not uv_layer:
        continue
 
    # output the vertices, using simple indices.
    mesh.calc_loop_triangles()
    for lt in mesh.loop_triangles:
        va = store_vertex(mesh, uv_layer, lt, lt.loops[0])
        vb = store_vertex(mesh, uv_layer, lt, lt.loops[1])
        vc = store_vertex(mesh, uv_layer, lt, lt.loops[2])
        output_indices.append(len(output_indices))
        output_indices.append(len(output_indices))
        output_indices.append(len(output_indices))
 
# Create the header + cpp
name = o.name.lower();
 
# header
out_header_str = "" \
 +"#ifndef MODEL_" +name.upper() +"_H\n" \
 +"#define MODEL_" +name.upper() +"_H\n" \
 +"extern int nfloats_" +name +";\n" \
 +"extern int nvertices_" +name +";\n" \
 +"extern int nindices_" +name +";\n" \
 +"extern float vertices_" +name +"[];\n" \
 +"extern int indices_" +name +"[];\n" \
 +"#endif"
 
# source
out_cpp_str = "" \
 +"#include \"model_" +name +".h\"\n" \
 +"int nfloats_"      +name +" = " +str(len(output_vertices))            +";\n" \
 +"int nvertices_"    +name +" = " +str((int(len(output_vertices) / 8))) +";\n" \
 +"int nindices_"     +name +" = " +str((int(len(output_indices))))      +";\n"
 
# collect values.
out_cpp_floats = [ '%.3f' % elem for elem in output_vertices ]
out_cpp_ints   = [ '%d'   % elem for elem in output_indices ]
out_cpp_str    += "float vertices_" +name +"[] = {"  +",".join(out_cpp_floats) +"};\n"
out_cpp_str    += "int indices_"    +name +"[] = { " +",".join(out_cpp_ints)   +"};\n"
 
# write header and values.
header_filepath = bpy.path.abspath("//") +"model_" +name +".h"
cpp_filepath = bpy.path.abspath("//") +"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("Written %s." % header_filepath)
print("Written %s." % cpp_filepath)