Papervision Development Note 2: Flex Component as a Material

If you read the previous post, you know that I was struggling to change the material on a mesh at runtime in papervision3d, and you know that I solved that problem. So, what was all that struggling for?

Well, I wanted to be able to add Flex components on a mesh at run-time. The following image shows you a snapshot of just that and links to the corresponding application where you can play with those components. AWDS or Arrow buttons to move; mouse button down while moving the mouse to rotate point of view.

So I added a Flex textarea component and a Flex button to the back wall of this very tacky room. If you click in the textarea, you can type stuff in it. If you click the button, the text area is cleared. So: An interactive wall. Just a test though, and obviously not that visually appealing.

Here is how it was done. First, the important mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
	layout="absolute" backgroundColor="#000000"
	width="100%" height="100%" preinitialize="preInit()"
	applicationComplete="startUp()"
	xmlns:fc="http://www.adobe.com/2006/fc"
	xmlns:local="*" historyManagementEnabled="false"
	xmlns:components="com.components.*">
 
	<mx:Script>
		<![CDATA[include "Main.as";]]>
       </mx:Script>
 
	<mx:Style source="/assets/style/style.css"/>
	<mx:Canvas id="ghost1" width="300" height="100" >
		<local:CustomPanel id="customPanel">
		</local:CustomPanel>
	</mx:Canvas>
	<mx:UIComponent id="uiComponent3d" top="0" bottom="0" left="0" right="0"/>
	<mx:TextArea backgroundAlpha="0" borderStyle="none" width="100%" paddingLeft="20"
		id="renderStats" text="{_rStats}" fontSize="12" fontFamily="" textAlign="left"
		 color="#FFFFFF">
	</mx:TextArea>
</mx:Application>

The "customPanel" object is the one that contains the flex components you want to attach to the material. Notice the "ghost1" canvas that wraps that panel. Once you finish loading the panel, you then make that canvas invisible (ghost1.visible = false;). Don't ask me why.

Next, the actionscript code:

private function swapMaterial(mesh:DisplayObject3D):void{
	var r:Rectangle = new Rectangle(this.customPanel.x, this.customPanel.y, this.customPanel.width, this.customPanel.height);
	var material:MovieMaterial = new MovieMaterial(this.customPanel, true, true, true, r);
	material.interactive = true;
	material.smooth = true; 
	material.doubleSided = false;
	MeshManipulator.swapObjectMaterials(mesh, material, true); //see below
}

Here is the code for the MeshManipulator.swapObjectMaterials() function:

public static function swapObjectMaterials(mesh:DisplayObject3D, material:MaterialObject3D, resetUV:Boolean):void{
	if(resetUV == true){
		resetUVs(mesh);
	}
 
	if(mesh != null && material != null){
		var length:int = mesh.geometry.faces.length;
 
		for (var i:int = 0; i < length; i++){
			Triangle3D(mesh.geometry.faces[i]).material = material;
		}
	} else {
		trace("ERROR in MeshManipulator.swapObjectMaterials");
		trace("mesh= " + mesh + ", material=" + material);
	}
}

See the previous post for a complete discussion of the reasons for applying this method. The following function, MeshManipulator.resetUVs(), finds the maximum U and V values for the mesh. If either maximum U or maximum V is lower than 1, then all of the UV coordinates for the mesh are multipled by 1/maximumU or 1/maximumV, etc.... Its actually pretty self explanatory. This is needed if the UVs for the mesh have been altered from their defaults to conform to a texture image file. The function simply resets them to an even distribution. If you don't reset them, and they have previosuly been manipulated, there will be distortion of the applied MovieMaterial.

public static function resetUVs(mesh:DisplayObject3D):void{
	var length:int = mesh.geometry.faces.length;
	var maxUArray:Array = new Array();
	var maxVArray:Array = new Array();
 
	for(var i:int = 0; i < length; i++){
		var triangle:Triangle3D = mesh.geometry.faces[i];
		maxVArray.push(triangle.uv0.v);
		maxVArray.push(triangle.uv1.v);
		maxVArray.push(triangle.uv2.v);
		maxUArray.push(triangle.uv0.u);
		maxUArray.push(triangle.uv1.u);
		maxUArray.push(triangle.uv2.u);
	}
 
	var maxV:Number = ArrayMath.maxValue(maxVArray);
	var maxU:Number = ArrayMath.maxValue(maxUArray);
	trace(maxU);
	trace(maxV);
 
	var multiplyU:Number = 1;
	var multiplyV:Number = 1;
 
	if(maxU != 1 || maxV != 1){
		if (maxU != 1 && maxV == 1){
			multiplyU = 1/maxU;
			multiplyV = 1;
		} else if (maxU == 1 && maxV !=1){
			multiplyU = 1;
			multiplyV = 1/maxV;
		} else if (maxU != 1 && maxV != 1){
			multiplyU = 1/maxU;
			multiplyV = 1/maxV;
		}
 
		for (var j:int = 0; j < length; j++){
			var oldTriangle:Triangle3D = mesh.geometry.faces[j];
			var newUVs:Array = new Array(new NumberUV(oldTriangle.uv0.u*multiplyU, oldTriangle.uv0.v*multiplyV), new NumberUV(oldTriangle.uv1.u*multiplyU, oldTriangle.uv1.v*multiplyV), new NumberUV(oldTriangle.uv2.u*multiplyU, oldTriangle.uv2.v*multiplyV));
			var newMaterial:MaterialObject3D = triangle.material;
			var newTriangle:Triangle3D = new Triangle3D(mesh, oldTriangle.vertices, newMaterial, newUVs);
			mesh.geometry.faces[j] = newTriangle;
		}
	}
 }

Thats it. That ArrayMath.maxValue() function simply finds the maximum value in the supplied array.

So: Flex components on a 3D object. Nice.

Trackback URL for this post:

http://www.stevenjbarnes.com/trackback/28

Hi Steven

Thanks for this post , I've been looking for an efficient generic method to add flex components with full interaction for a while now and this App is the closest I have come for what I want to do.

Could you direct me to the full code for this example pleaee?

I was wondering if there were another example somewhere or weather you could comment on my requirements. I wanted to put a fully interactive flex chart with drilldown on a papervision plane or cube, and I have found nothing that works 100%. IE I dont want to use a Collada model of anything nsimilar.

Would I be right in thinking that I could alter this Apps code to work with the triangles and meshes of these papervision3D components and do the same think here?

Look forward to your reply.

Regards

Duncan

Thanks!

Thanks--this is so useful!

I'm have a problem trying to change the material on my mesh in PV3D. It changes the material, but the material is not UV mapped correctly at all.

Is this what your resetUVs function fixes? Could you elaborate more on it, if possible?

Is ArrayMath.maxValue() something you wrote yourself?

Thanks