diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..574e920 --- /dev/null +++ b/pom.xml @@ -0,0 +1,57 @@ + + 4.0.0 + platform.clients.fp11.libraries + Alternativa3D + swc + 8.30.0-SNAPSHOT + + platform.clients.fp11.tools.maven + BasePom + 2.58.0 + + + + scm:git:https://github.com/AlternativaPlatform/Alternativa3D/master/ + + + + + + + platform.client.formats + A3DModelsBase + 2.5.2 + swc + external + + + platform.clients.fp10.libraries + AlternativaProtocol + 2.53.0 + swc + external + + + + + + + platform.client.formats + A3DModelsBase + swc + external + + + platform.clients.fp10 + OSGiBase + swc + external + + + platform.clients.fp10.libraries + AlternativaProtocol + swc + external + + + diff --git a/src/alternativa/Alternativa3D.as b/src/alternativa/Alternativa3D.as index a18bba8..fc704c0 100644 --- a/src/alternativa/Alternativa3D.as +++ b/src/alternativa/Alternativa3D.as @@ -17,6 +17,6 @@ package alternativa { /** * Library version in the format: generation.feature-version.fix-version. */ - public static const version:String = "8.27.0"; + public static const version:String = "8.29.0"; } } diff --git a/src/alternativa/engine3d/core/Camera3D.as b/src/alternativa/engine3d/core/Camera3D.as index dad6962..e3bf0b4 100644 --- a/src/alternativa/engine3d/core/Camera3D.as +++ b/src/alternativa/engine3d/core/Camera3D.as @@ -220,6 +220,7 @@ public class Camera3D extends Object3D { if (transformChanged) composeTransforms(); localToGlobalTransform.copy(transform); globalToLocalTransform.copy(inverseTransform); + // Searching for upper hierarchy point var root:Object3D = this; while (root.parent != null) { root = root.parent; @@ -227,6 +228,8 @@ public class Camera3D extends Object3D { localToGlobalTransform.append(root.transform); globalToLocalTransform.prepend(root.inverseTransform); } + var excludedLightLength:int = root.excludedLights.length; + // Calculating the rays of mouse events view.calculateRays(this); for (i = origins.length; i < view.raysLength; i++) { @@ -283,7 +286,7 @@ public class Camera3D extends Object3D { occluder = occluders[i]; if (occluder.enabled) { // Debug - occluder.collectDraws(this, null, 0); + occluder.collectDraws(this, null, 0, false); if (debug && occluder.boundBox != null && (checkInDebug(occluder) & Debug.BOUNDS)) Debug.drawBoundBox(this, occluder.boundBox, occluder.localToCameraTransform); occluders[j] = occluder; j++; @@ -300,12 +303,11 @@ public class Camera3D extends Object3D { light.green = ((light.color >> 8) & 0xFF) * light.intensity / 255; light.blue = (light.color & 0xFF) * light.intensity / 255; // Debug - light.collectDraws(this, null, 0); + light.collectDraws(this, null, 0, false); if (debug && light.boundBox != null && (checkInDebug(light) & Debug.BOUNDS)) Debug.drawBoundBox(this, light.boundBox, light.localToCameraTransform); // Shadows preparing if (light.shadow != null) { - light.shadow._light = light; light.shadow.process(this); } lights[j] = light; @@ -327,10 +329,15 @@ public class Camera3D extends Object3D { // Check if object needs in lightning if (lightsLength > 0 && root.useLights) { // Pass the lights to children and calculate appropriate transformations + var childLightsLength:int = 0; if (root.boundBox != null) { - var childLightsLength:int = 0; for (i = 0; i < lightsLength; i++) { light = lights[i]; + // Checking light source for existing in excludedLights + j = 0; + while (j = new Vector.(); + /** * @private */ @@ -282,6 +287,16 @@ package alternativa.engine3d.core { */ alternativa3d var globalToLocalTransform:Transform3D = new Transform3D(); + /** + * @private + */ + alternativa3d var localToLightTransform:Transform3D = new Transform3D(); + + /** + * @private + */ + alternativa3d var lightToLocalTransform:Transform3D = new Transform3D(); + /** * @private */ @@ -1272,6 +1287,7 @@ package alternativa.engine3d.core { */ alternativa3d function calculateChildrenVisibility(camera:Camera3D):void { for (var child:Object3D = childrenList; child != null; child = child.next) { + // Checking visibility flag if (child.visible) { // Compose matrix and inverse matrix if (child.transformChanged) child.composeTransforms(); @@ -1297,16 +1313,19 @@ package alternativa.engine3d.core { /** * @private */ - alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { } /** * @private */ - alternativa3d function collectChildrenDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + alternativa3d function collectChildrenDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { var i:int; var light:Light3D; + var excludedLightLength:int = excludedLights.length; + for (var child:Object3D = childrenList; child != null; child = child.next) { + // Checking visibility flag if (child.visible) { // Check getting in frustum and occluding if (child.culling >= 0 && (child.boundBox == null || camera.occludersLength == 0 || !child.boundBox.checkOcclusion(camera.occluders, camera.occludersLength, child.localToCameraTransform))) { @@ -1320,10 +1339,16 @@ package alternativa.engine3d.core { // Check if object needs in lightning if (lightsLength > 0 && child.useLights) { // Pass the lights to children and calculate appropriate transformations + var childLightsLength:int = 0; + var j:int; if (child.boundBox != null) { - var childLightsLength:int = 0; for (i = 0; i < lightsLength; i++) { light = lights[i]; + // Checking object for existing in excludedLights + j = 0; + while (jObject3D. + * Toggle off light source from litting this object + */ + public function excludeLight(light:Light3D):void{ + excludedLights.push(light); + } + + /** + * Resets list of lights excluded from litting this object + */ + public function resetLights():void{ + excludedLights.length = 0; + } + + /** + * Returns a copy of object * @return A copy of this Object3D. */ public function clone():Object3D { diff --git a/src/alternativa/engine3d/core/Occluder.as b/src/alternativa/engine3d/core/Occluder.as index 8e87e33..7e55335 100644 --- a/src/alternativa/engine3d/core/Occluder.as +++ b/src/alternativa/engine3d/core/Occluder.as @@ -133,7 +133,7 @@ package alternativa.engine3d.core { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { // Debug if (camera.debug) { if (camera.checkInDebug(this) & Debug.CONTENT) { @@ -145,7 +145,7 @@ package alternativa.engine3d.core { debugWire.geometry.upload(camera.context3D); } debugWire.localToCameraTransform.copy(localToCameraTransform); - debugWire.collectDraws(camera, null, 0); + debugWire.collectDraws(camera, null, 0, false); } } } @@ -567,9 +567,9 @@ package alternativa.engine3d.core { island[i] = newFace; island[j] = null; face = newFace; + // TODO: comment to ENG // Если, то собираться будет парами, иначе к одной прицепляется максимально (это чуть быстрее) //if (pairWeld) break; - } } } diff --git a/src/alternativa/engine3d/core/Renderer.as b/src/alternativa/engine3d/core/Renderer.as index 3838ee8..0dec821 100644 --- a/src/alternativa/engine3d/core/Renderer.as +++ b/src/alternativa/engine3d/core/Renderer.as @@ -29,6 +29,8 @@ package alternativa.engine3d.core { public static const OPAQUE:int = 20; + public static const OPAQUE_OVERHEAD:int = 25; + public static const DECALS:int = 30; public static const TRANSPARENT_SORT:int = 40; @@ -62,6 +64,9 @@ package alternativa.engine3d.core { case OPAQUE: _context3D.setDepthTest(true, Context3DCompareMode.LESS); break; + case OPAQUE_OVERHEAD: + _context3D.setDepthTest(false, Context3DCompareMode.EQUAL); + break; case DECALS: _context3D.setDepthTest(false, Context3DCompareMode.LESS_EQUAL); break; diff --git a/src/alternativa/engine3d/effects/ParticlePrototype.as b/src/alternativa/engine3d/effects/ParticlePrototype.as index cf3bda9..0c85c9d 100644 --- a/src/alternativa/engine3d/effects/ParticlePrototype.as +++ b/src/alternativa/engine3d/effects/ParticlePrototype.as @@ -4,8 +4,8 @@ * 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.effects { import alternativa.engine3d.alternativa3d; diff --git a/src/alternativa/engine3d/effects/ParticleSystem.as b/src/alternativa/engine3d/effects/ParticleSystem.as index f0c7f42..70c469d 100644 --- a/src/alternativa/engine3d/effects/ParticleSystem.as +++ b/src/alternativa/engine3d/effects/ParticleSystem.as @@ -126,7 +126,7 @@ package alternativa.engine3d.effects { return pause ? (stopTime - subtractiveTime) : (getTimer()*0.001 - subtractiveTime); } - override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { // Create geometry and program if (vertexBuffer == null) createAndUpload(camera.context3D); // Average size diff --git a/src/alternativa/engine3d/lights/OmniLight.as b/src/alternativa/engine3d/lights/OmniLight.as index ee23aec..cb9b502 100644 --- a/src/alternativa/engine3d/lights/OmniLight.as +++ b/src/alternativa/engine3d/lights/OmniLight.as @@ -13,6 +13,8 @@ package alternativa.engine3d.lights { import alternativa.engine3d.core.Light3D; import alternativa.engine3d.core.Object3D; import alternativa.engine3d.core.Transform3D; + import alternativa.engine3d.shadows.OmniLightShadow; + import alternativa.engine3d.shadows.Shadow; use namespace alternativa3d; @@ -200,5 +202,14 @@ package alternativa.engine3d.lights { return res; } + /** + * @private + */ + 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); + } } } diff --git a/src/alternativa/engine3d/loaders/Parser.as b/src/alternativa/engine3d/loaders/Parser.as index d5aca3c..c3364c2 100644 --- a/src/alternativa/engine3d/loaders/Parser.as +++ b/src/alternativa/engine3d/loaders/Parser.as @@ -436,7 +436,6 @@ package alternativa.engine3d.loaders { for each(a3DDirLight in a3d.directionalLights) { var resDirLight:DirectionalLight = new DirectionalLight(a3DDirLight.color); - resDirLight.intensity = resDirLight.intensity; resDirLight.visible = a3DDirLight.visible; resDirLight.name = a3DDirLight.name; parents[resDirLight] = a3DDirLight.parentId; diff --git a/src/alternativa/engine3d/loaders/Parser3DS.as b/src/alternativa/engine3d/loaders/Parser3DS.as index d148da3..115bbea 100644 --- a/src/alternativa/engine3d/loaders/Parser3DS.as +++ b/src/alternativa/engine3d/loaders/Parser3DS.as @@ -44,7 +44,9 @@ package alternativa.engine3d.loaders { private static const CHUNK_FACESMATERIAL:int = 0x4130; private static const CHUNK_FACESSMOOTHGROUPS:int = 0x4150; private static const CHUNK_MAPPINGCOORDS:int = 0x4140; + //private static const CHUNK_OBJECTCOLOR:int = 0x4165; private static const CHUNK_TRANSFORMATION:int = 0x4160; + //private static const CHUNK_MESHANIMATION:int = 0xB002; private static const CHUNK_MATERIAL:int = 0xAFFF; private var data:ByteArray; diff --git a/src/alternativa/engine3d/loaders/ParserMaterial.as b/src/alternativa/engine3d/loaders/ParserMaterial.as index 0c12f7b..a41172a 100644 --- a/src/alternativa/engine3d/loaders/ParserMaterial.as +++ b/src/alternativa/engine3d/loaders/ParserMaterial.as @@ -81,7 +81,7 @@ package alternativa.engine3d.loaders { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, objectRenderPriority:int = -1):void { + override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void { var colorO:Object = colors[renderChannel]; var map:ExternalTextureResource; if (colorO != null) { @@ -90,14 +90,14 @@ package alternativa.engine3d.loaders { } else { fillMaterial.color = int(colorO); } - fillMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, objectRenderPriority); + fillMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, false, objectRenderPriority); } else if ((map = textures[renderChannel]) != null) { if(textureMaterial == null) { textureMaterial = new TextureMaterial(map); } else { textureMaterial.diffuseMap = map; } - textureMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, objectRenderPriority); + textureMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, false, objectRenderPriority); } } diff --git a/src/alternativa/engine3d/loaders/collada/DaeController.as b/src/alternativa/engine3d/loaders/collada/DaeController.as index c04600c..f6401da 100644 --- a/src/alternativa/engine3d/loaders/collada/DaeController.as +++ b/src/alternativa/engine3d/loaders/collada/DaeController.as @@ -138,6 +138,10 @@ package alternativa.engine3d.loaders.collada { private function transformVertices(geometry:Geometry):void { var data:ByteArray = geometry._vertexStreams[0].data; var numMappings:int = geometry._vertexStreams[0].attributes.length; + + var normalOffset:uint = geometry.getAttributeOffset(VertexAttributes.NORMAL); + var tangentOffset:uint = geometry.getAttributeOffset(VertexAttributes.TANGENT4); + for (var i:int = 0; i < geometry._numVertices; i++) { data.position = 4*numMappings*i; var x:Number = data.readFloat(); @@ -147,6 +151,26 @@ package alternativa.engine3d.loaders.collada { data.writeFloat(x*bindShapeMatrix[0] + y*bindShapeMatrix[1] + z*bindShapeMatrix[2] + bindShapeMatrix[3]); data.writeFloat(x*bindShapeMatrix[4] + y*bindShapeMatrix[5] + z*bindShapeMatrix[6] + bindShapeMatrix[7]); data.writeFloat(x*bindShapeMatrix[8] + y*bindShapeMatrix[9] + z*bindShapeMatrix[10] + bindShapeMatrix[11]); + + data.position = 4*(numMappings*i + normalOffset); + var normalX:Number = data.readFloat(); + var normalY:Number = data.readFloat(); + var normalZ:Number = data.readFloat(); + data.position -= 12; + + data.writeFloat(normalX*bindShapeMatrix[0] + normalY*bindShapeMatrix[1] + normalZ*bindShapeMatrix[2]); + data.writeFloat(normalX*bindShapeMatrix[4] + normalY*bindShapeMatrix[5] + normalZ*bindShapeMatrix[6]); + data.writeFloat(normalX*bindShapeMatrix[8] + normalY*bindShapeMatrix[9] + normalZ*bindShapeMatrix[10]); + + data.position = 4*(numMappings*i + tangentOffset); + var tangentX:Number = data.readFloat(); + var tangentY:Number = data.readFloat(); + var tangentZ:Number = data.readFloat(); + data.position -= 12; + data.writeFloat(tangentX*bindShapeMatrix[0] + tangentY*bindShapeMatrix[1] + tangentZ*bindShapeMatrix[2]); + data.writeFloat(tangentX*bindShapeMatrix[4] + tangentY*bindShapeMatrix[5] + tangentZ*bindShapeMatrix[6]); + data.writeFloat(tangentX*bindShapeMatrix[8] + tangentY*bindShapeMatrix[9] + tangentZ*bindShapeMatrix[10]); + } } @@ -399,7 +423,6 @@ package alternativa.engine3d.loaders.collada { * Auxiliary joints will be added to the end of the vector, if it's necessary. * @param parentNode Node of parent joint * @param nodes Dictionary. Key is a node of joint. And value is an index of joint in animatedJoints vector - * */ private function addJointChildren(parent:Joint, animatedJoints:Vector., parentNode:DaeNode, nodes:Dictionary):void { var object:DaeObject; @@ -462,7 +485,6 @@ package alternativa.engine3d.loaders.collada { * Returns true if joint hasn't parent joint. * @param node Joint node * @param nodes Dictionary. It items are the nodes keys. - * */ private function isRootJointNode(node:DaeNode, nodes:Dictionary):Boolean { for (var parent:DaeNode = node.parent; parent != null; parent = parent.parent) { diff --git a/src/alternativa/engine3d/loaders/collada/DaeVertices.as b/src/alternativa/engine3d/loaders/collada/DaeVertices.as index 2cc81ca..ba37e37 100644 --- a/src/alternativa/engine3d/loaders/collada/DaeVertices.as +++ b/src/alternativa/engine3d/loaders/collada/DaeVertices.as @@ -21,7 +21,7 @@ package alternativa.engine3d.loaders.collada { /** * Source of vertex coordinates data. Stores coordinates in numbers array. - *stride property of source is not less than three. + * stride property of source is not less than three. * Call parse() before using. */ public var positions:DaeSource; diff --git a/src/alternativa/engine3d/materials/EnvironmentMaterial.as b/src/alternativa/engine3d/materials/EnvironmentMaterial.as index 02c7ec8..d1e8478 100644 --- a/src/alternativa/engine3d/materials/EnvironmentMaterial.as +++ b/src/alternativa/engine3d/materials/EnvironmentMaterial.as @@ -189,6 +189,7 @@ package alternativa.engine3d.materials { crsInSpace, "mul t1.xyz, t1.xyz, i0.w", // Transpose normal matrix + // TODO: can be optimized like in StandardMaterial "mov v0.x, i0.x", "mov v0.y, t1.x", "mov v0.z, i1.x", @@ -807,7 +808,7 @@ package alternativa.engine3d.materials { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, objectRenderPriority:int = -1):void { + override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void { if (diffuseMap == null || diffuseMap._texture == null) return; if (_environmentMap == null || _environmentMap._texture == null || !(_environmentMap._texture is CubeTexture)) return; if (opacityMap != null && opacityMap._texture == null) return; diff --git a/src/alternativa/engine3d/materials/FillMaterial.as b/src/alternativa/engine3d/materials/FillMaterial.as index b8383ca..9a031bf 100644 --- a/src/alternativa/engine3d/materials/FillMaterial.as +++ b/src/alternativa/engine3d/materials/FillMaterial.as @@ -96,7 +96,7 @@ package alternativa.engine3d.materials { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, objectRenderPriority:int = -1):void { + 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; // Strams var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION); diff --git a/src/alternativa/engine3d/materials/LightMapMaterial.as b/src/alternativa/engine3d/materials/LightMapMaterial.as index e90e7f2..c48c978 100644 --- a/src/alternativa/engine3d/materials/LightMapMaterial.as +++ b/src/alternativa/engine3d/materials/LightMapMaterial.as @@ -186,7 +186,7 @@ package alternativa.engine3d.materials { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, objectRenderPriority:int = -1):void { + override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void { if (diffuseMap == null || lightMap == null || diffuseMap._texture == null || lightMap._texture == null) return; if (opacityMap != null && opacityMap._texture == null) return; diff --git a/src/alternativa/engine3d/materials/Material.as b/src/alternativa/engine3d/materials/Material.as index 6f8d74f..47a34e3 100644 --- a/src/alternativa/engine3d/materials/Material.as +++ b/src/alternativa/engine3d/materials/Material.as @@ -89,7 +89,7 @@ package alternativa.engine3d.materials { /** * @private */ - alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, objectRenderPriority:int = -1):void { + alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void { } /** diff --git a/src/alternativa/engine3d/materials/StandardMaterial.as b/src/alternativa/engine3d/materials/StandardMaterial.as index 34b5198..018a952 100644 --- a/src/alternativa/engine3d/materials/StandardMaterial.as +++ b/src/alternativa/engine3d/materials/StandardMaterial.as @@ -56,6 +56,7 @@ package alternativa.engine3d.materials { private static var caches:Dictionary = new Dictionary(true); private var cachedContext3D:Context3D; private var programsCache:Dictionary; + private var groups:Vector.> = new Vector.>(); /** * @private @@ -133,19 +134,16 @@ package alternativa.engine3d.materials { // Calculate binormal crsInSpace, "mul t1.xyz, t1.xyz, i0.w", - // Transpose normal matrix + // Транспонируем матрицу нормалей + "mov v0.xyzw, i1.xyxw", "mov v0.x, i0.x", "mov v0.y, t1.x", - "mov v0.z, i1.x", - "mov v0.w, i1.w", + "mov v1.xyzw, i1.xyyw", "mov v1.x, i0.y", "mov v1.y, t1.y", - "mov v1.z, i1.y", - "mov v1.w, i1.w", + "mov v2.xyzw, i1.xyzw", "mov v2.x, i0.z", - "mov v2.y, t1.z", - "mov v2.z, i1.z", - "mov v2.w, i1.w" + "mov v2.y, t1.z" ], "passTBNProcedure"); } @@ -427,10 +425,9 @@ package alternativa.engine3d.materials { "sat t0.x, t0.x", ]; if (useShadow) { - source.push("mul t0.x, t0.x, i2.x"); + source.push("mul t0.xw, t0.xw, i2.x"); source.push("mul t0.xyz, c1.xyz, t0.xxx"); source.push("add o0.xyz, t0.xyz, i3.xyz"); - source.push("mul t0.w, i2.x, t0.w"); source.push("mul o1.xyz, c1.xyz, t0.www"); } else { // Apply calculated values @@ -442,16 +439,89 @@ package alternativa.engine3d.materials { procedure.compileFromArray(source); } + private function formOmniProcedure(procedure:Procedure, light:Light3D, useShadow:Boolean):void { +// fragmentLinker.setInputParams(omniMulShadowProcedure, "tNormal", "tViewVector", "tTotalLight", "cAmbientColor"); + var source:Array = [ + "#c0=c" + light.lightID + "Position", + "#c1=c" + light.lightID + "Color", + "#c2=c" + light.lightID + "Radius", + "#v0=vPosition" + ]; + if (useShadow) { + // Считаем вектор из точки к свету + source.push("sub t0, c0, v0"); // L = lightPos - PointPos + source.push("dp3 t0.w, t0.xyz, t0.xyz"); // lenSqr + source.push("nrm t0.xyz, t0.xyz"); // L = normalize(L) + // Считаем half-way вектор + source.push("add t1.xyz, i1.xyz, t0.xyz"); + source.push("nrm t1.xyz, t1.xyz"); + // Считаем блик + source.push("dp3 t1.w, t1.xyz, i0.xyz"); + source.push("pow t1.w, t1.w, o1.w"); + // Считаем расстояние до источника света + source.push("sqt t1.x, t0.w"); // len = sqt(lensqr) + // Считаем свет + source.push("dp3 t0.w, t0.xyz, i0.xyz"); // dot = dot(normal, L) + // Считаем затухание + source.push("sub t0.x, t1.x, c2.z"); // len = len - atenuationBegin + source.push("div t0.y, t0.x, c2.y"); // att = len/radius + source.push("sub t0.x, c2.x, t0.y"); // att = 1 - len/radius + source.push("sat t0.xw, t0.xw"); // t = max(t, 0) + + // i3 - ambient + // i2 - shadow-test + + source.push("mul t0.xw, t0.xw, i2.xw"); + source.push("mul t0.xyz, c1.xyz, t0.xxx"); // t = color*t + source.push("mul t1.xyz, t0.xyz, t1.w"); + source.push("add o1.xyz, o1.xyz, t1.xyz"); + source.push("mul t0.xyz, t0.xyz, t0.www"); + source.push("add o0.xyz, t0.xyz, i3.xyz"); + + } else { + + // Считаем вектор из точки к свету + source.push("sub t0, c0, v0"); // L = lightPos - PointPos + source.push("dp3 t0.w, t0.xyz, t0.xyz"); // lenSqr + source.push("nrm t0.xyz, t0.xyz"); // L = normalize(L) + // Считаем half-way вектор + source.push("add t1.xyz, i1.xyz, t0.xyz"); + source.push("mov t1.w, c0.w"); + source.push("nrm t1.xyz, t1.xyz"); + // Считаем блик + source.push("dp3 t1.w, t1.xyz, i0.xyz"); + source.push("pow t1.w, t1.w, o1.w"); //!!! + // Считаем расстояние до источника света + source.push("sqt t1.x, t0.w"); // len = sqt(lensqr) + // Считаем свет + source.push("dp3 t0.w, t0.xyz, i0.xyz"); // dot = dot(normal, L) + // Считаем затухание + source.push("sub t0.x, t1.x, c2.z"); // len = len - atenuationBegin + source.push("div t0.y, t0.x, c2.y"); // att = len/radius + source.push("sub t0.x, c2.x, t0.y"); // att = 1 - len/radius + source.push("sat t0.xw, t0.xw"); // t = max(t, 0) + + // Перемножаем цвет источника с затуханием + source.push("mul t0.xyz, c1.xyz, t0.xxx"); // t = color*t + source.push("mul t1.xyz, t0.xyz, t1.w"); + source.push("add o1.xyz, o1.xyz, t1.xyz"); + source.push("mul t0.xyz, t0.xyz, t0.www"); + source.push("add o0.xyz, o0.xyz, t0.xyz"); + } + + procedure.compileFromArray(source); + } + /** * @param object * @param materialKey * @param opacityMap * @param alphaTest 0:disabled 1:alpha-test 2:contours - * @param lights + * @param lightsGroup * @param directionalLight * @param lightsLength */ - private function getProgram(object:Object3D, programs:Dictionary, camera:Camera3D, materialKey:String, opacityMap:TextureResource, alphaTest:int, lights:Vector., lightsLength:int, shadowedLight:Light3D):ShaderProgram { + private function getProgram(object:Object3D, programs:Dictionary, camera:Camera3D, materialKey:String, opacityMap:TextureResource, alphaTest:int, lightsGroup:Vector., lightsLength:int, isFirstGroup:Boolean, shadowedLight:Light3D):ShaderProgram { var key:String = materialKey + (opacityMap != null ? "O" : "o") + alphaTest.toString(); var program:ShaderProgram = programs[key]; if (program == null) { @@ -459,13 +529,34 @@ package alternativa.engine3d.materials { var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); var i:int; + // Merge program using lightsGroup + // add property useShadow + fragmentLinker.declareVariable("tTotalLight"); fragmentLinker.declareVariable("tTotalHighLight"); fragmentLinker.declareVariable("tNormal"); - fragmentLinker.declareVariable("cAmbientColor", VariableType.CONSTANT); - fragmentLinker.addProcedure(_ambientLightProcedure); - fragmentLinker.setInputParams(_ambientLightProcedure, "cAmbientColor"); - fragmentLinker.setOutputParams(_ambientLightProcedure, "tTotalLight", "tTotalHighLight"); + + if (isFirstGroup){ + fragmentLinker.declareVariable("cAmbientColor", VariableType.CONSTANT); + fragmentLinker.addProcedure(_ambientLightProcedure); + fragmentLinker.setInputParams(_ambientLightProcedure, "cAmbientColor"); + fragmentLinker.setOutputParams(_ambientLightProcedure, "tTotalLight", "tTotalHighLight"); + + if (lightMap != null) { + vertexLinker.addProcedure(_passLightMapUVProcedure); + fragmentLinker.addProcedure(_addLightMapProcedure); + fragmentLinker.setInputParams(_addLightMapProcedure, "tTotalLight"); + fragmentLinker.setOutputParams(_addLightMapProcedure, "tTotalLight"); + } + } + else{ + // сбросить tTotalLight tTotalHighLight + fragmentLinker.declareVariable("cAmbientColor", VariableType.CONSTANT); + fragmentLinker.addProcedure(_ambientLightProcedure); + fragmentLinker.setInputParams(_ambientLightProcedure, "cAmbientColor"); + fragmentLinker.setOutputParams(_ambientLightProcedure, "tTotalLight", "tTotalHighLight"); + } + var positionVar:String = "aPosition"; var normalVar:String = "aNormal"; var tangentVar:String = "aTangent"; @@ -488,7 +579,8 @@ package alternativa.engine3d.materials { fragmentLinker.addProcedure(_setGlossinessFromConstantProcedure); fragmentLinker.setOutputParams(_setGlossinessFromConstantProcedure, "tTotalHighLight"); } - if (lightsLength > 0) { + + if (lightsLength > 0 || shadowedLight) { var procedure:Procedure; if (object.deltaTransformProcedure != null) { vertexLinker.declareVariable("tTransformedNormal"); @@ -519,25 +611,44 @@ package alternativa.engine3d.materials { fragmentLinker.addProcedure(_getNormalAndViewObjectProcedure); fragmentLinker.setOutputParams(_getNormalAndViewObjectProcedure, "tNormal", "tViewVector"); } - if (shadowedLight != null && shadowedLight is DirectionalLight) { - vertexLinker.addProcedure(shadowedLight.shadow.vertexShadowProcedure, positionVar); - var shadowProc:Procedure = shadowedLight.shadow.fragmentShadowProcedure; - fragmentLinker.addProcedure(shadowProc); - fragmentLinker.setOutputParams(shadowProc, "tTotalLight"); + if (shadowedLight != null) { + var shadowProc:Procedure; + if (shadowedLight is DirectionalLight){ + vertexLinker.addProcedure(shadowedLight.shadow.vertexShadowProcedure, positionVar); + shadowProc = shadowedLight.shadow.fragmentShadowProcedure; + fragmentLinker.addProcedure(shadowProc); + fragmentLinker.setOutputParams(shadowProc, "tTotalLight"); - var dirMulShadowProcedure:Procedure = _lightFragmentProcedures[shadowedLight.shadow]; - if (dirMulShadowProcedure == null) { - dirMulShadowProcedure = new Procedure(); - formDirectionalProcedure(dirMulShadowProcedure, shadowedLight, true); + var dirMulShadowProcedure:Procedure = _lightFragmentProcedures[shadowedLight.shadow]; + if (dirMulShadowProcedure == null) { + dirMulShadowProcedure = new Procedure(); + formDirectionalProcedure(dirMulShadowProcedure, shadowedLight, true); + } + fragmentLinker.addProcedure(dirMulShadowProcedure); + fragmentLinker.setInputParams(dirMulShadowProcedure, "tNormal", "tViewVector", "tTotalLight", "cAmbientColor"); + fragmentLinker.setOutputParams(dirMulShadowProcedure, "tTotalLight", "tTotalHighLight"); + } + + if (shadowedLight is OmniLight){ + vertexLinker.addProcedure(shadowedLight.shadow.vertexShadowProcedure, positionVar); + shadowProc = shadowedLight.shadow.fragmentShadowProcedure; + fragmentLinker.addProcedure(shadowProc); + fragmentLinker.setOutputParams(shadowProc, "tTotalLight"); + + var omniMulShadowProcedure:Procedure = _lightFragmentProcedures[shadowedLight.shadow]; + if (omniMulShadowProcedure == null) { + omniMulShadowProcedure= new Procedure(); + formOmniProcedure(omniMulShadowProcedure, shadowedLight, true); + } + fragmentLinker.addProcedure(omniMulShadowProcedure); + fragmentLinker.setInputParams(omniMulShadowProcedure, "tNormal", "tViewVector", "tTotalLight", "cAmbientColor"); + fragmentLinker.setOutputParams(omniMulShadowProcedure, "tTotalLight", "tTotalHighLight"); } - fragmentLinker.addProcedure(dirMulShadowProcedure); - fragmentLinker.setInputParams(dirMulShadowProcedure, "tNormal", "tViewVector", "tTotalLight", "cAmbientColor"); - fragmentLinker.setOutputParams(dirMulShadowProcedure, "tTotalLight", "tTotalHighLight"); } for (i = 0; i < lightsLength; i++) { - var light:Light3D = lights[i]; - if (light == shadowedLight) continue; + var light:Light3D = lightsGroup[i]; + if (light == shadowedLight && (shadowedLight is DirectionalLight || shadowedLight is OmniLight)) continue; var lightFragmentProcedure:Procedure = _lightFragmentProcedures[light]; if (lightFragmentProcedure == null) { lightFragmentProcedure = new Procedure(); @@ -546,38 +657,7 @@ package alternativa.engine3d.materials { formDirectionalProcedure(lightFragmentProcedure, light, false); lightFragmentProcedure.name += "Directional"; } else if (light is OmniLight) { - lightFragmentProcedure.compileFromArray([ - "#c0=c" + light.lightID + "Position", - "#c1=c" + light.lightID + "Color", - "#c2=c" + light.lightID + "Radius", - "#v0=vPosition", - // Calculate vector from the point to light - "sub t0, c0, v0", // L = lightPos - PointPos - "dp3 t0.w, t0.xyz, t0.xyz", // lenSqr - "nrm t0.xyz, t0.xyz", // L = normalize(L) - // Calculate half-way vector - "add t1.xyz, i1.xyz, t0.xyz", - "mov t1.w, c0.w", - "nrm t1.xyz, t1.xyz", - // Calculate a flare - "dp3 t1.w, t1.xyz, i0.xyz", - "pow t1.w, t1.w, o1.w", - // Calculate distance to the light source - "sqt t1.x, t0.w", // len = sqt(lensqr) - // Calculate light - "dp3 t0.w, t0.xyz, i0.xyz", // dot = dot(normal, L) - // Calculate decay - "sub t0.x, t1.x, c2.z", // len = len - atenuationBegin - "div t0.y, t0.x, c2.y", // att = len/radius - "sub t0.x, c2.x, t0.y", // att = 1 - len/radius - "sat t0.xw, t0.xw", // t = max(t, 0) - // Multiply light color with the decay value - "mul t0.xyz, c1.xyz, t0.xxx", // t = color*t - "mul t1.xyz, t0.xyz, t1.w", - "add o1.xyz, o1.xyz, t1.xyz", - "mul t0.xyz, t0.xyz, t0.www", - "add o0.xyz, o0.xyz, t0.xyz" - ]); + formOmniProcedure(lightFragmentProcedure, light, false); lightFragmentProcedure.name += "Omni"; } else if (light is SpotLight) { lightFragmentProcedure.compileFromArray([ @@ -628,13 +708,7 @@ package alternativa.engine3d.materials { fragmentLinker.setOutputParams(_applySpecularProcedure, "tTotalHighLight"); outputProcedure = _applySpecularProcedure; } - if (lightMap != null) { - vertexLinker.addProcedure(_passLightMapUVProcedure); - fragmentLinker.addProcedure(_addLightMapProcedure); - fragmentLinker.setInputParams(_addLightMapProcedure, "tTotalLight"); - fragmentLinker.setOutputParams(_addLightMapProcedure, "tTotalLight"); - } - + fragmentLinker.declareVariable("tColor"); outputProcedure = opacityMap != null ? getDiffuseOpacityProcedure : getDiffuseProcedure; fragmentLinker.addProcedure(outputProcedure); @@ -648,37 +722,37 @@ package alternativa.engine3d.materials { fragmentLinker.addProcedure(_mulLightingProcedure, "tColor", "tTotalLight", "tTotalHighLight"); - - if (fogMode == SIMPLE || fogMode == ADVANCED) { - fragmentLinker.setOutputParams(_mulLightingProcedure, "tColor"); - } - if (fogMode == SIMPLE) { - vertexLinker.addProcedure(passSimpleFogConstProcedure); - vertexLinker.setInputParams(passSimpleFogConstProcedure, positionVar); - fragmentLinker.addProcedure(outputWithSimpleFogProcedure); - fragmentLinker.setInputParams(outputWithSimpleFogProcedure, "tColor"); - outputProcedure = outputWithSimpleFogProcedure; - } else if (fogMode == ADVANCED) { - vertexLinker.declareVariable("tProjected"); - vertexLinker.setOutputParams(_projectProcedure, "tProjected"); - vertexLinker.addProcedure(postPassAdvancedFogConstProcedure); - vertexLinker.setInputParams(postPassAdvancedFogConstProcedure, positionVar, "tProjected"); - fragmentLinker.addProcedure(outputWithAdvancedFogProcedure); - fragmentLinker.setInputParams(outputWithAdvancedFogProcedure, "tColor"); - outputProcedure = outputWithAdvancedFogProcedure; - } + +// if (fogMode == SIMPLE || fogMode == ADVANCED) { +// fragmentLinker.setOutputParams(_mulLightingProcedure, "tColor"); +// } +// if (fogMode == SIMPLE) { +// vertexLinker.addProcedure(passSimpleFogConstProcedure); +// vertexLinker.setInputParams(passSimpleFogConstProcedure, positionVar); +// fragmentLinker.addProcedure(outputWithSimpleFogProcedure); +// fragmentLinker.setInputParams(outputWithSimpleFogProcedure, "tColor"); +// outputProcedure = outputWithSimpleFogProcedure; +// } else if (fogMode == ADVANCED) { +// vertexLinker.declareVariable("tProjected"); +// vertexLinker.setOutputParams(_projectProcedure, "tProjected"); +// vertexLinker.addProcedure(postPassAdvancedFogConstProcedure); +// vertexLinker.setInputParams(postPassAdvancedFogConstProcedure, positionVar, "tProjected"); +// fragmentLinker.addProcedure(outputWithAdvancedFogProcedure); +// fragmentLinker.setInputParams(outputWithAdvancedFogProcedure, "tColor"); +// outputProcedure = outputWithAdvancedFogProcedure; +// } fragmentLinker.varyings = vertexLinker.varyings; program = new ShaderProgram(vertexLinker, fragmentLinker); - program.upload(camera.context3D); programs[key] = program; } return program; } - private function getDrawUnit(program:ShaderProgram, camera:Camera3D, surface:Surface, geometry:Geometry, opacityMap:TextureResource, lights:Vector., lightsLength:int, shadowedLight:Light3D):DrawUnit { + // TODO: return not neccessary more + private function getDrawUnit(program:ShaderProgram, camera:Camera3D, surface:Surface, geometry:Geometry, opacityMap:TextureResource, lights:Vector., lightsLength:int, isFirstGroup:Boolean, shadowedLight:Light3D, opaqueOption:Boolean, transparentOption:Boolean, objectRenderPriority:int):DrawUnit { // Buffers var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION); var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]); @@ -701,7 +775,7 @@ package alternativa.engine3d.materials { drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cSurface"), 0, glossiness, specularPower, 1); drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cThresholdAlpha"), alphaThreshold, 0, 0, alpha); - if (lightsLength > 0) { + if (lightsLength > 0 || shadowedLight) { if (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) { drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aNormal"), normalsBuffer, geometry._attributesOffsets[VertexAttributes.NORMAL], VertexAttributes.FORMATS[VertexAttributes.NORMAL]); drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aTangent"), tangentsBuffer, geometry._attributesOffsets[VertexAttributes.TANGENT4], VertexAttributes.FORMATS[VertexAttributes.TANGENT4]); @@ -750,6 +824,40 @@ package alternativa.engine3d.materials { } } + if (shadowedLight){ + var light:Light3D = shadowedLight; + if (light is DirectionalLight) { + transform = light.lightToObjectTransform; + var len:Number = Math.sqrt(transform.c*transform.c + transform.g*transform.g + transform.k*transform.k); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + light.lightID + "Direction"), -transform.c/len, -transform.g/len, -transform.k/len, 1); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + light.lightID + "Color"), light.red, light.green, light.blue); + } else if (light is OmniLight) { + var omni:OmniLight = light as OmniLight; + transform = light.lightToObjectTransform; + rScale = Math.sqrt(transform.a*transform.a + transform.e*transform.e + transform.i*transform.i); + rScale += Math.sqrt(transform.b*transform.b + transform.f*transform.f + transform.j*transform.j); + rScale += Math.sqrt(transform.c*transform.c + transform.g*transform.g + transform.k*transform.k); + rScale /= 3; + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + light.lightID + "Position"), transform.d, transform.h, transform.l); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + light.lightID + "Radius"), 1, omni.attenuationEnd*rScale - omni.attenuationBegin*rScale, omni.attenuationBegin*rScale); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + light.lightID + "Color"), light.red, light.green, light.blue); + } else if (light is SpotLight) { + var spot:SpotLight = light as SpotLight; + transform = light.lightToObjectTransform; + rScale = Math.sqrt(transform.a*transform.a + transform.e*transform.e + transform.i*transform.i); + rScale += Math.sqrt(transform.b*transform.b + transform.f*transform.f + transform.j*transform.j); + rScale += len = Math.sqrt(transform.c*transform.c + transform.g*transform.g + transform.k*transform.k); + rScale /= 3; + var falloff:Number = Math.cos(spot.falloff*0.5); + var hotspot:Number = Math.cos(spot.hotspot*0.5); + + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + light.lightID + "Position"), transform.d, transform.h, transform.l); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + light.lightID + "Axis"), -transform.c/len, -transform.g/len, -transform.k/len); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + light.lightID + "Radius"), spot.attenuationEnd*rScale - spot.attenuationBegin*rScale, spot.attenuationBegin*rScale, hotspot == falloff ? 0.000001 : hotspot - falloff, falloff); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("c" + light.lightID + "Color"), light.red, light.green, light.blue); + } + } + // Textures drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sDiffuse"), diffuseMap._texture); if (opacityMap != null) { @@ -762,69 +870,104 @@ package alternativa.engine3d.materials { drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sSpecular"), specularMap._texture); } - if (lightMap != null) { - drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aUV1"), - geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[lightMapChannel]), - geometry._attributesOffsets[VertexAttributes.TEXCOORDS[lightMapChannel]], - Context3DVertexBufferFormat.FLOAT_2); + + if (isFirstGroup){ + if (lightMap != null) { + drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aUV1"), + geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[lightMapChannel]), + geometry._attributesOffsets[VertexAttributes.TEXCOORDS[lightMapChannel]], + Context3DVertexBufferFormat.FLOAT_2); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cAmbientColor"), 0,0,0, 1); + drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sLightMap"), lightMap._texture); + } else { + drawUnit.setFragmentConstantsFromVector(program.fragmentShader.getVariableIndex("cAmbientColor"), camera.ambient, 1); + } + } + else{ drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cAmbientColor"), 0,0,0, 1); - drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sLightMap"), lightMap._texture); - } else { - drawUnit.setFragmentConstantsFromVector(program.fragmentShader.getVariableIndex("cAmbientColor"), camera.ambient, 1); } setPassUVProcedureConstants(drawUnit, program.vertexShader); - if (shadowedLight != null && shadowedLight is DirectionalLight) { + if (shadowedLight != null && ((shadowedLight is DirectionalLight)||(shadowedLight is OmniLight))) { shadowedLight.shadow.setup(drawUnit, program.vertexShader, program.fragmentShader, surface); } - if (fogMode == SIMPLE || fogMode == ADVANCED) { - var lm:Transform3D = object.localToCameraTransform; - var dist:Number = fogFar - fogNear; - drawUnit.setVertexConstantsFromNumbers(program.vertexShader.getVariableIndex("cFogSpace"), lm.i/dist, lm.j/dist, lm.k/dist, (lm.l - fogNear)/dist); - drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogRange"), fogMaxDensity, 1, 0, 1 - fogMaxDensity); - } - if (fogMode == SIMPLE) { - drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogColor"), fogColorR, fogColorG, fogColorB); - } - if (fogMode == ADVANCED) { - if (fogTexture == null) { - var bmd:BitmapData = new BitmapData(32, 1, false, 0xFF0000); - for (i = 0; i < 32; i++) { - bmd.setPixel(i, 0, ((i/32)*255) << 16); - } - fogTexture = new BitmapTextureResource(bmd); - fogTexture.upload(camera.context3D); - } - var cLocal:Transform3D = camera.localToGlobalTransform; - var halfW:Number = camera.view.width/2; - var leftX:Number = -halfW*cLocal.a + camera.focalLength*cLocal.c; - var leftY:Number = -halfW*cLocal.e + camera.focalLength*cLocal.g; - var rightX:Number = halfW*cLocal.a + camera.focalLength*cLocal.c; - var rightY:Number = halfW*cLocal.e + camera.focalLength*cLocal.g; - // Finding UV - var angle:Number = (Math.atan2(leftY, leftX) - Math.PI/2); - if (angle < 0) angle += Math.PI*2; - var dx:Number = rightX - leftX; - var dy:Number = rightY - leftY; - var lens:Number = Math.sqrt(dx*dx + dy*dy); - leftX /= lens; - leftY /= lens; - rightX /= lens; - rightY /= lens; - var uScale:Number = Math.acos(leftX*rightX + leftY*rightY)/Math.PI/2; - var uRight:Number = angle/Math.PI/2; - drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogConsts"), 0.5*uScale, 0.5 - uRight, 0); - drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sFogTexture"), fogTexture._texture); + // Inititalizing render properties + if (opaqueOption) + // Use z-buffer within DrawCall, draws without blending + if (isFirstGroup){ + drawUnit.blendSource = Context3DBlendFactor.ONE; + drawUnit.blendDestination = Context3DBlendFactor.ZERO; + camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE); + } + else{ + drawUnit.blendSource = Context3DBlendFactor.ONE; + drawUnit.blendDestination = Context3DBlendFactor.ONE; + camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE_OVERHEAD); + } + + if (transparentOption){ + // Do not use z-buffer, draws with blending + if (isFirstGroup){ + drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA; + drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA; + } + else{ + drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA; + drawUnit.blendDestination = Context3DBlendFactor.ONE; + } + camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT); } + + +// if (fogMode == SIMPLE || fogMode == ADVANCED) { +// var lm:Transform3D = object.localToCameraTransform; +// var dist:Number = fogFar - fogNear; +// drawUnit.setVertexConstantsFromNumbers(program.vertexShader.getVariableIndex("cFogSpace"), lm.i/dist, lm.j/dist, lm.k/dist, (lm.l - fogNear)/dist); +// drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogRange"), fogMaxDensity, 1, 0, 1 - fogMaxDensity); +// } +// if (fogMode == SIMPLE) { +// drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogColor"), fogColorR, fogColorG, fogColorB); +// } +// if (fogMode == ADVANCED) { +// if (fogTexture == null) { +// var bmd:BitmapData = new BitmapData(32, 1, false, 0xFF0000); +// for (i = 0; i < 32; i++) { +// bmd.setPixel(i, 0, ((i/32)*255) << 16); +// } +// fogTexture = new BitmapTextureResource(bmd); +// fogTexture.upload(camera.context3D); +// } +// var cLocal:Transform3D = camera.localToGlobalTransform; +// var halfW:Number = camera.view.width/2; +// var leftX:Number = -halfW*cLocal.a + camera.focalLength*cLocal.c; +// var leftY:Number = -halfW*cLocal.e + camera.focalLength*cLocal.g; +// var rightX:Number = halfW*cLocal.a + camera.focalLength*cLocal.c; +// var rightY:Number = halfW*cLocal.e + camera.focalLength*cLocal.g; +// // Finding UV +// var angle:Number = (Math.atan2(leftY, leftX) - Math.PI/2); +// if (angle < 0) angle += Math.PI*2; +// var dx:Number = rightX - leftX; +// var dy:Number = rightY - leftY; +// var lens:Number = Math.sqrt(dx*dx + dy*dy); +// leftX /= lens; +// leftY /= lens; +// rightX /= lens; +// rightY /= lens; +// var uScale:Number = Math.acos(leftX*rightX + leftY*rightY)/Math.PI/2; +// var uRight:Number = angle/Math.PI/2; +// +// drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogConsts"), 0.5*uScale, 0.5 - uRight, 0); +// drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sFogTexture"), fogTexture._texture); +// } return drawUnit; } /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, objectRenderPriority:int = -1):void { + override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void { if (diffuseMap == null || normalMap == null || diffuseMap._texture == null || normalMap._texture == null) return; // Check if textures uploaded in to the context. if (opacityMap != null && opacityMap._texture == null || glossinessMap != null && glossinessMap._texture == null || specularMap != null && specularMap._texture == null || lightMap != null && lightMap._texture == null) return; @@ -839,26 +982,12 @@ package alternativa.engine3d.materials { if (positionBuffer == null || uvBuffer == null) return; + var i:int; + if (lightsLength > 0 && (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED)) { if (normalsBuffer == null || tangentsBuffer == null) return; } - // Make shared part of the key. - var materialKey:String = (fogMode.toString()) + - ((lightMap != null) ? "L" : "l") + - (_normalMapSpace.toString()) + - ((glossinessMap != null) ? "G" : "g") + - ((specularMap != null) ? "S" : "s"); - var shadowedLight:Light3D; - for (var i:int = 0; i < lightsLength; i++) { - var light:Light3D = lights[i]; - if (light.shadow != null && shadowedLight == null) { - shadowedLight = light; - materialKey += light.shadow.type; - } - materialKey += light.lightID; - } - // Refresh programs for this context. if (camera.context3D != cachedContext3D) { cachedContext3D = camera.context3D; @@ -875,40 +1004,170 @@ package alternativa.engine3d.materials { programsCache[object.transformProcedure] = optionsPrograms; } + + // Form groups + var groupsCount:int = groups.length = 0; + var firstGroup:Vector. = new Vector.(); + var shadowGroup:Vector. = new Vector.(); + var firstGroupLength:int = 0; + for (i = 0; i < lightsLength; i++) { + var light:Light3D = lights[i]; + if (light.shadow!=null && useShadow){ + shadowGroup.push(light); + } + else{ + if (firstGroupLength==6){ + groups[groupsCount++] = firstGroup; + firstGroup = new Vector.(); + firstGroupLength = 0; + } + firstGroup[firstGroupLength++] = light; + } + } + if (firstGroupLength!=0){ + groups[groupsCount++] = firstGroup; + } + var shadowGroupLength:int = shadowGroup.length; + + // Iterate groups + var materialKey:String; var program:ShaderProgram; var drawUnit:DrawUnit; - // Opaque pass - if (opaquePass && alphaThreshold <= alpha) { - if (alphaThreshold > 0) { - // Alpha test + + if (groupsCount==0 && shadowGroupLength==0){ + // There is only Ambient light on the scene + // Form key + materialKey = (lightMap != null) ? "L" : "l"+ + ((glossinessMap != null) ? "G" : "g") + + ((specularMap != null) ? "S" : "s"); + + if (opaquePass && alphaThreshold <= alpha) { + if (alphaThreshold > 0) { + // Alpha test + // use opacityMap if it is presented + program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 1, null, 0, true, null); + drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, null, 0, true, null, true, false, objectRenderPriority); + } else { + // do not use opacityMap at all + program = getProgram(object, optionsPrograms, camera, materialKey, null, 0, null, 0, true, null); + drawUnit = getDrawUnit(program, camera, surface, geometry, null, null, 0, true, null, true, false, objectRenderPriority); + } + } + // Transparent pass + if (transparentPass && alphaThreshold > 0 && alpha > 0) { // use opacityMap if it is presented - program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 1, lights, lightsLength, shadowedLight); - drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, lights, lightsLength, shadowedLight); - } else { - // do not use opacityMap at all - program = getProgram(object, optionsPrograms, camera, materialKey, null, 0, lights, lightsLength, shadowedLight); - drawUnit = getDrawUnit(program, camera, surface, geometry, null, lights, lightsLength, shadowedLight); + if (alphaThreshold <= alpha && !opaquePass) { + // Alpha threshold + program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 2, null, 0, true, null); + drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, null, 0, true, null, false, true, objectRenderPriority); + } else { + // There is no Alpha threshold or check z-buffer by previous pass + program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 0, null, 0, true, null); + drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, null, 0, true, null, false, true, objectRenderPriority); + } } - // Use z-buffer within DrawCall, draws without blending - camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE); } - // Transparent pass - if (transparentPass && alphaThreshold > 0 && alpha > 0) { - // use opacityMap if it is presented - if (alphaThreshold <= alpha && !opaquePass) { - // Alpha threshold - program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 2, lights, lightsLength, shadowedLight); - drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, lights, lightsLength, shadowedLight); - } else { - // There is no Alpha threshold or check z-buffer by previous pass - program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 0, lights, lightsLength, shadowedLight); - drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, lights, lightsLength, shadowedLight); + else{ + var lightLengthInGroup:int; + var isFirstGroup:Boolean = true; + for (i = 0; i < groupsCount; i++) { + var lightGroup:Vector. = groups[i]; + lightLengthInGroup = lightGroup.length; + + // Group of lights without shadow + // Form key + materialKey = (isFirstGroup)?((lightMap != null) ? "L" : "l"):""; + materialKey += + (_normalMapSpace.toString()) + + ((glossinessMap != null) ? "G" : "g") + + ((specularMap != null) ? "S" : "s"); + for (var j:int = 0; j < lightLengthInGroup; j++) { + var light:Light3D = lightGroup[j]; + materialKey += light.lightID; + } + + // Create program and drawUnit for group + // Opaque pass + if (opaquePass && alphaThreshold <= alpha) { + if (alphaThreshold > 0) { + // Alpha test + // use opacityMap if it is presented + program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 1, lightGroup, lightLengthInGroup, isFirstGroup, null); + drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, lightGroup, lightLengthInGroup, isFirstGroup, null, true, false, objectRenderPriority); + } else { + // do not use opacityMap at all + program = getProgram(object, optionsPrograms, camera, materialKey, null, 0, lightGroup, lightLengthInGroup, isFirstGroup, null); + drawUnit = getDrawUnit(program, camera, surface, geometry, null, lightGroup, lightLengthInGroup, isFirstGroup, null, true, false, objectRenderPriority); + } + } + // Transparent pass + if (transparentPass && alphaThreshold > 0 && alpha > 0) { + // use opacityMap if it is presented + if (alphaThreshold <= alpha && !opaquePass) { + // Alpha threshold + program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 2, lightGroup, lightLengthInGroup, isFirstGroup, null); + drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, lightGroup, lightLengthInGroup, isFirstGroup, null, false, true, objectRenderPriority); + } else { + // There is no Alpha threshold or check z-buffer by previous pass + program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 0, lightGroup, lightLengthInGroup, isFirstGroup, null); + drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, lightGroup, lightLengthInGroup, isFirstGroup, null, false, true, objectRenderPriority); + } + } + isFirstGroup = false; + lightGroup.length = 0; } - // Do not use z-buffer, draws with blending - drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA; - drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA; - camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT); + + if (shadowGroupLength>0){ + // Group of ligths with shadow + // For each light we will create new drawUnit + for (var j:int = 0; j < shadowGroupLength; j++) { + + var light:Light3D = shadowGroup[j]; + // Form key + materialKey = (isFirstGroup)?((lightMap != null) ? "L" : "l"):""; + materialKey += + (_normalMapSpace.toString()) + + ((glossinessMap != null) ? "G" : "g") + + ((specularMap != null) ? "S" : "s"); + materialKey += light.shadow.type; + materialKey += light.lightID; + + // Для группы создаем программу и дроуюнит + // Opaque pass + if (opaquePass && alphaThreshold <= alpha) { + if (alphaThreshold > 0) { + // Alpha test + // use opacityMap if it is presented + program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 1, null, 0, isFirstGroup, light); + drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, null, 0, isFirstGroup, light, true, false, objectRenderPriority); + } else { + // do not use opacityMap at all + program = getProgram(object, optionsPrograms, camera, materialKey, null, 0, null, 0, isFirstGroup, light); + drawUnit = getDrawUnit(program, camera, surface, geometry, null, null, 0, isFirstGroup, light, true, false, objectRenderPriority); + } + trace(program.vertexShader.describeLinkageInfo()); + trace(program.fragmentShader.describeLinkageInfo()); + + } + // Transparent pass + if (transparentPass && alphaThreshold > 0 && alpha > 0) { + // use opacityMap if it is presented + if (alphaThreshold <= alpha && !opaquePass) { + // Alpha threshold + program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 2, null, 0, isFirstGroup, light); + drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, null, 0, isFirstGroup, light, false, true, objectRenderPriority); + } else { + // There is no Alpha threshold or check z-buffer by previous pass + program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 0, null, 0, isFirstGroup, light); + drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap, null, 0, isFirstGroup, light, false, true, objectRenderPriority); + } + } + isFirstGroup = false; + } + } + shadowGroup.length = 0; } + groups.length = 0; } /** diff --git a/src/alternativa/engine3d/materials/TextureMaterial.as b/src/alternativa/engine3d/materials/TextureMaterial.as index 012ca27..e8f1805 100644 --- a/src/alternativa/engine3d/materials/TextureMaterial.as +++ b/src/alternativa/engine3d/materials/TextureMaterial.as @@ -243,7 +243,7 @@ package alternativa.engine3d.materials { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, objectRenderPriority:int = -1):void { + 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; // Buffers diff --git a/src/alternativa/engine3d/materials/VertexLightTextureMaterial.as b/src/alternativa/engine3d/materials/VertexLightTextureMaterial.as index 477b950..d75a601 100644 --- a/src/alternativa/engine3d/materials/VertexLightTextureMaterial.as +++ b/src/alternativa/engine3d/materials/VertexLightTextureMaterial.as @@ -279,7 +279,7 @@ package alternativa.engine3d.materials { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, objectRenderPriority:int = -1):void { + override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void { if (diffuseMap == null || diffuseMap._texture == null || opacityMap != null && opacityMap._texture == null) return; var object:Object3D = surface.object; diff --git a/src/alternativa/engine3d/objects/AxisAlignedSprite.as b/src/alternativa/engine3d/objects/AxisAlignedSprite.as index 44cdbdc..52f046b 100644 --- a/src/alternativa/engine3d/objects/AxisAlignedSprite.as +++ b/src/alternativa/engine3d/objects/AxisAlignedSprite.as @@ -151,9 +151,9 @@ package alternativa.engine3d.objects { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { var geometry:Geometry = getGeometry(camera.context3D); - if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength); + if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, -1); // Mouse events if (listening) camera.view.addSurfaceToMouseEvents(surface, geometry, transformProcedure); } diff --git a/src/alternativa/engine3d/objects/Decal.as b/src/alternativa/engine3d/objects/Decal.as index db0745e..baa8832 100644 --- a/src/alternativa/engine3d/objects/Decal.as +++ b/src/alternativa/engine3d/objects/Decal.as @@ -71,10 +71,10 @@ package alternativa.engine3d.objects { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { for (var i:int = 0; i < _surfacesLength; i++) { var surface:Surface = _surfaces[i]; - if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength, Renderer.DECALS); + if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, Renderer.DECALS); // Mouse events if (listening) camera.view.addSurfaceToMouseEvents(surface, geometry, transformProcedure); } diff --git a/src/alternativa/engine3d/objects/LOD.as b/src/alternativa/engine3d/objects/LOD.as index ea48ef5..7f0bcac 100644 --- a/src/alternativa/engine3d/objects/LOD.as +++ b/src/alternativa/engine3d/objects/LOD.as @@ -172,11 +172,11 @@ package alternativa.engine3d.objects { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { var distance:Number = Math.sqrt(localToCameraTransform.d*localToCameraTransform.d + localToCameraTransform.h*localToCameraTransform.h + localToCameraTransform.l*localToCameraTransform.l); for (var level:Object3D = levelList; level != null; level = level.next) { if (distance <= level.distance) { - collectChildDraws(level, this, camera, lights, lightsLength); + collectChildDraws(level, this, camera, lights, lightsLength, useShadow); break; } } @@ -185,7 +185,7 @@ package alternativa.engine3d.objects { /** * @private */ - alternativa3d function collectChildDraws(child:Object3D, parent:Object3D, camera:Camera3D, lights:Vector., lightsLength:int):void { + alternativa3d function collectChildDraws(child:Object3D, parent:Object3D, camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { // Composing direct and reverse matrices if (child.transformChanged) child.composeTransforms(); // Calculation of transfer matrix from camera to local space. @@ -198,17 +198,25 @@ package alternativa.engine3d.objects { // If object needs on light sources. if (lightsLength > 0 && child.useLights) { // Calculation of transfer matrices from sources to object. + var excludedLightLength:int = excludedLights.length; + var childLightsLength:int = 0; for (var i:int = 0; i < lightsLength; i++) { var light:Light3D = lights[i]; + var j:int = 0; + while (j, lightsLength:int):void { + override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { for (var i:int = 0; i < _surfacesLength; i++) { var surface:Surface = _surfaces[i]; - if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength); + if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, -1); // Mouse events if (listening) camera.view.addSurfaceToMouseEvents(surface, geometry, transformProcedure); } diff --git a/src/alternativa/engine3d/objects/MeshSet.as b/src/alternativa/engine3d/objects/MeshSet.as index bad4766..1eb604f 100644 --- a/src/alternativa/engine3d/objects/MeshSet.as +++ b/src/alternativa/engine3d/objects/MeshSet.as @@ -73,14 +73,14 @@ package alternativa.engine3d.objects { } } - override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { if (geometry == null) return; // Calculation of joints matrices. for (var i:int = 0; i < _surfacesLength; i++) { var surface:Surface = _surfaces[i]; transformProcedure = surfaceTransformProcedures[i]; deltaTransformProcedure = surfaceDeltaTransformProcedures[i]; - if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength); + if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow); // Mouse events if (listening) camera.view.addSurfaceToMouseEvents(surface, geometry, transformProcedure); } diff --git a/src/alternativa/engine3d/objects/Skin.as b/src/alternativa/engine3d/objects/Skin.as index 268e286..df4972b 100644 --- a/src/alternativa/engine3d/objects/Skin.as +++ b/src/alternativa/engine3d/objects/Skin.as @@ -565,7 +565,7 @@ package alternativa.engine3d.objects { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { if (geometry == null) return; // Calculate joints matrices for (var child:Object3D = childrenList; child != null; child = child.next) { @@ -582,7 +582,7 @@ package alternativa.engine3d.objects { var surface:Surface = _surfaces[i]; transformProcedure = surfaceTransformProcedures[i]; deltaTransformProcedure = surfaceDeltaTransformProcedures[i]; - if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength); + if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow); /*var destination:DrawUnit = surface.getDrawUnit(camera, geometry, lights, lightsLength); if (destination == null) continue; diff --git a/src/alternativa/engine3d/objects/SkyBox.as b/src/alternativa/engine3d/objects/SkyBox.as index 14c899e..e047a1e 100644 --- a/src/alternativa/engine3d/objects/SkyBox.as +++ b/src/alternativa/engine3d/objects/SkyBox.as @@ -196,10 +196,10 @@ package alternativa.engine3d.objects { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { for (var i:int = 0; i < _surfacesLength; i++) { var surface:Surface = _surfaces[i]; - if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength, Renderer.SKY); + if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, Renderer.SKY); //Mouse events if (listening) camera.view.addSurfaceToMouseEvents(surface, geometry, transformProcedure); } diff --git a/src/alternativa/engine3d/objects/Sprite3D.as b/src/alternativa/engine3d/objects/Sprite3D.as index c10a870..4397f70 100644 --- a/src/alternativa/engine3d/objects/Sprite3D.as +++ b/src/alternativa/engine3d/objects/Sprite3D.as @@ -182,9 +182,9 @@ package alternativa.engine3d.objects { /** * @private */ - override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + override alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { var geometry:Geometry = getGeometry(camera.context3D); - if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength, alwaysOnTop ? Renderer.NEXT_LAYER : -1); + if (surface.material != null) surface.material.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, alwaysOnTop ? Renderer.NEXT_LAYER : -1); // Mouse events. if (listening) camera.view.addSurfaceToMouseEvents(surface, geometry, transformProcedure); } diff --git a/src/alternativa/engine3d/objects/WireFrame.as b/src/alternativa/engine3d/objects/WireFrame.as index 22763ec..b19da9f 100644 --- a/src/alternativa/engine3d/objects/WireFrame.as +++ b/src/alternativa/engine3d/objects/WireFrame.as @@ -171,7 +171,7 @@ package alternativa.engine3d.objects { /** * @private */ - alternativa3d override function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int):void { + alternativa3d override function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { if (camera.context3D != cachedContext3D) { cachedContext3D = camera.context3D; shaderProgram = cachedPrograms[cachedContext3D]; diff --git a/src/alternativa/engine3d/shadows/DirectionalLightShadow.as b/src/alternativa/engine3d/shadows/DirectionalLightShadow.as index 78ebeb4..dc76fcf 100644 --- a/src/alternativa/engine3d/shadows/DirectionalLightShadow.as +++ b/src/alternativa/engine3d/shadows/DirectionalLightShadow.as @@ -5,7 +5,6 @@ * * 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; @@ -60,8 +59,6 @@ package alternativa.engine3d.shadows { /** * Debug mode. */ - public var debug:Boolean = false; - /** * Degree of correcting offset of shadow map space. It need for getting rid of self-shadowing artifacts. */ @@ -164,6 +161,7 @@ package alternativa.engine3d.shadows { * Enable/disable automatic calculation of shadow zone parameters on specified bound-box at shadowBoundBox property. */ public var calculateParametersByVolume:Boolean = false; + public var volume:BoundBox = null; // TODO: implement special shader for display of shadowmap in debug (black-and-white). @@ -217,7 +215,7 @@ package alternativa.engine3d.shadows { this._mapSize = mapSize; this._pcfOffset = pcfOffset; - this.type = _pcfOffset > 0 ? "S" : "s"; + this.type = _pcfOffset > 0 ? "DS" : "ds"; vertexShadowProcedure = getVShader(); fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); @@ -381,12 +379,12 @@ package alternativa.engine3d.shadows { // Draw var debugSurface:Surface = debugPlane._surfaces[0]; - debugSurface.material.collectDraws(camera, debugSurface, debugPlane.geometry, emptyLightVector, 0, -1); + debugSurface.material.collectDraws(camera, debugSurface, debugPlane.geometry, emptyLightVector, 0, false, -1); // Form transformation matrix for debugPlane debugPlane.transform.compose((frustumMinX + frustumMaxX) / 2, (frustumMinY + frustumMaxY) / 2, frustumMaxZ, 0, 0, 0, (frustumMaxX - frustumMinX), (frustumMaxY - frustumMinY), 1); debugPlane.localToCameraTransform.combine(_light.localToCameraTransform, debugPlane.transform); - debugSurface.material.collectDraws(camera, debugSurface, debugPlane.geometry, emptyLightVector, 0, -1); + debugSurface.material.collectDraws(camera, debugSurface, debugPlane.geometry, emptyLightVector, 0, false, -1); } tempBounds.minX = frustumMinX; @@ -567,7 +565,6 @@ package alternativa.engine3d.shadows { fLinker.addProcedure(Procedure.compileFromArray([ "#v0=vDistance", "#c0=cConstants", - "mov t0.xy, v0.zz", "frc t0.y, v0.z", "sub t0.x, v0.z, t0.y", "mul t0.x, t0.x, c0.x", @@ -711,6 +708,28 @@ package alternativa.engine3d.shadows { //------------- ShadowMap Shader ---------- + /** + * @private + */ + alternativa3d override function setup(drawUnit:DrawUnit, vertexLinker:Linker, fragmentLinker:Linker, surface:Surface):void { + // Устанавливаем матрицу перевода в шедоумапу + objectToShadowMapTransform.combine(cameraToShadowMapUVProjection, surface.object.localToCameraTransform); + + drawUnit.setVertexConstantsFromTransform(vertexLinker.getVariableIndex("cUVProjection"), objectToShadowMapTransform); + // Устанавливаем шедоумапу + drawUnit.setTextureAt(fragmentLinker.getVariableIndex("sShadowMap"), shadowMap); + // TODO: сделать множитель более корректный. Возможно 65536 (разрешающая способность глубины буфера). + // Устанавливаем коеффициенты + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -255*10000, -10000, biasMultiplier*255*10000, 1/16); + if (_pcfOffset > 0) { + var offset1:Number = _pcfOffset/_mapSize; + var offset2:Number = offset1/3; + + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cPCFOffsets"), -offset1, -offset2, offset2, offset1); + } + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cDist"), 0.9999, 10000, 1); + } + private static function getVShader():Procedure { var shader:Procedure = Procedure.compileFromArray([ "#v0=vSample", @@ -737,6 +756,7 @@ package alternativa.engine3d.shadows { // Clipping by distance. shaderArr[line++] = "sub t0.y, c1.x, t0.z"; // maxDist - z shaderArr[line++] = "mul t0.y, t0.y, c1.y"; // mul 10000 + shaderArr[line++] = "sat t0.xy, t0.xy"; shaderArr[line++] = "mul t0.x, t0.x, t0.y"; shaderArr[line++] = "sub o0, c1.z, t0.x"; @@ -790,28 +810,6 @@ package alternativa.engine3d.shadows { return Procedure.compileFromArray(shaderArr, "DirectionalShadowMapFragment"); } - /** - * @private - */ - alternativa3d override function setup(drawUnit:DrawUnit, vertexLinker:Linker, fragmentLinker:Linker, surface:Surface):void { - // Set transfer matrix to shadowmap. - objectToShadowMapTransform.combine(cameraToShadowMapUVProjection, surface.object.localToCameraTransform); - - drawUnit.setVertexConstantsFromTransform(vertexLinker.getVariableIndex("cUVProjection"), objectToShadowMapTransform); - // Set shadowmap. - drawUnit.setTextureAt(fragmentLinker.getVariableIndex("sShadowMap"), shadowMap); - // TODO: set multiplier more correct. It is possible that 65536 (resolution of the buffer depth). - // Set coefficients. - drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -255*10000, -10000, biasMultiplier*255*10000, 1/16); - if (_pcfOffset > 0) { - var offset1:Number = _pcfOffset/_mapSize; - var offset2:Number = offset1/3; - - drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cPCFOffsets"), -offset1, -offset2, offset2, offset1); - } - drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cDist"), 0.9999, 10000, 1); - } - /** * Adds given object to list of objects, that cast shadow. * @param object Added object. diff --git a/src/alternativa/engine3d/shadows/OmniLightShadow.as b/src/alternativa/engine3d/shadows/OmniLightShadow.as new file mode 100644 index 0000000..caf9a0f --- /dev/null +++ b/src/alternativa/engine3d/shadows/OmniLightShadow.as @@ -0,0 +1,903 @@ +/** + * 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.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.core.View; + 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{ + + /** + * Степень корректирующего смещения пространства карты теней для избавления от артефактов самозатенения. + */ + public var biasMultiplier:Number = 0.99; + + private var renderer:Renderer = new Renderer(); + + private var boundSize:Number = 1; + private var _mapSize:Number; + private var _pcfOffset:Number; + + private var cubeShadowMap:CubeTexture; + private var cameras:Vector. = new Vector.(); + + private var debugObject:Mesh; + private var debugMaterial:ShadowDebugMaterial; + + private var _casters:Vector. = new Vector.(); + + private var cachedContext:Context3D; + private var programs:Dictionary = new Dictionary(); + + private var actualCasters:Vector. = new Vector.(); + private var actualCastersCount:int; + + private var edgeCameraToCasterTransform:Transform3D = new Transform3D(); + private var casterToEdgedCameraTransform:Transform3D = new Transform3D(); + private var objectToLightTransform:Transform3D = new Transform3D(); + + private var prevActualCasterCountForEdge:Vector. = new Vector.(6); + + /** + * Создает экземпляр OmniLightShadow. + * @param mapSize Размер карты теней. Должен быть степенью 2. + * @param pcfOffset Смягчение границ тени. + */ + public function OmniLightShadow(mapSize:int = 128, pcfOffset:Number = 0) { + + this.mapSize = mapSize; + this.pcfOffset = pcfOffset; + vertexShadowProcedure = getVShader(); + type = _pcfOffset > 0 ? "OS" : "os"; + fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); + + debugMaterial = new ShadowDebugMaterial(); + debugMaterial.alpha = 1.0; + + for (var i:int = 0; i < 6; i++) { + // создаем камеры + var cam:Camera3D = new Camera3D(1, boundSize); + cam.fov = 1.910633237; + cam.view = new View(boundSize, boundSize); + cam.renderer = renderer; + cameras[i] = cam; + + prevActualCasterCountForEdge[i] = 0; + } + + // 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 + */ + alternativa3d function setBoundSize(value:Number):void{ + this.boundSize = value; + for (var i:int = 0; i < 6; i++) { + var cam:Camera3D = cameras[i]; + cam.view.width = cam.view.height = int (value); + cam.farClipping = value; + cam.calculateProjection(value,value); + } + } + + private function createDebugCube(material:Material, context:Context3D):Mesh{ + var mesh:Mesh = new Mesh(); + // TODO: определиться куб или сфера +// var geometry: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.([-0.5, -0.5, -0.5, +// 0.5, -0.5, -0.5, +// 0.5, 0.5, -0.5, +// -0.5, 0.5, -0.5, +// -0.5, -0.5, 0.5, +// 0.5, -0.5, 0.5, +// 0.5, 0.5, 0.5, +// -0.5, 0.5, 0.5])); +// +// 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); + var sphere:GeoSphere = new GeoSphere(1, 4, false); + var geometry:Geometry = sphere.geometry; + mesh.geometry = geometry; + mesh.addSurface(material, 0, geometry.numTriangles); + + geometry.upload(context); + + return mesh; + } + + + // Вычисление шедоумапы + override alternativa3d function process(camera:Camera3D):void { + var i:int; + var j:int; + var caster:Object3D; + var context:Context3D = camera.context3D; + var castersCount:int = _casters.length; + // Отсечение кастеров, тени которых не видны + + // Обработка смены контекста + if (context != cachedContext) { + programs = new Dictionary(); + cubeShadowMap = null; + cachedContext = context; + for (i = 0; i < cameras.length; i++) { + cameras[i].context3D = cachedContext; + } + } + + if (cubeShadowMap == null) { + cubeShadowMap = context.createCubeTexture(_mapSize, Context3DTextureFormat.BGRA, true); + debugMaterial.cubeMap = cubeShadowMap; + for (i = 0; i < 6; i++) { + context.setRenderToTexture(cubeShadowMap, true, 0, i); + context.clear(1, 0, 0, 0.3); + } + } + + // предрасчитаем некоторые матрицы трансформации + for (j = 0; j < castersCount; j++) { + caster = _casters[j]; + + 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) { + // Расчет матриц джоинтов + for (var child:Object3D = skin.childrenList; child != null; child = child.next) { + if (child.transformChanged) child.composeTransforms(); + // Записываем в localToGlobalTransform матрицу перевода в скин + child.localToGlobalTransform.copy(child.transform); + if (child is Joint) { + Joint(child).calculateTransform(); + } + skin.calculateJointsTransforms(child); + } + } + else{ + if (caster.childrenList) + calculateChildrenTransforms(caster); + } + } + + + // Пробегаемся по 6-и камерам + for (i = 0; i < 6; i++) { + // камера соответствующая грани куба + var edgeCamera:Camera3D = cameras[i]; + + // проверяем, есть ли видимые кастеры попадающие на грань куба + actualCastersCount = 0; + 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); + } + } + + if (actualCastersCount>0){ + // Настройка параметров рендеринга: + renderer.camera = edgeCamera; + context.setRenderToTexture(cubeShadowMap, true, 0, i); + context.clear(1, 0, 0, 0.0); + + // Пробегаемся по кастерам + for (j = 0; j 0) { + // Создаем дебаговый объект, если он не создан + if (debugObject == null) { + debugObject = createDebugCube(debugMaterial, camera.context3D); + debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = boundSize/12; + 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); + } + } + } + + // предрасчитывает матрицы для всех детей + // localToLightTransform, lightToLocalTransform, transform, и calculateTransform для Joint + private function calculateChildrenTransforms(root:Object3D):void{ + var childrenList:Object3D = root.childrenList; + + for (var child:Object3D = childrenList; child != null; child = child.next) { + + // расчет матриц трансформаций для объектов + if (child.transformChanged) child.composeTransforms(); + child.localToLightTransform.combine(root.localToLightTransform, child.transform); + child.lightToLocalTransform.combine(child.inverseTransform, root.lightToLocalTransform); + + // расчет матриц трансформаций для скинов + var skin:Skin = child as Skin; + if (skin != null) { + // Расчет матриц джоинтов + for (var skinChild:Object3D = skin.childrenList; skinChild != null; skinChild = skinChild.next) { + if (skinChild.transformChanged) skinChild.composeTransforms(); + // Записываем в localToGlobalTransform матрицу перевода в скин + skinChild.localToGlobalTransform.copy(skinChild.transform); + if (skinChild is Joint) { + Joint(skinChild).calculateTransform(); + } + skin.calculateJointsTransforms(skinChild); + } + } + else{ + if (child.childrenList) + calculateChildrenTransforms(child); + } + } + } + + // собирает список actualCasters для одной из 6-и камер + private function calculateVisibility(root:Object3D, camera:Camera3D):void{ + var casterCulling:int; + + if (root.visible) { + var skin:Skin = root as Skin; + + // Вычисляем результат кулинга для объекта + 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){ + if (skin){ + actualCasters[actualCastersCount++] = root; + } + else{ + var childrenList:Object3D = root.childrenList; + // Если есть дочерние объекты, + if(childrenList!=null){ + // Проверяем их на кулинг + for (var child:Object3D = childrenList; child != null; child = child.next) { + calculateVisibility(child, camera); + } + } + // Если дочерних объектов нет + else{ + // добавляем кастер в список актуальных кастеров + actualCasters[actualCastersCount++] = root; + } + } + } + } + } + + + 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.setVertexConstantsFromNumbers(program.vertexShader.getVariableIndex("cScale"), 255/boundSize, 0, 0, 1); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cConstants"), 1 / 255, 0, 0, 1); + + renderer.addDrawUnit(drawUnit, Renderer.OPAQUE); + } + } + + var child:Object3D; + for (child = caster.childrenList; child != null; child = child.next) { + if (!(child as Joint) && child.visible) collectDraws(context, child, edgeCamera); + } + } + + + /** + * @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([ + "#c1=cScale", + "#v0=vDistance", + + "m34 t0.xyz, i0, c2", + "dp3 t0.x, t0.xyz, t0.xyz", + "sqt t0.x, t0.x", // x: [0, boundSize] + "mul t0.x, t0.x, c1.x", // x: [0, 255] + "mov t0.w, c1.w", + "mov v0, t0", + + "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", // x: [0, 255] + "#c0=cConstants", // 1/255, 0, 0, 1 + "frc t0.y, v0.x", + "sub t0.x, v0.x, t0.y", + "mul t0.x, t0.x, c0.x", + "mov t0.zw, c0.zw", + + "mov o0, t0" + ])); + program = new ShaderProgram(vLinker, fLinker); + fLinker.varyings = vLinker.varyings; + programListByTransformProcedure[key] = program; + program.upload(context); + + } + return program; + } + + + + //------------- ShadowMap Shader ---------- + + /** + * @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); + + // Устанавливаем коеффициенты + // TODO: сделать множитель более корректный. Возможно 65536 (разрешающая способность глубины буфера). + if (_pcfOffset > 0) { + + var offset:Number = _pcfOffset*0.0175; //1 градус + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cPCFOffsets"), -3/2, 1/16, 0, 0); + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -1, 1, 0, offset/boundSize); + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cDecode"), -10000, -10000/255, biasMultiplier*10000/boundSize, 10); + } + else{ + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -10000, -10000/255, biasMultiplier*10000/boundSize, 1); + } + } + + private static function getVShader():Procedure { + var shader:Procedure = Procedure.compileFromArray([ + "#v0=vSample", + + "m34 t0.xyz, i0, c0", + "dp3 t0.w, t0.xyz, t0.xyz", + "sqt t0.w, t0.w", // w: [0, boundSize] +// "div t0.xyz, t0.xyz, t0.w", // norm + + "mov v0, t0" + ], "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++] = "mov t0.z, v0.w"; // w: [0, boundSize] + shaderArr[line++] = "tex t0.xy, v0, s0 "; + shaderArr[line++] = "dp3 t0.x, t0.xyz, c0.xyz"; // декодируем, находим разницу между расстояниями и умножаем ее на большое число + + // рассчитываем значение тени + shaderArr[line++] = "sat t0, t0.x"; + shaderArr[line++] = "sub o0, c0.w, 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 + // v0.w - length(sample) [0, boundSize] + + // ищем 2-а перпендикулярных вектора + // (-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"; + + // нормируем их + shaderArr[line++] = "nrm t0.xyz, t0.xyz"; + shaderArr[line++] = "nrm t1.xyz, t1.xyz"; + + // задаем оффсеты + shaderArr[line++] = "mul t0.w, c1.w, v0.w"; // с1.w = offset/boundSize + shaderArr[line++] = "mul t0.xyz, t0.xyz, t0.w"; + shaderArr[line++] = "mul t1.xyz, t1.xyz, t0.w"; + // --------- {13 opcode} + + // t0, t1 - перпендикуляры ↑→ + // t2 - текущий вектор + + // в v0.w, t3.z расстояние до объекта + // t3.xy - результат из текстуры + // t3.w - сумма sat-ов + + // первая точка + 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, v0.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 opcode} + 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 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 > 2048) { + throw new ArgumentError("Map size exceeds maximum value 2048."); + } + 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. Этот способ фильтрации используется для смягчения границ тени. + */ + public function get pcfOffset():Number { + return _pcfOffset; + } + + /** + * @private + */ + public function set pcfOffset(value:Number):void { + _pcfOffset = value; + type = _pcfOffset > 0 ? "OS" : "os"; + 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.Program3D; + +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 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); + } + +} + diff --git a/src/alternativa/engine3d/shadows/Shadow.as b/src/alternativa/engine3d/shadows/Shadow.as index 24648e3..8b67f97 100644 --- a/src/alternativa/engine3d/shadows/Shadow.as +++ b/src/alternativa/engine3d/shadows/Shadow.as @@ -23,6 +23,11 @@ package alternativa.engine3d.shadows { */ public class Shadow { + /** + * Debug mode. + */ + public var debug:Boolean = false; + /** * @private * Key for processing in materials.