585 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			585 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
|  | package kha; | ||
|  | 
 | ||
|  | import haxe.io.Bytes; | ||
|  | import js.Browser; | ||
|  | import js.lib.Uint8Array; | ||
|  | import js.lib.Uint16Array; | ||
|  | import js.lib.Float32Array; | ||
|  | import js.html.VideoElement; | ||
|  | import js.html.webgl.GL; | ||
|  | import js.html.webgl.Framebuffer; | ||
|  | import js.html.webgl.Renderbuffer; | ||
|  | import js.html.webgl.Texture; | ||
|  | import kha.graphics4.TextureFormat; | ||
|  | import kha.graphics4.DepthStencilFormat; | ||
|  | import kha.js.graphics4.Graphics; | ||
|  | 
 | ||
|  | class WebGLImage extends Image { | ||
|  | 	public var image: Dynamic; | ||
|  | 	public var video: VideoElement; | ||
|  | 
 | ||
|  | 	static var context: js.html.CanvasRenderingContext2D; | ||
|  | 
 | ||
|  | 	var data: js.html.ImageData; | ||
|  | 
 | ||
|  | 	var myWidth: Int; | ||
|  | 	var myHeight: Int; | ||
|  | 	var myFormat: TextureFormat; | ||
|  | 	var renderTarget: Bool; | ||
|  | 	var samples: Int; | ||
|  | 
 | ||
|  | 	public var frameBuffer: Framebuffer = null; | ||
|  | 	public var renderBuffer: Renderbuffer = null; | ||
|  | 	public var texture: Texture = null; | ||
|  | 	public var depthTexture: Texture = null; | ||
|  | 	public var MSAAFrameBuffer: Framebuffer = null; | ||
|  | 
 | ||
|  | 	var MSAAColorBuffer: Renderbuffer; | ||
|  | 	var MSAADepthBuffer: Renderbuffer; | ||
|  | 
 | ||
|  | 	var graphics1: kha.graphics1.Graphics; | ||
|  | 	var graphics2: kha.graphics2.Graphics; | ||
|  | 	var graphics4: kha.graphics4.Graphics; | ||
|  | 
 | ||
|  | 	var depthStencilFormat: DepthStencilFormat; | ||
|  | 
 | ||
|  | 	var readable: Bool; | ||
|  | 
 | ||
|  | 	// WebGL2 constants | ||
|  | 	static inline var GL_RGBA16F = 0x881A; | ||
|  | 	static inline var GL_RGBA32F = 0x8814; | ||
|  | 	static inline var GL_R16F = 0x822D; | ||
|  | 	static inline var GL_R32F = 0x822E; | ||
|  | 	static inline var GL_RED = 0x1903; | ||
|  | 	static inline var GL_DEPTH_COMPONENT24 = 0x81A6; | ||
|  | 	static inline var GL_DEPTH24_STENCIL8 = 0x88F0; | ||
|  | 	static inline var GL_DEPTH32F_STENCIL8 = 0x8CAD; | ||
|  | 
 | ||
|  | 	static var canvas: js.html.CanvasElement; | ||
|  | 
 | ||
|  | 	public static function init() { | ||
|  | 		if (context == null) { | ||
|  | 			// create only once | ||
|  | 			canvas = Browser.document.createCanvasElement(); | ||
|  | 			if (canvas != null) { | ||
|  | 				context = canvas.getContext("2d"); | ||
|  | 				canvas.width = 4096; | ||
|  | 				canvas.height = 4096; | ||
|  | 				context.globalCompositeOperation = "copy"; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function new(width: Int, height: Int, format: TextureFormat, renderTarget: Bool, depthStencilFormat: DepthStencilFormat, samples: Int, | ||
|  | 			readable: Bool) { | ||
|  | 		myWidth = width; | ||
|  | 		myHeight = height; | ||
|  | 		myFormat = format; | ||
|  | 		this.renderTarget = renderTarget; | ||
|  | 		this.samples = samples; | ||
|  | 		this.readable = readable; | ||
|  | 		image = null; | ||
|  | 		video = null; | ||
|  | 		this.depthStencilFormat = depthStencilFormat; | ||
|  | 		init(); | ||
|  | 		if (renderTarget) | ||
|  | 			createTexture(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function get_g1(): kha.graphics1.Graphics { | ||
|  | 		if (graphics1 == null) { | ||
|  | 			graphics1 = new kha.graphics2.Graphics1(this); | ||
|  | 		} | ||
|  | 		return graphics1; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function get_g2(): kha.graphics2.Graphics { | ||
|  | 		if (graphics2 == null) { | ||
|  | 			graphics2 = new kha.js.graphics4.Graphics2(this); | ||
|  | 		} | ||
|  | 		return graphics2; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function get_g4(): kha.graphics4.Graphics { | ||
|  | 		if (graphics4 == null) { | ||
|  | 			graphics4 = new Graphics(this); | ||
|  | 		} | ||
|  | 		return graphics4; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function get_width(): Int { | ||
|  | 		return myWidth; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function get_height(): Int { | ||
|  | 		return myHeight; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function get_format(): TextureFormat { | ||
|  | 		return myFormat; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function get_realWidth(): Int { | ||
|  | 		return myWidth; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function get_realHeight(): Int { | ||
|  | 		return myHeight; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function get_stride(): Int { | ||
|  | 		return formatByteSize(myFormat) * width; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override public function isOpaque(x: Int, y: Int): Bool { | ||
|  | 		if (data == null) { | ||
|  | 			if (context == null) | ||
|  | 				return true; | ||
|  | 			else | ||
|  | 				createImageData(); | ||
|  | 		} | ||
|  | 		return (data.data[y * Std.int(image.width) * 4 + x * 4 + 3] != 0); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override public function at(x: Int, y: Int): Color { | ||
|  | 		if (bytes != null) { | ||
|  | 			var r = bytes.get(y * width * 4 + x * 4); | ||
|  | 			var g = bytes.get(y * width * 4 + x * 4 + 1); | ||
|  | 			var b = bytes.get(y * width * 4 + x * 4 + 2); | ||
|  | 			var a = bytes.get(y * width * 4 + x * 4 + 3); | ||
|  | 
 | ||
|  | 			return Color.fromValue((a << 24) | (r << 16) | (g << 8) | b); | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			if (data == null) { | ||
|  | 				if (context == null) | ||
|  | 					return Color.Black; | ||
|  | 				else | ||
|  | 					createImageData(); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			var r = data.data[y * width * 4 + x * 4]; | ||
|  | 			var g = data.data[y * width * 4 + x * 4 + 1]; | ||
|  | 			var b = data.data[y * width * 4 + x * 4 + 2]; | ||
|  | 			var a = data.data[y * width * 4 + x * 4 + 3]; | ||
|  | 
 | ||
|  | 			return Color.fromValue((a << 24) | (r << 16) | (g << 8) | b); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function createImageData() { | ||
|  | 		if (Std.isOfType(image, Uint8Array)) { | ||
|  | 			data = new js.html.ImageData(new js.lib.Uint8ClampedArray(image.buffer), this.width, this.height); | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			if (this.width > canvas.width || this.height > canvas.height) { | ||
|  | 				var cw = canvas.width; | ||
|  | 				var ch = canvas.height; | ||
|  | 				while (this.width > cw || this.height > ch) { | ||
|  | 					cw *= 2; | ||
|  | 					ch *= 2; | ||
|  | 				} | ||
|  | 				canvas.width = cw; | ||
|  | 				canvas.height = ch; | ||
|  | 			} | ||
|  | 			context.strokeStyle = "rgba(0,0,0,0)"; | ||
|  | 			context.fillStyle = "rgba(0,0,0,0)"; | ||
|  | 			context.fillRect(0, 0, image.width, image.height); | ||
|  | 			context.drawImage(image, 0, 0, image.width, image.height, 0, 0, image.width, image.height); | ||
|  | 			data = context.getImageData(0, 0, image.width, image.height); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function upperPowerOfTwo(v: Int): Int { | ||
|  | 		v--; | ||
|  | 		v |= v >>> 1; | ||
|  | 		v |= v >>> 2; | ||
|  | 		v |= v >>> 4; | ||
|  | 		v |= v >>> 8; | ||
|  | 		v |= v >>> 16; | ||
|  | 		v++; | ||
|  | 		return v; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function createTexture(): Void { | ||
|  | 		if (SystemImpl.gl == null) | ||
|  | 			return; | ||
|  | 		texture = SystemImpl.gl.createTexture(); | ||
|  | 		// texture.image = image; | ||
|  | 		SystemImpl.gl.bindTexture(GL.TEXTURE_2D, texture); | ||
|  | 		// Sys.gl.pixelStorei(Sys.gl.UNPACK_FLIP_Y_WEBGL, true); | ||
|  | 
 | ||
|  | 		SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR); | ||
|  | 		SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR); | ||
|  | 		SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE); | ||
|  | 		SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE); | ||
|  | 		if (renderTarget) { | ||
|  | 			frameBuffer = SystemImpl.gl.createFramebuffer(); | ||
|  | 			SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, frameBuffer); | ||
|  | 			switch (myFormat) { | ||
|  | 				case DEPTH16: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL.DEPTH_COMPONENT16 : GL.DEPTH_COMPONENT, realWidth, realHeight, 0, | ||
|  | 						GL.DEPTH_COMPONENT, GL.UNSIGNED_SHORT, null); | ||
|  | 				case RGBA128: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_RGBA32F : GL.RGBA, realWidth, realHeight, 0, GL.RGBA, GL.FLOAT, null); | ||
|  | 				case RGBA64: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_RGBA16F : GL.RGBA, realWidth, realHeight, 0, GL.RGBA, | ||
|  | 						SystemImpl.halfFloat.HALF_FLOAT_OES, null); | ||
|  | 				case RGBA32: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, realWidth, realHeight, 0, GL.RGBA, GL.UNSIGNED_BYTE, null); | ||
|  | 				case A32: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_R32F : GL.ALPHA, realWidth, realHeight, 0, | ||
|  | 						SystemImpl.gl2 ? GL_RED : GL.ALPHA, GL.FLOAT, null); | ||
|  | 				case A16: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_R16F : GL.ALPHA, realWidth, realHeight, 0, | ||
|  | 						SystemImpl.gl2 ? GL_RED : GL.ALPHA, SystemImpl.halfFloat.HALF_FLOAT_OES, null); | ||
|  | 				default: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, realWidth, realHeight, 0, GL.RGBA, GL.UNSIGNED_BYTE, null); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (myFormat == DEPTH16) { | ||
|  | 				SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST); | ||
|  | 				SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST); | ||
|  | 				SystemImpl.gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.TEXTURE_2D, texture, 0); | ||
|  | 				// Some WebGL implementations throw incomplete framebuffer error, create color attachment | ||
|  | 				if (!SystemImpl.gl2) { | ||
|  | 					var colortex = SystemImpl.gl.createTexture(); | ||
|  | 					SystemImpl.gl.bindTexture(GL.TEXTURE_2D, colortex); | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, realWidth, realHeight, 0, GL.RGBA, GL.UNSIGNED_BYTE, null); | ||
|  | 					SystemImpl.gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.TEXTURE_2D, colortex, 0); | ||
|  | 					SystemImpl.gl.bindTexture(GL.TEXTURE_2D, texture); | ||
|  | 				} | ||
|  | 			} | ||
|  | 			else { | ||
|  | 				if (samples > 1 && SystemImpl.gl2) { | ||
|  | 					MSAAFrameBuffer = SystemImpl.gl.createFramebuffer(); | ||
|  | 					MSAAColorBuffer = SystemImpl.gl.createRenderbuffer(); | ||
|  | 					SystemImpl.gl.bindRenderbuffer(GL.RENDERBUFFER, MSAAColorBuffer); | ||
|  | 					var MSAAFormat = switch (myFormat) { | ||
|  | 						case RGBA128: | ||
|  | 							untyped SystemImpl.gl.RGBA32F; | ||
|  | 						case RGBA64: | ||
|  | 							untyped SystemImpl.gl.RGBA16F; | ||
|  | 						case RGBA32: | ||
|  | 							untyped SystemImpl.gl.RGBA8; | ||
|  | 						case A32: | ||
|  | 							GL_R32F; | ||
|  | 						case A16: | ||
|  | 							GL_R16F; | ||
|  | 						default: | ||
|  | 							untyped SystemImpl.gl.RGBA8; | ||
|  | 					}; | ||
|  | 					untyped SystemImpl.gl.renderbufferStorageMultisample(GL.RENDERBUFFER, samples, MSAAFormat, realWidth, realHeight); | ||
|  | 					SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, frameBuffer); | ||
|  | 					SystemImpl.gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.RENDERBUFFER, MSAAColorBuffer); | ||
|  | 					SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, MSAAFrameBuffer); | ||
|  | 				} | ||
|  | 				SystemImpl.gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.TEXTURE_2D, texture, 0); | ||
|  | 				SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, null); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			initDepthStencilBuffer(depthStencilFormat); | ||
|  | 			var e = SystemImpl.gl.checkFramebufferStatus(GL.FRAMEBUFFER); | ||
|  | 			if (e != GL.FRAMEBUFFER_COMPLETE) { | ||
|  | 				trace("checkframebufferStatus error " + e); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			SystemImpl.gl.bindRenderbuffer(GL.RENDERBUFFER, null); | ||
|  | 			SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, null); | ||
|  | 		} | ||
|  | 		else if (video != null) { | ||
|  | 			SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, video); | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			switch (myFormat) { | ||
|  | 				case RGBA128: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_RGBA32F : GL.RGBA, myWidth, myHeight, 0, GL.RGBA, GL.FLOAT, image); | ||
|  | 				case RGBA64: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_RGBA16F : GL.RGBA, myWidth, myHeight, 0, GL.RGBA, | ||
|  | 						SystemImpl.halfFloat.HALF_FLOAT_OES, image); | ||
|  | 				case RGBA32: | ||
|  | 					if (Std.isOfType(image, Uint8Array)) { | ||
|  | 						SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, myWidth, myHeight, 0, GL.RGBA, GL.UNSIGNED_BYTE, image); | ||
|  | 					} | ||
|  | 					else { | ||
|  | 						SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, image); | ||
|  | 					} | ||
|  | 				case A32: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_R32F : GL.ALPHA, myWidth, myHeight, 0, SystemImpl.gl2 ? GL_RED : GL.ALPHA, | ||
|  | 						GL.FLOAT, image); | ||
|  | 				case A16: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_R16F : GL.ALPHA, myWidth, myHeight, 0, SystemImpl.gl2 ? GL_RED : GL.ALPHA, | ||
|  | 						SystemImpl.halfFloat.HALF_FLOAT_OES, image); | ||
|  | 				case L8: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.LUMINANCE, myWidth, myHeight, 0, GL.LUMINANCE, GL.UNSIGNED_BYTE, image); | ||
|  | 				default: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, image); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		SystemImpl.gl.bindTexture(GL.TEXTURE_2D, null); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function initDepthStencilBuffer(depthStencilFormat: DepthStencilFormat) { | ||
|  | 		switch (depthStencilFormat) { | ||
|  | 			case NoDepthAndStencil: | ||
|  | 			case DepthOnly, Depth16: | ||
|  | 				{ | ||
|  | 					if (SystemImpl.depthTexture == null) { | ||
|  | 						renderBuffer = SystemImpl.gl.createRenderbuffer(); | ||
|  | 						SystemImpl.gl.bindRenderbuffer(GL.RENDERBUFFER, renderBuffer); | ||
|  | 						SystemImpl.gl.renderbufferStorage(GL.RENDERBUFFER, GL.DEPTH_COMPONENT16, realWidth, realHeight); | ||
|  | 						SystemImpl.gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, renderBuffer); | ||
|  | 					} | ||
|  | 					else { | ||
|  | 						depthTexture = SystemImpl.gl.createTexture(); | ||
|  | 						SystemImpl.gl.bindTexture(GL.TEXTURE_2D, depthTexture); | ||
|  | 						if (depthStencilFormat == DepthOnly) | ||
|  | 							SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_DEPTH_COMPONENT24 : GL.DEPTH_COMPONENT, realWidth, realHeight, 0, | ||
|  | 								GL.DEPTH_COMPONENT, GL.UNSIGNED_INT, null); | ||
|  | 						else | ||
|  | 							SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL.DEPTH_COMPONENT16 : GL.DEPTH_COMPONENT, realWidth, realHeight, 0, | ||
|  | 								GL.DEPTH_COMPONENT, GL.UNSIGNED_SHORT, null); | ||
|  | 						SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST); | ||
|  | 						SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST); | ||
|  | 						SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE); | ||
|  | 						SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE); | ||
|  | 						SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, frameBuffer); | ||
|  | 
 | ||
|  | 						if (samples > 1 && SystemImpl.gl2) { | ||
|  | 							MSAADepthBuffer = SystemImpl.gl.createRenderbuffer(); | ||
|  | 							SystemImpl.gl.bindRenderbuffer(GL.RENDERBUFFER, MSAADepthBuffer); | ||
|  | 							if (depthStencilFormat == DepthOnly) | ||
|  | 								untyped SystemImpl.gl.renderbufferStorageMultisample(GL.RENDERBUFFER, samples, GL_DEPTH_COMPONENT24, realWidth, realHeight); | ||
|  | 							else | ||
|  | 								untyped SystemImpl.gl.renderbufferStorageMultisample(GL.RENDERBUFFER, samples, GL.DEPTH_COMPONENT16, realWidth, realHeight); | ||
|  | 							SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, frameBuffer); | ||
|  | 							SystemImpl.gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, MSAADepthBuffer); | ||
|  | 							SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, MSAAFrameBuffer); | ||
|  | 						} | ||
|  | 						SystemImpl.gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.TEXTURE_2D, depthTexture, 0); | ||
|  | 						SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, null); | ||
|  | 					} | ||
|  | 				} | ||
|  | 			case DepthAutoStencilAuto, Depth24Stencil8, Depth32Stencil8: | ||
|  | 				if (SystemImpl.depthTexture == null) { | ||
|  | 					renderBuffer = SystemImpl.gl.createRenderbuffer(); | ||
|  | 					SystemImpl.gl.bindRenderbuffer(GL.RENDERBUFFER, renderBuffer); | ||
|  | 					SystemImpl.gl.renderbufferStorage(GL.RENDERBUFFER, GL.DEPTH_STENCIL, realWidth, realHeight); | ||
|  | 					SystemImpl.gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.RENDERBUFFER, renderBuffer); | ||
|  | 				} | ||
|  | 				else { | ||
|  | 					depthTexture = SystemImpl.gl.createTexture(); | ||
|  | 					SystemImpl.gl.bindTexture(GL.TEXTURE_2D, depthTexture); | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_DEPTH24_STENCIL8 : GL.DEPTH_STENCIL, realWidth, realHeight, 0, | ||
|  | 						GL.DEPTH_STENCIL, SystemImpl.depthTexture.UNSIGNED_INT_24_8_WEBGL, null); | ||
|  | 					SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST); | ||
|  | 					SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST); | ||
|  | 					SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE); | ||
|  | 					SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE); | ||
|  | 					SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, frameBuffer); | ||
|  | 					if (samples > 1 && SystemImpl.gl2) { | ||
|  | 						MSAADepthBuffer = SystemImpl.gl.createRenderbuffer(); | ||
|  | 						SystemImpl.gl.bindRenderbuffer(GL.RENDERBUFFER, MSAADepthBuffer); | ||
|  | 						untyped SystemImpl.gl.renderbufferStorageMultisample(GL.RENDERBUFFER, samples, GL_DEPTH24_STENCIL8, realWidth, realHeight); | ||
|  | 						SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, frameBuffer); | ||
|  | 						SystemImpl.gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.RENDERBUFFER, MSAADepthBuffer); | ||
|  | 						SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, MSAAFrameBuffer); | ||
|  | 					} | ||
|  | 					SystemImpl.gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.TEXTURE_2D, depthTexture, 0); | ||
|  | 				} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function set(stage: Int): Void { | ||
|  | 		SystemImpl.gl.activeTexture(GL.TEXTURE0 + stage); | ||
|  | 		SystemImpl.gl.bindTexture(GL.TEXTURE_2D, texture); | ||
|  | 		if (video != null) | ||
|  | 			SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, video); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function setDepth(stage: Int): Void { | ||
|  | 		SystemImpl.gl.activeTexture(GL.TEXTURE0 + stage); | ||
|  | 		SystemImpl.gl.bindTexture(GL.TEXTURE_2D, depthTexture); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override public function setDepthStencilFrom(image: Image): Void { | ||
|  | 		depthTexture = cast(image, WebGLImage).depthTexture; | ||
|  | 		SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, frameBuffer); | ||
|  | 		SystemImpl.gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.TEXTURE_2D, depthTexture, 0); | ||
|  | 		if (samples > 1 && SystemImpl.gl2) { | ||
|  | 			MSAADepthBuffer = cast(image, WebGLImage).MSAADepthBuffer; | ||
|  | 			SystemImpl.gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, MSAADepthBuffer); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function formatByteSize(format: TextureFormat): Int { | ||
|  | 		return switch (format) { | ||
|  | 			case RGBA32: 4; | ||
|  | 			case L8: 1; | ||
|  | 			case RGBA128: 16; | ||
|  | 			case DEPTH16: 2; | ||
|  | 			case RGBA64: 8; | ||
|  | 			case A32: 4; | ||
|  | 			case A16: 2; | ||
|  | 			default: 4; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function bytesToArray(bytes: Bytes): js.lib.ArrayBufferView { | ||
|  | 		return switch (myFormat) { | ||
|  | 			case RGBA32, L8: | ||
|  | 				new Uint8Array(bytes.getData()); | ||
|  | 			case RGBA128, RGBA64, A32, A16: | ||
|  | 				new Float32Array(bytes.getData()); | ||
|  | 			default: | ||
|  | 				new Uint8Array(bytes.getData()); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public var bytes: Bytes; | ||
|  | 
 | ||
|  | 	override public function lock(level: Int = 0): Bytes { | ||
|  | 		bytes = Bytes.alloc(formatByteSize(myFormat) * width * height); | ||
|  | 		return bytes; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override public function unlock(): Void { | ||
|  | 		data = null; | ||
|  | 		image = null; | ||
|  | 
 | ||
|  | 		if (SystemImpl.gl != null) { | ||
|  | 			texture = SystemImpl.gl.createTexture(); | ||
|  | 			// texture.image = image; | ||
|  | 			SystemImpl.gl.bindTexture(GL.TEXTURE_2D, texture); | ||
|  | 			// Sys.gl.pixelStorei(Sys.gl.UNPACK_FLIP_Y_WEBGL, true); | ||
|  | 
 | ||
|  | 			SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR); | ||
|  | 			SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR); | ||
|  | 			SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE); | ||
|  | 			SystemImpl.gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE); | ||
|  | 
 | ||
|  | 			switch (myFormat) { | ||
|  | 				case L8: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.LUMINANCE, width, height, 0, GL.LUMINANCE, GL.UNSIGNED_BYTE, bytesToArray(bytes)); | ||
|  | 
 | ||
|  | 					if (SystemImpl.ie && SystemImpl.gl.getError() == 1282) { // no LUMINANCE support in IE11 | ||
|  | 						var rgbaBytes = Bytes.alloc(width * height * 4); | ||
|  | 						for (y in 0...height) | ||
|  | 							for (x in 0...width) { | ||
|  | 								var value = bytes.get(y * width + x); | ||
|  | 								rgbaBytes.set(y * width * 4 + x * 4 + 0, value); | ||
|  | 								rgbaBytes.set(y * width * 4 + x * 4 + 1, value); | ||
|  | 								rgbaBytes.set(y * width * 4 + x * 4 + 2, value); | ||
|  | 								rgbaBytes.set(y * width * 4 + x * 4 + 3, 255); | ||
|  | 							} | ||
|  | 						SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, width, height, 0, GL.RGBA, GL.UNSIGNED_BYTE, bytesToArray(rgbaBytes)); | ||
|  | 					} | ||
|  | 				case RGBA128: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_RGBA32F : GL.RGBA, width, height, 0, GL.RGBA, GL.FLOAT, | ||
|  | 						bytesToArray(bytes)); | ||
|  | 				case RGBA64: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_RGBA16F : GL.RGBA, width, height, 0, GL.RGBA, | ||
|  | 						SystemImpl.halfFloat.HALF_FLOAT_OES, bytesToArray(bytes)); | ||
|  | 				case A32: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_R32F : GL.ALPHA, width, height, 0, SystemImpl.gl2 ? GL_RED : GL.ALPHA, | ||
|  | 						GL.FLOAT, bytesToArray(bytes)); | ||
|  | 				case A16: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, SystemImpl.gl2 ? GL_R16F : GL.ALPHA, width, height, 0, SystemImpl.gl2 ? GL_RED : GL.ALPHA, | ||
|  | 						SystemImpl.halfFloat.HALF_FLOAT_OES, bytesToArray(bytes)); | ||
|  | 				case RGBA32: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, width, height, 0, GL.RGBA, GL.UNSIGNED_BYTE, bytesToArray(bytes)); | ||
|  | 				default: | ||
|  | 					SystemImpl.gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, width, height, 0, GL.RGBA, GL.UNSIGNED_BYTE, bytesToArray(bytes)); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			SystemImpl.gl.bindTexture(GL.TEXTURE_2D, null); | ||
|  | 
 | ||
