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$ Commented Mar 20, 2022 at 7:12

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.