From cffd3bf50dbaf72df8de5ab3e8684961a3656285 Mon Sep 17 00:00:00 2001 From: artem pecheny Date: Mon, 30 Jul 2012 14:57:28 +0600 Subject: [PATCH] ASDoc for shadows --- .../shadows/DirectionalLightShadow.as | 6 +- .../engine3d/shadows/OmniLightShadow.as | 2184 +++++++++-------- 2 files changed, 1102 insertions(+), 1088 deletions(-) diff --git a/src/alternativa/engine3d/shadows/DirectionalLightShadow.as b/src/alternativa/engine3d/shadows/DirectionalLightShadow.as index 4c11eb0..5c47afe 100644 --- a/src/alternativa/engine3d/shadows/DirectionalLightShadow.as +++ b/src/alternativa/engine3d/shadows/DirectionalLightShadow.as @@ -43,9 +43,9 @@ package alternativa.engine3d.shadows { use namespace alternativa3d; /** - * Class of shadow, that is created by one source of light(DirectionalLight). Shadow is rendered in fixed volume. + * Class of the shadow, that is created by one source of light(DirectionalLight). Shadow is rendered in fixed volume. * For binding of shadow to light source you need: - * 1) to set DirectionalLightShadow as a value of property shadow of light source; + * 1) to set instance of the DirectionalLightShadow as a value of property shadow of light source; * 2) to add Object3D to corresponding list, using the method addCaster(). * * @see #addCaster() @@ -820,7 +820,7 @@ package alternativa.engine3d.shadows { } /** - * Clears the list of objects, that cast shadow. + * Clears the list of objects, which cast shadow. */ public function clearCasters():void { _casters.length = 0; diff --git a/src/alternativa/engine3d/shadows/OmniLightShadow.as b/src/alternativa/engine3d/shadows/OmniLightShadow.as index 8e0a166..511d5f0 100644 --- a/src/alternativa/engine3d/shadows/OmniLightShadow.as +++ b/src/alternativa/engine3d/shadows/OmniLightShadow.as @@ -1,1085 +1,1099 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - * 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/ - */ -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.Renderer; - import alternativa.engine3d.core.Transform3D; - import alternativa.engine3d.core.VertexAttributes; - 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; - import alternativa.engine3d.materials.compiler.Procedure; - import alternativa.engine3d.materials.compiler.VariableType; - import alternativa.engine3d.objects.Joint; - import alternativa.engine3d.objects.Mesh; - import alternativa.engine3d.objects.Skin; - import alternativa.engine3d.objects.Surface; - import alternativa.engine3d.primitives.GeoSphere; - import alternativa.engine3d.resources.Geometry; - import alternativa.engine3d.resources.TextureResource; - - import flash.display3D.Context3D; - import flash.display3D.Context3DProgramType; - import flash.display3D.Context3DTextureFormat; - import flash.display3D.Context3DTriangleFace; - import flash.display3D.VertexBuffer3D; - import flash.display3D.textures.CubeTexture; - import flash.utils.Dictionary; - - use namespace alternativa3d; - - public class OmniLightShadow extends Shadow{ - - // TODO: calculate bias automaticaly - /** - * Degree of correcting offset of shadow map space. It need for getting rid of self-shadowing artifacts. - */ - public var biasMultiplier:Number = 0.97; - private static const DIFFERENCE_MULTIPLIER:Number = 32768; - private static const DEBUG_TYPE:String = "Sphere"; // Box - /** - * @private - */ - alternativa3d static var debugRadiusScale:Number = 0.2; - - private var renderer:Renderer = new Renderer(); - - // radius of the light source - private var radius:Number = 100; - - // cube map size - private var _mapSize:Number; - - private var _pcfOffset:Number; - - private var cubeShadowMap:CubeTexture; - - // Sides cameras - private var cameras:Vector. = new Vector.(); - - private var debugObject:Mesh; - private var debugMaterial:ShadowDebugMaterial; - - private var _casters:Vector. = new Vector.(); - - private var actualCasters:Vector. = new Vector.(); - private var actualCastersCount:int; - - // caster -> cube face - private var casterToEdgedCameraTransform:Transform3D = new Transform3D(); - // object -> light - private var objectToLightTransform:Transform3D = new Transform3D(); - // casters count in edge - private var prevActualCastersMask:int; - - private var cachedContext:Context3D; - private var programs:Dictionary = new Dictionary(); - - /** - * Создает экземпляр OmniLightShadow. - * @param mapSize Размер карты теней. Должен быть степенью 2. - * @param pcfOffset Смягчение границ тени. - */ - public function OmniLightShadow(mapSize:int = 128, pcfOffset:Number = 0) { - sections = new SectionPlane(0x11, 0x22, 0xC); // RU - sections.next = new SectionPlane(0x12, 0x21, 0xC); // LU - sections.next.next = new SectionPlane(0x14, 0x28, 0x3); // FU - sections.next.next.next = new SectionPlane(0x18, 0x24, 0x3); // BU - sections.next.next.next.next = new SectionPlane(0x5, 0xA, 0x30); // RF - sections.next.next.next.next.next = new SectionPlane(0x9, 0x6, 0x30); // RB - - this.mapSize = mapSize; - this.pcfOffset = pcfOffset; - - vertexShadowProcedure = getVShader(); - type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; - fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); - - debugMaterial = new ShadowDebugMaterial(); - debugMaterial.alpha = 0.3; - - for (var i:int = 0; i < 6; i++) { - var cam:Camera3D = new Camera3D(radius/1000, radius); - cam.fov = 1.910633237; - cameras[i] = cam; - } - - // Left - cameras[1].rotationY = -Math.PI/2; - cameras[1].scaleY = -1; - cameras[1].composeTransforms(); - // Right - cameras[0].rotationY = Math.PI/2; - cameras[0].scaleY = -1; - cameras[0].composeTransforms(); - // Back - cameras[3].rotationX = -Math.PI/2; - cameras[3].rotationZ = Math.PI; - cameras[3].scaleX = -1; - cameras[3].composeTransforms(); - // Front - cameras[2].rotationX = -Math.PI/2; - cameras[2].scaleY = -1; - cameras[2].composeTransforms(); - // Bottom - cameras[5].rotationX = Math.PI; - cameras[5].scaleX = -1; - cameras[5].composeTransforms(); - // Top - cameras[4].rotationX = 0; - cameras[4].scaleY = -1; - cameras[4].composeTransforms(); - } - - private function createDebugObject(material:Material, context:Context3D):Mesh{ - var geometry:Geometry; - var mesh:Mesh; - if (DEBUG_TYPE == "Box") { - mesh = new Mesh(); - geometry = new Geometry(8); - mesh.geometry = geometry; - - var attributes:Array = new Array(); - attributes[0] = VertexAttributes.POSITION; - attributes[1] = VertexAttributes.POSITION; - attributes[2] = VertexAttributes.POSITION; - geometry.addVertexStream(attributes); - - geometry.setAttributeValues(VertexAttributes.POSITION, Vector.([ - -1, -1, -1, - 1, -1, -1, - 1, 1, -1, - -1, 1, -1, - -1, -1, 1, - 1, -1, 1, - 1, 1, 1, - -1, 1, 1])); - geometry.indices = Vector.([ - 0, 1, 2, 3, 0, 2, 2, 1, 0, 3, 2, 0, - 2, 6, 1, 1, 6, 2, 1, 6, 5, 5, 6, 1, - 6, 4, 5, 5, 4, 6, 6, 4, 7, 7, 4, 6, - 0, 7, 4, 4, 7, 0, 0, 7, 3, 3, 7, 0, - 3, 6, 2, 2, 6, 3, 3, 7, 6, 6, 7, 3, - 0, 5, 1, 1, 5, 0, 0, 4, 5, 5, 4, 0]); - mesh.addSurface(material, 0, 24); - } else { - mesh = new GeoSphere(1, 4, true); - // Create two side - var triangles:Vector. = mesh.geometry.indices; - var numTriangles:int = triangles.length; - for (var i:int = 0; i < numTriangles; i += 3) { - var a:uint = triangles[i]; - var b:uint = triangles[int(i + 1)]; - var c:uint = triangles[int(i + 2)]; - triangles.push(c, b, a); - } - mesh.geometry.indices = triangles; - mesh.getSurface(0).numTriangles = triangles.length/3; - mesh.setMaterialToAllSurfaces(material); - } - mesh.geometry.upload(context); - return mesh; - } - - // Draw in shadow map - override alternativa3d function process(camera:Camera3D):void { - var i:int; - var j:int; - var caster:Object3D; - var context:Context3D = camera.context3D; - - // Checking changed context - if (context != cachedContext) { - programs = new Dictionary(); - cubeShadowMap = null; - cachedContext = context; - } - - // Culling invisible casters - if (cubeShadowMap == null) { - cubeShadowMap = context.createCubeTexture(_mapSize, Context3DTextureFormat.BGRA, true); - debugMaterial.cubeMap = cubeShadowMap; - prevActualCastersMask = 63; - } - - // Calculate parameters - radius = OmniLight(_light).attenuationEnd; - for (i = 0; i < 6; i++) { - var cam:Camera3D = cameras[i]; - cam.nearClipping = radius/1000; - cam.farClipping = radius; - cam.calculateProjection(1, 1); - } - - var castersCount:int = _casters.length; - actualCastersCount = 0; - - for (i = 0; i < castersCount; i++) { - caster = _casters[i]; - - var visible:Boolean = caster.visible; - var parent:Object3D = caster._parent; - while (visible && parent != null) { - visible = parent.visible; - parent = parent._parent; - } - - 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++; - - // Pack camera culling - caster.culling <<= 16; - if (caster.boundBox != null) { - // 1 - calculate planes in object space - calculatePlanes(caster.localToLightTransform); - // 2 - check object location cameras (sections) - caster.culling |= recognizeObjectCameras(caster.boundBox); - } - } - - // 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]; - - var edgeBit:int = (1< 0) { - // Настройка параметров рендеринга: - renderer.camera = camera; - context.setRenderToTexture(cubeShadowMap, true, 0, i); - context.clear(1, 0, 0, 0.0); - - // Пробегаемся по кастерам - for (j = 0; j < actualCastersCount; j++) { - caster = actualCasters[j]; - - // Проверить находится ли кастер в зоне 4-х плоскостей - if ((caster.culling & edgeBit)) { - // собираем матрицу перевода из кастера в пространство edgeCamera - casterToEdgedCameraTransform.combine(edgeCamera.inverseTransform, caster.localToLightTransform); - // Собираем драуколлы для кастера и его дочерних объектов - collectDraws(context, caster, edgeCamera); - } - } - -// if (renderer.drawUnits.length == 0) context.clear(0, 0, 0, 0.0); - - // Drawing - renderer.render(context); - prevActualCastersMask |= edgeBit; - } - else{ - // Если относительно одной из камер ничего не менялось, не вызываем отрисовочный вызов - - if ((prevActualCastersMask & edgeBit)){ - context.setRenderToTexture(cubeShadowMap, false, 0, i); - context.clear(1, 0, 0, 0); - - prevActualCastersMask &= ~edgeBit; - } - } - } - context.setRenderToBackBuffer(); - - // Unpack camera culling value - for (j = 0; j < actualCastersCount; j++) { - caster = actualCasters[j]; - // If there was -1, after shift it will be -1 too - caster.culling >>= 16; - } - - if (debug) { - // Create debug object if needed - if (debugObject == null) { - debugObject = createDebugObject(debugMaterial, camera.context3D); - } - debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = radius*debugRadiusScale; - debugObject.composeTransforms(); - - // Формируем матрицу трансформации для debugObject - debugObject.localToCameraTransform.combine(_light.localToCameraTransform, debugObject.transform); - - // Отрисовываем - var debugSurface:Surface = debugObject._surfaces[0]; - debugMaterial.collectDraws(camera, debugSurface, debugObject.geometry, null, 0, false, -1); - } - actualCasters.length = 0; - } - - 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); - - // collect actualCasters for light - if (child.boundBox == null || OmniLight(_light).checkBound(child)){ - actualCasters[actualCastersCount] = child; - actualCastersCount++; - - // Pack camera culling - child.culling <<= 16; - if (child.boundBox != null) { - // 1 - calculate planes in object space - calculatePlanes(child.localToLightTransform); - // 2 - check object location cameras (sections) - child.culling |= recognizeObjectCameras(child.boundBox); - } - } - - // 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); - } - } - } - - private var sections:SectionPlane; - - private function calculatePlanes(transform:Transform3D):void { - // DUBFLR - var planeRU:SectionPlane = sections; - var planeLU:SectionPlane = sections.next; - var planeFU:SectionPlane = sections.next.next; - var planeBU:SectionPlane = sections.next.next.next; - var planeRF:SectionPlane = sections.next.next.next.next; - var planeRB:SectionPlane = sections.next.next.next.next.next; - - // 1, 0, 1 - planeRU.x = transform.a + transform.i; - planeRU.y = transform.b + transform.j; - planeRU.z = transform.c + transform.k; - planeRU.offset = -(transform.d + transform.l); - - // -1, 0, 1 - planeLU.x = transform.i - transform.a; - planeLU.y = transform.j - transform.b; - planeLU.z = transform.k - transform.c; - planeLU.offset = transform.d - transform.l; - - // 0, 1, 1 - planeFU.x = transform.e + transform.i; - planeFU.y = transform.f + transform.j; - planeFU.z = transform.g + transform.k; - planeFU.offset = -(transform.h + transform.l); - - // 0, -1, 1 - planeBU.x = transform.i - transform.e; - planeBU.y = transform.j - transform.f; - planeBU.z = transform.k - transform.g; - planeBU.offset = transform.h - transform.l; - - // 1, 1, 0 - planeRF.x = transform.a + transform.e; - planeRF.y = transform.b + transform.f; - planeRF.z = transform.c + transform.g; - planeRF.offset = -(transform.d + transform.h); - - // 1, -1, 0 - planeRB.x = transform.a - transform.e; - planeRB.y = transform.b - transform.f; - planeRB.z = transform.c - transform.g; - planeRB.offset = transform.h - transform.d; - - // var ax:Number = transform.c - transform.a + transform.b; // E -// var ay:Number = transform.g - transform.e + transform.f; -// var az:Number = transform.k - transform.i + transform.j; -// var bx:Number = transform.c - transform.a - transform.b; // H -// var by:Number = transform.g - transform.e - transform.f; -// var bz:Number = transform.k - transform.i - transform.j; -// planeRU.x = bz * ay - by * az; -// planeRU.y = bx * az - bz * ax; -// planeRU.z = by * ax - bx * ay; -// planeRU.offset = transform.d*planeRU.x + transform.h*planeRU.y + transform.l*planeRU.z; -// -// ax = transform.c + transform.a - transform.b; // D -// ay = transform.g + transform.e - transform.f; -// az = transform.k + transform.i - transform.j; -// bx = transform.c + transform.a + transform.b; // A -// by = transform.g + transform.e + transform.f; -// bz = transform.k + transform.i + transform.j; -// planeLU.x = bz * ay - by * az; -// planeLU.y = bx * az - bz * ax; -// planeLU.z = by * ax - bx * ay; -// planeLU.offset = transform.d*planeLU.x + transform.h*planeLU.y + transform.l*planeLU.z; -// -// ax = transform.c - transform.a - transform.b; // H -// ay = transform.g - transform.e - transform.f; -// az = transform.k - transform.i - transform.j; -// bx = transform.c + transform.a - transform.b; // D -// by = transform.g + transform.e - transform.f; -// bz = transform.k + transform.i - transform.j; -// planeFU.x = bz * ay - by * az; -// planeFU.y = bx * az - bz * ax; -// planeFU.z = by * ax - bx * ay; -// planeFU.offset = transform.d*planeFU.x + transform.h*planeFU.y + transform.l*planeFU.z; -// -// ax = transform.c + transform.a + transform.b; // A -// ay = transform.g + transform.e + transform.f; -// az = transform.k + transform.i + transform.j; -// bx = transform.c - transform.a + transform.b; // E -// by = transform.g - transform.e + transform.f; -// bz = transform.k - transform.i + transform.j; -// planeBU.x = bz * ay - by * az; -// planeBU.y = bx * az - bz * ax; -// planeBU.z = by * ax - bx * ay; -// planeBU.offset = transform.d*planeBU.x + transform.h*planeBU.y + transform.l*planeBU.z; -// -// ax = transform.a - transform.b + transform.c; // D -// ay = transform.e - transform.f + transform.g; -// az = transform.i - transform.j + transform.k; -// bx = transform.a - transform.b - transform.c; // C -// by = transform.e - transform.f - transform.g; -// bz = transform.i - transform.j - transform.k; -// planeRF.x = bz * ay - by * az; -// planeRF.y = bx * az - bz * ax; -// planeRF.z = by * ax - bx * ay; -// planeRF.offset = transform.d*planeRF.x + transform.h*planeRF.y + transform.l*planeRF.z; -// -// ax = transform.a + transform.b - transform.c; // B -// ay = transform.e + transform.f - transform.g; -// az = transform.i + transform.j - transform.k; -// bx = transform.a + transform.b + transform.c; // A -// by = transform.e + transform.f + transform.g; -// bz = transform.i + transform.j + transform.k; -// planeRB.x = bz * ay - by * az; -// planeRB.y = bx * az - bz * ax; -// planeRB.z = by * ax - bx * ay; -// planeRB.offset = transform.d*planeRB.x + transform.h*planeRB.y + transform.l*planeRB.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; - } - 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; - } - 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 { - 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.unusedBits; - } - return culling; - } - - private function collectDraws(context:Context3D, caster:Object3D, edgeCamera:Camera3D):void{ - // если объект является мешем, собираем для него дроуколы - var mesh:Mesh = caster as Mesh; - if (mesh != null && mesh.geometry != null) { - var program:ShaderProgram; - var programListByTransformProcedure:Vector.; - var skin:Skin = mesh as Skin; - - // пробегаемся по сурфейсам - for (var i:int = 0; i < mesh._surfacesLength; i++) { - var surface:Surface = mesh._surfaces[i]; - if (surface.material == null) continue; - - var material:Material = surface.material; - var geometry:Geometry = mesh.geometry; - var alphaTest:Boolean; - var useDiffuseAlpha:Boolean; - var alphaThreshold:Number; - var materialAlpha:Number; - var diffuse:TextureResource; - var opacity:TextureResource; - var uvBuffer:VertexBuffer3D; - - // ловим параметры прозрачности - if (material is TextureMaterial) { - alphaThreshold = TextureMaterial(material).alphaThreshold; - materialAlpha = TextureMaterial(material).alpha; - diffuse = TextureMaterial(material).diffuseMap; - opacity = TextureMaterial(material).opacityMap; - alphaTest = alphaThreshold > 0; - useDiffuseAlpha = TextureMaterial(material).opacityMap == null; - uvBuffer = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]); - if (uvBuffer == null) continue; - } else { - alphaTest = false; - useDiffuseAlpha = false; - } - - - var positionBuffer:VertexBuffer3D = mesh.geometry.getVertexBuffer(VertexAttributes.POSITION); - if (positionBuffer == null) continue; - - // поднимаем и кэшируем programListByTransformProcedure - if (skin != null) { - caster.transformProcedure = skin.surfaceTransformProcedures[i]; - } - programListByTransformProcedure = programs[caster.transformProcedure]; - if (programListByTransformProcedure == null) { - programListByTransformProcedure = new Vector.(3, true); - programs[caster.transformProcedure] = programListByTransformProcedure; - } - - // собираем программу и Формируем дроуюнит - program = getProgram(caster.transformProcedure, programListByTransformProcedure, context, alphaTest, useDiffuseAlpha); - var drawUnit:DrawUnit = renderer.createDrawUnit(caster, program.program, mesh.geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program); - drawUnit.culling = Context3DTriangleFace.BACK; - - // Установка стрима - drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, mesh.geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]); - - if (alphaTest) { - drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aUV"), uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[0]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[0]]); - drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cThresholdAlpha"), alphaThreshold, 0, 0, materialAlpha); - if (useDiffuseAlpha) { - drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sTexture"), diffuse._texture); - } else { - drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sTexture"), opacity._texture); - } - } - - // Установка констант - caster.setTransformConstants(drawUnit, surface, program.vertexShader, null); - drawUnit.setProjectionConstants(edgeCamera, program.vertexShader.getVariableIndex("cProjMatrix"), casterToEdgedCameraTransform); - drawUnit.setVertexConstantsFromTransform(program.vertexShader.getVariableIndex("cCasterToOmni"), caster.localToLightTransform); - - drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cConstants"), 1 / 255, 0, 255/radius, 1); - - renderer.addDrawUnit(drawUnit, Renderer.OPAQUE); - } - } - } - - /** - * @private - * Процедура для передачи UV координат во фрагментный шейдер - */ - static private const passUVProcedure:Procedure = new Procedure(["#v0=vUV", "#a0=aUV", "mov v0, a0"], "passUVProcedure"); - - // diffuse alpha test - private static const diffuseAlphaTestProcedure:Procedure = new Procedure([ - "#v0=vUV", - "#s0=sTexture", - "#c0=cThresholdAlpha", - "tex t0, v0, s0 <2d, linear,repeat, miplinear>", - "mul t0.w, t0.w, c0.w", - "sub t0.w, t0.w, c0.x", - "kil t0.w" - ], "diffuseAlphaTestProcedure"); - - // opacity alpha test - private static const opacityAlphaTestProcedure:Procedure = new Procedure([ - "#v0=vUV", - "#s0=sTexture", - "#c0=cThresholdAlpha", - "tex t0, v0, s0 <2d, linear,repeat, miplinear>", - "mul t0.w, t0.x, c0.w", - "sub t0.w, t0.w, c0.x", - "kil t0.w" - ], "opacityAlphaTestProcedure"); - - - private function getProgram(transformProcedure:Procedure, programListByTransformProcedure:Vector., context:Context3D, alphaTest:Boolean, useDiffuseAlpha:Boolean):ShaderProgram { - var key:int = (alphaTest ? (useDiffuseAlpha ? 1 : 2) : 0); - var program:ShaderProgram = programListByTransformProcedure[key]; - - if (program == null) { - var vLinker:Linker = new Linker(Context3DProgramType.VERTEX); - var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); - - var positionVar:String = "aPosition"; - vLinker.declareVariable(positionVar, VariableType.ATTRIBUTE); - - if (alphaTest) { - vLinker.addProcedure(passUVProcedure); - } - - if (transformProcedure != null) { - var newPosVar:String = "tTransformedPosition"; - vLinker.declareVariable(newPosVar); - vLinker.addProcedure(transformProcedure, positionVar); - vLinker.setOutputParams(transformProcedure, newPosVar); - positionVar = newPosVar; - } - - var proc:Procedure = Procedure.compileFromArray([ - "#v0=vDistance", - - "m34 t0.xyz, i0, c2", - "mov v0, t0.xyzx", - - "m44 o0, i0, c0" - ]); - proc.assignVariableName(VariableType.CONSTANT, 0, "cProjMatrix", 4); - proc.assignVariableName(VariableType.CONSTANT, 2, "cCasterToOmni", 3); - - vLinker.addProcedure(proc, positionVar); - - if (alphaTest) { - if (useDiffuseAlpha) { - fLinker.addProcedure(diffuseAlphaTestProcedure); - } else { - fLinker.addProcedure(opacityAlphaTestProcedure); - } - } - fLinker.addProcedure(Procedure.compileFromArray([ - "#v0=vDistance", // xyz - "#c0=cConstants", // 1/255, 0, 255/radius, 1 - // calculate distance - "dp3 t0.z, v0.xyz, v0.xyz", - "sqt t0.z, t0.z", // x: [0, radius] - "mul t0.z, t0.z, c0.z", // x: [0, 255] - // codeing - "frc t0.y, t0.z", - "sub t0.x, t0.z, t0.y", - "mul t0.x, t0.x, c0.x", - - "mov t0.w, c0.w", - "mov o0, t0" - ])); - program = new ShaderProgram(vLinker, fLinker); - fLinker.varyings = vLinker.varyings; - programListByTransformProcedure[key] = program; - program.upload(context); - - } - return program; - } - - - - //------------- ShadowMap Shader in material---------- - - /** - * @private - */ - alternativa3d override function setup(drawUnit:DrawUnit, vertexLinker:Linker, fragmentLinker:Linker, surface:Surface):void { - // Устанавливаем матрицу перевода в шедоумапу - objectToLightTransform.combine(_light.cameraToLocalTransform, surface.object.localToCameraTransform); - drawUnit.setVertexConstantsFromTransform(vertexLinker.getVariableIndex("cObjectToLightTransform"), objectToLightTransform); - - // Устанавливаем шедоумапу - drawUnit.setTextureAt(fragmentLinker.getVariableIndex("sCubeMap"), cubeShadowMap); - - // Устанавливаем коеффициенты - if (_pcfOffset > 0) { - var offset:Number = Math.tan(_pcfOffset/180*Math.PI)/3; - drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cPCFOffsets"), -3/2, 1/16, 0, 0); - drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -1, 1, 0, offset); - drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cDecode"), -DIFFERENCE_MULTIPLIER, -DIFFERENCE_MULTIPLIER/255, biasMultiplier*DIFFERENCE_MULTIPLIER/radius, 10); - } else { - drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -DIFFERENCE_MULTIPLIER, -DIFFERENCE_MULTIPLIER/255, biasMultiplier*DIFFERENCE_MULTIPLIER/radius, 1.0); - } - } - - private static function getVShader():Procedure { - var shader:Procedure = Procedure.compileFromArray([ - "#v0=vSample", - - "m34 t0.xyz, i0, c0", - - "mov v0, t0.xyz" - ], "OmniShadowMapVertex"); - shader.assignVariableName(VariableType.CONSTANT, 0, "cObjectToLightTransform", 3); - return shader; - } - - private static function getFShader():Procedure { - var shaderArr:Array = [ - "#v0=vSample", - "#c0=cConstants", - "#s0=sCubeMap" - ]; - var line:int = 3; - // Расстояние - shaderArr[line++] = "dp3 t0.z, v0.xyz, v0.xyz"; - shaderArr[line++] = "sqt t0.z, t0.z"; // w: [0, radius] - shaderArr[line++] = "tex t0.xy, v0, s0 "; - shaderArr[line++] = "dp3 t0.x, t0.xyz, c0.xyz"; // декодируем, находим разницу между расстояниями и умножаем ее на большое число - - // рассчитываем значение тени - shaderArr[line++] = "sat t0.x, t0.x"; - shaderArr[line++] = "sub o0, c0.w, t0.x"; - -// shaderArr[line++] = "sat t0.x, t0.x"; -// shaderArr[line++] = "sub t0.x, c0.w, t0.x"; -// shaderArr[line++] = "sat t0.x, t0.x"; -// shaderArr[line++] = "mov o0, t0.x"; - - return Procedure.compileFromArray(shaderArr, "OmniShadowMapFragment"); - } - - private static function getFShaderPCF():Procedure { - var shaderArr:Array = [ - "#v0=vSample", - "#c0=cDecode", - "#c1=cConstants", - "#c2=cPCFOffsets", - "#s0=sCubeMap" - ]; - var line:int = 5; - var i:int; - var j:int; - - // допустимо использование временных переменных t0 t1 t2 t3 - // v0 - sample - - // calculate 2 ortogonal vectors - // (-y, x, 0) - shaderArr[line++] = "mov t1.xyzw, v0.yxzw"; - shaderArr[line++] = "mul t1.xyzw, t1.xyzw, c1.xyzz"; - - shaderArr[line++] = "crs t0.xyz, v0.xyz, t1.xyz"; - - // normalize vectors - shaderArr[line++] = "nrm t0.xyz, t0.xyz"; - shaderArr[line++] = "nrm t1.xyz, t1.xyz"; - - shaderArr[line++] = "dp3 t3.z, v0.xyz, v0.xyz"; - shaderArr[line++] = "sqt t3.z, t3.z"; // distance - - // apply pcf offset - shaderArr[line++] = "mul t0.w, c1.w, t3.z"; // с1.w = offset/radius - shaderArr[line++] = "mul t0.xyz, t0.xyz, t0.w"; - shaderArr[line++] = "mul t1.xyz, t1.xyz, t0.w"; - // --------- {13 opcode} - - // t0, t1 - ortogonals ↑→ - // t2 - current vector - - // t3.z distance to object - // t3.xy - result from shadow map - // t3.w - summ of sat - - // first point - shaderArr[line++] = "add t2.xyz, t0.xyz, t1.xyz"; - shaderArr[line++] = "mul t2.xyz, t2.xyz, c2.xxx"; - shaderArr[line++] = "add t2.xyz, t2.xyz, v0.xyz"; - - // получаем длинну из шадоумапы [0, 1] -// shaderArr[line++] = "mov t3.z, t0.w"; - - shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; - shaderArr[line++] = "dp3 o0." +componentByIndex[0] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число - - //----- - - for (j = 1; j < 4; j++) { - shaderArr[line++] = "add t2.xyz, t2.xyz, t1.xyz"; - - shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; - shaderArr[line++] = "dp3 o0." +componentByIndex[j] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число - } - - shaderArr[line++] = "sat o0, o0"; - shaderArr[line++] = "dp4 t3.w, o0, c2.y"; - - //----- - - for (i = 0; i < 3; i++) { - shaderArr[line++] = "add t2.xyz, t2.xyz, t0.xyz"; - - shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; - shaderArr[line++] = "dp3 o0." +componentByIndex[0] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число - - for (j = 1; j < 4; j++){ - shaderArr[line++] = (i%2 == 1)?("add t2.xyz, t2.xyz, t1.xyz"):("sub t2.xyz, t2.xyz, t1.xyz"); - - shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; - shaderArr[line++] = "dp3 o0." +componentByIndex[j] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число - } - shaderArr[line++] = "sat o0, o0"; - shaderArr[line++] = "dp4 o0.x, o0, c2.y"; - shaderArr[line++] = "add t3.w, t3.w, o0.x"; - } - - shaderArr[line++] = "sub o0, c1.y, t3.w"; - - //--------- {73 opcodes} - return Procedure.compileFromArray(shaderArr, "OmniShadowMapFragment"); - } - - private static const componentByIndex:Array = ["x", "y", "z", "w"]; - - /** - * Добавляет object в список объектов, отбрасывающих тень. - * @param object Добавляемый объект. - */ - public function addCaster(object:Object3D):void { - if (_casters.indexOf(object) < 0) { - _casters.push(object); - } - } - - public function removeCaster(object:Object3D):void { - var index:int = _casters.indexOf(object); - if (index < 0) throw new Error("Caster not found"); - _casters[index] = _casters.pop(); - } - - /** - * Очищает список объектов, отбрасывающих тень. - */ - public function clearCasters():void { - _casters.length = 0; - } - - /** - * Качество тени. Задает разрешение shadowmap. Может принимать значения от 2 до 11. - */ - public function get mapSize():int { - return _mapSize; - } - - /** - * @private - */ - public function set mapSize(value:int):void { - if (value != _mapSize) { - this._mapSize = value; - if (value < 2) { - throw new ArgumentError("Map size cannot be less than 2."); - } else if (value > 1024) { - throw new ArgumentError("Map size exceeds maximum value 1024."); - } - if ((Math.log(value)/Math.LN2 % 1) != 0) { - throw new ArgumentError("Map size must be power of two."); - } - if (cubeShadowMap != null) { - cubeShadowMap.dispose(); - } - cubeShadowMap = null; - } - } - - /** - * Смещение Percentage Closer Filtering. Этот способ фильтрации используется для смягчения границ тени. - * 1 pcfOffset equivalent 1 degree for all blur - */ - public function get pcfOffset():Number { - return _pcfOffset; - } - - /** - * @private - */ - public function set pcfOffset(value:Number):void { - _pcfOffset = value; - type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; - fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); - } - - } -} - -import alternativa.engine3d.alternativa3d; -import alternativa.engine3d.core.Camera3D; -import alternativa.engine3d.core.DrawUnit; -import alternativa.engine3d.core.Light3D; -import alternativa.engine3d.core.Object3D; -import alternativa.engine3d.core.Renderer; -import alternativa.engine3d.core.VertexAttributes; -import alternativa.engine3d.materials.Material; -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.Surface; -import alternativa.engine3d.resources.Geometry; - -import flash.display3D.Context3D; -import flash.display3D.Context3DBlendFactor; -import flash.display3D.Context3DProgramType; -import flash.display3D.VertexBuffer3D; -import flash.display3D.textures.CubeTexture; -import flash.utils.Dictionary; - -class ShadowDebugMaterial extends Material { - - use namespace alternativa3d; - /** - * Прозрачность. - * Является дополнительным множителем к прозрачности текстуры. - * Значение по умолчанию 1. - */ - alternativa3d var alpha:Number = 1; - - private var cachedContext3D:Context3D; - private static var caches:Dictionary = new Dictionary(true); - private var program:ShaderProgram; - - /** - * Текстура. - */ - alternativa3d var cubeMap:CubeTexture; - - /** - * @private - */ - override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void { - var object:Object3D = surface.object; - // Стримы - var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION); - // Проверка на валидность - if (positionBuffer == null) return; - - // Обновляем кеш программы для данного контекста - if (camera.context3D != cachedContext3D) { - cachedContext3D = camera.context3D; - program = caches[cachedContext3D]; - } - - if (program == null) { - program = setupProgram(object); - program.upload(camera.context3D); - caches[cachedContext3D] = program; - } - - // Создание отрисовочного вызова - var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program); - // Установка стримов - drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]); - // Установка констант - 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); - - // Отправка на отрисовку - if (alpha < 1) { - drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA; - drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA; - camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT); - } else { - 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"; - vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE); - - var proc:Procedure = Procedure.compileFromArray([ - "#v0=vCubeMapCoord", - "mov v0, i0", - "m44 o0, i0, c0" - ]); - proc.assignVariableName(VariableType.CONSTANT, 0, "cProjMatrix", 4); - vertexLinker.addProcedure(proc, positionVar); - - var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); - var colorProc:Procedure = Procedure.compileFromArray([ - "#v0=vCubeMapCoord", - "#s0=sCubeMap", - "#c0=cDecode", - - "tex t0.xy, v0, s0 ", - "dp3 t0.xyz, t0.xy, c0.xy", - "mov t0.w, c0.w", - "mov o0, t0" - ]); - fragmentLinker.addProcedure(colorProc, "vCubeMapCoord"); - fragmentLinker.varyings = vertexLinker.varyings; - return new ShaderProgram(vertexLinker, fragmentLinker); - } - -} - -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 unusedBits:int = 63; - - public function SectionPlane(frontCameras:int, backCameras:int, unused:int) { - this.frontCameras = frontCameras; - this.backCameras = backCameras; - this.unusedBits = unused; - } - -} +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + * 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/ + */ +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.Renderer; + import alternativa.engine3d.core.Transform3D; + import alternativa.engine3d.core.VertexAttributes; + 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; + import alternativa.engine3d.materials.compiler.Procedure; + import alternativa.engine3d.materials.compiler.VariableType; + import alternativa.engine3d.objects.Joint; + import alternativa.engine3d.objects.Mesh; + import alternativa.engine3d.objects.Skin; + import alternativa.engine3d.objects.Surface; + import alternativa.engine3d.primitives.GeoSphere; + import alternativa.engine3d.resources.Geometry; + import alternativa.engine3d.resources.TextureResource; + + import flash.display3D.Context3D; + import flash.display3D.Context3DProgramType; + import flash.display3D.Context3DTextureFormat; + import flash.display3D.Context3DTriangleFace; + import flash.display3D.VertexBuffer3D; + import flash.display3D.textures.CubeTexture; + import flash.utils.Dictionary; + + use namespace alternativa3d; + + /** + * Class of the shadow, that is created by one source of light(OmniLight). Shadow is rendered in fixed volume. + * For binding of shadow to light source you need: + * 1) to set instance of the OmniLight as a value of property shadow of light source; + * 2) to add Object3D to corresponding list, using the method addCaster(). + * + * @see #addCaster() + * @see alternativa.engine3d.lights.OmniLight#shadow + * @see #farBoundPosition + */ + + public class OmniLightShadow extends Shadow { + + // TODO: calculate bias automaticaly + /** + * Degree of correcting offset of shadow map space. It need for getting rid of self-shadowing artifacts. + */ + public var biasMultiplier:Number = 0.97; + private static const DIFFERENCE_MULTIPLIER:Number = 32768; + private static const DEBUG_TYPE:String = "Sphere"; // Box + /** + * @private + */ + alternativa3d static var debugRadiusScale:Number = 0.2; + + private var renderer:Renderer = new Renderer(); + + // radius of the light source + private var radius:Number = 100; + + // cube map size + private var _mapSize:Number; + + private var _pcfOffset:Number; + + private var cubeShadowMap:CubeTexture; + + // Sides cameras + private var cameras:Vector. = new Vector.(); + + private var debugObject:Mesh; + private var debugMaterial:ShadowDebugMaterial; + + private var _casters:Vector. = new Vector.(); + + private var actualCasters:Vector. = new Vector.(); + private var actualCastersCount:int; + + // caster -> cube face + private var casterToEdgedCameraTransform:Transform3D = new Transform3D(); + // object -> light + private var objectToLightTransform:Transform3D = new Transform3D(); + // casters count in edge + private var prevActualCastersMask:int; + + private var cachedContext:Context3D; + private var programs:Dictionary = new Dictionary(); + + /** + * Создает экземпляр OmniLightShadow. + * @param mapSize Размер карты теней. Должен быть степенью 2. + * @param pcfOffset Смягчение границ тени. + */ + public function OmniLightShadow(mapSize:int = 128, pcfOffset:Number = 0) { + sections = new SectionPlane(0x11, 0x22, 0xC); // RU + sections.next = new SectionPlane(0x12, 0x21, 0xC); // LU + sections.next.next = new SectionPlane(0x14, 0x28, 0x3); // FU + sections.next.next.next = new SectionPlane(0x18, 0x24, 0x3); // BU + sections.next.next.next.next = new SectionPlane(0x5, 0xA, 0x30); // RF + sections.next.next.next.next.next = new SectionPlane(0x9, 0x6, 0x30); // RB + + this.mapSize = mapSize; + this.pcfOffset = pcfOffset; + + vertexShadowProcedure = getVShader(); + type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; + fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); + + debugMaterial = new ShadowDebugMaterial(); + debugMaterial.alpha = 0.3; + + for (var i:int = 0; i < 6; i++) { + var cam:Camera3D = new Camera3D(radius / 1000, radius); + cam.fov = 1.910633237; + cameras[i] = cam; + } + + // Left + cameras[1].rotationY = -Math.PI / 2; + cameras[1].scaleY = -1; + cameras[1].composeTransforms(); + // Right + cameras[0].rotationY = Math.PI / 2; + cameras[0].scaleY = -1; + cameras[0].composeTransforms(); + // Back + cameras[3].rotationX = -Math.PI / 2; + cameras[3].rotationZ = Math.PI; + cameras[3].scaleX = -1; + cameras[3].composeTransforms(); + // Front + cameras[2].rotationX = -Math.PI / 2; + cameras[2].scaleY = -1; + cameras[2].composeTransforms(); + // Bottom + cameras[5].rotationX = Math.PI; + cameras[5].scaleX = -1; + cameras[5].composeTransforms(); + // Top + cameras[4].rotationX = 0; + cameras[4].scaleY = -1; + cameras[4].composeTransforms(); + } + + private function createDebugObject(material:Material, context:Context3D):Mesh { + var geometry:Geometry; + var mesh:Mesh; + if (DEBUG_TYPE == "Box") { + mesh = new Mesh(); + geometry = new Geometry(8); + mesh.geometry = geometry; + + var attributes:Array = new Array(); + attributes[0] = VertexAttributes.POSITION; + attributes[1] = VertexAttributes.POSITION; + attributes[2] = VertexAttributes.POSITION; + geometry.addVertexStream(attributes); + + geometry.setAttributeValues(VertexAttributes.POSITION, Vector.([ + -1, -1, -1, + 1, -1, -1, + 1, 1, -1, + -1, 1, -1, + -1, -1, 1, + 1, -1, 1, + 1, 1, 1, + -1, 1, 1])); + geometry.indices = Vector.([ + 0, 1, 2, 3, 0, 2, 2, 1, 0, 3, 2, 0, + 2, 6, 1, 1, 6, 2, 1, 6, 5, 5, 6, 1, + 6, 4, 5, 5, 4, 6, 6, 4, 7, 7, 4, 6, + 0, 7, 4, 4, 7, 0, 0, 7, 3, 3, 7, 0, + 3, 6, 2, 2, 6, 3, 3, 7, 6, 6, 7, 3, + 0, 5, 1, 1, 5, 0, 0, 4, 5, 5, 4, 0]); + mesh.addSurface(material, 0, 24); + } else { + mesh = new GeoSphere(1, 4, true); + // Create two side + var triangles:Vector. = mesh.geometry.indices; + var numTriangles:int = triangles.length; + for (var i:int = 0; i < numTriangles; i += 3) { + var a:uint = triangles[i]; + var b:uint = triangles[int(i + 1)]; + var c:uint = triangles[int(i + 2)]; + triangles.push(c, b, a); + } + mesh.geometry.indices = triangles; + mesh.getSurface(0).numTriangles = triangles.length / 3; + mesh.setMaterialToAllSurfaces(material); + } + mesh.geometry.upload(context); + return mesh; + } + + // Draw in shadow map + override alternativa3d function process(camera:Camera3D):void { + var i:int; + var j:int; + var caster:Object3D; + var context:Context3D = camera.context3D; + + // Checking changed context + if (context != cachedContext) { + programs = new Dictionary(); + cubeShadowMap = null; + cachedContext = context; + } + + // Culling invisible casters + if (cubeShadowMap == null) { + cubeShadowMap = context.createCubeTexture(_mapSize, Context3DTextureFormat.BGRA, true); + debugMaterial.cubeMap = cubeShadowMap; + prevActualCastersMask = 63; + } + + // Calculate parameters + radius = OmniLight(_light).attenuationEnd; + for (i = 0; i < 6; i++) { + var cam:Camera3D = cameras[i]; + cam.nearClipping = radius / 1000; + cam.farClipping = radius; + cam.calculateProjection(1, 1); + } + + var castersCount:int = _casters.length; + actualCastersCount = 0; + + for (i = 0; i < castersCount; i++) { + caster = _casters[i]; + + var visible:Boolean = caster.visible; + var parent:Object3D = caster._parent; + while (visible && parent != null) { + visible = parent.visible; + parent = parent._parent; + } + + 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++; + + // Pack camera culling + caster.culling <<= 16; + if (caster.boundBox != null) { + // 1 - calculate planes in object space + calculatePlanes(caster.localToLightTransform); + // 2 - check object location cameras (sections) + caster.culling |= recognizeObjectCameras(caster.boundBox); + } + } + + // 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]; + + var edgeBit:int = (1 << i); + if (actualCastersCount > 0) { + // Настройка параметров рендеринга: + renderer.camera = camera; + context.setRenderToTexture(cubeShadowMap, true, 0, i); + context.clear(1, 0, 0, 0.0); + + // Пробегаемся по кастерам + for (j = 0; j < actualCastersCount; j++) { + caster = actualCasters[j]; + + // Проверить находится ли кастер в зоне 4-х плоскостей + if ((caster.culling & edgeBit)) { + // собираем матрицу перевода из кастера в пространство edgeCamera + casterToEdgedCameraTransform.combine(edgeCamera.inverseTransform, caster.localToLightTransform); + // Собираем драуколлы для кастера и его дочерних объектов + collectDraws(context, caster, edgeCamera); + } + } + +// if (renderer.drawUnits.length == 0) context.clear(0, 0, 0, 0.0); + + // Drawing + renderer.render(context); + prevActualCastersMask |= edgeBit; + } + else { + // Если относительно одной из камер ничего не менялось, не вызываем отрисовочный вызов + + if ((prevActualCastersMask & edgeBit)) { + context.setRenderToTexture(cubeShadowMap, false, 0, i); + context.clear(1, 0, 0, 0); + + prevActualCastersMask &= ~edgeBit; + } + } + } + context.setRenderToBackBuffer(); + + // Unpack camera culling value + for (j = 0; j < actualCastersCount; j++) { + caster = actualCasters[j]; + // If there was -1, after shift it will be -1 too + caster.culling >>= 16; + } + + if (debug) { + // Create debug object if needed + if (debugObject == null) { + debugObject = createDebugObject(debugMaterial, camera.context3D); + } + debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = radius * debugRadiusScale; + debugObject.composeTransforms(); + + // Формируем матрицу трансформации для debugObject + debugObject.localToCameraTransform.combine(_light.localToCameraTransform, debugObject.transform); + + // Отрисовываем + var debugSurface:Surface = debugObject._surfaces[0]; + debugMaterial.collectDraws(camera, debugSurface, debugObject.geometry, null, 0, false, -1); + } + actualCasters.length = 0; + } + + 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); + + // collect actualCasters for light + if (child.boundBox == null || OmniLight(_light).checkBound(child)) { + actualCasters[actualCastersCount] = child; + actualCastersCount++; + + // Pack camera culling + child.culling <<= 16; + if (child.boundBox != null) { + // 1 - calculate planes in object space + calculatePlanes(child.localToLightTransform); + // 2 - check object location cameras (sections) + child.culling |= recognizeObjectCameras(child.boundBox); + } + } + + // 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); + } + } + } + + private var sections:SectionPlane; + + private function calculatePlanes(transform:Transform3D):void { + // DUBFLR + var planeRU:SectionPlane = sections; + var planeLU:SectionPlane = sections.next; + var planeFU:SectionPlane = sections.next.next; + var planeBU:SectionPlane = sections.next.next.next; + var planeRF:SectionPlane = sections.next.next.next.next; + var planeRB:SectionPlane = sections.next.next.next.next.next; + + // 1, 0, 1 + planeRU.x = transform.a + transform.i; + planeRU.y = transform.b + transform.j; + planeRU.z = transform.c + transform.k; + planeRU.offset = -(transform.d + transform.l); + + // -1, 0, 1 + planeLU.x = transform.i - transform.a; + planeLU.y = transform.j - transform.b; + planeLU.z = transform.k - transform.c; + planeLU.offset = transform.d - transform.l; + + // 0, 1, 1 + planeFU.x = transform.e + transform.i; + planeFU.y = transform.f + transform.j; + planeFU.z = transform.g + transform.k; + planeFU.offset = -(transform.h + transform.l); + + // 0, -1, 1 + planeBU.x = transform.i - transform.e; + planeBU.y = transform.j - transform.f; + planeBU.z = transform.k - transform.g; + planeBU.offset = transform.h - transform.l; + + // 1, 1, 0 + planeRF.x = transform.a + transform.e; + planeRF.y = transform.b + transform.f; + planeRF.z = transform.c + transform.g; + planeRF.offset = -(transform.d + transform.h); + + // 1, -1, 0 + planeRB.x = transform.a - transform.e; + planeRB.y = transform.b - transform.f; + planeRB.z = transform.c - transform.g; + planeRB.offset = transform.h - transform.d; + + // var ax:Number = transform.c - transform.a + transform.b; // E +// var ay:Number = transform.g - transform.e + transform.f; +// var az:Number = transform.k - transform.i + transform.j; +// var bx:Number = transform.c - transform.a - transform.b; // H +// var by:Number = transform.g - transform.e - transform.f; +// var bz:Number = transform.k - transform.i - transform.j; +// planeRU.x = bz * ay - by * az; +// planeRU.y = bx * az - bz * ax; +// planeRU.z = by * ax - bx * ay; +// planeRU.offset = transform.d*planeRU.x + transform.h*planeRU.y + transform.l*planeRU.z; +// +// ax = transform.c + transform.a - transform.b; // D +// ay = transform.g + transform.e - transform.f; +// az = transform.k + transform.i - transform.j; +// bx = transform.c + transform.a + transform.b; // A +// by = transform.g + transform.e + transform.f; +// bz = transform.k + transform.i + transform.j; +// planeLU.x = bz * ay - by * az; +// planeLU.y = bx * az - bz * ax; +// planeLU.z = by * ax - bx * ay; +// planeLU.offset = transform.d*planeLU.x + transform.h*planeLU.y + transform.l*planeLU.z; +// +// ax = transform.c - transform.a - transform.b; // H +// ay = transform.g - transform.e - transform.f; +// az = transform.k - transform.i - transform.j; +// bx = transform.c + transform.a - transform.b; // D +// by = transform.g + transform.e - transform.f; +// bz = transform.k + transform.i - transform.j; +// planeFU.x = bz * ay - by * az; +// planeFU.y = bx * az - bz * ax; +// planeFU.z = by * ax - bx * ay; +// planeFU.offset = transform.d*planeFU.x + transform.h*planeFU.y + transform.l*planeFU.z; +// +// ax = transform.c + transform.a + transform.b; // A +// ay = transform.g + transform.e + transform.f; +// az = transform.k + transform.i + transform.j; +// bx = transform.c - transform.a + transform.b; // E +// by = transform.g - transform.e + transform.f; +// bz = transform.k - transform.i + transform.j; +// planeBU.x = bz * ay - by * az; +// planeBU.y = bx * az - bz * ax; +// planeBU.z = by * ax - bx * ay; +// planeBU.offset = transform.d*planeBU.x + transform.h*planeBU.y + transform.l*planeBU.z; +// +// ax = transform.a - transform.b + transform.c; // D +// ay = transform.e - transform.f + transform.g; +// az = transform.i - transform.j + transform.k; +// bx = transform.a - transform.b - transform.c; // C +// by = transform.e - transform.f - transform.g; +// bz = transform.i - transform.j - transform.k; +// planeRF.x = bz * ay - by * az; +// planeRF.y = bx * az - bz * ax; +// planeRF.z = by * ax - bx * ay; +// planeRF.offset = transform.d*planeRF.x + transform.h*planeRF.y + transform.l*planeRF.z; +// +// ax = transform.a + transform.b - transform.c; // B +// ay = transform.e + transform.f - transform.g; +// az = transform.i + transform.j - transform.k; +// bx = transform.a + transform.b + transform.c; // A +// by = transform.e + transform.f + transform.g; +// bz = transform.i + transform.j + transform.k; +// planeRB.x = bz * ay - by * az; +// planeRB.y = bx * az - bz * ax; +// planeRB.z = by * ax - bx * ay; +// planeRB.offset = transform.d*planeRB.x + transform.h*planeRB.y + transform.l*planeRB.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; + } + 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; + } + 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 { + 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.unusedBits; + } + return culling; + } + + private function collectDraws(context:Context3D, caster:Object3D, edgeCamera:Camera3D):void { + // если объект является мешем, собираем для него дроуколы + var mesh:Mesh = caster as Mesh; + if (mesh != null && mesh.geometry != null) { + var program:ShaderProgram; + var programListByTransformProcedure:Vector.; + var skin:Skin = mesh as Skin; + + // пробегаемся по сурфейсам + for (var i:int = 0; i < mesh._surfacesLength; i++) { + var surface:Surface = mesh._surfaces[i]; + if (surface.material == null) continue; + + var material:Material = surface.material; + var geometry:Geometry = mesh.geometry; + var alphaTest:Boolean; + var useDiffuseAlpha:Boolean; + var alphaThreshold:Number; + var materialAlpha:Number; + var diffuse:TextureResource; + var opacity:TextureResource; + var uvBuffer:VertexBuffer3D; + + // ловим параметры прозрачности + if (material is TextureMaterial) { + alphaThreshold = TextureMaterial(material).alphaThreshold; + materialAlpha = TextureMaterial(material).alpha; + diffuse = TextureMaterial(material).diffuseMap; + opacity = TextureMaterial(material).opacityMap; + alphaTest = alphaThreshold > 0; + useDiffuseAlpha = TextureMaterial(material).opacityMap == null; + uvBuffer = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]); + if (uvBuffer == null) continue; + } else { + alphaTest = false; + useDiffuseAlpha = false; + } + + + var positionBuffer:VertexBuffer3D = mesh.geometry.getVertexBuffer(VertexAttributes.POSITION); + if (positionBuffer == null) continue; + + // поднимаем и кэшируем programListByTransformProcedure + if (skin != null) { + caster.transformProcedure = skin.surfaceTransformProcedures[i]; + } + programListByTransformProcedure = programs[caster.transformProcedure]; + if (programListByTransformProcedure == null) { + programListByTransformProcedure = new Vector.(3, true); + programs[caster.transformProcedure] = programListByTransformProcedure; + } + + // собираем программу и Формируем дроуюнит + program = getProgram(caster.transformProcedure, programListByTransformProcedure, context, alphaTest, useDiffuseAlpha); + var drawUnit:DrawUnit = renderer.createDrawUnit(caster, program.program, mesh.geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program); + drawUnit.culling = Context3DTriangleFace.BACK; + + // Установка стрима + drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, mesh.geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]); + + if (alphaTest) { + drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aUV"), uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[0]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[0]]); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cThresholdAlpha"), alphaThreshold, 0, 0, materialAlpha); + if (useDiffuseAlpha) { + drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sTexture"), diffuse._texture); + } else { + drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sTexture"), opacity._texture); + } + } + + // Установка констант + caster.setTransformConstants(drawUnit, surface, program.vertexShader, null); + drawUnit.setProjectionConstants(edgeCamera, program.vertexShader.getVariableIndex("cProjMatrix"), casterToEdgedCameraTransform); + drawUnit.setVertexConstantsFromTransform(program.vertexShader.getVariableIndex("cCasterToOmni"), caster.localToLightTransform); + + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cConstants"), 1 / 255, 0, 255 / radius, 1); + + renderer.addDrawUnit(drawUnit, Renderer.OPAQUE); + } + } + } + + /** + * @private + * Процедура для передачи UV координат во фрагментный шейдер + */ + static private const passUVProcedure:Procedure = new Procedure(["#v0=vUV", "#a0=aUV", "mov v0, a0"], "passUVProcedure"); + + // diffuse alpha test + private static const diffuseAlphaTestProcedure:Procedure = new Procedure([ + "#v0=vUV", + "#s0=sTexture", + "#c0=cThresholdAlpha", + "tex t0, v0, s0 <2d, linear,repeat, miplinear>", + "mul t0.w, t0.w, c0.w", + "sub t0.w, t0.w, c0.x", + "kil t0.w" + ], "diffuseAlphaTestProcedure"); + + // opacity alpha test + private static const opacityAlphaTestProcedure:Procedure = new Procedure([ + "#v0=vUV", + "#s0=sTexture", + "#c0=cThresholdAlpha", + "tex t0, v0, s0 <2d, linear,repeat, miplinear>", + "mul t0.w, t0.x, c0.w", + "sub t0.w, t0.w, c0.x", + "kil t0.w" + ], "opacityAlphaTestProcedure"); + + + private function getProgram(transformProcedure:Procedure, programListByTransformProcedure:Vector., context:Context3D, alphaTest:Boolean, useDiffuseAlpha:Boolean):ShaderProgram { + var key:int = (alphaTest ? (useDiffuseAlpha ? 1 : 2) : 0); + var program:ShaderProgram = programListByTransformProcedure[key]; + + if (program == null) { + var vLinker:Linker = new Linker(Context3DProgramType.VERTEX); + var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); + + var positionVar:String = "aPosition"; + vLinker.declareVariable(positionVar, VariableType.ATTRIBUTE); + + if (alphaTest) { + vLinker.addProcedure(passUVProcedure); + } + + if (transformProcedure != null) { + var newPosVar:String = "tTransformedPosition"; + vLinker.declareVariable(newPosVar); + vLinker.addProcedure(transformProcedure, positionVar); + vLinker.setOutputParams(transformProcedure, newPosVar); + positionVar = newPosVar; + } + + var proc:Procedure = Procedure.compileFromArray([ + "#v0=vDistance", + + "m34 t0.xyz, i0, c2", + "mov v0, t0.xyzx", + + "m44 o0, i0, c0" + ]); + proc.assignVariableName(VariableType.CONSTANT, 0, "cProjMatrix", 4); + proc.assignVariableName(VariableType.CONSTANT, 2, "cCasterToOmni", 3); + + vLinker.addProcedure(proc, positionVar); + + if (alphaTest) { + if (useDiffuseAlpha) { + fLinker.addProcedure(diffuseAlphaTestProcedure); + } else { + fLinker.addProcedure(opacityAlphaTestProcedure); + } + } + fLinker.addProcedure(Procedure.compileFromArray([ + "#v0=vDistance", // xyz + "#c0=cConstants", // 1/255, 0, 255/radius, 1 + // calculate distance + "dp3 t0.z, v0.xyz, v0.xyz", + "sqt t0.z, t0.z", // x: [0, radius] + "mul t0.z, t0.z, c0.z", // x: [0, 255] + // codeing + "frc t0.y, t0.z", + "sub t0.x, t0.z, t0.y", + "mul t0.x, t0.x, c0.x", + + "mov t0.w, c0.w", + "mov o0, t0" + ])); + program = new ShaderProgram(vLinker, fLinker); + fLinker.varyings = vLinker.varyings; + programListByTransformProcedure[key] = program; + program.upload(context); + + } + return program; + } + + + //------------- ShadowMap Shader in material---------- + + /** + * @private + */ + alternativa3d override function setup(drawUnit:DrawUnit, vertexLinker:Linker, fragmentLinker:Linker, surface:Surface):void { + // Устанавливаем матрицу перевода в шедоумапу + objectToLightTransform.combine(_light.cameraToLocalTransform, surface.object.localToCameraTransform); + drawUnit.setVertexConstantsFromTransform(vertexLinker.getVariableIndex("cObjectToLightTransform"), objectToLightTransform); + + // Устанавливаем шедоумапу + drawUnit.setTextureAt(fragmentLinker.getVariableIndex("sCubeMap"), cubeShadowMap); + + // Устанавливаем коеффициенты + if (_pcfOffset > 0) { + var offset:Number = Math.tan(_pcfOffset / 180 * Math.PI) / 3; + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cPCFOffsets"), -3 / 2, 1 / 16, 0, 0); + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -1, 1, 0, offset); + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cDecode"), -DIFFERENCE_MULTIPLIER, -DIFFERENCE_MULTIPLIER / 255, biasMultiplier * DIFFERENCE_MULTIPLIER / radius, 10); + } else { + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -DIFFERENCE_MULTIPLIER, -DIFFERENCE_MULTIPLIER / 255, biasMultiplier * DIFFERENCE_MULTIPLIER / radius, 1.0); + } + } + + private static function getVShader():Procedure { + var shader:Procedure = Procedure.compileFromArray([ + "#v0=vSample", + + "m34 t0.xyz, i0, c0", + + "mov v0, t0.xyz" + ], "OmniShadowMapVertex"); + shader.assignVariableName(VariableType.CONSTANT, 0, "cObjectToLightTransform", 3); + return shader; + } + + private static function getFShader():Procedure { + var shaderArr:Array = [ + "#v0=vSample", + "#c0=cConstants", + "#s0=sCubeMap" + ]; + var line:int = 3; + // Расстояние + shaderArr[line++] = "dp3 t0.z, v0.xyz, v0.xyz"; + shaderArr[line++] = "sqt t0.z, t0.z"; // w: [0, radius] + shaderArr[line++] = "tex t0.xy, v0, s0 "; + shaderArr[line++] = "dp3 t0.x, t0.xyz, c0.xyz"; // декодируем, находим разницу между расстояниями и умножаем ее на большое число + + // рассчитываем значение тени + shaderArr[line++] = "sat t0.x, t0.x"; + shaderArr[line++] = "sub o0, c0.w, t0.x"; + +// shaderArr[line++] = "sat t0.x, t0.x"; +// shaderArr[line++] = "sub t0.x, c0.w, t0.x"; +// shaderArr[line++] = "sat t0.x, t0.x"; +// shaderArr[line++] = "mov o0, t0.x"; + + return Procedure.compileFromArray(shaderArr, "OmniShadowMapFragment"); + } + + private static function getFShaderPCF():Procedure { + var shaderArr:Array = [ + "#v0=vSample", + "#c0=cDecode", + "#c1=cConstants", + "#c2=cPCFOffsets", + "#s0=sCubeMap" + ]; + var line:int = 5; + var i:int; + var j:int; + + // допустимо использование временных переменных t0 t1 t2 t3 + // v0 - sample + + // calculate 2 ortogonal vectors + // (-y, x, 0) + shaderArr[line++] = "mov t1.xyzw, v0.yxzw"; + shaderArr[line++] = "mul t1.xyzw, t1.xyzw, c1.xyzz"; + + shaderArr[line++] = "crs t0.xyz, v0.xyz, t1.xyz"; + + // normalize vectors + shaderArr[line++] = "nrm t0.xyz, t0.xyz"; + shaderArr[line++] = "nrm t1.xyz, t1.xyz"; + + shaderArr[line++] = "dp3 t3.z, v0.xyz, v0.xyz"; + shaderArr[line++] = "sqt t3.z, t3.z"; // distance + + // apply pcf offset + shaderArr[line++] = "mul t0.w, c1.w, t3.z"; // с1.w = offset/radius + shaderArr[line++] = "mul t0.xyz, t0.xyz, t0.w"; + shaderArr[line++] = "mul t1.xyz, t1.xyz, t0.w"; + // --------- {13 opcode} + + // t0, t1 - ortogonals ↑→ + // t2 - current vector + + // t3.z distance to object + // t3.xy - result from shadow map + // t3.w - summ of sat + + // first point + shaderArr[line++] = "add t2.xyz, t0.xyz, t1.xyz"; + shaderArr[line++] = "mul t2.xyz, t2.xyz, c2.xxx"; + shaderArr[line++] = "add t2.xyz, t2.xyz, v0.xyz"; + + // получаем длинну из шадоумапы [0, 1] +// shaderArr[line++] = "mov t3.z, t0.w"; + + shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; + shaderArr[line++] = "dp3 o0." + componentByIndex[0] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число + + //----- + + for (j = 1; j < 4; j++) { + shaderArr[line++] = "add t2.xyz, t2.xyz, t1.xyz"; + + shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; + shaderArr[line++] = "dp3 o0." + componentByIndex[j] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число + } + + shaderArr[line++] = "sat o0, o0"; + shaderArr[line++] = "dp4 t3.w, o0, c2.y"; + + //----- + + for (i = 0; i < 3; i++) { + shaderArr[line++] = "add t2.xyz, t2.xyz, t0.xyz"; + + shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; + shaderArr[line++] = "dp3 o0." + componentByIndex[0] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число + + for (j = 1; j < 4; j++) { + shaderArr[line++] = (i % 2 == 1) ? ("add t2.xyz, t2.xyz, t1.xyz") : ("sub t2.xyz, t2.xyz, t1.xyz"); + + shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; + shaderArr[line++] = "dp3 o0." + componentByIndex[j] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число + } + shaderArr[line++] = "sat o0, o0"; + shaderArr[line++] = "dp4 o0.x, o0, c2.y"; + shaderArr[line++] = "add t3.w, t3.w, o0.x"; + } + + shaderArr[line++] = "sub o0, c1.y, t3.w"; + + //--------- {73 opcodes} + return Procedure.compileFromArray(shaderArr, "OmniShadowMapFragment"); + } + + private static const componentByIndex:Array = ["x", "y", "z", "w"]; + + /** + * Adds given object to list of objects, that cast shadow. + * @param object Added object. + */ + public function addCaster(object:Object3D):void { + if (_casters.indexOf(object) < 0) { + _casters.push(object); + } + } + + /** + * Removes given object from shadow casters list. + * @param object Object which should be removed from shadow casters list. + */ + public function removeCaster(object:Object3D):void { + var index:int = _casters.indexOf(object); + if (index < 0) throw new Error("Caster not found"); + _casters[index] = _casters.pop(); + } + + /** + * Clears the list of objects, that cast shadow. + */ + public function clearCasters():void { + _casters.length = 0; + } + + /** + * Set resolution of shadow map. This property can get value of power of 2 (up to 2048). + * OmniLightShadow uses 6 shadow maps. + */ + public function get mapSize():int { + return _mapSize; + } + + /** + * @private + */ + public function set mapSize(value:int):void { + if (value != _mapSize) { + this._mapSize = value; + if (value < 2) { + throw new ArgumentError("Map size cannot be less than 2."); + } else if (value > 1024) { + throw new ArgumentError("Map size exceeds maximum value 1024."); + } + if ((Math.log(value) / Math.LN2 % 1) != 0) { + throw new ArgumentError("Map size must be power of two."); + } + if (cubeShadowMap != null) { + cubeShadowMap.dispose(); + } + cubeShadowMap = null; + } + } + + /** + * Offset of Percentage Closer Filtering. This way of filtering is used for mitigation of shadow bounds. + */ + public function get pcfOffset():Number { + return _pcfOffset; + } + + /** + * @private + */ + public function set pcfOffset(value:Number):void { + _pcfOffset = value; + type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; + fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); + } + + } +} + +import alternativa.engine3d.alternativa3d; +import alternativa.engine3d.core.Camera3D; +import alternativa.engine3d.core.DrawUnit; +import alternativa.engine3d.core.Light3D; +import alternativa.engine3d.core.Object3D; +import alternativa.engine3d.core.Renderer; +import alternativa.engine3d.core.VertexAttributes; +import alternativa.engine3d.materials.Material; +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.Surface; +import alternativa.engine3d.resources.Geometry; + +import flash.display3D.Context3D; +import flash.display3D.Context3DBlendFactor; +import flash.display3D.Context3DProgramType; +import flash.display3D.VertexBuffer3D; +import flash.display3D.textures.CubeTexture; +import flash.utils.Dictionary; + +class ShadowDebugMaterial extends Material { + + use namespace alternativa3d; + + /** + * Прозрачность. + * Является дополнительным множителем к прозрачности текстуры. + * Значение по умолчанию 1. + */ + alternativa3d var alpha:Number = 1; + + private var cachedContext3D:Context3D; + private static var caches:Dictionary = new Dictionary(true); + private var program:ShaderProgram; + + /** + * Текстура. + */ + alternativa3d var cubeMap:CubeTexture; + + /** + * @private + */ + override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void { + var object:Object3D = surface.object; + // Стримы + var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION); + // Проверка на валидность + if (positionBuffer == null) return; + + // Обновляем кеш программы для данного контекста + if (camera.context3D != cachedContext3D) { + cachedContext3D = camera.context3D; + program = caches[cachedContext3D]; + } + + if (program == null) { + program = setupProgram(object); + program.upload(camera.context3D); + caches[cachedContext3D] = program; + } + + // Создание отрисовочного вызова + var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program); + // Установка стримов + drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]); + // Установка констант + 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); + + // Отправка на отрисовку + if (alpha < 1) { + drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA; + drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA; + camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT); + } else { + 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"; + vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE); + + var proc:Procedure = Procedure.compileFromArray([ + "#v0=vCubeMapCoord", + "mov v0, i0", + "m44 o0, i0, c0" + ]); + proc.assignVariableName(VariableType.CONSTANT, 0, "cProjMatrix", 4); + vertexLinker.addProcedure(proc, positionVar); + + var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); + var colorProc:Procedure = Procedure.compileFromArray([ + "#v0=vCubeMapCoord", + "#s0=sCubeMap", + "#c0=cDecode", + + "tex t0.xy, v0, s0 ", + "dp3 t0.xyz, t0.xy, c0.xy", + "mov t0.w, c0.w", + "mov o0, t0" + ]); + fragmentLinker.addProcedure(colorProc, "vCubeMapCoord"); + fragmentLinker.varyings = vertexLinker.varyings; + return new ShaderProgram(vertexLinker, fragmentLinker); + } + +} + +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 unusedBits:int = 63; + + public function SectionPlane(frontCameras:int, backCameras:int, unused:int) { + this.frontCameras = frontCameras; + this.backCameras = backCameras; + this.unusedBits = unused; + } + +}