It took me a while longer to get this ready for you, sorry.
here is my approach to export mesh data as json.
I decided to leave you the whole code in context, so that you can really try to understand how to calculate the split normals.
I also left how to extract uvs, vertices, positions and how to do this for subsets of polygons.
from bpy_extras.io_utils import unpack_list
def parse_mesh(bl_mesh, faces=None):
"""
Parses a blender mesh into json arrays
Example:
Non-interleaved:
positions: [vx, vy, vz, ... ] size: multiple of 3
normals: [nx, ny, nz, ...] size: multiple of 3
uv: [u1, v1, ...] optional, multiple of 2
uv2: [u2, v2, ...] optional, multiple of 2
Interleaved: )
(... TODO)
Args:
bl_mesh ('bpy.types.Mesh') - The mesh data block to parse.
Kwargs:
faces ('list(bpy.types.MeshPolygon)') - The subset of polygons to parse.
Returns:
al_mesh ('dict') - The json mesh dictionary.
"""
# UV Textures by name
texture_uvs = bl_mesh.tessface_uv_textures.get('UVMap')
if faces is None:
faces = bl_mesh.polygons
_vertices = []
_normals = []
_uvs = []
_uvs2 = []
# Used for split normals export
face_index_pairs = [(face, index) for index, face in enumerate(faces)]
# Calculate the split normals from the shading
bl_mesh.calc_normals_split()
loops = bl_mesh.loops
for face, face_index in face_index_pairs:
# gather the face vertices
face_vertices = [bl_mesh.vertices[v] for v in face.vertices]
face_vertices_length = len(face_vertices)
vertices = [(v.co.x, v.co.y, v.co.z) for v in face_vertices]
# This is where you extract the split normals from the mesh
normals = [(loops[l_idx].normal.x, loops[l_idx].normal.y, loops[l_idx].normal.z) for l_idx in face.loop_indices]
uvs = [None] * face_vertices_length
if texture_uvs:
uv_layer = texture_uvs.data[face.index].uv
uvs = [(uv[0], uv[1]) for uv in uv_layer]
_vertices += vertices
_normals += normals
_uvs += uvs
_uvs2 += uvs2
al_mesh = OrderedDict()
al_mesh['vertices'] = unpack_list(_vertices)
al_mesh['normals'] = unpack_list(_normals)
if texture_uvs:
al_mesh['uvs'] = unpack_list(_uvs)
return al_mesh
you should perform the following operations on the blender object before calling the parse_mesh function (this function returns an altered copy of the mesh datablock, with triangulated faces and applied modifiers, so if you for example use an edge split modifier on a smooth mesh with angle of 66°, you will get excactly this result in your export.):
def prepare_mesh(obj):
# This applies all the modifiers (without altering the scene)
mesh = obj.to_mesh(context.scene, apply_modifiers=True, settings='RENDER')
# Triangulate for web export
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.triangulate(bm, faces=bm.faces)
bm.to_mesh(mesh)
bm.free()
del bm
mesh.calc_normals()
mesh.calc_tessface()
return mesh
if you need further information, ask me anytime. Or better, if you find improvements, I'd love to hear them!