DX Render to Texture

BlitzMax Forums/BlitzMax Programming/DX Render to Texture

This is a bit hacky and, I'm sure, could be cleaned up.
I have tried to comment but, in some cases, I had no idea what I was doing.
Feedback and comments appreciated.
Strict
Graphics 640,480
Local image1:TImage=LoadImage("max.png") 
Local image2:TImage=LoadImage("light.png")
' Because Bmax users DX Texture Manager it is necessary to create a dummy rendertarget surface
' and then blit from that surface to the image surface.
Global mysurf:IDirectDrawSurface7   ' The dummy surf to be used as the rendertarget.
While Not KeyHit(KEY_ESCAPE)
   render2image_start(image1)   ' set image1 as the destination image for the draw
   SetBlend lightblend
   DrawImage image2,0,0 
   SetColor 255,0,0 
   SetBlend alphablend
   SetAlpha 0.5
   DrawOval 0,0,200,200
   SetColor 255,255,255
   render2image_stop(image1)    ' stop rendering to the image and reset to the backbuffer
   Cls
   SetBlend maskblend
   SetAlpha 1.0
   DrawImage image1,0,0         ' draw the new image
   DrawText GCMemAlloced(),0,300
   Flip
Wend
Function render2image_start(image:TImage,frame:Int=0)
	Local mypix:TPixmap=LockImage(image)  ' lock the image to obtain a pixmap.
    mysurf:IDirectDrawSurface7=createsurffrompixmap(mypix)  ' Create the rendertarget surface
	Local res=PrimaryDevice.Device.SetRenderTarget(mysurf,0)  ' set the new surface as the rendertarget.
	If res<>DD_OK RuntimeError "D3D7Max2D Rendertarget failed" ' Check it worked OK.
	Cls  ' Clear the rendertarget
	DrawImage image,0,0  'need to copy the 'destination' image to the render surface.
End Function
Function render2image_stop(image:TImage,frame=0)
    ' Set the src and dest sizes to copy from/to. Thought I could leave these null but it produces horrid results.
    ' These parms could be used to copy a part of an image to another.
	Local src[]=[0,0,ImageWidth(image),ImageHeight(image)]  	
	Local dest[]=[0,0,ImageWidth(image),ImageHeight(image)]  
	dest[2]:+dest[0]
	dest[3]:+dest[1]
	' Do the blit from the dummy rendertarget to the image surface.
	TD3D7ImageFrame(image.frame(frame)).surface.Blt dest,mysurf,src,0,Null
	' Release the surface
	mysurf.release_()
	' reset rendering to the backbuffer
    render2backbuffer()
End Function
Function render2backbuffer()
    'set the rendertarget to the backbuffer
   	PrimaryDevice.Device.SetRenderTarget PrimaryDevice.backbuffer,0
End Function
Function createsurffrompixmap:IDirectDrawSurface7(temppix:TPixmap)
'lifted and butchered from Bmax source.
' I Don't use the POW2SIZE in this case.
		Function Pow2Size( n )
			Local t=1
			While t<n
				t:*2
			Wend
			Return t
		End Function
		Local 	surf:IDirectDrawSurface7  ' The dummy surface to return as the rendertarget
		Local	desc:DDSURFACEDESC2=New DDSURFACEDESC2  ' the 'description' of the surface
		Local	res   ' Used to hold the return code of DX operations.
		desc.dwSize=SizeOf(desc)  ' size of the description 
		desc.dwFlags=DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT|DDSD_PITCH   'Something to do with valid members?
		desc.dwWidth=temppix.width   ' Width of the surface = to the pixmap width
		desc.dwHeight=temppix.height ' Same but for height.
		desc.lPitch=temppix.pitch  ' Think this is for pixmap memory location
		desc.lpSurface=temppix.pixels ' and this
		desc.ddsCaps=DDSCAPS_3DDEVICE|DDSCAPS_VIDEOMEMORY  ' Surface can be rendered to and held in videomem.
		desc.ddpf_dwSize=SizeOf(DDPIXELFORMAT)
		Select temppix.format
			Case PF_BGR888
				desc.ddpf_dwFlags=DDPF_RGB
				desc.ddpf_BitCount=24
				desc.ddpf_BitMask_0=$ff0000
				desc.ddpf_BitMask_1=$00ff00
				desc.ddpf_BitMask_2=$0000ff
			Case PF_RGB888
				desc.ddpf_dwFlags=DDPF_RGB
				desc.ddpf_BitCount=24
				desc.ddpf_BitMask_0=$0000ff
				desc.ddpf_BitMask_1=$00ff00
				desc.ddpf_BitMask_2=$ff0000
			Case PF_BGRA8888
				desc.ddpf_dwFlags=DDPF_RGB|DDPF_ALPHAPIXELS
				desc.ddpf_BitCount=32
				desc.ddpf_BitMask_0=$ff0000
				desc.ddpf_BitMask_1=$00ff00
				desc.ddpf_BitMask_2=$0000ff
				desc.ddpf_BitMask_3=$ff000000
			Case PF_RGBA8888
				desc.ddpf_dwFlags=DDPF_RGB|DDPF_ALPHAPIXELS
				desc.ddpf_BitCount=32
				desc.ddpf_BitMask_0=$0000ff
				desc.ddpf_BitMask_1=$00ff00
				desc.ddpf_BitMask_2=$ff0000
				desc.ddpf_BitMask_3=$ff000000
		End Select	
		' Create the surface and return the result into RES	
		res=PrimaryDevice.DDraw.CreateSurface( desc,Varptr surf,Null )
		If res<>DD_OK RuntimeError "D3D7Max2D Create System Surface Failed"
		Return surf		
End Function

P.S. Not a huge amount of testing done on this either.
<edit> Hmmm, oddly it remembers images drawn to it between renders but not primitives when they're drawn in the mainloop. Anybody see what's wrong?
<edit> Actually, it just doesn't remember what was rendered. It must be an error in my blt.

' Because Bmax users DX Texture Manager it is necessary to create a dummy rendertarget surface
' and then blit from that surface to the image surface.

So there is no benefit in using this then? Seems like it would be just as slow to render to the backbuffer, and blit from there to a texture.

You might also want to consider how you would render an entire scene to a texture and how you would deal with transparency.

or just use Tim's, it's free :-)

So there is no benefit in using this then?

Well, the fact it doesn't work is probably more of a limiting factor.
or just use Tim's, it's free :-)

...happier to do that now the source has been released.

Tim, I just double-checked your R2T code and it renders to a seperate surface which it then displays.
What I am trying to do here is change the image's texture which is why I need the blt... unless I have missed something.
<edit> In fact, I think we're doing the same thing. My render surface displays correctly and can be drawn. However, I need to then blt the contents of that surface to the image surface.
Does anybody know if this is right...
	Local src[]=[0,0,ImageWidth(image),ImageHeight(image)]  	
	Local dest[]=[0,0,ImageWidth(image),ImageHeight(image)]  
	dest[2]:+dest[0]
	dest[3]:+dest[1]
	' Do the blit from the dummy rendertarget to the image surface.
	TD3D7ImageFrame(image.frame(frame)).surface.Blt dest,mysurf,src,0,Null

?

Hmmm... the blit does work but a 'cls' was removing the render. Without it there is a bit of corruption at high speeds
Still lots I don't understand. Once the render-to-texture is complete I want to replace the image surface with the render surface. I guess the only way to do this is to blt *(or bitblt).
Anybody?