在 Blender 的 Python API 中访问指向顶点的 C 指针

问题描述 投票:0回答:1

我目前正在用 C 和 C++ 为 Blender 制作渲染引擎。我想通过指针从 C 语言访问网格的顶点,以减少在 Python 中花费的时间并避免不必要的数据重复。

我知道从

ID
类派生的对象有一个
as_pointer()
方法可用。但是,当我尝试在网格的顶点集合上使用
as_pointer()
时,如下所示:

mesh = bpy.context.object.data
pointer = mesh.vertices.as_pointer()

我收到一条错误,指出

bpy_prop_collection
对象没有属性
as_pointer
,这是有道理的,因为
bpy_prop_collection
不是派生自
ID

文档提到

vertices
的类型是“MeshVertices bpy_prop_collection of MeshVertex,(只读)”(doc),并且
MeshVertices
应该返回一个指针,但这不是两者的类型
vertices
也不是它的元素。

作为解决方法,我一直将顶点数据检索到 numpy 数组中,然后将其传递到我的 C 库,如以下示例代码所示:

import bpy
import numpy as np
import ctypes

obj = bpy.context.object  # Suppose 'obj' is the mesh object
mesh = obj.data

# Allocate an array and copy the data, then set the pointer
# of the struct to the array
vert_array = np.zeros((len(mesh.vertices) * 3), dtype=np.float32)
mesh.vertices.foreach_get("co", vert_array)
vert_ptr = vert_array.ctypes.data_as(ctypes.POINTER(ctypes.c_float))

# Pass the pointer
lib = ctypes.CDLL("bin/libengine.so")
lib.load_vert.argtypes = [ctypes.POINTER(ctypes.float)]
lib.load_vert(vert_ptr)

但是,这种方法会复制内存中的顶点数据(一次在 Blender 的内部数据结构中,一次在 numpy 数组中),并且需要进行可以避免的处理。

我查看了 Blender 的源代码,注意到底层 C/C++ API 确实允许直接内存访问。通过查看

BKE_mesh_vert_positions
CustomData_get_layer_named
函数,我们看到顶点存储在连续的数据块中:

BLI_INLINE const float (*BKE_mesh_vert_positions(const Mesh *mesh))[3]
{
  return (const float(*)[3])CustomData_get_layer_named(&mesh->vdata, CD_PROP_FLOAT3, "position");
}
const void *CustomData_get_layer_named(const CustomData *data,
                                       const eCustomDataType type,
                                       const char *name)
{
  int layer_index = CustomData_get_named_layer_index(data, type, name);
  if (layer_index == -1) {
    return nullptr;
  }
  return data->layers[layer_index].data;
}

这意味着,至少在理论上,我们可以拥有一个指向数据的指针。

Python API 中是否有一种方法可以公开这些指针,或者是否可以使用 Python 中的 C/C++ API 来直接获取此内存,而无需编译自定义版本的 Blender?

任何有关如何直接访问这些指针或避免内存重复的替代解决方案的指导都将受到高度赞赏。

python ctypes blender bpy
1个回答
1
投票

ctypes
模块可以从 Vertex 对象的 Python 列表创建 C 兼容数组。但你也可以只获取第一个顶点
mesh.vertices[0].as_pointer()
的指针,因为指向第一个元素的指针也是指向整个数组的指针。我在 Windows 11 上对此进行了测试,但它也应该适用于 Linux。我使用 Ubuntu 应用程序和交叉编译器命令
print_pointer.c
编译了以下 C 代码
x86_64-w64-mingw32-gcc -shared -o print_pointer.dll print_pointer.c
。我假设你在 Linux 上并用
gcc -shared -fPIC -o print_pointer.so print_pointer.c

编译它
#include <stdio.h>

typedef struct {
    float* data;
    int num_vertices;
} VertexData;

void print_pointer(VertexData* vertex_data) {
    printf("Vertices:\n");
    for (int i = 0; i < vertex_data->num_vertices; i += 3) {
        float* vertex = vertex_data->data + i;
        printf("Vertex %d: (%f, %f, %f)\n", i / 3, vertex[0], vertex[1], vertex[2]);
    }
}

这是 Python 脚本:

import bpy
import ctypes

lib = ctypes.CDLL('/path/to/dll_or_so/print_pointer.dll') # in your case your .so 

class VertexData(ctypes.Structure):
    _fields_ = [("data", ctypes.POINTER(ctypes.c_float)), ("num_vertices", ctypes.c_int)]

obj = bpy.context.object
mesh = obj.data

num_vertices = len(mesh.vertices) * 3
ptr = mesh.vertices[0].as_pointer()
ptr_as_float = ctypes.cast(ptr, ctypes.POINTER(ctypes.c_float))
vertex_data = VertexData(ptr_as_float, num_vertices)

lib.print_pointer(ctypes.byref(vertex_data))

默认立方体的结果:

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.