From 38b6af446b98572bd0af679a627c3cbefb71141a Mon Sep 17 00:00:00 2001 From: Leonid Gaev Date: Thu, 3 May 2012 18:05:45 +0600 Subject: [PATCH] OmniShadowLight - new culling --- src/alternativa/engine3d/core/BoundBox.as | 69 +-- src/alternativa/engine3d/core/Object3D.as | 2 + src/alternativa/engine3d/lights/OmniLight.as | 20 +- src/alternativa/engine3d/loaders/ParserA3D.as | 10 +- .../shadows/DirectionalLightShadow.as | 2 + .../engine3d/shadows/OmniLightShadow.as | 486 +++++++++++++----- 6 files changed, 398 insertions(+), 191 deletions(-) diff --git a/src/alternativa/engine3d/core/BoundBox.as b/src/alternativa/engine3d/core/BoundBox.as index 3b07adc..259fbed 100644 --- a/src/alternativa/engine3d/core/BoundBox.as +++ b/src/alternativa/engine3d/core/BoundBox.as @@ -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., 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., directions:Vector., 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 BoundBox. * @return New BoundBox instance with same set of properties. @@ -284,7 +291,7 @@ package alternativa.engine3d.core { res.maxZ = maxZ; return res; } - + /** * Returns a string representation of BoundBox. * @return A string representation of BoundBox. @@ -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) + "]]"; } - + } } diff --git a/src/alternativa/engine3d/core/Object3D.as b/src/alternativa/engine3d/core/Object3D.as index 460e642..4b1a003 100644 --- a/src/alternativa/engine3d/core/Object3D.as +++ b/src/alternativa/engine3d/core/Object3D.as @@ -302,6 +302,8 @@ package alternativa.engine3d.core { */ alternativa3d var culling:int; + public var cameras:uint; + /** * @private */ diff --git a/src/alternativa/engine3d/lights/OmniLight.as b/src/alternativa/engine3d/lights/OmniLight.as index c9db019..abb1e2c 100644 --- a/src/alternativa/engine3d/lights/OmniLight.as +++ b/src/alternativa/engine3d/lights/OmniLight.as @@ -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); - } } } diff --git a/src/alternativa/engine3d/loaders/ParserA3D.as b/src/alternativa/engine3d/loaders/ParserA3D.as index 15a6e8c..873bca4 100644 --- a/src/alternativa/engine3d/loaders/ParserA3D.as +++ b/src/alternativa/engine3d/loaders/ParserA3D.as @@ -58,7 +58,7 @@ public class ParserA3D extends Parser { * @param input ByteArray 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; +// } } diff --git a/src/alternativa/engine3d/shadows/DirectionalLightShadow.as b/src/alternativa/engine3d/shadows/DirectionalLightShadow.as index 1e34ea2..b7563d4 100644 --- a/src/alternativa/engine3d/shadows/DirectionalLightShadow.as +++ b/src/alternativa/engine3d/shadows/DirectionalLightShadow.as @@ -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); diff --git a/src/alternativa/engine3d/shadows/OmniLightShadow.as b/src/alternativa/engine3d/shadows/OmniLightShadow.as index 87086e7..4c471f0 100644 --- a/src/alternativa/engine3d/shadows/OmniLightShadow.as +++ b/src/alternativa/engine3d/shadows/OmniLightShadow.as @@ -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. = new Vector.(); + + private var castersInLight:Vector. = new Vector.(); + private var castersInLightCount:int; private var actualCasters:Vector. = new Vector.(); 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. = new Vector.(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< 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; + +}