Removes old shadow classes

This commit is contained in:
maltsev
2012-03-30 18:12:11 +06:00
parent c9816581c5
commit 26b8a0b05f
7 changed files with 0 additions and 3005 deletions

View File

@@ -1,540 +0,0 @@
package alternativa.engine3d.shadows {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Transform3D;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.lights.DirectionalLight;
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.materials.compiler.Linker;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.compiler.VariableType;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.primitives.Box;
import alternativa.engine3d.resources.ExternalTextureResource;
import alternativa.engine3d.resources.TextureResource;
import flash.display3D.Context3D;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.Context3DTriangleFace;
import flash.display3D.Program3D;
import flash.display3D.textures.Texture;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
use namespace alternativa3d;
/**
* @private
*/
public class DirectionalShadowRenderer extends ShadowRenderer {
public var offset:Vector3D = new Vector3D();
public var caster:Object3D;
private var context:Context3D;
private var shadowMap:Texture;
private var _worldSize:Number;
private var light:DirectionalLight;
alternativa3d var globalToShadowMap:Matrix3D = new Matrix3D();
private var debugObject:Mesh;
public var debugMaterial:TextureMaterial = new TextureMaterial();
private var debugTexture:TextureResource = new ExternalTextureResource("null");
// private var debugTexture:TextureResource = new BitmapTextureResource(new BitmapData(4, 4, false, 0xFF0000));
private static const constants:Vector.<Number> = Vector.<Number>([
// 255, 255*0.98, 100, 1
255, 255*0.96, 100, 1
]);
private var pcfOffset:Number = 0;
private var pcfOffsets:Vector.<Number>;
public function DirectionalShadowRenderer(context:Context3D, size:int, worldSize:Number, pcfSize:Number = 0) {
this.context = context;
this._worldSize = worldSize;
this.pcfOffset = pcfSize/worldSize/255;
// this.pcfOffset = pcfSize;
if (pcfOffset > 0) {
pcfOffsets = Vector.<Number>([
-pcfOffset, -pcfOffset, 0, 1/4,
-pcfOffset, pcfOffset, 0, 1,
pcfOffset, -pcfOffset, 0, 1,
pcfOffset, pcfOffset, 0, 1
]);
}
this.shadowMap = context.createTexture(size, size, Context3DTextureFormat.BGRA, true);
debugTexture._texture = this.shadowMap;
debugMaterial.diffuseMap = debugTexture;
debugMaterial.alpha = 0.9;
// TODO: fix
debugMaterial.transparentPass = true;
debugMaterial.opaquePass = false;
debugMaterial.alphaThreshold = 1.1;
// debugTexture.upload(context);
debugObject = new Box(worldSize, worldSize, 1, 1, 1, 1, false, debugMaterial);
debugObject.geometry.upload(context);
}
public function get worldSize():Number {
return _worldSize;
}
public function set worldSize(value:Number):void {
_worldSize = value;
var newDebug:Mesh = new Box(_worldSize, _worldSize, 1, 1, 1, 1, false, debugMaterial);
newDebug.geometry.upload(context);
if (debugObject._parent != null) {
debugObject._parent.addChild(newDebug);
debugObject._parent.removeChild(debugObject);
}
debugObject = newDebug;
}
private var _debug:Boolean = false;
public function setLight(value:DirectionalLight):void {
light = value;
if (_debug) {
light.addChild(debugObject);
}
}
override public function get debug():Boolean {
return _debug;
}
override public function set debug(value:Boolean):void {
_debug = value;
if (_debug) {
if (light != null) {
light.addChild(debugObject);
}
} else {
if (debugObject._parent != null) {
debugObject._parent.removeChild(debugObject);
}
}
}
private static var matrix:Matrix3D = new Matrix3D();
override alternativa3d function cullReciever(boundBox:BoundBox, object:Object3D):Boolean {
copyMatrixFromTransform(matrix, object.localToGlobalTransform);
matrix.append(this.globalToShadowMap);
return cullObjectImplementation(boundBox, matrix);
}
private var lightProjectionMatrix:Matrix3D = new Matrix3D();
private var uvMatrix:Matrix3D = new Matrix3D();
private var center:Vector3D = new Vector3D();
override public function update():void {
active = true;
var root:Object3D;
// Расчитываем матрицу объекта
// if (caster.transformChanged) {
caster.localToCameraTransform.compose(caster._x, caster._y, caster._z, caster._rotationX, caster._rotationY, caster._rotationZ, caster._scaleX, caster._scaleY, caster._scaleZ);
// } else {
// caster.localToCameraTransform.copy(caster.transform);
// }
root = caster;
while (root._parent != null) {
root = root._parent;
// if (root.transformChanged) {
root.localToGlobalTransform.compose(root._x, root._y, root._z, root._rotationX, root._rotationY, root._rotationZ, root._scaleX, root._scaleY, root._scaleZ);
// }
caster.localToCameraTransform.append(root.localToGlobalTransform);
}
// Расчитываем матрицу лайта
light.localToGlobalTransform.compose(light._x, light._y, light._z, light._rotationX, light._rotationY, light._rotationZ, light._scaleX, light._scaleY, light._scaleZ);
root = light;
while (root._parent != null) {
root = root._parent;
// if (root.transformChanged) {
root.localToGlobalTransform.compose(root._x, root._y, root._z, root._rotationX, root._rotationY, root._rotationZ, root._scaleX, root._scaleY, root._scaleZ);
// }
light.localToGlobalTransform.append(root.localToGlobalTransform);
}
light.globalToLocalTransform.copy(light.localToGlobalTransform);
light.globalToLocalTransform.invert();
// Получаем матрицу перевода из объекта в лайт
caster.localToCameraTransform.append(light.globalToLocalTransform);
// Расчет матрицы проецирования
var t:Transform3D = caster.localToCameraTransform;
center.x = t.a*offset.x + t.b*offset.y + t.c*offset.z + t.d;
center.y = t.e*offset.x + t.f*offset.y + t.g*offset.z + t.h;
center.z = t.i*offset.x + t.j*offset.y + t.k*offset.z + t.l;
// var center:Vector3D = new Vector3D(caster.localToCameraTransform.d, caster.localToCameraTransform.h, caster.localToCameraTransform.l);
calculateShadowMapProjection(lightProjectionMatrix, uvMatrix, center, _worldSize, _worldSize, _worldSize);
copyMatrixFromTransform(globalToShadowMap, light.globalToLocalTransform);
globalToShadowMap.append(uvMatrix);
debugObject.x = center.x;
debugObject.y = center.y;
debugObject.z = center.z - _worldSize/2;
// trace("center", center);
debugMaterial.diffuseMap = null;
// Рисуем в шедоумапу
context.setRenderToTexture(shadowMap, true, 0, 0);
// context.clear(1);
context.clear(1, 1, 1, 1);
cleanContext(context);
drawObjectToShadowMap(context, caster, light, lightProjectionMatrix);
context.setRenderToBackBuffer();
cleanContext(context);
debugMaterial.diffuseMap = debugTexture;
}
private static var transformToMatrixRawData:Vector.<Number> = new Vector.<Number>(16);
alternativa3d static function copyMatrixFromTransform(matrix:Matrix3D, transform:Transform3D):void {
transformToMatrixRawData[0] = transform.a;
transformToMatrixRawData[1] = transform.e;
transformToMatrixRawData[2] = transform.i;
transformToMatrixRawData[3] = 0;
transformToMatrixRawData[4] = transform.b;
transformToMatrixRawData[5] = transform.f;
transformToMatrixRawData[6] = transform.j;
transformToMatrixRawData[7] = 0;
transformToMatrixRawData[8] = transform.c;
transformToMatrixRawData[9] = transform.g;
transformToMatrixRawData[10] = transform.k;
transformToMatrixRawData[11] = 0;
transformToMatrixRawData[12] = transform.d;
transformToMatrixRawData[13] = transform.h;
transformToMatrixRawData[14] = transform.l;
transformToMatrixRawData[15] = 1;
// matrix.copyRawDataFrom(transformToMatrixRawData);
matrix.rawData = transformToMatrixRawData;
}
alternativa3d static function drawObjectToShadowMap(context:Context3D, object:Object3D, light:DirectionalLight, projection:Matrix3D):void {
if (object is Mesh) {
drawMeshToShadowMap(context, Mesh(object), projection);
}
for (var child:Object3D = object.childrenList; child != null; child = child.next) {
if (child.visible && child.useShadow) {
if (child.transformChanged) child.composeTransforms();
child.localToCameraTransform.combine(object.localToCameraTransform, child.transform);
drawObjectToShadowMap(context, child, light, projection);
}
}
}
private static var drawProjection:Matrix3D = new Matrix3D();
private static var directionalShadowMapProgram:Program3D;
private static function drawMeshToShadowMap(context:Context3D, mesh:Mesh, projection:Matrix3D):void {
if (mesh.geometry == null || mesh.geometry.numTriangles == 0 || !mesh.geometry.isUploaded) {
return;
}
copyMatrixFromTransform(drawProjection, mesh.localToCameraTransform);
drawProjection.append(projection);
if (directionalShadowMapProgram == null) directionalShadowMapProgram = initMeshToShadowMapProgram(context);
context.setProgram(directionalShadowMapProgram);
context.setVertexBufferAt(0, mesh.geometry.getVertexBuffer(VertexAttributes.POSITION), mesh.geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, drawProjection, true);
context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 4, Vector.<Number>([255, 0, 0, 1]));
context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([1/255, 0, 0, 1]));
context.setCulling(Context3DTriangleFace.BACK);
for (var i:int = 0; i < mesh._surfacesLength; i++) {
var surface:Surface = mesh._surfaces[i];
if (surface.material == null || !surface.material.canDrawInShadowMap) continue;
context.drawTriangles(mesh.geometry._indexBuffer, surface.indexBegin, surface.numTriangles);
}
context.setVertexBufferAt(0, null);
}
private static function initMeshToShadowMapProgram(context3d:Context3D):Program3D {
var vLinker:Linker = new Linker(Context3DProgramType.VERTEX);
var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
var proc:Procedure = Procedure.compileFromArray([
"#a0=a0",
"#c4=c4",
"#v0=v0",
"m44 t0, a0, c0",
"mul v0, t0, c4.x",
"mov o0, t0"
]);
proc.assignVariableName(VariableType.CONSTANT, 0, "c0", 4);
vLinker.addProcedure(proc);
fLinker.addProcedure(Procedure.compileFromArray([
"#v0=v0",
"#c0=c0",
"mov t0.xy, v0.zz",
"frc t0.y, v0.z",
"sub t0.x, v0.z, t0.y",
"mul t0.x, t0.x, c0.x",
"mov t0.z, c0.z",
"mov t0.w, c0.w",
"mov o0, t0"
]));
var program:Program3D = context3d.createProgram();
// trace("VERTEX");
// trace(A3DUtils.disassemble(vLinker.getByteCode()));
// trace("FRAGMENT");
// trace(A3DUtils.disassemble(fLinker.getByteCode()));
fLinker.varyings = vLinker.varyings;
vLinker.link();
fLinker.link();
program.upload(vLinker.data, fLinker.data);
return program;
}
// должен быть заполнен нулями
private var rawData:Vector.<Number> = new Vector.<Number>(16);
private function calculateShadowMapProjection(matrix:Matrix3D, uvMatrix:Matrix3D, offset:Vector3D, width:Number, height:Number, length:Number):void {
var halfW:Number = width/2;
var halfH:Number = height/2;
var halfL:Number = length/2;
var frustumMinX:Number = offset.x - halfW;
var frustumMaxX:Number = offset.x + halfW;
var frustumMinY:Number = offset.y - halfH;
var frustumMaxY:Number = offset.y + halfH;
var frustumMinZ:Number = offset.z - halfL;
var frustumMaxZ:Number = offset.z + halfL;
// Считаем матрицу проецирования
rawData[0] = 2/(frustumMaxX - frustumMinX);
rawData[5] = 2/(frustumMaxY - frustumMinY);
rawData[10]= 1/(frustumMaxZ - frustumMinZ);
rawData[12] = (-0.5 * (frustumMaxX + frustumMinX) * rawData[0]);
rawData[13] = (-0.5 * (frustumMaxY + frustumMinY) * rawData[5]);
rawData[14]= -frustumMinZ/(frustumMaxZ - frustumMinZ);
rawData[15]= 1;
matrix.rawData = rawData;
rawData[0] = 1/((frustumMaxX - frustumMinX));
// if (useSingle) {
// rawData[5] = 1/((frustumMaxY - frustumMinY));
// } else {
rawData[5] = -1/((frustumMaxY - frustumMinY));
// }
rawData[12] = 0.5 - (0.5 * (frustumMaxX + frustumMinX) * rawData[0]);
rawData[13] = 0.5 - (0.5 * (frustumMaxY + frustumMinY) * rawData[5]);
uvMatrix.rawData = rawData;
}
/*
private static const fullVShader:Procedure = initFullVShader();
private static function initFullVShader():Procedure {
var shader:Procedure = Procedure.compileFromArray([
"m44 o0, a0, c0",
// Координата вершины в локальном пространстве
"m44 v0, a0, c4",
]);
shader.assignVariableName(VariableType.ATTRIBUTE, 0, "aPosition");
shader.assignVariableName(VariableType.CONSTANT, 0, "cPROJ", 4);
shader.assignVariableName(VariableType.CONSTANT, 4, "cTOSHADOW", 4);
shader.assignVariableName(VariableType.VARYING, 0, "vSHADOWSAMPLE");
return shader;
}
*/
private static function initVShader(index:int):Procedure {
var shader:Procedure = Procedure.compileFromArray([
"m44 v0, a0, c0"
]);
shader.assignVariableName(VariableType.ATTRIBUTE, 0, "aPosition");
shader.assignVariableName(VariableType.CONSTANT, 0, index + "cTOSHADOW", 4);
shader.assignVariableName(VariableType.VARYING, 0, index + "vSHADOWSAMPLE");
return shader;
}
private static function initFShader(mult:Boolean, usePCF:Boolean, index:int, grayScale:Boolean = false):Procedure {
var i:int;
var line:int = 0;
var shaderArr:Array = [];
var numPass:uint = (usePCF) ? 4 : 1;
for (i = 0; i < numPass; i++) {
// Расстояние
shaderArr[line++] = "mov t0.w, v0.z";
shaderArr[line++] = "mul t0.w, t0.w, c4.y"; // bias [0.99] * 255
if (usePCF) {
// Добавляем смещение
shaderArr[line++] = "mul t1, c" + (i + 6).toString() + ", t0.w";
shaderArr[line++] = "add t1, v0, t1";
shaderArr[line++] = "tex t1, t1, s0 <2d,clamp,near,nomip>";
} else {
shaderArr[line++] = "tex t1, v0, s0 <2d,clamp,near,nomip>";
}
// Восстанавливаем расстояние
shaderArr[line++] = "mul t1.w, t1.x, c4.x"; // * 255
shaderArr[line++] = "add t1.w, t1.w, t1.y";
// Перекрытие тенью
shaderArr[line++] = "sub t2.z, t1.w, t0.w";
shaderArr[line++] = "mul t2.z, t2.z, c4.z"; // smooth [10000]
shaderArr[line++] = "sat t2.z, t2.z";
// Добавляем маску и прозрачность, затем sat
if (grayScale) {
shaderArr[line++] = "add t2, t2.zzzz, t1.zzzz"; // маска тени
} else {
shaderArr[line++] = "add t2.z, t2.z, t1.z"; // маска тени
shaderArr[line++] = "add t2, t2.zzzz, c5"; // цвет тени
}
shaderArr[line++] = "sat t2, t2";
if (usePCF) {
if (i == 0) {
shaderArr[line++] = "mov t3, t2";
} else {
shaderArr[line++] = "add t3, t3, t2";
}
}
}
if (usePCF) {
shaderArr[line++] = "mul t2, t3, c6.w";
}
if (grayScale) {
shaderArr[line++] = "mov o0.w, t2.x";
} else {
if (mult) {
shaderArr[line++] = "mul t0.xyz, i0.xyz, t2.xyz";
shaderArr[line++] = "mov t0.w, i0.w";
shaderArr[line++] = "mov o0, t0";
} else {
shaderArr[line++] = "mov o0, t2";
}
}
var shader:Procedure = Procedure.compileFromArray(shaderArr, "DirectionalShadowMap");
shader.assignVariableName(VariableType.VARYING, 0, index + "vSHADOWSAMPLE");
shader.assignVariableName(VariableType.CONSTANT, 4, index + "cConstants", 1);
if (!grayScale) shader.assignVariableName(VariableType.CONSTANT, 5, index + "cShadowColor", 1);
if (usePCF) {
for (i = 0; i < numPass; i++) {
shader.assignVariableName(VariableType.CONSTANT, i + 6, "cDPCF" + i.toString(), 1);
}
}
shader.assignVariableName(VariableType.SAMPLER, 0, index + "sSHADOWMAP");
return shader;
}
override public function getVShader(index:int = 0):Procedure {
return initVShader(index);
}
override public function getFShader(index:int = 0):Procedure {
return initFShader(false, (pcfOffset > 0), index);
}
// override public function getMultFShader():Procedure {
// return initFShader(true, (pcfOffset > 0), 0);
// }
// override public function getMultVShader():Procedure {
// return initVShader(0);
// }
override public function getFIntensityShader():Procedure {
return initFShader(false, (pcfOffset > 0), 0, true);
}
private static const objectToShadowMap:Matrix3D = new Matrix3D();
private static const localToGlobal:Transform3D = new Transform3D();
private static const vector:Vector.<Number> = new Vector.<Number>(16, false);
override public function applyShader(drawUnit:DrawUnit, program:ShaderProgram, object:Object3D, camera:Camera3D, index:int = 0):void {
// Считаем матрицу перевода в лайт из объекта
localToGlobal.combine(camera.localToGlobalTransform, object.localToCameraTransform);
copyMatrixFromTransform(objectToShadowMap, localToGlobal);
objectToShadowMap.append(globalToShadowMap);
objectToShadowMap.copyRawDataTo(vector, 0, true);
// objectToShadowMap.transpose();
// drawUnit.setVertexConstantsFromVector(program.vertexShader.getVariableIndex(index + "cTOSHADOW"), objectToShadowMap.rawData, 4)
drawUnit.setVertexConstantsFromVector(program.vertexShader.getVariableIndex(index + "cTOSHADOW"), vector, 4)
drawUnit.setFragmentConstantsFromVector(program.fragmentShader.getVariableIndex(index + "cConstants"), constants, 1);
if (program.fragmentShader.containsVariable(index + "cShadowColor")) {
// drawUnit.setFragmentConstantsFromVector(program.fragmentShader.getVariableIndex(index + "cShadowColor"), camera.ambient, 1);
// В дальнейшем яркость тени увеличтся в два раза
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex(index + "cShadowColor"), camera.ambient[0]/2, camera.ambient[1]/2, camera.ambient[2]/2, 1);
}
if (pcfOffset > 0) {
// destination.addFragmentConstantSet(program.fragmentShader.getVariableIndex(index + "cPCF0"), pcfOffsets, pcfOffsets.length/4);
drawUnit.setFragmentConstantsFromVector(program.fragmentShader.getVariableIndex("cDPCF0"), pcfOffsets, pcfOffsets.length/4);
}
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex(index + "sSHADOWMAP"), shadowMap);
}
// override public function getTextureIndex(fLinker:Linker):int {
// return fLinker.getVariableIndex("sSHADOWMAP");
// }
// private static var program:ShaderProgram;
// private static var programPCF:ShaderProgram;
// private static function initMeshProgram(context:Context3D, usePCF:Boolean):ShaderProgram {
// var vLinker:Linker = new Linker(Context3DProgramType.VERTEX);
// vLinker.addProcedure(fullVShader);
//
// var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
// if (usePCF) {
// fLinker.addProcedure(pcfFShader);
// } else {
// fLinker.addProcedure(fShader);
// }
//
// vLinker.setOppositeLinker(fLinker);
// fLinker.setOppositeLinker(vLinker);
//
//// trace("[VERTEX]");
//// trace(AgalUtils.disassemble(vLinker.getByteCode()));
//// trace("[FRAGMENT]");
//// trace(AgalUtils.disassemble(fLinker.getByteCode()));
//
// var result:ShaderProgram;
// if (usePCF) {
// programPCF = new ShaderProgram(vLinker, fLinker);
// result = programPCF;
// } else {
// program = new ShaderProgram(vLinker, fLinker);
// result = program;
// }
// return result;
// }
// override public function drawShadow(mesh:Mesh, camera:Camera3D, texture:Texture):void {
// var context3d:Context3D = camera.view._context3d;
//
// var linkedProgram:ShaderProgram;
// if (pcfOffset > 0) {
// linkedProgram = (programPCF == null) ? initMeshProgram(context3d, true) : programPCF;
// } else {
// linkedProgram = (program == null) ? initMeshProgram(context3d, false) : program;
// }
// var vLinker:Linker = linkedProgram.vLinker;
// var fLinker:Linker = linkedProgram.fLinker;
// context3d.setProgram(linkedProgram.program);
//
// context3d.setVertexBufferAt(vLinker.getVariableIndex("aPOSITION"), mesh.geometry.vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
// context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, vLinker.getVariableIndex("cPROJ"), mesh.projectionMatrix, true);
// applyShader(context3d, linkedProgram, mesh, camera);
// context3d.setVertexBufferAt(1, null);
//
// context3d.setCulling(Context3DTriangleFace.FRONT);
// context3d.drawTriangles(mesh.geometry.indexBuffer, 0, mesh.geometry.numTriangles);
//
// context3d.setVertexBufferAt(vLinker.getVariableIndex("aPOSITION"), null);
// context.setTextureAt(getTextureIndex(fLinker), texture);
// }
}
}

