Texture Projection Material Alternativa3d

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