2
$\begingroup$

I'm trying to figure out how to align my 3D viewport to the line connecting two 3D points.

For example, say I want my 3D viewport to be oriented such that it looks directly along the line connecting (1, 2, 3) to (6, 5, 4). As an added constraint, say I wanted to position the viewpoint a distance of 10 from the point (1, 2, 3).

I know how to query the current view_location, view_matrix, view_distance, and view_rotation, but I am unfamiliar with how to transform it to be in the correct position and orientation.

C = bpy.context
viewports_3D = []
for area in C.screen.areas:
    if area.type == 'VIEW_3D':
        viewports_3D.append(area)
viewports_3D[0].spaces.active.region_3d.view_location
viewports_3D[0].spaces.active.region_3d.view_matrix
viewports_3D[0].spaces.active.region_3d.view_distance
viewports_3D[0].spaces.active.region_3d.view_rotation

$\endgroup$
2
  • $\begingroup$ If no one else does, I might write a full answer later... You basically subtract (1,2,3) from (4,5,6) and normalize the result.. This gives you yaw and pitch, but not roll. Then you multiply the normalized result by -10 and add back the (1,2,3) to get your camera position. $\endgroup$ – Ron Jensen Dec 8 '20 at 23:37
  • $\begingroup$ Thanks for the reply. I'm still not sure how to get the angles, and how I should feed this into the blender python commands to change the viewport $\endgroup$ – Will Sni Dec 9 '20 at 13:51
2
$\begingroup$

I've been way busier than expected, so I'm going to post some code and skip the explanation for now... Basically, the viewport is a lookat camera that looks at its location from the distance specified.


import bpy
from mathutils import Vector, Euler
from math import atan2, sqrt

start = Vector([1,2,3])
stop  = Vector([4,5,6])


norm = start - stop
print(norm)
norm.normalize()
print(norm)

heading = -atan2( norm.y, norm.x )

pitch = -atan2( sqrt( norm.x**2 + norm.y**2), norm.z )

print('Heading {}, Pitch {}'.format(heading, pitch))

euler = Euler( (pitch, 0, heading ) , 'XYZ' )
print(euler)
quat  = euler.to_quaternion()
print(quat) 

C = bpy.context
viewports_3D = []
for area in C.screen.areas:
    if area.type == 'VIEW_3D':
        viewports_3D.append(area)
        
viewports_3D[0].spaces.active.region_3d.view_rotation=quat
viewports_3D[0].spaces.active.region_3d.view_location=stop #lookat this point
viewports_3D[0].spaces.active.region_3d.view_distance=6 # from this far away

$\endgroup$
3
$\begingroup$

Track to quaternion.

Can get the quaternion rotation of any vector using Vector.to_track_quat(track, up) and assign an axis to track and an axis for up.

Test script to make the largest 3d view in the area track from the scene camera to the active object.

import bpy

context = bpy.context
scene = context.scene
cam = scene.camera
ob = context.object

start = cam.matrix_world.translation
stop  = ob.matrix_world.translation

norm = start - stop
norm.normalize()

q = norm.to_track_quat()

viewports_3D = [
        a
        for a in context.screen.areas
        if a.type == 'VIEW_3D'
        ]

viewports_3D.sort(
        key=lambda vp: (vp.width * vp.height), 
        )     

# use max area vp
if viewports_3D: 
    vp = viewports_3D.pop() # largest
    r3d = vp.spaces.active.region_3d
    r3d.view_rotation = q
    r3d.view_location = start #lookat this point
    r3d.view_distance = 0 # from this far away

Note above uses default for track to so vector is flipped, more concise would be

norm = stop - start #start - stop
norm.normalize()

q = norm.to_track_quat('-Z', 'Y')

This is equivalent of the result of adding a track to constraint on camera to object. Have used to test

enter image description here

$\endgroup$

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.