more programmed camera motion: generator funcs in Intervals

This is another small programmed scene with a small visual element arranged 20 times. First the camera faces one and pans to get an angle. Then it bounces around the scene centering on different elements, keeping that angle. Extremely minimal at the moment.

Things to look for:

  - The visual element itself.  Slick, stylish.
    - And generating the scene with it
  - The camera_generator generator function
    - Yields intervals, e.g. LerpPos and LerpHpr
    - Gets called back when they complete
  - The slick little SequenceAutoChain class
    - Gets its argument called back for more intervals

The code:

import direct.directbase.DirectStart
#import panda_async
from panda3d.core import *
from direct.interval.IntervalGlobal import *
import random as ran
import time

render.setTwoSided( True )
base.disableMouse()

# visual item composed of the 3 axes-planes
visualelement= NodePath( 'vixel' )
cardmaker= CardMaker( 'cardmaker' )
cardmaker.setFrame( -.5, .5, -.5, .5 )
cardNPs= [ ] # compress these--> 5 lines to 1.  readable?
for _ in range( 3 ):
    card= cardmaker.generate( )
    cardNP= visualelement.attachNewNode( card )
    cardNPs.append( cardNP )
cardNPs[ 1 ].setHpr( 0, -90, 0 )
cardNPs[ 2 ].setHpr( 90, 0, 0 )

# light in 3 directions
dirs= [
    [ 0, 0, 0 ],
    [ 90, 0, 0 ],
    [ 0, 90, 0 ],
]
dlightNPs= [ ]
for dir in dirs:
    dlight= DirectionalLight( 'dlight' )
    dlightNP= render.attachNewNode( dlight )
    #dlight.setColor( Vec4( .5, .5, .5, 1 ) )
    dlightNP.setHpr( *dir )
    render.setLight( dlightNP )
    dlightNPs.append( dlightNP )

# share the processor please
def ratecap( task ):
    time.sleep( .01 )
    return task.cont
taskMgr.add( ratecap, 'ratecap' )

# make a small scene of vixels in a zig-zag
vixcount= 20
vixs= [ ]
class Vixel: pass
for j in range( vixcount ):
    viz= visualelement.copyTo( render )
    viz.setScale( .5 )
    viz.setPos( j, 0, j% 2 )
    viz.setColor( 1, j/ float( vixcount ), 0 )
    viz.setHpr( ran.uniform( 0, 360 ), ran.uniform( 0, 360 ), 0 )
    vix= Vixel( )
    vix.viz= viz
    vix.speedhpr= ran.uniform( 0, 2 ), ran.uniform( 0, 2 ), 0
    vixs.append( vix )
base.camera.setPos( 0, -3, 0 )

# the vixes will be rotating at various speeds
def spinvixes( task ):
    for vix in vixs:
        vix.viz.setHpr( vix.viz, vix.speedhpr )
    return task.cont
taskMgr.add( spinvixes, 'spinvixes' )

# the camera interval generator
def camera_generator( ):
    # first rotate to look at element #5 (arbitrary, get an angle)
    hpr0= base.camera.getHpr( )
    base.camera.lookAt( vixs[ 5 ].viz )
    hpr1= base.camera.getHpr( )
    base.camera.setHpr( hpr0 )
    offset= base.camera.getPos( render )- vixs[ 5 ].viz.getPos( render )
    yield LerpHprInterval( base.camera, 3, hpr1, blendType= 'noBlend' )

    # then pick random others and get them center-screen.  repeat.
    while 1:
        vix= ran.choice( vixs )
        pos1= offset+ vix.viz.getPos( )
        yield LerpPosInterval( base.camera, 3, pos1, blendType= 'noBlend' )

# chasis for the generator
class AutoGenerator:
    def __init__( self, othergenob ):
        self.genob= self.gensomethingspecial( othergenob )
        self.genob.next( )

class SequenceAutoChain( AutoGenerator ):
    def gensomethingspecial( self, othergenob ):
        while 1:
            # call next on the generator, get an interval back
            interval= othergenob.next( )
            # chain it with this generator
            seq= Sequence( interval, Func( self.genob.next ) )
            # give it a push
            seq.start( )
            yield

# and give it a push
SequenceAutoChain( camera_generator( ) )

#panda_async.G.__dict__.update( globals( ) )
run( )

Wish list:

  • Lerps have a ‘rate’ argument, optional alternative to ‘duration’

A second strategy for camera_generator.

We swivel to look towards the new vix (visual element), then pick a new point on the segment connecting the old and new vixes 3 units from the new; project that onto the Y= -3 plane, and Lerp to that position in parallel with keeping the camera pointed towards the new vix.

# the camera interval generator
def camera_generator( ):
    def _face( _, vix0 ):
        base.camera.lookAt( vix0.viz )
    while 1:
        hpr0= base.camera.getHpr( )
        # pick a random vix
        vix= ran.choice( vixs )
        # get the angle towards to
        base.camera.lookAt( vix.viz )
        hpr1= base.camera.getHpr( )
        # get point three units towards the camera away from the vix and set Y= -3
        pos1= base.camera.getPos( )- vix.viz.getPos( )
        pos1.normalize( )
        pos1= pos1* 3
        pos1= pos1+ vix.viz.getPos( )
        pos1.setY( -3 )
        # restore original hpr
        base.camera.setHpr( hpr0 )
        # swivel
        yield LerpHprInterval( base.camera, 3, hpr1, blendType= 'noBlend' )
        # slide
        x= LerpPosInterval( base.camera, 3, pos1, blendType= 'noBlend' )
        y= LerpFunc( _face, duration= x.getDuration( ), extraArgs= [ vix ] )
        yield Parallel( x, y )

Slight revision.

We have a better demonstration of generator functions here, since the swivel-slide combination is used in every iteration of the while loop, and we are alternating back and forth between two Lerp creations. Otherwise we would need two functions, SwivelInterval and SlideInterval that add each other in sequence when their own intervals are complete; and a number of function-external variables.