ModelPart

From FiguraMC
Revision as of 00:29, 27 September 2024 by PenguinEncounter (talk | contribs) (→‎ModelPart:getChildren: use new array syntax)

What are ModelParts

A ModelPart represents a Cube, Mesh, or Group from Blockbench.

Via scripting, ModelParts can be modified dynamically to do achieve whatever visuals you need (limited to Minecraft's rendering engine)

Accessing ModelParts

models is the global that acts as the Root of your avatar, and is where all ModelParts are stored, either as a child of models, a child of a child, or further down the tree.

Specifically, models's children are Blockbench models. When an avatar is selected, not only are a bbmodel's Cubes, Meshes, and Groups converted into ModelParts, but the bbmodel file itself is converted into ModelParts.

To access a child ModelPart from a parent ModelPart, index the parent using the child's name as a string.

Example:

local bbmodel = models["model"] -- access the bbmodel "model.bbmodel"

This logic is the same for every single ModelPart. If you want to access a child, index the parent with the child's name.

Fun Fact: models itself is a ModelPart

The below example accesses the Head Group from the WikiExampleAvatar

local head = models["model"]["root"]["Head"]

If the above seems tedious, Lua provides a shorthand way of indexing with strings. It only works when the string contains no spaces or special characters.

local head = models.model.root.Head

-- Below is invalid, as the name contains spaces
local superMegaAwsomeThing = models.model.root.Super Mega Awsome Thing

-- Super Mega Awsome Thing would need to be index with the normal method
local superMegaAwsomeThing = models.model.root["Super Mega Awsome Thing"]

BBModels within folders

When a bbmodel is within a folder, Figura will actually turn the folder itself into a ModelPart. This can be confusing as ModelParts are the only thing that does this.

local folderHead = models.folder.model.root.Head

ModelPart ParentTypes

In Figura, a ParentType is applied to a ModelPart causing the ModelPart to behave in a predefined behaviour. This behaviour includes behaving like a vanilla part like the Head or LeftLeg, moving the rendering of special parts like held items, armor, and parrots, or binding the ModelPart to the World instead of the Player.

ParentTypes can either be applied in Blockbench itself, or via script.

ParentTypes via BlockBench

To define a ParentType in Blockbench, the name of a Group must begin with a ParentType

For example, the Head ParentType causes the ModelPart to move and rotate like the vanilla Head. If you name a Group the following, it will have this ParentType:

  • Head
  • HEAD
  • Head2
  • HeadPhones

If you name a group the following, it will not have the Head ParentType:

  • head => 'head' is not a valid ParentType. Capitalization matters!
  • BigHead => Does not begin with ParentType
  • HeAd => 'HeAd' is not a valid ParentType. Capitalization matters!

Do note that some English words naturally start with a ParentType and can cause unintended behaviour!

For example, the word "Guitar". Seems innocent, right? Well, "Guitar" starts with "Gui", and "Gui" is an alias for the ParentType Hud, a ParentType that binds the ModelPart to the Heads Up Display where the hotbar and inventory reside. This causes the ModelPart to not be visible, as Hud parts have special rules. Be careful!

Note: ParentTypes are sometimes called Keywords by legacy Figura users

ParentTypes via Script

Calling the setParentType on any ModelPart will override it's ParentType.

Simply access the ModelPart, then call setParentType on it, passing in the ParentType as a string.

local head = models.model.root.Head
head:setParentType("RightArm")

To remove a ParentType from a ModelPart, call setParentType with the string "None"

local head = models.model.root.Head
head:setParentType("None")

ModelParts do not remember their original ParentType! If you need a ModelPart's original ParentType, store it in a variable for later:

local head = models.model.root.Head
local headParentType = head:getParentType()
head:setParentType("None")

ModelPart Methods

ModelPart Properties

ModelPart:getName

Returns the name of this ModelPart. ModelParts can have duplicate names.

Arguments Return Type
getName() string
local head = models.model.root.Head
print(head:getName()) --> "Head"

ModelPart:getType

Returns the original type of this ModelPart. Can be Cube, Mesh, or Group. BBModels and folders are considered Groups. Cubes with no texture data are also considered Groups, as with no texture data their face data is stripped.

Arguments Return Type
getType() "Cube" | "Mesh" | "Group"
local head = models.model.root.Head
print(head:getType()) --> "Group"

Child Manipulation

ModelPart:getParent

Returns the parent of this ModelPart, or nil if this is models or a loose ModelPart created via script.

Arguments Return Type
getParent() ModelPart | nil
local head = models.model.root.Head
print(head:getParent()) --> ModelPart(root)

ModelPart:getChildren

Returns an array of all children of this ModelPart

Arguments Return Type
getChildren() ModelPart[]
local root = models.model.root
print(root:getChildren()) --[[
ModelPart(Head), 
ModelPart(Body), 
ModelPart(RightArm), 
ModelPart(LeftArm), 
ModelPart(RightLeg), 
ModelPart(LeftLeg),
]]--

ModelPart:isChildOf

Recursivly checks if this child is a decedent of the provided ModelPart.

Arguments Return Type
isChildOf(part ModelPart) boolean
local model = models.model
local head = model.root.Head
print(head:isChildOf(model)) --> true

ModelPart:copy

Copies the data of this ModelPart, assigning it a new name, then returning it. The new name has nothing to do with ParentTypes. The children of the copy are references to the original ModelPart's children. Extra work needs to be done to recursively copy it's children. The addChild method can then be used to add the copy to the ModelPart tree.

Arguments Return Type
copy(newName string) ModelPart
local head = models.model.root.Head

local headCopy = head:copy("NewHead")
print(headCopy) --> ModelPart(NewHead)
-- The parentType is copied with the ModelPart
local head = models.model.root.Head

local headCopy = head:copy("NewHead")
print(headCopy:getParentType()) --> "Head"

headCopy:setParentType("None")
print(headCopy:getParentType()) --> "None"

@see setParentType, getParentType

-- Extra work is needed to deep copy a ModelPart's children as well
local function deepCopy(model)
    local copy = model:copy(model:getName())
    for _, child in pairs(copy:getChildren()) do
        copy:removeChild(child):addChild(deepCopy(child))
    end
    return copy
end

local head = models.model.root.Head
local headCube = head.Head

-- headCopyA contains references to head's children cause it is a shallow copy
local headCopyA = head:copy("NewHead")
print(headCopyA.Head == headCube) --> true

-- headCopyB contains true copies of head's children as it was passed through a function that recursively copies children
local headCopyB = deepCopy(head)
print(headCopyB.Head == headCube) --> false

@see getName, getChildren, addChild, removeChild

ModelPart:addChild

Adds the provided ModelPart as one of this ModelPart's children. Beware of adding a parent of this ModelPart as a child! This method returns this ModelPart for method chaining.

Arguments Return Type
addChild(part ModelPart) self ModelPart
local root = models.model.root
local headCopy = root.Head:copy("NewHead")
root:addChild(headCopy)
headCopy:setPos(10,0,0)

@see copy, setPos

ModelPart:removeChild

Removes a child from this ModelPart. Takes in the child ModelPart itself. This method returns this ModelPart for method chaining.

Arguments Return Type
removeChild(partToRemove ModelPart) self ModelPart
local root = models.model.root
local head = root.Head
root:removeChild(head) -- Bye bye Head!

-- Head is still valid and can be re-added later, so long as we keep a variable referencing it.
print(head) --> ModelPart(Head)

ModelPart:remove

Removes this ModelPart from the parent's child list. Returns self

Arguments Return Type
remove() self ModelPart
local head = models.model.root.Head
head:remove() -- Bye bye Head!

-- Head is still valid and can be re-added, so long as we keep a variable referencing it.
print(head) --> ModelPart(Head)
models.model.root.LeftLeg:addChild(head)

@see addChild