MirrorDemo.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. """This file demonstrates one way to create a mirror effect in Panda.
  2. Call setupMirror() to create a mirror in the world that reflects
  3. everything in front of it.
  4. The approach taken here is to create an offscreen buffer with its own
  5. camera that renders its view into a texture, which is then applied to
  6. the mirror geometry. The mirror's camera is repositioned each frame
  7. with a task to keep it always on the opposite side of the mirror from
  8. the main camera.
  9. This demonstrates the basic interface for offscreen
  10. render-to-a-texture in Panda. Similar approaches can be used for
  11. related effects, such as a remote spy camera presenting its view onto
  12. a closed-circuit television screen.
  13. In this example the mirror itself is always perfectly flat--it's just
  14. a single polygon, after all--but small distortions of the mirror
  15. surface are possible, like a funhouse mirror. However, the reflection
  16. itself is always basically planar; for more accurate convex
  17. reflections, you will need to use a sphere map or a cube map."""
  18. __all__ = ['setupMirror', 'showFrustum']
  19. from panda3d.core import *
  20. from direct.task import Task
  21. def setupMirror(name, width, height, rootCamera = None,
  22. bufferSize = 256, clearColor = None):
  23. # The return value is a NodePath that contains a rectangle that
  24. # reflects render. You can reparent, reposition, and rotate it
  25. # anywhere you like.
  26. if rootCamera is None:
  27. rootCamera = base.camera
  28. root = render.attachNewNode(name)
  29. # Create a polygon to be the visible representation of the mirror.
  30. cm = CardMaker('mirror')
  31. cm.setFrame(width / 2.0, -width / 2.0, -height / 2.0, height / 2.0)
  32. cm.setHasUvs(1)
  33. card = root.attachNewNode(cm.generate())
  34. # Create a PlaneNode to represent the mirror's position, for
  35. # computing where the mirror's camera belongs each frame.
  36. plane = Plane(Vec3(0, 1, 0), Point3(0, 0, 0))
  37. planeNode = PlaneNode('mirrorPlane')
  38. planeNode.setPlane(plane)
  39. planeNP = root.attachNewNode(planeNode)
  40. # Now create an offscreen buffer for rendering the mirror's point
  41. # of view. The parameters here control the resolution of the
  42. # texture.
  43. buffer = base.win.makeTextureBuffer(name, bufferSize, bufferSize)
  44. if clearColor is None:
  45. buffer.setClearColor(base.win.getClearColor())
  46. #buffer.setClearColor(VBase4(0, 0, 1, 1))
  47. else:
  48. buffer.setClearColor(clearColor)
  49. # Set up a display region on this buffer, and create a camera.
  50. dr = buffer.makeDisplayRegion()
  51. camera = Camera('mirrorCamera')
  52. lens = PerspectiveLens()
  53. lens.setFilmSize(width, height)
  54. camera.setLens(lens)
  55. cameraNP = planeNP.attachNewNode(camera)
  56. dr.setCamera(cameraNP)
  57. # Since the reflection matrix will reverse the vertex-winding
  58. # order of all the polygons in the world, we have to tell the
  59. # camera to reverse the direction of its face culling. We also
  60. # tell it not to draw (that is, to clip) anything behind the
  61. # mirror plane.
  62. dummy = NodePath('dummy')
  63. dummy.setAttrib(CullFaceAttrib.makeReverse())
  64. dummy.setClipPlane(planeNP)
  65. camera.setInitialState(dummy.getState())
  66. # Create a visible representation of the camera so we can see it.
  67. #cameraVis = loader.loadModel('camera.egg')
  68. #if not cameraVis.isEmpty():
  69. # cameraVis.reparentTo(cameraNP)
  70. # Spawn a task to keep that camera on the opposite side of the
  71. # mirror.
  72. def moveCamera(task, cameraNP = cameraNP, plane = plane,
  73. planeNP = planeNP, card = card, lens = lens,
  74. width = width, height = height, rootCamera = rootCamera):
  75. # Set the camera to the mirror-image position of the main camera.
  76. cameraNP.setMat(rootCamera.getMat(planeNP) * plane.getReflectionMat())
  77. # Set the cameras roll to the roll of the mirror. Otherwise
  78. # mirrored objects will be moved unexpectedly
  79. cameraNP.setR(planeNP.getR()-180)
  80. # And reset the frustum to exactly frame the mirror's corners.
  81. # This is a minor detail, but it helps to provide a realistic
  82. # reflection and keep the subject centered.
  83. ul = cameraNP.getRelativePoint(card, Point3(-width / 2.0, 0, height / 2.0))
  84. ur = cameraNP.getRelativePoint(card, Point3(width / 2.0, 0, height / 2.0))
  85. ll = cameraNP.getRelativePoint(card, Point3(-width / 2.0, 0, -height / 2.0))
  86. lr = cameraNP.getRelativePoint(card, Point3(width / 2.0, 0, -height / 2.0))
  87. # get the distance from the mirrors camera to the mirror plane
  88. camvec = planeNP.getPos() - cameraNP.getPos()
  89. camdist = camvec.length()
  90. # set the discance on the mirrors corners so it will keep correct
  91. # sizes of the mirrored objects
  92. ul.setY(camdist)
  93. ur.setY(camdist)
  94. ll.setY(camdist)
  95. lr.setY(camdist)
  96. lens.setFrustumFromCorners(ul, ur, ll, lr, Lens.FCCameraPlane | Lens.FCOffAxis | Lens.FCAspectRatio)
  97. return Task.cont
  98. # Add it with a fairly high priority to make it happen late in the
  99. # frame, after the avatar controls (or whatever) have been applied
  100. # but before we render.
  101. taskMgr.add(moveCamera, name, priority = 40)
  102. # Now apply the output of this camera as a texture on the mirror's
  103. # visible representation.
  104. card.setTexture(buffer.getTexture())
  105. return root
  106. def showFrustum(np):
  107. # Utility function to reveal the frustum for a particular camera.
  108. cameraNP = np.find('**/+Camera')
  109. camera = cameraNP.node()
  110. lens = camera.getLens()
  111. geomNode = GeomNode('frustum')
  112. geomNode.addGeom(lens.makeGeometry())
  113. cameraNP.attachNewNode(geomNode)
  114. if __name__ == "__main__":
  115. from direct.showbase.ShowBase import ShowBase
  116. base = ShowBase()
  117. panda = loader.loadModel("panda")
  118. panda.setH(180)
  119. panda.setPos(0, 10, -2.5)
  120. panda.setScale(0.5)
  121. panda.reparentTo(render)
  122. myMirror = setupMirror("mirror", 10, 10, bufferSize=1024, clearColor=(0, 0, 1, 1))
  123. myMirror.setPos(0, 15, 2.5)
  124. myMirror.setH(180)
  125. # Uncomment this to show the frustum of the camera in the mirror
  126. #showFrustum(render)
  127. base.run()