File:Lens tutorial perspective.jpg

Don't mind the mess!

We're currently in the process of migrating the Panda3D Manual to a new service. This is a temporary layout in the meantime.

Top
Python C++
Lens_tutorial_perspective.jpg(400 × 300 pixels, file size: 13 KB, MIME type: image/jpeg)

This image, as well as the other two lens tutorial images, were generated with the following Panda code. Set your window-size to 800x600 or 1200x900 for best effect.

from direct.directbase.DirectStart import *
from direct.actor.Actor import Actor
from pandac.PandaModules import *

buf = None
bufcamera = None

# Define bitmasks to control what should be seen by each camera.
TopViewMask = BitMask32.bit(0)
PerspectiveViewMask = BitMask32.bit(1)
InternalViewMask = BitMask32.bit(2)

def setupStage():
    base.setBackgroundColor(1, 1, 1, 0)

    # A "stage" node to hold the stuff the lens is looking at.
    stage = render.attachNewNode('stage')
    stage.setPos(0, 35, -5)

    # A light to illuminate the items on stage.
    dlnp = base.camera.attachNewNode(DirectionalLight('dl').upcastToPandaNode())
    stage.setLight(dlnp)

    # Some geometry on the stage for the camera to look at.
    panda = Actor('panda.egg', { 'walk' : 'panda-walk.egg' })
    panda.reparentTo(stage)
    panda.setHpr(-20, 0, 0)
    panda.pose('walk', 0)

    # An offscreen buffer to render the stage for the film plane.
    global buf, bufcamera
    buf = base.win.makeTextureBuffer('panda', 256, 256)
    bufcamera = NodePath(Camera('bufcamera'))
    bufcamera.node().setCameraMask(InternalViewMask)
    buf.makeDisplayRegion().setCamera(bufcamera)


def setupLens(lens):
    # We create a camera "rig" node to hold the lens assembly.
    rig = render.attachNewNode('rig')

    # The entire rig assembly is invisible to the internal camera.
    rig.hide(InternalViewMask)

    # The lens is used to draw the black frustum, and also serves as
    # the lens for rendering the offscreen image that is drawn on the
    # film plane.
    
    lens.setFilmSize(20, 15)
    lens.setNearFar(10, 50)
    lensNP = rig.attachNewNode(GeomNode('lens'))
    lensNP.node().addGeom(lens.makeGeometry())

    # Make the frustum black.
    lensNP.setColor(0, 0, 0, 1)

    # And also make it have thick (2-pixel) lines.  This only works on
    # OpenGL.
    lensNP.setRenderMode(RenderModeAttrib.MUnchanged, 2)

    # Get the 3-d points corresponding to the diagonal corners of the
    # lens, on the near plane and on the far plane.
    nll = Point3()
    nur = Point3()
    fll = Point3()
    fur = Point3()
    lens.extrude(Point2(-1, -1), nll, fll)
    lens.extrude(Point2(1, 1), nur, fur)

    # Flip those around to the film plane.
    if lens.isOrthographic():
        pll = Point3(nll[0], nll[1] * -0.6, nll[2])
        pur = Point3(nur[0], nur[1] * -0.6, nur[2])
    else:
        pll = nll * -0.6
        pur = nur * -0.6

    # Now draw gray lines from the near plane to the film plane.
    ls = LineSegs()
    ls.setColor(0.8, 0.8, 0.8)
    ls.moveTo(nur)
    ls.drawTo(pur)
    ls.moveTo(nur[0], nur[1], nll[2])
    ls.drawTo(pur[0], pur[1], pll[2])
    ls.moveTo(nll)
    ls.drawTo(pll)
    ls.moveTo(nll[0], nll[1], nur[2])
    ls.drawTo(pll[0], pll[1], pur[2])
    ls.moveTo(pur)
    ls.drawTo(pur[0], pur[1], pll[2])
    ls.drawTo(pll)
    ls.drawTo(pll[0], pll[1], pur[2])
    ls.drawTo(pur)
    rig.attachNewNode(ls.create())

    # A node for stuff that is visible only to the topview camera.
    topViewStuff = rig.attachNewNode('topViewStuff')
    topViewStuff.hide()
    topViewStuff.show(TopViewMask)

    # Draw some more lines for the topview camera, in blue this time,
    # to measure things.
    ls = LineSegs()
    ls.setColor(0.4, 0.5, 1)
    
    tw = 0.5  # tick width for measurer ticks
    bl = 0.5  # baseline for labels

    # A TextNode to generate labels for each measurer.
    labels = TextNode('labels')
    labels.setTextColor(0.4, 0.5, 1, 1)
    labels.setAlign(TextNode.ACenter)
    labels.setTransform(Mat4.scaleMat(3))

    # The near distance measurer
    nm = 10
    ls.moveTo(nm - tw, nur[1], 0)
    ls.drawTo(nm + tw, nur[1], 0)
    ls.moveTo(nm, nur[1], 0)
    ls.drawTo(nm, 0, 0)
    ls.moveTo(nm - tw, 0, 0)
    ls.drawTo(nm + tw, 0, 0)
    labels.setText('C')
    topViewStuff.attachNewNode(labels.generate()).setPosHpr(nm - bl, nur[1] / 2, 0,
                                                            90, -90, 0)

    # The far distance measurer
    fm = 25
    ls.moveTo(fm - tw, fur[1], 0)
    ls.drawTo(fm + tw, fur[1], 0)
    ls.moveTo(fm, fur[1], 0)
    ls.drawTo(fm, 0, 0)
    ls.moveTo(fm - tw, 0, 0)
    ls.drawTo(fm + tw, 0, 0)
    labels.setText('D')
    topViewStuff.attachNewNode(labels.generate()).setPosHpr(fm - bl, fur[1] / 2, 0,
                                                            90, -90, 0)

    # The nodal point indicator
    np = 2.5
    ls.moveTo(0, 0, 0)
    ls.drawTo(np, 0, 0)
    labels.setText('A')
    topViewStuff.attachNewNode(labels.generate()).setPosHpr(np + 2.5, 0, 0,
                                                            90, -90, 0)

    # The focal length measurer
    fl = -3.5
    ls.moveTo(fl - tw, pur[1], 0)
    ls.drawTo(fl + tw, pur[1], 0)
    ls.moveTo(fl, pur[1], 0)
    ls.drawTo(fl, 0, 0)
    ls.moveTo(fl - tw, 0, 0)
    ls.drawTo(fl + tw, 0, 0)
    labels.setText('F')
    topViewStuff.attachNewNode(labels.generate()).setPosHpr(fl - bl, pur[1] / 2, 0,
                                                            90, -90, 0)

    # The film size measurer
    fs = -7.5
    ls.moveTo(pll[0], fs - tw, 0)
    ls.drawTo(pll[0], fs + tw, 0)
    ls.moveTo(pll[0], fs, 0)
    ls.drawTo(pur[0], fs, 0)
    ls.moveTo(pur[0], fs - tw, 0)
    ls.drawTo(pur[0], fs + tw, 0)
    labels.setText('E')
    labels.setAlign(TextNode.ARight)
    topViewStuff.attachNewNode(labels.generate()).setPosHpr(1.5 - bl, fs - bl, 0,
                                                            90, -90, 0)

    topViewStuff.attachNewNode(ls.create())

    # Draw an arc to measure field of view
    ls.reset()
    radius = 8
    numTicks = 100
    for i in range(numTicks):
        x = (float(i) / float(numTicks - 1)) * 2 - 1
        n = Point3()
        f = Point3()
        lens.extrude(Point2(x, 0), n, f)
        v = f - n
        v.normalize()
        v = v * radius
        ls.drawTo(v)

    labels.setText('B')
    topViewStuff.attachNewNode(labels.generate()).setPosHpr(1.5 - bl, radius - bl, 0,
                                                            90, -90, 0)

    topViewStuff.attachNewNode(ls.create())

    # Make sure the buffer camera uses this lens to view this scene.
    bufcamera.reparentTo(rig)
    bufcamera.node().setLens(lens)

    # Now a polygon to show the contents of whatever that camera sees.
    cm = CardMaker('filmPlane')
    cm.setFrame(pll[0], pur[0], pll[2], pur[2])
    card = rig.attachNewNode(cm.generate())
    card.setPos(0, pll[1], 0)
    card.setTexture(buf.getTexture())

    return rig

