main.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #!/usr/bin/env python
  2. # Author: Shao Zhang and Phil Saltzman
  3. # Last Updated: 2015-03-13
  4. #
  5. # This tutorial will cover fog and how it can be used to make a finite length
  6. # tunnel seem endless by hiding its endpoint in darkness. Fog in panda works by
  7. # coloring objects based on their distance from the camera. Fog is not a 3D
  8. # volume object like real world fog.
  9. # With the right settings, Fog in panda can mimic the appearence of real world # fog.
  10. #
  11. # The tunnel and texture was originally created by Vamsi Bandaru and Victoria
  12. # Webb for the Entertainment Technology Center class Building Virtual Worlds
  13. from direct.showbase.ShowBase import ShowBase
  14. from panda3d.core import Fog
  15. from panda3d.core import TextNode
  16. from direct.gui.OnscreenText import OnscreenText
  17. from direct.showbase.DirectObject import DirectObject
  18. from direct.interval.MetaInterval import Sequence
  19. from direct.interval.LerpInterval import LerpFunc
  20. from direct.interval.FunctionInterval import Func
  21. import sys
  22. # Global variables for the tunnel dimensions and speed of travel
  23. TUNNEL_SEGMENT_LENGTH = 50
  24. TUNNEL_TIME = 2 # Amount of time for one segment to travel the
  25. # distance of TUNNEL_SEGMENT_LENGTH
  26. class FogDemo(ShowBase):
  27. # Macro-like function used to reduce the amount to code needed to create the
  28. # on screen instructions
  29. def genLabelText(self, i, text):
  30. return OnscreenText(text=text, parent=base.a2dTopLeft, scale=.05,
  31. pos=(0.06, -.065 * i), fg=(1, 1, 1, 1),
  32. align=TextNode.ALeft)
  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. # Standard initialization stuff
  38. # Standard title that's on screen in every tutorial
  39. self.title = OnscreenText(text="Panda3D: Tutorial - Fog", style=1,
  40. fg=(1, 1, 1, 1), shadow=(0, 0, 0, .5), parent=base.a2dBottomRight,
  41. align=TextNode.ARight, pos=(-0.1, 0.1), scale=.08)
  42. # Code to generate the on screen instructions
  43. self.escapeEventText = self.genLabelText(1, "ESC: Quit")
  44. self.pkeyEventText = self.genLabelText(2, "[P]: Pause")
  45. self.tkeyEventText = self.genLabelText(3, "[T]: Toggle Fog")
  46. self.dkeyEventText = self.genLabelText(4, "[D]: Make fog color black")
  47. self.sdkeyEventText = self.genLabelText(5, "[SHIFT+D]: Make background color black")
  48. self.rkeyEventText = self.genLabelText(6, "[R]: Make fog color red")
  49. self.srkeyEventText = self.genLabelText(7, "[SHIFT+R]: Make background color red")
  50. self.bkeyEventText = self.genLabelText(8, "[B]: Make fog color blue")
  51. self.sbkeyEventText = self.genLabelText(9, "[SHIFT+B]: Make background color blue")
  52. self.gkeyEventText = self.genLabelText(10, "[G]: Make fog color green")
  53. self.sgkeyEventText = self.genLabelText(11, "[SHIFT+G]: Make background color green")
  54. self.lkeyEventText = self.genLabelText(12, "[L]: Make fog color light grey")
  55. self.slkeyEventText = self.genLabelText(13, "[SHIFT+L]: Make background color light grey")
  56. self.pluskeyEventText = self.genLabelText(14, "[+]: Increase fog density")
  57. self.minuskeyEventText = self.genLabelText(15, "[-]: Decrease fog density")
  58. # disable mouse control so that we can place the camera
  59. base.disableMouse()
  60. camera.setPosHpr(0, 0, 10, 0, -90, 0)
  61. base.setBackgroundColor(0, 0, 0) # set the background color to black
  62. # World specific-code
  63. # Create an instance of fog called 'distanceFog'.
  64. #'distanceFog' is just a name for our fog, not a specific type of fog.
  65. self.fog = Fog('distanceFog')
  66. # Set the initial color of our fog to black.
  67. self.fog.setColor(0, 0, 0)
  68. # Set the density/falloff of the fog. The range is 0-1.
  69. # The higher the numer, the "bigger" the fog effect.
  70. self.fog.setExpDensity(.08)
  71. # We will set fog on render which means that everything in our scene will
  72. # be affected by fog. Alternatively, you could only set fog on a specific
  73. # object/node and only it and the nodes below it would be affected by
  74. # the fog.
  75. render.setFog(self.fog)
  76. # Define the keyboard input
  77. # Escape closes the demo
  78. self.accept('escape', sys.exit)
  79. # Handle pausing the tunnel
  80. self.accept('p', self.handlePause)
  81. # Handle turning the fog on and off
  82. self.accept('t', toggleFog, [render, self.fog])
  83. # Sets keys to set the fog to various colors
  84. self.accept('r', self.fog.setColor, [1, 0, 0])
  85. self.accept('g', self.fog.setColor, [0, 1, 0])
  86. self.accept('b', self.fog.setColor, [0, 0, 1])
  87. self.accept('l', self.fog.setColor, [.7, .7, .7])
  88. self.accept('d', self.fog.setColor, [0, 0, 0])
  89. # Sets keys to change the background colors
  90. self.accept('shift-r', base.setBackgroundColor, [1, 0, 0])
  91. self.accept('shift-g', base.setBackgroundColor, [0, 1, 0])
  92. self.accept('shift-b', base.setBackgroundColor, [0, 0, 1])
  93. self.accept('shift-l', base.setBackgroundColor, [.7, .7, .7])
  94. self.accept('shift-d', base.setBackgroundColor, [0, 0, 0])
  95. # Increases the fog density when "+" key is pressed
  96. self.accept('+', self.addFogDensity, [.01])
  97. # This is to handle the other "+" key (it's over = on the keyboard)
  98. self.accept('=', self.addFogDensity, [.01])
  99. self.accept('shift-=', self.addFogDensity, [.01])
  100. # Decreases the fog density when the "-" key is pressed
  101. self.accept('-', self.addFogDensity, [-.01])
  102. # Load the tunel and start the tunnel
  103. self.initTunnel()
  104. self.contTunnel()
  105. # This function will change the fog density by the amount passed into it
  106. # This function is needed so that it can look up the current value and
  107. # change it when the key is pressed. If you wanted to bind a key to set it
  108. # at a given value you could call self.fog.setExpDensity directly
  109. def addFogDensity(self, change):
  110. # The min() statement makes sure the density is never over 1
  111. # The max() statement makes sure the density is never below 0
  112. self.fog.setExpDensity(
  113. min(1, max(0, self.fog.getExpDensity() + change)))
  114. # Code to initialize the tunnel
  115. def initTunnel(self):
  116. self.tunnel = [None] * 4
  117. for x in range(4):
  118. # Load a copy of the tunnel
  119. self.tunnel[x] = loader.loadModel('models/tunnel')
  120. # The front segment needs to be attached to render
  121. if x == 0:
  122. self.tunnel[x].reparentTo(render)
  123. # The rest of the segments parent to the previous one, so that by moving
  124. # the front segement, the entire tunnel is moved
  125. else:
  126. self.tunnel[x].reparentTo(self.tunnel[x - 1])
  127. # We have to offset each segment by its length so that they stack onto
  128. # each other. Otherwise, they would all occupy the same space.
  129. self.tunnel[x].setPos(0, 0, -TUNNEL_SEGMENT_LENGTH)
  130. # Now we have a tunnel consisting of 4 repeating segments with a
  131. # hierarchy like this:
  132. # render<-tunnel[0]<-tunnel[1]<-tunnel[2]<-tunnel[3]
  133. # This function is called to snap the front of the tunnel to the back
  134. # to simulate traveling through it
  135. def contTunnel(self):
  136. # This line uses slices to take the front of the list and put it on the
  137. # back. For more information on slices check the Python manual
  138. self.tunnel = self.tunnel[1:] + self.tunnel[0:1]
  139. # Set the front segment (which was at TUNNEL_SEGMENT_LENGTH) to 0, which
  140. # is where the previous segment started
  141. self.tunnel[0].setZ(0)
  142. # Reparent the front to render to preserve the hierarchy outlined above
  143. self.tunnel[0].reparentTo(render)
  144. # Set the scale to be apropriate (since attributes like scale are
  145. # inherited, the rest of the segments have a scale of 1)
  146. self.tunnel[0].setScale(.155, .155, .305)
  147. # Set the new back to the values that the rest of teh segments have
  148. self.tunnel[3].reparentTo(self.tunnel[2])
  149. self.tunnel[3].setZ(-TUNNEL_SEGMENT_LENGTH)
  150. self.tunnel[3].setScale(1)
  151. # Set up the tunnel to move one segment and then call contTunnel again
  152. # to make the tunnel move infinitely
  153. self.tunnelMove = Sequence(
  154. LerpFunc(self.tunnel[0].setZ,
  155. duration=TUNNEL_TIME,
  156. fromData=0,
  157. toData=TUNNEL_SEGMENT_LENGTH * .305),
  158. Func(self.contTunnel)
  159. )
  160. self.tunnelMove.start()
  161. # This function calls toggle interval to pause or unpause the tunnel.
  162. # Like addFogDensity, toggleInterval could not be passed directly in the
  163. # accept command since the value of self.tunnelMove changes whenever
  164. #self.contTunnel is called
  165. def handlePause(self):
  166. toggleInterval(self.tunnelMove)
  167. # End Class World
  168. # This function will toggle any interval passed to it between playing and
  169. # paused
  170. def toggleInterval(interval):
  171. if interval.isPlaying():
  172. interval.pause()
  173. else:
  174. interval.resume()
  175. # This function will toggle fog on a node
  176. def toggleFog(node, fog):
  177. # If the fog attached to the node is equal to the one we passed in, then
  178. # fog is on and we should clear it
  179. if node.getFog() == fog:
  180. node.clearFog()
  181. # Otherwise fog is not set so we should set it
  182. else:
  183. node.setFog(fog)
  184. demo = FogDemo()
  185. demo.run()