Forum: Poser - OFFICIAL


Subject: How to assign a Control Handle to more than one object?

Chanc opened this issue on Apr 11, 2020 ยท 40 posts


an0malaus posted Mon, 13 April 2020 at 5:13 AM

Just an observation on Point At, the pointAt dials can themselves be controlled by valueOperations (ERC) and will take intermediate settings between 0 and 1, where they act accordingly: at 0, rotation dials are in full control, at 0.5, rotation and pointAt have equal, reduced influence, and at 1.0, pointAt operates along.

The PointAt feature really is missing some required functionality, as in actually adding its influence to the actor's rotation dials, as though it were another form of ERC/valueOperation. In such a scenario, pointAt strength could then be dialled back when an eyeball reaches the end of its rotation limits (if applied).

I've used a similar pointAt operation to simulate gravity, by having a magnet rotate a ponytail or hair part and point at a nadir prop set at 0, -maxint, 0. The difficulty is imposing limits on pointAt, so I used a python callback instead, to extract the Euler angles and apply them to set rotation keyframes instead. For each frame that needs the gravity/pointAt effect, the script will set rotations, then when completed, Another script will kill the callback and the dial rotations take sole effect.

Save this as .py in the same folder as the hair prop .pp2 file

# HamptonGravity.py
# (c) 2018-2020 Geoff Hicks (an0malaus|GeoffIX|gwhicks)
#
# Callback function to apply gravity to HamptonHair prop.
# Pitch is applied to CTRLSwing.
# Roll is applied to CTRLSway.
# Script should be co-located with the Hair Prop.
#
# v1.0  20181101    Initial version based on ParisHairGravity.py v1.1
#                   Added kEventCodeACTORSELECTIONCHANGED and kEventCodePARMCHANGED to watchCode
#                   Look a bit harder for the 'HamptonHair' prop, in case it's parented to the current figure, but not
#                   actually selected.
# v1.1  20190114    Add copysign to math module explicit imports
# v1.2  20200408    Added Side and Back deformers and overall Control parameters to HamptonHair instead of just Bangs.
########################################################################################################################
version = '1.2'
debug = False
pitchParmName = 'CTRLSwing'#'CTRLBangsSwing'
rollParmName = 'CTRLSway'#'CTRLBangsSway'

import poser
from math import atan2, asin, degrees, copysign

def getAngle( actor, bScale ):
    WM = actor.WorldMatrix()
    ## Define required cells of matrix. Note: bScale is propagating scale of the figure to which the prop is parented.
    WM12 = WM[0][1]/bScale
    WM22 = WM[1][1]/bScale
    WM31 = WM[2][0]/bScale
    WM32 = WM[2][1]/bScale
    WM33 = WM[2][2]/bScale
    if abs( WM32 ) > 1.0: # Out of [ -1 .. +1 ] range for arcsine, so crop to valid range
        if debug:
            print 'WARNING: WM32 out of valid arcsine range [-1..1] ({})'.format( WM32 )    
        WM32 = copysign( 1.0, WM32 )
    AngleX = degrees( asin( WM32*-1 ) ) 
    AngleY = degrees( atan2( WM31, WM33 ) ) 
    AngleZ = degrees( atan2( WM12, WM22 ) )
    if debug:
        print 'Euler Angles {}, {}, {}'.format( AngleX, AngleY, AngleZ )
    return ( AngleX, AngleY, AngleZ )

def HamptonActor():
    actor = poser.Scene().CurrentActor()
    if actor:
        if actor.IsProp() and 'Hampton' in actor.Name():
            return actor
        else:
            figure = actor.ItsFigure()
            if figure:
                for actor in figure.Actors():
                    if actor.IsProp() and 'Hampton' in actor.Name():
                        break
                else:
                    return None
            else:
                return None
    return actor

def HamptonUpdate():
    scene = poser.Scene()
    actor = HamptonActor()
    if not actor:
        return
    pitch = actor.Parameter( pitchParmName )
    roll = actor.Parameter( rollParmName )
    if not ( pitch and roll ):
        return
    bScale = 1.0
    figure = actor.ItsFigure()
    if figure:
        body = figure.RootActor()
        if body:
            bScaleParm = body.ParameterByCode( poser.kParmCodeASCALE )
            if bScaleParm:
                bScale = bScaleParm.Value()
    ( aX, aY, aZ ) = getAngle( actor, bScale ) # We only care about the pitch (aX) and roll (aZ)
    newPitch = -aX
    newRoll = -aZ
    needDraw = False
    if pitch.Value() != newPitch:
        pitch.SetValue( newPitch )
        needDraw = True
    if roll.Value() != newRoll:
        roll.SetValue( newRoll )
        needDraw = True
    if needDraw:
        scene.DrawAll() 

scene = poser.Scene()

watchCode = poser.kEventCodeKEYSCHANGED | poser.kEventCodePARMCHANGED | poser.kEventCodeACTORSELECTIONCHANGED

def HamptonCallback( iScene, iEventType ):
    if ( iEventType & watchCode ) != 0:
        HamptonUpdate()

scene.SetEventCallback( HamptonCallback )

### END ###

You can replicate this for any hair prop with a magnet set to move a ponytail, or even an eye/multiple eyes simply by adjusting which parameters get the Euler angles assigned.

The StopGravityUpdates.py script below stops all callbacks.

# StopGravityUpdates.py
# (c) 2018 Geoff Hicks (an0malaus|GeoffIX|gwhicks)
#
# Clear the Callback function installed by any Hair Gravity scripts.
# This is literally the shortest Poser Python Script I have ever written!
#
# v1.0  20180926    Initial version to match ParisHairGravity.py
########################################################################################################################
version = '1.0'
debug = False

import poser

poser.Scene().ClearEventCallback()

### END ###



My ShareCG Stuff

Verbosity: Profusely promulgating Graham's number epics of complete and utter verbiage by the metric monkey barrel.