View File

@@ -1,798 +0,0 @@
package alternativa.engine3d.shadows {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Transform3D;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.core.View;
import alternativa.engine3d.lights.OmniLight;
import alternativa.engine3d.materials.FillMaterial;
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.compiler.Linker;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.compiler.VariableType;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.primitives.GeoSphere;
import alternativa.engine3d.resources.Geometry;
import flash.display3D.Context3D;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.Context3DTriangleFace;
import flash.display3D.Program3D;
import flash.display3D.textures.CubeTexture;
import flash.geom.Vector3D;
use namespace alternativa3d;
/**
* @private
*/
public class OmniShadowRenderer extends ShadowRenderer {
// [Embed("geosphere.A3D", mimeType="application/octet-stream")] private static const ModelClass:Class;
private static const debugGeometry:Geometry = createDebugGeometry();
private static function createDebugGeometry():Geometry {
// var parser:ParserA3D = new ParserA3D();
// parser.parse(new ModelClass());
// var mesh:Mesh = Mesh(parser.getObjectByName("sphere"));
// return mesh.geometry;
var geo:GeoSphere = new GeoSphere(0.5, 4);
return geo.geometry;
}
private var caster:Object3D;
private var casterBounds:BoundBox = new BoundBox();
private var pcfOffset:Number = 0;
private var context:Context3D;
public var omnies:Vector.<OmniLight>;
private var shadowMap:CubeTexture;
private var shadowMapSize:int;
private var cameras:Vector.<Camera3D> = new Vector.<Camera3D>();
private var clearBits:uint = 0xFF;
private var currentOmni:OmniLight = new OmniLight(0, 0, 0);
private static const constants:Vector.<Number> = Vector.<Number>([
255, 0.97, 10000, 1/255
]);
private static const offset:Number = 0.005;
private static const pcfOffsets:Vector.<Number> = Vector.<Number>([
-offset, -offset, -offset, 1/8,
-offset, -offset, offset, 1,
-offset, offset, -offset, 1,
-offset, offset, offset, 1,
offset, -offset, -offset, 1,
offset, -offset, offset, 1,
offset, offset, -offset, 1,
offset, offset, offset, 1,
]);
public function OmniShadowRenderer(context:Context3D, size:int, pcfSize:Number = 0) {
this.context = context;
this.pcfOffset = pcfSize;
shadowMapSize = size;
shadowMap = context.createCubeTexture(size, Context3DTextureFormat.BGRA, true);
debugGeometry.upload(context);
for (var i:int = 0; i < 6; i++) {
var cam:Camera3D = new Camera3D(1, 100);
cam.fov = 1.910633237;
cam.view = new View(size, size);
cameras[i] = cam;
}
// Left
cameras[1].rotationY = -Math.PI/2;
cameras[1].scaleY = -1;
// Right
cameras[0].rotationY = Math.PI/2;
cameras[0].scaleY = -1;
// Back
cameras[3].rotationX = -Math.PI/2;
cameras[3].rotationZ = Math.PI;
cameras[3].scaleX = -1;
// Front
cameras[2].rotationX = -Math.PI/2;
cameras[2].scaleY = -1;
// Bottom
cameras[5].rotationX = Math.PI;
cameras[5].scaleX = -1;
// Top
cameras[4].rotationX = 0;
cameras[4].scaleY = -1;
}
alternativa3d override function cullReciever(boundBox:BoundBox, object:Object3D):Boolean {
// tempBounds.reset();
// object.localToCameraTransform.copy(object.localToGlobalTransform);
// StaticShadowRenderer.calculateBoundBox(tempBounds, object, false);
var bounds:BoundBox = object.boundBox;
object.globalToLocalTransform.copy(object.localToGlobalTransform);
object.globalToLocalTransform.invert();
var inverseMatrix:Transform3D = object.globalToLocalTransform;
// trace(object.scaleX, object.scaleY, object.scaleZ);
var ox:Number = inverseMatrix.a*currentOmni._x + inverseMatrix.b*currentOmni._y + inverseMatrix.c*currentOmni._z + inverseMatrix.d;
var oy:Number = inverseMatrix.e*currentOmni._x + inverseMatrix.f*currentOmni._y + inverseMatrix.g*currentOmni._z + inverseMatrix.h;
var oz:Number = inverseMatrix.i*currentOmni._x + inverseMatrix.j*currentOmni._y + inverseMatrix.k*currentOmni._z + inverseMatrix.l;
var radius:Number = currentOmni.attenuationEnd;
if (ox + radius > bounds.minX && ox - radius < bounds.maxX && oy + radius > bounds.minY && oy - radius < bounds.maxY && oz + radius > bounds.minZ && oz - radius < bounds.maxZ) {
return true;
}
return false;
}
alternativa3d override function get needMultiplyBlend():Boolean {
return true;
}
public var debugObject:Mesh = new Mesh();
// TODO: repair
// private var debugMaterial:OmniShadowRendererDebugMaterial = new OmniShadowRendererDebugMaterial();
private var debugMaterial:Object;
public function setCaster(object:Object3D):void {
caster = object;
object.localToCameraTransform.identity();
StaticShadowRenderer.calculateBoundBox(casterBounds, object);
debugObject.geometry = debugGeometry;
// debugObject.addSurface(debugMaterial, 0, debugGeometry.numTriangles);
debugObject.addSurface(new FillMaterial(0xFFFFFF), 0, debugGeometry.numTriangles);
debugObject.scaleX = 400;
debugObject.scaleY = 400;
debugObject.scaleZ = 400;
}
private var culledOmnies:Vector.<OmniLight> = new Vector.<OmniLight>();
private var influences:Vector.<Number> = new Vector.<Number>();
private static const inverseMatrix:Transform3D = new Transform3D();
// private static const omniLocalCoords:Vector.<Number> = new Vector.<Number>(3);
override public function update():void {
// Расчет матрицы объекта
caster.localToGlobalTransform.compose(caster._x, caster._y, caster._z, caster._rotationX, caster._rotationY, caster._rotationZ, caster._scaleX, caster._scaleY, caster._scaleZ);
var root:Object3D = caster;
while (root._parent != null) {
root = root._parent;
root.localToGlobalTransform.compose(root._x, root._y, root._z, root._rotationX, root._rotationY, root._rotationZ, root._scaleX, root._scaleY, root._scaleZ);
caster.localToGlobalTransform.append(root.localToGlobalTransform);
}
// Расчет матрицы перевода в объект
caster.globalToLocalTransform.copy(caster.localToGlobalTransform);
caster.globalToLocalTransform.invert();
/**
// // Вычисление множителя масштаба
// caster.inverseCameraMatrix.transformVectors(sIn, sOut);
// var dx:Number = sOut[0] - sOut[3];
// var dy:Number = sOut[1] - sOut[4];
// var dz:Number = sOut[2] - sOut[5];
// var scale:Number = Math.sqrt(dx*dx + dy*dy + dz*dz);*/
// var selectedOmni:OmniLight;
// var selectedOmniInfluence:Number = -1;
var influenceSum:Number = 0;
var omni:OmniLight;
culledOmnies.length = 0;
influences.length = 0;
// Куллинг источников света и нахождение основного
for each (omni in omnies) {
// Вычисление глобальной позиции омника
inverseMatrix.identity();
var parent:Object3D = omni._parent;
if (parent != null) {
parent.localToGlobalTransform.compose(parent._x, parent._y, parent._z, parent._rotationX, parent._rotationY, parent._rotationZ, parent._scaleX, parent._scaleY, parent._scaleZ);
root = parent;
while (root._parent != null) {
if (root == caster || parent == caster) {
throw new Error("Caster can not be parent of light");
}
root = root._parent;
root.localToGlobalTransform.compose(root._x, root._y, root._z, root._rotationX, root._rotationY, root._rotationZ, root._scaleX, root._scaleY, root._scaleZ);
parent.localToGlobalTransform.append(root.localToGlobalTransform);
}
inverseMatrix.append(parent.localToGlobalTransform);
}
inverseMatrix.append(caster.globalToLocalTransform);
var ox:Number = inverseMatrix.a*omni._x + inverseMatrix.b*omni._y + inverseMatrix.c*omni._z + inverseMatrix.d;
var oy:Number = inverseMatrix.e*omni._x + inverseMatrix.f*omni._y + inverseMatrix.g*omni._z + inverseMatrix.h;
var oz:Number = inverseMatrix.i*omni._x + inverseMatrix.j*omni._y + inverseMatrix.k*omni._z + inverseMatrix.l;
// Использовать описывающий баунд-бокс объекта
// Куллинг
if (ox + omni.attenuationEnd > casterBounds.minX && ox - omni.attenuationEnd < casterBounds.maxX && oy + omni.attenuationEnd > casterBounds.minY && oy - omni.attenuationEnd < casterBounds.maxY && oz + omni.attenuationEnd > casterBounds.minZ && oz - omni.attenuationEnd < casterBounds.maxZ) {
// В зоне действия источника
// Считаем степень влияния
var d:Number = Math.sqrt(ox*ox + oy*oy + oz*oz)/omni.attenuationEnd - 0.1;
var influence:Number;
if (d > 1) {
influence = 0;
} else {
influence = omni.intensity*calcBrightness(omni.color) * (1 - d);
}
// if (influence > selectedOmniInfluence) {
// selectedOmni = omni;
// selectedOmniInfluence = influence;
// }
influenceSum += influence;
influences.push(influence);
culledOmnies.push(omni);
}
}
debugMaterial.texture = null;
var i:int;
var surface:uint;
var drawed:int = 0;
/** if (selectedOmni == null || influenceSum <= 0) {*/
if (culledOmnies.length == 0 || influenceSum <= 0) {
// Ни один источник не влияет
for (i = 0; i < 6; i++) {
surface = 1 << i;
if (clearBits & surface) {
context.setRenderToTexture(shadowMap, true, 0, i);
// context.clear(1);
context.clear(1, 1, 1, 1);
// trace("clear", i);
clearBits &= ~surface;
}
}
// trace("INVISIBLE");
} else {
currentOmni._x = 0;
currentOmni._y = 0;
currentOmni._z = 0;
currentOmni.attenuationEnd = 0;
for (i = 0; i < culledOmnies.length; i++) {
var weight:Number = influences[i]/influenceSum;
omni = culledOmnies[i];
// Считаем матрицу перевода в глобальное пространство из омника
omni.localToGlobalTransform.identity();
omni.localToGlobalTransform.d = omni.x;
omni.localToGlobalTransform.h = omni.y;
omni.localToGlobalTransform.l = omni.z;
root = omni;
while (root._parent != null) {
root = root._parent;
if (root.transformChanged) root.composeTransforms();
omni.localToGlobalTransform.append(root.transform);
}
currentOmni._x += omni.localToGlobalTransform.d*weight;
currentOmni._y += omni.localToGlobalTransform.h*weight;
currentOmni._z += omni.localToGlobalTransform.l*weight;
currentOmni.attenuationEnd += omni.attenuationEnd*weight;
}
currentOmni.localToGlobalTransform.identity();
currentOmni.localToGlobalTransform.d = currentOmni._x;
currentOmni.localToGlobalTransform.h = currentOmni._y;
currentOmni.localToGlobalTransform.l = currentOmni._z;
// constants[3] = 0.5*1/255;
constants[3] = 1.0/255;
/** // Расчитываем яркость тени
// var weight:Number = (selectedOmniInfluence > 0) ? 1 - (influenceSum - selectedOmniInfluence)/influenceSum : 0;
// trace(weight, influenceSum, selectedOmniInfluence);
// trace(weight);
// var weight:Number = 1;
// if (weight > 0) {
// constants[3] = (1 + (1 - weight)*5)/255;
// } else {
// constants[3] = 1/255;
// }
// // Считаем матрицу перевода в глобальное пространство из омника
// selectedOmni.cameraMatrix.identity();
// selectedOmni.cameraMatrix.appendTranslation(selectedOmni.x, selectedOmni.y, selectedOmni.z);
//// selectedOmni.composeMatrix();
// root = selectedOmni;
// while (root._parent != null) {
// root = root._parent;
// root.composeMatrix();
// selectedOmni.cameraMatrix.append(root.cameraMatrix);
// }
//// // Матрица родителя уже посчитана
//// if (omni._parent != null) {
//// omni.cameraMatrix.append(omni._parent.cameraMatrix);
//// }
// selectedOmni.globalCoords[0] = 0;
// selectedOmni.globalCoords[1] = 0;
// selectedOmni.globalCoords[2] = 0;
// selectedOmni.cameraMatrix.transformVectors(selectedOmni.globalCoords, selectedOmni.globalCoords); */
// Записываем параметры омника в константы
debugObject.x = currentOmni._x;
debugObject.y = currentOmni._y;
debugObject.z = currentOmni._z;
cleanContext(context);
for (i = 0; i < 6; i++) {
surface = 1 << i;
context.setRenderToTexture(shadowMap, true, 0, i);
// trace("SIDE:", i);
if (renderToOmniShadowMap(currentOmni, cameras[i])) {
drawed++;
clearBits |= surface;
} else {
if (clearBits & surface) {
// trace("clear", i);
context.clear(1, 1, 1, 1);
// context.clear(1, 1, 1, 1);
clearBits &= ~surface;
}
}
}
// trace("NUMSIDES:", drawed);
debugMaterial.texture = shadowMap;
}
context.setRenderToBackBuffer();
cleanContext(context);
active = drawed > 0;
}
private function calcBrightness(color:uint):Number {
var r:uint = color & 0xFF;
var g:uint = (color >> 8) & 0xFF;
var b:uint = (color >> 16) & 0xFF;
var result:uint = (r > g) ? ((r > b) ? r : b) : ((g > b) ? g : b);
return result/255;
}
// private var axises:Vector.<Number> = Vector.<Number>([
// 1, 0, 0,
// 0, 1, 0,
// ]);
// private var globalAxises:Vector.<Number> = new Vector.<Number>(6);
public function renderToOmniShadowMap(omni:OmniLight, camera:Camera3D):Boolean {
camera.nearClipping = 1;
camera.farClipping = omni.attenuationEnd;
// Расчёт параметров проецирования
camera.calculateProjection(camera.view._width, camera.view._height);
if (camera.transformChanged) camera.composeTransforms();
// Считаем омник родительским объектом камеры
camera.localToGlobalTransform.combine(omni.localToGlobalTransform, camera.transform);
camera.globalToLocalTransform.copy(camera.localToGlobalTransform);
camera.globalToLocalTransform.invert();
caster.localToCameraTransform.compose(caster._x, caster._y, caster._z, caster._rotationX, caster._rotationY, caster._rotationZ, caster._scaleX, caster._scaleY, caster._scaleZ);
var root:Object3D = caster;
while (root._parent != null) {
root = root._parent;
if (root.transformChanged) root.composeTransforms();
caster.localToCameraTransform.append(root.transform);
}
caster.localToCameraTransform.append(camera.globalToLocalTransform);
/** if (pcfOffset > 0.1) {
// axises[0] = pcfOffset;
// // Считаем преобразования PCF
// camera.globalMatrix.transformVectors(axises, globalAxises);
// pcfOffsets[0] = -pcfOffset*globalAxises[0];
// pcfOffsets[1] = -pcfOffset*globalAxises[1];
// pcfOffsets[2] = -pcfOffset*globalAxises[2];
// pcfOffsets[4] = pcfOffset*globalAxises[0];
// pcfOffsets[5] = pcfOffset*globalAxises[1];
// pcfOffsets[6] = pcfOffset*globalAxises[2];
// pcfOffsets[8] = -pcfOffset*globalAxises[3];
// pcfOffsets[9] = -pcfOffset*globalAxises[4];
// pcfOffsets[10] = -pcfOffset*globalAxises[5];
// pcfOffsets[12] = pcfOffset*globalAxises[3];
// pcfOffsets[13] = pcfOffset*globalAxises[4];
// pcfOffsets[14] = pcfOffset*globalAxises[5];
} */
// Отрисовка в шедоумапу
if (cullingInCamera(caster, casterBounds)) {
context.clear(1, 1, 0, 1);
drawObjectToShadowMap(context, caster, camera);
return true;
}
return false;
}
private static const points:Vector.<Vector3D> = Vector.<Vector3D>([
new Vector3D(), new Vector3D(), new Vector3D(), new Vector3D(),
new Vector3D(), new Vector3D(), new Vector3D(), new Vector3D(),
new Vector3D(), new Vector3D(), new Vector3D(), new Vector3D(),
new Vector3D(), new Vector3D(), new Vector3D(), new Vector3D()
]);
private static const boundVertices:Vector.<Number> = new Vector.<Number>(24);
alternativa3d function cullingInCamera(object:Object3D, objectBounds:BoundBox):Boolean {
var i:int;
var infront:Boolean;
var behind:Boolean;
// Заполнение
var point:Vector3D;
var bb:BoundBox = objectBounds;
point = points[0];
point.x = bb.minX;
point.y = bb.minY;
point.z = bb.minZ;
point = points[1];
point.x = bb.minX;
point.y = bb.minY;
point.z = bb.maxZ;
point = points[2];
point.x = bb.minX;
point.y = bb.maxY;
point.z = bb.minZ;
point = points[3];
point.x = bb.minX;
point.y = bb.maxY;
point.z = bb.maxZ;
point = points[4];
point.x = bb.maxX;
point.y = bb.minY;
point.z = bb.minZ;
point = points[5];
point.x = bb.maxX;
point.y = bb.minY;
point.z = bb.maxZ;
point = points[6];
point.x = bb.maxX;
point.y = bb.maxY;
point.z = bb.minZ;
point = points[7];
point.x = bb.maxX;
point.y = bb.maxY;
point.z = bb.maxZ;
// Коррекция под 90 градусов
var transform:Transform3D = object.localToCameraTransform;
for (i = 0; i < 8; i++) {
point = points[i];
var x:Number = transform.a*point.x + transform.b*point.y + transform.c*point.z + transform.d;
var y:Number = transform.e*point.x + transform.f*point.y + transform.g*point.z + transform.h;
var z:Number = transform.i*point.x + transform.j*point.y + transform.k*point.z + transform.l;
var index:int = 3*i;
boundVertices[int(index++)] = x;
boundVertices[int(index++)] = y;
boundVertices[index] = z;
}
// Куллинг
for (i = 0, infront = false, behind = false; i <= 21; i += 3) {
if (-boundVertices[i] < boundVertices[int(i + 2)]) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
// trace("L", infront);
if (!infront) return false;
}
for (i = 0, infront = false, behind = false; i <= 21; i += 3) {
if (boundVertices[i] < boundVertices[int(i + 2)]) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
// trace("R", infront);
if (!infront) return false;
}
for (i = 1, infront = false, behind = false; i <= 22; i += 3) {
if (-boundVertices[i] < boundVertices[int(i + 1)]) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
// trace("U", infront);
if (!infront) return false;
}
for (i = 1, infront = false, behind = false; i <= 22; i += 3) {
if (boundVertices[i] < boundVertices[int(i + 1)]) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
// trace("D", infront);
if (!infront) return false;
}
return true;
}
alternativa3d static function drawObjectToShadowMap(context:Context3D, object:Object3D, camera:Camera3D):void {
if (object is Mesh) {
drawMeshToShadowMap(context, Mesh(object), camera);
}
for (var child:Object3D = object.childrenList; child != null; child = child.next) {
if (child.visible) {
if (child.transformChanged) child.composeTransforms();
child.localToCameraTransform.combine(object.localToCameraTransform, child.transform);
drawObjectToShadowMap(context, child, camera);
}
}
}
private static function copyRawFromTransform(raw:Vector.<Number>, transform:Transform3D):void {
raw[0] = transform.a;
raw[1] = transform.b;
raw[2] = transform.c;
raw[3] = transform.d;
raw[4] = transform.e;
raw[5] = transform.f;
raw[6] = transform.g;
raw[7] = transform.h;
raw[8] = transform.i;
raw[9] = transform.j;
raw[10] = transform.k;
raw[11] = transform.l;
raw[12] = 0;
raw[13] = 0;
raw[14] = 0;
raw[15] = 1;
}
private static var shadowMapProgram:Program3D;
private static var projectionVector:Vector.<Number> = new Vector.<Number>(16);
private static function drawMeshToShadowMap(context:Context3D, mesh:Mesh, camera:Camera3D):void {
if (mesh.geometry == null || mesh.geometry.numTriangles == 0 || !mesh.geometry.isUploaded) {
return;
}
// TODO : update to new logic
if (shadowMapProgram == null) shadowMapProgram = initMeshToShadowMapProgram(context);
context.setProgram(shadowMapProgram);
context.setVertexBufferAt(0, mesh.geometry.getVertexBuffer(VertexAttributes.POSITION), mesh.geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
// TODO: uncomment
// camera.composeProjectionMatrix(projectionVector, 0, mesh.localToCameraTransform);
context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 0, projectionVector, 4);
copyRawFromTransform(projectionVector, mesh.localToCameraTransform);
context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 4, projectionVector, 4);
context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 8, Vector.<Number>([Math.sqrt(255)/camera.farClipping, 0, 0, 1]));
context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([1/255, 0, 0, 1]));
context.setCulling(Context3DTriangleFace.BACK);
for (var i:int = 0; i < mesh._surfacesLength; i++) {
var surface:Surface = mesh._surfaces[i];
if (surface.material == null) continue;
context.drawTriangles(mesh.geometry._indexBuffer, surface.indexBegin, surface.numTriangles);
}
context.setVertexBufferAt(0, null);
}
private static function initMeshToShadowMapProgram(context3d:Context3D):Program3D {
var vLinker:Linker = new Linker(Context3DProgramType.VERTEX);
var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
var proc:Procedure = Procedure.compileFromArray([
"#a0=a0",
"#c8=c8",
"#v0=v0",
"m44 o0, a0, c0",
"m44 t0, a0, c4",
"mul t0, t0, c8.x",
"mov v0, t0"
]);
proc.assignVariableName(VariableType.CONSTANT, 0, "c0", 4);
proc.assignVariableName(VariableType.CONSTANT, 4, "c4", 4);
vLinker.addProcedure(proc);
fLinker.addProcedure(Procedure.compileFromArray([
"#v0=v0",
"#c0=c0",
"mov t0.zw, c0.z \n",
"dp3 t1.w, v0.xyz, v0.xyz \n",
"frc t0.y, t1.w",
"sub t0.x, t1.w, t0.y",
"mul t0.x, t0.x, c0.x",
"mov o0, to"
]));
var program:Program3D = context3d.createProgram();
// trace("VERTEX");
// trace(A3DUtils.disassemble(vLinker.getByteCode()));
// trace("FRAGMENT");
// trace(A3DUtils.disassemble(fLinker.getByteCode()));
fLinker.varyings = vLinker.varyings;
vLinker.link();
fLinker.link();
program.upload(vLinker.data, fLinker.data);
return program;
}
private static function initVShader():Procedure {
var shader:Procedure = Procedure.compileFromArray([
// Координата вершины в глобальном пространстве
"m44 v0, a0, c0"
]);
shader.assignVariableName(VariableType.ATTRIBUTE, 0, "aPosition");
shader.assignVariableName(VariableType.CONSTANT, 0, "cGLOBALMATRIX", 4);
shader.assignVariableName(VariableType.VARYING, 0, "vPOSITION");
return shader;
}
private static function initFShader(mult:Boolean, usePCF:Boolean):Procedure {
var line:int = 0;
var shaderArr:Array = [];
var numPass:uint = (usePCF) ? 8 : 1;
for (var i:int = 0; i < numPass; i++) {
// Вектор от источника света к точке
shaderArr[line++] = "sub t0.xyz, v0.xyz, c4.xyz";
// Квадрат расстояния
shaderArr[line++] = "dp3 t0.w, t0.xyz, t0.xyz";
shaderArr[line++] = "mul t0.w, t0.w, c4.w"; // * (255 / radius^2)
shaderArr[line++] = "mul t0.w, t0.w, c5.y"; // bias [0.95]
// Квадрат расстояния из карты теней
shaderArr[line++] = "nrm t0.xyz, t0.xyz";
if (usePCF) {
shaderArr[line++] = "add t0.xyz, t0.xyz, c" + (i + 6).toString();
}
shaderArr[line++] = "tex t1, t0, s0 <cube,clamp,near,nomip>";
shaderArr[line++] = "mov t3, t1";
shaderArr[line++] = "mul t1.w, t1.x, c5.x"; // 255
shaderArr[line++] = "add t1.w, t1.w, t1.y";
// Перекрытие тенью
shaderArr[line++] = "sub t2.z, t1.w, t0.w";
shaderArr[line++] = "mul t2.z, t2.z, c5.z"; // smooth [10000]
shaderArr[line++] = "sat t2.z, t2.z";
// // Затухание тени по расстоянию
shaderArr[line++] = "mul t1.x, t0.w, c5.w"; // div 255
shaderArr[line++] = "add t2.z, t2.z, t1.x";
if (i == 0) {
shaderArr[line++] = "sat t2.x, t2.z";
} else {
shaderArr[line++] = "sat t2.z, t2.z";
shaderArr[line++] = "add t2.x, t2.x, t2.z";
}
}
if (usePCF) {
shaderArr[line++] = "mul t2.x, t2.x, c6.w";
}
if (mult) {
shaderArr.push("mul t0.xyz, i0.xyz, t2.x");
// shaderArr.push("mul t0.xyz, t1.w, c5.w");
shaderArr.push("mov t0.w, i0.w");
shaderArr.push("mov o0, t0");
} else {
shaderArr.push("mov o0, t2.xxxx");
}
var shader:Procedure = Procedure.compileFromArray(shaderArr, "OmniShadowMap");
shader.assignVariableName(VariableType.VARYING, 0, "vPOSITION");
shader.assignVariableName(VariableType.CONSTANT, 4, "cOmni", 1);
shader.assignVariableName(VariableType.CONSTANT, 5, "cConstants", 1);
if (usePCF) {
shader.assignVariableName(VariableType.CONSTANT, 6, "cPCF0", 1);
shader.assignVariableName(VariableType.CONSTANT, 7, "cPCF1", 1);
shader.assignVariableName(VariableType.CONSTANT, 8, "cPCF2", 1);
shader.assignVariableName(VariableType.CONSTANT, 9, "cPCF3", 1);
shader.assignVariableName(VariableType.CONSTANT, 10, "cPCF4", 1);
shader.assignVariableName(VariableType.CONSTANT, 11, "cPCF5", 1);
shader.assignVariableName(VariableType.CONSTANT, 12, "cPCF6", 1);
shader.assignVariableName(VariableType.CONSTANT, 13, "cPCF7", 1);
}
shader.assignVariableName(VariableType.SAMPLER, 0, "sCUBE");
return shader;
}
override public function getVShader(index:int = 0):Procedure {
return initVShader();
}
override public function getFShader(index:int = 0):Procedure {
return initFShader(false, pcfOffset > 0);
}
private static const globalMatrix:Transform3D = new Transform3D();
override public function applyShader(drawUnit:DrawUnit, program:ShaderProgram, object:Object3D, camera:Camera3D, index:int = 0):void {
var fLinker:Linker = program.fragmentShader;
globalMatrix.combine(camera.localToGlobalTransform, object.localToCameraTransform);
var mIndex:int = program.vertexShader.getVariableIndex("cGLOBALMATRIX");
drawUnit.setVertexConstantsFromNumbers(mIndex, globalMatrix.a, globalMatrix.b, globalMatrix.c, globalMatrix.d);
drawUnit.setVertexConstantsFromNumbers(mIndex+1, globalMatrix.e, globalMatrix.f, globalMatrix.g, globalMatrix.h);
drawUnit.setVertexConstantsFromNumbers(mIndex+2, globalMatrix.i, globalMatrix.j, globalMatrix.k, globalMatrix.l);
drawUnit.setVertexConstantsFromNumbers(mIndex+3, 0, 0, 0, 1);
// destination.addFragmentConstantSet(fLinker.getVariableIndex("cOmni"), omniPos, 1);
drawUnit.setFragmentConstantsFromNumbers(fLinker.getVariableIndex("cOmni"), currentOmni._x, currentOmni._y, currentOmni._z, 255/currentOmni.attenuationEnd/currentOmni.attenuationEnd);
drawUnit.setFragmentConstantsFromVector(fLinker.getVariableIndex("cConstants"), constants, 1);
if (pcfOffset > 0) {
drawUnit.setVertexConstantsFromVector(fLinker.getVariableIndex("cPCF0"), pcfOffsets, 8);
}
drawUnit.setTextureAt(fLinker.getVariableIndex("sCUBE"), shadowMap);
}
// private static var program:LinkedProgram;
// private static var programPCF:LinkedProgram;
// private static function initMeshProgram(context:Context3D, usePCF:Boolean):LinkedProgram {
// var vLinker:Linker = new Linker(Context3DProgramType.VERTEX);
// vLinker.addShader(vShader);
//
// var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
// if (usePCF) {
// fLinker.addShader(pcfFShader);
// } else {
// fLinker.addShader(fShader);
// }
//
// vLinker.setOppositeLinker(fLinker);
// fLinker.setOppositeLinker(vLinker);
//
// trace("[VERTEX]");
// trace(AgalUtils.disassemble(vLinker.getByteCode()));
// trace("[FRAGMENT]");
// trace(AgalUtils.disassemble(fLinker.getByteCode()));
//
// var result:LinkedProgram;
// if (usePCF) {
// programPCF = new LinkedProgram();
// result = programPCF;
// } else {
// program = new LinkedProgram();
// result = program;
// }
// result.vLinker = vLinker;
// result.fLinker = fLinker;
// result.program = context.createProgram();
// result.program.upload(vLinker.getByteCode(), fLinker.getByteCode());
//
// return result;
// }
//
// override public function drawShadow(mesh:Mesh, camera:Camera3D, texture:Texture):void {
// var context3d:Context3D = camera.view._context3d;
//
// var linkedProgram:LinkedProgram;
// if (pcfOffset > 0) {
// linkedProgram = (programPCF == null) ? initMeshProgram(context3d, true) : programPCF;
// } else {
// linkedProgram = (program == null) ? initMeshProgram(context3d, false) : program;
// }
// var vLinker:Linker = linkedProgram.vLinker;
// var fLinker:Linker = linkedProgram.fLinker;
// context3d.setProgram(linkedProgram.program);
//
// context3d.setVertexBufferAt(vLinker.getVariableIndex("aPOSITION"), mesh.geometry.vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
// context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, vLinker.getVariableIndex("cPROJ"), mesh.projectionMatrix, true);
// applyShader(context3d, linkedProgram, mesh, camera);
// context3d.setVertexBufferAt(1, null);
//
// context3d.setCulling(Context3DTriangleFace.FRONT);
// context3d.drawTriangles(mesh.geometry.indexBuffer, 0, mesh.geometry.numTriangles);
//
// context3d.setVertexBufferAt(vLinker.getVariableIndex("aPOSITION"), null);
// context.setTextureAt(getTextureIndex(fLinker), texture);
// }
}
}

