forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			1254 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			1254 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
|  | package kha.graphics4; | ||
|  | 
 | ||
|  | import kha.arrays.ByteArray; | ||
|  | import kha.arrays.Float32Array; | ||
|  | import kha.Canvas; | ||
|  | import kha.Color; | ||
|  | import kha.FastFloat; | ||
|  | import kha.Font; | ||
|  | import kha.graphics2.ImageScaleQuality; | ||
|  | import kha.Image; | ||
|  | import kha.graphics4.BlendingOperation; | ||
|  | import kha.graphics4.ConstantLocation; | ||
|  | import kha.graphics4.CullMode; | ||
|  | import kha.graphics4.IndexBuffer; | ||
|  | import kha.graphics4.MipMapFilter; | ||
|  | import kha.graphics4.TextureAddressing; | ||
|  | import kha.graphics4.TextureFilter; | ||
|  | import kha.graphics4.TextureFormat; | ||
|  | import kha.graphics4.TextureUnit; | ||
|  | import kha.graphics4.Usage; | ||
|  | import kha.graphics4.VertexBuffer; | ||
|  | import kha.graphics4.VertexData; | ||
|  | import kha.graphics4.VertexStructure; | ||
|  | import kha.math.FastMatrix3; | ||
|  | import kha.math.FastMatrix4; | ||
|  | import kha.math.FastVector2; | ||
|  | import kha.math.Matrix3; | ||
|  | import kha.math.Matrix4; | ||
|  | import kha.math.Vector2; | ||
|  | import kha.Shaders; | ||
|  | import kha.simd.Float32x4; | ||
|  | 
 | ||
