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$
  • 1
    $\begingroup$ Possible duplicate of Script for getting Camera Look At co-ordinates $\endgroup$ – Leander Jul 28 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 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$ – Pastrokkio Jul 28 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 at 7:31
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$
  • $\begingroup$ Why do you remove all handlers? $\endgroup$ – lemon Jul 29 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 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 at 7:35
  • $\begingroup$ @lemon Shouldn't have been lazy, updated. $\endgroup$ – Leander Jul 29 at 7:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.