OmniShadowLight - new culling

This commit is contained in:
Leonid Gaev
2012-05-03 18:05:45 +06:00
parent 24dc612ae5
commit 38b6af446b
6 changed files with 398 additions and 191 deletions

View File

@@ -3,7 +3,7 @@
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
@@ -13,7 +13,7 @@ package alternativa.engine3d.core {
import flash.geom.Vector3D;
use namespace alternativa3d;
/**
* Class stores object's bounding box object's local space. Generally, position of child objects isn't considered at BoundBox calculation.
* Ray intersection always made boundBox check at first, but it's possible to check on crossing boundBox only.
@@ -44,7 +44,7 @@ package alternativa.engine3d.core {
* Top face.
*/
public var maxZ:Number = -1e+22;
/**
* Resets all bounds values to its initial state.
@@ -57,33 +57,40 @@ package alternativa.engine3d.core {
maxY = -1e+22;
maxZ = -1e+22;
}
/**
* @private
* @private
*/
alternativa3d function checkFrustumCulling(frustum:CullingPlane, culling:int):int {
var side:int = 1;
for (var plane:CullingPlane = frustum; plane != null; plane = plane.next) {
if (culling & side) {
if (plane.x >= 0) if (plane.y >= 0) if (plane.z >= 0) {
if (maxX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else {
if (maxX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
} else if (plane.z >= 0) {
if (maxX*plane.x + minY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + maxY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else {
if (maxX*plane.x + minY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + maxY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
} else if (plane.y >= 0) if (plane.z >= 0) {
if (minX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else {
if (minX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
} else if (plane.z >= 0) {
if (plane.x >= 0)
if (plane.y >= 0)
if (plane.z >= 0) {
if (maxX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else {
if (maxX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
}
else
if (plane.z >= 0) {
if (maxX*plane.x + minY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + maxY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else {
if (maxX*plane.x + minY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + maxY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
}
else if (plane.y >= 0)
if (plane.z >= 0) {
if (minX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else {
if (minX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
}
else if (plane.z >= 0) {
if (minX*plane.x + minY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + maxY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else {
@@ -95,9 +102,9 @@ package alternativa.engine3d.core {
}
return culling;
}
/**
* @private
* @private
*/
alternativa3d function checkOcclusion(occluders:Vector.<Occluder>, occludersLength:int, transform:Transform3D):Boolean {
var ax:Number = transform.a*minX + transform.b*minY + transform.c*minZ + transform.d;
@@ -140,9 +147,9 @@ package alternativa.engine3d.core {
}
return false;
}
/**
* @private
* @private
*/
alternativa3d function checkRays(origins:Vector.<Vector3D>, directions:Vector.<Vector3D>, raysLength:int):Boolean {
for (var i:int = 0; i < raysLength; i++) {
@@ -269,7 +276,7 @@ package alternativa.engine3d.core {
if (c >= b || d <= a) return false;
return true;
}
/**
* Duplicates an instance of <code>BoundBox</code>.
* @return New <code>BoundBox</code> instance with same set of properties.
@@ -284,7 +291,7 @@ package alternativa.engine3d.core {
res.maxZ = maxZ;
return res;
}
/**
* Returns a string representation of <code>BoundBox</code>.
* @return A string representation of <code>BoundBox</code>.
@@ -292,6 +299,6 @@ package alternativa.engine3d.core {
public function toString():String {
return "[BoundBox " + "X:[" + minX.toFixed(2) + ", " + maxX.toFixed(2) + "] Y:[" + minY.toFixed(2) + ", " + maxY.toFixed(2) + "] Z:[" + minZ.toFixed(2) + ", " + maxZ.toFixed(2) + "]]";
}
}
}

View File

@@ -302,6 +302,8 @@ package alternativa.engine3d.core {
*/
alternativa3d var culling:int;
public var cameras:uint;
/**
* @private
*/

View File

@@ -32,7 +32,7 @@ package alternativa.engine3d.lights {
/**
* Distance from at which falloff is complete.
*/
public var _attenuationEnd:Number;
public var attenuationEnd:Number;
/**
* Creates a OmniLight object.
@@ -207,23 +207,7 @@ package alternativa.engine3d.lights {
*/
override public function set shadow(value:Shadow):void {
_shadow = value;
_shadow._light = this;
var omniShadow:OmniLightShadow = value as OmniLightShadow;
if (omniShadow!=null) omniShadow.setBoundSize(this.attenuationEnd*1.5);
if (_shadow!=null) _shadow._light = this;
}
public function get attenuationEnd():Number{
return _attenuationEnd;
}
/**
* @private
*/
public function set attenuationEnd(value:Number):void{
// TODO: recalculate BoundBox
_attenuationEnd = value;
var omniShadow:OmniLightShadow = shadow as OmniLightShadow;
if (omniShadow!=null) omniShadow.setBoundSize(this._attenuationEnd*1.0);
}
}
}

View File

@@ -58,7 +58,7 @@ public class ParserA3D extends Parser {
* @param input <code>ByteArray</code> consists of A3D data.
*/
public function parse(input:ByteArray):void {
try {
// try {
input.position = 0;
var version:int = input.readByte();
if (version == 0) {
@@ -69,10 +69,10 @@ public class ParserA3D extends Parser {
// Bit of packing. It always equal to 1, because version 2 and above is always packed.
parseVersionOver1(input);
}
} catch (e:Error) {
e.message = "Parsing failed: " + e.message;
throw e;
}
// } catch (e:Error) {
// e.message = "Parsing failed: " + e.message;
// throw e;
// }
}

View File

@@ -348,6 +348,8 @@ package alternativa.engine3d.shadows {
shadowMap = camera.context3D.createTexture(_mapSize, _mapSize, Context3DTextureFormat.BGRA, true);
debugTexture._texture = shadowMap;
}
// TODO Don't clear if there was no casters
camera.context3D.setRenderToTexture(shadowMap, true);
camera.context3D.clear(1, 0, 0, 0.3);

View File

@@ -8,13 +8,17 @@
package alternativa.engine3d.shadows {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.CullingPlane;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Renderer;
import alternativa.engine3d.core.Transform3D;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.lights.OmniLight;
import alternativa.engine3d.lights.OmniLight;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.materials.compiler.Linker;
@@ -36,7 +40,9 @@ package alternativa.engine3d.shadows {
import flash.display3D.textures.CubeTexture;
import flash.utils.Dictionary;
use namespace alternativa3d;
import spark.effects.easing.Elastic;
use namespace alternativa3d;
public class OmniLightShadow extends Shadow{
@@ -52,6 +58,7 @@ package alternativa.engine3d.shadows {
// cube map size
private var _mapSize:Number;
private var _pcfOffset:Number;
private var cubeShadowMap:CubeTexture;
@@ -64,6 +71,9 @@ package alternativa.engine3d.shadows {
private var _casters:Vector.<Object3D> = new Vector.<Object3D>();
private var castersInLight:Vector.<Object3D> = new Vector.<Object3D>();
private var castersInLightCount:int;
private var actualCasters:Vector.<Object3D> = new Vector.<Object3D>();
private var actualCastersCount:int;
@@ -74,7 +84,7 @@ package alternativa.engine3d.shadows {
// object -> light
private var objectToLightTransform:Transform3D = new Transform3D();
// casters count in edge
private var prevActualCasterCountForEdge:Vector.<int> = new Vector.<int>(6);
private var prevActualCastersMask:int;
private var cachedContext:Context3D;
private var programs:Dictionary = new Dictionary();
@@ -85,6 +95,13 @@ package alternativa.engine3d.shadows {
* @param pcfOffset Смягчение границ тени.
*/
public function OmniLightShadow(mapSize:int = 128, pcfOffset:Number = 0) {
sections = new SectionPlane();
sections.next = new SectionPlane();
sections.next.next = new SectionPlane();
sections.next.next.next = new SectionPlane();
sections.next.next.next.next = new SectionPlane();
sections.next.next.next.next.next = new SectionPlane();
this.mapSize = mapSize;
this.pcfOffset = pcfOffset;
@@ -93,7 +110,7 @@ package alternativa.engine3d.shadows {
fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader();
debugMaterial = new ShadowDebugMaterial();
debugMaterial.alpha = 1.0;
debugMaterial.alpha = 0.3;
for (var i:int = 0; i < 6; i++) {
// Create cameras
@@ -101,8 +118,6 @@ package alternativa.engine3d.shadows {
var cam:Camera3D = new Camera3D(10, radius);
cam.fov = 1.910633237;
cameras[i] = cam;
prevActualCasterCountForEdge[i] = 0;
}
// Left
@@ -131,27 +146,16 @@ package alternativa.engine3d.shadows {
cameras[4].scaleY = -1;
cameras[4].composeTransforms();
// DUBFLR
// TODO: boundBox of light?
// TODO: remove setBoundSize
// TODO: overwrite calculateFrustum function or setTransformConstants function
// TODO: 2 step culling. 1-culling by radius for light. 2-culling for current camera by 4 planes
}
/**
* @private
*/
alternativa3d function setBoundSize(value:Number):void{
this.radius = value;
for (var i:int = 0; i < 6; i++) {
var cam:Camera3D = cameras[i];
cam.farClipping = value;
cam.calculateProjection(value,value);
}
}
private function createDebugObject(material:Material, context:Context3D):Mesh{
var geometry:Geometry;
var mesh:Mesh;
// TODO: определиться куб или сфера
var isBox:Boolean = false;
if (isBox) {
mesh = new Mesh();
@@ -207,77 +211,89 @@ package alternativa.engine3d.shadows {
if (cubeShadowMap == null) {
cubeShadowMap = context.createCubeTexture(_mapSize, Context3DTextureFormat.BGRA, true);
debugMaterial.cubeMap = cubeShadowMap;
// TODO: not clear here
for (i = 0; i < 6; i++) {
context.setRenderToTexture(cubeShadowMap, true, 0, i);
context.clear(1, 0, 0, 0.3);
}
prevActualCastersMask = 63;
}
// Calculate parameters
radius = OmniLight(_light).attenuationEnd;
for (i = 0; i < 6; i++) {
var cam:Camera3D = cameras[i];
cam.farClipping = radius;
cam.calculateProjection(radius, radius);
}
var castersCount:int = _casters.length;
// calculating some transformation matrices
// TODO: skip invisible objects
actualCastersCount = 0;
for (i = 0; i < castersCount; i++) {
caster = _casters[i];
if (caster.transformChanged) caster.composeTransforms();
caster.lightToLocalTransform.combine(caster.cameraToLocalTransform, _light.localToCameraTransform);
caster.localToLightTransform.combine(_light.cameraToLocalTransform, caster.localToCameraTransform);
var skin:Skin = caster as Skin;
if (skin != null) {
// Calculate joints matrices
for (var child:Object3D = skin.childrenList; child != null; child = child.next) {
if (child.transformChanged) child.composeTransforms();
// Write transformToSkin matrix to localToGlobalTransform property
child.localToGlobalTransform.copy(child.transform);
if (child is Joint) {
Joint(child).calculateTransform();
}
skin.calculateJointsTransforms(child);
}
var visible:Boolean = caster.visible;
var parent:Object3D = caster._parent;
while (visible && parent != null) {
visible = parent.visible;
parent = parent._parent;
}
if (caster.childrenList != null) calculateChildrenTransforms(caster);
if (visible) {
// calculate transform matrices
_light.lightToObjectTransform.combine(caster.cameraToLocalTransform, _light.localToCameraTransform);
caster.localToLightTransform.combine(_light.cameraToLocalTransform, caster.localToCameraTransform);
// collect actualCasters for light
if (caster.boundBox == null || OmniLight(_light).checkBound(caster)){
actualCasters[actualCastersCount] = caster;
actualCastersCount++;
if (caster.boundBox != null) {
// 1 - calculate planes in object space
calculatePlanes(_light.lightToObjectTransform);
// 2 - check object location cameras (sections)
caster.cameras = recognizeObjectCameras(caster.boundBox);
}
} else {
caster.cameras = 63;
}
// update Skin Joints matrices
var skin:Skin = caster as Skin;
if (skin != null) {
// Calculate joints matrices
for (var child:Object3D = skin.childrenList; child != null; child = child.next) {
if (child.transformChanged) child.composeTransforms();
// Write transformToSkin matrix to localToGlobalTransform property
child.localToGlobalTransform.copy(child.transform);
if (child is Joint) {
Joint(child).calculateTransform();
}
skin.calculateJointsTransforms(child);
}
}
if (caster.childrenList != null) collectActualChildren(caster);
}
}
// Iterate through six cameras
for (i = 0; i < 6; i++) {
// Cube side camera
var edgeCamera:Camera3D = cameras[i];
// проверяем, есть ли видимые кастеры попадающие на грань куба
actualCastersCount = 0;
numCulled = 0;
// var flipX:Boolean = edgeCamera.scaleX < 0;
// var flipY:Boolean = edgeCamera.scaleY < 0;
// edgeCamera.scaleX = 1;
// edgeCamera.scaleY = 1;
// edgeCamera.composeTransforms();
var flipX:Boolean = edgeCamera.scaleX < 0;
var flipY:Boolean = edgeCamera.scaleY < 0;
edgeCamera.scaleX = 1;
edgeCamera.scaleY = 1;
edgeCamera.composeTransforms();
for (j = 0; j < castersCount; j++) {
caster = _casters[j];
var visible:Boolean = caster.visible;
var parent:Object3D = caster._parent;
while (visible && parent != null) {
visible = parent.visible;
parent = parent._parent;
}
if (visible) {
// Проверка куллинга
// формируем actualCasters
calculateVisibility(caster, edgeCamera);
}
}
// trace("face:" + i + " culled:" + numCulled + " rest:" + actualCastersCount);
if (flipX) edgeCamera.scaleX = -1;
if (flipY) edgeCamera.scaleY = -1;
edgeCamera.composeTransforms();
// if (flipX) edgeCamera.scaleX = -1;
// if (flipY) edgeCamera.scaleY = -1;
// edgeCamera.composeTransforms();
var edgeBit:int = (1<<i);
if (actualCastersCount > 0) {
// Настройка параметров рендеринга:
renderer.camera = camera;
@@ -287,33 +303,59 @@ package alternativa.engine3d.shadows {
// Пробегаемся по кастерам
for (j = 0; j < actualCastersCount; j++) {
caster = actualCasters[j];
// собираем матрицу перевода из кастера в пространство edgeCamera
casterToEdgedCameraTransform.combine(edgeCamera.inverseTransform, caster.localToLightTransform);
// Собираем драуколлы для кастера и его дочерних объектов
collectDraws(context, caster, edgeCamera);
// Проверить находится ли кастер в зоне 4-х плоскостей
if (caster.cameras & edgeBit) {
// собираем матрицу перевода из кастера в пространство edgeCamera
casterToEdgedCameraTransform.combine(edgeCamera.inverseTransform, caster.localToLightTransform);
// Собираем драуколлы для кастера и его дочерних объектов
collectDraws(context, caster, edgeCamera);
}
}
if (renderer.drawUnits.length == 0) context.clear(0, 0, 0, 0.0);
// Отрисовка дроуколов
renderer.render(context);
prevActualCastersMask |= edgeBit;
}
else{
// Если относительно одной из камер ничего не менялось, не вызываем отрисовочный вызов
if (prevActualCasterCountForEdge[i]!=0){
if (prevActualCastersMask & edgeBit){
context.setRenderToTexture(cubeShadowMap, false, 0, i);
context.clear(1, 0, 0, 0);
context.clear(0, 0, 0, 0);
prevActualCastersMask &= ~edgeBit;
}
}
prevActualCasterCountForEdge[i] = actualCastersCount;
}
context.setRenderToBackBuffer();
// // Пробегаемся по кастерам
// for (j = 0; j < actualCastersCount; j++) {
// caster = actualCasters[j];
// caster.culling &= 0x8000003F;
//
// // Проверить находится ли кастер в зоне 4-х плоскостей
// if (caster.culling & (edgeBit << 8)) {
// // собираем матрицу перевода из кастера в пространство edgeCamera
// casterToEdgedCameraTransform.combine(edgeCamera.inverseTransform, caster.localToLightTransform);
// // Собираем драуколлы для кастера и его дочерних объектов
// collectDraws(context, caster, edgeCamera);
// }
// }
if (debug) {
if (actualCastersCount > 0) {
if (actualCastersCount > 0 || true) {
// TODO: draw debug mesh always (DirectionalLightShadow)
// Создаем дебаговый объект, если он не создан
if (debugObject == null) {
debugObject = createDebugObject(debugMaterial, camera.context3D);
debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = radius/12;
// TODO: select wright radius
// debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = radius/12;
debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = radius;
debugObject.composeTransforms();
}
@@ -327,69 +369,205 @@ package alternativa.engine3d.shadows {
}
}
// Precalculate children matrices
// localToLightTransform, lightToLocalTransform, transform, и calculateTransform для Joint
private function calculateChildrenTransforms(root:Object3D):void{
for (var child:Object3D = root.childrenList; child != null; child = child.next) {
private var sections:SectionPlane;
// расчет матриц трансформаций для объектов
// if (child.transformChanged) child.composeTransforms();
// child.localToLightTransform.combine(root.localToLightTransform, child.transform);
// child.lightToLocalTransform.combine(child.inverseTransform, root.lightToLocalTransform);
private function calculatePlanes(lightToObjectTransform:Transform3D):void {
var planeRU:SectionPlane = sections;
var planeLU:SectionPlane = sections.next;
child.lightToLocalTransform.combine(child.cameraToLocalTransform, _light.localToCameraTransform);
child.localToLightTransform.combine(_light.cameraToLocalTransform, child.localToCameraTransform);
sections.x = 0.707;
sections.z = 0.707;
sections.offset = sections.x*lightToObjectTransform.d + sections.z*lightToObjectTransform.l;
var skin:Skin = child as Skin;
if (skin != null) {
// Calculate joints matrices
for (var skinChild:Object3D = skin.childrenList; skinChild != null; skinChild = skinChild.next) {
if (skinChild.transformChanged) skinChild.composeTransforms();
// Write transformToSkin matrix to localToGlobalTransform property
skinChild.localToGlobalTransform.copy(skinChild.transform);
if (skinChild is Joint) {
Joint(skinChild).calculateTransform();
//var RIGHT_SIDE:int = 0;
sections.frontCameras = 0x11;
sections.backCameras = 0x22;
// sections.unused = 0x33;
sections.unused = 0xC;
planeLU.x = -0.707;
planeLU.z = 0.707;
planeLU.offset = planeLU.x*lightToObjectTransform.d + planeLU.z*lightToObjectTransform.l;
planeLU.frontCameras = 0x12;
planeLU.backCameras = 0x21;
planeLU.unused = 0xC;
// var nearPlane:CullingPlane = sections;
// var farPlane:CullingPlane = nearPlane.next;
// var leftPlane:CullingPlane = farPlane.next;
// var rightPlane:CullingPlane = leftPlane.next;
// var topPlane:CullingPlane = rightPlane.next;
// var bottomPlane:CullingPlane = topPlane.next;
//
// var fa:Number = transform.a * correctionX;
// var fe:Number = transform.e * correctionX;
// var fi:Number = transform.i * correctionX;
// var fb:Number = transform.b * correctionY;
// var ff:Number = transform.f * correctionY;
// var fj:Number = transform.j * correctionY;
//
// var ax:Number = -fa - fb + transform.c;
// var ay:Number = -fe - ff + transform.g;
// var az:Number = -fi - fj + transform.k;
// var bx:Number = fa - fb + transform.c;
// var by:Number = fe - ff + transform.g;
// var bz:Number = fi - fj + transform.k;
// topPlane.x = bz * ay - by * az;
// topPlane.y = bx * az - bz * ax;
// topPlane.z = by * ax - bx * ay;
// topPlane.offset = transform.d * topPlane.x + transform.h * topPlane.y + transform.l * topPlane.z;
// // Right plane.
// ax = bx;
// ay = by;
// az = bz;
// bx = fa + fb + transform.c;
// by = fe + ff + transform.g;
// bz = fi + fj + transform.k;
// rightPlane.x = bz * ay - by * az;
// rightPlane.y = bx * az - bz * ax;
// rightPlane.z = by * ax - bx * ay;
// rightPlane.offset = transform.d * rightPlane.x + transform.h * rightPlane.y + transform.l * rightPlane.z;
// // Bottom plane.
// ax = bx;
// ay = by;
// az = bz;
// bx = -fa + fb + transform.c;
// by = -fe + ff + transform.g;
// bz = -fi + fj + transform.k;
// bottomPlane.x = bz*ay - by*az;
// bottomPlane.y = bx*az - bz*ax;
// bottomPlane.z = by*ax - bx*ay;
// bottomPlane.offset = transform.d*bottomPlane.x + transform.h*bottomPlane.y + transform.l*bottomPlane.z;
// // Left plane.
// ax = bx;
// ay = by;
// az = bz;
// bx = -fa - fb + transform.c;
// by = -fe - ff + transform.g;
// bz = -fi - fj + transform.k;
// leftPlane.x = bz*ay - by*az;
// leftPlane.y = bx*az - bz*ax;
// leftPlane.z = by*ax - bx*ay;
// leftPlane.offset = transform.d*leftPlane.x + transform.h*leftPlane.y + transform.l*leftPlane.z;
}
private function recognizeObjectCameras(bb:BoundBox):int {
var culling:int = 63;
for (var plane:SectionPlane = sections; plane != null; plane = plane.next) {
var result:int = 0;
if (plane.x >= 0)
if (plane.y >= 0)
if (plane.z >= 0) {
if (bb.maxX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.minX*plane.x + bb.minY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras;
} else {
if (bb.maxX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.minX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras;
}
skin.calculateJointsTransforms(skinChild);
else
if (plane.z >= 0) {
if (bb.maxX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.minX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras;
} else {
if (bb.maxX*plane.x + bb.minY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.minX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras;
}
else if (plane.y >= 0)
if (plane.z >= 0) {
if (bb.minX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.maxX*plane.x + bb.minY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras;
} else {
if (bb.minX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.maxX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras;
}
}
if (child.childrenList != null) calculateChildrenTransforms(child);
}
}
private static var numCulled:int;
// собирает список actualCasters для одной из 6-и камер
private function calculateVisibility(root:Object3D, camera:Camera3D):void{
var casterCulling:int;
if (root.visible) {
// Вычисляем результат кулинга для объекта
if (root.boundBox != null) {
edgeCameraToCasterTransform.combine(root.lightToLocalTransform, camera.transform);
camera.calculateFrustum(edgeCameraToCasterTransform);
casterCulling = root.boundBox.checkFrustumCulling(camera.frustum, 63);
else if (plane.z >= 0) {
if (bb.minX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.maxX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras;
} else {
casterCulling = 63;
if (bb.minX*plane.x + bb.minY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.maxX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras;
}
culling &= result | plane.unused;
}
return culling;
}
if (casterCulling <= 0) numCulled++;
private function collectActualChildren(root:Object3D):void{
for (var child:Object3D = root.childrenList; child != null; child = child.next) {
if (child.visible){
// calculate transform matrices
_light.lightToObjectTransform.combine(child.cameraToLocalTransform, _light.localToCameraTransform);
child.localToLightTransform.combine(_light.cameraToLocalTransform, child.localToCameraTransform);
// добавляем кастер в список актуальных кастеров
if (casterCulling >= 0) {
actualCasters[actualCastersCount] = root;
actualCastersCount++
}
// collect actualCasters for light
if (child.boundBox == null || OmniLight(_light).checkBound(child)){
actualCasters[actualCastersCount] = child;
actualCastersCount++;
// Если есть дочерние объекты,
// Проверяем их на кулинг
for (var child:Object3D = root.childrenList; child != null; child = child.next) {
calculateVisibility(child, camera);
if (child.boundBox != null) {
// 1 - calculate planes in object space
calculatePlanes(_light.lightToObjectTransform);
// 2 - check object location cameras (sections)
child.cameras = recognizeObjectCameras(child.boundBox);
}
} else {
child.cameras = 63;
}
// update Skin Joints matrices
var skin:Skin = child as Skin;
if (skin != null) {
// Calculate joints matrices
for (var skinChild:Object3D = skin.childrenList; skinChild != null; skinChild = skinChild.next) {
if (skinChild.transformChanged) skinChild.composeTransforms();
// Write transformToSkin matrix to localToGlobalTransform property
skinChild.localToGlobalTransform.copy(skinChild.transform);
if (skinChild is Joint) {
Joint(skinChild).calculateTransform();
}
skin.calculateJointsTransforms(skinChild);
}
}
if (child.childrenList != null) collectActualChildren(child);
}
}
}
// // собирает список actualCasters для одной из 6-и камер
// private function calculateVisibility(root:Object3D, camera:Camera3D):void{
// var casterCulling:int;
//
// if (root.visible) {
// // Вычисляем результат кулинга для объекта
// if (root.boundBox != null) {
// edgeCameraToCasterTransform.combine(root.lightToLocalTransform, camera.transform);
// camera.calculateFrustum(edgeCameraToCasterTransform);
// casterCulling = root.boundBox.checkFrustumCulling(camera.frustum, 63);
// } else {
// casterCulling = 63;
// }
//
// if (casterCulling <= 0) numCulled++;
//
// // добавляем кастер в список актуальных кастеров
// if (casterCulling >= 0) {
// actualCasters[actualCastersCount] = root;
// actualCastersCount++
// }
//
// // Если есть дочерние объекты,
// // Проверяем их на кулинг
// for (var child:Object3D = root.childrenList; child != null; child = child.next) {
// calculateVisibility(child, camera);
// }
// }
// }
private function collectDraws(context:Context3D, caster:Object3D, edgeCamera:Camera3D):void{
// если объект является мешем, собираем для него дроуколы
var mesh:Mesh = caster as Mesh;
@@ -587,10 +765,10 @@ package alternativa.engine3d.shadows {
// Устанавливаем коеффициенты
// TODO: сделать множитель более корректный. Возможно 65536 (разрешающая способность глубины буфера).
if (_pcfOffset > 0) {
var offset:Number = Math.tan(_pcfOffset/180*Math.PI)/3;
var offset:Number = _pcfOffset*0.0175; //TODO: make equivalent 1 offset ~ 1 degree
drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cPCFOffsets"), -3/2, 1/16, 0, 0);
drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -1, 1, 0, offset/radius);
drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -1, 1, 0, offset);
drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cDecode"), -10000, -10000/255, biasMultiplier*10000/radius, 10);
}
else{
@@ -781,6 +959,7 @@ package alternativa.engine3d.shadows {
/**
* Смещение Percentage Closer Filtering. Этот способ фильтрации используется для смягчения границ тени.
* 1 pcfOffset equivalent 1 degree for all blur
*/
public function get pcfOffset():Number {
return _pcfOffset;
@@ -816,6 +995,7 @@ import alternativa.engine3d.resources.Geometry;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DTriangleFace;
import flash.display3D.VertexBuffer3D;
import flash.display3D.textures.CubeTexture;
import flash.utils.Dictionary;
@@ -869,17 +1049,35 @@ class ShadowDebugMaterial extends Material {
drawUnit.setProjectionConstants(camera, program.vertexShader.getVariableIndex("cProjMatrix"), object.localToCameraTransform);
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cDecode"), 1, 1/255, 0, alpha);
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sCubeMap"), cubeMap);
// TODO: draw two-sided debug mesh
var drawUnit2:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
// Установка стримов
drawUnit2.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
// Установка констант
drawUnit2.setProjectionConstants(camera, program.vertexShader.getVariableIndex("cProjMatrix"), object.localToCameraTransform);
drawUnit2.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cDecode"), 1, 1/255, 0, alpha);
drawUnit2.setTextureAt(program.fragmentShader.getVariableIndex("sCubeMap"), cubeMap);
drawUnit2.culling = Context3DTriangleFace.BACK;
// Отправка на отрисовку
if (alpha < 1) {
drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
drawUnit2.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
drawUnit2.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
camera.renderer.addDrawUnit(drawUnit2, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
} else {
camera.renderer.addDrawUnit(drawUnit2, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
}
}
private function copyDrawUnit(source:DrawUnit, dest:DrawUnit):void {
}
private function setupProgram(object:Object3D):ShaderProgram {
var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
var positionVar:String = "aPosition";
@@ -911,3 +1109,17 @@ class ShadowDebugMaterial extends Material {
}
class SectionPlane {
public var x:Number = 0;
public var y:Number = 0;
public var z:Number = 0;
public var offset:Number = 0;
public var next:SectionPlane;
public var frontCameras:int;
public var backCameras:int;
public var unused:int = 63;
}