I’ve noticed quite a few people posting questions about creating skyboxes (sky boxes, skycubes, sky cubes, skydomes, sky domes, there, all the tags I can think of. XD) for use in Panda3D. I was looking around for ways to do that myself, and I decided to come up with my own solution. Then I figured I should share that solution with all of you.
The purpose of this script is to create a sphere and use a 3D cube map to take a 6 image skybox and wrap it seamlessly around the sphere, to create a perfect all around backdrop.
My intention when making this script was to use it in combination with the Spacescape program created in Ogre3D by Alex Peterson. It’s a phenomenal program for making beautiful outer space skyboxes and I highly recommend it. Here’s a link to the Spacescape page on his blog: http://alexcpeterson.com/spacescape or you can look up Spacescape on Sourceforge. Note that if you do use Spacescape, you’ll need to renumber the images it creates from 1-6 to 0-5. I also had to swap images 2 and 3 (after they were renumbered, to be even more explicit I am referring to the images called top and bottom by Spacescape) by changing their names.
This script will load an InvertedSphere egg file, generate the 3D texture coordinates, apply the cube map, and then save out a bam file. All you need to do in your game, then, is load the bam file, set the bin and depth sorting, and create a task so that repositions the sphere to the camera’s position each frame. The last piece of code in this post will explain how to do that stuff as well.
Here is the script:
import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import Texture, TextureStage, DirectionalLight, AmbientLight, TexGenAttrib, VBase4
class SkySphere(DirectObject):
def __init__(self):
self.sphere = loader.loadModel("InvertedSphere.egg")
# Load a sphere with a radius of 1 unit and the faces directed inward.
self.sphere.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldPosition)
self.sphere.setTexProjector(TextureStage.getDefault(), render, self.sphere)
self.sphere.setTexPos(TextureStage.getDefault(), 0, 0, 0)
self.sphere.setTexScale(TextureStage.getDefault(), .5)
# Create some 3D texture coordinates on the sphere. For more info on this, check the Panda3D manual.
tex = loader.loadCubeMap("BlueGreenNebula_#.png")
self.sphere.setTexture(tex)
# Load the cube map and apply it to the sphere.
self.sphere.setLightOff()
# Tell the sphere to ignore the lighting.
self.sphere.setScale(1000)
# Increase the scale of the sphere so it will be larger than the scene.
self.sphere.reparentTo(render)
# Reparent the sphere to render so you can see it.
result = self.sphere.writeBamFile("SkySphere.bam")
# Save out the bam file.
print(result)
# Print out whether the saving succeeded or not.
SS = SkySphere()
run()
Here is the code you need to use the skysphere in your game:
self.skysphere = loader.loadModel("Tracks/Morgenstern/SkySphere.bam")
self.skysphere.setBin('background', 1)
self.skysphere.setDepthWrite(0)
self.skysphere.reparentTo(render)
taskMgr.add(self.skysphereTask, "SkySphere Task")
def skysphereTask(self, task):
self.skysphere.setPos(base.camera, 0, 0, 0)
return task.cont
Edit: One more note. You should make sure to run this script in the folder where the SkySphere.bam will be loaded from, and have your images for the cubemap be wherever they are going to be in relation to the bam file. When it saves out the bam, it will be written with relative path names, and you want those path names to be correct.