View File

@@ -1,162 +0,0 @@
package alternativa.engine3d.shadows {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.materials.*;
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 flash.display3D.Context3DProgramType;
import flash.display3D.textures.CubeTexture;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* @private
*/
public class OmniShadowRendererDebugMaterial extends Material {
alternativa3d override function get canDrawInShadowMap():Boolean {
return false;
}
static alternativa3d const _samplerSetProcedure:Procedure = new Procedure(
[
"#v0=vUV",
"#s0=sTexture",
"#c0=cAlpha",
"tex t0, v0, s0 <cube,clamp,near,nomip>",
"mov t0.w, c0.w",
"mov o0, t0"
]);
static alternativa3d const _samplerSetProcedureDiffuseAlpha:Procedure = new Procedure(
[
"#v0=vUV",
"#s0=sTexture",
"#c0=cAlpha",
"tex t0, v0, s0 <cube,clamp,near,nomip>",
"mul t0.w, t0.w, c0.w",
"mov o0, t0"
]);
static alternativa3d const _passUVProcedure:Procedure = new Procedure(["#v0=vUV", "#a0=aNORMAL", "mov v0, a0"]);
private static var _programs:Dictionary = new Dictionary();
/**
* Текстура
*/
public var texture:CubeTexture;
/**
* Прозрачность
*/
public var alpha:Number = 1;
/**
* Использование alpha канала текстуры
*/
public var useDiffuseAlphaChannel:Boolean = false;
/**
* Создает экземпляр материала
* @param texture текстура
* @param alpha прозрачность
*/
public function OmniShadowRendererDebugMaterial(texture:CubeTexture = null, alpha:Number = 1) {
this.texture = texture;
this.alpha = alpha;
}
private function setupProgram(targetObject:Object3D):Vector.<ShaderProgram> {
var optionsPrograms:Vector.<ShaderProgram> = new Vector.<ShaderProgram>();
var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
var positionVar:String = "aPosition";
vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
if (targetObject.transformProcedure != null) {
positionVar = appendPositionTransformProcedure(targetObject.transformProcedure, vertexLinker);
}
vertexLinker.addProcedure(_projectProcedure);
vertexLinker.setInputParams(_projectProcedure, positionVar);
vertexLinker.addProcedure(_passUVProcedure);
vertexLinker.link();
var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
fragmentLinker.addProcedure(_samplerSetProcedure);
fragmentLinker.varyings = vertexLinker.varyings;
optionsPrograms[optionsPrograms.length] = new ShaderProgram(vertexLinker, fragmentLinker);
var fragmentLinkerDiffuseAlpha:Linker = new Linker(Context3DProgramType.FRAGMENT);
fragmentLinkerDiffuseAlpha.addProcedure(_samplerSetProcedureDiffuseAlpha);
fragmentLinkerDiffuseAlpha.varyings = vertexLinker.varyings;
optionsPrograms[optionsPrograms.length] = new ShaderProgram(vertexLinker, fragmentLinkerDiffuseAlpha);
// trace(A3DUtils.disassemble(fragmentLinker.getByteCode()));
_programs[targetObject.transformProcedure] = optionsPrograms;
return optionsPrograms;
}
/**
* @private
*/
override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, objectRenderPriority:int = -1):void {
// TODO: repair
/*
destination.isValid = destination.isValid && texture != null;
var optionsPrograms:Vector.<ShaderProgram> = _programs[transformHolder.transformProcedure];
if(!optionsPrograms) optionsPrograms = setupProgram(transformHolder);
var program:ShaderProgram;
if(!useDiffuseAlphaChannel){
program = optionsPrograms[0];
}else {
program = optionsPrograms[1];
}
if (!destination.isValid) {
return;
}
if (alpha < 1 || useDiffuseAlphaChannel) {
destination.blendModeSource = Context3DBlendFactor.SOURCE_ALPHA;
destination.blendModeDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
destination.renderPriority = Renderer.TRANSPARENT_SORT;
} else {
destination.blendModeSource = Context3DBlendFactor.ONE;
destination.blendModeDestination = Context3DBlendFactor.ZERO;
destination.renderPriority = Renderer.OPAQUE;
}
destination.program = program;
geometry.setAttribute(destination, VertexAttributes.POSITION, program.vertexShader.getVariableIndex("aPosition"));
geometry.setAttribute(destination, VertexAttributes.NORMAL, program.vertexShader.getVariableIndex("aNORMAL"));
camera.composeProjectionMatrix(destination.constantSetVertexVectorValues, program.vertexShader.getVariableIndex("cProjMatrix") << 2, transformHolder.localToCameraTransform);
destination.constantSetVertexRegistersCount = destination.constantSetVertexVectorValues.length >> 2;
destination.addTextureSet(program.fragmentShader.getVariableIndex("sTexture"), texture);
destination.addFragmentConstantVector(program.fragmentShader.getVariableIndex("cAlpha"), 0, 0, 0, alpha);
destination.cullingMode = cullingMode;
*/
}
/**
* @inheritDoc
*/
override public function clone():Material {
var res:OmniShadowRendererDebugMaterial = new OmniShadowRendererDebugMaterial(texture, alpha);
res.clonePropertiesFrom(this);
return res;
}
override protected function clonePropertiesFrom(source:Material):void {
super.clonePropertiesFrom(source);
var t:OmniShadowRendererDebugMaterial = OmniShadowRendererDebugMaterial(source);
texture = t.texture;
alpha = t.alpha;
}
}
}