def perspectiveViewA():
    base.camNode.setLens(base.camLens)
    base.camNode.setCameraMask(PerspectiveViewMask)
    base.disableMouse()
    base.camera.setPosHpr(42.4, -46.6, 23.4, 31, -15, 0)

def perspectiveViewB():
    base.camNode.setLens(base.camLens)
    base.camNode.setCameraMask(PerspectiveViewMask)
    base.disableMouse()
    base.camera.setPosHpr(50, -46, 32, 38, -22, 0)

def topView():
    orthoLens = OrthographicLens()
    orthoLens.setFilmSize(80, 60)
    base.camNode.setLens(orthoLens)
    base.camNode.setCameraMask(TopViewMask)
    base.disableMouse()
    base.camera.setPosHpr(0, 22, 50, 90, -90, 0)
    
def mouseView():
    base.camNode.setLens(base.camLens)
    base.camNode.setCameraMask(BitMask32.allOn())
    base.enableMouse()

def antialiasedScreenshot(filename):
    # Ensure the frame is rendered.
    base.graphicsEngine.renderFrame()

    # Grab the screenshot into a big image
    full = PNMImage()
    base.win.getScreenshot(full)

    # Now reduce it
    reduced = PNMImage(400, 300)
    reduced.gaussianFilterFrom(1, full)

    # And write it out.
    reduced.write(Filename(filename))
    

def saveScreenshots():
    rig = setupLens(PerspectiveLens())

    perspectiveViewA()
    antialiasedScreenshot('lens_tutorial_perspective.jpg')

    topView()
    antialiasedScreenshot('lens_tutorial_top.jpg')

    rig.detachNode()
    rig = setupLens(OrthographicLens())

    perspectiveViewB()
    antialiasedScreenshot('lens_tutorial_orthographic.jpg')

    rig.detachNode()

setupStage()
saveScreenshots()

File history

Click on a date/time to view the file as it appeared at that time.

Date/TimeThumbnailDimensionsUserComment
current19:15, 11 August 2005Thumbnail for version as of 19:15, 11 August 2005400 × 300 (13 KB)Drwr (Talk | contribs)
18:38, 11 August 2005Thumbnail for version as of 18:38, 11 August 2005400 × 300 (11 KB)Drwr (Talk | contribs)

The following page links to this file:

Top