Performance Issues - Script Linking Switch Layers Together

Moho allows users to write new tools and plugins. Discuss scripting ideas and problems here.

Moderators: Víctor Paredes, Belgarath, slowtiger

GaryC
Posts: 53
Joined: Tue Feb 03, 2015 1:02 pm

Performance Issues - Script Linking Switch Layers Together

Post by GaryC »

So I've been working on a script from a while back that I got off here, a script that could let you link two switch layers so that switching one would switch the other.

Here's the original script:
viewtopic.php?f=12&t=2506

It finds a layer with .sslv at the end and controls that from the layer with the script attached.
But I adjusted it to specifically look for a layer with the same name as well as that ending.
ie. If you attach the script to a layer named "Eyes" then it would control a layer named "Eyes.sslv"
That way you could have multiple sets of these connections in one file without them interfering with each other.

That worked fine, however I then wanted a system where it could look for a folder first and then search through that folder for a slave layer.
Because in plenty of files you might have multiple "Eyes" layers for different characters, rather than always naming them character specific things I wanted the script to be able to find the character folder and then in that folder locate the Eyes layer.

That ran into some performance issues. I am able to make the script work, but on our production with a lot of layers and characters, it just crashes from trying to search through all the files and find results. The curious thing is, it does fine when I don't specify a folder and it runs through all the layers in the whole file to find every matching slave name, but once I'm trying to get it to find the character folder and then the slave, it's much more resource heavy.
I'm new to working in Anime Studio so perhaps I'm unintentionally doing something that's draining on the program, so I was hoping some more savvy person from here could help me out. I'll include the code below and I cleaned it up a bit, but note it includes both code for searching for all results and code for searching to grab layers from certain characters.

Code: Select all

function LayerScript(moho)
	--************************************************************
	--SlaveSwitch (c)2005 B. Quinn
	--V 1.0
	--************************************************************
	
	local frame = moho.frame
	local layer = moho:LayerAsSwitch(moho.layer)
	local target = layer:Name() .. ".sslv"
    local targetChar = ""
    local slaves = {}
    local foundLayers = 1
    
    --splitTarget
    splitTarg = string.find(target,'@')

    --Check if there's an @, as this indicates that it's seeking a character folder and then a slave
    if (splitTarg ~= nil) then
        targetChar = string.sub(target, 0, splitTarg - 1)
        target = string.sub(target, splitTarg+1, -1)

        if (charLayer == nil or charLayer:Name() ~= targetChar) then charLayer = searchOneResult(targetChar) end
    end
    
    --Function for adding slaves to the list
    function addSlave(layer)
        slaves[foundLayers] = layer
        foundLayers = foundLayers + 1
    end
    
    --This method searches all layers for the target name and adds all answers to the slave list
	function searchAllResults(currentLayer, targetName)
		local layerCount = currentLayer:CountLayers()
		for count = 0, layerCount - 1 do
			local testLayer = currentLayer:Layer(count)
			if ( testLayer:Name() == targetName ) then addSlave(testLayer) end
			testLayer = moho:LayerAsGroup(testLayer)
			if (testLayer ~= nil) then
				local testCount = testLayer:CountLayers()
				if (testCount > 0) then
					searchAllResults(testLayer, target)
				end
			end
		end	
	end

    --This method searches for a specific unique layer result and returns it
	function searchOneResult(targetName)
		local layerCount = moho.document:CountLayers()
        local testLayer = nil

		for count = 0, layerCount - 1 do
			testLayer = moho.document:Layer(count)
			if ( testLayer:Name() == "CHARACTERS" ) then

                --Now look for the intended character within that layer
                testLayer = moho:LayerAsGroup(testLayer)
                layerCount = testLayer:CountLayers()
                for count = 0, layerCount - 1 do
                    current = testLayer(count)
                    if (current:Name() == targetName) then
                        return current
                    end
                end	
                
            end
            if (count == layerCount - 1) then return nil end 
        end
        return nil
	end

	if (layer:LayerType() ~= MOHO.LT_SWITCH) then return end

	local switch = layer:SwitchValues()
	local ctrlLayer = nil
	local error = false
	
    --If there was no split, just search every layer and get every result
    if (splitTarg == nil) then
        for count = 0, moho.document:CountLayers() - 1 do
            local currentLayer = moho.document:Layer(count)
            if ( currentLayer:Name() == target ) then addSlave(currentLayer) end
            currentLayerGroup = moho:LayerAsGroup(currentLayer)
            if (currentLayerGroup ~= nil) then searchAllResults(currentLayerGroup, target) end
        end	
    else 
        --If there was a split, use the search one result as your starting point and search all the way down from there
        local charLayer = searchOneResult(targetChar)
        charLayer = moho:LayerAsGroup(charLayer)
        searchAllResults(charLayer, target)
    end
    
	if (slaves == {}) then return end --If no matching layers were found then it aborts
	
    --And this is where it runs through all the found layers to actually adjust them
    for i, slave in ipairs(slaves) do
        if switch:HasKey(frame) then
            local switchName = layer:SwitchValues():GetValue(frame)
            slave:SwitchValues():SetValue(frame, switchName) 
        else
            slave:SwitchValues():DeleteKey(frame)
        end
    end