View File

@@ -1,184 +0,0 @@
package alternativa.engine3d.shadows {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.compiler.Procedure;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DCompareMode;
import flash.geom.Matrix3D;
use namespace alternativa3d;
/**
* @private
*/
public class ShadowRenderer {
alternativa3d var shaderKey:String;
private static var counter:int = 0;
public var active:Boolean = false;
public function ShadowRenderer() {
counter++;
shaderKey = "M" + counter.toString();
}
alternativa3d function get needMultiplyBlend():Boolean {
return false;
}
public function update():void {}
// Нет входных, выходных параметров
public function getVShader(index:int = 0):Procedure {return null}
public function getFShader(index:int = 0):Procedure {return null}
public function getFIntensityShader():Procedure { throw new Error("Not implemented") };
// public function getMultVShader():Procedure {return null};
// // i0 - input color
// // o0 - shadowed result
// public function getMultFShader():Procedure {return null};
public function applyShader(destination:DrawUnit, program:ShaderProgram, object:Object3D, camera:Camera3D, index:int = 0):void {}
// public function drawShadow(mesh:Mesh, camera:Camera3D, texture:Texture):void {}
// public function getTextureIndex(fLinker:Linker):int {return 0};
public function get debug():Boolean { return false }
public function set debug(value:Boolean):void { }
alternativa3d function cullReciever(boundBox:BoundBox, object:Object3D):Boolean {
return false;
}
protected function cleanContext(context:Context3D):void {
context.setTextureAt(0, null);
context.setTextureAt(1, null);
context.setTextureAt(2, null);
context.setTextureAt(3, null);
context.setTextureAt(4, null);
context.setTextureAt(5, null);
context.setTextureAt(6, null);
context.setTextureAt(7, null);
context.setVertexBufferAt(1, null);
context.setVertexBufferAt(2, null);
context.setVertexBufferAt(3, null);
context.setVertexBufferAt(4, null);
context.setVertexBufferAt(5, null);
context.setVertexBufferAt(6, null);
context.setVertexBufferAt(7, null);
context.setDepthTest(true, Context3DCompareMode.LESS);
context.setBlendFactors(Context3DBlendFactor.ONE, Context3DBlendFactor.ZERO);
}
static private const boundVertices:Vector.<Number> = new Vector.<Number>(24);
alternativa3d function cullObjectImplementation(bounds:BoundBox, matrix:Matrix3D):Boolean {
var i:int;
var infront:Boolean;
var behind:Boolean;
// Заполнение
boundVertices[0] = bounds.minX;
boundVertices[1] = bounds.minY;
boundVertices[2] = bounds.minZ;
boundVertices[3] = bounds.maxX;
boundVertices[4] = bounds.minY;
boundVertices[5] = bounds.minZ;
boundVertices[6] = bounds.minX;
boundVertices[7] = bounds.maxY;
boundVertices[8] = bounds.minZ;
boundVertices[9] = bounds.maxX;
boundVertices[10] = bounds.maxY;
boundVertices[11] = bounds.minZ;
boundVertices[12] = bounds.minX;
boundVertices[13] = bounds.minY;
boundVertices[14] = bounds.maxZ;
boundVertices[15] = bounds.maxX;
boundVertices[16] = bounds.minY;
boundVertices[17] = bounds.maxZ;
boundVertices[18] = bounds.minX;
boundVertices[19] = bounds.maxY;
boundVertices[20] = bounds.maxZ;
boundVertices[21] = bounds.maxX;
boundVertices[22] = bounds.maxY;
boundVertices[23] = bounds.maxZ;
// Трансформация в камеру
matrix.transformVectors(boundVertices, boundVertices);
// Куллинг
for (i = 2, infront = false, behind = false; i <= 23; i += 3) {
if (boundVertices[i] > 0) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
if (!infront) return false;
}
// left
for (i = 0, infront = false, behind = false; i <= 21; i += 3) {
if (boundVertices[i] > 0) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
if (!infront) return false;
}
// right
for (i = 0, infront = false, behind = false; i <= 21; i += 3) {
if (boundVertices[i] < 1) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
if (!infront) return false;
}
// up
for (i = 1, infront = false, behind = false; i <= 22; i += 3) {
if (boundVertices[i] > 0) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
if (!infront) return false;
}
// down
for (i = 1, infront = false, behind = false; i <= 22; i += 3) {
if (boundVertices[i] < 1) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
if (!infront) return false;
}
return true;
}
}
}

