Browse Source

*** empty log message ***

David Rose 21 years ago
parent
commit
b639794b19
1 changed files with 142 additions and 0 deletions
  1. 142 0
      direct/src/showbase/ShadowDemo.py

+ 142 - 0
direct/src/showbase/ShadowDemo.py

@@ -0,0 +1,142 @@
+
+"""Create a cheesy shadow effect by rendering the view of an
+object (e.g. the local avatar) from a special camera as seen from
+above (as if from the sun), using a solid gray foreground and a
+solid white background, and then multitexturing that view onto the
+world.
+
+This is meant primarily as a demonstration of multipass and
+multitexture rendering techniques.  It's not a particularly great
+way to do shadows.
+"""
+    
+from pandac.PandaModules import *
+from direct.task import Task
+
+sc = None
+
+class ShadowCaster:
+    texXSize = 256
+    texYSize = 256
+    
+    def __init__(self, lightPath, objectPath):
+        self.lightPath = lightPath
+        self.objectPath = objectPath
+        self.groundPath = None
+
+        # Create an offscreen buffer to render the view of the avatar
+        # into a texture.
+        self.buffer = base.win.makeTextureBuffer(
+            'shadowBuffer', self.texXSize, self.texYSize)
+
+        # The background of this buffer--and the border of the
+        # texture--is pure white.
+        clearColor = VBase4(1, 1, 1, 1)
+        self.buffer.setClearColor(clearColor)
+        self.tex = self.buffer.getTexture()
+        self.tex.setBorderColor(clearColor)
+        self.tex.setWrapu(Texture.WMBorderColor)
+        self.tex.setWrapv(Texture.WMBorderColor)
+
+        # Set up a display region on this buffer, and create a camera.
+        layer = self.buffer.getChannel(0).makeLayer()
+        dr = layer.makeDisplayRegion()
+        self.camera = Camera('shadowCamera')
+        self.cameraPath = self.lightPath.attachNewNode(self.camera)
+        self.camera.setScene(self.objectPath)
+        dr.setCamera(self.cameraPath)
+
+        # Use a temporary NodePath to define the initial state for the
+        # camera.  The initial state will render everything in a
+        # flat-shaded gray, as if it were a shadow.
+        initial = NodePath('initial')
+        initial.setColor(0.5, 0.5, 0.5, 1, 1)
+        initial.setTextureOff(2)
+        self.camera.setInitialState(initial.getState())
+
+        # Use an orthographic lens for this camera instead of the
+        # usual perspective lens.  An orthographic lens is better to
+        # simulate sunlight, which is (almost) orthographic.  We set
+        # the film size large enough to render a typical avatar (but
+        # not so large that we lose detail in the texture).
+        self.lens = OrthographicLens()
+        self.lens.setFilmSize(4, 6)
+        self.camera.setLens(self.lens)
+
+        # Finally, we'll need a unique TextureStage to apply this
+        # shadow texture to the world.
+        self.stage = TextureStage('shadow')
+
+        # Make sure the shadowing object doesn't get its own shadow
+        # applied to it.
+        self.objectPath.setTextureOff(self.stage)
+
+    def setGround(self, groundPath):
+        """ Specifies the part of the world that is to be considered
+        the ground: this is the part onto which the rendered texture
+        will be applied. """
+        
+        if self.groundPath:
+            self.groundPath.clearProjectTexture(self.stage)
+
+        self.groundPath = groundPath
+        self.groundPath.projectTexture(self.stage, self.tex, self.cameraPath)
+
+    def clear(self):
+        """ Undoes the effect of the ShadowCaster. """
+        if self.groundPath:
+            self.groundPath.clearProjectTexture(self.stage)
+            self.groundPath = None
+
+        if self.lightPath:
+            self.lightPath.detachNode()
+            self.lightPath = None
+
+        if self.cameraPath:
+            self.cameraPath.detachNode()
+            self.cameraPath = None
+            self.camera = None
+            self.lens = None
+
+        if self.buffer:
+            base.graphicsEngine.removeWindow(self.buffer)
+            self.tex = None
+            self.buffer = None
+
+def avatarShadow():
+    # Turn off the existing drop shadow.
+    base.localAvatar.dropShadow.hide()
+
+    # Set up a new node to hold the "light": this is an abitrary point
+    # somewhere above the avatar, looking down, as if from the sun.
+    objectPath = base.localAvatar
+    shadowCamera = objectPath.attachNewNode('shadowCamera')
+    lightPath = shadowCamera.attachNewNode('lightPath')
+
+    # We can change this position at will to change the angle of the
+    # sun.
+    lightPath.setPos(5, 0, 7)
+
+    # We need a task to keep the shadowCamera rotated in the same
+    # direction relative to render (otherwise, the shadow seems to
+    # rotate when you rotate your avatar, which is strange).  We can't
+    # just use a compass effect, since that doesn't work on cameras.
+    def shadowCameraRotate(task, shadowCamera = shadowCamera):
+        shadowCamera.setHpr(render, 0, 0, 0)
+        lightPath.lookAt(shadowCamera, 0, 0, 3)
+        return Task.cont
+
+    taskMgr.remove('shadowCamera')
+    taskMgr.add(shadowCameraRotate, 'shadowCamera')    
+
+    global sc
+    if sc != None:
+        sc.clear()
+        
+    sc = ShadowCaster(lightPath, objectPath)
+
+    # Naively, just apply the shadow to everything in the world.  It
+    # would probably be better to use a little restraint.
+    sc.setGround(render)
+
+    return sc