end	
User avatar
synthsin75
Posts: 9934
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by synthsin75 »

I don't think there's any reason to be building tables for a script like this, especially in a layerscript. The only reason to search a document from the top down would be if source/slave layers could be anywhere. Since you say these are within separate character groups, this overhead is not necessary.

If your switch layers are always sibling layers, simply count through the sublayers of the parent layer to find the slave switch. If they're not, then it's probably best to include a special letter, symbol, or extension in the character's main group/bone layer so the script knows where to stop looking for further parent layers.
dkwroot
Posts: 677
Joined: Thu May 02, 2013 6:56 am
Location: USA
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by dkwroot »

If I were to do something like this, I would use a for statement to cycle through all the layers in the document and then check the layer type and name to look for duplicates. If the duplicate matches, just perform the switch and move on. I think it would look something like this.

Code: Select all

local Layer = moho.layer
local Name = Layer:Name()
local LayerType = Layer:LayerType()
local LayerNum = MohoDoc:CountLayers()-1

for i=0,LayerNum do
      local LayerDup = MohoDoc:Layer(i)
      if LayerDup:LayerType() == LayerType then
              local NameDup = LayerDup:Name()
              local Check = string.match(NameDup,Name) --This checks to see if the layer has the same name
              local DupCheck = string.match(NameDup,".DUP") --This checks if the sublayer is a duplicate or not
              if (Check ~= nil and DupCheck ~= nil) then --This means a slave layer is selected
                    LayerDup:SetValue(Layer:GetValue())
                    moho:NewKeyframe(CHANNEL_SWITCH)    
              end
     end
end
This should allow you to control duplicate switch layers no matter where they are.
User avatar
synthsin75
Posts: 9934
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by synthsin75 »

I assumed from the OP that source and slave would both be independent for each character. I can't imagine wanting all the characters to blink at the same time or something.
dkwroot
Posts: 677
Joined: Thu May 02, 2013 6:56 am
Location: USA
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by dkwroot »

synthsin75 wrote:I assumed from the OP that source and slave would both be independent for each character. I can't imagine wanting all the characters to blink at the same time or something.
If the OP is careful with naming their layers, the code should work fine.
For example, name a mouth switch layer: BOB.MOUTH and any slave layers: BOB.MOUTH.DUP

The first check will see if a given layer is the same layer type as the embedded layer. I made it this way so the code could be expanded upon to include mesh instancing. My theory is that now that we can copy and paste keyframes between layers pretty easily, it might be a lot easier to do global mesh instancing. :D

The second check will see if BOB.MOUTH exists within the name of a given layer. This will be true or 'not nil' if the layer is a slave or master. In other words "BOB.MOUTH" or "BOB.MOUTH.DUP" will trigger this check to be true.

The final check will see if .DUP exists within the layer name which tells us whether it's a slave or master. Since we only want to effect slaves, the master layer is overlooked.

On a side note, we could have just concatenated the Layer:Name() with .DUP and cross checked that with the given layer name. Well ... Anyway, it should also be noted that this code would only have to be embedded on the Master switch.
User avatar
synthsin75
Posts: 9934
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by synthsin75 »

Yeah, the OP said, "Because in plenty of files you might have multiple "Eyes" layers for different characters, rather than always naming them character specific things I wanted the script to be able to find the character folder and then in that folder locate the Eyes layer."

So it seems he didn't want to have unique names.
dkwroot
Posts: 677
Joined: Thu May 02, 2013 6:56 am
Location: USA
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by dkwroot »

I see... Hmm, I can understand why he would request this. It would make it easier to duplicate entire rigs and still have everything carry over without having to go through the tedious effort of renaming every switch. On the other hand, I'm not sure how practical it would be to check parent hierarchy for each duplicate. This definitely wouldn't help performance when expanded out to crowded scenes.

This is my two-cents, but I think an animator has to make sacrifices when building rigs. The type of script the OP mentions is great on paper, but will hurt in performance. The script I mentioned would be better on performance, but would require the user manually change names.

I think a good compromise would be a second script that auto-names sublayers. The user would basically duplicate a rig and then name the master layer something like "BOB.MASTER" and then run the script with it selected. The script would then dive through the sublayers and look for instances of names that have a period within them. The first part of that name would then be replaced with "BOB". This kind of prepping would make the program have to work a lot less. :D
User avatar
synthsin75
Posts: 9934
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by synthsin75 »

Checking parent layers doesn't usually significantly impact performance. Much less than checking the entire document hierarchy or building and reading tables.
dkwroot
Posts: 677
Joined: Thu May 02, 2013 6:56 am
Location: USA
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by dkwroot »

Yeah, but you'd be checking the hierarchy twice, right? You'd have to check the hierarchy to find slave layers and then again to check whether that slave layer is part of the desired character. This might not seem like a lot, but if you applied this to a crowd I imagine it would cause some performance loss. I think this would have to be experimented with and stress checked.
User avatar
synthsin75
Posts: 9934
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by synthsin75 »

dkwroot wrote:Yeah, but you'd be checking the hierarchy twice, right? You'd have to check the hierarchy to find slave layers and then again to check whether that slave layer is part of the desired character.
No need to check twice. If you have some indicator of what the top layer is of each character (like a naming convention, symbol, extension, or simply a specific hierarchy, like it being the only bone/group layer not named after a body part or something) then you just check parent layers until you find this, and then only look for a match within that group.

If all source/slave pairs are siblings, then you can just assume it's in the parent layer. But ALL layerscripts are performance heavy when used in great numbers. There's really no avoiding that. If I ran into problems while animating, I'd simply disable the layerscript while the source layer is not visible (use layer comps to enable/disable all).
dkwroot
Posts: 677
Joined: Thu May 02, 2013 6:56 am
Location: USA
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by dkwroot »

So backtrack to the master layer and then use a recursive function to systematically check all sub-layers?

Maybe something like:

Code: Select all

local ControlLayer = moho.Layer()
local Parent = moho.Layer()
local go = true
while go do   --FIRST LOOK FOR MASTER CHARACTER LAYER
   Parent = Parent:Parent()
   if Parent == nil then
      break
   else
      local CheckMaster = string.match(Control,".MASTER")
      if CheckMaster then
         SlaveCheck(Parent)
      end
   end
end

function SlaveCheck(group)
   for i=0,group:CountLayers() do
      local Layer = group:Layer(i)
      if Layer:LayerType() == LT_SWITCH then
         local CheckLayer = string.match(Control,Sub)
         local CheckSlave = string.match(sub,".SLAVE")
         if CheckLayer and CheckSlave then
            Layer:SetValue(ControlLayer:GetValue())
         end
      end
   end

   if Layer:IsGroupType() then
      SlaveCheck(Layer)
   end
end

User avatar
synthsin75
Posts: 9934
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by synthsin75 »

Yeah, that's the idea.
dkwroot
Posts: 677
Joined: Thu May 02, 2013 6:56 am
Location: USA
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by dkwroot »

When I get the chance, I'll more formally prep the code and add some mesh instancing with it. You guys can do a stress test on it and see how snappy it is.
GaryC
Posts: 53
Joined: Tue Feb 03, 2015 1:02 pm

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by GaryC »

Just got back to this, thanks for the great discussion.

For clarification, yes we would like the characters to have non-unique names. I'm aware that a more specific naming convention would solve the problem since the part of my script that searches the whole document works fine, but there are characters on the show that are similar and reuse the same rigs so ideally they could keep the same naming convention on all the elements of their rigs.

Even then, the reason I'm not just making the argument to the team for giving up on that is that as far as I can tell performance should be worse with that method.
The two methods I'm using go like this:
1) Find the character layer, once that's found search all the layers within it for slaves.
2) Search every single layer in the whole document for slaves.

Technically method 1 ought to have less of an overhead than 2, yet 2 works perfectly fine, it's not even introducing lag.
While method 1 crashes the program in larger files.

What I was trying to set up was a similar flow to what dkwroot typed up there, though I was still using a table to store them and then just iterate through at the end of the sake of code neatness but it's a good point to cut that out to reduce overhead. (it was originally written when it only needed to handle a single target slave instead of multiple slaves)
dkwroot
Posts: 677
Joined: Thu May 02, 2013 6:56 am
Location: USA
Contact:

Re: Performance Issues - Script Linking Switch Layers Togeth

Post by dkwroot »

I'm still working on the script, but I have the basics done. This script should control all switch layers within a character, but there are rules.

STEPS
#1. The root folder for your character must have ".MASTER" at the end.
#2. You only embed this folder in the switch layer you want to be the master switch. Name this layer whatever you want.
#3. All slave switchlayers must be named the same as the master switch AND have ".SLAVE" at the end.

CODE:
https://drive.google.com/file/d/0Bwzk-f ... sp=sharing

EXAMPLE FILE (MAKE SURE YOU EMBED THE CODE IN THIS FILE!):
https://drive.google.com/file/d/0Bwzk-f ... sp=sharing
Post Reply