Texture Projection Material Alternativa3d

David Jones
@david3jones
avatar-davidejones

I’ve been experimenting some more with the alternativa3d materials in the hope to create some interesting demos. I’ve been trying to help out in the creation of a reflection plane and water type materials. In my experiments I’ve created a type of texture projection material check it out. texture projection Here is the material

package alternativa.engine3d.materials {
    
    	import alternativa.engine3d.alternativa3d;
    	import alternativa.engine3d.core.Camera3D;
    	import alternativa.engine3d.core.DrawUnit;
    	import alternativa.engine3d.core.Light3D;
    	import alternativa.engine3d.core.Object3D;
    	import alternativa.engine3d.core.Renderer;
    	import alternativa.engine3d.core.VertexAttributes;
    	import alternativa.engine3d.materials.compiler.Linker;
    	import alternativa.engine3d.materials.compiler.Procedure;
    	import alternativa.engine3d.materials.compiler.VariableType;
    	import alternativa.engine3d.objects.Surface;
    	import alternativa.engine3d.resources.Geometry;
    	import alternativa.engine3d.resources.TextureResource;
    
    	import avmplus.getQualifiedClassName;
    
    	import flash.display3D.Context3D;
    	import flash.display3D.Context3DBlendFactor;
    	import flash.display3D.Context3DProgramType;
    	import flash.display3D.VertexBuffer3D;
    	import flash.utils.Dictionary;
    	import flash.utils.getDefinitionByName;
    
    	use namespace alternativa3d;
    
    	public class TextureProjectionMaterial extends Material {
    
    		private static var caches:Dictionary = new Dictionary(true);
    		private var cachedContext3D:Context3D;
    		private var programsCache:Dictionary;
    
    		// Vertex Procedure
    		static alternativa3d const getProjectionVProcedure:Procedure = new Procedure([
    			"#a0=aPosition",
    			"m44 t0.xyzw a0.xyzw c0.xyzw",
    			"mov v0.xyzw t0.xyzw",
    			"mov o0.xyzw t0.xyzw"
    		], "getProjectionVProcedure");
    		
    		// Fragment procedure
    		static alternativa3d const getProjectionSProcedure:Procedure = new Procedure([
    			"#c0=cAmount",
    			"#c1=cThresholdAlpha",
    			"#s0=sDiffuse",
    			"div t0.xy v0.xyzw v0.wwxw",
    			"mul t0.zw t0.xxxy c0.xxxy",
    			"add t0.xy t0.zwzw c0.zzzz",
    			"tex t1,t0.xyzw,s0 <2d,repeat,linear>",
    			"mul t1.w, t1.w, c1.w",
    			"mov o0, t1"
    		], "getProjectionSProcedure");		
    		
    		static alternativa3d const getProjectionOpacitySProcedure:Procedure = new Procedure([
    			"#c0=cAmount",
    			"#c1=cThresholdAlpha",
    			"#s0=sDiffuse",
    			"#s1=sOpacity",
    			"div t0.xy v0.xyzw v0.wwxw",
    			"mul t0.zw t0.xxxy c0.xxxy",
    			"add t0.xy t0.zwzw c0.zzzz",
    			"tex t1, t0.xyzw, s0 <2d, linear,repeat, miplinear>",
    			"tex t2, t0.xyzw, s1 <2d, linear,repeat, miplinear>",
    			"mul t1.w, t2.x, c1.w",
    			"mov o0, t1"
    		], "getProjectionOpacitySProcedure");
    		
    		static alternativa3d const thresholdOpaqueAlphaProcedure:Procedure = new Procedure([
    			"#c0=cThresholdAlpha",
    			"sub t0.w, i0.w, c0.x",
    			"kil t0.w",
    			"mov o0, i0"
    		], "thresholdOpaqueAlphaProcedure");
    
    		static alternativa3d const thresholdTransparentAlphaProcedure:Procedure = new Procedure([
    			"#c0=cThresholdAlpha",
    			"slt t0.w, i0.w, c0.x",
    			"mul i0.w, t0.w, i0.w",
    			"mov o0, i0"
    		], "thresholdTransparentAlphaProcedure");
    
    
    		public var diffuseMap:TextureResource;
    		public var opacityMap:TextureResource;
    		public var transparentPass:Boolean = true;
    		public var opaquePass:Boolean = true;
    		public var alphaThreshold:Number = 0;
    		public var alpha:Number = 1;
    		
    		/**
    		 * Creates a new TextureMaterial instance.
    		 *
    		 * @param diffuseMap Diffuse map.
    		 * @param alpha Transparency.
    		 */
    		public function TextureProjectionMaterial(diffuseMap:TextureResource = null, opacityMap:TextureResource = null, alpha:Number = 1) {
    			this.diffuseMap = diffuseMap;
    			this.opacityMap = opacityMap;
    			this.alpha = alpha;
    		}
    
    		/**
    		 * @private
    		 */
    		override alternativa3d function fillResources(resources:Dictionary, resourceType:Class):void {
    			super.fillResources(resources, resourceType);
    			if (diffuseMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(diffuseMap)) as Class, resourceType)) {
    				resources[diffuseMap] = true;
    			}
    			if (opacityMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(opacityMap)) as Class, resourceType)) {
    				resources[opacityMap] = true;
    			}
    		}
    
    		/**
    		 * @param object
    		 * @param programs
    		 * @param camera
    		 * @param opacityMap
    		 * @param alphaTest 0 - disabled, 1 - opaque, 2 - contours
    		 * @return
    		 */
    		private function getProgram(object:Object3D, programs:Vector., camera:Camera3D, opacityMap:TextureResource, alphaTest:int):TextureProjectionMaterialProgram {
    			var key:int = (opacityMap != null ? 3 : 0) + alphaTest;
    			var program:TextureProjectionMaterialProgram = programs[key];
    			if (program == null) {
    				// Make program
    				// Vertex shader
    				var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
    				
    				var positionVar:String = "aPosition";
    				vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
    				getProjectionVProcedure.assignVariableName(VariableType.CONSTANT, 0, "cProjMatrix", 4);
    				
    				if (object.transformProcedure != null) {
    					positionVar = appendPositionTransformProcedure(object.transformProcedure, vertexLinker);
    				}
    				vertexLinker.addProcedure(getProjectionVProcedure);
    				vertexLinker.setInputParams(getProjectionVProcedure, positionVar);
    				
    
    				// Pixel shader
    				var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
    				var outProcedure:Procedure = (opacityMap != null ? getProjectionOpacitySProcedure : getProjectionSProcedure);
    				fragmentLinker.addProcedure(outProcedure);
    				if (alphaTest > 0) {
    					fragmentLinker.declareVariable("tColor");
    					fragmentLinker.setOutputParams(outProcedure, "tColor");
    					if (alphaTest == 1) {
    						fragmentLinker.addProcedure(thresholdOpaqueAlphaProcedure, "tColor");
    					} else {
    						fragmentLinker.addProcedure(thresholdTransparentAlphaProcedure, "tColor");
    					}
    				}
    				fragmentLinker.varyings = vertexLinker.varyings;
    				
    				program = new TextureProjectionMaterialProgram(vertexLinker, fragmentLinker);
    
    				program.upload(camera.context3D);
    				programs[key] = program;
    			}
    			return program;
    		}
    		
    		private function getDrawUnit(program:TextureProjectionMaterialProgram, camera:Camera3D, surface:Surface, geometry:Geometry, opacityMap:TextureResource):DrawUnit {
    			var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
    			var object:Object3D = surface.object;
    
    			// Draw call
    			var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
    
    			// Streams
    			drawUnit.setVertexBufferAt(program.aPosition, positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
    
    			//Constants
    			object.setTransformConstants(drawUnit, surface, program.vertexShader, camera);
    			drawUnit.setProjectionConstants(camera, program.cProjMatrix, object.localToCameraTransform);
    			drawUnit.setFragmentConstantsFromNumbers(program.cAmount, 0.5, 0.5, 0.5, 0.5);
    			drawUnit.setFragmentConstantsFromNumbers(program.cThresholdAlpha, alphaThreshold, 0, 0, alpha);
    			
    			// Textures
    			drawUnit.setTextureAt(program.sDiffuse, diffuseMap._texture);
    			
    			if (opacityMap != null) {
    				drawUnit.setTextureAt(program.sOpacity, opacityMap._texture);
    			}
    			
    			return drawUnit;
    		}
    
    		/**
    		 * @private
    		 */
    		override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void {
    			var object:Object3D = surface.object;
    			
    			// Buffers
    			var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
    			var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);
    			
    			// Check validity
    			if (positionBuffer == null || uvBuffer == null || diffuseMap == null || diffuseMap._texture == null || opacityMap != null && opacityMap._texture == null) return;
    			
    			// Refresh program cache for this context
    			if (camera.context3D != cachedContext3D) {
    				cachedContext3D = camera.context3D;
    				programsCache = caches[cachedContext3D];
    				if (programsCache == null) {
    					programsCache = new Dictionary();
    					caches[cachedContext3D] = programsCache;
    				}
    			}
    			var optionsPrograms:Vector. = programsCache[object.transformProcedure];
    			if(optionsPrograms == null) {
    				optionsPrograms = new Vector.(6, true);
    				programsCache[object.transformProcedure] = optionsPrograms;
    			}
    
    			var program:TextureProjectionMaterialProgram;
    			var drawUnit:DrawUnit;
    			// Opaque pass
    			if (opaquePass && alphaThreshold <= alpha) {
    				if (alphaThreshold > 0) {
    					// Alpha test
    					// use opacityMap if it is presented
    					program = getProgram(object, optionsPrograms, camera, opacityMap, 1);
    					drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
    				} else {
    					// do not use opacityMap at all
    					program = getProgram(object, optionsPrograms, camera, null, 0);
    					drawUnit = getDrawUnit(program, camera, surface, geometry, null);
    				}
    				// Use z-buffer within DrawCall, draws without blending
    				camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
    			}
    			// Transparent pass
    			if (transparentPass && alphaThreshold > 0 && alpha > 0) {
    				// use opacityMap if it is presented
    				if (alphaThreshold <= alpha && !opaquePass) {
    					// Alpha threshold
    					program = getProgram(object, optionsPrograms, camera, opacityMap, 2);
    					drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
    				} else {
    					// There is no Alpha threshold or check z-buffer by previous pass
    					program = getProgram(object, optionsPrograms, camera, opacityMap, 0);
    					drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
    				}
    				// Do not use z-buffer, draws with blending
    				drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
    				drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
    				camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
    			}
    		}
    
    		/**
    		 * @inheritDoc
    		 */
    		override public function clone():Material {
    			var res:TextureProjectionMaterial = new TextureProjectionMaterial(diffuseMap, opacityMap, alpha);
    			res.clonePropertiesFrom(this);
    			return res;
    		}
    
    		/**
    		 * @inheritDoc
    		 */
    		override protected function clonePropertiesFrom(source:Material):void {
    			super.clonePropertiesFrom(source);
    			var tex:TextureProjectionMaterial = source as TextureProjectionMaterial;
    			diffuseMap = tex.diffuseMap;
    			opacityMap = tex.opacityMap;
    			opaquePass = tex.opaquePass;
    			transparentPass = tex.transparentPass;
    			alphaThreshold = tex.alphaThreshold;
    			alpha = tex.alpha;
    		}
    
    	}
}
    
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.compiler.Linker;
    
