Panda3D is a framework for 3D rendering and game development for Python and C++ programs.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Panda3D provides comprehensive animation capabilities including character animation, procedural animation, and smooth interpolation systems for creating dynamic 3D content.
The Actor class handles animated 3D characters with skeletal animation, multiple animation sets, and advanced playback control.
class Actor:
def __init__(self,
model: str = None,
anims: dict = None,
copy: bool = True,
flattenable: bool = True,
setFinal: bool = False) -> None:
"""
Create animated character.
Args:
model: Path to character model file
anims: Dictionary mapping animation names to file paths
copy: Whether to copy geometry (True) or instance it (False)
flattenable: Whether model can be flattened for optimization
setFinal: Whether to finalize model structure
"""
def loadModel(self, modelPath: str) -> None:
"""Load character model from file."""
def loadAnims(self, anims: dict) -> None:
"""Load animation files. Dict maps names to file paths."""
def bindAnim(self, animName: str, animPath: str, partName: str = None) -> None:
"""Bind single animation to character or part."""
def unloadAnims(self, animNames: list = None, partNames: list = None) -> None:
"""Unload specified animations."""
def getAnimNames(self, partName: str = None) -> list:
"""Get list of loaded animation names."""
def hasAnim(self, animName: str) -> bool:
"""Check if animation is loaded."""Control animation playback with various modes, blending, and timing options.
class Actor:
def play(self, animName: str, partName: str = None, fromFrame: int = None, toFrame: int = None) -> None:
"""Play animation once from start to end."""
def loop(self, animName: str, partName: str = None, fromFrame: int = None, toFrame: int = None) -> None:
"""Loop animation continuously."""
def pingpong(self, animName: str, partName: str = None, fromFrame: int = None, toFrame: int = None) -> None:
"""Play animation back and forth."""
def stop(self, partName: str = None) -> None:
"""Stop animation playback."""
def pause(self, partName: str = None) -> None:
"""Pause animation at current frame."""
def resume(self, partName: str = None) -> None:
"""Resume paused animation."""
def pose(self, animName: str, frame: int, partName: str = None) -> None:
"""Set character to specific animation frame."""
def isPlaying(self, animName: str = None, partName: str = None) -> bool:
"""Check if animation is currently playing."""
def getCurrentAnim(self, partName: str = None) -> str:
"""Get name of currently playing animation."""
def getCurrentFrame(self, partName: str = None) -> int:
"""Get current animation frame number."""
def getNumFrames(self, animName: str = None, partName: str = None) -> int:
"""Get total frames in animation."""
def getDuration(self, animName: str = None, partName: str = None) -> float:
"""Get animation duration in seconds."""
def getFrameRate(self, animName: str = None, partName: str = None) -> float:
"""Get animation frame rate."""
def setPlayRate(self, rate: float, animName: str = None, partName: str = None) -> None:
"""Set animation playback speed multiplier."""
def getPlayRate(self, animName: str = None, partName: str = None) -> float:
"""Get current playback rate."""Advanced animation features for smooth transitions and blending between animations.
class Actor:
def enableBlend(self, blendType: int = 0, partName: str = None) -> None:
"""Enable animation blending."""
def disableBlend(self, partName: str = None) -> None:
"""Disable animation blending."""
def setBlend(self,
animBlend: bool = True,
frameBlend: bool = True,
blendType: int = 0,
partName: str = None) -> None:
"""Configure blending options."""
def setControlEffect(self, animName: str, effect: float, partName: str = None) -> None:
"""Set animation influence/weight (0.0-1.0)."""
def getControlEffect(self, animName: str, partName: str = None) -> float:
"""Get animation influence/weight."""Support for characters with multiple independently animated parts.
class Actor:
def makeSubpart(self, partName: str, includeJoints: list, excludeJoints: list = []) -> None:
"""Create animation subpart from specified joints."""
def getPartNames(self) -> list:
"""Get list of all part names."""
def removePart(self, partName: str) -> None:
"""Remove animation part."""
def hidePart(self, partName: str) -> None:
"""Hide part geometry."""
def showPart(self, partName: str) -> None:
"""Show part geometry."""
def showAllParts(self) -> None:
"""Show all parts."""The interval system provides procedural animation through interpolation of object properties over time.
class LerpPosInterval:
def __init__(self, nodePath: NodePath,
duration: float,
pos: Vec3,
startPos: Vec3 = None,
blendType: str = 'noBlend') -> None:
"""Animate object position over time."""
class LerpHprInterval:
def __init__(self, nodePath: NodePath,
duration: float,
hpr: Vec3,
startHpr: Vec3 = None,
blendType: str = 'noBlend') -> None:
"""Animate object rotation over time."""
class LerpScaleInterval:
def __init__(self, nodePath: NodePath,
duration: float,
scale: Vec3,
startScale: Vec3 = None,
blendType: str = 'noBlend') -> None:
"""Animate object scale over time."""
class LerpColorInterval:
def __init__(self, nodePath: NodePath,
duration: float,
color: Vec4,
startColor: Vec4 = None,
blendType: str = 'noBlend') -> None:
"""Animate object color over time."""
class LerpColorScaleInterval:
def __init__(self, nodePath: NodePath,
duration: float,
colorScale: Vec4,
startColorScale: Vec4 = None,
blendType: str = 'noBlend') -> None:
"""Animate color scale over time."""Create complex animations by combining multiple intervals in sequences and parallel groups.
class Sequence:
def __init__(self, *intervals) -> None:
"""Create sequence of intervals that play one after another."""
def start(self) -> None:
"""Start the sequence."""
def finish(self) -> None:
"""Jump to end of sequence."""
def pause(self) -> None:
"""Pause sequence playback."""
def resume(self) -> None:
"""Resume paused sequence."""
def loop(self) -> None:
"""Loop sequence continuously."""
def isPlaying(self) -> bool:
"""Check if sequence is playing."""
def getDuration(self) -> float:
"""Get total sequence duration."""
class Parallel:
def __init__(self, *intervals) -> None:
"""Create group of intervals that play simultaneously."""
def start(self) -> None: ...
def finish(self) -> None: ...
def pause(self) -> None: ...
def resume(self) -> None: ...
def loop(self) -> None: ...
def isPlaying(self) -> bool: ...
def getDuration(self) -> float: ...
def Wait(duration: float) -> WaitInterval:
"""Create pause/delay interval."""
def Func(function: callable, *args) -> FunctionInterval:
"""Create interval that calls function."""Control animation curves and transitions for more natural motion.
# Easing functions for smooth animation curves
def easeIn(t: float) -> float:
"""Ease-in curve (slow start)."""
def easeOut(t: float) -> float:
"""Ease-out curve (slow end)."""
def easeInOut(t: float) -> float:
"""Ease-in-out curve (slow start and end)."""
# Blend types for intervals
BLEND_EASE_IN = "easeIn"
BLEND_EASE_OUT = "easeOut"
BLEND_EASE_IN_OUT = "easeInOut"
BLEND_NO_BLEND = "noBlend"from direct.showbase.ShowBase import ShowBase
from direct.actor.Actor import Actor
from panda3d.core import Vec3
class CharacterDemo(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# Load animated character
self.character = Actor(
"models/characters/panda",
{
"walk": "models/characters/panda-walk",
"run": "models/characters/panda-run",
"idle": "models/characters/panda-idle"
}
)
self.character.reparentTo(self.render)
self.character.setScale(0.5)
self.character.setPos(0, 10, 0)
# Start with idle animation
self.character.loop("idle")
# Setup input
self.accept("1", self.playIdle)
self.accept("2", self.playWalk)
self.accept("3", self.playRun)
def playIdle(self):
"""Switch to idle animation."""
self.character.loop("idle")
def playWalk(self):
"""Switch to walking animation."""
self.character.loop("walk")
def playRun(self):
"""Switch to running animation."""
self.character.loop("run")
app = CharacterDemo()
app.run()from direct.showbase.ShowBase import ShowBase
from direct.interval.IntervalGlobal import *
from panda3d.core import Vec3
class AnimationDemo(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# Create object to animate
self.cube = self.loader.loadModel("models/cube")
self.cube.reparentTo(self.render)
self.cube.setPos(-5, 10, 0)
# Create complex animation sequence
self.createAnimation()
# Start animation
self.accept("space", self.startAnimation)
def createAnimation(self):
"""Create complex animation sequence."""
# Move right while growing
move_right = LerpPosInterval(
self.cube, 2.0, Vec3(5, 10, 0), blendType='easeInOut'
)
grow = LerpScaleInterval(
self.cube, 2.0, Vec3(2, 2, 2), blendType='easeInOut'
)
# Parallel movement and scaling
phase1 = Parallel(move_right, grow)
# Rotate and change color
rotate = LerpHprInterval(
self.cube, 1.5, Vec3(360, 0, 0)
)
color_change = LerpColorInterval(
self.cube, 1.5, (1, 0, 0, 1) # Red
)
phase2 = Parallel(rotate, color_change)
# Move up with pause
pause = Wait(0.5)
move_up = LerpPosInterval(
self.cube, 1.0, Vec3(5, 10, 3), blendType='easeOut'
)
# Final callback
finish_func = Func(self.onAnimationComplete)
# Combine into sequence
self.animation = Sequence(
phase1,
phase2,
pause,
move_up,
finish_func
)
def startAnimation(self):
"""Start the animation sequence."""
# Reset object state
self.cube.setPos(-5, 10, 0)
self.cube.setScale(1, 1, 1)
self.cube.setHpr(0, 0, 0)
self.cube.clearColor()
# Start animation
self.animation.start()
def onAnimationComplete(self):
"""Called when animation finishes."""
print("Animation completed!")
# Loop the animation
self.startAnimation()
app = AnimationDemo()
app.run()from direct.showbase.ShowBase import ShowBase
from direct.actor.Actor import Actor
class MultiPartDemo(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# Load character with multiple parts
self.character = Actor("models/characters/robot")
self.character.loadAnims({
"torso_idle": "models/anims/robot-torso-idle",
"torso_wave": "models/anims/robot-torso-wave",
"legs_walk": "models/anims/robot-legs-walk",
"legs_run": "models/anims/robot-legs-run"
})
self.character.reparentTo(self.render)
# Create animation parts
self.character.makeSubpart(
"torso",
["torso", "left_arm", "right_arm", "head"]
)
self.character.makeSubpart(
"legs",
["left_leg", "right_leg", "pelvis"]
)
# Start different animations on different parts
self.character.loop("torso_idle", partName="torso")
self.character.loop("legs_walk", partName="legs")
# Setup controls
self.accept("w", self.wave)
self.accept("r", self.run)
self.accept("s", self.stop)
def wave(self):
"""Play wave animation on torso."""
self.character.play("torso_wave", partName="torso")
# Return to idle after wave completes
duration = self.character.getDuration("torso_wave", "torso")
self.taskMgr.doMethodLater(
duration, self.returnToIdle, "return-idle"
)
def returnToIdle(self, task):
"""Return torso to idle animation."""
self.character.loop("torso_idle", partName="torso")
return task.done
def run(self):
"""Switch legs to running."""
self.character.loop("legs_run", partName="legs")
def stop(self):
"""Switch legs to walking."""
self.character.loop("legs_walk", partName="legs")
app = MultiPartDemo()
app.run()class AnimControl:
"""Animation control object for fine-tuned animation management."""
def play(self) -> None: ...
def stop(self) -> None: ...
def pose(self, frame: int) -> None: ...
class PartBundle:
"""Animation part bundle for multi-part characters."""
def getName(self) -> str: ...
def update(self) -> None: ...
# Animation blend types
BT_NO_BLEND = 0 # No blending
BT_NORMAL_BLEND = 1 # Normal animation blending
BT_ADDITIVE_BLEND = 2 # Additive blending
# Interval blend type constants
from direct.interval.IntervalGlobal import *
# Available blend types: 'noBlend', 'easeIn', 'easeOut', 'easeInOut'Install with Tessl CLI
npx tessl i tessl/pypi-panda3d