3
$\begingroup$

I am writing an importer and exporter for a custom binary format. In this format I have triangulated mesh. Each vertex has position, normal, and UV.

The importer code sets them like this and it is displayed properly:

mesh = MyMesh(file_name)
bl_mesh = bpy.data.meshes.new(object_name)
bl_mesh.from_pydata(mesh.positions, [], mesh.indices)
bl_mesh.create_normals_split()
for l in bl_mesh.loops:
    l.normal[:] = mesh.normals[l.vertex_index]

bl_mesh.normals_split_custom_set_from_vertices(mesh.normals)
bl_mesh.use_auto_smooth = True

But the exporter code does not give me the same results.

mesh = MyMesh()
bl_mesh.calc_normals_split()
for l in bl_mesh.loops:
    mesh.indices.append(l.vertex_index)
    mesh.normals.insert(l.vertex_index, (*l.normal[:]))

enter image description here

I have tried accessing the obj.data.vertices[].normal but the normals there are wrong as well.

How do I get custom normals from the mesh?

UPD. The full source code of the extension is in this repo. The model used is nvx/model.nvx from the repo. All interactions with blender objects happen in the io_nebula/bl_nvx.py

$\endgroup$
2
  • $\begingroup$ Could give a running version of your script with example file, so we can test it ourselves? It is kind of hard to pinpoint the bug with just snippets $\endgroup$
    – WhatAMesh
    Feb 21, 2019 at 2:32
  • $\begingroup$ @WhatAMesh the extension can be found in this repo github.com/Teivaz/PNUnpacker and the mentioned mesh file you can find in the “nvx/model.nvx” in the repo $\endgroup$
    – Teivaz
    Feb 21, 2019 at 7:17

1 Answer 1

2
$\begingroup$

After hours of debugging I found out that list.insert function does not do what I expected (I was not alone in this assumption https://stackoverflow.com/q/25840177/3344612). And the order of vertices was messed because I inserted at a wrong index.

Answering my question. To get custom normals first call calc_normals_split() on the mesh and then access through loops[].normal with corresponding vertex indices in loops[].vertex_index.

To fix the mentioned code first resize the list and then modify with [] operator.

mesh.normals = [None] * len(bl_mesh.vertices)
for l in bl_mesh.loops:
    mesh.indices.append(l.vertex_index)
    mesh.normals[l.vertex_index] = l.normal[:]
$\endgroup$
2
  • $\begingroup$ When this works, it works great. But in some not very clean meshes you can end up with "None" elements in your normals[] array. $\endgroup$ Sep 22, 2020 at 23:38
  • 1
    $\begingroup$ This is answer is not entirely correct. The number of MeshLoop's in a Blender mesh does not equal to the number of vertices. Loop is a face corner, so each face has at least 3 or more of the loops. Multiple loops can point to the same vertex. In this case, this algorithm only extracts the normal data partially. So, in order to extract the data correctly, you need to calculate the average normal. And even in that case, the custom normal data will only be approximated. $\endgroup$
    – D. Skarn
    Apr 16, 2022 at 9:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .