Sprite Sheet Extractor Fuse

User avatar
Midgardsormr
Fusionator
Posts: 1318
Joined: Wed Nov 26, 2014 8:04 pm
Answers: 6
Location: Los Angeles, CA, USA
Been thanked: 120 times
Contact:

Sprite Sheet Extractor Fuse

#1

Post by Midgardsormr » Tue Dec 03, 2019 5:44 pm

One of my coworkers asked for a Fusion tool that could handle sprite sheets, such as are commonly used for video game fx animation. This is rough, but it works. Suggestions for refinement are welcome. I'd like to have a trim control in case the last row doesn't have an image in every column, but I haven't quite sorted out how to do that.
Code: [Select all] [Expand/Collapse] [Download] (spritesheet_extractor.fuse)
  1. --[[--
  2. spriteSheet_extractor.Fuse
  3. by Bryan Ray for Muse VFX
  4.  
  5. Converts a sprite sheet into an image sequence
  6.  
  7. version 1.0
  8. December 3, 2019
  9. --]]--
  10.  
  11.  
  12. FuRegisterClass("SpriteSheet", CT_Tool, {
  13.     REGS_Name               = "SpriteSheet",
  14.     REGS_Category           = "Miscellaneous",
  15.     REGS_OpIconString       = "ss",
  16.     REGS_OpDescription      = "Sprite Sheet Extractor",
  17.     REG_OpNoMask            = true,
  18.     REG_NoBlendCtrls        = true,
  19.     REG_NoObjMatCtrls       = true,
  20.     REG_NoMotionBlurCtrls   = true,
  21.     REG_NoPreCalcProcess    = true,
  22.     REG_TimeVariant         = true,
  23.     REG_Version             = 100,
  24.     })
  25.  
  26. function Create()
  27.     InColumns = self:AddInput("Columns", "Columns", {
  28.         LINKID_DataType = "Number",
  29.         INPID_InputControl = "SliderControl",
  30.         INP_MinScale = 0.0,
  31.         INP_MaxScale = 24.0,
  32.         INP_Default = 4,
  33.         INP_Integer = true,
  34.         })
  35.        
  36.     InRows = self:AddInput("Rows", "Rows", {
  37.         LINKID_DataType = "Number",
  38.         INPID_InputControl = "SliderControl",
  39.         INP_MinScale = 0.0,
  40.         INP_MaxScale = 24.0,
  41.         INP_Default = 4,
  42.         INP_Integer = true,    
  43.         })
  44.        
  45.     InStartFrame = self:AddInput("Start Frame", "StartFrame", {
  46.         LINKID_DataType = "Number",
  47.         INPID_InputControl = "ScrewControl",
  48.         INP_MinScale = 1001,
  49.         INP_MaxScale = 2000,
  50.         INP_Default = 1001,
  51.         INP_Integer = true,    
  52.         })
  53.        
  54.     -- InTrimEnd = self:AddInput("Trim End", "TrimEnd", {
  55.         -- LINKID_DataType = "Number",
  56.         -- INPID_InputControl = "SliderControl",
  57.         -- INP_MinScale = 0.0,
  58.         -- INP_MaxScale = 24.0,
  59.         -- INP_Default = 0,
  60.         -- INP_Integer = true, 
  61.         -- })
  62.        
  63.     InImage = self:AddInput("Input", "Input", {
  64.         LINKID_DataType = "Image",
  65.         LINK_Main = 1,
  66.         })
  67.  
  68.     OutImage = self:AddOutput("Output", "Output", {
  69.         LINKID_DataType = "Image",
  70.         LINK_Main = 1,
  71.         INP_SendRequest = false,
  72.         INP_Required = false,
  73.         })
  74.        
  75. end
  76.  
  77. function Process(req)
  78.  
  79.     local img = InImage:GetValue(req)
  80.     local rows = InRows:GetValue(req).Value
  81.     local columns = InColumns:GetValue(req).Value
  82.     --local trim = InTrimEnd:GetValue(req).Value
  83.  
  84.     local width = img.Width / columns
  85.     local height = img.Height / rows
  86.     local length = rows * columns - 1 -- - trim
  87.    
  88.     local startFrame = InStartFrame:GetValue(req).Value
  89.     local currentTime = req.Time - startFrame
  90.    
  91.     local out = Image({
  92.         IMG_Like = img,
  93.         IMG_Width = math.ceil(width),
  94.         IMG_Height = math.ceil(height),
  95.     })
  96.    
  97.     -- Which image?  
  98.     local column = currentTime % columns
  99.     local row = math.floor(currentTime / columns) % rows
  100.    
  101.     img:Crop(out, {
  102.         CROP_Width = width,
  103.         CROP_Height = height,
  104.         CROP_XOffset = width*column,
  105.         CROP_YOffset = height*(rows-row-1),
  106.     })
  107.  
  108.    
  109.     OutImage:Set(req, out)
  110. end
  111.  
spriteSheet_extractor.fuse
You do not have the required permissions to view the files attached to this post.

User avatar
Midgardsormr
Fusionator
Posts: 1318
Joined: Wed Nov 26, 2014 8:04 pm
Answers: 6
Location: Los Angeles, CA, USA
Been thanked: 120 times
Contact:

Re: Sprite Sheet Extractor Fuse

#2

Post by Midgardsormr » Wed Dec 04, 2019 5:55 pm

Small update to this guy adds a control to trim off empty frames at the end of the animation. It's also been submitted to Reactor.
Code: [Select all] [Expand/Collapse] [Download] (MT_SpriteSheet_Extractor.fuse)
  1. --[[--
  2. spriteSheet_extractor.Fuse
  3. by Bryan Ray for Muse VFX
  4.  
  5. Converts a sprite sheet into an image sequence
  6.  
  7. version 1.0
  8. December 4, 2019
  9. --]]--
  10.  
  11.  
  12. FuRegisterClass("SpriteSheet", CT_Tool, {
  13.     REGS_Name               = "SpriteSheet",
  14.     REGS_Category           = "Miscellaneous",
  15.     REGS_OpIconString       = "ss",
  16.     REGS_OpDescription      = "Sprite Sheet Extractor",
  17.     REG_OpNoMask            = true,
  18.     REG_NoBlendCtrls        = true,
  19.     REG_NoObjMatCtrls       = true,
  20.     REG_NoMotionBlurCtrls   = true,
  21.     REG_NoPreCalcProcess    = true,
  22.     REG_TimeVariant         = true,
  23.     REG_Version             = 100,
  24.     })
  25.  
  26. function Create()
  27.     InColumns = self:AddInput("Columns", "Columns", {
  28.         LINKID_DataType = "Number",
  29.         INPID_InputControl = "SliderControl",
  30.         INP_MinScale = 0.0,
  31.         INP_MaxScale = 24.0,
  32.         INP_Default = 4,
  33.         INP_Integer = true,
  34.         })
  35.        
  36.     InRows = self:AddInput("Rows", "Rows", {
  37.         LINKID_DataType = "Number",
  38.         INPID_InputControl = "SliderControl",
  39.         INP_MinScale = 0.0,
  40.         INP_MaxScale = 24.0,
  41.         INP_Default = 4,
  42.         INP_Integer = true,    
  43.         })
  44.        
  45.     InStartFrame = self:AddInput("Start Frame", "StartFrame", {
  46.         LINKID_DataType = "Number",
  47.         INPID_InputControl = "ScrewControl",
  48.         INP_MinScale = 1001,
  49.         INP_MaxScale = 2000,
  50.         INP_Default = 1001,
  51.         INP_Integer = true,    
  52.         })
  53.        
  54.     InTrimEnd = self:AddInput("Trim End", "TrimEnd", {
  55.         LINKID_DataType = "Number",
  56.         INPID_InputControl = "SliderControl",
  57.         INP_MinScale = 0.0,
  58.         INP_MaxScale = 24.0,
  59.         INP_Default = 0,
  60.         INP_Integer = true,
  61.         })
  62.        
  63.     InImage = self:AddInput("Input", "Input", {
  64.         LINKID_DataType = "Image",
  65.         LINK_Main = 1,
  66.         })
  67.  
  68.     OutImage = self:AddOutput("Output", "Output", {
  69.         LINKID_DataType = "Image",
  70.         LINK_Main = 1,
  71.         INP_SendRequest = false,
  72.         INP_Required = false,
  73.         })
  74.        
  75. end
  76.  
  77. function Process(req)
  78.  
  79.     local img = InImage:GetValue(req)
  80.     local rows = InRows:GetValue(req).Value
  81.     local columns = InColumns:GetValue(req).Value
  82.     local trim = InTrimEnd:GetValue(req).Value
  83.  
  84.     local width = img.Width / columns
  85.     local height = img.Height / rows
  86.     local length = rows * columns - trim
  87.    
  88.     local startFrame = InStartFrame:GetValue(req).Value
  89.     local currentTime = req.Time - startFrame
  90.     local offset = math.floor(currentTime / length)
  91.    
  92.     local out = Image({
  93.         IMG_Like = img,
  94.         IMG_Width = math.ceil(width),
  95.         IMG_Height = math.ceil(height),
  96.     })
  97.    
  98.     -- Which image?  
  99.     local column = (currentTime-(length*offset)) % columns
  100.     local row = math.floor((currentTime-(length*offset)) / columns) % rows
  101.    
  102.     img:Crop(out, {
  103.         CROP_Width = width,
  104.         CROP_Height = height,
  105.         CROP_XOffset = width*column,
  106.         CROP_YOffset = height*(rows-row-1),
  107.     })
  108.  
  109.    
  110.     OutImage:Set(req, out)
  111. end
  112.  

User avatar
SecondMan
Site Admin
Posts: 3799
Joined: Thu Jul 31, 2014 5:31 pm
Answers: 8
Location: Vancouver, Canada
Been thanked: 149 times
Contact:

Re: Sprite Sheet Extractor Fuse

#3

Post by SecondMan » Wed Dec 04, 2019 10:52 pm

It's a really fun little tool that can probably be leveraged for some other quirky uses. One issue that I've found with it is that it doesn't play very nicely with Proxy Scaling...

User avatar
Midgardsormr
Fusionator
Posts: 1318
Joined: Wed Nov 26, 2014 8:04 pm
Answers: 6
Location: Los Angeles, CA, USA
Been thanked: 120 times
Contact:

Re: Sprite Sheet Extractor Fuse

#4

Post by Midgardsormr » Thu Dec 05, 2019 6:51 am

Oh, yeah, that would make sense. I didn't even think about proxy (again). Probably reasonably easy to fix.

One thing that I hope to experiment with is using it for animated VolumeFog texture. I'm not 100% sure I know what will happen, but you could, theoretically, put the slices in a sequence of sprite sheets. Will Fusion be able to use it the way I imagine? I have no idea. There may need to be another layer of some kind. Two dimensional time makes my brain hurt.

User avatar
SecondMan
Site Admin
Posts: 3799
Joined: Thu Jul 31, 2014 5:31 pm
Answers: 8
Location: Vancouver, Canada
Been thanked: 149 times
Contact:

Re: Sprite Sheet Extractor Fuse

#5

Post by SecondMan » Thu Dec 05, 2019 6:58 am

Hah! That's the quirky use I had in mind 😊

I guess to make it easy to use you'd need some sort of "spritesheet unpacker" and a way to preview the shape somehow... build a proof of concept with a couple of macros perhaps... 🤔

3dmus
Posts: 43
Joined: Sat Jan 10, 2015 3:14 am

Re: Sprite Sheet Extractor Fuse

#6

Post by 3dmus » Thu Dec 05, 2019 1:46 pm

This is great Bryan! I actually had made a macro with similar functionality, but this is much more elegant! I used my macro for slicing and dicing pictures and then feeding it into a particle system to animate. Like in the example below.