View File

@@ -1,96 +0,0 @@
package alternativa.engine3d.shadows {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Object3D;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* @private
*/
public class ShadowsSystem {
private static const MAX_SHADOWMAPS:int = 3;
// private static const MAX_SHADOWMAPS:int = 4;
public var renderers:Vector.<ShadowRenderer> = new Vector.<ShadowRenderer>();
private var containers:Dictionary = new Dictionary();
public function ShadowsSystem() {
}
private var numShadowed:int;
private var numActiveRenderers:int;
private var activeRenderers:Vector.<ShadowRenderer> = new Vector.<ShadowRenderer>();
private var maxShadows:int;
public function update(root:Object3D):void {
if (renderers.length == 0) return;
numActiveRenderers = 0;
var num:int = renderers.length;
for (var i:int = 0; i < num; i++) {
var renderer:ShadowRenderer = renderers[i];
renderer.update();
if (renderer.active) {
activeRenderers[numActiveRenderers] = renderer;
numActiveRenderers++;
}
}
// Пробегаемся иерархически по объектам и проверяем наложение на них тени
if (root.transformChanged) root.composeTransforms();
root.localToGlobalTransform.copy(root.transform);
numShadowed = 0;
maxShadows = 0;
recursive(root);
// trace("SHADOWED:", numShadowed, ":", maxShadows);
}
private function recursive(object:Object3D):void {
for (var child:Object3D = object.childrenList; child != null; child = child.next) {
var value:Vector.<ShadowRenderer> = null;
var numRenderers:int = 0;
if (child.visible) {
if (child.transformChanged) child.composeTransforms();
child.localToGlobalTransform.combine(object.localToGlobalTransform, child.transform);
for (var i:int = 0; i < numActiveRenderers; i++) {
var renderer:ShadowRenderer = activeRenderers[i];
if (child.useShadow) {
if (child.boundBox == null || renderer.cullReciever(child.boundBox, child)) {
numShadowed++;
if (value == null) {
value = containers[child];
if (value == null) {
value = new Vector.<ShadowRenderer>();
containers[child] = value;
} else {
value.length = 0;
}
}
value[numRenderers] = renderer;
numRenderers++;
}
}
}
recursive(child);
}
setRenderers(child, value, numRenderers);
}
}
private function setRenderers(object:Object3D, renderers:Vector.<ShadowRenderer>, numShadowRenderers:int):void {
if (numShadowRenderers > maxShadows) maxShadows = numShadowRenderers;
if (numShadowRenderers > MAX_SHADOWMAPS) {
numShadowRenderers = MAX_SHADOWMAPS;
renderers.length = MAX_SHADOWMAPS;
}
object.shadowRenderers = renderers;
object.numShadowRenderers = numShadowRenderers;
}
}
}

View File

