diff --git a/src/alternativa/engine3d/core/Camera3D.as b/src/alternativa/engine3d/core/Camera3D.as index 87b16b9..0c97c41 100644 --- a/src/alternativa/engine3d/core/Camera3D.as +++ b/src/alternativa/engine3d/core/Camera3D.as @@ -233,7 +233,6 @@ public class Camera3D extends Object3D { localToGlobalTransform.append(root.transform); globalToLocalTransform.prepend(root.inverseTransform); } - var excludedLightLength:int = root.excludedLights.length; // Check if object of hierarchy is visible if (root.visible) { @@ -320,6 +319,9 @@ public class Camera3D extends Object3D { lightsLength = j; lights.length = j; + // Sort lights by types + if (lightsLength > 0) sortLights(0, lightsLength - 1); + // Calculating the rays of mouse events view.calculateRays(this, (globalMouseHandlingType & Object3D.MOUSE_HANDLING_MOVING) != 0, (globalMouseHandlingType & Object3D.MOUSE_HANDLING_PRESSING) != 0, (globalMouseHandlingType & Object3D.MOUSE_HANDLING_WHEEL) != 0); for (i = origins.length; i < view.raysLength; i++) { @@ -338,6 +340,7 @@ public class Camera3D extends Object3D { root.listening = globalMouseHandlingType > 0; } // Check if object needs in lightning + var excludedLightLength:int = root.excludedLights.length; if (lightsLength > 0 && root.useLights) { // Pass the lights to children and calculate appropriate transformations var childLightsLength:int = 0; @@ -346,7 +349,7 @@ public class Camera3D extends Object3D { light = lights[i]; // Checking light source for existing in excludedLights j = 0; - while (j> 1; + var m:Light3D = lights[index]; + var mid:int = m.type; + var right:Light3D; + do { + while ((left = lights[i]).type < mid) { + i++; + } + while (mid < (right = lights[j]).type) { + j--; + } + if (i <= j) { + lights[i++] = right; + lights[j--] = left; + } + } while (i <= j); + if (l < j) { + sortLights(l, j); + } + if (i < r) { + sortLights(i, r); + } + } + /** * Transforms point from global space to screen space. The view property should be defined. * @param point Point in global space. diff --git a/src/alternativa/engine3d/core/Light3D.as b/src/alternativa/engine3d/core/Light3D.as index 31fae99..851aef0 100644 --- a/src/alternativa/engine3d/core/Light3D.as +++ b/src/alternativa/engine3d/core/Light3D.as @@ -24,6 +24,32 @@ package alternativa.engine3d.core { */ public class Light3D extends Object3D { + /** + * @private + */ + alternativa3d static const AMBIENT:int = 1; + /** + * @private + */ + alternativa3d static const DIRECTIONAL:int = 2; + /** + * @private + */ + alternativa3d static const OMNI:int = 3; + /** + * @private + */ + alternativa3d static const SPOT:int = 4; + /** + * @private + */ + alternativa3d static const SHADOW_BIT:int = 0x100; + + /** + * @private + */ + alternativa3d var type:int = 0; + /** * @private */ @@ -124,6 +150,7 @@ package alternativa.engine3d.core { if (_shadow != null) _shadow._light = null; _shadow = value; if (value != null) value._light = this; + type = (value != null) ? type | SHADOW_BIT : type & ~SHADOW_BIT; } } } diff --git a/src/alternativa/engine3d/lights/AmbientLight.as b/src/alternativa/engine3d/lights/AmbientLight.as index 829462b..10fba53 100644 --- a/src/alternativa/engine3d/lights/AmbientLight.as +++ b/src/alternativa/engine3d/lights/AmbientLight.as @@ -30,6 +30,7 @@ package alternativa.engine3d.lights { * @param color Light color. */ public function AmbientLight(color:uint) { + this.type = AMBIENT; this.color = color; } diff --git a/src/alternativa/engine3d/lights/DirectionalLight.as b/src/alternativa/engine3d/lights/DirectionalLight.as index a5bc48f..a1e2547 100644 --- a/src/alternativa/engine3d/lights/DirectionalLight.as +++ b/src/alternativa/engine3d/lights/DirectionalLight.as @@ -31,6 +31,7 @@ package alternativa.engine3d.lights { * @param color Color of light source. */ public function DirectionalLight(color:uint) { + this.type = DIRECTIONAL; this.color = color; } diff --git a/src/alternativa/engine3d/lights/OmniLight.as b/src/alternativa/engine3d/lights/OmniLight.as index ee23aec..07c55ae 100644 --- a/src/alternativa/engine3d/lights/OmniLight.as +++ b/src/alternativa/engine3d/lights/OmniLight.as @@ -39,6 +39,7 @@ package alternativa.engine3d.lights { * @param attenuationEnd Distance from at which falloff is complete. */ public function OmniLight(color:uint, attenuationBegin:Number, attenuationEnd:Number) { + this.type = OMNI; this.color = color; this.attenuationBegin = attenuationBegin; this.attenuationEnd = attenuationEnd; diff --git a/src/alternativa/engine3d/lights/SpotLight.as b/src/alternativa/engine3d/lights/SpotLight.as index fdd8d9e..4f54935 100644 --- a/src/alternativa/engine3d/lights/SpotLight.as +++ b/src/alternativa/engine3d/lights/SpotLight.as @@ -54,6 +54,7 @@ package alternativa.engine3d.lights { * @param falloff Adjusts the angle of a light's falloff. The Falloff value is measured in radians. */ public function SpotLight(color:uint, attenuationBegin:Number, attenuationEnd:Number, hotspot:Number, falloff:Number) { + this.type = SPOT; this.color = color; this.attenuationBegin = attenuationBegin; this.attenuationEnd = attenuationEnd; diff --git a/src/alternativa/engine3d/materials/StandardMaterial.as b/src/alternativa/engine3d/materials/StandardMaterial.as index adc2f77..7ce0217 100644 --- a/src/alternativa/engine3d/materials/StandardMaterial.as +++ b/src/alternativa/engine3d/materials/StandardMaterial.as @@ -51,6 +51,19 @@ package alternativa.engine3d.materials { */ public class StandardMaterial extends TextureMaterial { + private static const LIGHT_MAP_BIT:int = 1; + private static const GLOSSINESS_MAP_BIT:int = 2; + private static const SPECULAR_MAP_BIT:int = 4; + private static const OPACITY_MAP_BIT:int = 8; + private static const NORMAL_MAP_SPACE_OFFSET:int = 4; // shift value + private static const ALPHA_TEST_OFFSET:int = 6; + private static const OMNI_LIGHT_OFFSET:int = 8; + private static const DIRECTIONAL_LIGHT_OFFSET:int = 11; + private static const SPOT_LIGHT_OFFSET:int = 14; + private static const SHADOW_OFFSET:int = 17; + // TODO: remove double cash by transform procedure. It increase speed by 1% +// private static const OBJECT_TYPE_BIT:int = 19; + private static var caches:Dictionary = new Dictionary(true); private var cachedContext3D:Context3D; private var programsCache:Dictionary; @@ -514,9 +527,21 @@ package alternativa.engine3d.materials { * @param directionalLight * @param lightsLength */ - private function getProgram(object:Object3D, programs:Dictionary, camera:Camera3D, materialKey:String, opacityMap:TextureResource, alphaTest:int, lightsGroup:Vector., lightsLength:int, isFirstGroup:Boolean, shadowedLight:Light3D):StandardMaterialProgram { - var key:String = materialKey + (opacityMap != null ? "O" : "o") + alphaTest.toString(); + private function getProgram(object:Object3D, programs:Array, camera:Camera3D, materialKey:int, opacityMap:TextureResource, alphaTest:int, lightsGroup:Vector., lightsLength:int, isFirstGroup:Boolean, shadowedLight:Light3D):StandardMaterialProgram { + // 0 bit - lightmap + // 1 bit - glossiness map + // 2 bit - opacity map + // 3 bit - specular map + // 4-5 bits - normalMapSpace + // 6-7 bits - alphaTest + // 8-10 bits - OmniLight count + // 11-13 bits - DirectionalLight count + // 14-16 bits - SpotLight count + // 17-18 bit - Shadow Type (PCF, SIMPLE, NONE) + + var key:int = materialKey | (opacityMap != null ? OPACITY_MAP_BIT : 0) | (alphaTest << ALPHA_TEST_OFFSET); var program:StandardMaterialProgram = programs[key]; + if (program == null) { var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX); var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); @@ -983,14 +1008,14 @@ package alternativa.engine3d.materials { cachedContext3D = camera.context3D; programsCache = caches[cachedContext3D]; if (programsCache == null) { - programsCache = new Dictionary(); + programsCache = new Dictionary(false); caches[cachedContext3D] = programsCache; } } - var optionsPrograms:Dictionary = programsCache[object.transformProcedure]; + var optionsPrograms:Array = programsCache[object.transformProcedure]; if (optionsPrograms == null) { - optionsPrograms = new Dictionary(false); + optionsPrograms = []; programsCache[object.transformProcedure] = optionsPrograms; } @@ -1016,15 +1041,16 @@ package alternativa.engine3d.materials { } // Iterate groups - var materialKey:String; + var materialKey:int; var program:StandardMaterialProgram; + var omniLightCount:int = 0; + var directionalLightCount:int = 0; + var spotLightCount:int = 0; 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"); + materialKey = ((lightMap != null) ? LIGHT_MAP_BIT : 0) | ((glossinessMap != null) ? GLOSSINESS_MAP_BIT : 0) | ((specularMap != null) ? SPECULAR_MAP_BIT : 0); if (opaquePass && alphaThreshold <= alpha) { if (alphaThreshold > 0) { @@ -1060,15 +1086,15 @@ package alternativa.engine3d.materials { // Group of lights without shadow // Form key - materialKey = (isFirstGroup)?((lightMap != null) ? "L" : "l"):""; - materialKey += - (_normalMapSpace.toString()) + - ((glossinessMap != null) ? "G" : "g") + - ((specularMap != null) ? "S" : "s"); + materialKey = (isFirstGroup) ? ((lightMap != null) ? LIGHT_MAP_BIT : 0) : 0; + materialKey |= (_normalMapSpace << NORMAL_MAP_SPACE_OFFSET) | ((glossinessMap != null) ? GLOSSINESS_MAP_BIT : 0) | ((specularMap != null) ? SPECULAR_MAP_BIT : 0); for (j = 0; j < lightGroupLength; j++) { light = lightGroup[j]; - materialKey += light.lightID; + if (light is OmniLight) omniLightCount++; else if (light is DirectionalLight) directionalLightCount++; else if (light is SpotLight) spotLightCount++; } + materialKey |= omniLightCount << OMNI_LIGHT_OFFSET; + materialKey |= directionalLightCount << DIRECTIONAL_LIGHT_OFFSET; + materialKey |= spotLightCount << SPOT_LIGHT_OFFSET; // Create program and drawUnit for group // Opaque pass @@ -1108,13 +1134,10 @@ package alternativa.engine3d.materials { light = 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; + materialKey = (isFirstGroup) ? ((lightMap != null) ? LIGHT_MAP_BIT : 0) : 0; + materialKey |= (_normalMapSpace << NORMAL_MAP_SPACE_OFFSET) | ((glossinessMap != null) ? GLOSSINESS_MAP_BIT : 0) | ((specularMap != null) ? SPECULAR_MAP_BIT : 0); + materialKey |= light.shadow.type << SHADOW_OFFSET; + if (light is OmniLight) materialKey |= 1 << OMNI_LIGHT_OFFSET; else if (light is DirectionalLight) materialKey |= 1 << DIRECTIONAL_LIGHT_OFFSET; else if (light is SpotLight) materialKey |= 1 << SPOT_LIGHT_OFFSET; // Для группы создаем программу и дроуюнит // Opaque pass diff --git a/src/alternativa/engine3d/shadows/DirectionalLightShadow.as b/src/alternativa/engine3d/shadows/DirectionalLightShadow.as index 6ab2a00..154a6be 100644 --- a/src/alternativa/engine3d/shadows/DirectionalLightShadow.as +++ b/src/alternativa/engine3d/shadows/DirectionalLightShadow.as @@ -214,7 +214,7 @@ package alternativa.engine3d.shadows { this._mapSize = mapSize; this._pcfOffset = pcfOffset; - this.type = _pcfOffset > 0 ? "DS" : "ds"; + this.type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; vertexShadowProcedure = getVShader(); fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); @@ -866,7 +866,7 @@ package alternativa.engine3d.shadows { */ public function set pcfOffset(value:Number):void { _pcfOffset = value; - type = _pcfOffset > 0 ? "S" : "s"; + type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); } diff --git a/src/alternativa/engine3d/shadows/OmniLightShadow.as b/src/alternativa/engine3d/shadows/OmniLightShadow.as index 8310ece..8e0a166 100644 --- a/src/alternativa/engine3d/shadows/OmniLightShadow.as +++ b/src/alternativa/engine3d/shadows/OmniLightShadow.as @@ -52,7 +52,7 @@ package alternativa.engine3d.shadows { /** * @private */ - alternativa3d static var debugRadiusScale:Number = 0.5; + alternativa3d static var debugRadiusScale:Number = 0.2; private var renderer:Renderer = new Renderer(); @@ -104,7 +104,7 @@ package alternativa.engine3d.shadows { this.pcfOffset = pcfOffset; vertexShadowProcedure = getVShader(); - type = _pcfOffset > 0 ? "OS" : "os"; + type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); debugMaterial = new ShadowDebugMaterial(); @@ -331,7 +331,7 @@ package alternativa.engine3d.shadows { if (debugObject == null) { debugObject = createDebugObject(debugMaterial, camera.context3D); } - debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = debugRadiusScale; + debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = radius*debugRadiusScale; debugObject.composeTransforms(); // Формируем матрицу трансформации для debugObject @@ -939,7 +939,7 @@ package alternativa.engine3d.shadows { */ public function set pcfOffset(value:Number):void { _pcfOffset = value; - type = _pcfOffset > 0 ? "OS" : "os"; + type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); } diff --git a/src/alternativa/engine3d/shadows/Shadow.as b/src/alternativa/engine3d/shadows/Shadow.as index 8b67f97..6a5c553 100644 --- a/src/alternativa/engine3d/shadows/Shadow.as +++ b/src/alternativa/engine3d/shadows/Shadow.as @@ -23,6 +23,19 @@ package alternativa.engine3d.shadows { */ public class Shadow { + /** + * @private + */ + alternativa3d static const NONE_MODE:int = 0; + /** + * @private + */ + alternativa3d static const SIMPLE_MODE:int = 1; + /** + * @private + */ + alternativa3d static const PCF_MODE:int = 2; + /** * Debug mode. */ @@ -32,7 +45,7 @@ package alternativa.engine3d.shadows { * @private * Key for processing in materials. */ - alternativa3d var type:String = "s"; + alternativa3d var type:int = 0; /** * @private