from direct.showbase.DirectObject import DirectObject
from direct.showbase.MessengerGlobal import messenger
from direct.directtools.DirectGeometry import *
from panda3d.core import NodePath, LineSegs, ClockObject
[docs]class Mopath(DirectObject):
nameIndex = 1
[docs] def __init__(self, name = None, fluid = 1, objectToLoad = None, upVectorNodePath = None, reverseUpVector = False):
if name is None:
name = 'mopath%d' % self.nameIndex
self.nameIndex = self.nameIndex + 1
self.name = name
self.fluid = fluid
self.tPoint = Point3(0)
self.posPoint = Point3(0)
self.hprPoint = Point3(0)
self.tangentVec = Vec3(0)
self.fFaceForward = 0
self.faceForwardDelta = None
self.faceForwardNode = None
self.timeScale = 1
self.upVectorNodePath = upVectorNodePath
self.reverseUpVector = reverseUpVector
self.reset()
if isinstance( objectToLoad, NodePath ):
self.loadNodePath( objectToLoad )
elif isinstance( objectToLoad, str ):
self.loadFile( objectToLoad )
elif objectToLoad is not None:
print("Mopath: Unable to load object '%s', objectToLoad must be a file name string or a NodePath" % objectToLoad)
[docs] def getMaxT(self):
return self.maxT * self.timeScale
[docs] def loadFile(self, filename, fReset = 1):
nodePath = base.loader.loadModel(filename)
if nodePath:
self.loadNodePath(nodePath)
nodePath.removeNode()
else:
print('Mopath: no data in file: %s' % filename)
[docs] def loadNodePath(self, nodePath, fReset = 1):
if fReset:
self.reset()
self.__extractCurves(nodePath)
if self.tNurbsCurve != []:
self.maxT = self.tNurbsCurve[-1].getMaxT()
elif self.xyzNurbsCurve is not None:
self.maxT = self.xyzNurbsCurve.getMaxT()
elif self.hprNurbsCurve is not None:
self.maxT = self.hprNurbsCurve.getMaxT()
else:
print('Mopath: no valid curves in nodePath: %s' % nodePath)
[docs] def reset(self):
self.maxT = 0.0
self.loop = 0
self.xyzNurbsCurve = None
self.hprNurbsCurve = None
self.tNurbsCurve = []
self.node = None
def __extractCurves(self, nodePath):
node = nodePath.node()
if isinstance(node, ParametricCurve):
if node.getCurveType() == PCTXYZ:
self.xyzNurbsCurve = node
elif node.getCurveType() == PCTHPR:
self.hprNurbsCurve = node
elif node.getCurveType() == PCTNONE:
if self.xyzNurbsCurve is None:
self.xyzNurbsCurve = node
else:
print('Mopath: got a PCT_NONE curve and an XYZ Curve in nodePath: %s' % nodePath)
elif node.getCurveType() == PCTT:
self.tNurbsCurve.append(node)
else:
# Iterate over children if any
for child in nodePath.getChildren():
self.__extractCurves(child)
[docs] def calcTime(self, tIn):
return self.__calcTime(tIn, self.tNurbsCurve)
def __calcTime(self, tIn, tCurveList):
if tCurveList:
tCurveList[-1].getPoint(tIn, self.tPoint)
return self.__calcTime(self.tPoint[0], tCurveList[:-1])
else:
return tIn
[docs] def getFinalState(self):
pos = Point3(0)
if self.xyzNurbsCurve is not None:
self.xyzNurbsCurve.getPoint(self.maxT, pos)
hpr = Point3(0)
if self.hprNurbsCurve is not None:
self.hprNurbsCurve.getPoint(self.maxT, hpr)
return (pos, hpr)
[docs] def goTo(self, node, time):
if self.xyzNurbsCurve is None and self.hprNurbsCurve is None:
print('Mopath: Mopath has no curves')
return
time /= self.timeScale
self.playbackTime = self.calcTime(CLAMP(time, 0.0, self.maxT))
if self.xyzNurbsCurve is not None:
self.xyzNurbsCurve.getPoint(self.playbackTime, self.posPoint)
if self.fluid:
node.setFluidPos(self.posPoint)
else:
node.setPos(self.posPoint)
if self.hprNurbsCurve is not None:
self.hprNurbsCurve.getPoint(self.playbackTime, self.hprPoint)
node.setHpr(self.hprPoint)
elif self.fFaceForward and self.xyzNurbsCurve is not None:
if self.faceForwardDelta:
# Look at a point a bit ahead in parametric time.
t = min(self.playbackTime + self.faceForwardDelta, self.xyzNurbsCurve.getMaxT())
lookPoint = Point3()
self.xyzNurbsCurve.getPoint(t, lookPoint)
if self.faceForwardNode:
self.faceForwardNode.setPos(lookPoint)
else:
self.xyzNurbsCurve.getTangent(self.playbackTime, self.tangentVec)
lookPoint = self.posPoint + self.tangentVec
# use the self.upVectorNodePath position if it exists to
# create an up vector for lookAt
if self.upVectorNodePath is None:
node.lookAt(lookPoint)
else:
if not self.reverseUpVector:
node.lookAt(lookPoint,
self.upVectorNodePath.getPos() - self.posPoint)
else:
node.lookAt(lookPoint,
self.posPoint - self.upVectorNodePath.getPos())
[docs] def play(self, node, time = 0.0, loop = 0):
if self.xyzNurbsCurve is None and self.hprNurbsCurve is None:
print('Mopath: Mopath has no curves')
return
self.node = node
self.loop = loop
self.stop()
t = taskMgr.add(self.__playTask, self.name + '-play')
t.currentTime = time
t.lastTime = ClockObject.getGlobalClock().getFrameTime()
[docs] def stop(self):
taskMgr.remove(self.name + '-play')
def __playTask(self, task):
time = ClockObject.getGlobalClock().getFrameTime()
dTime = time - task.lastTime
task.lastTime = time
if self.loop:
cTime = (task.currentTime + dTime) % self.getMaxT()
else:
cTime = task.currentTime + dTime
if self.loop == 0 and cTime > self.getMaxT():
self.stop()
messenger.send(self.name + '-done')
self.node = None
return task.done
self.goTo(self.node, cTime)
task.currentTime = cTime
return task.cont
[docs] def draw(self, subdiv = 1000):
""" Draws a quick and cheesy visualization of the Mopath using
LineSegs. Returns the NodePath representing the drawing. """
ls = LineSegs('mopath')
p = Point3()
for ti in range(subdiv):
t = float(ti) / float(subdiv) * self.maxT
tp = self.calcTime(t)
self.xyzNurbsCurve.getPoint(tp, p)
ls.drawTo(p)
return NodePath(ls.create())