import flash.display3D.Context3D;
    
class TextureProjectionMaterialProgram extends ShaderProgram {
    
    	public var aPosition:int = -1;
    	public var aUV:int = -1;
    	public var cProjMatrix:int = -1;
    	public var cThresholdAlpha:int = -1;
    	public var cAmount:int = -1;
    	public var sDiffuse:int = -1;
    	public var sOpacity:int = -1;
    
    	public function TextureProjectionMaterialProgram(vertex:Linker, fragment:Linker) {
    		super(vertex, fragment);
    	}
    
    	override public function upload(context3D:Context3D):void {
    		super.upload(context3D);
    
    		aPosition = vertexShader.findVariable("aPosition");
    		aUV = vertexShader.findVariable("aUV");
    		cProjMatrix = vertexShader.findVariable("cProjMatrix");
    		cThresholdAlpha = fragmentShader.findVariable("cThresholdAlpha");
    		cAmount = fragmentShader.findVariable("cAmount");
    		sDiffuse = fragmentShader.findVariable("sDiffuse");
    		sOpacity = fragmentShader.findVariable("sOpacity");
    	}
    
}

and the usage is just like any other material in this case just pass in the texture and that’s it.

b = new Box();
var m:TextureProjectionMaterial = new TextureProjectionMaterial(new BitmapTextureResource(new IMG().bitmapData));
b.setMaterialToAllSurfaces(m);
scene.addChild(b);
uploadResources(b.getResources(true));

You can download the example files here

Downloads

Comments

  • avatar-therabbit
    # TheRabbit

    Hello David,

    Your demos is great! Thank you!

  • avatar-davidejones
    # davidejones
    Thanks man glad you enjoy it :)

Comments are currently closed