main.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. #!/usr/bin/env python
  2. # Author: Shao Zhang and Phil Saltzman
  3. # Models and Textures by: Shaun Budhram, Will Houng, and David Tucker
  4. # Last Updated: 2015-03-13
  5. #
  6. # This tutorial will cover exposing joints and manipulating them. Specifically,
  7. # we will take control of the neck joint of a humanoid character and rotate that
  8. # joint to always face the mouse cursor. This will in turn make the head of the
  9. # character "look" at the mouse cursor. We will also expose the hand joint and
  10. # use it as a point to "attach" objects that the character can hold. By
  11. # parenting an object to a hand joint, the object will stay in the character's
  12. # hand even if the hand is moving through an animation.
  13. from direct.showbase.ShowBase import ShowBase
  14. from panda3d.core import AmbientLight, DirectionalLight
  15. from panda3d.core import TextNode, NodePath, LightAttrib
  16. from panda3d.core import LVector3
  17. from direct.actor.Actor import Actor
  18. from direct.task.Task import Task
  19. from direct.gui.OnscreenText import OnscreenText
  20. from direct.showbase.DirectObject import DirectObject
  21. import sys
  22. # A simple function to make sure a value is in a given range, -1 to 1 by
  23. # default
  24. def clamp(i, mn=-1, mx=1):
  25. return min(max(i, mn), mx)
  26. # Macro-like function used to reduce the amount to code needed to create the
  27. # on screen instructions
  28. def genLabelText(text, i):
  29. return OnscreenText(text=text, parent=base.a2dTopLeft, scale=.06,
  30. pos=(0.06, -.08 * i), fg=(1, 1, 1, 1),
  31. shadow=(0, 0, 0, .5), align=TextNode.ALeft)
  32. class LookingGrippingDemo(ShowBase):
  33. def __init__(self):
  34. # Initialize the ShowBase class from which we inherit, which will
  35. # create a window and set up everything we need for rendering into it.
  36. ShowBase.__init__(self)
  37. # This code puts the standard title and instruction text on screen
  38. self.title = OnscreenText(text="Panda3D: Tutorial - Joint Manipulation",
  39. fg=(1, 1, 1, 1), parent=base.a2dBottomRight,
  40. align=TextNode.ARight, pos=(-0.1, 0.1),
  41. shadow=(0, 0, 0, .5), scale=.08)
  42. self.onekeyText = genLabelText("ESC: Quit", 1)
  43. self.onekeyText = genLabelText("[1]: Teapot", 2)
  44. self.twokeyText = genLabelText("[2]: Candy cane", 3)
  45. self.threekeyText = genLabelText("[3]: Banana", 4)
  46. self.fourkeyText = genLabelText("[4]: Sword", 5)
  47. # Set up key input
  48. self.accept('escape', sys.exit)
  49. self.accept('1', self.switchObject, [0])
  50. self.accept('2', self.switchObject, [1])
  51. self.accept('3', self.switchObject, [2])
  52. self.accept('4', self.switchObject, [3])
  53. base.disableMouse() # Disable mouse-based camera-control
  54. camera.setPos(0, -15, 2) # Position the camera
  55. self.eve = Actor("models/eve", # Load our animated charachter
  56. {'walk': "models/eve_walk"})
  57. self.eve.reparentTo(render) # Put it in the scene
  58. # Now we use controlJoint to get a NodePath that's in control of her neck
  59. # This must be done before any animations are played
  60. self.eveNeck = self.eve.controlJoint(None, 'modelRoot', 'Neck')
  61. # We now play an animation. An animation must be played, or at least posed
  62. # for the nodepath we just got from controlJoint to actually effect the
  63. # model
  64. self.eve.actorInterval("walk", playRate=2).loop()
  65. # Now we add a task that will take care of turning the head
  66. taskMgr.add(self.turnHead, "turnHead")
  67. # Now we will expose the joint the hand joint. ExposeJoint allows us to
  68. # get the position of a joint while it is animating. This is different than
  69. # controlJonit which stops that joint from animating but lets us move it.
  70. # This is particularly usefull for putting an object (like a weapon) in an
  71. # actor's hand
  72. self.rightHand = self.eve.exposeJoint(None, 'modelRoot', 'RightHand')
  73. # This is a table with models, positions, rotations, and scales of objects to
  74. # be attached to our exposed joint. These are stock models and so they needed
  75. # to be repositioned to look right.
  76. positions = [("teapot", (0, -.66, -.95), (90, 0, 90), .4),
  77. ("models/candycane", (.15, -.99, -.22), (90, 0, 90), 1),
  78. ("models/banana", (.08, -.1, .09), (0, -90, 0), 1.75),
  79. ("models/sword", (.11, .19, .06), (0, 0, 90), 1)]
  80. self.models = [] # A list that will store our models objects
  81. for row in positions:
  82. np = loader.loadModel(row[0]) # Load the model
  83. np.setPos(row[1][0], row[1][1], row[1][2]) # Position it
  84. np.setHpr(row[2][0], row[2][1], row[2][2]) # Rotate it
  85. np.setScale(row[3]) # Scale it
  86. # Reparent the model to the exposed joint. That way when the joint moves,
  87. # the model we just loaded will move with it.
  88. np.reparentTo(self.rightHand)
  89. self.models.append(np) # Add it to our models list
  90. self.switchObject(0) # Make object 0 the first shown
  91. self.setupLights() # Put in some default lighting
  92. # This is what we use to change which object it being held. It just hides all of
  93. # the objects and then unhides the one that was selected
  94. def switchObject(self, i):
  95. for np in self.models:
  96. np.hide()
  97. self.models[i].show()
  98. # This task gets the position of mouse each frame, and rotates the neck based
  99. # on it.
  100. def turnHead(self, task):
  101. # Check to make sure the mouse is readable
  102. if base.mouseWatcherNode.hasMouse():
  103. # get the mouse position as a LVector2. The values for each axis are from -1 to
  104. # 1. The top-left is (-1,-1), the bottom right is (1,1)
  105. mpos = base.mouseWatcherNode.getMouse()
  106. # Here we multiply the values to get the amount of degrees to turn
  107. # Restrain is used to make sure the values returned by getMouse are in the
  108. # valid range. If this particular model were to turn more than this,
  109. # significant tearing would be visable
  110. self.eveNeck.setP(clamp(mpos.getX()) * 50)
  111. self.eveNeck.setH(clamp(mpos.getY()) * 20)
  112. return Task.cont # Task continues infinitely
  113. def setupLights(self): # Sets up some default lighting
  114. ambientLight = AmbientLight("ambientLight")
  115. ambientLight.setColor((.4, .4, .35, 1))
  116. directionalLight = DirectionalLight("directionalLight")
  117. directionalLight.setDirection(LVector3(0, 8, -2.5))
  118. directionalLight.setColor((0.9, 0.8, 0.9, 1))
  119. render.setLight(render.attachNewNode(directionalLight))
  120. render.setLight(render.attachNewNode(ambientLight))
  121. demo = LookingGrippingDemo() # Create an instance of our class
  122. demo.run() # Run the simulation