2
$\begingroup$

I would like to create mathemical objects within blender directly from numpy array data (see a small example below). I was able to implemented verts and edges without converting the array into list as required by from_pydata(verts, edges, faces). I suppose that converting a very large number of coordinates into a list from an array is an ineffective coding bottleneck ...

The problem I encounter is that I don't see how to add the faces. Informations I find on the net opens up an ocean of perplexity... Could someone help me to find the simplest possibility for directly construct the faces starting from an array (as done below for verts and edges) ?

# parameters 
start = -40
end = 40
density = (end - start)*2 

# x y meshgrid
xx, yy = np.meshgrid(np.linspace(start,end,density), np.linspace(start,end,density))

# z computation
R = np.sqrt(xx**2 + yy**2) 
zz = 2*np.sin(R) 

# coordinate formating
coords = np.stack((xx, yy, zz), axis=2).ravel() 


# mesh creation
mesh = bpy.data.meshes.new(name='my_shape_data')
# adding verts
num_vertices = coords.shape[0] // 3
mesh.vertices.add(num_vertices)
mesh.vertices.foreach_set("co", coords)

# optionally - adding edges seems possible using this
edges = (np.arange(2*num_vertices)+1)//2
num_edges = edges.shape[0] // 2
mesh.edges.add(num_edges)
mesh.edges.foreach_set("vertices", edges)

# # update mesh and do some checks on it

mesh.update()
mesh.validate()
# object creation
obj = bpy.data.objects.new('my_shape', mesh)
# Add object to the scene
scene = bpy.context.scene
scene.collection.objects.link(obj)
# Select the new object and make it active
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
bpy.context.view_layer.objects.active = obj

enter image description here

$\endgroup$
4
  • $\begingroup$ Do you have any data on the faces? As in, how is the algorithm supposed to know which vertices belong to which faces? Was the code written by you or by chatGPT? GPT can be very misleading in regards to intent... $\endgroup$ Commented Jun 25 at 13:56
  • $\begingroup$ @MarkusvonBroady Yes, I will do an array grouping faces data. I think there are several ways to generate this array (a bit like you can calculate the nodes and sides in the script in different ways). The problem is how to inject directly this array in the object 'avoiding' from_pydata (and the to_list method). PS: No, I wrote this script by myself... Is that so bad :-) ! $\endgroup$
    – Certes
    Commented Jun 25 at 14:18
  • $\begingroup$ AFAIK from_pydata is the fastest method available to the API to generate meshes from arbitrary data entry. Under the hood it is very optimized. The question is how to generate optimized vertices and face vertices indices arrays. But I seem to remember you can directly feed numpy arrays into from_pydata ? $\endgroup$
    – Gorgious
    Commented Jun 25 at 17:58
  • $\begingroup$ @AFAIK "But I seem to remember you can directly feed numpy arrays into from_pydata ?" If this is true I would be really interested to know how to do it! So far I have not found an example that demonstrates this possibility. I've seen several time this way to inject array data # Must call tolist() to pass to from_pydata()! mesh.from_pydata(verts.tolist(),[],faces.tolist()) $\endgroup$
    – Certes
    Commented Jun 26 at 8:03

1 Answer 1

2
$\begingroup$

Finally,

this solution works for me:

import bpy
import numpy as np

# parameters
start = -20
end = 20
densite_x = 80 
densite_y = 80  
# x y z computation
u, v = (densite_x, densite_y)
x = np.linspace(start, end, u)
y = np.linspace(start, end, v)
xx, yy, zz = np.meshgrid(x, y,0)
R = np.sqrt(xx**2 + yy**2) 
zz = 2*np.sin(R) 

# matrix shaping
coords = np.dstack((xx,yy,zz))
coords = np.vstack(coords)

# optional renaming

vertices = coords

# faces = np.array([((u)*j+i, (u)*j+i+1, (u)*j+i+u+1, (u)*j+i+u) for j in range(v-1) for i in range(u-1)])
# faces = []

v_i = np.arange(u*v).reshape((v, u))

# faces quads construction 
# Inspiration taken from 
# https://stackoverflow.com/a/78696530/25574561 
faces = np.array([
    v_i[:-1, :-1].flatten(),  
    v_i[:-1, 1:].flatten(),    
    v_i[1:, 1:].flatten(),   
    v_i[1:, :-1].flatten(),   
]).T


mesh_data = bpy.data.meshes.new("cube_mesh_data")
mesh_data.from_pydata(vertices, [], faces)
mesh_data.update()
      
obj = bpy.data.objects.new("My_Object", mesh_data)
      
scene = bpy.context.scene
scene.collection.objects.link(obj)

Meaning that well formatted there's no problem to from_pydata method to accept directly an array even for faces. enter image description here

inspiration for faces list building

$\endgroup$

You must log in to answer this question.

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