2
$\begingroup$

I have found an awsome script by @Leander to do exactly what im trying to acheive here:

https://blender.stackexchange.com/a/109468/30363

which has two scripts, the first generates an object, the second script uses that generated object to extend its mesh until it hits another object in the scene, this then produces an object which looks like a laser plane scan.

I have managed to get the 1st script to run with blender 2.83.1 by updating the python code, using this https://blenderartists.org/t/2-80-cheat-sheet-for-updating-add-ons/1148974 as a guide,

import bpy
import bmesh
from mathutils.bvhtree import BVHTree
import mathutils
import math
import numpy as np

EPSILON = 0.00001
MAXIMUM_DISTANCE = 10

object_name = 'light_plane'
radius_start = math.pi*1/4
radius_end   = math.pi*3/4
radius_step  = math.pi/256

angles = [(math.cos(a), math.sin(a)) for a in np.arange(radius_start, radius_end + 0.00001, radius_step)]

def create_mesh_obj(bm, p_name = "from_bmesh"):
    scn = bpy.context
    me = bpy.data.meshes.new(name = p_name)
    bm.to_mesh(me)

    if (p_name not in bpy.data.objects):
        ob = bpy.data.objects.new(name = p_name, object_data = me)
        scn.collection.objects.link(ob)
    else:
        ob = bpy.data.objects[p_name]
        ob.data = me
    scn.view_layer.update()
    return ob

def create_vertice_shapes(angles):
    bm = bmesh.new()
    for a in angles:
        bm.verts.new((a[0], a[1], 0))
    bm.verts.new((0, 0, 0))
    ob = create_mesh_obj(bm)
    bm.free()
#    basis = ob.shape_key_add(from_mix=False)
#    basis.name = "basis"
    return ob

ob = create_vertice_shapes(angles)
ob.name = object_name

but i have had no such luck with the second script.

import bpy
import bmesh
from mathutils.bvhtree import BVHTree
import mathutils
import math
import numpy as np

EPSILON = 0.00001
MAXIMUM_DISTANCE = 5

object_name = 'light_plane'
radius_start = math.pi*1/4
radius_end   = math.pi*3/4
radius_step  = math.pi/256


angles = [(math.cos(a), math.sin(a)) for a in np.arange(radius_start, radius_end + 0.00001, radius_step)]
print(angles)

def set_positions(ob, angles, obstacles):
    trees = []
    for obstacle in obstacles:
        bm = bmesh.new()
        bm.from_object(obstacle, bpy.context.scene)
        bmesh.ops.transform(bm, matrix=obstacle.matrix_world, verts=bm.verts)
        trees.append(BVHTree.FromBMesh(bm, epsilon=EPSILON))

    for idx, a in enumerate(angles):
        min_dist = MAXIMUM_DISTANCE
        v_loc = mathutils.Vector((a[0], a[1], 0)) * MAXIMUM_DISTANCE
        for bvh in trees:
            v = (ob.matrix_world * mathutils.Vector((a[0], a[1], 0))) - ob.location
            loc, no, i, d = bvh.ray_cast(ob.location, v)
            if d is not None:
                if d < min_dist:
                    min_dist = d
                    v_loc = ob.matrix_world.inverted() * loc
        ob.data.vertices[idx].co = v_loc


def my_handler(scene):
    ob = bpy.data.objects[object_name]
    selection = [oo for oo in bpy.data.objects if oo != ob]
    print(ob.location)
    set_positions(ob, angles, selection)

for h in bpy.app.handlers.depsgraph_update_pre:
    bpy.app.handlers.scene_update_pre.remove(h)
for h in bpy.app.handlers.frame_change_pre:
    bpy.app.handlers.frame_change_pre.remove(h)
#bpy.app.handlers.scene_update_pre.append(my_handler)
#bpy.app.handlers.frame_change_pre.append(my_handler)
my_handler(bpy.context.scene)

The error i get is "TypeError: expected 'Depsgraph' type found 'Scene' instead" highlighted on line 24

bm.from_object(obstacle, bpy.context.scene) 

Can anyone offer some assistance?

$\endgroup$

1 Answer 1

5
$\begingroup$

Some API changes are newer than others

The bmesh from object method requires argument 2 to be a depsgraph, not a scene, as IIRC it was prior, circa when answer was written.

Consult more recent docs, however for this stuff highly recommend using the console's autocomplete feature