@@ -1,656 +0,0 @@
package alternativa.engine3d.shadows {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Transform3D;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.lights.SpotLight;
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.materials.compiler.Linker;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.compiler.VariableType;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.primitives.Box;
import alternativa.engine3d.resources.TextureResource;
import flash.display3D.Context3D;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.Context3DTriangleFace;
import flash.display3D.Program3D;
import flash.display3D.textures.Texture;
import flash.geom.Matrix3D;
use namespace alternativa3d;
/**
* @private
*/
public class SpotShadowRenderer extends ShadowRenderer {
public var caster:Object3D;
private var context:Context3D;
private var shadowMap:Texture;
private var light:SpotLight;
private var debugObject:Mesh;
public var debugMaterial:TextureMaterial = new TextureMaterial();
private var debugTexture:TextureResource = new TextureResource();
private var globalToShadowMap:Matrix3D = new Matrix3D();
private static const constants:Vector.<Number> = Vector.<Number>([
255, 255*0.96, 100, 1
]);
private var pcfSize:Number = 0;
private var pcfOffset:Number = 0;
private var pcfOffsets:Vector.<Number>;
public function SpotShadowRenderer(context:Context3D, size:int, pcfSize:Number = 0) {
this.context = context;
this.shadowMap = context.createTexture(size, size, Context3DTextureFormat.BGRA, true);
this.pcfSize = pcfSize;
debugTexture._texture = this.shadowMap;
// debugMaterial.diffuseMap = debugTexture;
debugMaterial.alpha = 0.9;
debugMaterial.opaquePass = false;
debugMaterial.transparentPass = true;
debugMaterial.alphaThreshold = 1.1;
}
public function setLight(value:SpotLight):void {
light = value;
var width:Number = 2*Math.sin(light.falloff*0.5)*light.attenuationEnd;
this.pcfOffset = pcfSize/width/255;
if (pcfOffset > 0) {
pcfOffsets = Vector.<Number>([
-pcfOffset, -pcfOffset, 0, 1/4,
-pcfOffset, pcfOffset, 0, 1,
pcfOffset, -pcfOffset, 0, 1,
pcfOffset, pcfOffset, 0, 1
]);
}
debugObject = new Box(width, width, 1, 1, 1, 1, false, debugMaterial);
debugObject.rotationX = Math.PI;
debugObject.geometry.upload(context);
if (_debug) {
light.addChild(debugObject);
}
}
private var _debug:Boolean = false;
override public function get debug():Boolean {
return _debug;
}
override public function set debug(value:Boolean):void {
_debug = value;
if (_debug) {
if (light != null) {
light.addChild(debugObject);
}
} else {
if (debugObject != null && debugObject._parent != null) {
debugObject._parent.removeChild(debugObject);
}
}
}
private static var matrix:Matrix3D = new Matrix3D();
override alternativa3d function cullReciever(boundBox:BoundBox, object:Object3D):Boolean {
copyMatrixFromTransform(matrix, object.localToGlobalTransform);
matrix.append(this.globalToShadowMap);
return cullObjectImpl(boundBox, matrix);
}
private function cullCaster(boundBox:BoundBox, objectToGlobal:Transform3D):Boolean {
copyMatrixFromTransform(matrix, objectToGlobal);
matrix.append(this.globalToShadowMap);
return cullObjectImpl(boundBox, matrix, light.attenuationEnd);
}
private var projection:ProjectionTransform3D = new ProjectionTransform3D();
private var uvProjection:Matrix3D = new Matrix3D();
override public function update():void {
// Считаем матрицу перевода в лайт
var root:Object3D;
// Расчитываем матрицу объекта для перевода в глобал
// if (caster.transformChanged) {
caster.localToGlobalTransform.compose(caster._x, caster._y, caster._z, caster._rotationX, caster._rotationY, caster._rotationZ, caster._scaleX, caster._scaleY, caster._scaleZ);
// } else {
// caster.localToCameraTransform.copy(caster.transform);
// }
root = caster;
while (root._parent != null) {
root = root._parent;
// if (root.transformChanged) {
root.localToGlobalTransform.compose(root._x, root._y, root._z, root._rotationX, root._rotationY, root._rotationZ, root._scaleX, root._scaleY, root._scaleZ);
// }
caster.localToGlobalTransform.append(root.localToGlobalTransform);
}
// Расчитываем матрицу лайта
light.localToGlobalTransform.compose(light._x, light._y, light._z, light._rotationX, light._rotationY, light._rotationZ, light._scaleX, light._scaleY, light._scaleZ);
root = light;
while (root._parent != null) {
root = root._parent;
// if (root.transformChanged) {
root.localToGlobalTransform.compose(root._x, root._y, root._z, root._rotationX, root._rotationY, root._rotationZ, root._scaleX, root._scaleY, root._scaleZ);
// }
light.localToGlobalTransform.append(root.localToGlobalTransform);
}
light.globalToLocalTransform.copy(light.localToGlobalTransform);
light.globalToLocalTransform.invert();
// Получаем матрицу перевода из объекта в лайт
caster.localToCameraTransform.combine(light.globalToLocalTransform, caster.localToGlobalTransform);
// caster.localToCameraTransform.append(light.globalToLocalTransform);
// Считаем матрицу проецирования
calculateProjection(projection, uvProjection, light.falloff, 1, light.attenuationEnd);
// globalToShadowMap.copy(light.globalToLocalTransform);
copyMatrixFromTransform(globalToShadowMap, light.globalToLocalTransform);
globalToShadowMap.append(uvProjection);
debugMaterial.diffuseMap = null;
// trace("TEST:", testCasterCulling(caster));
if (!testCasterCulling(caster)) {
active = false;
return;
}
active = true;
// Рисуем в шедоумапу
context.setRenderToTexture(shadowMap, true, 0, 0);
// context.clear(1);
context.clear(1, 1, 1, 1);
cleanContext(context);
drawObjectToShadowMap(context, caster, projection);
context.setRenderToBackBuffer();
cleanContext(context);
debugMaterial.diffuseMap = debugTexture;
}
private function testCasterCulling(object:Object3D):Boolean {
for (var child:Object3D = object.childrenList; child != null; child = child.next) {
if (child.visible) {
if (child.transformChanged) child.composeTransforms();
child.localToGlobalTransform.combine(object.localToGlobalTransform, child.transform);
if (child.boundBox == null || cullCaster(child.boundBox, child.localToGlobalTransform)) {
return true;
}
if (testCasterCulling(child)) {
return true;
}
}
}
return false;
}
static private const boundVertices:Vector.<Number> = new Vector.<Number>(24);
alternativa3d function cullObjectImpl(bounds:BoundBox, matrix:Matrix3D, far:Number = 0):Boolean {
var i:int;
var infront:Boolean;
var behind:Boolean;
// Заполнение
boundVertices[0] = bounds.minX;
boundVertices[1] = bounds.minY;
boundVertices[2] = bounds.minZ;
boundVertices[3] = bounds.maxX;
boundVertices[4] = bounds.minY;
boundVertices[5] = bounds.minZ;
boundVertices[6] = bounds.minX;
boundVertices[7] = bounds.maxY;
boundVertices[8] = bounds.minZ;
boundVertices[9] = bounds.maxX;
boundVertices[10] = bounds.maxY;
boundVertices[11] = bounds.minZ;
boundVertices[12] = bounds.minX;
boundVertices[13] = bounds.minY;
boundVertices[14] = bounds.maxZ;
boundVertices[15] = bounds.maxX;
boundVertices[16] = bounds.minY;
boundVertices[17] = bounds.maxZ;
boundVertices[18] = bounds.minX;
boundVertices[19] = bounds.maxY;
boundVertices[20] = bounds.maxZ;
boundVertices[21] = bounds.maxX;
boundVertices[22] = bounds.maxY;
boundVertices[23] = bounds.maxZ;
// Трансформация в камеру
matrix.transformVectors(boundVertices, boundVertices);
// Куллинг
// left
for (i = 0, infront = false, behind = false; i <= 21; i += 3) {
// trace("POS", boundVertices[i], boundVertices[int(i + 2)]);
if (boundVertices[i] > 0) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
// trace("L", infront);
if (!infront) return false;
}
// right
for (i = 0, infront = false, behind = false; i <= 21; i += 3) {
if (boundVertices[i] < boundVertices[int(i + 2)]) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
// trace("R", infront);
if (!infront) return false;
}
// up
for (i = 1, infront = false, behind = false; i <= 22; i += 3) {
// if (-boundVertices[i] < boundVertices[int(i + 1)]) {
if (boundVertices[i] > 0) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
// trace("U", infront);
if (!infront) return false;
}
// down
for (i = 1, infront = false, behind = false; i <= 22; i += 3) {
if (boundVertices[i] < boundVertices[int(i + 1)]) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
// trace("D", infront);
if (!infront) return false;
}
if (far > 0) {
for (i = 2, infront = false, behind = false; i <= 23; i += 3) {
if (boundVertices[i] < far) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
// trace("N", infront);
if (!infront) return false;
}
}
for (i = 2, infront = false, behind = false; i <= 23; i += 3) {
if (boundVertices[i] > 0) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
// trace("N", infront);
if (!infront) return false;
}
return true;
}
// должен быть заполнен нулями
private var rawData:Vector.<Number> = new Vector.<Number>(16);
private var m:Matrix3D = new Matrix3D();
private function calculateProjection(projection:ProjectionTransform3D, uvProjection:Matrix3D, fov:Number, nearClipping:Number, farClipping:Number):void {
var viewSize:Number = 1;
var focalLength:Number = viewSize/Math.tan(fov*0.5);
projection.m0 = focalLength/viewSize;
projection.m5 = -focalLength/viewSize;
projection.m10 = farClipping/(farClipping - nearClipping);
projection.m14 = -nearClipping*projection.m10;
for (var i:int = 0; i < 16; i++) {
rawData[i] = 0;
}
// TODO: предумножить матрицы
rawData[0] = projection.m0;
rawData[5] = -projection.m5;
rawData[10]= projection.m10;
rawData[11]= 1;
rawData[14]= projection.m14;
uvProjection.rawData = rawData;
// 0.5f, 0.0f, 0.0f, 0.0f,
// 0.0f, 0.5f, 0.0f, 0.0f,
// 0.0f, 0.0f, 0.5f, 0.0f,
// 0.5f, 0.5f, 0.5f, 1.0f
rawData[0] = 0.5;
rawData[12] = 0.5;
rawData[5] = 0.5;
rawData[13] = 0.5;
rawData[10] = 0.5;
rawData[14] = 0.5;
rawData[11] = 0;
rawData[15] = 1;
m.rawData = rawData;
uvProjection.append(m);
}
private var transformToMatrixRawData:Vector.<Number> = new Vector.<Number>(16);
private function copyMatrixFromTransform(matrix:Matrix3D, transform:Transform3D):void {
transformToMatrixRawData[0] = transform.a;
transformToMatrixRawData[1] = transform.e;
transformToMatrixRawData[2] = transform.i;
transformToMatrixRawData[3] = 0;
transformToMatrixRawData[4] = transform.b;
transformToMatrixRawData[5] = transform.f;
transformToMatrixRawData[6] = transform.j;
transformToMatrixRawData[7] = 0;
transformToMatrixRawData[8] = transform.c;
transformToMatrixRawData[9] = transform.g;
transformToMatrixRawData[10] = transform.k;
transformToMatrixRawData[11] = 0;
transformToMatrixRawData[12] = transform.d;
transformToMatrixRawData[13] = transform.h;
transformToMatrixRawData[14] = transform.l;
transformToMatrixRawData[15] = 1;
matrix.rawData = transformToMatrixRawData;
}
alternativa3d static function drawObjectToShadowMap(context:Context3D, object:Object3D, projection:ProjectionTransform3D):void {
if (object is Mesh) {
drawMeshToShadowMap(context, Mesh(object), projection);
}
for (var child:Object3D = object.childrenList; child != null; child = child.next) {
if (child.visible) {
if (child.transformChanged) child.composeTransforms();
child.localToCameraTransform.combine(object.localToCameraTransform, child.transform);
drawObjectToShadowMap(context, child, projection);
}
}
}
private static var shadowMapProgram:Program3D;
private static var projectionVector:Vector.<Number> = new Vector.<Number>(16);
private static function drawMeshToShadowMap(context:Context3D, mesh:Mesh, projection:ProjectionTransform3D):void {
if (mesh.geometry == null || mesh.geometry.numTriangles == 0 || !mesh.geometry.isUploaded) {
return;
}
if (shadowMapProgram == null) shadowMapProgram = initMeshToShadowMapProgram(context);
context.setProgram(shadowMapProgram);
context.setVertexBufferAt(0, mesh.geometry.getVertexBuffer(VertexAttributes.POSITION), mesh.geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
var transform:Transform3D = mesh.localToCameraTransform;
projectionVector[0] = transform.a*projection.m0;
projectionVector[1] = transform.b*projection.m0;
projectionVector[2] = transform.c*projection.m0;
projectionVector[3] = transform.d*projection.m0;
projectionVector[4] = transform.e*projection.m5;
projectionVector[5] = transform.f*projection.m5;
projectionVector[6] = transform.g*projection.m5;
projectionVector[7] = transform.h*projection.m5;
projectionVector[8] = transform.i*projection.m10;
projectionVector[9] = transform.j*projection.m10;
projectionVector[10] = transform.k*projection.m10;
projectionVector[11] = transform.l*projection.m10 + projection.m14;
projectionVector[12] = transform.i;
projectionVector[13] = transform.j;
projectionVector[14] = transform.k;
projectionVector[15] = transform.l;
context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 0, projectionVector, 4);
context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 4, Vector.<Number>([255, 0, 0, 1]));
context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([1/255, 0, 0, 1]));
context.setCulling(Context3DTriangleFace.BACK);
for (var i:int = 0; i < mesh._surfacesLength; i++) {
var surface:Surface = mesh._surfaces[i];
if (surface.material == null) continue;
context.drawTriangles(mesh.geometry._indexBuffer, surface.indexBegin, surface.numTriangles);
}
context.setVertexBufferAt(0, null);
}
private static function initMeshToShadowMapProgram(context3d:Context3D):Program3D {
var vLinker:Linker = new Linker(Context3DProgramType.VERTEX);
var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
var proc:Procedure = Procedure.compileFromArray([
"#a0=a0",
"#c4=c4",
"#v0=v0",
"m44 t0, a0, c0",
"mul v0, t0, c4.x",
"mov o0, t0"
]);
proc.assignVariableName(VariableType.CONSTANT, 0, "c0", 4);
vLinker.addProcedure(proc);
fLinker.addProcedure(Procedure.compileFromArray([
"#v0=v0",
"#c0=c0",
"mov t0.xy, v0.zz",
"frc t0.y, v0.z",
"sub t0.x, v0.z, t0.y",
"mul t0.x, t0.x, c0.x",
"mov t0.z, c0.z",
"mov t0.w, c0.w",
"mov o0, t0"
]));
var program:Program3D = context3d.createProgram();
// trace("VERTEX");
// trace(A3DUtils.disassemble(vLinker.getByteCode()));
// trace("FRAGMENT");
// trace(A3DUtils.disassemble(fLinker.getByteCode()));
fLinker.varyings = vLinker.varyings;
vLinker.link();
fLinker.link();
program.upload(vLinker.data, fLinker.data);
return program;
}
// Rendering with shadow
private static function initVShader(index:int):Procedure {
var shader:Procedure = Procedure.compileFromArray([
"m44 v0, a0, c0",
"mov v1, a0"
]);
shader.assignVariableName(VariableType.ATTRIBUTE, 0, "aPosition");
shader.assignVariableName(VariableType.CONSTANT, 0, index + "cTOSHADOW", 4);
shader.assignVariableName(VariableType.VARYING, 0, index + "vSHADOWSAMPLE");
shader.assignVariableName(VariableType.VARYING, 1, "vPosition");
return shader;
}
private static function initFShader(mult:Boolean, usePCF:Boolean, index:int):Procedure {
var i:int;
var line:int = 0;
var shaderArr:Array = [];
var numPass:uint = (usePCF) ? 4 : 1;
for (i = 0; i < numPass; i++) {
// Расстояние
shaderArr[line++] = "mov t0.w, v0.z";
shaderArr[line++] = "div t2, v0, v0.w";
shaderArr[line++] = "mul t0.w, t0.w, c4.y"; // bias [0.99] * 255
if (usePCF) {
// Добавляем смещение
shaderArr[line++] = "mul t1, c" + (i + 9).toString() + ", t0.w";
// shaderArr[line++] = "add t1, v0, t1";
shaderArr[line++] = "add t1, t2, t1";
shaderArr[line++] = "tex t1, t1, s0 <2d,clamp,near,nomip>";
} else {
// shaderArr[line++] = "tex t1, v0, s0 <2d,clamp,near,nomip>";
shaderArr[line++] = "tex t1, t2, s0 <2d,clamp,near,nomip>";
}
// Восстанавливаем расстояние
shaderArr[line++] = "mul t1.w, t1.x, c4.x"; // * 255
shaderArr[line++] = "add t1.w, t1.w, t1.y";
// Перекрытие тенью
shaderArr[line++] = "sub t2.z, t1.w, t0.w";
shaderArr[line++] = "mul t2.z, t2.z, c4.z"; // smooth [10000]
shaderArr[line++] = "sat t2.z, t2.z";
// Добавляем маску и прозрачность, затем sat
shaderArr[line++] = "add t2.z, t2.z, t1.z"; // маска тени
shaderArr[line++] = "add t2, t2.zzzz, c5"; // цвет тени
// Плавный уход в прозрачность -------------
shaderArr[line++] = "#c6=c" + index + "Spot";
shaderArr[line++] = "#c7=c" + index + "Direction";
shaderArr[line++] = "#c8=c" + index + "Geometry";
shaderArr[line++] = "#v1=vPosition";
// Считаем вектор из точки к свету
// Вектор из точки к свету
shaderArr[line++] = "sub t0, c6, v1";
// shaderArr[line++] = "sub t0, v1, c6";
// Квадрат расстояния до точки
shaderArr[line++] = "dp3 t0.w, t0, t0";
// Расстояние до точки
shaderArr[line++] = "sqt t0.w, t0.w";
// Нормализованное направление к источнику
shaderArr[line++] = "nrm t0.xyz, t0.xyz";
// cos(Угол) между направлением к точке и направлением спота
shaderArr[line++] = "dp3 t0.y, t0.xyz, c7.xyz";
// // cos(угол) - cos(falloff*0.5)
shaderArr[line++] = "sub t0.y, t0.y, c8.w";
// // Делим на (cos(hotspot*0.5) - cos(falloff*0.5))
shaderArr[line++] = "div t0.y, t0.y, c8.z";
// Минус atenuationBegin
shaderArr[line++] = "sub t0.w, t0.w, c8.y"; // len = len - atenuationBegin
// Делим на (atenuationEnd - atenuationBegin)
shaderArr[line++] = "div t0.w, t0.w, c8.x"; // att = len/radius
// 1 - соотношение между расстоянием до точки и максимальным расстоянием
shaderArr[line++] = "sub t0.w, c6.w, t0.w"; // att = 1 - len/radius
shaderArr[line++] = "mul t0.w, t0.y, t0.w";
// shaderArr[line++] = "mov t0.w, t0.y";
shaderArr[line++] = "sub t0.w, c7.w, t0.w";
// shaderArr[line++] = "mov t2, t0.wwww";
shaderArr[line++] = "sat t0.w, t0.w";
shaderArr[line++] = "add t2, t2, t0.wwww";
// -----------------------------------------------------
shaderArr[line++] = "sat t2, t2";
if (usePCF) {
if (i == 0) {
shaderArr[line++] = "mov t3, t2";
} else {
shaderArr[line++] = "add t3, t3, t2";
}
}
}
if (usePCF) {
shaderArr[line++] = "mul t2, t3, c9.w";
}
if (mult) {
shaderArr[line++] = "mul t0.xyz, i0.xyz, t2.xyz";
shaderArr[line++] = "mov t0.w, i0.w";
shaderArr[line++] = "mov o0, t0";
} else {
shaderArr[line++] = "mov o0, t2";
// shaderArr[line++] = "mov o0, t1";
}
var shader:Procedure = Procedure.compileFromArray(shaderArr);
shader.assignVariableName(VariableType.VARYING, 0, index + "vSHADOWSAMPLE");
shader.assignVariableName(VariableType.CONSTANT, 4, index + "cConstants", 1);
shader.assignVariableName(VariableType.CONSTANT, 5, index + "cShadowColor", 1);
if (usePCF) {
for (i = 0; i < numPass; i++) {
shader.assignVariableName(VariableType.CONSTANT, i + 9, "cSPCF" + i.toString(), 1);
}
}
shader.assignVariableName(VariableType.SAMPLER, 0, index + "sSHADOWMAP");
return shader;
}
override public function getFShader(index:int = 0):Procedure {
return initFShader(false, (pcfOffset > 0), index);
}
override public function getVShader(index:int = 0):Procedure {
return initVShader(index);
}
private static const objectToShadowMap:Matrix3D = new Matrix3D();
private static const localToGlobal:Transform3D = new Transform3D();
override public function applyShader(drawUnit:DrawUnit, program:ShaderProgram, object:Object3D, camera:Camera3D, index:int = 0):void {
// Считаем матрицу перевода в лайт из объекта
localToGlobal.combine(camera.localToGlobalTransform, object.localToCameraTransform);
copyMatrixFromTransform(objectToShadowMap, localToGlobal);
objectToShadowMap.append(globalToShadowMap);
// var casterPos:Vector3D = new Vector3D(caster.localToGlobalTransform.d, caster.localToGlobalTransform.h, caster.localToGlobalTransform.l);
// var p:Vector3D = objectToShadowMap.transformVector(casterPos);
// p.scaleBy(1/p.w);
// trace("caster pos:", p);
objectToShadowMap.transpose();
drawUnit.setVertexConstantsFromVector(program.vertexShader.getVariableIndex(index + "cTOSHADOW"), objectToShadowMap.rawData, 4);
drawUnit.setFragmentConstantsFromVector(program.fragmentShader.getVariableIndex(index + "cConstants"), constants, constants.length/4);
drawUnit.setFragmentConstantsFromVector(program.fragmentShader.getVariableIndex(index + "cShadowColor"), camera.ambient, 1);
if (pcfOffset > 0) {
drawUnit.setFragmentConstantsFromVector(program.fragmentShader.getVariableIndex("cSPCF0"), pcfOffsets, pcfOffsets.length/4);
}
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex(index + "sSHADOWMAP"), shadowMap);
// localToGlobal.combine(light.cameraToLocalTransform, object.localToCameraTransform);
localToGlobal.combine(object.cameraToLocalTransform, light.localToCameraTransform);
// localToGlobal.invert();
// Настройки затухания
var transform:Transform3D = localToGlobal;
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + index + "Spot"), transform.d, transform.h, transform.l, 1);
var rScale:Number = Math.sqrt(transform.a * transform.a + transform.e * transform.e + transform.i * transform.i);
rScale += Math.sqrt(transform.b * transform.b + transform.f * transform.f + transform.j * transform.j);
var dLen:Number = Math.sqrt(transform.c * transform.c + transform.g * transform.g + transform.k * transform.k);
rScale += dLen;
rScale /= 3;
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + index + "Direction"), -transform.c / dLen, -transform.g / dLen, -transform.k / dLen, 0.5);
var falloff:Number = Math.cos(light.falloff * 0.5);
var hotspot:Number = Math.cos(light.hotspot * 0.5);
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + index + "Geometry"), light.attenuationEnd * rScale - light.attenuationBegin * rScale, light.attenuationBegin * rScale, hotspot == falloff ? 0.000001 : hotspot - falloff, falloff);
}
}
}
class ProjectionTransform3D {
public var m0:Number;
public var m5:Number;
public var m10:Number;
public var m14:Number;
}

