| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- """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.
- """
- __all__ = ['ShadowCaster', 'avatarShadow', 'piratesAvatarShadow', 'arbitraryShadow']
- from panda3d.core import *
- from direct.task import Task
- sc = None
- class ShadowCaster:
- texXSize = 128
- texYSize = 128
- def __init__(self, lightPath, objectPath, filmX, filmY):
- 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.
- dr = self.buffer.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.6, 0.6, 0.6, 1, 1)
- initial.setTextureOff(2)
- initial.setLightOff(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(filmX, filmY)
- 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.
- # stash it so that when the game hides and shows it, it will still be gone
- base.localAvatar.dropShadow.stash()
- # 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.getGeomNode()
- 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, 4, 6)
- # 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
- def piratesAvatarShadow():
- a = avatarShadow()
- # Force the lod to be 0 at all times
- base.localAvatar.getGeomNode().getChild(0).node().forceSwitch(0)
- return a
- def arbitraryShadow(node):
- # Turn off the existing drop shadow, if any
- if hasattr(node, "dropShadow"):
- # stash it so that when the game hides and shows it, it will still be gone
- node.dropShadow.stash()
- # Set up a new node to hold the "light": this is an abitrary point
- # somewhere above the node, looking down, as if from the sun.
- objectPath = node
- shadowCamera = objectPath.attachNewNode('shadowCamera')
- lightPath = shadowCamera.attachNewNode('lightPath')
- # We can change this position at will to change the angle of the
- # sun.
- lightPath.setPos(50, 0, 50)
- # 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, 100, 100)
- # 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
- ##def testShadow():
- ## a = piratesAvatarShadow()
- ##
- ##from direct.showbase.ShadowDemo import *
- ##from direct.interval.IntervalGlobal import *
- ##b = loader.loadModel('/i/beta/PotC/Maya/Pirates/scenes/models/sets/buildings/spanish_buildings/TavernIntExt/tavern_ext/bar.egg')
- ##bs = arbitraryShadow(b)
- ##s = loader.loadModel('smiley')
- ##s.reparentTo(bs.lightPath)
- ##b.reparentTo((base.localAvatar))
- ##a = AmbientLight('cloudAmbientHi')
- ##a.setColor(Vec4(0.9, 0.9, 0.9, 1.000))
- ##aNP = s.attachNewNode(a.upcastToPandaNode())
- ##b.setLight(aNP)
- ##d = DirectionalLight("chernabogDirectionalLight")
- ##d.setDirection(Vec3(0, 1, 0))
- ##d.setColor(Vec4(1))
- ###d.setColor(Vec4(0.9, 0.7, 0.7, 1.000))
- ##dNP = s.attachNewNode(d.upcastToPandaNode())
- ##b.setLight(dNP)
- ##
- ##ival = Sequence(LerpPosInterval(bs.lightPath, 0.0, Vec3(-200, 0, 50)),
- ## LerpPosInterval(bs.lightPath, 10.0, Vec3(-200, 0, 200)),
- ## LerpPosInterval(bs.lightPath, 10.0, Vec3(200, 0, 200)),
- ## LerpPosInterval(bs.lightPath, 10.0, Vec3(200, 0, 50)),
- ##)
- ##ival.loop()
|