ShadowDemo.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. """Create a cheesy shadow effect by rendering the view of an
  2. object (e.g. the local avatar) from a special camera as seen from
  3. above (as if from the sun), using a solid gray foreground and a
  4. solid white background, and then multitexturing that view onto the
  5. world.
  6. This is meant primarily as a demonstration of multipass and
  7. multitexture rendering techniques. It's not a particularly great
  8. way to do shadows.
  9. """
  10. from pandac.PandaModules import *
  11. from direct.task import Task
  12. sc = None
  13. class ShadowCaster:
  14. texXSize = 128
  15. texYSize = 128
  16. def __init__(self, lightPath, objectPath, filmX, filmY):
  17. self.lightPath = lightPath
  18. self.objectPath = objectPath
  19. self.groundPath = None
  20. # Create an offscreen buffer to render the view of the avatar
  21. # into a texture.
  22. self.buffer = base.win.makeTextureBuffer(
  23. 'shadowBuffer', self.texXSize, self.texYSize)
  24. # The background of this buffer--and the border of the
  25. # texture--is pure white.
  26. clearColor = VBase4(1, 1, 1, 1)
  27. self.buffer.setClearColor(clearColor)
  28. self.tex = self.buffer.getTexture()
  29. self.tex.setBorderColor(clearColor)
  30. self.tex.setWrapU(Texture.WMBorderColor)
  31. self.tex.setWrapV(Texture.WMBorderColor)
  32. # Set up a display region on this buffer, and create a camera.
  33. dr = self.buffer.makeDisplayRegion()
  34. self.camera = Camera('shadowCamera')
  35. self.cameraPath = self.lightPath.attachNewNode(self.camera)
  36. self.camera.setScene(self.objectPath)
  37. dr.setCamera(self.cameraPath)
  38. # Use a temporary NodePath to define the initial state for the
  39. # camera. The initial state will render everything in a
  40. # flat-shaded gray, as if it were a shadow.
  41. initial = NodePath('initial')
  42. initial.setColor(0.6, 0.6, 0.6, 1, 1)
  43. initial.setTextureOff(2)
  44. initial.setLightOff(2)
  45. self.camera.setInitialState(initial.getState())
  46. # Use an orthographic lens for this camera instead of the
  47. # usual perspective lens. An orthographic lens is better to
  48. # simulate sunlight, which is (almost) orthographic. We set
  49. # the film size large enough to render a typical avatar (but
  50. # not so large that we lose detail in the texture).
  51. self.lens = OrthographicLens()
  52. self.lens.setFilmSize(filmX, filmY)
  53. self.camera.setLens(self.lens)
  54. # Finally, we'll need a unique TextureStage to apply this
  55. # shadow texture to the world.
  56. self.stage = TextureStage('shadow')
  57. # Make sure the shadowing object doesn't get its own shadow
  58. # applied to it.
  59. self.objectPath.setTextureOff(self.stage)
  60. def setGround(self, groundPath):
  61. """ Specifies the part of the world that is to be considered
  62. the ground: this is the part onto which the rendered texture
  63. will be applied. """
  64. if self.groundPath:
  65. self.groundPath.clearProjectTexture(self.stage)
  66. self.groundPath = groundPath
  67. self.groundPath.projectTexture(self.stage, self.tex, self.cameraPath)
  68. def clear(self):
  69. """ Undoes the effect of the ShadowCaster. """
  70. if self.groundPath:
  71. self.groundPath.clearProjectTexture(self.stage)
  72. self.groundPath = None
  73. if self.lightPath:
  74. self.lightPath.detachNode()
  75. self.lightPath = None
  76. if self.cameraPath:
  77. self.cameraPath.detachNode()
  78. self.cameraPath = None
  79. self.camera = None
  80. self.lens = None
  81. if self.buffer:
  82. base.graphicsEngine.removeWindow(self.buffer)
  83. self.tex = None
  84. self.buffer = None
  85. def avatarShadow():
  86. # Turn off the existing drop shadow.
  87. # stash it so that when the game hides and shows it, it will still be gone
  88. base.localAvatar.dropShadow.stash()
  89. # Set up a new node to hold the "light": this is an abitrary point
  90. # somewhere above the avatar, looking down, as if from the sun.
  91. objectPath = base.localAvatar.getGeomNode()
  92. shadowCamera = objectPath.attachNewNode('shadowCamera')
  93. lightPath = shadowCamera.attachNewNode('lightPath')
  94. # We can change this position at will to change the angle of the
  95. # sun.
  96. lightPath.setPos(5, 0, 7)
  97. # We need a task to keep the shadowCamera rotated in the same
  98. # direction relative to render (otherwise, the shadow seems to
  99. # rotate when you rotate your avatar, which is strange). We can't
  100. # just use a compass effect, since that doesn't work on cameras.
  101. def shadowCameraRotate(task, shadowCamera = shadowCamera):
  102. shadowCamera.setHpr(render, 0, 0, 0)
  103. lightPath.lookAt(shadowCamera, 0, 0, 3)
  104. return Task.cont
  105. taskMgr.remove('shadowCamera')
  106. taskMgr.add(shadowCameraRotate, 'shadowCamera')
  107. global sc
  108. if sc != None:
  109. sc.clear()
  110. sc = ShadowCaster(lightPath, objectPath, 4, 6)
  111. # Naively, just apply the shadow to everything in the world. It
  112. # would probably be better to use a little restraint.
  113. sc.setGround(render)
  114. return sc
  115. def piratesAvatarShadow():
  116. a = avatarShadow()
  117. # Force the lod to be 0 at all times
  118. base.localAvatar.getGeomNode().getChild(1).node().forceSwitch(0)
  119. return a
  120. def arbitraryShadow(node):
  121. # Turn off the existing drop shadow, if any
  122. if hasattr(node, "dropShadow"):
  123. # stash it so that when the game hides and shows it, it will still be gone
  124. base.localAvatar.dropShadow.stash()
  125. # Set up a new node to hold the "light": this is an abitrary point
  126. # somewhere above the node, looking down, as if from the sun.
  127. objectPath = node
  128. shadowCamera = objectPath.attachNewNode('shadowCamera')
  129. lightPath = shadowCamera.attachNewNode('lightPath')
  130. # We can change this position at will to change the angle of the
  131. # sun.
  132. lightPath.setPos(50, 0, 50)
  133. # We need a task to keep the shadowCamera rotated in the same
  134. # direction relative to render (otherwise, the shadow seems to
  135. # rotate when you rotate your avatar, which is strange). We can't
  136. # just use a compass effect, since that doesn't work on cameras.
  137. def shadowCameraRotate(task, shadowCamera = shadowCamera):
  138. shadowCamera.setHpr(render, 0, 0, 0)
  139. lightPath.lookAt(shadowCamera, 0, 0, 3)
  140. return Task.cont
  141. taskMgr.remove('shadowCamera')
  142. taskMgr.add(shadowCameraRotate, 'shadowCamera')
  143. global sc
  144. if sc != None:
  145. sc.clear()
  146. sc = ShadowCaster(lightPath, objectPath, 100, 100)
  147. # Naively, just apply the shadow to everything in the world. It
  148. # would probably be better to use a little restraint.
  149. sc.setGround(render)
  150. return sc
  151. def testShadow():
  152. ## a = piratesAvatarShadow()
  153. """
  154. from direct.showbase.ShadowDemo import *
  155. from direct.interval.IntervalGlobal import *
  156. b = loader.loadModel('/i/beta/PotC/Maya/Pirates/scenes/models/sets/buildings/spanish_buildings/TavernIntExt/tavern_ext/bar.egg')
  157. bs = arbitraryShadow(b)
  158. s = loader.loadModel('smiley')
  159. s.reparentTo(bs.lightPath)
  160. b.reparentTo((base.localAvatar))
  161. a = AmbientLight('cloudAmbientHi')
  162. a.setColor(Vec4(0.9, 0.9, 0.9, 1.000))
  163. aNP = s.attachNewNode(a.upcastToPandaNode())
  164. b.setLight(aNP)
  165. d = DirectionalLight("chernabogDirectionalLight")
  166. d.setDirection(Vec3(0,1,0))
  167. d.setColor(Vec4(1))
  168. #d.setColor(Vec4(0.9, 0.7, 0.7, 1.000))
  169. dNP = s.attachNewNode(d.upcastToPandaNode())
  170. b.setLight(dNP)
  171. ival = Sequence(LerpPosInterval(bs.lightPath, 0.0, Vec3(-200,0,50)),
  172. LerpPosInterval(bs.lightPath, 10.0, Vec3(-200,0,200)),
  173. LerpPosInterval(bs.lightPath, 10.0, Vec3(200,0,200)),
  174. LerpPosInterval(bs.lightPath, 10.0, Vec3(200,0,50)),
  175. )
  176. ival.loop()
  177. """