Adding ribbon trails to an object

This recipe will show you how to implement a ribbon trail effect that is often used for emphasizing high-speed movements such as sword slashes, very fast vehicles passing by or, as you will see after finishing this recipe, the fastest running panda in the world.

Getting ready

Follow the instructions of Setting up the game structure found in Chapter 1 before you proceed to create a basic project setup.

How to do it...

The following steps are necessary for implementing the ribbon trail effect:

  1. Add a new file called Ribbon.py to the project and add the following code:
    from direct.task import Task
    from direct.task.TaskManagerGlobal import taskMgr
    from panda3d.core import *
    class RibbonNode():
    def __init__(self, pos, damping):
    self.pos = Vec3(pos)
    self.damping = damping
    self.delta = Vec3()
    def update(self, pos):
    self.delta = (pos - self.pos) * self.damping
    self.pos += self.delta
    class Ribbon():
    def __init__(self, parent, color, thickness, length, damping):
    self.parent = parent
    self.length = length
    self.thickness = thickness
    self.color = color
    self.lineGen = MeshDrawer()
    self.lineGen.setBudget(100)
    genNode = self.lineGen.getRoot()
    genNode.reparentTo(render)
    genNode.setTwoSided(True)
    genNode.setTransparency(True)
    pos = parent.getPos(render)
    self.trailPoints = []
    for i in range(length):
    self.trailPoints.append(RibbonNode(pos, damping))
    taskMgr.add(self.trail, "update trail")
    def getRoot(self):
    return self.lineGen.getRoot()
    def trail(self, task):
    pos = self.parent.getPos(render)
    self.trailPoints[0].update(pos)
    for i in range(1, self.length):
    ribbon trails effectimplementing, on objectself.trailPoints[i].update(self.trailPoints[i - 1].pos)
    self.lineGen.begin(base.cam, render)
    color = Vec4(self.color)
    thickness = self.thickness
    for i in range(self.length - 1):
    p1 = self.trailPoints[i].pos
    p2 = self.trailPoints[i + 1].pos
    startColor = Vec4(color)
    endColor = Vec4(color)
    endColor.setW(color.getW() - 0.2)
    color = Vec4(endColor)
    self.lineGen.unevenSegment(p1, p2, 0, thickness, startColor, thickness - 0.3, endColor)
    thickness -= 0.3
    self.lineGen.end()
    return task.cont
    
  2. Open Application.py and enter the following lines of code:
    from direct.showbase.ShowBase import ShowBase
    from direct.showbase.RandomNumGen import RandomNumGen
    from direct.actor.Actor import Actor
    from panda3d.core import *
    from direct.interval.IntervalGlobal import *
    from Ribbon import Ribbon
    class Application(ShowBase):
    def __init__(self):
    ShowBase.__init__(self)
    self.panda = Actor("panda", {"walk": "panda-walk"})
    self.panda.reparentTo(render)
    self.panda.loop("walk")
    self.panda.setHpr(-90, 0, 0)
    self.ribbon = Ribbon(self.panda, Vec4(1, 1, 1, 1), 3, 10, 0.3)
    ribbon trails effectimplementing, on objectself.ribbon.getRoot().setZ(5)
    self.walkIval1 = self.panda.posInterval(1, Vec3(-12, 0, 0), startPos = Vec3(12, 0, 0))
    self.walkIval2 = self.panda.posInterval(1, Vec3(12, 0, 0), startPos = Vec3(-12, 0, 0))
    self.turnIval1 = self.panda.hprInterval(0.1, Vec3(90, 0, 0), startHpr = Vec3(-90, 0, 0))
    self.turnIval2 = self.panda.hprInterval(0.1, Vec3(-90, 0, 0), startHpr = Vec3(90, 0, 0))
    self.pandaWalk = Sequence(self.walkIval1, self.turnIval1, self.walkIval2, self.turnIval2)
    self.pandaWalk.loop()
    self.cam.setPos(0, -60, 6)
    self.cam.lookAt(0, 0, 6)
    
  3. Press F6 to start the program and see the panda running:
How to do it...

How it works...

Our code puts a trail behind our panda actor that slowly fades out. Let's take a closer look at the code that produced this effect.

In the constructor of the Ribbon class, after initializing our member variables, we set up a new MeshDrawer, which is a very convenient class for working with dynamically updated geometry like our ribbons. We configure it to use a budget of 100 triangles and enable transparency and double sided rendering for the generated geometry.

After this is done, we fill a list of RibbonNodes. Each of these nodes will then try to follow its predecessor in the list, but will be hampered by the amount of damping we specified in the constructor parameter, so our nodes are keeping some distance, between which we span some geometry using a MeshDrawer and unevenSegment() method that draws line segments with different sized ends. Not only the size of the line decreases, but we also make the alpha smaller and smaller with each segment until the trail smoothly fades out.

This leaves us with building a little test scene in our Application class, connecting the ribbon to the panda that is moved back and forth using intervals.

There's more

The Ribbon class is far from complete, but it does its job and shows nicely how the MeshDrawer class can help to procedurally generate and modify geometry. Some points you may want to extend, for example, are the way the alpha value and the ribbon size are controlled.

Additionally, you could experiment with different damping values and behaviors. Instead of using the same damping value for all RibbonNode objects in the trail, you could try to assign different values to the nodes, making the ones in the back of the trail slower, for example.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset