4
$\begingroup$

I have a python numpy array similar to the following:

Array = ([[1, 2, 1, -1, -1, -3],
          [3, 4, 2, 1, -2, -2],
          [3, 4, 1, -1, -2, -2]])

I want to convert this to a heightmap in blender where a plane is subdivided into the equal number of rows and columns and each subdivision is displaced proportional to the numbers in the array.

Can this be done through a python script running in Blender? Are there any other methods of creating a heightmap from a python array?

$\endgroup$

3 Answers 3

6
$\begingroup$

enter image description here

Here's a script that generates a blender image from the array, and maps it to an image texture on a displacement modifier assigned to a Plane object.

import bpy, math
import numpy as np

a = np.array([
    [1, 2, 1, -1, -1, -3],
    [3, 4, 2, 1, -2, -2],
    [3, 4, 1, -1, -2, -2]
])

# Normalize values
anorm = (a - a.min()) / (a.max() - a.min())

# Generate RGBA image from grayscale normalized array
pixels = np.dstack([anorm]*3+[np.ones(anorm.shape)])

# Generate or update image object in Blender
# Based on this answer: https://blender.stackexchange.com/a/105312/15861
height, width = anorm.shape
image_name = "dispmap.png" 
if image_name not in bpy.data.images.keys():
    bpy.data.images.new(image_name, width=width, height=height, alpha=True, float_buffer=False)

outputImg  = bpy.data.images[image_name] 
input_res  = int(math.sqrt(outputImg.size[0]))
np_out_img = pixels.astype( np.float16 )
outputImg.pixels = np_out_img.ravel()


# Add or get displacement modifier
plane_obj = bpy.data.objects['Plane'] # Assumes the plane object is named "Plane"
if 'Disp' not in plane_obj.modifiers:
    m = plane_obj.modifiers.new('Disp', 'DISPLACE')
    
m = plane_obj.modifiers["Disp"]

# Add or get displacement image texture
if not m.texture:
    t = bpy.data.textures.new('DispTex', 'IMAGE')
    
t = bpy.data.textures['DispTex']

# Set image texture and image
t.image = outputImg
m.texture = t

Generated height (displacement) map and resulting mesh: enter image description here

$\endgroup$
2
$\begingroup$

I use a grid (instead a plane, i hope that's ok). Then i just loop over the vertices and set the vertices height from the array.

Code:

import bpy
import bmesh

bpy.ops.mesh.primitive_grid_add(x_subdivisions=5, y_subdivisions=5)
bpy.ops.object.mode_set(mode="EDIT")

Array = ([[1, 2, 1, -1, -1, -3],
          [3, 4, 2, 1, -2, -2],
          [3, 4, 1, -1, -2, -2]])
          
context = bpy.context   
   
bm = bmesh.from_edit_mesh(context.edit_object.data)
# deselect all
for v in bm.verts:
    v.select = False

bm.verts.ensure_lookup_table()

for x in range(6):
    print("x",x)
    for y in range(2): 
        print("y",y)
        bm.verts[y * 6 + x].co.z = Array[y][x]

Result:

enter image description here

enter image description here

$\endgroup$
2
$\begingroup$

Array as vertex height

Similarly to @Chris's answer if the array values are interpreted as vertex z values

  • Add a grid of same dimensions (unit size)
  • Scale such that each xy grid is square
  • Use foreach_get and foreach_set to quickly set the z offset without looping. (Or edit mode)

Test script.

import bpy
import numpy as np
from mathutils import Matrix

scale_factor = 2

a = np.array([
    [1, 2, 1, -1, -1, -3],
    [3, 4, 2, 1, -2, -2],
    [3, 4, 1, -1, -2, -2]
])

rows, cols = a.shape
# make the grid rows x cols blender  units
S = Matrix.Diagonal((rows - 1, cols - 1, 1, 1))
# add a grid
bpy.ops.mesh.primitive_grid_add(
        size=1,
        x_subdivisions=rows,
        y_subdivisions=cols,
        )
        
ob = bpy.context.object
me = ob.data
#scale it 
me.transform(scale_factor * S)
# update z coord from data
coords = np.empty(rows * cols * 3)
me.vertices.foreach_get("co", coords)

x, y, z = coords.reshape(-1, 3).T

me.vertices.foreach_set(
        "co",
        np.array([x, y, a.T.ravel()]).T.ravel(),
        )
$\endgroup$
1
  • $\begingroup$ "co", RuntimeError: internal error setting the array. not sure why? $\endgroup$
    – Derekcbr
    Mar 20, 2022 at 7:12

You must log in to answer this question.

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