1
$\begingroup$

At this moment I would like to move an object, suppose a "cube" along its y axis, with a slider from the UI panel with a FloatProperty. So far there is no problem. Now, suppose this object encounters a terrain (an irregular mesh with various polygons) is there a way to make it follow the normals of this hypothetical path? Normally I would have used a path with a bezier, but I would like to be able to do this with a simple control in the UI panel, I insert an image to understand betterenter image description here

I have no idea to do this, I suppose I probably have to register all the faces of that plan? Or something similar? It seems a bit complex to me.

Edit: I was kindly suggested by Leander, to use the raycasting method, obviously I did not know this function , I would also like to understand if it is possible to record the animation correctly using keyframes, I could put a kind of operator that cooks the keyframes, or something similar

$\endgroup$
4
  • 1
    $\begingroup$ Possible duplicate of Script for getting Camera Look At co-ordinates $\endgroup$
    – Leander
    Jul 28, 2019 at 21:03
  • 1
    $\begingroup$ I've voted to close this as a duplicate. Even if the wording is not the same, the answer also solves your quest. Just raycast a ray straight downwards at the specific (X, Y) location and position the cube at the "hit" position. $\endgroup$
    – Leander
    Jul 28, 2019 at 21:03
  • $\begingroup$ @Leander Thank you very much for reporting, surely it's a similar question, but not too much, some people like me might find a solution to this specific question interesting, I really appreciate your script with the camera. The raycast method is very interesting. I should also take into account being able to animate everything with keyframes, which this seems a bit problematic $\endgroup$
    – Noob Cat
    Jul 28, 2019 at 21:19
  • 1
    $\begingroup$ I thought you would be willing to do the additional coding and transfer yourself. I added an answer, don't feel like you need to accept it though. Good luck. $\endgroup$
    – Leander
    Jul 29, 2019 at 7:31

1 Answer 1

2
$\begingroup$

There's something wrong with the ray_cast, which messes up on a few casts. But I think this could clear up if placed in a modal operator.

I believe the script from this answer achieves what you want.

The result looks very similiar to your gif.

result

  • Convert the collision geometry to a bmesh, apply the transformation matrix and get a bvhtree from the transformed bmesh.
  • Cast a ray from above the Cube's location straight downwards (0, 0, -1) and check if it intersects with the bvhtree.
  • Create a keyframe.
import bpy
import bmesh
from mathutils.bvhtree import BVHTree
import mathutils

# the epsilon value for the BVHTree calculations
EPSILON = 0.0000001

# maximum ray distance XY plane
MAXIMUM_Z = 1000

# make sure you have a Camera and an Empty named like this
landscape = bpy.data.objects["Landscape"]
cube = bpy.data.objects["Cube"]

# create the BVHTrees from a bmesh of the "sticky" object
# the bmesh conversion makes it easy to apply the individual objects transformation matrices
bm = bmesh.new()
bm.from_object(landscape, bpy.context.scene)
bmesh.ops.transform(bm, matrix=landscape.matrix_world, verts=bm.verts)
tree = BVHTree.FromBMesh(bm, epsilon=EPSILON)


# the main method for setting the new z position
def reposition_z(scene = bpy.context.scene):

    # create a direction vector, which points downwards and
    # a origin for the raycast
    dir = mathutils.Vector((0, 0, -1))
    origin = mathutils.Vector((*cube.location[:2], MAXIMUM_Z - EPSILON))

    # if there is a hit, move the cube to that location
    loc, no, i, d = tree.ray_cast(origin, dir)
    if d is not None:
        # assign the cube's location
        cube.location = loc

# remove all handlers, then create a new one with the reposition_z function
post_handlers= bpy.app.handlers.scene_update_post
[post_handlers.remove(h) for h in post_handlers if h.__name__ == "reposition_z"]
post_handlers.append(reposition_z)

Setting a keyframe could look like.

for i in range(start, end):
    bpy.data.scenes["Scene"].frame_set(i)
    reposition_z()
    cube.keyframe_insert(data_path = 'location')
$\endgroup$
4
  • $\begingroup$ Why do you remove all handlers? $\endgroup$
    – lemon
    Jul 29, 2019 at 7:31
  • $\begingroup$ @lemon Testing purposes... so I can run the script multiple times and "clean up " on the fly. Got anything more graceful? $\endgroup$
    – Leander
    Jul 29, 2019 at 7:32
  • $\begingroup$ More graceful I don't know... I usually test to remove only the handler that I will append so that I don't remove handlers from other scripts. Like in this answer (Scene Handler part) blender.stackexchange.com/questions/143273/… $\endgroup$
    – lemon
    Jul 29, 2019 at 7:35
  • $\begingroup$ @lemon Shouldn't have been lazy, updated. $\endgroup$
    – Leander
    Jul 29, 2019 at 7:38

You must log in to answer this question.

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