View File

@@ -1,569 +0,0 @@
package alternativa.engine3d.shadows {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Transform3D;
import alternativa.engine3d.lights.DirectionalLight;
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.compiler.VariableType;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.primitives.Box;
import alternativa.engine3d.resources.ExternalTextureResource;
import alternativa.engine3d.resources.TextureResource;
import flash.display3D.Context3D;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.textures.Texture;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* @private
*/
public class StaticShadowRenderer extends ShadowRenderer {
public var context:Context3D;
private const alpha:Number = 0.7;
private var bounds:BoundBox = new BoundBox();
private var partSize:Number;
private var partsShadowMaps:Vector.<Vector.<Texture>> = new Vector.<Vector.<Texture>>();
private var partsUVMatrices:Vector.<Vector.<Matrix3D>> = new Vector.<Vector.<Matrix3D>>();
private var light:DirectionalLight;
private var globalToLight:Transform3D = new Transform3D();
private var _debug:Boolean = false;
private var debugContainer:Object3D;
private var _recievers:Dictionary = new Dictionary();
public function addReciever(object:Object3D):void {
_recievers[object] = true;
}
public function removeReciever(object:Object3D):void {
delete _recievers[object];
}
private static const constants:Vector.<Number> = Vector.<Number>([
// 255, 255*0.99, 100, 1/255
// 255, 255*0.96, 100, 1
255, 255, 1000, 1
]);
private var pcfOffset:Number = 0;
private static var pcfOffsets:Vector.<Number>;
public function dispose():void {
for each (var textures:Vector.<Texture> in partsShadowMaps) {
for each (var texture:Texture in textures) {
texture.dispose();
}
}
partsShadowMaps.length = 0;
partsUVMatrices.length = 0;
}
public function StaticShadowRenderer(context:Context3D, partSize:int, pcfSize:Number = 0) {
this.context = context;
this.partSize = partSize;
this.pcfOffset = pcfSize;
constants[3] = 1 - alpha;
}
override alternativa3d function cullReciever(boundBox:BoundBox, object:Object3D):Boolean {
return _recievers[object];
}
private var lightProjectionMatrix:Matrix3D = new Matrix3D();
public function calculateShadows(object:Object3D, light:DirectionalLight, widthPartsCount:int = 1, heightPartsCount:int = 1, overlap:Number = 0):void {
this.light = light;
var root:Object3D;
// Расчитываем матрицу объекта
// if (object.transformChanged) {
object.localToCameraTransform.compose(object._x, object._y, object._z, object._rotationX, object._rotationY, object._rotationZ, object._scaleX, object._scaleY, object._scaleZ);
// } else {
// object.localToCameraTransform.copy(caster.transform);
// }
root = object;
while (root._parent != null) {
root = root._parent;
// if (root.transformChanged) {
root.localToGlobalTransform.compose(root._x, root._y, root._z, root._rotationX, root._rotationY, root._rotationZ, root._scaleX, root._scaleY, root._scaleZ);
// }
object.localToCameraTransform.append(root.localToGlobalTransform);
}
// Расчитываем матрицу лайта
light.localToGlobalTransform.compose(light._x, light._y, light._z, light._rotationX, light._rotationY, light._rotationZ, light._scaleX, light._scaleY, light._scaleZ);
root = light;
while (root._parent != null) {
root = root._parent;
// if (root.transformChanged) {
root.localToGlobalTransform.compose(root._x, root._y, root._z, root._rotationX, root._rotationY, root._rotationZ, root._scaleX, root._scaleY, root._scaleZ);
// }
light.localToGlobalTransform.append(root.localToGlobalTransform);
}
light.globalToLocalTransform.copy(light.localToGlobalTransform);
light.globalToLocalTransform.invert();
globalToLight.copy(light.globalToLocalTransform);
// Получаем матрицу перевода из объекта в лайт
object.localToCameraTransform.append(light.globalToLocalTransform);
bounds.reset();
calculateBoundBox(bounds, object);
var frustumMinX:Number = bounds.minX;
var frustumMaxX:Number = bounds.maxX;
var frustumMinY:Number = bounds.minY;
var frustumMaxY:Number = bounds.maxY;
var frustumMinZ:Number = bounds.minZ;
var frustumMaxZ:Number = bounds.maxZ;
// Считаем шаг
var halfOverlap:Number = overlap*0.5;
var partWorldWidth:Number = (frustumMaxX - frustumMinX)/widthPartsCount;
var partWorldHeight:Number = (frustumMaxY - frustumMinY)/heightPartsCount;
debugContainer = new Object3D();
if (_debug) {
light.addChild(debugContainer);
}
// Создаем шэдоумапы и рендерим
for (var xIndex:int = 0; xIndex < widthPartsCount; xIndex++) {
var maps:Vector.<Texture> = new Vector.<Texture>();
var matrices:Vector.<Matrix3D> = new Vector.<Matrix3D>();
for (var yIndex:int = 0; yIndex < heightPartsCount; yIndex++) {
var leftX:Number = frustumMinX + xIndex*partWorldWidth;
var leftY:Number = frustumMinY + yIndex*partWorldHeight;
var width:Number;
var height:Number;
if (xIndex == 0) {
width = partWorldWidth + halfOverlap;
} else if (xIndex == (widthPartsCount - 1)) {
leftX -= halfOverlap;
width = partWorldWidth + halfOverlap;
} else {
leftX -= halfOverlap;
width = partWorldWidth + overlap;
}
if (yIndex == 0) {
height = partWorldHeight + halfOverlap;
} else if (yIndex == (heightPartsCount - 1)) {
leftY -= halfOverlap;
height = partWorldHeight + halfOverlap;
} else {
leftY -= halfOverlap;
height = partWorldHeight + overlap;
}
var uvMatrix:Matrix3D = new Matrix3D();
calculateShadowMapProjection(lightProjectionMatrix, uvMatrix, leftX, leftY, frustumMinZ, leftX + width, leftY + height, frustumMaxZ);
var shadowMap:Texture = context.createTexture(partSize, partSize, Context3DTextureFormat.BGRA, true);
// Рисуем в шедоумапу
context.setRenderToTexture(shadowMap, true, 0, 0);
context.clear(1, 1, 1, 0.5);
cleanContext(context);
DirectionalShadowRenderer.drawObjectToShadowMap(context, object, light, lightProjectionMatrix);
cleanContext(context);
maps.push(shadowMap);
matrices.push(uvMatrix);
var texture:TextureResource = new ExternalTextureResource(null);
texture._texture = shadowMap;
var material:TextureMaterial = new TextureMaterial(texture);
material.opaquePass = false;
material.transparentPass = true;
material.alphaThreshold = 1.1;
var debugObject:Mesh = new Box(width, height, 1, 1, 1, 1, false, material);
// var debugObject:Mesh = new Box(width, height, 1, 1, 1, 1, false, new FillMaterial());
debugObject.geometry.upload(context);
debugObject.x = leftX + width/2;
debugObject.y = leftY + height/2;
debugObject.z = frustumMinZ;
debugContainer.addChild(debugObject);
}
partsShadowMaps.push(maps);
partsUVMatrices.push(matrices);
}
context.setRenderToBackBuffer();
if (pcfOffset > 0) {
var offset:Number = pcfOffset/partWorldWidth;
// pcfOffsets = Vector.<Number>([
// -offset, -offset, 0, 1/4,
// -offset, offset, 0, 1,
// offset, -offset, 0, 1,
// offset, offset, 0, 1,
// ]);
pcfOffsets = Vector.<Number>([
-offset, -offset, 0, 1/4,
-offset, offset, 0, 1,
offset, -offset, 0, 1,
offset, offset, 0, 1
]);
}
}
private static const points:Vector.<Vector3D> = Vector.<Vector3D>([
new Vector3D(), new Vector3D(), new Vector3D(), new Vector3D(),
new Vector3D(), new Vector3D(), new Vector3D(), new Vector3D(),
new Vector3D(), new Vector3D(), new Vector3D(), new Vector3D(),
new Vector3D(), new Vector3D(), new Vector3D(), new Vector3D()
]);
alternativa3d static function calculateBoundBox(boundBox:BoundBox, object:Object3D, hierarchy:Boolean = true):void {
// Считаем баунды объекта в лайте
var point:Vector3D;
if (object.boundBox != null) {
var bb:BoundBox = object.boundBox;
point = points[0];
point.x = bb.minX;
point.y = bb.minY;
point.z = bb.minZ;
point = points[1];
point.x = bb.minX;
point.y = bb.minY;
point.z = bb.maxZ;
point = points[2];
point.x = bb.minX;
point.y = bb.maxY;
point.z = bb.minZ;
point = points[3];
point.x = bb.minX;
point.y = bb.maxY;
point.z = bb.maxZ;
point = points[4];
point.x = bb.maxX;
point.y = bb.minY;
point.z = bb.minZ;
point = points[5];
point.x = bb.maxX;
point.y = bb.minY;
point.z = bb.maxZ;
point = points[6];
point.x = bb.maxX;
point.y = bb.maxY;
point.z = bb.minZ;
point = points[7];
point.x = bb.maxX;
point.y = bb.maxY;
point.z = bb.maxZ;
var transform:Transform3D = object.localToCameraTransform;
for (var i:int = 0; i < 8; i++) {
point = points[i];
var x:Number = transform.a*point.x + transform.b*point.y + transform.c*point.z + transform.d;
var y:Number = transform.e*point.x + transform.f*point.y + transform.g*point.z + transform.h;
var z:Number = transform.i*point.x + transform.j*point.y + transform.k*point.z + transform.l;
if (x < boundBox.minX) {
boundBox.minX = x;
}
if (x > boundBox.maxX) {
boundBox.maxX = x;
}
if (y < boundBox.minY) {
boundBox.minY = y;
}
if (y > boundBox.maxY) {
boundBox.maxY = y;
}
if (z < boundBox.minZ) {
boundBox.minZ = z;
}
if (z > boundBox.maxZ) {
boundBox.maxZ = z;
}
}
}
if (hierarchy) {
// Пробегаемся по дочерним объектам
for (var child:Object3D = object.childrenList; child != null; child = child.next) {
if (child.visible) {
if (child.transformChanged) {
child.composeTransforms();
}
child.localToCameraTransform.combine(object.localToCameraTransform, child.transform);
calculateBoundBox(boundBox, child);
}
}
}
}
private var rawData:Vector.<Number> = new Vector.<Number>(16);
private function calculateShadowMapProjection(matrix:Matrix3D, uvMatrix:Matrix3D, frustumMinX:Number, frustumMinY:Number, frustumMinZ:Number, frustumMaxX:Number, frustumMaxY:Number, frustumMaxZ:Number):void {
// Считаем матрицу проецирования
rawData[0] = 2/(frustumMaxX - frustumMinX);
rawData[5] = 2/(frustumMaxY - frustumMinY);
rawData[10]= 1/(frustumMaxZ - frustumMinZ);
rawData[12] = (-0.5 * (frustumMaxX + frustumMinX) * rawData[0]);
rawData[13] = (-0.5 * (frustumMaxY + frustumMinY) * rawData[5]);
rawData[14]= -frustumMinZ/(frustumMaxZ - frustumMinZ);
rawData[15]= 1;
matrix.rawData = rawData;
rawData[0] = 1/((frustumMaxX - frustumMinX));
// if (useSingle) {
// rawData[5] = 1/((frustumMaxY - frustumMinY));
// } else {
rawData[5] = -1/((frustumMaxY - frustumMinY));
// }
rawData[12] = 0.5 - (0.5 * (frustumMaxX + frustumMinX) * rawData[0]);
rawData[13] = 0.5 - (0.5 * (frustumMaxY + frustumMinY) * rawData[5]);
uvMatrix.rawData = rawData;
}
override public function get debug():Boolean {
return _debug;
}
override public function set debug(value:Boolean):void {
_debug = value;
if (debugContainer != null) {
if (value) {
if (light != null) {
light.addChild(debugContainer);
}
} else {
if (debugContainer._parent != null) {
debugContainer.removeFromParent();
}
}
}
}
// private static const vShader:Procedure = initVShader(index);
private static function initVShader(index:int):Procedure {
var shader:Procedure = Procedure.compileFromArray([
// "m44 o0, a0, c0",
// Координата вершины в локальном пространстве
"m44 v0, a0, c4"
]);
shader.assignVariableName(VariableType.ATTRIBUTE, 0, "aPosition");
shader.assignVariableName(VariableType.CONSTANT, 0, "cPROJ", 4);
shader.assignVariableName(VariableType.CONSTANT, 4, "cTOSHADOW", 4);
shader.assignVariableName(VariableType.VARYING, 0, "vSHADOWSAMPLE");
return shader;
}
// private static const multVShader:Procedure = initMultVShader();
// private static function initMultVShader():Procedure {
// var shader:Procedure = Procedure.compileFromArray([
// "m44 v0, a0, c0",
// ]);
// shader.assignVariableName(VariableType.ATTRIBUTE, 0, "aPOSITION");
// shader.assignVariableName(VariableType.CONSTANT, 0, "cTOSHADOW", 4);
// shader.assignVariableName(VariableType.VARYING, 0, "vSHADOWSAMPLE");
// return shader;
// }
// private static const fShader:Procedure = initFShader(false, false, index);
// private static const pcfFShader:Procedure = initFShader(false, true, index);
// i0 - input color
// o0 - shadowed result
// private static const multFShader:Procedure = initFShader(true, false, index);
// private static const pcfMultFShader:Procedure = initFShader(true, true, index);
private static function initFShader(mult:Boolean, usePCF:Boolean, index:int, grayScale:Boolean = false):Procedure {
var i:int;
var line:int = 0;
var shaderArr:Array = [];
var numPass:uint = (usePCF) ? 4 : 1;
for (i = 0; i < numPass; i++) {
// Расстояние
shaderArr[line++] = "mov t0.w, v0.z";
shaderArr[line++] = "mul t0.w, t0.w, c4.y"; // bias [0.99] * 255
if (usePCF) {
// Добавляем смещение
// shaderArr[line++] = "mul t1, c" + (i + 5).toString() + ", t0.w";
shaderArr[line++] = "add t1, v0, c" + (i + 5).toString() + "";
// shaderArr[line++] = "add t1, v0, c" + (i + 5).toString();
shaderArr[line++] = "tex t1, t1, s0 <2d,clamp,near,nomip>";
} else {
shaderArr[line++] = "tex t1, v0, s0 <2d,clamp,near,nomip>";
}
// Восстанавливаем расстояние
shaderArr[line++] = "mul t1.w, t1.x, c4.x"; // * 255
shaderArr[line++] = "add t1.w, t1.w, t1.y";
// Перекрытие тенью
shaderArr[line++] = "sub t2.z, t1.w, t0.w";
shaderArr[line++] = "mul t2.z, t2.z, c4.z"; // smooth [10000]
shaderArr[line++] = "sat t2.z, t2.z";
// Добавляем маску и прозрачность, затем sat
// shaderArr[line++] = "add t2.z, t2.z, t1.z"; // маска тени
// shaderArr[line++] = "add t2.z, t2.z, c4.w"; // вес тени
shaderArr[line++] = "sat t2.z, t2.z";
if (usePCF) {
if (i == 0) {
shaderArr[line++] = "mov t2.x, t2.z";
} else {
shaderArr[line++] = "add t2.x, t2.x, t2.z";
}
}
}
if (usePCF) {
shaderArr[line++] = "mul t2.z, t2.x, c5.w";
}
if (grayScale) {
shaderArr.push("mov o0.w, t2.z");
} else {
if (mult) {
shaderArr.push("mul t0.xyz, i0.xyz, t2.z");
shaderArr.push("mov t0.w, i0.w");
shaderArr.push("mov o0, t0");
} else {
shaderArr.push("mov t0, t2.z");
shaderArr.push("mov o0, t0");
}
}
var shader:Procedure = Procedure.compileFromArray(shaderArr, "StaticShadowMap");
shader.assignVariableName(VariableType.VARYING, 0, "vSHADOWSAMPLE");
shader.assignVariableName(VariableType.CONSTANT, 4, "cConstants", 1);
if (usePCF) {
for (i = 0; i < numPass; i++) {
shader.assignVariableName(VariableType.CONSTANT, i + 5, "cPCF" + i.toString(), 1);
}
}
shader.assignVariableName(VariableType.SAMPLER, 0, "sSHADOWMAP");
return shader;
}
override public function getVShader(index:int = 0):Procedure {
return initVShader(index);
}
override public function getFShader(index:int = 0):Procedure {
return initFShader(false, (pcfOffset > 0), index);
}
// override public function getMultFShader():Procedure {
// return (pcfOffset > 0) ? pcfMultFShader : multFShader;
// }
// override public function getMultVShader():Procedure {
// return multVShader;
// }
override public function getFIntensityShader():Procedure {
return initFShader(false, (pcfOffset > 0), 0, true);
}
private static const objectToShadowMap:Transform3D = new Transform3D();
private static const objectToUVMap:Matrix3D = new Matrix3D();
override public function applyShader(drawUnit:DrawUnit, program:ShaderProgram, object:Object3D, camera:Camera3D, index:int = 0):void {
// Считаем матрицу перевода в лайт из объекта
objectToShadowMap.combine(camera.localToGlobalTransform, object.localToCameraTransform);
objectToShadowMap.append(globalToLight);
// objectToShadowMap.identity();
// objectToShadowMap.append(object.cameraMatrix);
// objectToShadowMap.append(camera.globalMatrix);
// objectToShadowMap.append(globalToLight);
// Получаем индекс шедоумапы
// var coords:Vector3D = objectToShadowMap.position;
var coords:Vector3D = new Vector3D(objectToShadowMap.d, objectToShadowMap.h, objectToShadowMap.l);
var xIndex:int = (coords.x - bounds.minX)/(bounds.maxX - bounds.minX)*partsShadowMaps.length;
xIndex = (xIndex < 0) ? 0 : ((xIndex >= partsShadowMaps.length) ? partsShadowMaps.length - 1 : xIndex);
var maps:Vector.<Texture> = partsShadowMaps[xIndex];
var matrices:Vector.<Matrix3D> = partsUVMatrices[xIndex];
var yIndex:int = (coords.y - bounds.minY)/(bounds.maxY - bounds.minY)*maps.length;
yIndex = (yIndex < 0) ? 0 : ((yIndex >= maps.length) ? maps.length - 1 : yIndex);
// trace(xIndex, yIndex);
var shadowMap:Texture = maps[yIndex];
var uvMatrix:Matrix3D = matrices[yIndex];
DirectionalShadowRenderer.copyMatrixFromTransform(objectToUVMap, objectToShadowMap);
objectToUVMap.append(uvMatrix);
objectToUVMap.transpose();
// objectToShadowMap.append(uvMatrix);
drawUnit.setVertexConstantsFromVector(program.vertexShader.getVariableIndex("cTOSHADOW"), objectToUVMap.rawData, 4);
drawUnit.setFragmentConstantsFromVector(program.fragmentShader.getVariableIndex("cConstants"), constants, 1);
if (pcfOffset > 0) {
drawUnit.setFragmentConstantsFromVector(program.fragmentShader.getVariableIndex("cPCF0"), pcfOffsets, pcfOffsets.length >> 2)
}
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sSHADOWMAP"), shadowMap);
}
// private static var program:LinkedProgram;
// private static var programPCF:LinkedProgram;
// private static function initMeshProgram(context:Context3D, usePCF:Boolean):LinkedProgram {
// var vLinker:Linker = new Linker(Context3DProgramType.VERTEX);
// vLinker.addShader(vShader);
//
// var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
// if (usePCF) {
// fLinker.addShader(pcfFShader);
// } else {
// fLinker.addShader(fShader);
// }
//
// vLinker.setOppositeLinker(fLinker);
// fLinker.setOppositeLinker(vLinker);
//
//// trace("[VERTEX]");
//// trace(AgalUtils.disassemble(vLinker.getByteCode()));
//// trace("[FRAGMENT]");
//// trace(AgalUtils.disassemble(fLinker.getByteCode()));
//
// var result:LinkedProgram;
// if (usePCF) {
// programPCF = new LinkedProgram();
// result = programPCF;
// } else {
// program = new LinkedProgram();
// result = program;
// }
// result.vLinker = vLinker;
// result.fLinker = fLinker;
// result.program = context.createProgram();
// result.program.upload(vLinker.getByteCode(), fLinker.getByteCode());
//
// return result;
// }
//
// override public function drawShadow(mesh:Mesh, camera:Camera3D, texture:Texture):void {
// var context3d:Context3D = camera.view._context3d;
//
// var linkedProgram:LinkedProgram;
// if (pcfOffset > 0) {
// linkedProgram = (programPCF == null) ? initMeshProgram(context3d, true) : programPCF;
// } else {
// linkedProgram = (program == null) ? initMeshProgram(context3d, false) : program;
// }
// var vLinker:Linker = linkedProgram.vLinker;
// var fLinker:Linker = linkedProgram.fLinker;
// context3d.setProgram(linkedProgram.program);
//
// context3d.setVertexBufferAt(vLinker.getVariableIndex("aPOSITION"), mesh.geometry.vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
// context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, vLinker.getVariableIndex("cPROJ"), mesh.projectionMatrix, true);
// applyShader(context3d, linkedProgram, mesh, camera);
// context3d.setVertexBufferAt(1, null);
//
// context3d.setCulling(Context3DTriangleFace.FRONT);
// context3d.drawTriangles(mesh.geometry.indexBuffer, 0, mesh.geometry.numTriangles);
//
// context3d.setVertexBufferAt(vLinker.getVariableIndex("aPOSITION"), null);
// context.setTextureAt(getTextureIndex(fLinker), texture);
// }
}
}