>>> bm.from_object(
from_object(object, depsgraph, deform=True, cage=False, face_normals=True)
.. method:: from_object(object, depsgraph, deform=True, cage=False, face_normals=True)
Initialize this bmesh from existing object datablock (currently only meshes are supported).
:arg object: The object data to load.
:type object: :class:`Object`
:arg deform: Apply deformation modifiers.
:type deform: boolean
:arg cage: Get the mesh as a deformed cage.
:type cage: boolean
:arg face_normals: Calculate face normals.
:type face_normals: boolean

will notice depsgraph doesn't make the doc string, clearly it is an argument.

Test clean up. Have made minimal changes to get this to run as a frame change post handler. Have used a 256 vert "Circle" as a test object, (giving it the requirement that it have at least as many verts as the number of angles.)

The script throws an index error for mesh with too few verts...

The method is hooked up to a frame change handler, so it will do "its thing" when the frame is changing.

enter image description here _An example moving a couple of cubes with the animation playing, to call (handle) on a frame change event the mesh update with the handler code.

import bpy
import bmesh
from mathutils.bvhtree import BVHTree
import mathutils
import math
import numpy as np

EPSILON = 0.00001
MAXIMUM_DISTANCE = 5

object_name = 'Circle' # change this to your object name
radius_start = math.pi*1/4
radius_end   = math.pi*3/4
radius_step  = math.pi/256


angles = [(math.cos(a), math.sin(a)) for a in np.arange(radius_start, radius_end + 0.00001, radius_step)]
print(angles)

def set_positions(scene, depsgraph, ob, angles, obstacles):
    trees = []
    for obstacle in obstacles:
        bm = bmesh.new()
        bm.from_object(obstacle, depsgraph)
        bmesh.ops.transform(bm, matrix=obstacle.matrix_world, verts=bm.verts)
        trees.append(BVHTree.FromBMesh(bm, epsilon=EPSILON))

    for idx, a in enumerate(angles):
        min_dist = MAXIMUM_DISTANCE
        v_loc = mathutils.Vector((a[0], a[1], 0)) * MAXIMUM_DISTANCE
        for bvh in trees:
            v = (ob.matrix_world @ mathutils.Vector((a[0], a[1], 0))) - ob.location
            loc, no, i, d = bvh.ray_cast(ob.location, v)
            if d is not None:
                if d < min_dist:
                    min_dist = d
                    v_loc = ob.matrix_world.inverted() @ loc
        ob.data.vertices[idx].co = v_loc


def my_handler(scene, depsgraph):
    ob = scene.objects[object_name]
    selection = [oo for oo in scene.objects if oo != ob and oo.type == 'MESH']
    print(ob.location)
    set_positions(scene, depsgraph, ob, angles, selection)

# to debug n test clear the lot.
bpy.app.handlers.frame_change_post.clear()
bpy.app.handlers.frame_change_post.append(my_handler)

TypeError: Element-wise multiplication: not supported between 'Matrix' and 'Vector'

EDIT

See Scan plane "shadow" re my take on an update for 2.8.

$\endgroup$
6
  • $\begingroup$ Thanks for the re-work,but I must be doing somthing wrong here, the scrript you have done doesn't show any errors, but all it does is print out the angles and exits. For all my testing the "my_handler" function is never entered (placed print("my_handler") for testing in the function). can you elaborate on what else you have done for it to work? $\endgroup$ Sep 12, 2020 at 10:55
  • 1
    $\begingroup$ Managed to get an indent error when pasting, edited. Added a gif re running via handler requires animation playing, and my take on an update via answer to original question blender.stackexchange.com/questions/109178/scan-plane-shadow/… $\endgroup$
    – batFINGER
    Sep 12, 2020 at 17:26
  • $\begingroup$ Great work, works great and exactly how i expect it to, can make an animation with the scan plane updating while an object passed through, can render out an image with the "shadow" present, but rendering the animation doesn't show the object interacting with the scan plane. $\endgroup$ Sep 13, 2020 at 0:54
  • $\begingroup$ blender.stackexchange.com/questions/109178/scan-plane-shadow/… does render for me in 2.9 with no issue. $\endgroup$
    – batFINGER
    Sep 13, 2020 at 2:28
  • $\begingroup$ Video of what's happening on my end here , using 2.8-2.9 and anything in between on three seperate PC's. Rendering out a single image updates the Light Ray's mesh, rendering out an animation does not. $\endgroup$ Sep 16, 2020 at 0:34

You must log in to answer this question.

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