Code archives/3D Graphics - Misc/Test if cubemaps are *truly* supported.

This code has been declared by its author to be Public Domain code.

Download source code

Test if cubemaps are *truly* supported. by Cygnus
(Posted 19 years ago)
[Edit] Markcw has now added some more useful code to further extend the test. Also includes demo now.

GFXDriverCaps3D() is a useful function, or, it would be if graphics drivers reported their abilities correctly.

After spending a long while bored and toying with the old B3D (it's been a looong time...) I made this snippet to test if a cubemap is rendered correctly.

It tests if the cubemap texture is pixel perfect with what was rendered on the back buffer. If the backbuffer and the texture do not match, the function returns false.

You might want to alter the texture size to work on a texture the same size as your cubemaps in game to be sure.

Marks code was placed here as apparently my method still did not work on all machines. (it did work on the machines i tested it on... but who knows about other variations of machines!..)

Also, This can be optimised- the test doesn't technically need to render to set up a cubemap and test. Since you only need to call it after initing the graphics window, its hardly going to be slow anyway.
;TestCubeMap function example (fixed by markcw)

;setup scene
Graphics3D 320,240,0,2
SetBuffer BackBuffer()
camera=CreateCamera()
light=CreateLight()
RotateEntity light,90,0,0

;sky sphere
sky=CreateSphere(16)
ScaleEntity sky,500,500,500
FlipMesh sky
EntityFX sky,1
skytex=CreateSkyTexture(256,256)
EntityTexture sky,skytex,0,0

;cubemapped cube
cube=CreateCube()
PositionEntity cube,0,0,5
tex=CreateTexture(32,32,1+128+256)
For face=0 To 5
 If face=0 Then RotateEntity camera,0,90,0 ;left
 If face=1 Then RotateEntity camera,0,0,0 ;front
 If face=2 Then RotateEntity camera,0,-90,0 ;right
 If face=3 Then RotateEntity camera,0,180,0 ;back
 If face=4 Then RotateEntity camera,-90,0,0 ;up
 If face=5 Then RotateEntity camera,90,0,0 ;down
 SetCubeFace tex,face
 RenderWorld
 CopyRect 0,0,32,32,0,0,BackBuffer(),TextureBuffer(tex)
Next
RotateEntity camera,0,0,0
EntityTexture cube,tex

cubemapsupport=TestCubeMap(camera)
caps=GfxDriverCaps3D()

;main loop
While Not KeyDown(1)

 pitch#=0 : yaw#=0
 If KeyDown(208) Then pitch#=-1 
 If KeyDown(200) Then pitch#=1
 If KeyDown(203) Then yaw#=-1
 If KeyDown(205) Then yaw#=1
 TurnEntity cube,pitch#,yaw#,0

 RenderWorld

 Text 0,0,"cubemapsupport="+cubemapsupport
 Text 0,12,"caps="+caps

 Flip
Wend

Function CreateSkyTexture(Xsize,Ysize)
 ;Create a sky sphere texture of given dimensions

 Local hpal,hmap,htex,ix,iy,ip
 hpal=CreatePalette(256,100,160,230,255,255,255) ;cyan gradient
 hmap=CreateHeightMap(hpal,Xsize,Ysize,8,2,1,16,2,0,1,48,0)
 htex=CreateTexture(Xsize,Ysize)
 ;set all heightmap points
 LockBuffer(TextureBuffer(htex))
 For iy=0 To Ysize-1
  For ix=0 To Xsize-1
   ip=PeekByte(hmap,ix+(iy*Xsize)) ;index=x+(y*width)
   WritePixelFast ix,iy,PeekInt(hpal,ip*4),TextureBuffer(htex)
  Next
 Next
 UnlockBuffer(TextureBuffer(htex))
 FreeBank hmap
 FreeBank hpal
 Return htex ;texture handle

End Function

Function CreatePalette(ncol,rmin,gmin,bmin,rmax,gmax,bmax)
 ;Create a palette for a heightmap of given size and color range

 Local hpal,ic,red,green,blue
 hpal=CreateBank(ncol*4)
 For ic=0 To ncol-1
  red=(ic*(rmax-rmin)/ncol)+rmin
  green=(ic*(gmax-gmin)/ncol)+gmin
  blue=(ic*(bmax-bmin)/ncol)+bmin
  PokeInt hpal,ic*4,(red Shl 16)+(green Shl 8)+blue
 Next
 Return hpal ;palette bank handle

End Function

Function CreateHeightMap(hpal,Xdm,Ydm,Xps,Yps,Bps,Blr,Wpr,Mcv,Bcv,Bxs,Bys)
 ;From "lands.bas" by Per Larsson (www.programmersheaven.com)
 ;Xps/Yps/Bps=X/Y/Blur pixel step, Blr=blur (smoothing) amount,
 ;Wpr=water probability (not 0), Mcv=minimum color value,
 ;Bcv=border color value (0 for none), Bxs/Bys=border X/Y size

 Local hmap,hnewmap,ix,iy,ystep,xstep,val,ptx,pty,ib,lf,rt,up,dn
 SeedRnd MilliSecs() ;randomize seed
 hmap=CreateBank(Xdm*Ydm)
 hnewmap=CreateBank(Xdm*Ydm)
 ;make random 2-colors map
 For iy=0 To Ydm-1
  If ystep=0 ;instead of using Step, for variable steps
   For ix=0 To Xdm-1
    If xstep=0
     val=Rand(0,Wpr) ;water probability
     If val=1 : val=BankSize(hpal)-1 : Else : val=1 : EndIf ;set 2-colors
     If Bcv>0 ;draw border around map
      If ix<Bys Or ix>=Xdm-Bys Then val=Bcv
      If iy<Bxs Or iy>=Ydm-Bxs Then val=Bcv
     EndIf
     ;set heightmap points, and boxfill in-between points
     For ptx=0 To Xps-1
      For pty=0 To Yps-1
       PokeByte hmap,(ix+ptx)+((iy+pty)*Xdm),val ;calculate x,y offset
      Next
     Next
    EndIf
    xstep=xstep+1 : If xstep>=Xps Then xstep=0
   Next 
  EndIf
  ystep=ystep+1 : If ystep>=Yps Then ystep=0
 Next
 If Blr=0 Then CopyBank hmap,0,hnewmap,0,Xdm*Ydm ;copy 2-colors map
  ;blur smooth map by pixel steps, average out 2-colors map
  For ib=1 To Blr
   For iy=0 To Ydm-1
    If ystep=0
     For ix=0 To Xdm-1
      If xstep=0
       ;get surrounding points, and wrap overlapping points
       lf=ix-Bps : If lf<0 Then lf=Xdm-Bps
       rt=ix+Bps : If rt>Xdm-Bps Then rt=0
       up=iy-Bps : If up<0 Then up=Ydm-Bps
       dn=iy+Bps : If dn>Ydm-Bps Then dn=0
       ;calculate average of current point, blur
       ;color=(up+lf+rt+down+(pt*2)+lfup+rtup+lfdown+rtdown)/10
       val=PeekByte(hmap,ix+(up*Xdm))+PeekByte(hmap,lf+(iy*Xdm))
       val=val+PeekByte(hmap,rt+(iy*Xdm))+PeekByte(hmap,ix+(dn*Xdm))
       val=val+(PeekByte(hmap,ix+(iy*Xdm))*2)
       val=val+PeekByte(hmap,lf+(up*Xdm))+PeekByte(hmap,rt+(up*Xdm))
       val=val+PeekByte(hmap,lf+(dn*Xdm))+PeekByte(hmap,rt+(dn*Xdm))
       val=val/10
       If val<Mcv Then val=Mcv ;set minimum color
       If ib>1 Then PokeByte hmap,ix+(iy*Xdm),val ;set smoothed average
       ;Set actual heightmap points, in pixel steps
       For ptx=0 To Bps-1
        For pty=0 To Bps-1
         PokeByte hnewmap,(ix+ptx)+((iy+pty)*Xdm),val
        Next
       Next
      EndIf
      xstep=xstep+1 : If xstep>=Bps Then xstep=0
     Next
    EndIf
    ystep=ystep+1 : If ystep>=Bps Then ystep=0
   Next
  Next
 FreeBank hmap
 Return hnewmap ;heightmap bank handle

End Function

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;ACTUAL FUNCTION
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function TestCubeMap(maincamera)
 ;This function tests if a card truely does support cubemapping.
 ;Unfortunately some cards report that they do when they in fact do not.
 ;This results in garbled output. To test properly, we render some
 ;cubemaps and check if the texture matches what was rendered to the BackBuffer.
 ;Function by Damien Sturdy AKA Cygnus, Modified by MarkCW for accuracy.
 ;returns true or false. true if the test succeeded.

 If GfxDriverCaps3D()<>110 Then Return 0
 Local camera,tex,texsize=32
 Local clr[5],badpixels,tolerance=10
 Local ok=1,x,y,face,p1,p2
 camera=CreateCamera()
 CameraProjMode maincamera,0
 CameraViewport camera,0,0,texsize,texsize
 tex=CreateTexture(texsize,texsize,1+128+256) 
 For face=0 To 5
  If face=0 Then RotateEntity camera,0,90,0 ;left
  If face=1 Then RotateEntity camera,0,0,0 ;front
  If face=2 Then RotateEntity camera,0,-90,0 ;right
  If face=3 Then RotateEntity camera,0,180,0 ;back
  If face=4 Then RotateEntity camera,-90,0,0 ;up
  If face=5 Then RotateEntity camera,90,0,0 ;down
  SetCubeFace tex,face
  RenderWorld
  CopyRect 0,0,texsize,texsize,0,0,BackBuffer(),TextureBuffer(tex)
  For x=1 To texsize-2
   For y=1 To texsize-2
    p1=ReadPixel(x,y,BackBuffer()) And $FFFFFF
    p2=ReadPixel(x,y,TextureBuffer(tex)) And $FFFFFF
    clr[0]=((p1 And $FF0000)/$FF00)/16
    clr[3]=((p2 And $FF0000)/$FF00)/16
    clr[1]=((p1 And $00FF00)/$FF)/16
    clr[4]=((p2 And $00FF00)/$FF)/16
    clr[2]=(p1 And $0000FF)/16
    clr[5]=(p2 And $0000FF)/16
    If clr[0]<>clr[3] And clr[1]<>clr[4] And clr[2]<>clr[5]
     badpixels=badpixels+1
    EndIf
   Next
  Next
 ;Print " " : Delay(500)
 Next
 If badpixels<(((6*texsize*texsize)/100)*tolerance) Then ok=1
 CameraProjMode maincamera,1
 FreeEntity camera
 FreeTexture tex
 Return ok

End Function