occluder_culling.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. #!/usr/bin/env python
  2. """
  3. Author: Josh Enes
  4. Last Updated: 2015-03-13
  5. This is a demo of Panda's occluder-culling system. It demonstrates loading
  6. occluder from an EGG file and adding them to a CullTraverser.
  7. """
  8. # Load PRC data
  9. from panda3d.core import loadPrcFileData
  10. loadPrcFileData('', 'window-title Occluder Demo')
  11. loadPrcFileData('', 'sync-video false')
  12. loadPrcFileData('', 'show-frame-rate-meter true')
  13. loadPrcFileData('', 'texture-minfilter linear-mipmap-linear')
  14. #loadPrcFileData('', 'fake-view-frustum-cull true') # show culled nodes in red
  15. # Import needed modules
  16. import random
  17. from direct.showbase.ShowBase import ShowBase
  18. from direct.gui.OnscreenText import OnscreenText
  19. from panda3d.core import PerspectiveLens, TextNode, \
  20. TexGenAttrib, TextureStage, TransparencyAttrib, LPoint3, Texture
  21. def add_instructions(pos, msg):
  22. """Function to put instructions on the screen."""
  23. return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1),
  24. parent=base.a2dTopLeft, align=TextNode.ALeft,
  25. pos=(0.08, -pos - 0.04), scale=.05)
  26. def add_title(text):
  27. """Function to put title on the screen."""
  28. return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08,
  29. parent=base.a2dBottomRight, align=TextNode.ARight,
  30. fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1))
  31. class Game(ShowBase):
  32. """Sets up the game, camera, controls, and loads models."""
  33. def __init__(self):
  34. ShowBase.__init__(self)
  35. self.xray_mode = False
  36. self.show_model_bounds = False
  37. # Display instructions
  38. add_title("Panda3D Tutorial: Occluder Culling")
  39. add_instructions(0.06, "[Esc]: Quit")
  40. add_instructions(0.12, "[W]: Move Forward")
  41. add_instructions(0.18, "[A]: Move Left")
  42. add_instructions(0.24, "[S]: Move Right")
  43. add_instructions(0.30, "[D]: Move Back")
  44. add_instructions(0.36, "Arrow Keys: Look Around")
  45. add_instructions(0.42, "[F]: Toggle Wireframe")
  46. add_instructions(0.48, "[X]: Toggle X-Ray Mode")
  47. add_instructions(0.54, "[B]: Toggle Bounding Volumes")
  48. # Setup controls
  49. self.keys = {}
  50. for key in ['arrow_left', 'arrow_right', 'arrow_up', 'arrow_down',
  51. 'a', 'd', 'w', 's']:
  52. self.keys[key] = 0
  53. self.accept(key, self.push_key, [key, 1])
  54. self.accept('shift-%s' % key, self.push_key, [key, 1])
  55. self.accept('%s-up' % key, self.push_key, [key, 0])
  56. self.accept('f', self.toggleWireframe)
  57. self.accept('x', self.toggle_xray_mode)
  58. self.accept('b', self.toggle_model_bounds)
  59. self.accept('escape', __import__('sys').exit, [0])
  60. self.disableMouse()
  61. # Setup camera
  62. self.lens = PerspectiveLens()
  63. self.lens.setFov(60)
  64. self.lens.setNear(0.01)
  65. self.lens.setFar(1000.0)
  66. self.cam.node().setLens(self.lens)
  67. self.camera.setPos(-9, -0.5, 1)
  68. self.heading = -95.0
  69. self.pitch = 0.0
  70. # Load level geometry
  71. self.level_model = self.loader.loadModel('models/level')
  72. self.level_model.reparentTo(self.render)
  73. self.level_model.setTexGen(TextureStage.getDefault(),
  74. TexGenAttrib.MWorldPosition)
  75. self.level_model.setTexProjector(TextureStage.getDefault(),
  76. self.render, self.level_model)
  77. self.level_model.setTexScale(TextureStage.getDefault(), 4)
  78. tex = self.loader.load3DTexture('models/tex_#.png')
  79. self.level_model.setTexture(tex)
  80. # Load occluders
  81. occluder_model = self.loader.loadModel('models/occluders')
  82. occluder_nodepaths = occluder_model.findAllMatches('**/+OccluderNode')
  83. for occluder_nodepath in occluder_nodepaths:
  84. self.render.setOccluder(occluder_nodepath)
  85. occluder_nodepath.node().setDoubleSided(True)
  86. # Randomly spawn some models to test the occluders
  87. self.models = []
  88. box_model = self.loader.loadModel('box')
  89. for dummy in range(0, 500):
  90. pos = LPoint3((random.random() - 0.5) * 9,
  91. (random.random() - 0.5) * 9,
  92. random.random() * 8)
  93. box = box_model.copy_to(self.render)
  94. box.setScale(random.random() * 0.2 + 0.1)
  95. box.setPos(pos)
  96. box.setHpr(random.random() * 360,
  97. random.random() * 360,
  98. random.random() * 360)
  99. box.reparentTo(self.render)
  100. self.models.append(box)
  101. self.taskMgr.add(self.update, 'main loop')
  102. def push_key(self, key, value):
  103. """Stores a value associated with a key."""
  104. self.keys[key] = value
  105. def update(self, task):
  106. """Updates the camera based on the keyboard input."""
  107. delta = base.clock.dt
  108. move_x = delta * 3 * -self.keys['a'] + delta * 3 * self.keys['d']
  109. move_z = delta * 3 * self.keys['s'] + delta * 3 * -self.keys['w']
  110. self.camera.setPos(self.camera, move_x, -move_z, 0)
  111. self.heading += (delta * 90 * self.keys['arrow_left'] +
  112. delta * 90 * -self.keys['arrow_right'])
  113. self.pitch += (delta * 90 * self.keys['arrow_up'] +
  114. delta * 90 * -self.keys['arrow_down'])
  115. self.camera.setHpr(self.heading, self.pitch, 0)
  116. return task.cont
  117. def toggle_xray_mode(self):
  118. """Toggle X-ray mode on and off. This is useful for seeing the
  119. effectiveness of the occluder culling."""
  120. self.xray_mode = not self.xray_mode
  121. if self.xray_mode:
  122. self.level_model.setColorScale((1, 1, 1, 0.5))
  123. self.level_model.setTransparency(TransparencyAttrib.MDual)
  124. else:
  125. self.level_model.setColorScaleOff()
  126. self.level_model.setTransparency(TransparencyAttrib.MNone)
  127. def toggle_model_bounds(self):
  128. """Toggle bounding volumes on and off on the models."""
  129. self.show_model_bounds = not self.show_model_bounds
  130. if self.show_model_bounds:
  131. for model in self.models:
  132. model.showBounds()
  133. else:
  134. for model in self.models:
  135. model.hideBounds()
  136. game = Game()
  137. game.run()