OverviewFilesCommitsIssuesDocsReleasesPeers

blender-bridge commits / df8adb7

fix: send triangle indices from Blender, use unified blob format
- Change detector now sends has_normals flag + index buffer (from calc_loop_triangles) matching the C++ export format - Godot import reads has_normals flag and index buffer for both directions (Godot→Blender and Blender→Godot) - Fixes "vertex count not multiple of 3" error Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
df8adb7cc32d6803b34b4e8e0ae902448b9d93d8
by plankatron <fleeky@gmail.com> · 2026-03-03T00:56:16.000Z

Changed files (2) +49 -13

blender_addon/change_detector.py +18 -1
……@@ -178,12 +178,29 @@ class ChangeDetector:
178178 written = self._shm.write_blob_array(blob_offset, coords)
179179 blob_offset += written
180180
181 # Bulk-read normals (calc_normals removed in Blender 5.0)
181 # has_normals flag + normals
182182 normals = np.empty(vert_count * 3, dtype=np.float32)
183183 mesh.vertices.foreach_get("normal", normals)
184 self._shm.write_blob(blob_offset, struct.pack("<I", 1))
185 blob_offset += 4
184186 written = self._shm.write_blob_array(blob_offset, normals)
185187 blob_offset += written
186188
189 # Triangle indices from loop_triangles
190 mesh.calc_loop_triangles()
191 tri_count = len(mesh.loop_triangles)
192 idx_count = tri_count * 3
193 self._shm.write_blob(
194 blob_offset, struct.pack("<I", idx_count)
195 )
196 blob_offset += 4
197
198 if idx_count > 0:
199 indices = np.empty(idx_count, dtype=np.int32)
200 mesh.loop_triangles.foreach_get("vertices", indices)
201 written = self._shm.write_blob_array(blob_offset, indices)
202 blob_offset += written
203
187204 data_size = blob_offset - data_start
188205
189206 # Write object entry
src/blender_bridge.cpp +31 -12
……@@ -486,44 +486,60 @@ void BlenderBridge::import_changes(Node *root) {
486486 if (entry.update_type == UPDATE_VERTEX || entry.update_type == UPDATE_FULL) {
487487 MeshInstance3D *mesh_inst = Object::cast_to<MeshInstance3D>(target);
488488 if (mesh_inst && entry.data_size > TRANSFORM_SIZE + 4) {
489 uint32_t vert_offset = entry.data_offset + TRANSFORM_SIZE;
489 uint32_t cursor = entry.data_offset + TRANSFORM_SIZE;
490490 uint32_t vert_count;
491 memcpy(&vert_count, base + vert_offset, 4);
492 vert_offset += 4;
491 memcpy(&vert_count, base + cursor, 4);
492 cursor += 4;
493493
494494 PackedVector3Array positions;
495495 positions.resize(vert_count);
496496 Vector3 *pos_w = positions.ptrw();
497497 for (uint32_t v = 0; v < vert_count; v++) {
498498 float in[3];
499 memcpy(in, base + vert_offset, 12);
500 vert_offset += 12;
501 // Blender Z-up → Godot Y-up
499 memcpy(in, base + cursor, 12);
500 cursor += 12;
502501 pos_w[v].x = in[0];
503502 pos_w[v].y = in[2];
504503 pos_w[v].z = -in[1];
505504 }
506505
507 // Read normals if present
506 // Read has_normals flag
508507 PackedVector3Array normals;
509 uint32_t expected_normal_end = vert_offset + vert_count * 12;
510 if (expected_normal_end <= entry.data_offset + entry.data_size) {
508 uint32_t has_normals = 0;
509 if (cursor + 4 <= entry.data_offset + entry.data_size) {
510 memcpy(&has_normals, base + cursor, 4);
511 cursor += 4;
512 }
513
514 if (has_normals && cursor + vert_count * 12 <= entry.data_offset + entry.data_size) {
511515 normals.resize(vert_count);
512516 Vector3 *norm_w = normals.ptrw();
513517 for (uint32_t v = 0; v < vert_count; v++) {
514518 float in[3];
515 memcpy(in, base + vert_offset, 12);
516 vert_offset += 12;
519 memcpy(in, base + cursor, 12);
520 cursor += 12;
517521 norm_w[v].x = in[0];
518522 norm_w[v].y = in[2];
519523 norm_w[v].z = -in[1];
520524 }
521525 }
522526
527 // Read index buffer
528 PackedInt32Array indices;
529 uint32_t idx_count = 0;
530 if (cursor + 4 <= entry.data_offset + entry.data_size) {
531 memcpy(&idx_count, base + cursor, 4);
532 cursor += 4;
533 }
534
535 if (idx_count > 0 && cursor + idx_count * 4 <= entry.data_offset + entry.data_size) {
536 indices.resize(idx_count);
537 memcpy(indices.ptrw(), base + cursor, idx_count * 4);
538 }
539
523540 // Rebuild mesh surface — convert to ArrayMesh if needed
524541 Ref<ArrayMesh> arr_mesh = Object::cast_to<ArrayMesh>(mesh_inst->get_mesh().ptr());
525542 if (!arr_mesh.is_valid()) {
526 // Procedural mesh (BoxMesh, etc.) — replace with ArrayMesh
527543 arr_mesh.instantiate();
528544 mesh_inst->set_mesh(arr_mesh);
529545 }
……@@ -534,6 +550,9 @@ void BlenderBridge::import_changes(Node *root) {
534550 if (normals.size() > 0) {
535551 arrays[Mesh::ARRAY_NORMAL] = normals;
536552 }
553 if (indices.size() > 0) {
554 arrays[Mesh::ARRAY_INDEX] = indices;
555 }
537556
538557 arr_mesh->clear_surfaces();
539558 arr_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);