|  | class InternalPipeline { | ||
|  | 	public var pipeline: PipelineState; | ||
|  | 	public var projectionLocation: ConstantLocation; | ||
|  | 	public var textureLocation: TextureUnit; | ||
|  | 
 | ||
|  | 	public function new(pipeline: PipelineState, projectionLocation: ConstantLocation, textureLocation: TextureUnit) { | ||
|  | 		this.pipeline = pipeline; | ||
|  | 		this.projectionLocation = projectionLocation; | ||
|  | 		this.textureLocation = textureLocation; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | interface PipelineCache { | ||
|  | 	function get(colorFormat: Array<TextureFormat>, depthStencilFormat: DepthStencilFormat): InternalPipeline; | ||
|  | } | ||
|  | 
 | ||
|  | class SimplePipelineCache implements PipelineCache { | ||
|  | 	var pipeline: InternalPipeline; | ||
|  | 
 | ||
|  | 	public function new(pipeline: PipelineState, texture: Bool) { | ||
|  | 		var projectionLocation: ConstantLocation = null; | ||
|  | 		try { | ||
|  | 			projectionLocation = pipeline.getConstantLocation("projectionMatrix"); | ||
|  | 		} | ||
|  | 		catch (x:Dynamic) { | ||
|  | 			trace(x); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		var textureLocation: TextureUnit = null; | ||
|  | 		if (texture) { | ||
|  | 			try { | ||
|  | 				textureLocation = pipeline.getTextureUnit("tex"); | ||
|  | 			} | ||
|  | 			catch (x:Dynamic) { | ||
|  | 				trace(x); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		this.pipeline = new InternalPipeline(pipeline, projectionLocation, textureLocation); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function get(colorFormats: Array<TextureFormat>, depthStencilFormat: DepthStencilFormat): InternalPipeline { | ||
|  | 		return pipeline; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | class PerFramebufferPipelineCache implements PipelineCache { | ||
|  | 	var pipelines: Array<InternalPipeline> = []; | ||
|  | 
 | ||
|  | 	public function new(pipeline: PipelineState, texture: Bool) { | ||
|  | 		pipeline.compile(); | ||
|  | 
 | ||
|  | 		var projectionLocation: ConstantLocation = null; | ||
|  | 		try { | ||
|  | 			projectionLocation = pipeline.getConstantLocation("projectionMatrix"); | ||
|  | 		} | ||
|  | 		catch (x:Dynamic) { | ||
|  | 			trace(x); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		var textureLocation: TextureUnit = null; | ||
|  | 		if (texture) { | ||
|  | 			try { | ||
|  | 				textureLocation = pipeline.getTextureUnit("tex"); | ||
|  | 			} | ||
|  | 			catch (x:Dynamic) { | ||
|  | 				trace(x); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		pipelines.push(new InternalPipeline(pipeline, projectionLocation, textureLocation)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function get(colorFormats: Array<TextureFormat>, depthStencilFormat: DepthStencilFormat): InternalPipeline { | ||
|  | 		return pipelines[hash(colorFormats, depthStencilFormat)]; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function hash(colorFormats: Array<TextureFormat>, depthStencilFormat: DepthStencilFormat) { | ||
|  | 		return 0; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | class ImageShaderPainter { | ||
|  | 	var projectionMatrix: FastMatrix4; | ||
|  | 
 | ||
|  | 	static var standardImagePipeline: PipelineCache = null; | ||
|  | 	static var structure: VertexStructure = null; | ||
|  | 	static inline var bufferSize: Int = 1500; | ||
|  | 	static inline var vertexSize: Int = 6; | ||
|  | 	static var bufferStart: Int; | ||
|  | 	static var bufferIndex: Int; | ||
|  | 	static var rectVertexBuffer: VertexBuffer; | ||
|  | 	static var rectVertices: ByteArray; | ||
|  | 	static var indexBuffer: IndexBuffer; | ||
|  | 	static var lastTexture: Image; | ||
|  | 
 | ||
|  | 	var bilinear: Bool = false; | ||
|  | 	var bilinearMipmaps: Bool = false; | ||
|  | 	var g: Graphics; | ||
|  | 	var myPipeline: PipelineCache = null; | ||
|  | 
 | ||
|  | 	public var pipeline(get, set): PipelineCache; | ||
|  | 
 | ||
|  | 	public function new(g4: Graphics) { | ||
|  | 		this.g = g4; | ||
|  | 		bufferStart = 0; | ||
|  | 		bufferIndex = 0; | ||
|  | 		initShaders(); | ||
|  | 		myPipeline = standardImagePipeline; | ||
|  | 		initBuffers(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function get_pipeline(): PipelineCache { | ||
|  | 		return myPipeline; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function set_pipeline(pipe: PipelineCache): PipelineCache { | ||
|  | 		myPipeline = pipe != null ? pipe : standardImagePipeline; | ||
|  | 		return myPipeline; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function setProjection(projectionMatrix: FastMatrix4): Void { | ||
|  | 		this.projectionMatrix = projectionMatrix; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function initShaders(): Void { | ||
|  | 		if (structure == null) { | ||
|  | 			structure = Graphics2.createImageVertexStructure(); | ||
|  | 		} | ||
|  | 		if (standardImagePipeline == null) { | ||
|  | 			var pipeline = Graphics2.createImagePipeline(structure); | ||
|  | 			standardImagePipeline = new PerFramebufferPipelineCache(pipeline, true); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function initBuffers(): Void { | ||
|  | 		if (rectVertexBuffer == null) { | ||
|  | 			rectVertexBuffer = new VertexBuffer(bufferSize * 4, structure, Usage.DynamicUsage); | ||
|  | 			rectVertices = rectVertexBuffer.lock(); | ||
|  | 
 | ||
|  | 			indexBuffer = new IndexBuffer(bufferSize * 3 * 2, Usage.StaticUsage); | ||
|  | 			var indices = indexBuffer.lock(); | ||
|  | 			for (i in 0...bufferSize) { | ||
|  | 				indices[i * 3 * 2 + 0] = i * 4 + 0; | ||
|  | 				indices[i * 3 * 2 + 1] = i * 4 + 1; | ||
|  | 				indices[i * 3 * 2 + 2] = i * 4 + 2; | ||
|  | 				indices[i * 3 * 2 + 3] = i * 4 + 0; | ||
|  | 				indices[i * 3 * 2 + 4] = i * 4 + 2; | ||
|  | 				indices[i * 3 * 2 + 5] = i * 4 + 3; | ||
|  | 			} | ||
|  | 			indexBuffer.unlock(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	inline function setRectVertices(bottomleftx: FastFloat, bottomlefty: FastFloat, topleftx: FastFloat, toplefty: FastFloat, toprightx: FastFloat, | ||
|  | 			toprighty: FastFloat, bottomrightx: FastFloat, bottomrighty: FastFloat): Void { | ||
|  | 		var baseIndex: Int = (bufferIndex - bufferStart) * vertexSize * 4 * 4; | ||
|  | 		rectVertices.setFloat32(baseIndex + 0 * 4, bottomleftx); | ||
|  | 		rectVertices.setFloat32(baseIndex + 1 * 4, bottomlefty); | ||
|  | 		rectVertices.setFloat32(baseIndex + 2 * 4, -5.0); | ||
|  | 
 | ||
|  | 		rectVertices.setFloat32(baseIndex + 6 * 4, topleftx); | ||
|  | 		rectVertices.setFloat32(baseIndex + 7 * 4, toplefty); | ||
|  | 		rectVertices.setFloat32(baseIndex + 8 * 4, -5.0); | ||
|  | 
 | ||
|  | 		rectVertices.setFloat32(baseIndex + 12 * 4, toprightx); | ||
|  | 		rectVertices.setFloat32(baseIndex + 13 * 4, toprighty); | ||
|  | 		rectVertices.setFloat32(baseIndex + 14 * 4, -5.0); | ||
|  | 
 | ||
|  | 		rectVertices.setFloat32(baseIndex + 18 * 4, bottomrightx); | ||
|  | 		rectVertices.setFloat32(baseIndex + 19 * 4, bottomrighty); | ||
|  | 		rectVertices.setFloat32(baseIndex + 20 * 4, -5.0); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	inline function setRectTexCoords(left: FastFloat, top: FastFloat, right: FastFloat, bottom: FastFloat): Void { | ||
|  | 		var baseIndex: Int = (bufferIndex - bufferStart) * vertexSize * 4 * 4; | ||
|  | 		rectVertices.setFloat32(baseIndex + 3 * 4, left); | ||
|  | 		rectVertices.setFloat32(baseIndex + 4 * 4, bottom); | ||
|  | 
 | ||
|  | 		rectVertices.setFloat32(baseIndex + 9 * 4, left); | ||
|  | 		rectVertices.setFloat32(baseIndex + 10 * 4, top); | ||
|  | 
 | ||
|  | 		rectVertices.setFloat32(baseIndex + 15 * 4, right); | ||
|  | 		rectVertices.setFloat32(baseIndex + 16 * 4, top); | ||
|  | 
 | ||
|  | 		rectVertices.setFloat32(baseIndex + 21 * 4, right); | ||
|  | 		rectVertices.setFloat32(baseIndex + 22 * 4, bottom); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	inline function setRectColor(r: FastFloat, g: FastFloat, b: FastFloat, a: FastFloat): Void { | ||
|  | 		var baseIndex: Int = (bufferIndex - bufferStart) * vertexSize * 4 * 4; | ||
|  | 		rectVertices.setUint8(baseIndex + 5 * 4 + 0, Std.int(r * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 5 * 4 + 1, Std.int(g * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 5 * 4 + 2, Std.int(b * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 5 * 4 + 3, Std.int(a * 255)); | ||
|  | 
 | ||
|  | 		rectVertices.setUint8(baseIndex + 11 * 4 + 0, Std.int(r * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 11 * 4 + 1, Std.int(g * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 11 * 4 + 2, Std.int(b * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 11 * 4 + 3, Std.int(a * 255)); | ||
|  | 
 | ||
|  | 		rectVertices.setUint8(baseIndex + 17 * 4 + 0, Std.int(r * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 17 * 4 + 1, Std.int(g * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 17 * 4 + 2, Std.int(b * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 17 * 4 + 3, Std.int(a * 255)); | ||
|  | 
 | ||
|  | 		rectVertices.setUint8(baseIndex + 23 * 4 + 0, Std.int(r * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 23 * 4 + 1, Std.int(g * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 23 * 4 + 2, Std.int(b * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 23 * 4 + 3, Std.int(a * 255)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function drawBuffer(end: Bool): Void { | ||
|  | 		if (bufferIndex - bufferStart == 0) { | ||
|  | 			return; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		rectVertexBuffer.unlock((bufferIndex - bufferStart) * 4); | ||
|  | 		var pipeline = myPipeline.get(null, Depth24Stencil8); | ||
|  | 		g.setPipeline(pipeline.pipeline); | ||
|  | 		g.setVertexBuffer(rectVertexBuffer); | ||
|  | 		g.setIndexBuffer(indexBuffer); | ||
|  | 		g.setTexture(pipeline.textureLocation, lastTexture); | ||
|  | 		g.setTextureParameters(pipeline.textureLocation, TextureAddressing.Clamp, TextureAddressing.Clamp, | ||
|  | 			bilinear ? TextureFilter.LinearFilter : TextureFilter.PointFilter, bilinear ? TextureFilter.LinearFilter : TextureFilter.PointFilter, | ||
|  | 			bilinearMipmaps ? MipMapFilter.LinearMipFilter : MipMapFilter.NoMipFilter); | ||
|  | 		g.setMatrix(pipeline.projectionLocation, projectionMatrix); | ||
|  | 
 | ||
|  | 		g.drawIndexedVertices(bufferStart * 2 * 3, (bufferIndex - bufferStart) * 2 * 3); | ||
|  | 
 | ||
|  | 		g.setTexture(pipeline.textureLocation, null); | ||
|  | 
 | ||
|  | 		if (end || (bufferStart + bufferIndex + 1) * 4 >= bufferSize) { | ||
|  | 			bufferStart = 0; | ||
|  | 			bufferIndex = 0; | ||
|  | 			rectVertices = rectVertexBuffer.lock(0); | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			bufferStart = bufferIndex; | ||
|  | 			rectVertices = rectVertexBuffer.lock(bufferStart * 4); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function setBilinearFilter(bilinear: Bool): Void { | ||
|  | 		drawBuffer(false); | ||
|  | 		lastTexture = null; | ||
|  | 		this.bilinear = bilinear; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function setBilinearMipmapFilter(bilinear: Bool): Void { | ||
|  | 		drawBuffer(false); | ||
|  | 		lastTexture = null; | ||
|  | 		this.bilinearMipmaps = bilinear; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public inline function drawImage(img: kha.Image, bottomleftx: FastFloat, bottomlefty: FastFloat, topleftx: FastFloat, toplefty: FastFloat, | ||
|  | 			toprightx: FastFloat, toprighty: FastFloat, bottomrightx: FastFloat, bottomrighty: FastFloat, opacity: FastFloat, color: Color): Void { | ||
|  | 		var tex = img; | ||
|  | 		if (bufferStart + bufferIndex + 1 >= bufferSize || (lastTexture != null && tex != lastTexture)) | ||
|  | 			drawBuffer(false); | ||
|  | 
 | ||
|  | 		setRectColor(color.R, color.G, color.B, color.A * opacity); | ||
|  | 		setRectTexCoords(0, 0, tex.width / tex.realWidth, tex.height / tex.realHeight); | ||
|  | 		setRectVertices(bottomleftx, bottomlefty, topleftx, toplefty, toprightx, toprighty, bottomrightx, bottomrighty); | ||
|  | 
 | ||
|  | 		++bufferIndex; | ||
|  | 		lastTexture = tex; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public inline function drawImage2(img: kha.Image, sx: FastFloat, sy: FastFloat, sw: FastFloat, sh: FastFloat, bottomleftx: FastFloat, | ||
|  | 			bottomlefty: FastFloat, topleftx: FastFloat, toplefty: FastFloat, toprightx: FastFloat, toprighty: FastFloat, bottomrightx: FastFloat, | ||
|  | 			bottomrighty: FastFloat, opacity: FastFloat, color: Color): Void { | ||
|  | 		var tex = img; | ||
|  | 		if (bufferStart + bufferIndex + 1 >= bufferSize || (lastTexture != null && tex != lastTexture)) | ||
|  | 			drawBuffer(false); | ||
|  | 
 | ||
|  | 		setRectTexCoords(sx / tex.realWidth, sy / tex.realHeight, (sx + sw) / tex.realWidth, (sy + sh) / tex.realHeight); | ||
|  | 		setRectColor(color.R, color.G, color.B, color.A * opacity); | ||
|  | 		setRectVertices(bottomleftx, bottomlefty, topleftx, toplefty, toprightx, toprighty, bottomrightx, bottomrighty); | ||
|  | 
 | ||
|  | 		++bufferIndex; | ||
|  | 		lastTexture = tex; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public inline function drawImageScale(img: kha.Image, sx: FastFloat, sy: FastFloat, sw: FastFloat, sh: FastFloat, left: FastFloat, top: FastFloat, | ||
|  | 			right: FastFloat, bottom: FastFloat, opacity: FastFloat, color: Color): Void { | ||
|  | 		var tex = img; | ||
|  | 		if (bufferStart + bufferIndex + 1 >= bufferSize || (lastTexture != null && tex != lastTexture)) | ||
|  | 			drawBuffer(false); | ||
|  | 
 | ||
|  | 		setRectTexCoords(sx / tex.realWidth, sy / tex.realHeight, (sx + sw) / tex.realWidth, (sy + sh) / tex.realHeight); | ||
|  | 		setRectColor(color.R, color.G, color.B, opacity); | ||
|  | 		setRectVertices(left, bottom, left, top, right, top, right, bottom); | ||
|  | 
 | ||
|  | 		++bufferIndex; | ||
|  | 		lastTexture = tex; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function end(): Void { | ||
|  | 		if (bufferIndex > 0) { | ||
|  | 			drawBuffer(true); | ||
|  | 		} | ||
|  | 		lastTexture = null; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | class ColoredShaderPainter { | ||
|  | 	var projectionMatrix: FastMatrix4; | ||
|  | 
 | ||
|  | 	static var standardColorPipeline: PipelineCache = null; | ||
|  | 	static var structure: VertexStructure = null; | ||
|  | 
 | ||
|  | 	static inline var bufferSize: Int = 1000; | ||
|  | 	static var bufferIndex: Int; | ||
|  | 	static var rectVertexBuffer: VertexBuffer; | ||
|  | 	static var rectVertices: Float32Array; | ||
|  | 	static var indexBuffer: IndexBuffer; | ||
|  | 
 | ||
|  | 	static inline var triangleBufferSize: Int = 1000; | ||
|  | 	static var triangleBufferIndex: Int; | ||
|  | 	static var triangleVertexBuffer: VertexBuffer; | ||
|  | 	static var triangleVertices: Float32Array; | ||
|  | 	static var triangleIndexBuffer: IndexBuffer; | ||
|  | 
 | ||
|  | 	var g: Graphics; | ||
|  | 	var myPipeline: PipelineCache = null; | ||
|  | 
 | ||
|  | 	public var pipeline(get, set): PipelineCache; | ||
|  | 
 | ||
|  | 	public function new(g4: Graphics) { | ||
|  | 		this.g = g4; | ||
|  | 		bufferIndex = 0; | ||
|  | 		triangleBufferIndex = 0; | ||
|  | 		initShaders(); | ||
|  | 		myPipeline = standardColorPipeline; | ||
|  | 		initBuffers(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function get_pipeline(): PipelineCache { | ||
|  | 		return myPipeline; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function set_pipeline(pipe: PipelineCache): PipelineCache { | ||
|  | 		myPipeline = pipe != null ? pipe : standardColorPipeline; | ||
|  | 		return myPipeline; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function setProjection(projectionMatrix: FastMatrix4): Void { | ||
|  | 		this.projectionMatrix = projectionMatrix; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function initShaders(): Void { | ||
|  | 		if (structure == null) { | ||
|  | 			structure = Graphics2.createColoredVertexStructure(); | ||
|  | 		} | ||
|  | 		if (standardColorPipeline == null) { | ||
|  | 			var pipeline = Graphics2.createColoredPipeline(structure); | ||
|  | 			standardColorPipeline = new PerFramebufferPipelineCache(pipeline, false); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function initBuffers(): Void { | ||
|  | 		if (rectVertexBuffer == null) { | ||
|  | 			rectVertexBuffer = new VertexBuffer(bufferSize * 4, structure, Usage.DynamicUsage); | ||
|  | 			rectVertices = rectVertexBuffer.lock(); | ||
|  | 
 | ||
|  | 			indexBuffer = new IndexBuffer(bufferSize * 3 * 2, Usage.StaticUsage); | ||
|  | 			var indices = indexBuffer.lock(); | ||
|  | 			for (i in 0...bufferSize) { | ||
|  | 				indices[i * 3 * 2 + 0] = i * 4 + 0; | ||
|  | 				indices[i * 3 * 2 + 1] = i * 4 + 1; | ||
|  | 				indices[i * 3 * 2 + 2] = i * 4 + 2; | ||
|  | 				indices[i * 3 * 2 + 3] = i * 4 + 0; | ||
|  | 				indices[i * 3 * 2 + 4] = i * 4 + 2; | ||
|  | 				indices[i * 3 * 2 + 5] = i * 4 + 3; | ||
|  | 			} | ||
|  | 			indexBuffer.unlock(); | ||
|  | 
 | ||
|  | 			triangleVertexBuffer = new VertexBuffer(triangleBufferSize * 3, structure, Usage.DynamicUsage); | ||
|  | 			triangleVertices = triangleVertexBuffer.lock(); | ||
|  | 
 | ||
|  | 			triangleIndexBuffer = new IndexBuffer(triangleBufferSize * 3, Usage.StaticUsage); | ||
|  | 			var triIndices = triangleIndexBuffer.lock(); | ||
|  | 			for (i in 0...bufferSize) { | ||
|  | 				triIndices[i * 3 + 0] = i * 3 + 0; | ||
|  | 				triIndices[i * 3 + 1] = i * 3 + 1; | ||
|  | 				triIndices[i * 3 + 2] = i * 3 + 2; | ||
|  | 			} | ||
|  | 			triangleIndexBuffer.unlock(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function setRectVertices(bottomleftx: Float, bottomlefty: Float, topleftx: Float, toplefty: Float, toprightx: Float, toprighty: Float, | ||
|  | 			bottomrightx: Float, bottomrighty: Float): Void { | ||
|  | 		var baseIndex: Int = bufferIndex * 4 * 4; | ||
|  | 		rectVertices.set(baseIndex + 0, bottomleftx); | ||
|  | 		rectVertices.set(baseIndex + 1, bottomlefty); | ||
|  | 		rectVertices.set(baseIndex + 2, -5.0); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 4, topleftx); | ||
|  | 		rectVertices.set(baseIndex + 5, toplefty); | ||
|  | 		rectVertices.set(baseIndex + 6, -5.0); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 8, toprightx); | ||
|  | 		rectVertices.set(baseIndex + 9, toprighty); | ||
|  | 		rectVertices.set(baseIndex + 10, -5.0); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 12, bottomrightx); | ||
|  | 		rectVertices.set(baseIndex + 13, bottomrighty); | ||
|  | 		rectVertices.set(baseIndex + 14, -5.0); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function setRectColors(opacity: FastFloat, color: Color): Void { | ||
|  | 		var baseIndex: Int = bufferIndex * 4 * 4 * 4; | ||
|  | 
 | ||
|  | 		var a: FastFloat = opacity * color.A; | ||
|  | 		var r: FastFloat = a * color.R; | ||
|  | 		var g: FastFloat = a * color.G; | ||
|  | 		var b: FastFloat = a * color.B; | ||
|  | 
 | ||
|  | 		rectVertices.setUint8(baseIndex + 3 * 4 + 0, Std.int(r * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 3 * 4 + 1, Std.int(g * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 3 * 4 + 2, Std.int(b * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 3 * 4 + 3, Std.int(a * 255)); | ||
|  | 
 | ||
|  | 		rectVertices.setUint8(baseIndex + 7 * 4 + 0, Std.int(r * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 7 * 4 + 1, Std.int(g * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 7 * 4 + 2, Std.int(b * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 7 * 4 + 3, Std.int(a * 255)); | ||
|  | 
 | ||
|  | 		rectVertices.setUint8(baseIndex + 11 * 4 + 0, Std.int(r * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 11 * 4 + 1, Std.int(g * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 11 * 4 + 2, Std.int(b * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 11 * 4 + 3, Std.int(a * 255)); | ||
|  | 
 | ||
|  | 		rectVertices.setUint8(baseIndex + 15 * 4 + 0, Std.int(r * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 15 * 4 + 1, Std.int(g * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 15 * 4 + 2, Std.int(b * 255)); | ||
|  | 		rectVertices.setUint8(baseIndex + 15 * 4 + 3, Std.int(a * 255)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function setTriVertices(x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float): Void { | ||
|  | 		var baseIndex: Int = triangleBufferIndex * 4 * 3; | ||
|  | 
 | ||
|  | 		triangleVertices.set(baseIndex + 0, x1); | ||
|  | 		triangleVertices.set(baseIndex + 1, y1); | ||
|  | 		triangleVertices.set(baseIndex + 2, -5.0); | ||
|  | 
 | ||
|  | 		triangleVertices.set(baseIndex + 4, x2); | ||
|  | 		triangleVertices.set(baseIndex + 5, y2); | ||
|  | 		triangleVertices.set(baseIndex + 6, -5.0); | ||
|  | 
 | ||
|  | 		triangleVertices.set(baseIndex + 8, x3); | ||
|  | 		triangleVertices.set(baseIndex + 9, y3); | ||
|  | 		triangleVertices.set(baseIndex + 10, -5.0); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function setTriColors(opacity: FastFloat, color: Color): Void { | ||
|  | 		var baseIndex: Int = triangleBufferIndex * 4 * 4 * 3; | ||
|  | 
 | ||
|  | 		var a: FastFloat = opacity * color.A; | ||
|  | 		var r: FastFloat = a * color.R; | ||
|  | 		var g: FastFloat = a * color.G; | ||
|  | 		var b: FastFloat = a * color.B; | ||
|  | 
 | ||
|  | 		triangleVertices.setUint8(baseIndex + 3 * 4 + 0, Std.int(r * 255)); | ||
|  | 		triangleVertices.setUint8(baseIndex + 3 * 4 + 1, Std.int(g * 255)); | ||
|  | 		triangleVertices.setUint8(baseIndex + 3 * 4 + 2, Std.int(b * 255)); | ||
|  | 		triangleVertices.setUint8(baseIndex + 3 * 4 + 3, Std.int(a * 255)); | ||
|  | 
 | ||
|  | 		triangleVertices.setUint8(baseIndex + 7 * 4 + 0, Std.int(r * 255)); | ||
|  | 		triangleVertices.setUint8(baseIndex + 7 * 4 + 1, Std.int(g * 255)); | ||
|  | 		triangleVertices.setUint8(baseIndex + 7 * 4 + 2, Std.int(b * 255)); | ||
|  | 		triangleVertices.setUint8(baseIndex + 7 * 4 + 3, Std.int(a * 255)); | ||
|  | 
 | ||
|  | 		triangleVertices.setUint8(baseIndex + 11 * 4 + 0, Std.int(r * 255)); | ||
|  | 		triangleVertices.setUint8(baseIndex + 11 * 4 + 1, Std.int(g * 255)); | ||
|  | 		triangleVertices.setUint8(baseIndex + 11 * 4 + 2, Std.int(b * 255)); | ||
|  | 		triangleVertices.setUint8(baseIndex + 11 * 4 + 3, Std.int(a * 255)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function drawBuffer(trisDone: Bool): Void { | ||
|  | 		if (bufferIndex == 0) { | ||
|  | 			return; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (!trisDone) | ||
|  | 			endTris(true); | ||
|  | 
 | ||
|  | 		rectVertexBuffer.unlock(bufferIndex * 4); | ||
|  | 		var pipeline = myPipeline.get(null, Depth24Stencil8); | ||
|  | 		g.setPipeline(pipeline.pipeline); | ||
|  | 		g.setVertexBuffer(rectVertexBuffer); | ||
|  | 		g.setIndexBuffer(indexBuffer); | ||
|  | 		g.setMatrix(pipeline.projectionLocation, projectionMatrix); | ||
|  | 
 | ||
|  | 		g.drawIndexedVertices(0, bufferIndex * 2 * 3); | ||
|  | 
 | ||
|  | 		bufferIndex = 0; | ||
|  | 		rectVertices = rectVertexBuffer.lock(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function drawTriBuffer(rectsDone: Bool): Void { | ||
|  | 		if (!rectsDone) | ||
|  | 			endRects(true); | ||
|  | 
 | ||
|  | 		triangleVertexBuffer.unlock(triangleBufferIndex * 3); | ||
|  | 		var pipeline = myPipeline.get(null, Depth24Stencil8); | ||
|  | 		g.setPipeline(pipeline.pipeline); | ||
|  | 		g.setVertexBuffer(triangleVertexBuffer); | ||
|  | 		g.setIndexBuffer(triangleIndexBuffer); | ||
|  | 		g.setMatrix(pipeline.projectionLocation, projectionMatrix); | ||
|  | 
 | ||
|  | 		g.drawIndexedVertices(0, triangleBufferIndex * 3); | ||
|  | 
 | ||
|  | 		triangleBufferIndex = 0; | ||
|  | 		triangleVertices = triangleVertexBuffer.lock(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function fillRect(opacity: FastFloat, color: Color, bottomleftx: Float, bottomlefty: Float, topleftx: Float, toplefty: Float, toprightx: Float, | ||
|  | 			toprighty: Float, bottomrightx: Float, bottomrighty: Float): Void { | ||
|  | 		if (triangleBufferIndex > 0) | ||
|  | 			drawTriBuffer(true); // Flush other buffer for right render order | ||
|  | 
 | ||
|  | 		if (bufferIndex + 1 >= bufferSize) | ||
|  | 			drawBuffer(false); | ||
|  | 
 | ||
|  | 		setRectColors(opacity, color); | ||
|  | 		setRectVertices(bottomleftx, bottomlefty, topleftx, toplefty, toprightx, toprighty, bottomrightx, bottomrighty); | ||
|  | 		++bufferIndex; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function fillTriangle(opacity: FastFloat, color: Color, x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float) { | ||
|  | 		if (bufferIndex > 0) | ||
|  | 			drawBuffer(true); // Flush other buffer for right render order | ||
|  | 
 | ||
|  | 		if (triangleBufferIndex + 1 >= triangleBufferSize) | ||
|  | 			drawTriBuffer(false); | ||
|  | 
 | ||
|  | 		setTriColors(opacity, color); | ||
|  | 		setTriVertices(x1, y1, x2, y2, x3, y3); | ||
|  | 		++triangleBufferIndex; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public inline function endTris(rectsDone: Bool): Void { | ||
|  | 		if (triangleBufferIndex > 0) | ||
|  | 			drawTriBuffer(rectsDone); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public inline function endRects(trisDone: Bool): Void { | ||
|  | 		if (bufferIndex > 0) | ||
|  | 			drawBuffer(trisDone); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public inline function end(): Void { | ||
|  | 		endTris(false); | ||
|  | 		endRects(false); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | class TextShaderPainter { | ||
|  | 	var projectionMatrix: FastMatrix4; | ||
|  | 
 | ||
|  | 	static var standardTextPipeline: PipelineCache = null; | ||
|  | 	static var structure: VertexStructure = null; | ||
|  | 	static inline var bufferSize: Int = 1000; | ||
|  | 	static var bufferIndex: Int; | ||
|  | 	static var rectVertexBuffer: VertexBuffer; | ||
|  | 	static var rectVertices: Float32Array; | ||
|  | 	static var indexBuffer: IndexBuffer; | ||
|  | 
 | ||
|  | 	var font: Kravur; | ||
|  | 
 | ||
|  | 	static var lastTexture: Image; | ||
|  | 
 | ||
|  | 	var g: Graphics; | ||
|  | 	var myPipeline: PipelineCache = null; | ||
|  | 
 | ||
|  | 	public var pipeline(get, set): PipelineCache; | ||
|  | 	public var fontSize: Int; | ||
|  | 
 | ||
|  | 	var bilinear: Bool = false; | ||
|  | 
 | ||
|  | 	public function new(g4: Graphics) { | ||
|  | 		this.g = g4; | ||
|  | 		bufferIndex = 0; | ||
|  | 		initShaders(); | ||
|  | 		myPipeline = standardTextPipeline; | ||
|  | 		initBuffers(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function get_pipeline(): PipelineCache { | ||
|  | 		return myPipeline; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function set_pipeline(pipe: PipelineCache): PipelineCache { | ||
|  | 		myPipeline = pipe != null ? pipe : standardTextPipeline; | ||
|  | 		return myPipeline; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function setProjection(projectionMatrix: FastMatrix4): Void { | ||
|  | 		this.projectionMatrix = projectionMatrix; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function initShaders(): Void { | ||
|  | 		if (structure == null) { | ||
|  | 			structure = Graphics2.createTextVertexStructure(); | ||
|  | 		} | ||
|  | 		if (standardTextPipeline == null) { | ||
|  | 			var pipeline = Graphics2.createTextPipeline(structure); | ||
|  | 			standardTextPipeline = new PerFramebufferPipelineCache(pipeline, true); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function initBuffers(): Void { | ||
|  | 		if (rectVertexBuffer == null) { | ||
|  | 			rectVertexBuffer = new VertexBuffer(bufferSize * 4, structure, Usage.DynamicUsage); | ||
|  | 			rectVertices = rectVertexBuffer.lock(); | ||
|  | 
 | ||
|  | 			indexBuffer = new IndexBuffer(bufferSize * 3 * 2, Usage.StaticUsage); | ||
|  | 			var indices = indexBuffer.lock(); | ||
|  | 			for (i in 0...bufferSize) { | ||
|  | 				indices[i * 3 * 2 + 0] = i * 4 + 0; | ||
|  | 				indices[i * 3 * 2 + 1] = i * 4 + 1; | ||
|  | 				indices[i * 3 * 2 + 2] = i * 4 + 2; | ||
|  | 				indices[i * 3 * 2 + 3] = i * 4 + 0; | ||
|  | 				indices[i * 3 * 2 + 4] = i * 4 + 2; | ||
|  | 				indices[i * 3 * 2 + 5] = i * 4 + 3; | ||
|  | 			} | ||
|  | 			indexBuffer.unlock(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function setRectVertices(bottomleftx: Float, bottomlefty: Float, topleftx: Float, toplefty: Float, toprightx: Float, toprighty: Float, | ||
|  | 			bottomrightx: Float, bottomrighty: Float): Void { | ||
|  | 		var baseIndex: Int = bufferIndex * 9 * 4; | ||
|  | 		rectVertices.set(baseIndex + 0, bottomleftx); | ||
|  | 		rectVertices.set(baseIndex + 1, bottomlefty); | ||
|  | 		rectVertices.set(baseIndex + 2, -5.0); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 9, topleftx); | ||
|  | 		rectVertices.set(baseIndex + 10, toplefty); | ||
|  | 		rectVertices.set(baseIndex + 11, -5.0); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 18, toprightx); | ||
|  | 		rectVertices.set(baseIndex + 19, toprighty); | ||
|  | 		rectVertices.set(baseIndex + 20, -5.0); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 27, bottomrightx); | ||
|  | 		rectVertices.set(baseIndex + 28, bottomrighty); | ||
|  | 		rectVertices.set(baseIndex + 29, -5.0); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function setRectTexCoords(left: Float, top: Float, right: Float, bottom: Float): Void { | ||
|  | 		var baseIndex: Int = bufferIndex * 9 * 4; | ||
|  | 		rectVertices.set(baseIndex + 3, left); | ||
|  | 		rectVertices.set(baseIndex + 4, bottom); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 12, left); | ||
|  | 		rectVertices.set(baseIndex + 13, top); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 21, right); | ||
|  | 		rectVertices.set(baseIndex + 22, top); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 30, right); | ||
|  | 		rectVertices.set(baseIndex + 31, bottom); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function setRectColors(opacity: FastFloat, color: Color): Void { | ||
|  | 		var baseIndex: Int = bufferIndex * 9 * 4; | ||
|  | 		var a: FastFloat = opacity * color.A; | ||
|  | 		rectVertices.set(baseIndex + 5, color.R); | ||
|  | 		rectVertices.set(baseIndex + 6, color.G); | ||
|  | 		rectVertices.set(baseIndex + 7, color.B); | ||
|  | 		rectVertices.set(baseIndex + 8, a); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 14, color.R); | ||
|  | 		rectVertices.set(baseIndex + 15, color.G); | ||
|  | 		rectVertices.set(baseIndex + 16, color.B); | ||
|  | 		rectVertices.set(baseIndex + 17, a); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 23, color.R); | ||
|  | 		rectVertices.set(baseIndex + 24, color.G); | ||
|  | 		rectVertices.set(baseIndex + 25, color.B); | ||
|  | 		rectVertices.set(baseIndex + 26, a); | ||
|  | 
 | ||
|  | 		rectVertices.set(baseIndex + 32, color.R); | ||
|  | 		rectVertices.set(baseIndex + 33, color.G); | ||
|  | 		rectVertices.set(baseIndex + 34, color.B); | ||
|  | 		rectVertices.set(baseIndex + 35, a); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function drawBuffer(): Void { | ||
|  | 		if (bufferIndex == 0) { | ||
|  | 			return; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		rectVertexBuffer.unlock(bufferIndex * 4); | ||
|  | 		var pipeline = myPipeline.get(null, Depth24Stencil8); | ||
|  | 		g.setPipeline(pipeline.pipeline); | ||
|  | 		g.setVertexBuffer(rectVertexBuffer); | ||
|  | 		g.setIndexBuffer(indexBuffer); | ||
|  | 		g.setMatrix(pipeline.projectionLocation, projectionMatrix); | ||
|  | 		g.setTexture(pipeline.textureLocation, lastTexture); | ||
|  | 		g.setTextureParameters(pipeline.textureLocation, TextureAddressing.Clamp, TextureAddressing.Clamp, | ||
|  | 			bilinear ? TextureFilter.LinearFilter : TextureFilter.PointFilter, bilinear ? TextureFilter.LinearFilter : TextureFilter.PointFilter, | ||
|  | 			MipMapFilter.NoMipFilter); | ||
|  | 
 | ||
|  | 		g.drawIndexedVertices(0, bufferIndex * 2 * 3); | ||
|  | 
 | ||
|  | 		g.setTexture(pipeline.textureLocation, null); | ||
|  | 		bufferIndex = 0; | ||
|  | 		rectVertices = rectVertexBuffer.lock(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function setBilinearFilter(bilinear: Bool): Void { | ||
|  | 		end(); | ||
|  | 		this.bilinear = bilinear; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function setFont(font: Font): Void { | ||
|  | 		this.font = cast(font, Kravur); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function findIndex(charCode: Int): Int { | ||
|  | 		// var glyphs = kha.graphics2.Graphics.fontGlyphs; | ||
|  | 		var blocks = Kravur.KravurImage.charBlocks; | ||
|  | 		var offset = 0; | ||
|  | 		for (i in 0...Std.int(blocks.length / 2)) { | ||
|  | 			var start = blocks[i * 2]; | ||
|  | 			var end = blocks[i * 2 + 1]; | ||
|  | 			if (charCode >= start && charCode <= end) | ||
|  | 				return offset + charCode - start; | ||
|  | 			offset += end - start + 1; | ||
|  | 		} | ||
|  | 		return 0; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var bakedQuadCache = new kha.Kravur.AlignedQuad(); | ||
|  | 
 | ||
|  | 	public function drawString(text: String, opacity: FastFloat, color: Color, x: Float, y: Float, transformation: FastMatrix3): Void { | ||
|  | 		var font = this.font._get(fontSize); | ||
|  | 		var tex = font.getTexture(); | ||
|  | 		if (lastTexture != null && tex != lastTexture) | ||
|  | 			drawBuffer(); | ||
|  | 		lastTexture = tex; | ||
|  | 
 | ||
|  | 		var xpos = x; | ||
|  | 		var ypos = y; | ||
|  | 		for (i in 0...text.length) { | ||
|  | 			var charCode = StringTools.fastCodeAt(text, i); | ||
|  | 			var q = font.getBakedQuad(bakedQuadCache, findIndex(charCode), xpos, ypos); | ||
|  | 			if (q != null) { | ||
|  | 				if (bufferIndex + 1 >= bufferSize) | ||
|  | 					drawBuffer(); | ||
|  | 				setRectColors(opacity, color); | ||
|  | 				setRectTexCoords(q.s0 * tex.width / tex.realWidth, q.t0 * tex.height / tex.realHeight, q.s1 * tex.width / tex.realWidth, | ||
|  | 					q.t1 * tex.height / tex.realHeight); | ||
|  | 				var p0 = transformation.multvec(new FastVector2(q.x0, q.y1)); // bottom-left | ||
|  | 				var p1 = transformation.multvec(new FastVector2(q.x0, q.y0)); // top-left | ||
|  | 				var p2 = transformation.multvec(new FastVector2(q.x1, q.y0)); // top-right | ||
|  | 				var p3 = transformation.multvec(new FastVector2(q.x1, q.y1)); // bottom-right | ||
|  | 				setRectVertices(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); | ||
|  | 				xpos += q.xadvance; | ||
|  | 				++bufferIndex; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function drawCharacters(text: Array<Int>, start: Int, length: Int, opacity: FastFloat, color: Color, x: Float, y: Float, | ||
|  | 			transformation: FastMatrix3): Void { | ||
|  | 		var font = this.font._get(fontSize); | ||
|  | 		var tex = font.getTexture(); | ||
|  | 		if (lastTexture != null && tex != lastTexture) | ||
|  | 			drawBuffer(); | ||
|  | 		lastTexture = tex; | ||
|  | 
 | ||
|  | 		var xpos = x; | ||
|  | 		var ypos = y; | ||
|  | 		for (i in start...start + length) { | ||
|  | 			var q = font.getBakedQuad(bakedQuadCache, findIndex(text[i]), xpos, ypos); | ||
|  | 			if (q != null) { | ||
|  | 				if (bufferIndex + 1 >= bufferSize) | ||
|  | 					drawBuffer(); | ||
|  | 				setRectColors(opacity, color); | ||
|  | 				setRectTexCoords(q.s0 * tex.width / tex.realWidth, q.t0 * tex.height / tex.realHeight, q.s1 * tex.width / tex.realWidth, | ||
|  | 					q.t1 * tex.height / tex.realHeight); | ||
|  | 				var p0 = transformation.multvec(new FastVector2(q.x0, q.y1)); // bottom-left | ||
|  | 				var p1 = transformation.multvec(new FastVector2(q.x0, q.y0)); // top-left | ||
|  | 				var p2 = transformation.multvec(new FastVector2(q.x1, q.y0)); // top-right | ||
|  | 				var p3 = transformation.multvec(new FastVector2(q.x1, q.y1)); // bottom-right | ||
|  | 				setRectVertices(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); | ||
|  | 				xpos += q.xadvance; | ||
|  | 				++bufferIndex; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function end(): Void { | ||
|  | 		if (bufferIndex > 0) | ||
|  | 			drawBuffer(); | ||
|  | 		lastTexture = null; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | class Graphics2 extends kha.graphics2.Graphics { | ||
|  | 	var myColor: Color; | ||
|  | 	var myFont: Font; | ||
|  | 	var projectionMatrix: FastMatrix4; | ||
|  | 
 | ||
|  | 	public var imagePainter: ImageShaderPainter; | ||
|  | 
 | ||
|  | 	var coloredPainter: ColoredShaderPainter; | ||
|  | 	var textPainter: TextShaderPainter; | ||
|  | 
 | ||
|  | 	public static var videoPipeline: PipelineState; | ||
|  | 
 | ||
|  | 	var canvas: Canvas; | ||
|  | 	var g: Graphics; | ||
|  | 
 | ||
|  | 	static var current: Graphics2 = null; | ||
|  | 
 | ||
|  | 	public function new(canvas: Canvas) { | ||
|  | 		super(); | ||
|  | 		color = Color.White; | ||
|  | 		this.canvas = canvas; | ||
|  | 		g = canvas.g4; | ||
|  | 		imagePainter = new ImageShaderPainter(g); | ||
|  | 		coloredPainter = new ColoredShaderPainter(g); | ||
|  | 		textPainter = new TextShaderPainter(g); | ||
|  | 		textPainter.fontSize = fontSize; | ||
|  | 		projectionMatrix = FastMatrix4.identity(); | ||
|  | 		setProjection(); | ||
|  | 
 | ||
|  | 		if (videoPipeline == null) { | ||
|  | 			videoPipeline = createImagePipeline(createImageVertexStructure()); | ||
|  | 			videoPipeline.fragmentShader = Shaders.painter_video_frag; | ||
|  | 			videoPipeline.vertexShader = Shaders.painter_video_vert; | ||
|  | 			videoPipeline.compile(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	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; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function setProjection(): Void { | ||
|  | 		var width = canvas.width; | ||
|  | 		var height = canvas.height; | ||
|  | 		if (Std.isOfType(canvas, Framebuffer)) { | ||
|  | 			projectionMatrix.setFrom(FastMatrix4.orthogonalProjection(0, width, height, 0, 0.1, 1000)); | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			if (!Image.nonPow2Supported) { | ||
|  | 				width = upperPowerOfTwo(width); | ||
|  | 				height = upperPowerOfTwo(height); | ||
|  | 			} | ||
|  | 			if (Image.renderTargetsInvertedY()) { | ||
|  | 				projectionMatrix.setFrom(FastMatrix4.orthogonalProjection(0, width, 0, height, 0.1, 1000)); | ||
|  | 			} | ||
|  | 			else { | ||
|  | 				projectionMatrix.setFrom(FastMatrix4.orthogonalProjection(0, width, height, 0, 0.1, 1000)); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		imagePainter.setProjection(projectionMatrix); | ||
|  | 		coloredPainter.setProjection(projectionMatrix); | ||
|  | 		textPainter.setProjection(projectionMatrix); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	#if cpp | ||
|  | 	public override function drawImage(img: kha.Image, x: FastFloat, y: FastFloat): Void { | ||
|  | 		coloredPainter.end(); | ||
|  | 		textPainter.end(); | ||
|  | 		var xw: FastFloat = x + img.width; | ||
|  | 		var yh: FastFloat = y + img.height; | ||
|  | 
 | ||
|  | 		var xx = Float32x4.loadFast(x, x, xw, xw); | ||
|  | 		var yy = Float32x4.loadFast(yh, y, y, yh); | ||
|  | 
 | ||
|  | 		var _00 = Float32x4.loadAllFast(transformation._00); | ||
|  | 		var _01 = Float32x4.loadAllFast(transformation._01); | ||
|  | 		var _02 = Float32x4.loadAllFast(transformation._02); | ||
|  | 		var _10 = Float32x4.loadAllFast(transformation._10); | ||
|  | 		var _11 = Float32x4.loadAllFast(transformation._11); | ||
|  | 		var _12 = Float32x4.loadAllFast(transformation._12); | ||
|  | 		var _20 = Float32x4.loadAllFast(transformation._20); | ||
|  | 		var _21 = Float32x4.loadAllFast(transformation._21); | ||
|  | 		var _22 = Float32x4.loadAllFast(transformation._22); | ||
|  | 
 | ||
|  | 		// matrix multiply | ||
|  | 		var w = Float32x4.add(Float32x4.add(Float32x4.mul(_02, xx), Float32x4.mul(_12, yy)), _22); | ||
|  | 		var px = Float32x4.div(Float32x4.add(Float32x4.add(Float32x4.mul(_00, xx), Float32x4.mul(_10, yy)), _20), w); | ||
|  | 		var py = Float32x4.div(Float32x4.add(Float32x4.add(Float32x4.mul(_01, xx), Float32x4.mul(_11, yy)), _21), w); | ||
|  | 
 | ||
|  | 		imagePainter.drawImage(img, Float32x4.get(px, 0), Float32x4.get(py, 0), Float32x4.get(px, 1), Float32x4.get(py, 1), Float32x4.get(px, 2), | ||
|  | 			Float32x4.get(py, 2), Float32x4.get(px, 3), Float32x4.get(py, 3), opacity, this.color); | ||
|  | 	} | ||
|  | 	#else | ||
|  | 	public override function drawImage(img: kha.Image, x: FastFloat, y: FastFloat): Void { | ||
|  | 		coloredPainter.end(); | ||
|  | 		textPainter.end(); | ||
|  | 		var xw: FastFloat = x + img.width; | ||
|  | 		var yh: FastFloat = y + img.height; | ||
|  | 		var p1 = transformation.multvec(new FastVector2(x, yh)); | ||
|  | 		var p2 = transformation.multvec(new FastVector2(x, y)); | ||
|  | 		var p3 = transformation.multvec(new FastVector2(xw, y)); | ||
|  | 		var p4 = transformation.multvec(new FastVector2(xw, yh)); | ||
|  | 		imagePainter.drawImage(img, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, opacity, this.color); | ||
|  | 	} | ||
|  | 	#end | ||
|  | 
 | ||
|  | 	public override function drawScaledSubImage(img: kha.Image, sx: FastFloat, sy: FastFloat, sw: FastFloat, sh: FastFloat, dx: FastFloat, dy: FastFloat, | ||
|  | 			dw: FastFloat, dh: FastFloat): Void { | ||
|  | 		coloredPainter.end(); | ||
|  | 		textPainter.end(); | ||
|  | 		var p1 = transformation.multvec(new FastVector2(dx, dy + dh)); | ||
|  | 		var p2 = transformation.multvec(new FastVector2(dx, dy)); | ||
|  | 		var p3 = transformation.multvec(new FastVector2(dx + dw, dy)); | ||
|  | 		var p4 = transformation.multvec(new FastVector2(dx + dw, dy + dh)); | ||
|  | 		imagePainter.drawImage2(img, sx, sy, sw, sh, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, opacity, this.color); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function get_color(): Color { | ||
|  | 		return myColor; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function set_color(color: Color): Color { | ||
|  | 		return myColor = color; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public override function drawRect(x: Float, y: Float, width: Float, height: Float, strength: Float = 1.0): Void { | ||
|  | 		imagePainter.end(); | ||
|  | 		textPainter.end(); | ||
|  | 
 | ||
|  | 		var p1 = transformation.multvec(new FastVector2(x - strength / 2, y + strength / 2)); // bottom-left | ||
|  | 		var p2 = transformation.multvec(new FastVector2(x - strength / 2, y - strength / 2)); // top-left | ||
|  | 		var p3 = transformation.multvec(new FastVector2(x + width + strength / 2, y - strength / 2)); // top-right | ||
|  | 		var p4 = transformation.multvec(new FastVector2(x + width + strength / 2, y + strength / 2)); // bottom-right | ||
|  | 		coloredPainter.fillRect(opacity, color, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y); // top | ||
|  | 
 | ||
|  | 		p1.setFrom(transformation.multvec(new FastVector2(x - strength / 2, y + height - strength / 2))); | ||
|  | 		p2.setFrom(transformation.multvec(new FastVector2(x - strength / 2, y + strength / 2))); | ||
|  | 		p3.setFrom(transformation.multvec(new FastVector2(x + strength / 2, y + strength / 2))); | ||
|  | 		p4.setFrom(transformation.multvec(new FastVector2(x + strength / 2, y + height - strength / 2))); | ||
|  | 		coloredPainter.fillRect(opacity, color, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y); // left | ||
|  | 
 | ||
|  | 		p1.setFrom(transformation.multvec(new FastVector2(x - strength / 2, y + height + strength / 2))); | ||
|  | 		p2.setFrom(transformation.multvec(new FastVector2(x - strength / 2, y + height - strength / 2))); | ||
|  | 		p3.setFrom(transformation.multvec(new FastVector2(x + width + strength / 2, y + height - strength / 2))); | ||
|  | 		p4.setFrom(transformation.multvec(new FastVector2(x + width + strength / 2, y + height + strength / 2))); | ||
|  | 		coloredPainter.fillRect(opacity, color, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y); // bottom | ||
|  | 
 | ||
|  | 		p1.setFrom(transformation.multvec(new FastVector2(x + width - strength / 2, y + height - strength / 2))); | ||
|  | 		p2.setFrom(transformation.multvec(new FastVector2(x + width - strength / 2, y + strength / 2))); | ||
|  | 		p3.setFrom(transformation.multvec(new FastVector2(x + width + strength / 2, y + strength / 2))); | ||
|  | 		p4.setFrom(transformation.multvec(new FastVector2(x + width + strength / 2, y + height - strength / 2))); | ||
|  | 		coloredPainter.fillRect(opacity, color, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y); // right | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public override function fillRect(x: Float, y: Float, width: Float, height: Float): Void { | ||
|  | 		imagePainter.end(); | ||
|  | 		textPainter.end(); | ||
|  | 
 | ||
|  | 		var p1 = transformation.multvec(new FastVector2(x, y + height)); | ||
|  | 		var p2 = transformation.multvec(new FastVector2(x, y)); | ||
|  | 		var p3 = transformation.multvec(new FastVector2(x + width, y)); | ||
|  | 		var p4 = transformation.multvec(new FastVector2(x + width, y + height)); | ||
|  | 		coloredPainter.fillRect(opacity, color, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public override function drawString(text: String, x: Float, y: Float): Void { | ||
|  | 		imagePainter.end(); | ||
|  | 		coloredPainter.end(); | ||
|  | 
 | ||
|  | 		textPainter.drawString(text, opacity, color, x, y, transformation); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public override function drawCharacters(text: Array<Int>, start: Int, length: Int, x: Float, y: Float): Void { | ||
|  | 		imagePainter.end(); | ||
|  | 		coloredPainter.end(); | ||
|  | 
 | ||
|  | 		textPainter.drawCharacters(text, start, length, opacity, color, x, y, transformation); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function get_font(): Font { | ||
|  | 		return myFont; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function set_font(font: Font): Font { | ||
|  | 		textPainter.setFont(font); | ||
|  | 		return myFont = font; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function set_fontSize(value: Int): Int { | ||
|  | 		return super.fontSize = textPainter.fontSize = value; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public override function drawLine(x1: Float, y1: Float, x2: Float, y2: Float, strength: Float = 1.0): Void { | ||
|  | 		imagePainter.end(); | ||
|  | 		textPainter.end(); | ||
|  | 
 | ||
|  | 		var vec = new FastVector2(); | ||
|  | 		if (y2 == y1) | ||
|  | 			vec.setFrom(new FastVector2(0, -1)); | ||
|  | 		else | ||
|  | 			vec.setFrom(new FastVector2(1, -(x2 - x1) / (y2 - y1))); | ||
|  | 		vec.length = strength; | ||
|  | 		var p1 = new FastVector2(x1 + 0.5 * vec.x, y1 + 0.5 * vec.y); | ||
|  | 		var p2 = new FastVector2(x2 + 0.5 * vec.x, y2 + 0.5 * vec.y); | ||
|  | 		var p3 = p1.sub(vec); | ||
|  | 		var p4 = p2.sub(vec); | ||
|  | 
 | ||
|  | 		p1.setFrom(transformation.multvec(p1)); | ||
|  | 		p2.setFrom(transformation.multvec(p2)); | ||
|  | 		p3.setFrom(transformation.multvec(p3)); | ||
|  | 		p4.setFrom(transformation.multvec(p4)); | ||
|  | 
 | ||
|  | 		coloredPainter.fillTriangle(opacity, color, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); | ||
|  | 		coloredPainter.fillTriangle(opacity, color, p3.x, p3.y, p2.x, p2.y, p4.x, p4.y); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public override function fillTriangle(x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float) { | ||
|  | 		imagePainter.end(); | ||
|  | 		textPainter.end(); | ||
|  | 
 | ||
|  | 		var p1 = transformation.multvec(new FastVector2(x1, y1)); | ||
|  | 		var p2 = transformation.multvec(new FastVector2(x2, y2)); | ||
|  | 		var p3 = transformation.multvec(new FastVector2(x3, y3)); | ||
|  | 		coloredPainter.fillTriangle(opacity, color, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var myImageScaleQuality: ImageScaleQuality = ImageScaleQuality.Low; | ||
|  | 
 | ||
|  | 	override function get_imageScaleQuality(): ImageScaleQuality { | ||
|  | 		return myImageScaleQuality; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function set_imageScaleQuality(value: ImageScaleQuality): ImageScaleQuality { | ||
|  | 		if (value == myImageScaleQuality) { | ||
|  | 			return value; | ||
|  | 		} | ||
|  | 		imagePainter.setBilinearFilter(value == ImageScaleQuality.High); | ||
|  | 		textPainter.setBilinearFilter(value == ImageScaleQuality.High); | ||
|  | 		return myImageScaleQuality = value; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var myMipmapScaleQuality: ImageScaleQuality = ImageScaleQuality.Low; | ||
|  | 
 | ||
|  | 	override function get_mipmapScaleQuality(): ImageScaleQuality { | ||
|  | 		return myMipmapScaleQuality; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override function set_mipmapScaleQuality(value: ImageScaleQuality): ImageScaleQuality { | ||
|  | 		imagePainter.setBilinearMipmapFilter(value == ImageScaleQuality.High); | ||
|  | 		// textPainter.setBilinearMipmapFilter(value == ImageScaleQuality.High); // TODO (DK) implement for fonts as well? | ||
|  | 		return myMipmapScaleQuality = value; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var pipelineCache = new Map<PipelineState, PipelineCache>(); | ||
|  | 	var lastPipeline: PipelineState = null; | ||
|  | 
 | ||
|  | 	override function setPipeline(pipeline: PipelineState): Void { | ||
|  | 		if (pipeline == lastPipeline) { | ||
|  | 			return; | ||
|  | 		} | ||
|  | 		lastPipeline = pipeline; | ||
|  | 		flush(); | ||
|  | 		if (pipeline == null) { | ||
|  | 			imagePainter.pipeline = null; | ||
|  | 			coloredPainter.pipeline = null; | ||
|  | 			textPainter.pipeline = null; | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			var cache = pipelineCache[pipeline]; | ||
|  | 			if (cache == null) { | ||
|  | 				cache = new SimplePipelineCache(pipeline, true); | ||
|  | 				pipelineCache[pipeline] = cache; | ||
|  | 			} | ||
|  | 			imagePainter.pipeline = cache; | ||
|  | 			coloredPainter.pipeline = cache; | ||
|  | 			textPainter.pipeline = cache; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var scissorEnabled = false; | ||
|  | 	var scissorX: Int = -1; | ||
|  | 	var scissorY: Int = -1; | ||
|  | 	var scissorW: Int = -1; | ||
|  | 	var scissorH: Int = -1; | ||
|  | 
 | ||
|  | 	override public function scissor(x: Int, y: Int, width: Int, height: Int): Void { | ||
|  | 		// if (!scissorEnabled || x != scissorX || y != scissorY || width != scissorW || height != scissorH) { | ||
|  | 		scissorEnabled = true; | ||
|  | 		scissorX = x; | ||
|  | 		scissorY = y; | ||
|  | 		scissorW = width; | ||
|  | 		scissorH = height; | ||
|  | 		flush(); | ||
|  | 		g.scissor(x, y, width, height); | ||
|  | 		// } | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override public function disableScissor(): Void { | ||
|  | 		// if (scissorEnabled) { | ||
|  | 		scissorEnabled = false; | ||
|  | 		flush(); | ||
|  | 		g.disableScissor(); | ||
|  | 		// } | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override public function begin(clear: Bool = true, clearColor: Color = null): Void { | ||
|  | 		if (current == null) { | ||
|  | 			current = this; | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			throw "End before you begin"; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		g.begin(); | ||
|  | 		if (clear) | ||
|  | 			this.clear(clearColor); | ||
|  | 		setProjection(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	override public function clear(color: Color = null): Void { | ||
|  | 		flush(); | ||
|  | 		g.clear(color == null ? Color.Black : color); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public override function flush(): Void { | ||
|  | 		imagePainter.end(); | ||
|  | 		textPainter.end(); | ||
|  | 		coloredPainter.end(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public override function end(): Void { | ||
|  | 		flush(); | ||
|  | 		g.end(); | ||
|  | 
 | ||
|  | 		if (current == this) { | ||
|  | 			current = null; | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			throw "Begin before you end"; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function drawVideoInternal(video: kha.Video, x: Float, y: Float, width: Float, height: Float): Void {} | ||
|  | 
 | ||
|  | 	override public function drawVideo(video: kha.Video, x: Float, y: Float, width: Float, height: Float): Void { | ||
|  | 		setPipeline(videoPipeline); | ||
|  | 		drawVideoInternal(video, x, y, width, height); | ||
|  | 		setPipeline(null); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function createImageVertexStructure(): VertexStructure { | ||
|  | 		var structure = new VertexStructure(); | ||
|  | 		structure.add("vertexPosition", VertexData.Float32_3X); | ||
|  | 		structure.add("vertexUV", VertexData.Float32_2X); | ||
|  | 		structure.add("vertexColor", VertexData.UInt8_4X_Normalized); | ||
|  | 		return structure; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function createImagePipeline(structure: VertexStructure): PipelineState { | ||
|  | 		var shaderPipeline = new PipelineState(); | ||
|  | 		shaderPipeline.fragmentShader = Shaders.painter_image_frag; | ||
|  | 		shaderPipeline.vertexShader = Shaders.painter_image_vert; | ||
|  | 		shaderPipeline.inputLayout = [structure]; | ||
|  | 		shaderPipeline.blendSource = BlendingFactor.BlendOne; | ||
|  | 		shaderPipeline.blendDestination = BlendingFactor.InverseSourceAlpha; | ||
|  | 		shaderPipeline.alphaBlendSource = BlendingFactor.BlendOne; | ||
|  | 		shaderPipeline.alphaBlendDestination = BlendingFactor.InverseSourceAlpha; | ||
|  | 		return shaderPipeline; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function createColoredVertexStructure(): VertexStructure { | ||
|  | 		var structure = new VertexStructure(); | ||
|  | 		structure.add("vertexPosition", VertexData.Float32_3X); | ||
|  | 		structure.add("vertexColor", VertexData.UInt8_4X_Normalized); | ||
|  | 		return structure; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function createColoredPipeline(structure: VertexStructure): PipelineState { | ||
|  | 		var shaderPipeline = new PipelineState(); | ||
|  | 		shaderPipeline.fragmentShader = Shaders.painter_colored_frag; | ||
|  | 		shaderPipeline.vertexShader = Shaders.painter_colored_vert; | ||
|  | 		shaderPipeline.inputLayout = [structure]; | ||
|  | 		shaderPipeline.blendSource = BlendingFactor.BlendOne; | ||
|  | 		shaderPipeline.blendDestination = BlendingFactor.InverseSourceAlpha; | ||
|  | 		shaderPipeline.alphaBlendSource = BlendingFactor.BlendOne; | ||
|  | 		shaderPipeline.alphaBlendDestination = BlendingFactor.InverseSourceAlpha; | ||
|  | 		return shaderPipeline; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function createTextVertexStructure(): VertexStructure { | ||
|  | 		var structure = new VertexStructure(); | ||
|  | 		structure.add("vertexPosition", VertexData.Float32_3X); | ||
|  | 		structure.add("vertexUV", VertexData.Float32_2X); | ||
|  | 		structure.add("vertexColor", VertexData.Float32_4X); | ||
|  | 		return structure; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function createTextPipeline(structure: VertexStructure): PipelineState { | ||
|  | 		var shaderPipeline = new PipelineState(); | ||
|  | 		shaderPipeline.fragmentShader = Shaders.painter_text_frag; | ||
|  | 		shaderPipeline.vertexShader = Shaders.painter_text_vert; | ||
|  | 		shaderPipeline.inputLayout = [structure]; | ||
|  | 		shaderPipeline.blendSource = BlendingFactor.SourceAlpha; | ||
|  | 		shaderPipeline.blendDestination = BlendingFactor.InverseSourceAlpha; | ||
|  | 		shaderPipeline.alphaBlendSource = BlendingFactor.SourceAlpha; | ||
|  | 		shaderPipeline.alphaBlendDestination = BlendingFactor.InverseSourceAlpha; | ||
|  | 		return shaderPipeline; | ||
|  | 	} | ||
|  | } |