import { Util } from "../util" ; import { CubeCamera } from "./cube_camera" ; import { Renderer } from "./renderer" ; import { Scene } from "./scene" ; /** Stores a cube texture. */ export class CubeTexture { id = Util . getRandomId ( ) ; renderer : Renderer ; glTexture : WebGLTexture ; size : number ; framebuffer ? : WebGLFramebuffer ; depthBuffer ? : WebGLRenderbuffer ; /** For efficiency purposes, not all faces of the texture are rendered to in one step. Therefore, we need to keep track of where we left off. */ nextFaceToRender = 0 ; /** Creates a cube texture from 6 images. */ constructor ( renderer , images ) ; /** Creates an empty sqauare cube texture with a specified dimension. */ constructor ( renderer , dimension ) ; constructor ( renderer , arg2 ) { this . renderer = renderer ; let { gl } = renderer ; let texture = gl . createTexture ( ) ; gl . bindTexture ( gl . TEXTURE_CUBE_MAP , texture ) ; if ( Array . isArray ( arg2 ) ) { let images = arg2 ; if ( images . some ( x => ! x . complete ) ) throw new Error ( "Can only pass loaded images into CubeTexture." ) ; this . size = images [ 0 ] . naturalWidth ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_POSITIVE_X , 0 , gl . RGBA , gl . RGBA , gl . UNSIGNED_BYTE , images [ 0 ] ) ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_NEGATIVE_X , 0 , gl . RGBA , gl . RGBA , gl . UNSIGNED_BYTE , images [ 1 ] ) ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_POSITIVE_Y , 0 , gl . RGBA , gl . RGBA , gl . UNSIGNED_BYTE , images [ 2 ] ) ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_NEGATIVE_Y , 0 , gl . RGBA , gl . RGBA , gl . UNSIGNED_BYTE , images [ 3 ] ) ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_POSITIVE_Z , 0 , gl . RGBA , gl . RGBA , gl . UNSIGNED_BYTE , images [ 4 ] ) ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_NEGATIVE_Z , 0 , gl . RGBA , gl . RGBA , gl . UNSIGNED_BYTE , images [ 5 ] ) ; gl . generateMipmap ( gl . TEXTURE_CUBE_MAP ) ; } else { let size = arg2 ; this . size = size ; let data = new Uint8Array ( size * size * 4 ) ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_POSITIVE_X , 0 , gl . RGBA , size , size , 0 , gl . RGBA , gl . UNSIGNED_BYTE , data ) ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_NEGATIVE_X , 0 , gl . RGBA , size , size , 0 , gl . RGBA , gl . UNSIGNED_BYTE , data ) ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_POSITIVE_Y , 0 , gl . RGBA , size , size , 0 , gl . RGBA , gl . UNSIGNED_BYTE , data ) ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_NEGATIVE_Y , 0 , gl . RGBA , size , size , 0 , gl . RGBA , gl . UNSIGNED_BYTE , data ) ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_POSITIVE_Z , 0 , gl . RGBA , size , size , 0 , gl . RGBA , gl . UNSIGNED_BYTE , data ) ; gl . texImage2D ( gl . TEXTURE_CUBE_MAP_NEGATIVE_Z , 0 , gl . RGBA , size , size , 0 , gl . RGBA , gl . UNSIGNED_BYTE , data ) ; gl . texParameteri ( gl . TEXTURE_CUBE_MAP , gl . TEXTURE_WRAP_S , gl . CLAMP_TO_EDGE ) ; gl . texParameteri ( gl . TEXTURE_CUBE_MAP , gl . TEXTURE_WRAP_T , gl . CLAMP_TO_EDGE ) ; } gl . texParameteri ( gl . TEXTURE_CUBE_MAP , gl . TEXTURE_MAG_FILTER , gl . LINEAR ) ; gl . texParameteri ( gl . TEXTURE_CUBE_MAP , gl . TEXTURE_MIN_FILTER , gl . LINEAR_MIPMAP_LINEAR ) ; this . glTexture = texture ; } /** Creates a framebuffer that will be used to render a scene to this cube texture. */ createFramebuffer ( ) { let { gl } = this . renderer ; let framebuffer = gl . createFramebuffer ( ) ; gl . bindFramebuffer ( gl . FRAMEBUFFER , framebuffer ) ; let depthBuffer = gl . createRenderbuffer ( ) ; gl . bindRenderbuffer ( gl . RENDERBUFFER , depthBuffer ) ; gl . renderbufferStorage ( gl . RENDERBUFFER , gl . DEPTH_COMPONENT16 , this . size , this . size ) ; gl . framebufferRenderbuffer ( gl . FRAMEBUFFER , gl . DEPTH_ATTACHMENT , gl . RENDERBUFFER , depthBuffer ) ; this . framebuffer = framebuffer ; this . depthBuffer = depthBuffer ; } param budget render ( scene , cubeCamera , budget = Infinity ) { let { gl } = this . renderer ; if ( ! this . framebuffer ) this . createFramebuffer ( ) ; let start = performance . now ( ) ; let renderedFaces = 0 ; gl . bindFramebuffer ( gl . FRAMEBUFFER , this . framebuffer ) ; for ( let i = 0 ; i < 6 ; i ++ ) { let index = ( this . nextFaceToRender + i ) % 6 ; let camera = cubeCamera . cameras [ index ] ; camera . position . copy ( cubeCamera . position ) ; camera . updateMatrixWorld ( ) ; let target = gl . TEXTURE_CUBE_MAP_POSITIVE_X + index ; gl . bindTexture ( gl . TEXTURE_CUBE_MAP , null ) ; gl . framebufferTexture2D ( gl . FRAMEBUFFER , gl . COLOR_ATTACHMENT0 , target , this . glTexture , 0 ) ; this . renderer . render ( scene , camera , { framebuffer : this . framebuffer , width : this . size , height : this . size , colorTexture : this . glTexture } ) ; renderedFaces ++ ; let time = performance . now ( ) ; let elapsed = time - start ; let elapsedPerFace = elapsed / renderedFaces ; if ( elapsedPerFace * ( renderedFaces + 1 ) >= budget ) break ; } gl . bindTexture ( gl . TEXTURE_CUBE_MAP , this . glTexture ) ; gl . generateMipmap ( gl . TEXTURE_CUBE_MAP ) ; this . nextFaceToRender += renderedFaces ; this . nextFaceToRender %= 6 ; } dispose ( ) { let { gl } = this . renderer ; gl . deleteTexture ( this . glTexture ) ; gl . deleteFramebuffer ( this . framebuffer ) ; gl . deleteRenderbuffer ( this . depthBuffer ) ; } }