HDScreenShot

Here is my little HDScreenShot function:

def HDScreenShot():
    tex=Texture()
    width=1024*4
    height=1024*4
    mybuffer=base.win.makeTextureBuffer('HDScreenShotBuff',width,height,tex,True)  
    
    cam=Camera('HDCam')
    cam.setLens(base.camLens.makeCopy())
    cam.getLens().setAspectRatio(width/height)
    pCam=NodePath(cam)
    print "HDScreenShot "+str(width)+" by "+str(height)
      
    mycamera = base.makeCamera(mybuffer,useCamera=pCam)
    myscene = base.render
    mycamera.node().setScene(myscene)
    base.graphicsEngine.renderFrame()
    tex = mybuffer.getTexture()
    mybuffer.setActive(False)
    tex.write("ScreenShots/FG_ScreenShotHD"+str(time())+".jpg")
    base.graphicsEngine.removeWindow(mybuffer)

Known Issues: Does not apply any filters that may be applied to the scene. I tried, and I could not make it work.

Saves a screenshot (currently 40964096) to a screenshots folder. You will probably want to change the naming convention. Also, for cards that don’t support 40964096, you will have to use a smaller size. You might also want to use the same aspect ratio as the screen, though this code avoids distortion caused by using a a different shape.

Incase you find this post looking for how to do a regular basic screen shot, here is the code:

base.win.saveScreenshot("ScreenShot.jpg")

Edit:
Here is code that applies a bloom filter to the HD Screenshot:

def HDScreenShot():
    tex=Texture()
    width=1024*4
    height=1024*4
    mybuffer=base.win.makeTextureBuffer('HDScreenShotBuff',width,height,tex,True)  
    dis=mybuffer.makeDisplayRegion()
    cam=Camera('HDCam')
    cam.setLens(base.camLens.makeCopy())
    cam.getLens().setAspectRatio(width/height)
    pCam=NodePath(cam)
    print "HDScreenShot "+str(width)+" by "+str(height)
    
    dis.setCamera(pCam)
    print cam.getNumDisplayRegions()
    print base.cam.node().getNumDisplayRegions()
      
    mycamera = base.makeCamera(mybuffer,useCamera=pCam)
    
    filters = CommonFilters(mybuffer, mycamera)
    filterok = filters.setBloom(blend=(0,0,0,1), desat=0.5, intensity=0.5, size="large",mintrigger=0.6, maxtrigger=1.0)
    
    
    
    myscene = base.render
    mycamera.node().setScene(myscene)
    base.graphicsEngine.renderFrame()
    tex = mybuffer.getTexture()
    mybuffer.setActive(False)
    tex.write("ScreenShots/FG_ScreenShotHD"+str(time())+".jpg")
    base.graphicsEngine.removeWindow(mybuffer)
1 Like

not sure what you’d need this for, but thanks. it shows how easy to use buffers are :slight_smile:

Not sure? So you can show off your cool panda3d game, of course!

The reason why filters won’t apply is that they assume the final output of all filters is the base.camera object. Since you’re making a new camera, it’s not getting any output at all from the filters.

I’m not sure if it’s possible to hand off another camera to the FilterManager so it’s tied in. I believe it’s all hard-coded assumptions that the full-screen quads you want the effects applied to are viewed from the base.camera, but I could be wrong.

I tried to make a new instance of CommonFilters to do it.

something like:

filters = CommonFilters(mybuffer, cam)
filterok = filters.setBloom(blend=(0,0,0,1), desat=0.5, intensity=0.5, size="large",mintrigger=0.6, maxtrigger=1.0)
    

but I just get:
StandardError: Could not find appropriate DisplayRegion to filter

I poked around in the CommonFilters source a bit, but it is beyond me.

I think you need to make a display region that renders the camera into the buffer.

Well, cam.getNumDisplayRegions() is 0, so that makes sense.
DisplayRegion does not seem to have a constructor, and I don’t see a way to add one to a camera anyway. How would I do this?

You can let the buffer make one for you, using buffer.makeDisplayRegion().
panda3d.org/apiref.php?page= … playRegion

PS. How is your code different from base.screenshot(), exactly?

Thanks. I got it working.

Take a look at this particular screenshot, and see if you can find the difference:
http://f-g.wdfiles.com/local–files/start/FG_ScreenShotHD1252684706.12.jpg
You might need to look at the full size version.

…but I tried to solve this problem myself a few months ago, and I didn’t manage to make it work. I found your solution today, I tried it and I got this:

:gobj(error): Texture::write() - couldn’t write: /home/zzarko/DMKTS(005)NOVI_Nterm1_Self1.jpg

I have the write permission on that directory, and filename is ok (base.screenshot is working fine with that filename). I tried different resolutions for the image, but I always get the same error. I’m using Ubuntu 9.10/Panda3D 1.6.1 (I know it’s old version, I’ll try newer one soon).

I know it’s not your code fault and I should probably do something elsewhere, but maybe you stumbled upon the same problem and solved it.

1.6.1 is quite buggy. I recommend upgrading to 1.6.2 at least - this may very well be caused by a bug in 1.6.1.

I just tried it, same error… 1.7.0 is still experimental, and there are no packages for older Ubuntu’s (I need to run my program on 8.04 - 9.10). I guess it’s not a bug, it obviously works for Craig :slight_smile:
Is there a Panda debug mode or something that could give me more information about this error (“couldn’t write” isn’t very descriptive)?

Hmm, it is strange indeed. Can you write code like this?

tex = loader.loadTexture('/my/existing/tex.jpg')
print tex
print tex.write('/home/zzarko/DMKTS(005)NOVI_Nterm1_Self1.jpg')
print tex.write('/tmp/valid.jpg')

There are three reasons that tex.write() might report that message: either it doesn’t understand the file extension (.jpg in this case, seems an unlikely problem), it can’t write to the directory (also seems unlikely), or there is some problem with the image data itself (e.g. the image is absent).

It seems most likely that the problem in your case is the third case. If so, all of the above code will work correctly. You can insert a “print tex” in your own code that is attempting to save the HD screenshot to prove that the texture is or is not properly loaded before attempting to write it.

David

I tried this and it worked. I found the solution, but what’s the problem is beyond me… I had a code like this to create a filename:

fname = self.fileLayout.combo.currentText()+"_Nterm"+nterm+"_Self"+selfvalue+".jpg"

and it worked with base.win.saveScreenshot just fine. With tex.write it gave me “couldn’t write” error. After some experimenting (I even checked if string created with this code is the same as hard-coded “DMKTS(005)NOVI_Nterm1_Self1.jpg” and it returned True), I found this to work:

fname = str(self.fileLayout.combo.currentText())+"_Nterm"+nterm+"_Self"+selfvalue+".jpg"

Why I need str() to make it work with tex.write I have no idea… Anyway, thanks, with your help I found what was wrong.

One more question, if you don’t mind: is it possible that screenshot generated this way include aspect2d elements I also have on my display (base.win.saveScreenshot saves with that layer too)?

Hmm, so that means that self.fileLayout.combo.currentText() is not returning a string. Perhaps it is returning a Unicode object? What do you see for:

print self.fileLayout.combo.currentText().__class__

though I don’t know why a Unicode filename passed to Texture.write() would cause any problems either; that works fine for me too.

Do you mean, you want to save the aspect2d elements in your texture? Certainly this is possible, and it depends on how you are generating the texture in the first place. If your texture is generated by base.win.addRenderTexture() (with the appropriate parameters to copy the screen image all the way to RAM), it will contain the aspect2d elements along with everything else. But if your goal is to save a screenshot to disk, then why are you not simply using base.screenshot()?

David

Yes, exactly.

I’ll try this, thanks.

I’m making a program that draws some mechanical 3D data, and I need screenshots for the article my colleagues and me are writing. But, for that purpose, I need screenshots taken in higher resolution than my display (it is required by the magazine we are writing for).