|  | 			if (!readable) { | ||
|  | 				bytes = null; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var pixels: js.lib.ArrayBufferView = null; | ||
|  | 
 | ||
|  | 	override public function getPixels(): Bytes { | ||
|  | 		if (frameBuffer == null) | ||
|  | 			return null; | ||
|  | 		if (pixels == null) { | ||
|  | 			switch (myFormat) { | ||
|  | 				case RGBA128, A32: | ||
|  | 					pixels = new Float32Array(Std.int(formatByteSize(myFormat) / 4) * width * height); | ||
|  | 				case RGBA64, A16: | ||
|  | 					pixels = new Uint16Array(Std.int(formatByteSize(myFormat) / 2) * width * height); | ||
|  | 				case RGBA32, L8: | ||
|  | 					pixels = new Uint8Array(formatByteSize(myFormat) * width * height); | ||
|  | 				default: | ||
|  | 					pixels = new Uint8Array(formatByteSize(myFormat) * width * height); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, frameBuffer); | ||
|  | 		switch (myFormat) { | ||
|  | 			case RGBA128: | ||
|  | 				SystemImpl.gl.readPixels(0, 0, myWidth, myHeight, GL.RGBA, GL.FLOAT, pixels); | ||
|  | 			case RGBA64: | ||
|  | 				SystemImpl.gl.readPixels(0, 0, myWidth, myHeight, GL.RGBA, SystemImpl.halfFloat.HALF_FLOAT_OES, pixels); | ||
|  | 			case RGBA32: | ||
|  | 				SystemImpl.gl.readPixels(0, 0, myWidth, myHeight, GL.RGBA, GL.UNSIGNED_BYTE, pixels); | ||
|  | 			case A32: | ||
|  | 				SystemImpl.gl.readPixels(0, 0, myWidth, myHeight, SystemImpl.gl2 ? GL_RED : GL.ALPHA, GL.FLOAT, pixels); | ||
|  | 			case A16: | ||
|  | 				SystemImpl.gl.readPixels(0, 0, myWidth, myHeight, SystemImpl.gl2 ? GL_RED : GL.ALPHA, SystemImpl.halfFloat.HALF_FLOAT_OES, pixels); | ||
|  | 			case L8: | ||
|  | 				SystemImpl.gl.readPixels(0, 0, myWidth, myHeight, SystemImpl.gl2 ? GL_RED : GL.ALPHA, GL.UNSIGNED_BYTE, pixels); | ||
|  | 			default: | ||
|  | 				SystemImpl.gl.readPixels(0, 0, myWidth, myHeight, GL.RGBA, GL.UNSIGNED_BYTE, pixels); | ||
|  | 		} | ||
|  | 		return Bytes.ofData(pixels.buffer); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override public function unload(): Void { | ||
|  | 		if (texture != null) | ||
|  | 			SystemImpl.gl.deleteTexture(texture); | ||
|  | 		if (depthTexture != null) | ||
|  | 			SystemImpl.gl.deleteTexture(depthTexture); | ||
|  | 		if (frameBuffer != null) | ||
|  | 			SystemImpl.gl.deleteFramebuffer(frameBuffer); | ||
|  | 		if (renderBuffer != null) | ||
|  | 			SystemImpl.gl.deleteRenderbuffer(renderBuffer); | ||
|  | 		if (MSAAFrameBuffer != null) | ||
|  | 			SystemImpl.gl.deleteFramebuffer(MSAAFrameBuffer); | ||
|  | 		if (MSAAColorBuffer != null) | ||
|  | 			SystemImpl.gl.deleteRenderbuffer(MSAAColorBuffer); | ||
|  | 		if (MSAADepthBuffer != null) | ||
|  | 			SystemImpl.gl.deleteRenderbuffer(MSAADepthBuffer); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override public function generateMipmaps(levels: Int): Void { | ||
|  | 		// WebGL requires to generate all mipmaps down to 1x1 size, ignoring levels for now | ||
|  | 		SystemImpl.gl.bindTexture(GL.TEXTURE_2D, texture); | ||
|  | 		SystemImpl.gl.generateMipmap(GL.TEXTURE_2D); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override public function setMipmaps(mipmaps: Array<Image>): Void { | ||
|  | 		// Similar to generateMipmaps, specify all the levels down to 1x1 size | ||
|  | 		SystemImpl.gl.bindTexture(GL.TEXTURE_2D, texture); | ||
|  | 		if (myFormat == TextureFormat.RGBA128) { | ||
|  | 			for (i in 0...mipmaps.length) { | ||
|  | 				SystemImpl.gl.texImage2D(GL.TEXTURE_2D, i + 1, SystemImpl.gl2 ? GL_RGBA32F : GL.RGBA, mipmaps[i].width, mipmaps[i].height, 0, GL.RGBA, | ||
|  | 					GL.FLOAT, cast(mipmaps[i], WebGLImage).image); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		else if (myFormat == TextureFormat.RGBA64) { | ||
|  | 			for (i in 0...mipmaps.length) { | ||
|  | 				SystemImpl.gl.texImage2D(GL.TEXTURE_2D, i + 1, SystemImpl.gl2 ? GL_RGBA16F : GL.RGBA, mipmaps[i].width, mipmaps[i].height, 0, GL.RGBA, | ||
|  | 					SystemImpl.halfFloat.HALF_FLOAT_OES, cast(mipmaps[i], WebGLImage).image); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			for (i in 0...mipmaps.length) { | ||
|  | 				SystemImpl.gl.texImage2D(GL.TEXTURE_2D, i + 1, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, cast(mipmaps[i], WebGLImage).image); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | } |