Skip to content

Instantly share code, notes, and snippets.

@lukpazera
Last active December 19, 2015 17:49
Show Gist options
  • Save lukpazera/5994547 to your computer and use it in GitHub Desktop.
Save lukpazera/5994547 to your computer and use it in GitHub Desktop.
Implements a simple command that replicates what Direction Constraint does. It allows to aim one item at another using third item as an up vector.
#python
""" Aim Item Command.
Command sets one item (source item) rotation so that it
points at another item (target item) with an upvector defined
by third item (up vector item).
Command essentially replicates what Direction Constraint does.
Only it just applies rotation to the source item only at a current frame and action.
Usage:
- select source item
- select target item (the one to which source item will aim at)
- select up vector item
fire: item.aimAt
"""
import traceback
import lx
import lxifc
import lxu.command
import lxu.select
import lxu.vector
CMD_NAME = 'item.aimAt'
class CmdAimAt (lxu.command.BasicCommand):
def __init__(self):
lxu.command.BasicCommand.__init__(self)
self.command_service = lx.service.Command()
self.selection_service = lx.service.Selection()
self.value_service = lx.service.Value()
def basic_Enable (self, msg):
if self.selection_service.Count(lx.symbol.iSEL_ITEM) >= 3:
return True
else:
return False
def cmd_Flags (self):
return lx.symbol.fCMD_UNDO
def basic_Execute (self, msg, flags):
try:
item_sel = lxu.select.ItemSelection().current()
scene = lx.object.Scene(lxu.select.SceneSelection().current())
if not item_sel or len(item_sel) < 3:
return
source_item = lx.object.Item(item_sel[0])
target_item = lx.object.Item(item_sel[1])
upvec_item = lx.object.Item(item_sel[2])
#Initialize channel read and write interfaces.
# We will need them to read evaluated transforms (matrices) from items
# and to write the final rotation matrix for the source item.
# Passing None to the first scene.Channels argument will set Channel Read
# to obtain evaluated channels data. That is the only proper way
# or reading evaluated matrices such as World Position.
# Channel Write interface is set to write at current edit action at current time.
chan_read = lx.object.ChannelRead(scene.Channels(None, self.selection_service.GetTime()))
chan_write = lx.object.ChannelWrite(scene.Channels(lx.symbol.s_ACTIONLAYER_EDIT, self.selection_service.GetTime()))
source_world_idx = source_item.ChannelLookup(lx.symbol.sICHAN_XFRMCORE_WPOSMATRIX)
target_world_idx = target_item.ChannelLookup(lx.symbol.sICHAN_XFRMCORE_WPOSMATRIX)
upvec_world_idx = upvec_item.ChannelLookup(lx.symbol.sICHAN_XFRMCORE_WPOSMATRIX)
source_world_value = lx.object.Value(chan_read.ValueObj(source_item, source_world_idx))
target_world_value = lx.object.Value(chan_read.ValueObj(target_item, target_world_idx))
upvec_world_value = lx.object.Value(chan_read.ValueObj(upvec_item, upvec_world_idx))
# Matrix object is polymorphic with the Value object.
matrix = lx.object.Matrix()
matrix.set(source_world_value)
source_world_pos = matrix.GetOffset()
matrix.set(target_world_value)
target_world_pos = matrix.GetOffset()
matrix.set(upvec_world_value)
upvec_world_pos = matrix.GetOffset()
aim_vector = lxu.vector.sub(target_world_pos, source_world_pos)
aim_vector = lxu.vector.normalize(aim_vector)
# Do the same for up vector, so substract source world pos
# vector from up vector item world pos vector.
# NOTE: the up vector will not be orthogonal at this point.
up_vector = lxu.vector.sub(upvec_world_pos, source_world_pos)
up_vector = lxu.vector.normalize(up_vector)
# The side vector is the third, remaining axis for our
# aim rotation matrix. To get that vector we're doing a cross product
# of up and aim vectors.
side_vector = lxu.vector.cross(up_vector, aim_vector)
side_vector = lxu.vector.normalize(side_vector)
# Now we have aim and side vectors that are orthogonal to each other.
# We can calculate up vector again using cross product of aim and side vectors.
# This makes sure all 3 vectors will be orthogonal to each other which is
# the requirement for the rotation matrix.
up_vector = lxu.vector.cross(aim_vector, side_vector)
rot_matrix_val = lx.object.Value(self.value_service.CreateValue('matrix3'))
matrix.set(rot_matrix_val)
rot_matrix = [[1,0,0], [0,1,0], [0,0,1]]
rot_matrix[0][0] = side_vector[0]
rot_matrix[1][0] = side_vector[1]
rot_matrix[2][0] = side_vector[2]
rot_matrix[0][1] = up_vector[0]
rot_matrix[1][1] = up_vector[1]
rot_matrix[2][1] = up_vector[2]
rot_matrix[0][2] = aim_vector[0]
rot_matrix[1][2] = aim_vector[1]
rot_matrix[2][2] = aim_vector[2]
item_loc = lx.object.Locator(source_item)
item_loc.SetRotation(chan_read, chan_write, rot_matrix, lx.symbol.iLOCATOR_WORLD, 0)
except:
lx.out(traceback.format_exc())
raise
lx.bless(CmdAimAt, CMD_NAME)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment