main.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #!/usr/bin/env python
  2. # Author: Shao Zhang, Phil Saltzman, and Elan Ruskin
  3. # Last Updated: 2015-03-13
  4. #
  5. # This tutorial shows how to load, play, and manipulate sounds
  6. # and sound intervals in a panda project.
  7. from direct.showbase.ShowBase import ShowBase
  8. from panda3d.core import NodePath, TextNode
  9. from panda3d.core import PointLight, AmbientLight
  10. from direct.gui.OnscreenText import OnscreenText
  11. from direct.showbase.DirectObject import DirectObject
  12. from direct.interval.SoundInterval import SoundInterval
  13. from direct.gui.DirectSlider import DirectSlider
  14. from direct.gui.DirectButton import DirectButton
  15. from direct.interval.MetaInterval import Parallel
  16. from direct.interval.LerpInterval import LerpHprInterval
  17. import sys
  18. # Create an instance of ShowBase, which will open a window and set up a
  19. # scene graph and camera.
  20. base = ShowBase()
  21. class MusicBox(DirectObject):
  22. def __init__(self):
  23. # Our standard title and instructions text
  24. self.title = OnscreenText(text="Panda3D: Tutorial - Music Box",
  25. parent=base.a2dBottomCenter,
  26. pos=(0, 0.08), scale=0.08,
  27. fg=(1, 1, 1, 1), shadow=(0, 0, 0, .5))
  28. self.escapeText = OnscreenText(text="ESC: Quit", parent=base.a2dTopLeft,
  29. fg=(1, 1, 1, 1), pos=(0.06, -0.1),
  30. align=TextNode.ALeft, scale=.05)
  31. # Set up the key input
  32. self.accept('escape', sys.exit)
  33. # Fix the camera position
  34. base.disableMouse()
  35. # Loading sounds is done in a similar way to loading other things
  36. # Loading the main music box song
  37. self.musicBoxSound = loader.loadMusic('music/musicbox.ogg')
  38. self.musicBoxSound.setVolume(.5) # Volume is a percentage from 0 to 1
  39. # 0 means loop forever, 1 (default) means
  40. # play once. 2 or higher means play that many times
  41. self.musicBoxSound.setLoopCount(0)
  42. # Set up a simple light.
  43. self.plight = PointLight("light")
  44. self.plight.setColor((0.7, 0.7, 0.5, 1))
  45. light_path = base.render.attachNewNode(self.plight)
  46. light_path.setPos(0, 0, 20)
  47. base.render.setLight(light_path)
  48. alight = AmbientLight("ambient")
  49. alight.setColor((0.3, 0.3, 0.4, 1))
  50. base.render.setLight(base.render.attachNewNode(alight))
  51. # Enable per-pixel lighting
  52. base.render.setShaderAuto()
  53. # Sound objects do not have a pause function, just play and stop. So we will
  54. # Use this variable to keep track of where the sound is at when it was stoped
  55. # to impliment pausing
  56. self.musicTime = 0
  57. # Loading the open/close effect
  58. # loadSFX and loadMusic are identical. They are often used for organization
  59. #(loadMusic is used for background music, loadSfx is used for other effects)
  60. self.lidSfx = loader.loadSfx('music/openclose.ogg')
  61. # The open/close file has both effects in it. Fortunatly we can use intervals
  62. # to easily define parts of a sound file to play
  63. self.lidOpenSfx = SoundInterval(self.lidSfx, duration=2, startTime=0)
  64. self.lidCloseSfx = SoundInterval(self.lidSfx, startTime=5)
  65. # For this tutorial, it seemed appropriate to have on screen controls.
  66. # The following code creates them.
  67. # This is a label for a slider
  68. self.sliderText = OnscreenText("Volume", pos=(-0.1, 0.87), scale=.07,
  69. fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1))
  70. # The slider itself. It calls self.setMusicBoxVolume when changed
  71. self.slider = DirectSlider(pos=(-0.1, 0, .75), scale=0.8, value=.50,
  72. command=self.setMusicBoxVolume)
  73. # A button that calls self.toggleMusicBox when pressed
  74. self.button = DirectButton(pos=(.9, 0, .75), text="Open",
  75. scale=.1, pad=(.2, .2),
  76. rolloverSound=None, clickSound=None,
  77. command=self.toggleMusicBox)
  78. # A variable to represent the state of the simulation. It starts closed
  79. self.boxOpen = False
  80. # Here we load and set up the music box. It was modeled in a complex way, so
  81. # setting it up will be complicated
  82. self.musicBox = loader.loadModel('models/MusicBox')
  83. self.musicBox.setPos(0, 60, -9)
  84. self.musicBox.reparentTo(render)
  85. # Just like the scene graph contains hierarchies of nodes, so can
  86. # models. You can get the NodePath for the node using the find
  87. # function, and then you can animate the model by moving its parts
  88. # To see the hierarchy of a model, use, the ls function
  89. # self.musicBox.ls() prints out the entire hierarchy of the model
  90. # Finding pieces of the model
  91. self.Lid = self.musicBox.find('**/lid')
  92. self.Panda = self.musicBox.find('**/turningthing')
  93. # This model was made with the hinge in the wrong place
  94. # this is here so we have something to turn
  95. self.HingeNode = self.musicBox.find(
  96. '**/box').attachNewNode('nHingeNode')
  97. self.HingeNode.setPos(.8659, 6.5, 5.4)
  98. # WRT - ie with respect to. Reparents the object without changing
  99. # its position, size, or orientation
  100. self.Lid.wrtReparentTo(self.HingeNode)
  101. self.HingeNode.setHpr(0, 90, 0)
  102. # This sets up an interval to play the close sound and actually close the box
  103. # at the same time.
  104. self.lidClose = Parallel(
  105. self.lidCloseSfx,
  106. LerpHprInterval(self.HingeNode, 2.0, (0, 90, 0), blendType='easeInOut'))
  107. # Same thing for opening the box
  108. self.lidOpen = Parallel(
  109. self.lidOpenSfx,
  110. LerpHprInterval(self.HingeNode, 2.0, (0, 0, 0), blendType='easeInOut'))
  111. # The interval for turning the panda
  112. self.PandaTurn = self.Panda.hprInterval(7, (360, 0, 0))
  113. # Do a quick loop and pause to set it as a looping interval so it can be
  114. # started with resume and loop properly
  115. self.PandaTurn.loop()
  116. self.PandaTurn.pause()
  117. def setMusicBoxVolume(self):
  118. # Simply reads the current value from the slider and sets it in the
  119. # sound
  120. newVol = self.slider.guiItem.getValue()
  121. self.musicBoxSound.setVolume(newVol)
  122. def toggleMusicBox(self):
  123. #if self.lidOpen.isPlaying() or self.lidClose.isPlaying():
  124. # # It's currently already opening or closing
  125. # return
  126. if self.boxOpen:
  127. self.lidOpen.pause()
  128. self.lidClose.start() # Start the close box interval
  129. self.PandaTurn.pause() # Pause the figurine turning
  130. # Save the current time of the music
  131. self.musicTime = self.musicBoxSound.getTime()
  132. self.musicBoxSound.stop() # Stop the music
  133. self.button['text'] = "Open" # Prepare to change button label
  134. else:
  135. self.lidClose.pause()
  136. self.lidOpen.start() # Start the open box interval
  137. self.PandaTurn.resume() # Resume the figuring turning
  138. # Reset the time of the music so it starts where it left off
  139. self.musicBoxSound.setTime(self.musicTime)
  140. self.musicBoxSound.play() # Play the music
  141. self.button['text'] = "Close" # Prepare to change button label
  142. self.button.setText() # Actually change the button label
  143. # Set our state to opposite what it was
  144. self.boxOpen = not self.boxOpen
  145. #(closed to open or open to closed)
  146. # and we can run!
  147. mb = MusicBox()
  148. base.run()