package test
{
import com.adobe.utils.AGALMiniAssembler;
import com.adobe.utils.PerspectiveMatrix3D;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.display.Stage;
import flash.display3D.Context3D;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.display3D.textures.Texture;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.PressAndTapGestureEvent;
import flash.geom.Matrix;
import flash.geom.Matrix3D;
import flash.geom.Point;
import flash.geom.Vector3D;
import flash.ui.Keyboard;
public class TestPointLight extends Sprite
{
[Embed(source="/assets/cloth.jpg")]
private var cloth:Class;
[Embed(source="/assets/skyBox.png")]
private var skyBox:Class;
[Embed(source="/assets/flower.jpg")]
private var flower:Class;
[Embed(source="/assets/trinket_diffuse.jpg")]
private var trinketDiff:Class;
[Embed(source="/assets/trinket_normal.jpg")]
private var trinketNor:Class;
[Embed(source="/assets/trinket_specular.jpg")]
private var trinketSpe:Class;
protected var context3D:Context3D;
protected var program:Program3D;
protected var vertexbuffer:VertexBuffer3D;
protected var indexbuffer:IndexBuffer3D;
private var texture:Texture;
private var texture1:Texture;
private var xR:Boolean = false;
private var yR:Boolean = false;
private var zR:Boolean = false;
private var projectionTransform:PerspectiveMatrix3D;
private var lightPos:Vector3D = new Vector3D(0,0,-10);
private var lightStr:Number = 1;
private var lightStrAdd:Number = -0.01;
private var cameraMatrix:Matrix3D;
private var objMatrix:Matrix3D = new Matrix3D();
private var endMatrix:Matrix3D = new Matrix3D();
private var aspect:Number;
public function TestPointLight(context3D:Context3D,stage:Stage)
{
this.context3D = context3D;
stage.addEventListener(Event.ENTER_FRAME, onRender);
stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown)
stage.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
aspect = stage.width/stage.height;
initProjection();
var size:Number = 0.3;
initCube(size,size,size, new Vector3D(0,0,0));
}
protected function initProjection():void
{
aspect = 4/3;
var zNear:Number = 0.1;
var zFar:Number = 1000;
var fov:Number = 45*Math.PI/180;
cameraMatrix = new Matrix3D();
cameraMatrix.appendTranslation(0,0,-5);
// cameraMatrix.invert();
projectionTransform = new PerspectiveMatrix3D();
projectionTransform.perspectiveFieldOfViewLH(fov, aspect, zNear, zFar);
// projectionTransform.perspectiveFieldOfViewRH(45, aspect, zNear, zFar);
// projectionTransform.prepend(cameraMatrix);
}
private function initCube(l:Number,w:Number,h:Number,position:Vector3D):void
{
var size:uint = 1;
var unitWidth:Number = w / size;
var unitHeight:Number = h / size;
var unitLength:Number = l / size;
var gap:Number = 0//w/size*1.1;
// var hl:Number = l*0.5;
// var hw:Number = w*0.5;
// var hh:Number = h*0.5;
var hl:Number = unitWidth;
var hw:Number = unitHeight;
var hh:Number = unitLength;
var points:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
points.push(Vector.<Number>([-hl,-hh,hw]));//左下后
points.push(Vector.<Number>([-hl,-hh,-hw]));//左下前
points.push(Vector.<Number>([hl,-hh,-hw]));//右下前
points.push(Vector.<Number>([hl,-hh,hw]));//右下后
points.push(Vector.<Number>([-hl,hh,hw]));//左上后
points.push(Vector.<Number>([-hl,hh,-hw]));//左上前
points.push(Vector.<Number>([hl,hh,-hw]));//右上前
points.push(Vector.<Number>([hl,hh,hw]));//右上后
var normals:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
normals.push(Vector.<Number>([0,-1,0]));
normals.push(Vector.<Number>([0,1,0]));
normals.push(Vector.<Number>([0,0,-1]));
normals.push(Vector.<Number>([0,0,1]));
normals.push(Vector.<Number>([-1,0,0]));
normals.push(Vector.<Number>([1,0,0]));
var index:Array = [
0,1,2,3,//下面
4,5,6,7,//上面
1,2,5,6,//前面
0,3,7,4,//后面
0,1,5,4,//左面
3,2,6,7//右面
];
var indices:Vector.<uint> = Vector.<uint>([
0,1,2,0,2,3,
4,7,5,5,7,6,
8,10,9,9,10,11,//
12,13,14,12,14,15,
18,16,19,18,17,16,
21,22,23,20,21,23
]);
var uvs:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
uvs.push(Vector.<Number>([0,0]));//左上
uvs.push(Vector.<Number>([0,1]));//左下
uvs.push(Vector.<Number>([1,1]));//右下
uvs.push(Vector.<Number>([1,0]));//右上
var allVertices:Vector.<Number> = new Vector.<Number>();
var allIndices:Vector.<uint> = new Vector.<uint>();
var cubeIndex:uint = 0;
for (var i:int = 0; i < size; i++)
{
for (var j:int = 0; j < size; j++)
{
for (var k:int = 0; k < size; k++)
{
var matrix:Matrix3D = new Matrix3D();
matrix.appendTranslation(i*(unitLength+gap),j*(unitWidth+gap),k*(unitHeight+gap));
matrix.appendTranslation(-l*0.5-gap,-w*0.5-gap,0);
//一个批次的定点
var cubeVectexes:Vector.<Number> = new Vector.<Number>();
var cubeUVs:Vector.<Number> = new Vector.<Number>();
for (var n:int = 0; n < index.length; n+=4)
{
cubeVectexes = cubeVectexes.concat(points[index[n]],points[index[n+1]],points[index[n+2]],points[index[n+3]]);
}
matrix.transformVectors(cubeVectexes,cubeVectexes);
// trace(cubeVectexes);
var normalIndex:uint = 0;
var uvIndex:uint = 0;
for (n = 0; n < cubeVectexes.length; n+=3)
{
if(n%12 == 0) normalIndex = (normalIndex+1) % 6;
if(n % 3 == 0) uvIndex = (uvIndex + 1) % 4;
allVertices = allVertices.concat(Vector.<Number>([cubeVectexes[n],cubeVectexes[n+1],cubeVectexes[n+2]]).concat(uvs[uvIndex]).concat(normals[normalIndex]));
trace(n,uvIndex,uvs[uvIndex])
}
var cubeIndices:Vector.<uint> = new Vector.<uint>(indices.length);
for (n = 0; n < indices.length; n++)
{
cubeIndices[n] = indices[n] + index.length * cubeIndex;
}
allIndices = allIndices.concat(cubeIndices);
cubeIndex++;
}
}
}
trace(allVertices);
var offset:int = 8;
vertexbuffer = context3D.createVertexBuffer(allVertices.length/offset, offset);
vertexbuffer.uploadFromVector(allVertices, 0, allVertices.length/offset);
indexbuffer = context3D.createIndexBuffer(allIndices.length);
indexbuffer.uploadFromVector (allIndices, 0, allIndices.length);
var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
"m44 op va0 vc0
"+ //顶点
"mov v1 va1
"+ //UV
"mov v2 va2"//法线
);
var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
//fc0 光源位置
//fc1 环境光颜色 以及w光强
//fc2 半向量
//fc3 模型矩阵
"tex ft0,v1,fs0<2d,linear,repeat>
" +
"mov ft1 fc0
" +
"mov ft2 v2
" +
"m44 ft2 ft2 fc3
" +
//
// "sub ft5 fc0 ft2" +
// "mul ft5 ft5 ft5" +
//
"dp3 ft3.w ft1 ft2
" + //ft3 = cos涩塔
"abs ft3.w ft3.w
" +
"mul ft4 ft0 ft3.wwww
"+
"mul ft4 ft4 fc1.wwww
"+
//
"dp3 ft6.w fc2 ft2
" +
"abs ft6.w ft6.w
" +
"mul ft7 fc4 ft6.wwww
" +
//
"add ft0 ft0 ft4
" + // 原始颜色 += 漫反射光照加成颜色
"add ft0 ft0 ft7
" + // 原始颜色 += 高光加成颜色
// "add ft0 ft0 fc1
" + // 叠加一个环境光颜色
"mov oc,ft0"
);
var bitmap:Bitmap = new trinketDiff();
texture = context3D.createTexture(bitmap.width,bitmap.height,Context3DTextureFormat.BGRA,false);
texture.uploadFromBitmapData(bitmap.bitmapData);
var bitmap1:Bitmap = new trinketNor();
texture1 = context3D.createTexture(bitmap1.width,bitmap1.height,Context3DTextureFormat.BGRA,false);
texture1.uploadFromBitmapData(bitmap1.bitmapData);
program = context3D.createProgram();
program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
}
private function culculateNormal(vecVb:Vector.<Number>,vertexNum:int):void
{
var data32PerVertex:int = vecVb.length/vertexNum;
var vertexArr:Array = [];
for (var i:int=0;i<vertexNum;i++){
var x:Number = vecVb[data32PerVertex*i];
var y:Number = vecVb[data32PerVertex*i+1];
var z:Number = vecVb[data32PerVertex*i+2];
vertexArr.push(new Vector3D(x,y,z));
}
var calcNormal:Function = function(p1:Vector3D,p2:Vector3D,p3:Vector3D):Vector3D{
var source2:Vector3D = new Vector3D(p2.x-p1.x,p2.y-p1.y,p2.z-p1.z);
var source1:Vector3D = new Vector3D(p3.x-p1.x,p3.y-p1.y,p3.z-p1.z);
var norV:Vector3D = new Vector3D();
norV.x = source1.y * source2.z - source1.z * source2.y;
norV.y = source1.z * source2.x - source1.x * source2.z;
norV.z = source1.x * source2.y - source1.y * source2.x;
norV.normalize();
return norV;
}
var calcNormalAvg:Function = function(arr:Array,offsetNormal:int):void{
var sideLen:int = arr.length/3;
var sideAvg:Vector3D = new Vector3D();
for (var i:int=0;i<sideLen;i++){
var side:Vector3D = calcNormal(vertexArr[arr[i*3]],vertexArr[arr[i*3+1]],vertexArr[arr[i*3+2]]);
sideAvg=sideAvg.add(side);
}
sideAvg.x = sideAvg.x/sideLen;
sideAvg.y = sideAvg.y/sideLen;
sideAvg.z = sideAvg.z/sideLen;
sideAvg.normalize();
vecVb[offsetNormal] = -sideAvg.x;
vecVb[offsetNormal+1] = -sideAvg.y;
vecVb[offsetNormal+2] = -sideAvg.z;
}
// calcNormalAvg([0,1,3,1,2,3],vertexNum*data32PerVertex)
}
protected function onRender(e:Event):void
{
if ( !context3D ) return;
if(xR) objMatrix.appendRotation(1,Vector3D.X_AXIS);
if(yR) objMatrix.appendRotation(1,Vector3D.Y_AXIS);
if(zR) objMatrix.appendRotation(1,Vector3D.Z_AXIS);
endMatrix.identity();
endMatrix.append(objMatrix);
var viewMatrix:Matrix3D = cameraMatrix.clone();
viewMatrix.invert();
viewMatrix.append(projectionTransform);
endMatrix.append(viewMatrix);
// endMatrix.copyFrom(objMatrix);
// endMatrix.append(projectionTransform);
context3D.setVertexBufferAt (0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3);//顶点 Va0
context3D.setVertexBufferAt(1, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_2);// uv Va1
context3D.setVertexBufferAt(2,vertexbuffer,5,Context3DVertexBufferFormat.FLOAT_3); // 法线 Va2
var light:Vector3D=lightPos.clone(); // 不含平移元素的变换
light.normalize();
context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,0,Vector.<Number>([light.x,light.y,light.z,0]));
// -- 设置状态机当前的fc1 静态常量:xyz用于 环境光颜色叠加 W用于漫反射光照强度 这里叠加一个红色环境光
var ambientR:Number = 1//lightStr*0.2;
var ambientG:Number = 0//(1-lightStr)*0.2;
var ambientB:Number = 0//(lightStr/2)*0.2;
context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,1,Vector.<Number>([ambientR,ambientG,ambientB,lightStr]));//lightStr
var halfEyeLightPos:Vector3D = cameraMatrix.position.clone();
halfEyeLightPos=halfEyeLightPos.add(lightPos);
halfEyeLightPos.x/=2;
halfEyeLightPos.y/=2;
halfEyeLightPos.z/=2;
halfEyeLightPos.normalize();
context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,2,Vector.<Number>([halfEyeLightPos.x,halfEyeLightPos.y,halfEyeLightPos.z,0]));
// -- 设置状态机当前的fc3 静态常量:用于将模型矩阵变换传入
context3D.setProgramConstantsFromMatrix(Context3DProgramType.FRAGMENT,3,objMatrix);
// -- 设置状态机当前的fc4 静态常量:高光颜色
var specularR:Number = 0//lightStr*0.3;
var specularG:Number = 1//lightStr*0.3;
var specularB:Number = 0//lightStr*0.3;
context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,4,Vector.<Number>([specularR,specularG,specularB,0]));
context3D.setTextureAt(0,texture);
// context3D.setTextureAt(1,texture1);
context3D.setProgram(program);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, endMatrix, true);
context3D.clear();
context3D.drawTriangles(indexbuffer);
context3D.present();
}
/**
* 当鼠标移动的时候记录的鼠标点
*/
private var onMouseDownPt:Point = new Point();
/**
* 鼠标移动旋转物体
* 原理无非就是根据每次移动时的像素差距来计算让物体矩阵M在当前的状态下再围绕X和Y旋转(至于围绕Z轴旋转可以自己添加试试)
* @param e
*
*/
private function onMouseDown(e:MouseEvent):void{
onMouseDownPt.x = e.stageX;
onMouseDownPt.y = e.stageY;
stage.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP,onMouseUp);
}
private function onMouseMove(e:MouseEvent):void{
var dx:Number = e.stageX - onMouseDownPt.x;
var dy:Number = e.stageY - onMouseDownPt.y;
var degreesY:Number = -dx/2;
var degreesX:Number = -dy/2;
onMouseDownPt.x = e.stageX;
onMouseDownPt.y = e.stageY;
objMatrix.appendRotation(degreesY,Vector3D.Y_AXIS);
objMatrix.appendRotation(degreesX,Vector3D.X_AXIS);
}
private function onMouseUp(e:MouseEvent):void{
stage.removeEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
stage.removeEventListener(MouseEvent.MOUSE_UP,onMouseUp);
}
private var moveDis:Number = 0.1;
protected function onKeyDown(event:KeyboardEvent):void
{
switch(event.keyCode)
{
case Keyboard.NUMPAD_ADD:
lightStr += 0.1;
break;
case Keyboard.NUMPAD_SUBTRACT:
lightStr -= 0.1;
break;
case Keyboard.X:
xR = !xR;
break;
case Keyboard.C:
yR = !yR;
break;
case Keyboard.Z:
zR = !zR;
break;
case Keyboard.R:
objMatrix.identity();
xR = false;
yR = false;
zR = false;
break;
case Keyboard.LEFT:
lightPos.x -= moveDis;
break;
case Keyboard.RIGHT:
lightPos.x += moveDis;
break;
case Keyboard.UP:
lightPos.y += moveDis;
break;
case Keyboard.DOWN:
lightPos.y -= moveDis;
break;
case Keyboard.A:
cameraMatrix.appendTranslation(moveDis,0,0);
break;
case Keyboard.D:
cameraMatrix.appendTranslation(-moveDis,0,0);
break;
case Keyboard.W:
cameraMatrix.appendTranslation(0,-moveDis,0);
break;
case Keyboard.S:
cameraMatrix.appendTranslation(0,moveDis,0);
break;
}
trace("lightPos:"+lightPos);
trace("cameraPos"+cameraMatrix.decompose())
trace("lightStr"+lightStr);
}
/**
* 判断设备丢失
*
*/
private function get isContextDispose():Boolean{
return context3D==null||context3D.driverInfo=="Disposed"||context3D.driverInfo=="";
}
}
}