先来看一段代码
from math import pi,sin,cos
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import Sequence
from panda3d.core import Point3
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# Load the environment model.
self.scene = self.loader.loadModel("/Developer/Panda3D/models/environment")
# Reparent the model to render.
self.scene.reparentTo(self.render)
# Apply scale and position transforms on the model.
self.scene.setScale(0.25, 0.25, 0.25)
self.scene.setPos(-8, 42, 0)
# Add the spinCameraTask procedure to the task manager.
self.taskMgr.add(self.spinCameraTask,"SpinCameraTask")
#load and transfrom the panda actor
self.pandaActor = Actor("/Developer/Panda3D/models/panda-model",{"walk":"/Developer/Panda3D/models/panda-walk4"})
self.pandaActor.setScale(0.005,0.005,0.005)
self.pandaActor.reparentTo(self.render)
# Loop its animation
self.pandaActor.loop("walk")
# create the four lerp intervals needed for the panda to
# walk back and forth
pandaPosInterval1 = self.pandaActor.posInterval(13,Point3(0,-10,0),startPos=Point3(0,10,0))
pandaPosInterval2 = self.pandaActor.posInterval(13,Point3(0,-10,0),startPos=Point3(0,-10,0))
pandaHprInterval1 = self.pandaActor.hprInterval(3,Point3(180,0,0),startHpr=Point3(0,0,0))
pandaHprInterval2 = self.pandaActor.hprInterval(3,Point3(0,0,0),startHpr=Point3(180,0,0))
# Create and play the sequence that coordinates the intervals.
self.pandaPace = Sequence(pandaPosInterval1,pandaHprInterval1,pandaPosInterval2,pandaHprInterval2)
self.pandaPace.loop()
# Define a procedure to move the camera
def spinCameraTask(self,task):
angleDegrees = task.time * 6.0
angleRadians = angleDegrees * (pi/180.0)
self.camera.setPos(20 * sin(angleRadians),-20.0*cos(angleRadians),3)
self.camera.setHpr(angleDegrees,0,0)
return Task.cont
app = MyApp()
app.run()
pandaPosInterval1
间隔开始,他将会逐步的将熊猫在13秒内从(0,10,0)转移到(0,-10,0)的位置。
pandaHprInterval1
与上面的的累死,3秒旋转180度
而后面的Sequence则使panda按照一个顺序来执行这几个任务,即先直线走到那个位置,然后旋转180度之后回头直走,之后再转180度
场景图,是一个有节点的树
对于场景渲染来说,在panda3D中,只有当你把他插入render树中的时候,他才会可见。
render树由PandaNode节点构成,他是很多其他类的超类:Modelnode
、GeoNode
、LightNode
等等。但是所有的节点的总根是render
(注:已有特殊情况不一样,例如2D情况下根是render2D)
关于场景树的层级:
如果没有很明显的要求,那就平行地插入树中(形成父子关系要谨慎)
又一个帮助类叫做NodePath
,是一个非常小的包含了指向节点的指针的对象,他还有一些管理的信息。暂时,你可以不用了解那些管理的信息,在接下来我会介绍。Panda3D的设计者们把这个当作一个节点的句柄,任何的函数一旦create了一个节点则会生成这样一个句柄。
他不是一个指向节点的指针,他是一个句柄。概念上来说,这是两者最大的不同。但是有一些函数希望你传入的是NodePath,但是其他的一些函数希望你传入的是指针,因此要注意。
你可以通过nodePath.node()
函数随时把一个nodePath转换成一个指针。但是,很清楚的是没有办法转换回来。因此,建议你用nodePath来存储,而不是指针。
我们看一些例子:
# NODEPATH METHODS:
myNodePath.setPos(x, y, z)
myNodePath.setColor(banana)
# LODNODE METHODS:
myNodePath.node().addSwitch(1000, 100)
myNodePath.node().setCenter(Point3(0, 5, 0))
# CAMERA NODE METHODS:
myNodePath.node().setLens(PerspectiveLens())
myNodePath.node().getCameraMask()
总之要记住的是,当你操作一个NodePath的时候就是在操作一个node。
在上面的例子中通过NodePath
来调用Node
是一个best practice
默认有两种场景图(两种根节点):render
和render2d
正常的3d场景你需要把render作为第一个父亲。
而2d的GUI元素,例如text和buttons则需要render2d,所有的render2d都会在3d渲染场景的上部渲染,例如我们想将画面做成一个玻璃碎裂的效果
render2d的坐标系统被用来和鼠标输入来匹配:最左下角的边缘是(-1,0,-1)最右上角的坐标是(1,0,1)。既然这个是一个方形的坐标系统,但是屏幕通常不是放行的,有些对象可能是被压扁的,因此Panda3D海底泥来一个render2d的孩子叫做aspect2d,他有一个scale属性用来正确的控制非矩形的情况。更多的时候你的父亲GUI元素是aspect2d
而不是render2d
尤其是,这个坐标系统中的aspect2d
默认的scaled被置为[-ratio,ratio],而y被置为[-1,1]之间(因为一般情况下,长比宽要长)
还有一个更高一层的node叫做hidden
,如果作为他的孩子则无法被渲染,老的Panda3D需要使用hidden来移除一个场景图中的节点,但是这对于现在的我们来说不需要了。对于新版本来说,建议的方式是调用nodePath.detachNode()
函数。
你可以通过一个文件路径名来载入图像,panda3D中大部分是.egg``.bam
,如果没有后缀名则默认当做这两个去寻找。
如果要对节点和模型重定向父亲节点,则调用这个函数就可以了:
myModel.reparentTo(fooNode)
,fooNode
是个占位符,表示场景树中的一个节点。
然后再移动一次:myModel.detachNode()
如果确定以后再也不用这个模型,并且决定释放资源,则调用这个函数:
myModel.removeNode()
还有一个常用的函数是:
myModel.wrtReparentTo(fooNode)
,这里的wrt表示的是”with repect to”,相对于,表示一个相对于的位置。
不过这个函数的使用要小心,因为wrt操作是一个浮点数的矩阵运算,他每次操作都会造成部分误差,如果很多次这样的操作会造成明显的偏差,因此要小心使用。小心overuse。