PDA

View Full Version : Python script as Channel Modifier: how often/when evaluated?



jeric_synergy
09-28-2015, 12:36 PM
When a python script is a CHANNEL MODIFIER, how often and when is it evaluated?

jeric_synergy
09-28-2015, 04:38 PM
In an effort to determine this beyond the documentation's "evaluated at every timestep" (paraphrase), I inserted a :

lwsdk.LWPCoreConsole().show()
print(time.time())
into Bob Hood's noisy_channel.py in the section that defines the evaluate function. (I also imported the Time library.) Originally I just printed "evaluated" out, but thought a timestamp might be handy.

I applied the altered script to the X channel of a null in an otherwise new Scene, with a total of 3 items (null, Cam, Light). The interesting outcome is:

When first added to the channel, the console window immediately fills up, to, I believe, its max lines capacity.
if you hit CLEAR LOG, and then frame forward, it adds 14 lines of output to the console.


In a previous scene with MORE items, the script only added TEN lines of output to the console. iow, FEWER lines of output with more items. And 14 seems a curious quantity.

Soooooo..... no clue as to why these two things occur that way.

(For convenience I include the code below: )


#! /usr/bin/env python
# -*- Mode: Python -*-
# -*- coding: ascii -*-

"""
This is a LightWave Channel Filter plug-in based on the LWSDK sample.
"""

import sys
import lwsdk
import time

__author__ = "Bob Hood+JEC"
__date__ = "Sep 28, 2015"
__copyright__ = "Copyright (C) 2011 NewTek, Inc."
__version__ = "0.1"
__maintainer__ = "jec"
__email__ = "[email protected]"
__status__ = "Example"
__lwver__ = "11"

RESOURCE = '\04(k:"%s" c:LWPy)'

class noisy_channel(lwsdk.IChannel):
def __init__(self, context):
super(noisy_channel, self).__init__()

self._channelid = context

self._offset = 0.0
self._speed = 1.0
self._scale = 1.0
self._phase = 0.0

# LWInterfaceFuncs ------------------------------------
def inter_ui(self):
ui = lwsdk.LWPanels()
panel = ui.create(RESOURCE % 'Noisy Channel')
panel.setw(150)

offset_control = panel.float_ctl(RESOURCE % 'Offset')
offset_control.set_float(self._offset)

speed_control = panel.float_ctl(RESOURCE % 'Speed')
speed_control.set_float(self._speed)

scale_control = panel.float_ctl(RESOURCE % 'Scale')
scale_control.set_float(self._scale)

phase_control = panel.float_ctl(RESOURCE % 'Phase')
phase_control.set_float(self._phase)

self._controls = [offset_control, speed_control, scale_control, phase_control]
panel.align_controls_vertical(self._controls)
#panel.size_to_layout(5, 5)

if panel.open(lwsdk.PANF_BLOCKING | lwsdk.PANF_CANCEL):
self._offset = offset_control.get_float()
self._speed = speed_control.get_float()
self._scale = scale_control.get_float()
self._phase = phase_control.get_float()

ui.destroy(panel)

return lwsdk.AFUNC_OK

# LWChannel -------------------------------------------
def flags(self):
return 0

def evaluate(self, ca):
t = ca.time * self._speed

V = lwsdk.Vector()
V.x = 10 * t
V.y = self._phase
V.z = 20

# LWTextureFuncs is a complex mess, and is not completely exported for
# Python usage. Only simple function calls and data members can be
# accessed successfully. at this time.

val = lwsdk.LWTextureFuncs().noise(V)
val *= self._scale
val += self._offset
val += ca.value

ca.setChannel(ca.chan, val)

lwsdk.LWPCoreConsole().show()
print(time.time())





# LWInstanceFuncs -------------------------------------
def inst_load(self, state):
result, value = state.lwload_fp(1)
self._offset = value

result, value = state.lwload_fp(1)
self._speed = value

result, value = state.lwload_fp(1)
self._scale = value

result, value = state.lwload_fp(1)
self._phase = value

return None # LWError

def inst_save(self, state):
state.lwsave_fp(self._offset)
state.lwsave_fp(self._speed)
state.lwsave_fp(self._scale)
state.lwsave_fp(self._phase)

return None # LWError

def inst_copy(self, source):
self._offset = source._offset
self._speed = source._speed
self._scale = source._scale
self._phase = source._phase

return None # LWError

def inst_descln(self):
return "%s (%s: %.2f %s %2f)" % (RESOURCE % 'Noisy Channel', RESOURCE % 'Scale', self._scale, RESOURCE % 'Speed', self._speed)

# LWItemFuncs ----------------------------------------
def item_changeID(self, itemid_list):
# we have no dependency on any item ids, so we pass
pass

ServerTagInfo = [
( "Python Noisy Channel-JEC", lwsdk.SRVTAG_USERNAME | lwsdk.LANGID_USENGLISH ),
( "Noisy Channel-JEC", lwsdk.SRVTAG_BUTTONNAME | lwsdk.LANGID_USENGLISH ),
]

ServerRecord = { lwsdk.ChannelFactory("LW_PyNoisyChannel-JEC", noisy_channel) : ServerTagInfo }

jeric_synergy
09-28-2015, 06:58 PM
Here's a screengrab after two frames of a render. In THIS case (not just jogging the timeline), the Channel Modifier seems to get called twice per frame.

130015

clintonman
09-29-2015, 09:48 AM
That's interesting, I wonder why it does it twice.

jeric_synergy
09-29-2015, 09:57 AM
Twice seems quite restrained, contrasted with the 1000 or so when you activate the script.

Or the 14 when you frame forward once.

ernpchan
09-29-2015, 10:17 AM
Maybe there are multiple event calls happening?

jeric_synergy
10-03-2015, 11:26 AM
Maybe there are multiple event calls happening?
do you mean "added by the user" or "in the natural course of updating the frame"?

I was adding this to a New Scene, so there were no other plugins/scripts running.

ernpchan
10-03-2015, 12:56 PM
do you mean "added by the user" or "in the natural course of updating the frame"?


My guess would be during updating of the frame but this is all just a guess by me. We're seeing multiple print statement for each scrub. But I'm just speculating. I don't know enough about that stuff so don't look to me for any valuable information.

jeric_synergy
10-03-2015, 01:07 PM
It's just so odd that it's so MANY times, but then only twice during the render.

And the "fill the console buffer" number of times when you first add it. >|^P Boooooooo!

clintonman
10-03-2015, 09:49 PM
I suspect that the initial burst is to fill in the graph editor display.