Browse Source

Updating to Urho3D master (a5c5956103edeb98c7145b2b934371f078e74b9b)

1901271 Expose mouse position manipulation api in Input class
d4039d5 Add functions for mipmap manipulation to Image.
90efa3b Specify high precision for water shader varyings on GLES to fix shaky reflection/refraction texture sampling. Closes #1593.
5b67d28 Let Box2D world stay at default values for continuous (true) & substepping (false), since substepping enabled leads to erratic behavior under load. Closes #1592.
97eba58 Fix implicit conversion in normal offset shadow calculations. Closes #1586.
9c6e607 Fix memory leak in Dictionary.
00f0a87 Stop existing weight fade when AnimationController::SetWeight() is called, to ensure the set weight is not overridden one frame later. Closes #1583.
7c72c36 Fix LookAt Mixed rotation mode.
b227682 Possibility to enable alpha-to-coverage in either pass or material (all passes). Requires D3D11 or OpenGL and hardware multisampling enabled. Seems to look slightly different on D3D / OpenGL. Fixed depth bias not being copied in Material::Clone(). Closes #1578.
90d7d22 Do not make a value copy of the VariantVector inside the attribute in SceneResolver. Closes #1576.
32ef767 Make a value copy of the node IDs when resolving. Potential solution for #1576.
9114141 Add missing enum string name for serialization.
83acf5b Optional per-texture specifiable max. anisotropy level. Closes #1563.
6323aed Add NavigationPushiness::NAVIGATIONPUSHINESS_NONE
5f7ef27 fix skydome glsl error
19300a3 Add note that ResetTarget() doesn't stop automatically. Do not debug draw a "velocity" target for CrowdAgent. Closes #1572.
6895a51 Pass-level mechanism for eliminating unnecessary shader compilation defines. Closes #1567. Use this mechanism to eliminate PACKEDNORMAL define from depth & shadow pass in normalmapped techniques.
4e5f83a Make it safe to remove CrowdAgent during the reposition event. Closes #1570.
6be0d71 Fix keeping track of RigidBody2D constraints. Closes #1569.
02fae13 Update face camera mode name and add script bindings.
d344cae Add new face camera mode to BillboardSet and Text3D.
2b2f12f Added porting note related to light max extrusion parameter.
2fe4cb0 Add max extrusion parameter for directional lights. Default to 1000. This prevents large far clip causing poor shadow map depth resolution and too strong effect of depth bias parameters, but can be increased if very tall shadows are needed.
623639c Add note of example use of render order.
695f750 Remove specific AlphaMask & NormalPacked techniques, in favor of adding the necessary shader defines in the material instead.
dc90a2d Remove ambiguous weak ptr constructor call.
48f779e Add possibility for materials to define vertex & pixel shader compilation defines, like techniques & passes. Related to #1566. As a consequence, removed the "alphamask" hint from techniques (render order can be used instead) and automatic determination if material should occlude. Fixed bugs in Material JSON save. Allow JSONValue::Size() to also work in object mode. Added porting notes as necessary.
3dfb4a4 Find navigation mesh from the scene in a delayed manner. Plus other codepath consistency fixes for CrowdManager. Closes #1565.
1dbb990 Convert double to float when assigning a shader parameter (possible danger when setting material parameters from Lua)
0201718 Add missing assert to PODVector.
2d59e72 Guard against self-assignment clearing the containers.
7a600be Refactor Vector and PODVector implementation.
000b1af Check for obstacle node moving and update navigation mesh appropriately. Needs a hack to skip this during scene deletion to avoid crash. Closes #1558.
b7c7568 Add to/from string conversions for JSONValueType & JSONNumberType. Minor code cleanup and missing JSONValue script bindings. Closes #1546.
ab40958 Detect Push / Insert to Vector or PODVector from inside itself and make a value copy in that case. Closes #1556.
8bef941 Fix slowed down navigation crowd agent when node dirtied in E_CROWD_AGENT_NODE_REPOSITION. Send the event only after position has been updated. Check for only rotation changing when node dirtied, and do not update position / reset state in that case. Closes #1548.
d72de15 Add note that GetNextLevel() will keep returning 1x1x1 image if necessary. Closes #1554.
5b70c5d Restore earlier method of defining camera frustum when a custom projection matrix is not in use, to improve deferred depth reconstruction accuracy. Related to #1551.
b3964e2 Demonstrate Z fighting elimination by depth bias in 10_RenderToTexture sample. Closes #1543.
79f1a1b added a check for invalid boneIndex caused by geometry key animation
a1aa049 fixed geometry having animation key problem
489f759 Fix slope-scale depth bias potentially not creating new rasterizer state on D3D11 due to poor hashing. Remove mistaken adjustment of slope-scale bias on OpenGL. Now behavior should be uniform between D3D9/D3D11/OpenGL. Closes #1547.
9d5380b Remove extraneous f from GLSL code to satisfy the compiler.
6a72b70 Fix copypaste error in attribute accessor.
273a3ac Add support of free functions as attribute accessors.
6c4a112 Add note on camera projection matrix API change.
d9c0ce7 Send also node-specific 2D collision events. Closes #1535.
341f2d9 Normalize slash / backslash when AnimationController queries for existence of an animation state. Closes #1537.
bc8074a Added SetProjection() to camera, which sets custom projection matrix. This is reset by modifying any of the standard projection parameters (farClip, FOV etc.) Make sure camera's actual far & near clip are based on the projection matrix.
871e52f Base frustum calculation on the projection matrix.
3af3c2b fixed channelIndices[] indexing in ExpandAnimatedChannelKeys() func.
48cd15b Store camera projection matrix as API-independent and convert as necessary (preparation for allowing the user to define a custom projection matrix.) Camera GetProjection() API changed; GetGPUProjection() is now used to get the API-specific projection for use as a shader parameter.
bef8df2 Support defining neighbor terrains to avoid LOD level discontinuities at the edges.
d3cc011 Handle flipping of normals in collision event sending if body pointers were swapped. Do not discard contact results from the "other" manifold, rather concatenate them into the same collision event. Thanks to Enhex for highlighting the issue.
ecfa732 Workaround GCC issue producing shared lib containing undefined symbol. Close #1519.
9bbec4b Explain why normal offset is not saved for materials. Closes #1518.
7b5da66 Skip adding a deferred light volume batch when the light has low 8 bits of lightmask cleared; it would not have effect due to the stencil test. Skip shadow map render when there are no forward & deferred batches using it.
2c8ed38 Capitalize CrowdAgent enum attributes for consistency with other components. Loading is case-insensitive so this does not break existing scenes.
3fe5024 Remove deprecated autoRemove bool from SoundSource. Replace with a more generic AutoRemoveMode enum which is now used by both SoundSource & ParticleEmitter.
a2d86f1 Get screenshot as RGBA on OpenGL ES to fix Android black screenshot.
a76fc0a Undefine GetObject in TileMapLayer2D.h to prevent it getting confused with Win32 API function define. To help, the header should be included after Windows.h is included. Related to #1512.
0991c8e Ensure node's components are properly marked for network update in case node is late added to the scene. Make it impossible for a NetworkState to exist without properly allocated currentValues & previousValues. Fix erroneous comment in C++ version of SceneReplication sample. Code cleanup & minor optimization. Closes #1511.
2f25c3b Add note to discourage use of AddComponent(). Prevent attempting to assign model resource on StaticModel / AnimatedModel when no node set. Closes #1510.
Josh Engebretson 9 years ago
parent
commit
d2e23f99aa
100 changed files with 919 additions and 823 deletions
  1. 2 2
      Resources/CoreData/Shaders/GLSL/BRDF.glsl
  2. 2 2
      Resources/CoreData/Shaders/GLSL/Lighting.glsl
  3. 2 5
      Resources/CoreData/Shaders/GLSL/Skydome.glsl
  4. 8 1
      Resources/CoreData/Shaders/GLSL/Water.glsl
  5. 3 3
      Resources/CoreData/Shaders/HLSL/Lighting.hlsl
  6. 0 9
      Resources/CoreData/Techniques/DiffAOAlphaMask.xml
  7. 0 10
      Resources/CoreData/Techniques/DiffAlphaMask.xml
  8. 0 10
      Resources/CoreData/Techniques/DiffAlphaMaskTranslucent.xml
  9. 0 9
      Resources/CoreData/Techniques/DiffLightMapEnvCube.xml
  10. 2 2
      Resources/CoreData/Techniques/DiffNormal.xml
  11. 2 2
      Resources/CoreData/Techniques/DiffNormalAO.xml
  12. 1 1
      Resources/CoreData/Techniques/DiffNormalAOAlpha.xml
  13. 0 9
      Resources/CoreData/Techniques/DiffNormalAOAlphaMask.xml
  14. 1 1
      Resources/CoreData/Techniques/DiffNormalAlpha.xml
  15. 0 10
      Resources/CoreData/Techniques/DiffNormalAlphaMask.xml
  16. 0 10
      Resources/CoreData/Techniques/DiffNormalAlphaMaskTranslucent.xml
  17. 1 1
      Resources/CoreData/Techniques/DiffNormalAlphaTranslucent.xml
  18. 2 2
      Resources/CoreData/Techniques/DiffNormalEmissive.xml
  19. 1 1
      Resources/CoreData/Techniques/DiffNormalEmissiveAlpha.xml
  20. 2 2
      Resources/CoreData/Techniques/DiffNormalEnvCube.xml
  21. 1 1
      Resources/CoreData/Techniques/DiffNormalEnvCubeAlpha.xml
  22. 0 10
      Resources/CoreData/Techniques/DiffNormalPacked.xml
  23. 0 9
      Resources/CoreData/Techniques/DiffNormalPackedAO.xml
  24. 0 5
      Resources/CoreData/Techniques/DiffNormalPackedAOAlpha.xml
  25. 0 9
      Resources/CoreData/Techniques/DiffNormalPackedAOAlphaMask.xml
  26. 0 5
      Resources/CoreData/Techniques/DiffNormalPackedAlpha.xml
  27. 0 10
      Resources/CoreData/Techniques/DiffNormalPackedAlphaMask.xml
  28. 0 10
      Resources/CoreData/Techniques/DiffNormalPackedAlphaMaskTranslucent.xml
  29. 0 5
      Resources/CoreData/Techniques/DiffNormalPackedAlphaTranslucent.xml
  30. 0 9
      Resources/CoreData/Techniques/DiffNormalPackedEmissive.xml
  31. 0 5
      Resources/CoreData/Techniques/DiffNormalPackedEmissiveAlpha.xml
  32. 0 9
      Resources/CoreData/Techniques/DiffNormalPackedEnvCube.xml
  33. 0 5
      Resources/CoreData/Techniques/DiffNormalPackedEnvCubeAlpha.xml
  34. 0 10
      Resources/CoreData/Techniques/DiffNormalPackedSpec.xml
  35. 0 9
      Resources/CoreData/Techniques/DiffNormalPackedSpecAO.xml
  36. 0 5
      Resources/CoreData/Techniques/DiffNormalPackedSpecAOAlpha.xml
  37. 0 9
      Resources/CoreData/Techniques/DiffNormalPackedSpecAOAlphaMask.xml
  38. 0 5
      Resources/CoreData/Techniques/DiffNormalPackedSpecAlpha.xml
  39. 0 10
      Resources/CoreData/Techniques/DiffNormalPackedSpecAlphaMask.xml
  40. 0 9
      Resources/CoreData/Techniques/DiffNormalPackedSpecEmissive.xml
  41. 0 5
      Resources/CoreData/Techniques/DiffNormalPackedSpecEmissiveAlpha.xml
  42. 2 2
      Resources/CoreData/Techniques/DiffNormalSpec.xml
  43. 2 2
      Resources/CoreData/Techniques/DiffNormalSpecAO.xml
  44. 1 1
      Resources/CoreData/Techniques/DiffNormalSpecAOAlpha.xml
  45. 0 9
      Resources/CoreData/Techniques/DiffNormalSpecAOAlphaMask.xml
  46. 1 1
      Resources/CoreData/Techniques/DiffNormalSpecAlpha.xml
  47. 0 10
      Resources/CoreData/Techniques/DiffNormalSpecAlphaMask.xml
  48. 2 2
      Resources/CoreData/Techniques/DiffNormalSpecEmissive.xml
  49. 1 1
      Resources/CoreData/Techniques/DiffNormalSpecEmissiveAlpha.xml
  50. 0 10
      Resources/CoreData/Techniques/DiffSpecAlphaMask.xml
  51. 0 6
      Resources/CoreData/Techniques/DiffUnlitAlphaMask.xml
  52. 2 2
      Resources/CoreData/Techniques/NoTextureNormal.xml
  53. 1 1
      Resources/CoreData/Techniques/NoTextureNormalAlpha.xml
  54. 0 10
      Resources/CoreData/Techniques/NoTextureNormalPacked.xml
  55. 0 5
      Resources/CoreData/Techniques/NoTextureNormalPackedAlpha.xml
  56. 2 2
      Resources/CoreData/Techniques/PBR/DiffNormalSpecEmissive.xml
  57. 1 1
      Resources/CoreData/Techniques/PBR/DiffNormalSpecEmissiveAlpha.xml
  58. 2 2
      Resources/CoreData/Techniques/PBR/PBRDiffNormal.xml
  59. 1 1
      Resources/CoreData/Techniques/PBR/PBRDiffNormalAlpha.xml
  60. 2 2
      Resources/CoreData/Techniques/PBR/PBRDiffNormalEmissive.xml
  61. 1 1
      Resources/CoreData/Techniques/PBR/PBRDiffNormalEmissiveAlpha.xml
  62. 2 2
      Resources/CoreData/Techniques/PBR/PBRMetallicRoughDiffNormalSpec.xml
  63. 2 2
      Resources/CoreData/Techniques/PBR/PBRMetallicRoughDiffNormalSpecEmissive.xml
  64. 1 1
      Resources/CoreData/Techniques/PBR/PBRMetallicRoughDiffNormalSpecEmissiveAlpha.xml
  65. 0 10
      Resources/CoreData/Techniques/VegetationDiffAlphaMask.xml
  66. 0 8
      Resources/CoreData/Techniques/VegetationDiffUnlitAlphaMask.xml
  67. 12 6
      Source/Atomic/Atomic2D/Constraint2D.cpp
  68. 2 2
      Source/Atomic/Atomic2D/Constraint2D.h
  69. 21 29
      Source/Atomic/Atomic2D/PhysicsEvents2D.h
  70. 51 11
      Source/Atomic/Atomic2D/PhysicsWorld2D.cpp
  71. 5 3
      Source/Atomic/Atomic2D/RigidBody2D.cpp
  72. 4 0
      Source/Atomic/Atomic2D/TileMapLayer2D.h
  73. 7 26
      Source/Atomic/Audio/SoundSource.cpp
  74. 6 8
      Source/Atomic/Audio/SoundSource.h
  75. 6 2
      Source/Atomic/Container/HashMap.h
  76. 6 2
      Source/Atomic/Container/HashSet.h
  77. 6 3
      Source/Atomic/Container/List.h
  78. 135 142
      Source/Atomic/Container/Vector.h
  79. 2 2
      Source/Atomic/Core/Context.cpp
  80. 6 8
      Source/Atomic/Graphics/AnimatedModel.cpp
  81. 6 1
      Source/Atomic/Graphics/AnimationController.cpp
  82. 4 6
      Source/Atomic/Graphics/Batch.cpp
  83. 12 3
      Source/Atomic/Graphics/BillboardSet.cpp
  84. 7 0
      Source/Atomic/Graphics/BillboardSet.h
  85. 183 141
      Source/Atomic/Graphics/Camera.cpp
  86. 26 12
      Source/Atomic/Graphics/Camera.h
  87. 2 1
      Source/Atomic/Graphics/DebugRenderer.cpp
  88. 2 0
      Source/Atomic/Graphics/DebugRenderer.h
  89. 16 11
      Source/Atomic/Graphics/Graphics.h
  90. 3 2
      Source/Atomic/Graphics/GraphicsDefs.h
  91. 66 1
      Source/Atomic/Graphics/Light.cpp
  92. 30 6
      Source/Atomic/Graphics/Light.h
  93. 135 23
      Source/Atomic/Graphics/Material.cpp
  94. 29 5
      Source/Atomic/Graphics/Material.h
  95. 1 1
      Source/Atomic/Graphics/OcclusionBuffer.cpp
  96. 28 11
      Source/Atomic/Graphics/OpenGL/OGLGraphics.cpp
  97. 2 1
      Source/Atomic/Graphics/OpenGL/OGLTexture.cpp
  98. 23 5
      Source/Atomic/Graphics/ParticleEmitter.cpp
  99. 8 1
      Source/Atomic/Graphics/ParticleEmitter.h
  100. 11 8
      Source/Atomic/Graphics/Renderer.cpp

+ 2 - 2
Resources/CoreData/Shaders/GLSL/BRDF.glsl

@@ -113,8 +113,8 @@
         float energyFactor = mix(roughness, 1.0, 1.0 / 1.51);
         float fd90 = energyBias + 2.0 * VdotH * VdotH * roughness;
         float f0 = 1.0;
-        float lightScatter = f0 + (fd90 - f0) * pow(1.0f - NdotL, 5.0f);
-        float viewScatter = f0 + (fd90 - f0) * pow(1.0f - NdotV, 5.0f);
+        float lightScatter = f0 + (fd90 - f0) * pow(1.0 - NdotL, 5.0);
+        float viewScatter = f0 + (fd90 - f0) * pow(1.0 - NdotV, 5.0);
 
         return diffuseColor * lightScatter * viewScatter * energyFactor;
     }

+ 2 - 2
Resources/CoreData/Shaders/GLSL/Lighting.glsl

@@ -86,7 +86,7 @@ vec4 GetShadowPos(int index, vec3 normal, vec4 projWorldPos)
         #ifdef DIRLIGHT
             float cosAngle = clamp(1.0 - dot(normal, cLightDir), 0.0, 1.0);
         #else
-            float cosAngle = clamp(1.0 - dot(normal, normalize(cLightPos - projWorldPos.xyz)), 0.0, 1.0);
+            float cosAngle = clamp(1.0 - dot(normal, normalize(cLightPos.xyz - projWorldPos.xyz)), 0.0, 1.0);
         #endif
         projWorldPos.xyz += cosAngle * normalOffsetScale[index] * normal;
     #endif
@@ -353,7 +353,7 @@ float GetShadowDeferred(vec4 projWorldPos, vec3 normal, float depth)
         return GetDirShadowDeferred(projWorldPos, normal, depth);
     #else
         #ifdef NORMALOFFSET
-            float cosAngle = clamp(1.0 - dot(normal, normalize(cLightPosPS - projWorldPos.xyz)), 0.0, 1.0);
+            float cosAngle = clamp(1.0 - dot(normal, normalize(cLightPosPS.xyz - projWorldPos.xyz)), 0.0, 1.0);
             projWorldPos.xyz += cosAngle * cNormalOffsetScalePS.x * normal;
         #endif
 

+ 2 - 5
Resources/CoreData/Shaders/GLSL/Skydome.glsl

@@ -4,9 +4,6 @@
 
 varying vec2 vTexCoord;
 
-uniform mat4 gl_ModelViewMatrix;
-uniform mat4 gl_ProjectionMatrix;
-
 void VS()
 {
     mat4 modelMatrix = iModelMatrix;
@@ -17,6 +14,6 @@ void VS()
 }
 
 void PS()
-{   
-    gl_FragColor = texture2D(sDiffMap, vTexCoord);       
+{
+    gl_FragColor = texture2D(sDiffMap, vTexCoord);
 }

+ 8 - 1
Resources/CoreData/Shaders/GLSL/Water.glsl

@@ -4,11 +4,18 @@
 #include "ScreenPos.glsl"
 #include "Fog.glsl"
 
+#ifndef GL_ES
 varying vec4 vScreenPos;
 varying vec2 vReflectUV;
 varying vec2 vWaterUV;
-varying vec3 vNormal;
 varying vec4 vEyeVec;
+#else
+varying highp vec4 vScreenPos;
+varying highp vec2 vReflectUV;
+varying highp vec2 vWaterUV;
+varying highp vec4 vEyeVec;
+#endif
+varying vec3 vNormal;
 
 #ifdef COMPILEVS
 uniform vec2 cNoiseSpeed;

+ 3 - 3
Resources/CoreData/Shaders/HLSL/Lighting.hlsl

@@ -85,7 +85,7 @@ void GetShadowPos(float4 projWorldPos, float3 normal, out float4 shadowPos[NUMCA
         #ifdef DIRLIGHT
             float cosAngle = saturate(1.0 - dot(normal, cLightDir));
         #else
-            float cosAngle = saturate(1.0 - dot(normal, normalize(cLightPos - projWorldPos.xyz)));
+            float cosAngle = saturate(1.0 - dot(normal, normalize(cLightPos.xyz - projWorldPos.xyz)));
         #endif
 
         #if defined(DIRLIGHT)
@@ -191,7 +191,7 @@ float Chebyshev(float2 Moments, float depth)
 {  
     //One-tailed inequality valid if depth > Moments.x  
     float p = float(depth <= Moments.x);  
-    //Compute variance.  
+    //Compute variance.
     float Variance = Moments.y - (Moments.x * Moments.x); 
 
     float minVariance = cVSMShadowParams.x;
@@ -351,7 +351,7 @@ float GetShadowDeferred(float4 projWorldPos, float3 normal, float depth)
         return GetDirShadowDeferred(projWorldPos, normal, depth);
     #else
         #ifdef NORMALOFFSET
-            float cosAngle = saturate(1.0 - dot(normal, normalize(cLightPosPS - projWorldPos.xyz)));
+            float cosAngle = saturate(1.0 - dot(normal, normalize(cLightPosPS.xyz - projWorldPos.xyz)));
             projWorldPos.xyz += cosAngle * cNormalOffsetScalePS.x * normal;
         #endif
 

+ 0 - 9
Resources/CoreData/Techniques/DiffAOAlphaMask.xml

@@ -1,9 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true">
-    <pass name="base" vsdefines="AO" psdefines="AO" />
-    <pass name="light" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" psdefines="PREPASS" />
-    <pass name="material" vsdefines="AO" psdefines="MATERIAL AO" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="AO" psdefines="DEFERRED AO" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 0 - 10
Resources/CoreData/Techniques/DiffAlphaMask.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true" >
-    <pass name="base" />
-    <pass name="litbase" psdefines="AMBIENT" />
-    <pass name="light" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" psdefines="PREPASS" />
-    <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" psdefines="DEFERRED" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 0 - 10
Resources/CoreData/Techniques/DiffAlphaMaskTranslucent.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" vsdefines="TRANSLUCENT" psdefines="DIFFMAP ALPHAMASK TRANSLUCENT" alphamask="true" >
-    <pass name="base" />
-    <pass name="litbase" psdefines="AMBIENT" />
-    <pass name="light" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" psdefines="PREPASS" />
-    <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" psdefines="DEFERRED" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 0 - 9
Resources/CoreData/Techniques/DiffLightMapEnvCube.xml

@@ -1,9 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="base" vsdefines="ENVCUBEMAP LIGHTMAP" psdefines="ENVCUBEMAP LIGHTMAP" />
-    <pass name="prepass" psdefines="PREPASS" />
-    <pass name="light" vsdefines="LIGHTMAP" psdefines="LIGHTMAP" depthtest="equal" depthwrite="false" blend="subtract" />
-    <pass name="material" vsdefines="ENVCUBEMAP LIGHTMAP" psdefines="MATERIAL ENVCUBEMAP LIGHTMAP" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="ENVCUBEMAP LIGHTMAP" psdefines="DEFERRED ENVCUBEMAP LIGHTMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 2 - 2
Resources/CoreData/Techniques/DiffNormal.xml

@@ -5,6 +5,6 @@
     <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP" />
     <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
     <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 2 - 2
Resources/CoreData/Techniques/DiffNormalAO.xml

@@ -4,6 +4,6 @@
     <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP" />
     <pass name="material" vsdefines="AO" psdefines="MATERIAL AO" depthtest="equal" depthwrite="false" />
     <pass name="deferred" vsdefines="NORMALMAP AO" psdefines="DEFERRED NORMALMAP AO" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 1 - 1
Resources/CoreData/Techniques/DiffNormalAOAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
     <pass name="alpha" vsdefines="AO" psdefines="AO" depthwrite="false" blend="alpha" />
     <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 0 - 9
Resources/CoreData/Techniques/DiffNormalAOAlphaMask.xml

@@ -1,9 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true" >
-    <pass name="base" vsdefines="AO" psdefines="AO" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP" />
-    <pass name="material" vsdefines="AO" psdefines="MATERIAL AO" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP AO" psdefines="DEFERRED NORMALMAP AO" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 1 - 1
Resources/CoreData/Techniques/DiffNormalAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
     <pass name="alpha" depthwrite="false" blend="alpha" />
     <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 0 - 10
Resources/CoreData/Techniques/DiffNormalAlphaMask.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true">
-    <pass name="base" />
-    <pass name="litbase" vsdefines="NORMALMAP" psdefines="AMBIENT NORMALMAP" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP" />
-    <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 0 - 10
Resources/CoreData/Techniques/DiffNormalAlphaMaskTranslucent.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" vsdefines="TRANSLUCENT" psdefines="DIFFMAP ALPHAMASK TRANSLUCENT" alphamask="true">
-    <pass name="base" />
-    <pass name="litbase" vsdefines="NORMALMAP" psdefines="AMBIENT NORMALMAP" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP" />
-    <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 1 - 1
Resources/CoreData/Techniques/DiffNormalAlphaTranslucent.xml

@@ -1,5 +1,5 @@
 <technique vs="LitSolid" ps="LitSolid" vsdefines="TRANSLUCENT" psdefines="DIFFMAP TRANSLUCENT">
     <pass name="alpha" depthwrite="false" blend="alpha" />
     <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 2 - 2
Resources/CoreData/Techniques/DiffNormalEmissive.xml

@@ -4,6 +4,6 @@
     <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP" />
     <pass name="material" psdefines="MATERIAL EMISSIVEMAP" depthtest="equal" depthwrite="false" />
     <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP EMISSIVEMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 1 - 1
Resources/CoreData/Techniques/DiffNormalEmissiveAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
     <pass name="alpha" psdefines="EMISSIVEMAP" depthwrite="false" blend="alpha" />
     <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 2 - 2
Resources/CoreData/Techniques/DiffNormalEnvCube.xml

@@ -4,6 +4,6 @@
     <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP" />
     <pass name="material" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="MATERIAL NORMALMAP ENVCUBEMAP" depthtest="equal" depthwrite="false" />
     <pass name="deferred" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="DEFERRED NORMALMAP ENVCUBEMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 1 - 1
Resources/CoreData/Techniques/DiffNormalEnvCubeAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
     <pass name="alpha" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="NORMALMAP ENVCUBEMAP" depthwrite="false" blend="alpha" />
     <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 0 - 10
Resources/CoreData/Techniques/DiffNormalPacked.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="base" />
-    <pass name="litbase" vsdefines="NORMALMAP" psdefines="AMBIENT NORMALMAP PACKEDNORMAL" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL" />
-    <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP PACKEDNORMAL" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 9
Resources/CoreData/Techniques/DiffNormalPackedAO.xml

@@ -1,9 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="base" vsdefines="AO" psdefines="AO" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL" />
-    <pass name="material" vsdefines="AO" psdefines="MATERIAL AO" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP AO" psdefines="DEFERRED NORMALMAP PACKEDNORMAL AO" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 5
Resources/CoreData/Techniques/DiffNormalPackedAOAlpha.xml

@@ -1,5 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="alpha" vsdefines="AO" psdefines="AO" depthwrite="false" blend="alpha" />
-    <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 9
Resources/CoreData/Techniques/DiffNormalPackedAOAlphaMask.xml

@@ -1,9 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true" >
-    <pass name="base" vsdefines="AO" psdefines="AO" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL" />
-    <pass name="material" vsdefines="AO" psdefines="MATERIAL AO" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP AO" psdefines="DEFERRED NORMALMAP PACKEDNORMAL AO" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 0 - 5
Resources/CoreData/Techniques/DiffNormalPackedAlpha.xml

@@ -1,5 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="alpha" depthwrite="false" blend="alpha" />
-    <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 10
Resources/CoreData/Techniques/DiffNormalPackedAlphaMask.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true">
-    <pass name="base" />
-    <pass name="litbase" vsdefines="NORMALMAP" psdefines="AMBIENT NORMALMAP PACKEDNORMAL" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL" />
-    <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP PACKEDNORMAL" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 0 - 10
Resources/CoreData/Techniques/DiffNormalPackedAlphaMaskTranslucent.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" vsdefines="TRANSLUCENT" psdefines="DIFFMAP ALPHAMASK TRANSLUCENT" alphamask="true">
-    <pass name="base" />
-    <pass name="litbase" vsdefines="NORMALMAP" psdefines="AMBIENT NORMALMAP PACKEDNORMAL" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL" />
-    <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP PACKEDNORMAL" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 0 - 5
Resources/CoreData/Techniques/DiffNormalPackedAlphaTranslucent.xml

@@ -1,5 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" vsdefines="TRANSLUCENT" psdefines="DIFFMAP TRANSLUCENT">
-    <pass name="alpha" depthwrite="false" blend="alpha" />
-    <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 9
Resources/CoreData/Techniques/DiffNormalPackedEmissive.xml

@@ -1,9 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="base" psdefines="EMISSIVEMAP" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL" />
-    <pass name="material" psdefines="MATERIAL EMISSIVEMAP" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP PACKEDNORMAL EMISSIVEMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 5
Resources/CoreData/Techniques/DiffNormalPackedEmissiveAlpha.xml

@@ -1,5 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="alpha" psdefines="EMISSIVEMAP" depthwrite="false" blend="alpha" />
-    <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 9
Resources/CoreData/Techniques/DiffNormalPackedEnvCube.xml

@@ -1,9 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="base" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="NORMALMAP PACKEDNORMAL ENVCUBEMAP" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL" />
-    <pass name="material" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="MATERIAL NORMALMAP PACKEDNORMAL ENVCUBEMAP" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="DEFERRED NORMALMAP PACKEDNORMAL ENVCUBEMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 5
Resources/CoreData/Techniques/DiffNormalPackedEnvCubeAlpha.xml

@@ -1,5 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="alpha" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="NORMALMAP PACKEDNORMAL ENVCUBEMAP" depthwrite="false" blend="alpha" />
-    <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 10
Resources/CoreData/Techniques/DiffNormalPackedSpec.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="base" />
-    <pass name="litbase" vsdefines="NORMALMAP" psdefines="AMBIENT NORMALMAP PACKEDNORMAL SPECMAP" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL SPECMAP" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL SPECMAP" />
-    <pass name="material" psdefines="MATERIAL SPECMAP" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP PACKEDNORMAL SPECMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 9
Resources/CoreData/Techniques/DiffNormalPackedSpecAO.xml

@@ -1,9 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="base" vsdefines="AO" psdefines="AO" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL SPECMAP" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL SPECMAP" />
-    <pass name="material" vsdefines="AO" psdefines="MATERIAL SPECMAP AO" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP AO" psdefines="DEFERRED NORMALMAP PACKEDNORMAL SPECMAP AO" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 5
Resources/CoreData/Techniques/DiffNormalPackedSpecAOAlpha.xml

@@ -1,5 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="alpha" vsdefines="AO" psdefines="AO" depthwrite="false" blend="alpha" />
-    <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL SPECMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 9
Resources/CoreData/Techniques/DiffNormalPackedSpecAOAlphaMask.xml

@@ -1,9 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true" >
-    <pass name="base" vsdefines="AO" psdefines="AO" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL SPECMAP" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL SPECMAP" />
-    <pass name="material" vsdefines="AO" psdefines="MATERIAL SPECMAP AO" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP AO" psdefines="DEFERRED NORMALMAP PACKEDNORMAL SPECMAP AO" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 0 - 5
Resources/CoreData/Techniques/DiffNormalPackedSpecAlpha.xml

@@ -1,5 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="alpha" depthwrite="false" blend="alpha" />
-    <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL SPECMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 10
Resources/CoreData/Techniques/DiffNormalPackedSpecAlphaMask.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true">
-    <pass name="base" />
-    <pass name="litbase" vsdefines="NORMALMAP" psdefines="AMBIENT NORMALMAP PACKEDNORMAL SPECMAP" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL SPECMAP" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL SPECMAP" />
-    <pass name="material" psdefines="MATERIAL SPECMAP" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP PACKEDNORMAL SPECMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 0 - 9
Resources/CoreData/Techniques/DiffNormalPackedSpecEmissive.xml

@@ -1,9 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="base" psdefines="EMISSIVEMAP" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL SPECMAP" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL SPECMAP" />
-    <pass name="material" psdefines="MATERIAL SPECMAP EMISSIVEMAP" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP PACKEDNORMAL SPECMAP EMISSIVEMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 5
Resources/CoreData/Techniques/DiffNormalPackedSpecEmissiveAlpha.xml

@@ -1,5 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
-    <pass name="alpha" psdefines="EMISSIVEMAP" depthwrite="false" blend="alpha" />
-    <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL SPECMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 2 - 2
Resources/CoreData/Techniques/DiffNormalSpec.xml

@@ -5,6 +5,6 @@
     <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP SPECMAP" />
     <pass name="material" psdefines="MATERIAL SPECMAP" depthtest="equal" depthwrite="false" />
     <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP SPECMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 2 - 2
Resources/CoreData/Techniques/DiffNormalSpecAO.xml

@@ -4,6 +4,6 @@
     <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP SPECMAP" />
     <pass name="material" vsdefines="AO" psdefines="MATERIAL SPECMAP AO" depthtest="equal" depthwrite="false" />
     <pass name="deferred" vsdefines="NORMALMAP AO" psdefines="DEFERRED NORMALMAP SPECMAP AO" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 1 - 1
Resources/CoreData/Techniques/DiffNormalSpecAOAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
     <pass name="alpha" vsdefines="AO" psdefines="AO" depthwrite="false" blend="alpha" />
     <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP SPECMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 0 - 9
Resources/CoreData/Techniques/DiffNormalSpecAOAlphaMask.xml

@@ -1,9 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true" >
-    <pass name="base" vsdefines="AO" psdefines="AO" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP SPECMAP" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP SPECMAP" />
-    <pass name="material" vsdefines="AO" psdefines="MATERIAL SPECMAP AO" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP AO" psdefines="DEFERRED NORMALMAP SPECMAP AO" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 1 - 1
Resources/CoreData/Techniques/DiffNormalSpecAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
     <pass name="alpha" depthwrite="false" blend="alpha" />
     <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP SPECMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 0 - 10
Resources/CoreData/Techniques/DiffNormalSpecAlphaMask.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true">
-    <pass name="base" />
-    <pass name="litbase" vsdefines="NORMALMAP" psdefines="AMBIENT NORMALMAP SPECMAP" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP SPECMAP" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP SPECMAP" />
-    <pass name="material" psdefines="MATERIAL SPECMAP" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP SPECMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 2 - 2
Resources/CoreData/Techniques/DiffNormalSpecEmissive.xml

@@ -4,6 +4,6 @@
     <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP SPECMAP" />
     <pass name="material" psdefines="MATERIAL SPECMAP EMISSIVEMAP" depthtest="equal" depthwrite="false" />
     <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP SPECMAP EMISSIVEMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 1 - 1
Resources/CoreData/Techniques/DiffNormalSpecEmissiveAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP">
     <pass name="alpha" psdefines="EMISSIVEMAP" depthwrite="false" blend="alpha" />
     <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP SPECMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 0 - 10
Resources/CoreData/Techniques/DiffSpecAlphaMask.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true" >
-    <pass name="base" />
-    <pass name="litbase" psdefines="AMBIENT SPECMAP" />
-    <pass name="light" psdefines="SPECMAP" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" psdefines="PREPASS SPECMAP" />
-    <pass name="material" psdefines="MATERIAL SPECMAP" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" psdefines="DEFERRED SPECMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 0 - 6
Resources/CoreData/Techniques/DiffUnlitAlphaMask.xml

@@ -1,6 +0,0 @@
-<technique vs="Unlit" ps="Unlit" psdefines="DIFFMAP ALPHAMASK" alphamask="true" >
-    <pass name="base" />
-    <pass name="prepass" psdefines="PREPASS" />
-    <pass name="material" />
-    <pass name="deferred" psdefines="DEFERRED" />
-</technique>

+ 2 - 2
Resources/CoreData/Techniques/NoTextureNormal.xml

@@ -5,6 +5,6 @@
     <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP" />
     <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
     <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 1 - 1
Resources/CoreData/Techniques/NoTextureNormalAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="LitSolid" ps="LitSolid">
     <pass name="alpha"  depthwrite="false" blend="alpha" />
     <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 0 - 10
Resources/CoreData/Techniques/NoTextureNormalPacked.xml

@@ -1,10 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid">
-    <pass name="base" />
-    <pass name="litbase" vsdefines="NORMALMAP" psdefines="AMBIENT NORMALMAP PACKEDNORMAL" />
-    <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL" />
-    <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP PACKEDNORMAL" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 0 - 5
Resources/CoreData/Techniques/NoTextureNormalPackedAlpha.xml

@@ -1,5 +0,0 @@
-<technique vs="LitSolid" ps="LitSolid">
-    <pass name="alpha"  depthwrite="false" blend="alpha" />
-    <pass name="litalpha" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
-</technique>

+ 2 - 2
Resources/CoreData/Techniques/PBR/DiffNormalSpecEmissive.xml

@@ -3,6 +3,6 @@
     <pass name="light" depthtest="equal" depthwrite="false" blend="add" />>
     <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
     <pass name="deferred" psdefines="DEFERRED" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 1 - 1
Resources/CoreData/Techniques/PBR/DiffNormalSpecEmissiveAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="PBRLitSolid" ps="PBRLitSolid" psdefines="DIFFMAP SPECMAP EMISSIVEMAP NORMALMAP">
     <pass name="alpha" depthwrite="false" blend="alpha" />
     <pass name="litalpha" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 2 - 2
Resources/CoreData/Techniques/PBR/PBRDiffNormal.xml

@@ -3,6 +3,6 @@
     <pass name="light" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
     <pass name="deferred" psdefines="DEFERRED" blend="add" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 1 - 1
Resources/CoreData/Techniques/PBR/PBRDiffNormalAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="PBRLitSolid" ps="PBRLitSolid" vsdefines="IBL" psdefines="DIFFMAP NORMALMAP PBR IBL">
     <pass name="alpha" depthwrite="false" blend="alpha" />
     <pass name="litalpha" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 2 - 2
Resources/CoreData/Techniques/PBR/PBRDiffNormalEmissive.xml

@@ -2,6 +2,6 @@
     <pass name="light" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
     <pass name="deferred" psdefines="DEFERRED" blend="add" />
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 1 - 1
Resources/CoreData/Techniques/PBR/PBRDiffNormalEmissiveAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="PBRLitSolid" ps="PBRLitSolid"  vsdefines="IBL" psdefines="DIFFMAP NORMALMAP EMISSIVEMAP PBR IBL">
     <pass name="alpha" depthwrite="false" blend="alpha" />
     <pass name="litalpha" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 2 - 2
Resources/CoreData/Techniques/PBR/PBRMetallicRoughDiffNormalSpec.xml

@@ -3,6 +3,6 @@
     <pass name="light" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
     <pass name="deferred" psdefines="DEFERRED" blend="add"/>
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 2 - 2
Resources/CoreData/Techniques/PBR/PBRMetallicRoughDiffNormalSpecEmissive.xml

@@ -3,6 +3,6 @@
     <pass name="light" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
     <pass name="deferred" psdefines="DEFERRED" blend="add"/>
-    <pass name="depth" vs="Depth" ps="Depth" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="depth" vs="Depth" ps="Depth" psexcludes="PACKEDNORMAL" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 1 - 1
Resources/CoreData/Techniques/PBR/PBRMetallicRoughDiffNormalSpecEmissiveAlpha.xml

@@ -1,5 +1,5 @@
 <technique vs="PBRLitSolid" ps="PBRLitSolid" vsdefines="IBL" psdefines="DIFFMAP NORMALMAP EMISSIVEMAP PBR IBL METALLIC ROUGHNESS">
     <pass name="alpha" depthwrite="false" blend="alpha" />
     <pass name="litalpha" depthwrite="false" blend="addalpha" />
-    <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" psexcludes="PACKEDNORMAL" />
 </technique>

+ 0 - 10
Resources/CoreData/Techniques/VegetationDiffAlphaMask.xml

@@ -1,10 +0,0 @@
-<technique vs="Vegetation" ps="LitSolid" psdefines="DIFFMAP ALPHAMASK" alphamask="true" >
-    <pass name="base" />
-    <pass name="litbase" psdefines="AMBIENT" />
-    <pass name="light" depthtest="equal" depthwrite="false" blend="add" />
-    <pass name="prepass" psdefines="PREPASS" />
-    <pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
-    <pass name="deferred" psdefines="DEFERRED" />
-    <pass name="depth" vs="VegetationDepth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="VegetationShadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 0 - 8
Resources/CoreData/Techniques/VegetationDiffUnlitAlphaMask.xml

@@ -1,8 +0,0 @@
-<technique vs="Vegetation" ps="Unlit" psdefines="DIFFMAP ALPHAMASK" alphamask="true" >
-    <pass name="base" />
-    <pass name="prepass" psdefines="PREPASS" />
-    <pass name="material" />
-    <pass name="deferred" psdefines="DEFERRED" />
-    <pass name="depth" vs="VegetationDepth" ps="Depth" psdefines="ALPHAMASK" />
-    <pass name="shadow" vs="VegetationShadow" ps="Shadow" psdefines="ALPHAMASK" />
-</technique>

+ 12 - 6
Source/Atomic/Atomic2D/Constraint2D.cpp

@@ -48,12 +48,6 @@ Constraint2D::Constraint2D(Context* context) :
 
 Constraint2D::~Constraint2D()
 {
-    if (ownerBody_)
-        ownerBody_->RemoveConstraint2D(this);
-
-    if (otherBody_)
-        otherBody_->RemoveConstraint2D(this);
-
     ReleaseJoint();
 }
 
@@ -80,6 +74,12 @@ void Constraint2D::CreateJoint()
     {
         joint_ = physicsWorld_->GetWorld()->CreateJoint(jointDef);
         joint_->SetUserData(this);
+
+        if (ownerBody_)
+            ownerBody_->AddConstraint2D(this);
+
+        if (otherBody_)
+            otherBody_->AddConstraint2D(this);
     }
 }
 
@@ -88,6 +88,12 @@ void Constraint2D::ReleaseJoint()
     if (!joint_)
         return;
 
+    if (ownerBody_)
+        ownerBody_->RemoveConstraint2D(this);
+
+    if (otherBody_)
+        otherBody_->RemoveConstraint2D(this);
+
     if (physicsWorld_)
         physicsWorld_->GetWorld()->DestroyJoint(joint_);
 

+ 2 - 2
Source/Atomic/Atomic2D/Constraint2D.h

@@ -47,9 +47,9 @@ public:
 
     /// Handle enabled/disabled state change.
     virtual void OnSetEnabled();
-    /// Create Joint.
+    /// Create joint.
     void CreateJoint();
-    /// Release Joint.
+    /// Release joint.
     void ReleaseJoint();
 
     /// Set other rigid body.

+ 21 - 29
Source/Atomic/Atomic2D/PhysicsEvents2D.h

@@ -24,40 +24,14 @@
 
 #include "../Core/Object.h"
 
-// ATOMIC BEGIN
-
-//  Discrete Physics2D Events
-
 // For prestep / poststep events, which are the same for 2D and 3D physics. The events themselves don't depend
 // on whether 3D physics support or Bullet has been compiled in.
-// #include "../Physics/PhysicsEvents.h"
-
-// ATOMIC END
+#include "../Physics/PhysicsEvents.h"
 
 namespace Atomic
 {
 
-
-// ATOMIC BEGIN
-
-/// Physics world is about to be stepped.
-ATOMIC_EVENT(E_PHYSICSPRESTEP2D, PhysicsPreStep2D)
-{
-    ATOMIC_PARAM(P_WORLD, World);                  // PhysicsWorld2D pointer
-    ATOMIC_PARAM(P_TIMESTEP, TimeStep);            // float
-}
-
-/// Physics world has been stepped.
-ATOMIC_EVENT(E_PHYSICSPOSTSTEP2D, PhysicsPostStep2D)
-{
-    ATOMIC_PARAM(P_WORLD, World);                  // PhysicsWorld2D pointer
-    ATOMIC_PARAM(P_TIMESTEP, TimeStep);            // float
-}
-
-// ATOMIC END
-
-
-/// Physics begin contact.
+/// Physics begin contact. Global event sent by PhysicsWorld2D.
 ATOMIC_EVENT(E_PHYSICSBEGINCONTACT2D, PhysicsBeginContact2D)
 {
     ATOMIC_PARAM(P_WORLD, World);                  // PhysicsWorld2D pointer
@@ -68,7 +42,7 @@ ATOMIC_EVENT(E_PHYSICSBEGINCONTACT2D, PhysicsBeginContact2D)
     ATOMIC_PARAM(P_CONTACT, Contact);              // b2Contact pointer
 }
 
-/// Physics end contact.
+/// Physics end contact. Global event sent by PhysicsWorld2D.
 ATOMIC_EVENT(E_PHYSICSENDCONTACT2D, PhysicsEndContact2D)
 {
     ATOMIC_PARAM(P_WORLD, World);                  // PhysicsWorld2D pointer
@@ -79,4 +53,22 @@ ATOMIC_EVENT(E_PHYSICSENDCONTACT2D, PhysicsEndContact2D)
     ATOMIC_PARAM(P_CONTACT, Contact);              // b2Contact pointer
 }
 
+/// Node begin contact. Sent by scene nodes participating in a collision.
+ATOMIC_EVENT(E_NODEBEGINCONTACT2D, NodeBeginContact2D)
+{
+    ATOMIC_PARAM(P_BODY, Body);                    // RigidBody2D pointer
+    ATOMIC_PARAM(P_OTHERNODE, OtherNode);          // Node pointer
+    ATOMIC_PARAM(P_OTHERBODY, OtherBody);          // RigidBody2D pointer
+    ATOMIC_PARAM(P_CONTACT, Contact);              // b2Contact pointer
+}
+
+/// Node end contact. Sent by scene nodes participating in a collision.
+ATOMIC_EVENT(E_NODEENDCONTACT2D, NodeEndContact2D)
+{
+    ATOMIC_PARAM(P_BODY, Body);                    // RigidBody2D pointer
+    ATOMIC_PARAM(P_OTHERNODE, OtherNode);          // Node pointer
+    ATOMIC_PARAM(P_OTHERBODY, OtherBody);          // RigidBody2D pointer
+    ATOMIC_PARAM(P_CONTACT, Contact);              // b2Contact pointer
+}
+
 }

+ 51 - 11
Source/Atomic/Atomic2D/PhysicsWorld2D.cpp

@@ -95,7 +95,12 @@ void PhysicsWorld2D::RegisterObject(Context* context)
     ATOMIC_ACCESSOR_ATTRIBUTE("Draw CenterOfMass", GetDrawCenterOfMass, SetDrawCenterOfMass, bool, false, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Allow Sleeping", GetAllowSleeping, SetAllowSleeping, bool, false, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Warm Starting", GetWarmStarting, SetWarmStarting, bool, false, AM_DEFAULT);
+
+    // ATOMIC BEGIN
+    // default false
     ATOMIC_ACCESSOR_ATTRIBUTE("Continuous Physics", GetContinuousPhysics, SetContinuousPhysics, bool, false, AM_DEFAULT);
+    // ATOMIC END
+
     ATOMIC_ACCESSOR_ATTRIBUTE("Sub Stepping", GetSubStepping, SetSubStepping, bool, false, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Gravity", GetGravity, SetGravity, Vector2, DEFAULT_GRAVITY, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Auto Clear Forces", GetAutoClearForces, SetAutoClearForces, bool, false, AM_DEFAULT);
@@ -232,17 +237,12 @@ void PhysicsWorld2D::Update(float timeStep)
 {
     ATOMIC_PROFILE(UpdatePhysics2D);
 
-// ATOMIC BEGIN
-
-    using namespace PhysicsPreStep2D;
-
+    using namespace PhysicsPreStep;
 
     VariantMap& eventData = GetEventDataMap();
     eventData[P_WORLD] = this;
     eventData[P_TIMESTEP] = timeStep;
-    SendEvent(E_PHYSICSPRESTEP2D, eventData);
-
-// ATOMIC END
+    SendEvent(E_PHYSICSPRESTEP, eventData);
 
     physicsStepping_ = true;
     world_->Step(timeStep, velocityIterations_, positionIterations_);
@@ -285,10 +285,8 @@ void PhysicsWorld2D::Update(float timeStep)
     SendBeginContactEvents();
     SendEndContactEvents();
 
-// ATOMIC BEGIN
-    using namespace PhysicsPostStep2D;
-    SendEvent(E_PHYSICSPOSTSTEP2D, eventData);
-// ATOMIC END
+    using namespace PhysicsPostStep;
+    SendEvent(E_PHYSICSPOSTSTEP, eventData);
 }
 
 void PhysicsWorld2D::DrawDebugGeometry()
@@ -686,6 +684,7 @@ void PhysicsWorld2D::SendBeginContactEvents()
 
     using namespace PhysicsBeginContact2D;
     VariantMap& eventData = GetEventDataMap();
+    VariantMap nodeEventData;
     eventData[P_WORLD] = this;
 
     for (unsigned i = 0; i < beginContactInfos_.Size(); ++i)
@@ -698,6 +697,26 @@ void PhysicsWorld2D::SendBeginContactEvents()
         eventData[P_CONTACT] = (void*)contactInfo.contact_;
 
         SendEvent(E_PHYSICSBEGINCONTACT2D, eventData);
+
+        nodeEventData[NodeBeginContact2D::P_CONTACT] = (void*)contactInfo.contact_;
+
+        if (contactInfo.nodeA_)
+        {
+            nodeEventData[NodeBeginContact2D::P_BODY] = contactInfo.bodyA_.Get();
+            nodeEventData[NodeBeginContact2D::P_OTHERNODE] = contactInfo.nodeB_.Get();
+            nodeEventData[NodeBeginContact2D::P_OTHERBODY] = contactInfo.bodyB_.Get();
+
+            contactInfo.nodeA_->SendEvent(E_NODEBEGINCONTACT2D, nodeEventData);
+        }
+
+        if (contactInfo.nodeB_)
+        {
+            nodeEventData[NodeBeginContact2D::P_BODY] = contactInfo.bodyB_.Get();
+            nodeEventData[NodeBeginContact2D::P_OTHERNODE] = contactInfo.nodeA_.Get();
+            nodeEventData[NodeBeginContact2D::P_OTHERBODY] = contactInfo.bodyA_.Get();
+
+            contactInfo.nodeB_->SendEvent(E_NODEBEGINCONTACT2D, nodeEventData);
+        }
     }
 
     beginContactInfos_.Clear();
@@ -710,6 +729,7 @@ void PhysicsWorld2D::SendEndContactEvents()
 
     using namespace PhysicsEndContact2D;
     VariantMap& eventData = GetEventDataMap();
+    VariantMap nodeEventData;
     eventData[P_WORLD] = this;
 
     for (unsigned i = 0; i < endContactInfos_.Size(); ++i)
@@ -722,6 +742,26 @@ void PhysicsWorld2D::SendEndContactEvents()
         eventData[P_CONTACT] = (void*)contactInfo.contact_;
 
         SendEvent(E_PHYSICSENDCONTACT2D, eventData);
+
+        nodeEventData[NodeEndContact2D::P_CONTACT] = (void*)contactInfo.contact_;
+
+        if (contactInfo.nodeA_)
+        {
+            nodeEventData[NodeEndContact2D::P_BODY] = contactInfo.bodyA_.Get();
+            nodeEventData[NodeEndContact2D::P_OTHERNODE] = contactInfo.nodeB_.Get();
+            nodeEventData[NodeEndContact2D::P_OTHERBODY] = contactInfo.bodyB_.Get();
+
+            contactInfo.nodeA_->SendEvent(E_NODEENDCONTACT2D, nodeEventData);
+        }
+
+        if (contactInfo.nodeB_)
+        {
+            nodeEventData[NodeEndContact2D::P_BODY] = contactInfo.bodyB_.Get();
+            nodeEventData[NodeEndContact2D::P_OTHERNODE] = contactInfo.nodeA_.Get();
+            nodeEventData[NodeEndContact2D::P_OTHERBODY] = contactInfo.bodyA_.Get();
+
+            contactInfo.nodeB_->SendEvent(E_NODEENDCONTACT2D, nodeEventData);
+        }
     }
 
     endContactInfos_.Clear();

+ 5 - 3
Source/Atomic/Atomic2D/RigidBody2D.cpp

@@ -402,10 +402,12 @@ void RigidBody2D::ReleaseBody()
     if (!physicsWorld_ || !physicsWorld_->GetWorld())
         return;
 
-    for (unsigned i = 0; i < constraints_.Size(); ++i)
+    // Make a copy for iteration
+    Vector<WeakPtr<Constraint2D> > constraints = constraints_;
+    for (unsigned i = 0; i < constraints.Size(); ++i)
     {
-        if (constraints_[i])
-            constraints_[i]->ReleaseJoint();
+        if (constraints[i])
+            constraints[i]->ReleaseJoint();
     }
 
     for (unsigned i = 0; i < collisionShapes_.Size(); ++i)

+ 4 - 0
Source/Atomic/Atomic2D/TileMapLayer2D.h

@@ -25,6 +25,10 @@
 #include "../Scene/Component.h"
 #include "../Atomic2D/TileMapDefs2D.h"
 
+#ifdef GetObject
+#undef GetObject
+#endif
+
 namespace Atomic
 {
 

+ 7 - 26
Source/Atomic/Audio/SoundSource.cpp

@@ -94,12 +94,11 @@ namespace Atomic
 
 #define GET_IP_SAMPLE_RIGHT() (((((int)pos[3] - (int)pos[1]) * fractPos) / 65536) + (int)pos[1])
 
-static const float AUTOREMOVE_DELAY = 0.25f;
-
 static const int STREAM_SAFETY_SAMPLES = 4;
 
 extern const char* AUDIO_CATEGORY;
 
+extern const char* autoRemoveModeNames[];
 
 SoundSource::SoundSource(Context* context) :
     Component(context),
@@ -108,9 +107,8 @@ SoundSource::SoundSource(Context* context) :
     gain_(1.0f),
     attenuation_(1.0f),
     panning_(0.0f),
-    autoRemoveTimer_(0.0f),
-    autoRemove_(false),
     sendFinishedEvent_(false),
+    autoRemove_(REMOVE_DISABLED),
     position_(0),
     fractPosition_(0),
     timePosition_(0.0f),
@@ -142,7 +140,7 @@ void SoundSource::RegisterObject(Context* context)
     ATOMIC_ATTRIBUTE("Attenuation", float, attenuation_, 1.0f, AM_DEFAULT);
     ATOMIC_ATTRIBUTE("Panning", float, panning_, 0.0f, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Is Playing", IsPlaying, SetPlayingAttr, bool, false, AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Autoremove on Stop", bool, autoRemove_, false, AM_FILE);
+    ATOMIC_ENUM_ATTRIBUTE("Autoremove Mode", autoRemove_, autoRemoveModeNames, REMOVE_DISABLED, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Play Position", GetPositionAttr, SetPositionAttr, int, 0, AM_FILE);
 }
 
@@ -283,12 +281,10 @@ void SoundSource::SetPanning(float panning)
     MarkNetworkUpdate();
 }
 
-void SoundSource::SetAutoRemove(bool enable)
+void SoundSource::SetAutoRemoveMode(AutoRemoveMode mode)
 {
-    if (enable == true)
-        ATOMIC_LOGWARNING("SoundSource::SetAutoRemove is deprecated. Consider using the SoundFinished event instead");
-
-    autoRemove_ = enable;
+    autoRemove_ = mode;
+    MarkNetworkUpdate();
 }
 
 bool SoundSource::IsPlaying() const
@@ -338,23 +334,8 @@ void SoundSource::Update(float timeStep)
 
         if (self.Expired())
             return;
-    }
 
-    // Check for autoremove
-    if (autoRemove_)
-    {
-        if (!playing)
-        {
-            autoRemoveTimer_ += timeStep;
-            if (autoRemoveTimer_ > AUTOREMOVE_DELAY)
-            {
-                Remove();
-                // Note: this object is now deleted, so only returning immediately is safe
-                return;
-            }
-        }
-        else
-            autoRemoveTimer_ = 0.0f;
+        DoAutoRemove(autoRemove_);
     }
 }
 

+ 6 - 8
Source/Atomic/Audio/SoundSource.h

@@ -70,8 +70,8 @@ public:
     void SetAttenuation(float attenuation);
     /// Set stereo panning. -1.0 is full left and 1.0 is full right.
     void SetPanning(float panning);
-    /// \deprecated Set whether sound source will be automatically removed from the scene node when playback stops. Note: this is deprecated, consider subscribing to the SoundFinished event instead.
-    ATOMIC_DEPRECATED void SetAutoRemove(bool enable);
+    //// Set to remove either the sound source component or its owner node from the scene automatically on sound playback completion. Disabled by default.
+    void SetAutoRemoveMode(AutoRemoveMode mode);
     /// Set new playback position.
     void SetPlayPosition(signed char* pos);
 
@@ -99,8 +99,8 @@ public:
     /// Return stereo panning.
     float GetPanning() const { return panning_; }
 
-    /// \deprecated Return autoremove mode.
-    ATOMIC_DEPRECATED bool GetAutoRemove() const { return autoRemove_; }
+    /// Return automatic removal mode on sound playback completion.
+    AutoRemoveMode GetAutoRemoveMode() const { return autoRemove_; }
 
     /// Return whether is playing.
     bool IsPlaying() const;
@@ -138,14 +138,12 @@ protected:
     float attenuation_;
     /// Stereo panning.
     float panning_;
-    /// Autoremove timer.
-    float autoRemoveTimer_;
     /// Effective master gain.
     float masterGain_;
-    /// Autoremove flag.
-    bool autoRemove_;
     /// Whether finished event should be sent on playback stop.
     bool sendFinishedEvent_;
+    /// Automatic removal mode.
+    AutoRemoveMode autoRemove_;
 
 private:
     /// Play a sound without locking the audio mutex. Called internally.

+ 6 - 2
Source/Atomic/Container/HashMap.h

@@ -262,8 +262,12 @@ public:
     /// Assign a hash map.
     HashMap& operator =(const HashMap<T, U>& rhs)
     {
-        Clear();
-        Insert(rhs);
+        // In case of self-assignment do nothing
+        if (&rhs != this)
+        {
+            Clear();
+            Insert(rhs);
+        }
         return *this;
     }
 

+ 6 - 2
Source/Atomic/Container/HashSet.h

@@ -217,8 +217,12 @@ public:
     /// Assign a hash set.
     HashSet& operator =(const HashSet<T>& rhs)
     {
-        Clear();
-        Insert(rhs);
+        // In case of self-assignment do nothing
+        if (&rhs != this)
+        {
+            Clear();
+            Insert(rhs);
+        }
         return *this;
     }
 

+ 6 - 3
Source/Atomic/Container/List.h

@@ -209,9 +209,12 @@ public:
     /// Assign from another list.
     List& operator =(const List<T>& rhs)
     {
-        // Clear, then insert the nodes of the other list
-        Clear();
-        Insert(End(), rhs);
+        // Clear, then insert the nodes of the other list. In case of self-assignment do nothing
+        if (&rhs != this)
+        {
+            Clear();
+            Insert(End(), rhs);
+        }
         return *this;
     }
 

+ 135 - 142
Source/Atomic/Container/Vector.h

@@ -55,13 +55,21 @@ public:
     /// Construct with initial size.
     explicit Vector(unsigned size)
     {
-        Resize(size, 0);
+        Resize(size);
+    }
+
+    /// Construct with initial size and default value.
+    Vector(unsigned size, const T& value)
+    {
+        Resize(size);
+        for (unsigned i = 0; i < size; ++i)
+            At(i) = value;
     }
 
     /// Construct with initial data.
     Vector(const T* data, unsigned size)
     {
-        Resize(size, data);
+        InsertElements(0, data, data + size);
     }
 
     /// Construct from another vector.
@@ -82,15 +90,19 @@ public:
     /// Destruct.
     ~Vector()
     {
-        Clear();
+        DestructElements(Buffer(), size_);
         delete[] buffer_;
     }
 
     /// Assign from another vector.
     Vector<T>& operator =(const Vector<T>& rhs)
     {
-        Clear();
-        Resize(rhs.size_, rhs.Buffer());
+        // In case of self-assignment do nothing
+        if (&rhs != this)
+        {
+            Clear();
+            InsertElements(0, rhs.Begin(), rhs.End());
+        }
         return *this;
     }
 
@@ -188,105 +200,68 @@ public:
 
     /// Add an element at the end.
 #ifndef COVERITY_SCAN_MODEL
-    void Push(const T& value) { Resize(size_ + 1, &value); }
+    void Push(const T& value)
+    {
+        InsertElements(size_, &value, &value + 1);
+    }
 #else
     // FIXME: Attempt had been made to use this model in the Coverity-Scan model file without any success
     // Probably because the model had generated a different mangled name than the one used by static analyzer
     void Push(const T& value)
     {
         T array[] = {value};
-        Resize(size_ + 1, array);
+        InsertElements(size_, array, array + 1);
     }
 #endif
 
     /// Add another vector at the end.
-    void Push(const Vector<T>& vector) { Resize(size_ + vector.size_, vector.Buffer()); }
+    void Push(const Vector<T>& vector) { InsertElements(size_, vector.Begin(), vector.End()); }
 
     /// Remove the last element.
     void Pop()
     {
         if (size_)
-            Resize(size_ - 1, 0);
+            Resize(size_ - 1);
     }
 
     /// Insert an element at position.
     void Insert(unsigned pos, const T& value)
     {
-        if (pos > size_)
-            pos = size_;
-
-        unsigned oldSize = size_;
-        Resize(size_ + 1, 0);
-        MoveRange(pos + 1, pos, oldSize - pos);
-        Buffer()[pos] = value;
+        InsertElements(pos, &value, &value + 1);
     }
 
     /// Insert another vector at position.
     void Insert(unsigned pos, const Vector<T>& vector)
     {
-        if (pos > size_)
-            pos = size_;
-
-        unsigned oldSize = size_;
-        Resize(size_ + vector.size_, 0);
-        MoveRange(pos + vector.size_, pos, oldSize - pos);
-        CopyElements(Buffer() + pos, vector.Buffer(), vector.size_);
+        InsertElements(pos, vector.Begin(), vector.End());
     }
 
     /// Insert an element by iterator.
     Iterator Insert(const Iterator& dest, const T& value)
     {
         unsigned pos = (unsigned)(dest - Begin());
-        if (pos > size_)
-            pos = size_;
-        Insert(pos, value);
-
-        return Begin() + pos;
+        return InsertElements(pos, &value, &value + 1);
     }
 
     /// Insert a vector by iterator.
     Iterator Insert(const Iterator& dest, const Vector<T>& vector)
     {
         unsigned pos = (unsigned)(dest - Begin());
-        if (pos > size_)
-            pos = size_;
-        Insert(pos, vector);
-
-        return Begin() + pos;
+        return InsertElements(pos, vector.Begin(), vector.End());
     }
 
     /// Insert a vector partially by iterators.
     Iterator Insert(const Iterator& dest, const ConstIterator& start, const ConstIterator& end)
     {
         unsigned pos = (unsigned)(dest - Begin());
-        if (pos > size_)
-            pos = size_;
-        unsigned length = (unsigned)(end - start);
-        Resize(size_ + length, 0);
-        MoveRange(pos + length, pos, size_ - pos - length);
-
-        T* destPtr = Buffer() + pos;
-        for (ConstIterator it = start; it != end; ++it)
-            *destPtr++ = *it;
-
-        return Begin() + pos;
+        return InsertElements(pos, start, end);
     }
 
     /// Insert elements.
     Iterator Insert(const Iterator& dest, const T* start, const T* end)
     {
         unsigned pos = (unsigned)(dest - Begin());
-        if (pos > size_)
-            pos = size_;
-        unsigned length = (unsigned)(end - start);
-        Resize(size_ + length, 0);
-        MoveRange(pos + length, pos, size_ - pos - length);
-
-        T* destPtr = Buffer() + pos;
-        for (const T* i = start; i != end; ++i)
-            *destPtr++ = *i;
-
-        return Begin() + pos;
+        return InsertElements(pos, start, end);
     }
 
     /// Erase a range of elements.
@@ -297,7 +272,7 @@ public:
             return;
 
         MoveRange(pos, pos + length, size_ - pos - length);
-        Resize(size_ - length, 0);
+        Resize(size_ - length);
     }
 
     /// Erase a range of elements by swapping elements from the end of the array.
@@ -320,7 +295,7 @@ public:
             // Swap elements from the end of the array into the empty space
             CopyElements(Buffer() + pos, Buffer() + newSize, length);
         }
-        Resize(newSize, 0);
+        Resize(newSize);
     }
 
     /// Erase an element by iterator. Return iterator to the next element.
@@ -376,7 +351,7 @@ public:
     void Clear() { Resize(0); }
 
     /// Resize the vector.
-    void Resize(unsigned newSize) { Resize(newSize, 0); }
+    void Resize(unsigned newSize) { Vector<T> tempBuffer; Resize(newSize, 0, tempBuffer); }
 
     /// Set new capacity.
     void Reserve(unsigned newCapacity)
@@ -480,8 +455,8 @@ public:
     T* Buffer() const { return reinterpret_cast<T*>(buffer_); }
 
 private:
-    /// Resize the vector and create/remove new elements as necessary.
-    void Resize(unsigned newSize, const T* src)
+    /// Resize the vector and create/remove new elements as necessary. Current buffer will be stored in tempBuffer in case of reallocation.
+    void Resize(unsigned newSize, const T* src, Vector<T>& tempBuffer)
     {
         // If size shrinks, destruct the removed elements
         if (newSize < size_)
@@ -491,6 +466,10 @@ private:
             // Allocate new buffer if necessary and copy the current elements
             if (newSize > capacity_)
             {
+                Swap(tempBuffer);
+                size_ = tempBuffer.size_;
+                capacity_ = tempBuffer.capacity_;
+
                 if (!capacity_)
                     capacity_ = newSize;
                 else
@@ -499,14 +478,11 @@ private:
                         capacity_ += (capacity_ + 1) >> 1;
                 }
 
-                unsigned char* newBuffer = AllocateBuffer((unsigned)(capacity_ * sizeof(T)));
-                if (buffer_)
+                buffer_ = AllocateBuffer((unsigned)(capacity_ * sizeof(T)));
+                if (tempBuffer.Buffer())
                 {
-                    ConstructElements(reinterpret_cast<T*>(newBuffer), Buffer(), size_);
-                    DestructElements(Buffer(), size_);
-                    delete[] buffer_;
+                    ConstructElements(Buffer(), tempBuffer.Buffer(), size_);
                 }
-                buffer_ = newBuffer;
             }
 
             // Initialize the new elements
@@ -516,6 +492,26 @@ private:
         size_ = newSize;
     }
 
+    /// Insert elements.
+    template <typename RandomIteratorT>
+    Iterator InsertElements(unsigned pos, RandomIteratorT start, RandomIteratorT end)
+    {
+        assert(start <= end);
+
+        if (pos > size_)
+            pos = size_;
+        unsigned length = (unsigned)(end - start);
+        Vector<T> tempBuffer;
+        Resize(size_ + length, 0, tempBuffer);
+        MoveRange(pos + length, pos, size_ - pos - length);
+
+        T* destPtr = Buffer() + pos;
+        for (RandomIteratorT it = start; it != end; ++it)
+            *destPtr++ = *it;
+
+        return Begin() + pos;
+    }
+
     /// Move a range of elements within the vector.
     void MoveRange(unsigned dest, unsigned src, unsigned count)
     {
@@ -584,11 +580,18 @@ public:
         Resize(size);
     }
 
+    /// Construct with initial size and default value.
+    PODVector(unsigned size, const T& value)
+    {
+        Resize(size);
+        for (unsigned i = 0; i < size; ++i)
+            At(i) = value;
+    }
+
     /// Construct with initial data.
     PODVector(const T* data, unsigned size)
     {
-        Resize(size);
-        CopyElements(Buffer(), data, size);
+        InsertElements(0, data, data + size);
     }
 
     /// Construct from another vector.
@@ -615,8 +618,12 @@ public:
     /// Assign from another vector.
     PODVector<T>& operator =(const PODVector<T>& rhs)
     {
-        Resize(rhs.size_);
-        CopyElements(Buffer(), rhs.Buffer(), rhs.size_);
+        // In case of self-assignment do nothing
+        if (&rhs != this)
+        {
+            Clear();
+            InsertElements(0, rhs.Begin(), rhs.End());
+        }
         return *this;
     }
 
@@ -715,19 +722,13 @@ public:
     /// Add an element at the end.
     void Push(const T& value)
     {
-        if (size_ < capacity_)
-            ++size_;
-        else
-            Resize(size_ + 1);
-        Back() = value;
+        InsertElements(size_, &value, &value + 1);
     }
 
     /// Add another vector at the end.
     void Push(const PODVector<T>& vector)
     {
-        unsigned oldSize = size_;
-        Resize(size_ + vector.size_);
-        CopyElements(Buffer() + oldSize, vector.Buffer(), vector.size_);
+        InsertElements(size_, vector.Begin(), vector.End());
     }
 
     /// Remove the last element.
@@ -740,78 +741,41 @@ public:
     /// Insert an element at position.
     void Insert(unsigned pos, const T& value)
     {
-        if (pos > size_)
-            pos = size_;
-
-        unsigned oldSize = size_;
-        Resize(size_ + 1);
-        MoveRange(pos + 1, pos, oldSize - pos);
-        Buffer()[pos] = value;
+        InsertElements(pos, &value, &value + 1);
     }
 
     /// Insert another vector at position.
     void Insert(unsigned pos, const PODVector<T>& vector)
     {
-        if (pos > size_)
-            pos = size_;
-
-        unsigned oldSize = size_;
-        Resize(size_ + vector.size_);
-        MoveRange(pos + vector.size_, pos, oldSize - pos);
-        CopyElements(Buffer() + pos, vector.Buffer(), vector.size_);
+        InsertElements(pos, vector.Begin(), vector.End());
     }
 
     /// Insert an element by iterator.
     Iterator Insert(const Iterator& dest, const T& value)
     {
         unsigned pos = (unsigned)(dest - Begin());
-        if (pos > size_)
-            pos = size_;
-        Insert(pos, value);
-
-        return Begin() + pos;
+        return InsertElements(pos, &value, &value + 1);
     }
 
     /// Insert a vector by iterator.
     Iterator Insert(const Iterator& dest, const PODVector<T>& vector)
     {
         unsigned pos = (unsigned)(dest - Begin());
-        if (pos > size_)
-            pos = size_;
-        Insert(pos, vector);
-
-        return Begin() + pos;
+        return InsertElements(pos, vector.Begin(), vector.End());
     }
 
     /// Insert a vector partially by iterators.
     Iterator Insert(const Iterator& dest, const ConstIterator& start, const ConstIterator& end)
     {
         unsigned pos = (unsigned)(dest - Begin());
-        if (pos > size_)
-            pos = size_;
-        unsigned length = (unsigned)(end - start);
-        Resize(size_ + length);
-        MoveRange(pos + length, pos, size_ - pos - length);
-        CopyElements(Buffer() + pos, &(*start), length);
-
-        return Begin() + pos;
+        return InsertElements(pos, start, end);
     }
 
     /// Insert elements.
     Iterator Insert(const Iterator& dest, const T* start, const T* end)
     {
         unsigned pos = (unsigned)(dest - Begin());
-        if (pos > size_)
-            pos = size_;
-        unsigned length = (unsigned)(end - start);
-        Resize(size_ + length);
-        MoveRange(pos + length, pos, size_ - pos - length);
-
-        T* destPtr = Buffer() + pos;
-        for (const T* i = start; i != end; ++i)
-            *destPtr++ = *i;
-
-        return Begin() + pos;
+        return InsertElements(pos, start, end);
     }
 
     /// Erase a range of elements.
@@ -903,27 +867,8 @@ public:
     /// Resize the vector.
     void Resize(unsigned newSize)
     {
-        if (newSize > capacity_)
-        {
-            if (!capacity_)
-                capacity_ = newSize;
-            else
-            {
-                while (capacity_ < newSize)
-                    capacity_ += (capacity_ + 1) >> 1;
-            }
-
-            unsigned char* newBuffer = AllocateBuffer((unsigned)(capacity_ * sizeof(T)));
-            // Move the data into the new buffer and delete the old
-            if (buffer_)
-            {
-                CopyElements(reinterpret_cast<T*>(newBuffer), Buffer(), size_);
-                delete[] buffer_;
-            }
-            buffer_ = newBuffer;
-        }
-
-        size_ = newSize;
+        PODVector<T> tempBuffer;
+        Resize(newSize, tempBuffer);
     }
 
     /// Set new capacity.
@@ -1019,6 +964,54 @@ public:
     T* Buffer() const { return reinterpret_cast<T*>(buffer_); }
 
 private:
+    /// Resize the vector. Current buffer will be stored in tempBuffer in case of reallocation.
+    void Resize(unsigned newSize, PODVector<T>& tempBuffer)
+    {
+        if (newSize > capacity_)
+        {
+            Swap(tempBuffer);
+            size_ = tempBuffer.size_;
+            capacity_ = tempBuffer.capacity_;
+
+            if (!capacity_)
+                capacity_ = newSize;
+            else
+            {
+                while (capacity_ < newSize)
+                    capacity_ += (capacity_ + 1) >> 1;
+            }
+
+            buffer_ = AllocateBuffer((unsigned)(capacity_ * sizeof(T)));
+            // Move the data into the new buffer
+            if (tempBuffer.Buffer())
+            {
+                CopyElements(Buffer(), tempBuffer.Buffer(), size_);
+            }
+        }
+
+        size_ = newSize;
+    }
+
+    /// Insert elements.
+    template <typename RandomIteratorT>
+    Iterator InsertElements(unsigned pos, RandomIteratorT start, RandomIteratorT end)
+    {
+        assert(start <= end);
+
+        if (pos > size_)
+            pos = size_;
+        unsigned length = (unsigned)(end - start);
+        PODVector<T> tempBuffer;
+        Resize(size_ + length, tempBuffer);
+        MoveRange(pos + length, pos, size_ - pos - length);
+
+        T* destPtr = Buffer() + pos;
+        for (RandomIteratorT i = start; i != end; ++i)
+            *destPtr++ = *i;
+
+        return Begin() + pos;
+    }
+
     /// Move a range of elements within the vector.
     void MoveRange(unsigned dest, unsigned src, unsigned count)
     {

+ 2 - 2
Source/Atomic/Core/Context.cpp

@@ -286,7 +286,7 @@ void Context::RemoveEventReceiver(Object* receiver, Object* sender, StringHash e
 
 void Context::BeginSendEvent(Object* sender, StringHash eventType)
 {
-#ifdef URHO3D_PROFILING
+#ifdef ATOMIC_PROFILING
     if (EventProfiler::IsActive())
     {
         EventProfiler* eventProfiler = GetSubsystem<EventProfiler>();
@@ -302,7 +302,7 @@ void Context::EndSendEvent()
 {
     eventSenders_.Pop();
 
-#ifdef URHO3D_PROFILING
+#ifdef ATOMIC_PROFILING
     if (EventProfiler::IsActive())
     {
         EventProfiler* eventProfiler = GetSubsystem<EventProfiler>();

+ 6 - 8
Source/Atomic/Graphics/AnimatedModel.cpp

@@ -111,14 +111,6 @@ void AnimatedModel::RegisterObject(Context* context)
         Variant::emptyVariantVector, AM_FILE);
     ATOMIC_ACCESSOR_ATTRIBUTE("Morphs", GetMorphsAttr, SetMorphsAttr, PODVector<unsigned char>, Variant::emptyBuffer,
         AM_DEFAULT | AM_NOEDIT);
-
-    // ATOMIC BEGIN
-
-    ATOMIC_ACCESSOR_ATTRIBUTE("Geometry Enabled", GetGeometryEnabledAttr, SetGeometryEnabledAttr, VariantVector,
-        Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
-
-    // ATOMIC END
-
 }
 
 bool AnimatedModel::Load(Deserializer& source, bool setInstanceDefault)
@@ -396,6 +388,12 @@ void AnimatedModel::SetModel(Model* model, bool createBones)
     if (model == model_)
         return;
 
+    if (!node_)
+    {
+        ATOMIC_LOGERROR("Can not set model while model component is not attached to a scene node");
+        return;
+    }
+
     // Unsubscribe from the reload event of previous model (if any), then subscribe to the new
     if (model_)
         UnsubscribeFromEvent(model_, E_RELOADFINISHED);

+ 6 - 1
Source/Atomic/Graphics/AnimationController.cpp

@@ -28,6 +28,7 @@
 #include "../Graphics/Animation.h"
 #include "../Graphics/AnimationController.h"
 #include "../Graphics/AnimationState.h"
+#include "../IO/FileSystem.h"
 #include "../IO/Log.h"
 #include "../IO/MemoryBuffer.h"
 #include "../Resource/ResourceCache.h"
@@ -412,6 +413,10 @@ bool AnimationController::SetWeight(const String& name, float weight)
     animations_[index].setWeight_ = (unsigned char)(weight * 255.0f);
     animations_[index].setWeightTtl_ = COMMAND_STAY_TIME;
     ++animations_[index].setWeightRev_;
+    // Cancel any ongoing weight fade
+    animations_[index].targetWeight_ = weight;
+    animations_[index].fadeTime_ = 0.0f;
+
     MarkNetworkUpdate();
     return true;
 }
@@ -912,7 +917,7 @@ void AnimationController::RemoveAnimationState(AnimationState* state)
 
 void AnimationController::FindAnimation(const String& name, unsigned& index, AnimationState*& state) const
 {
-    StringHash nameHash(name);
+    StringHash nameHash(GetInternalPath(name));
 
 // ATOMIC BEGIN
 

+ 4 - 6
Source/Atomic/Graphics/Batch.cpp

@@ -86,7 +86,7 @@ void CalculateShadowMatrix(Matrix4& dest, LightBatchQueue* queue, unsigned split
     const IntRect& viewport = queue->shadowSplits_[split].shadowViewport_;
 
     Matrix3x4 shadowView(shadowCamera->GetView());
-    Matrix4 shadowProj(shadowCamera->GetProjection());
+    Matrix4 shadowProj(shadowCamera->GetGPUProjection());
     Matrix4 texAdjust(Matrix4::IDENTITY);
 
     Texture2D* shadowMap = queue->shadowMap_;
@@ -163,11 +163,9 @@ void Batch::CalculateSortKey()
 {
     unsigned shaderID = (unsigned)(
         ((*((unsigned*)&vertexShader_) / sizeof(ShaderVariation)) + (*((unsigned*)&pixelShader_) / sizeof(ShaderVariation))) &
-        0x3fff);
+        0x7fff);
     if (!isBase_)
         shaderID |= 0x8000;
-    if (pass_ && pass_->GetAlphaMask())
-        shaderID |= 0x4000;
 
     unsigned lightQueueID = (unsigned)((*((unsigned*)&lightQueue_) / sizeof(LightBatchQueue)) & 0xffff);
     unsigned materialID = (unsigned)((*((unsigned*)&material_) / sizeof(Material)) & 0xffff);
@@ -203,7 +201,7 @@ void Batch::Prepare(View* view, Camera* camera, bool setModelTransform, bool all
             else if (blend == BLEND_ADDALPHA)
                 blend = BLEND_SUBTRACTALPHA;
         }
-        graphics->SetBlendMode(blend);
+        graphics->SetBlendMode(blend, pass_->GetAlphaToCoverage() || material_->GetAlphaToCoverage());
 
         bool isShadowPass = pass_->GetIndex() == Technique::shadowPassIndex;
         CullMode effectiveCullMode = pass_->GetCullMode();
@@ -800,7 +798,7 @@ void BatchQueue::SortFrontToBack2Pass(PODVector<Batch*>& batches)
             shaderID = j->second_;
         else
         {
-            shaderID = shaderRemapping_[shaderID] = freeShaderID | (shaderID & 0xc0000000);
+            shaderID = shaderRemapping_[shaderID] = freeShaderID | (shaderID & 0x80000000);
             ++freeShaderID;
         }
 

+ 12 - 3
Source/Atomic/Graphics/BillboardSet.cpp

@@ -52,6 +52,7 @@ const char* faceCameraModeNames[] =
     "Rotate Y",
     "LookAt XYZ",
     "LookAt Y",
+    "LookAt Mixed",
     "Direction",
     0
 };
@@ -80,6 +81,7 @@ BillboardSet::BillboardSet(Context* context) :
     sorted_(false),
     fixedScreenSize_(false),
     faceCameraMode_(FC_ROTATE_XYZ),
+    minAngle_(0.0f),
     geometry_(new Geometry(context)),
     vertexBuffer_(new VertexBuffer(context_)),
     indexBuffer_(new IndexBuffer(context_)),
@@ -119,6 +121,7 @@ void BillboardSet::RegisterObject(Context* context)
     ATOMIC_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
     ATOMIC_ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
     ATOMIC_ENUM_ACCESSOR_ATTRIBUTE("Face Camera Mode", GetFaceCameraMode, SetFaceCameraMode, FaceCameraMode, faceCameraModeNames, FC_ROTATE_XYZ, AM_DEFAULT);
+    ATOMIC_ACCESSOR_ATTRIBUTE("Min Angle", GetMinAngle, SetMinAngle, float, 0.0f, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
@@ -216,7 +219,7 @@ void BillboardSet::UpdateBatches(const FrameInfo& frame)
     transforms_[0] = relative_ ? node_->GetWorldTransform() : Matrix3x4::IDENTITY;
     // Billboard rotation
     transforms_[1] = Matrix3x4(Vector3::ZERO, faceCameraMode_ != FC_NONE ? frame.camera_->GetFaceCameraRotation(
-        node_->GetWorldPosition(), node_->GetWorldRotation(), faceCameraMode_) : node_->GetWorldRotation(), Vector3::ONE);
+        node_->GetWorldPosition(), node_->GetWorldRotation(), faceCameraMode_, minAngle_) : node_->GetWorldRotation(), Vector3::ONE);
 }
 
 void BillboardSet::UpdateGeometry(const FrameInfo& frame)
@@ -229,7 +232,7 @@ void BillboardSet::UpdateGeometry(const FrameInfo& frame)
     if (faceCameraMode_ != FC_NONE)
     {
         transforms_[1] = Matrix3x4(Vector3::ZERO, frame.camera_->GetFaceCameraRotation(node_->GetWorldPosition(),
-            node_->GetWorldRotation(), faceCameraMode_), Vector3::ONE);
+            node_->GetWorldRotation(), faceCameraMode_, minAngle_), Vector3::ONE);
     }
 
     if (bufferSizeDirty_ || indexBuffer_->IsDataLost())
@@ -332,6 +335,12 @@ void BillboardSet::SetFaceCameraMode(FaceCameraMode mode)
     }
 }
 
+void BillboardSet::SetMinAngle(float angle)
+{
+    minAngle_ = angle;
+    MarkNetworkUpdate();
+}
+
 void BillboardSet::SetAnimationLodBias(float bias)
 {
     animationLodBias_ = Max(bias, 0.0f);
@@ -753,7 +762,7 @@ void BillboardSet::CalculateFixedScreenSize(const FrameInfo& frame)
 
     if (!frame.camera_->IsOrthographic())
     {
-        Matrix4 viewProj(frame.camera_->GetProjection(false) * frame.camera_->GetView());
+        Matrix4 viewProj(frame.camera_->GetProjection() * frame.camera_->GetView());
         const Matrix3x4& worldTransform = node_->GetWorldTransform();
         Matrix3x4 billboardTransform = relative_ ? worldTransform : Matrix3x4::IDENTITY;
 

+ 7 - 0
Source/Atomic/Graphics/BillboardSet.h

@@ -129,6 +129,8 @@ public:
     void SetFixedScreenSize(bool enable);
     /// Set how the billboards should rotate in relation to the camera. Default is to follow camera rotation on all axes (FC_ROTATE_XYZ.)
     void SetFaceCameraMode(FaceCameraMode mode);
+    /// Set minimal angle between billboard normal and look-at direction.
+    void SetMinAngle(float angle);
     /// Set animation LOD bias.
     void SetAnimationLodBias(float bias);
     /// Mark for bounding box and vertex buffer update. Call after modifying the billboards.
@@ -161,6 +163,9 @@ public:
     /// Return how the billboards rotate in relation to the camera.
     FaceCameraMode GetFaceCameraMode() const { return faceCameraMode_; }
 
+    /// Return minimal angle between billboard normal and look-at direction.
+    float GetMinAngle() const { return minAngle_; }
+
     /// Return animation LOD bias.
     float GetAnimationLodBias() const { return animationLodBias_; }
 
@@ -201,6 +206,8 @@ protected:
     bool fixedScreenSize_;
     /// Billboard rotation mode in relation to the camera.
     FaceCameraMode faceCameraMode_;
+    /// Minimal angle between billboard normal and look-at direction.
+    float minAngle_;
 
 private:
     /// Resize billboard vertex and index buffers.

+ 183 - 141
Source/Atomic/Graphics/Camera.cpp

@@ -72,7 +72,8 @@ Camera::Camera(Context* context) :
     autoAspectRatio_(true),
     flipVertical_(false),
     useReflection_(false),
-    useClipping_(false)
+    useClipping_(false),
+    customProjection_(false)
 {
     reflectionMatrix_ = reflectionPlane_.ReflectionMatrix();
 }
@@ -241,71 +242,144 @@ void Camera::SetUseClipping(bool enable)
 void Camera::SetClipPlane(const Plane& plane)
 {
     clipPlane_ = plane;
-    projectionDirty_ = true;
     MarkNetworkUpdate();
 }
 
-
 void Camera::SetFlipVertical(bool enable)
 {
     flipVertical_ = enable;
-    projectionDirty_ = true;
+    MarkNetworkUpdate();
+}
+
+void Camera::SetProjection(const Matrix4& projection)
+{
+    projection_ = projection;
+    Matrix4 projInverse = projection_.Inverse();
+
+    // Calculate the actual near & far clip from the custom matrix
+    projNearClip_ = (projInverse * Vector3(0.0f, 0.0f, 0.0f)).z_;
+    projFarClip_ = (projInverse * Vector3(0.0f, 0.0f, 1.0f)).z_;
+    projectionDirty_ = false;
+    autoAspectRatio_ = false;
+    frustumDirty_ = true;
+    customProjection_ = true;
+    // Called due to autoAspectRatio changing state, the projection itself is not serialized
     MarkNetworkUpdate();
 }
 
 float Camera::GetNearClip() const
 {
-    // Orthographic camera has always near clip at 0 to avoid trouble with shader depth parameters,
-    // and unlike in perspective mode there should be no depth buffer precision issue
-    if (!orthographic_)
-        return nearClip_;
-    else
-        return 0.0f;
+    if (projectionDirty_)
+        UpdateProjection();
+
+    return projNearClip_;
+}
+
+float Camera::GetFarClip() const
+{
+    if (projectionDirty_)
+        UpdateProjection();
+
+    return projFarClip_;
+}
+
+const Frustum& Camera::GetFrustum() const
+{
+    // Use projection_ instead of GetProjection() so that Y-flip has no effect. Update first if necessary
+    if (projectionDirty_)
+        UpdateProjection();
+
+    if (frustumDirty_)
+    {
+        if (customProjection_)
+            frustum_.Define(projection_ * GetView());
+        else
+        {
+            // If not using a custom projection, prefer calculating frustum from projection parameters instead of matrix
+            // for better accuracy
+            if (!orthographic_)
+                frustum_.Define(fov_, aspectRatio_, zoom_, GetNearClip(), GetFarClip(), GetEffectiveWorldTransform());
+            else
+                frustum_.DefineOrtho(orthoSize_, aspectRatio_, zoom_, GetNearClip(), GetFarClip(), GetEffectiveWorldTransform());
+        }
+
+        frustumDirty_ = false;
+    }
+
+    return frustum_;
 }
 
 Frustum Camera::GetSplitFrustum(float nearClip, float farClip) const
 {
-    Frustum ret;
+    if (projectionDirty_)
+        UpdateProjection();
 
-    Matrix3x4 worldTransform = GetEffectiveWorldTransform();
-    nearClip = Max(nearClip, GetNearClip());
-    farClip = Min(farClip, farClip_);
+    nearClip = Max(nearClip, projNearClip_);
+    farClip = Min(farClip, projFarClip_);
     if (farClip < nearClip)
         farClip = nearClip;
 
-    if (!orthographic_)
-        ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip, worldTransform);
+    Frustum ret;
+
+    if (customProjection_)
+    {
+        // DefineSplit() needs to project the near & far distances, so can not use a combined view-projection matrix.
+        // Transform to world space afterward instead
+        ret.DefineSplit(projection_, nearClip, farClip);
+        ret.Transform(GetEffectiveWorldTransform());
+    }
     else
-        ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip, worldTransform);
+    {
+        if (!orthographic_)
+            ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip, GetEffectiveWorldTransform());
+        else
+            ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip, GetEffectiveWorldTransform());
+    }
 
     return ret;
 }
 
 Frustum Camera::GetViewSpaceFrustum() const
 {
+    if (projectionDirty_)
+        UpdateProjection();
+
     Frustum ret;
 
-    if (!orthographic_)
-        ret.Define(fov_, aspectRatio_, zoom_, GetNearClip(), farClip_);
+    if (customProjection_)
+        ret.Define(projection_);
     else
-        ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, GetNearClip(), farClip_);
+    {
+        if (!orthographic_)
+            ret.Define(fov_, aspectRatio_, zoom_, GetNearClip(), GetFarClip());
+        else
+            ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, GetNearClip(), GetFarClip());
+    }
 
     return ret;
 }
 
 Frustum Camera::GetViewSpaceSplitFrustum(float nearClip, float farClip) const
 {
-    Frustum ret;
+    if (projectionDirty_)
+        UpdateProjection();
 
-    nearClip = Max(nearClip, GetNearClip());
-    farClip = Min(farClip, farClip_);
+    nearClip = Max(nearClip, projNearClip_);
+    farClip = Min(farClip, projFarClip_);
     if (farClip < nearClip)
         farClip = nearClip;
 
-    if (!orthographic_)
-        ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip);
+    Frustum ret;
+
+    if (customProjection_)
+        ret.DefineSplit(projection_, nearClip, farClip);
     else
-        ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip);
+    {
+        if (!orthographic_)
+            ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip);
+        else
+            ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip);
+    }
 
     return ret;
 }
@@ -322,7 +396,7 @@ Ray Camera::GetScreenRay(float x, float y) const
         return ret;
     }
 
-    Matrix4 viewProjInverse = (GetProjection(false) * GetView()).Inverse();
+    Matrix4 viewProjInverse = (GetProjection() * GetView()).Inverse();
 
     // The parameters range from 0.0 to 1.0. Expand to normalized device coordinates (-1.0 to 1.0) & flip Y axis
     x = 2.0f * x - 1.0f;
@@ -342,7 +416,7 @@ Vector2 Camera::WorldToScreenPoint(const Vector3& worldPos) const
 
     if (eyeSpacePos.z_ > 0.0f)
     {
-        Vector3 screenSpacePos = GetProjection(false) * eyeSpacePos;
+        Vector3 screenSpacePos = GetProjection() * eyeSpacePos;
         ret.x_ = screenSpacePos.x_;
         ret.y_ = screenSpacePos.y_;
     }
@@ -365,124 +439,38 @@ Vector3 Camera::ScreenToWorldPoint(const Vector3& screenPos) const
     return ray.origin_ + ray.direction_ * rayDistance;
 }
 
-const Frustum& Camera::GetFrustum() const
-{
-    if (frustumDirty_)
-    {
-        Matrix3x4 worldTransform = GetEffectiveWorldTransform();
-
-        if (!orthographic_)
-            frustum_.Define(fov_, aspectRatio_, zoom_, GetNearClip(), farClip_, worldTransform);
-        else
-            frustum_.DefineOrtho(orthoSize_, aspectRatio_, zoom_, GetNearClip(), farClip_, worldTransform);
-
-        frustumDirty_ = false;
-    }
-
-    return frustum_;
-}
-
-const Matrix4& Camera::GetProjection() const
+Matrix4 Camera::GetProjection() const
 {
     if (projectionDirty_)
-    {
-        projection_ = GetProjection(true);
-        projectionDirty_ = false;
-    }
+        UpdateProjection();
 
-    return projection_;
+    return flipVertical_ ? flipMatrix * projection_ : projection_;
 }
 
-Matrix4 Camera::GetProjection(bool apiSpecific) const
+Matrix4 Camera::GetGPUProjection() const
 {
-    Matrix4 ret(Matrix4::ZERO);
-
-    // Whether to construct matrix using OpenGL or Direct3D clip space convention
-#ifdef ATOMIC_OPENGL
-    bool openGLFormat = apiSpecific;
+#ifndef ATOMIC_OPENGL
+    return GetProjection(); // Already matches API-specific format
 #else
-    bool openGLFormat = false;
-#endif
-
-    if (!orthographic_)
-    {
-        float nearClip = GetNearClip();
-        float h = (1.0f / tanf(fov_ * M_DEGTORAD * 0.5f)) * zoom_;
-        float w = h / aspectRatio_;
-        float q, r;
+    // See formulation for depth range conversion at http://www.ogre3d.org/forums/viewtopic.php?f=4&t=13357
+    Matrix4 ret = GetProjection();
 
-        if (openGLFormat)
-        {
-            q = (farClip_ + nearClip) / (farClip_ - nearClip);
-            r = -2.0f * farClip_ * nearClip / (farClip_ - nearClip);
-        }
-        else
-        {
-            q = farClip_ / (farClip_ - nearClip);
-            r = -q * nearClip;
-        }
-
-        ret.m00_ = w;
-        ret.m02_ = projectionOffset_.x_ * 2.0f;
-        ret.m11_ = h;
-        ret.m12_ = projectionOffset_.y_ * 2.0f;
-        ret.m22_ = q;
-        ret.m23_ = r;
-        ret.m32_ = 1.0f;
-    }
-    else
-    {
-        // Disregard near clip, because it does not affect depth precision as with perspective projection
-        float h = (1.0f / (orthoSize_ * 0.5f)) * zoom_;
-        float w = h / aspectRatio_;
-        float q, r;
-
-        if (openGLFormat)
-        {
-            q = 2.0f / farClip_;
-            r = -1.0f;
-        }
-        else
-        {
-            q = 1.0f / farClip_;
-            r = 0.0f;
-        }
-
-        ret.m00_ = w;
-        ret.m03_ = projectionOffset_.x_ * 2.0f;
-        ret.m11_ = h;
-        ret.m13_ = projectionOffset_.y_ * 2.0f;
-        ret.m22_ = q;
-        ret.m23_ = r;
-        ret.m33_ = 1.0f;
-    }
-
-    if (flipVertical_)
-        ret = flipMatrix * ret;
+    ret.m20_ = 2.0f * ret.m20_ - ret.m30_;
+    ret.m21_ = 2.0f * ret.m21_ - ret.m31_;
+    ret.m22_ = 2.0f * ret.m22_ - ret.m32_;
+    ret.m23_ = 2.0f * ret.m23_ - ret.m33_;
 
     return ret;
+#endif
 }
 
 void Camera::GetFrustumSize(Vector3& near, Vector3& far) const
 {
-    near.z_ = GetNearClip();
-    far.z_ = farClip_;
-
-    if (!orthographic_)
-    {
-        float halfViewSize = tanf(fov_ * M_DEGTORAD * 0.5f) / zoom_;
-        near.y_ = near.z_ * halfViewSize;
-        near.x_ = near.y_ * aspectRatio_;
-        far.y_ = far.z_ * halfViewSize;
-        far.x_ = far.y_ * aspectRatio_;
-    }
-    else
-    {
-        float halfViewSize = orthoSize_ * 0.5f / zoom_;
-        near.y_ = far.y_ = halfViewSize;
-        near.x_ = far.x_ = near.y_ * aspectRatio_;
-    }
+    Frustum viewSpaceFrustum = GetViewSpaceFrustum();
+    near = viewSpaceFrustum.vertices_[0];
+    far = viewSpaceFrustum.vertices_[4];
 
+    /// \todo Necessary? Explain this
     if (flipVertical_)
     {
         near.y_ = -near.y_;
@@ -532,16 +520,13 @@ float Camera::GetLodDistance(float distance, float scale, float bias) const
         return orthoSize_ / d;
 }
 
-Quaternion Camera::GetFaceCameraRotation(const Vector3& position, const Quaternion& rotation, FaceCameraMode mode)
+Quaternion Camera::GetFaceCameraRotation(const Vector3& position, const Quaternion& rotation, FaceCameraMode mode, float minAngle)
 {
     if (!node_)
         return rotation;
 
     switch (mode)
     {
-    default:
-        return rotation;
-
     case FC_ROTATE_XYZ:
         return node_->GetWorldRotation();
 
@@ -560,19 +545,31 @@ Quaternion Camera::GetFaceCameraRotation(const Vector3& position, const Quaterni
         }
 
     case FC_LOOKAT_Y:
+    case FC_LOOKAT_MIXED:
         {
-            // Make the Y-only lookat happen on an XZ plane to make sure there are no unwanted transitions
-            // or singularities
-            Vector3 lookAtVec(position - node_->GetWorldPosition());
-            lookAtVec.y_ = 0.0f;
+            // Mixed mode needs true look-at vector
+            const Vector3 lookAtVec(position - node_->GetWorldPosition());
+            // While Y-only lookat happens on an XZ plane to make sure there are no unwanted transitions or singularities
+            const Vector3 lookAtVecXZ(lookAtVec.x_, 0.0f, lookAtVec.z_);
 
             Quaternion lookAt;
-            lookAt.FromLookRotation(lookAtVec);
+            lookAt.FromLookRotation(lookAtVecXZ);
 
             Vector3 euler = rotation.EulerAngles();
+            if (mode == FC_LOOKAT_MIXED)
+            {
+                const float angle = lookAtVec.Angle(rotation * Vector3::UP);
+                if (angle > 180 - minAngle)
+                    euler.x_ += minAngle - (180 - angle);
+                else if (angle < minAngle)
+                    euler.x_ -= minAngle - angle;
+            }
             euler.y_ = lookAt.EulerAngles().y_;
             return Quaternion(euler.x_, euler.y_, euler.z_);
         }
+
+    default:
+        return rotation;
     }
 }
 
@@ -584,7 +581,7 @@ Matrix3x4 Camera::GetEffectiveWorldTransform() const
 
 bool Camera::IsProjectionValid() const
 {
-    return farClip_ > GetNearClip();
+    return GetFarClip() > GetNearClip();
 }
 
 const Matrix3x4& Camera::GetView() const
@@ -650,4 +647,49 @@ void Camera::OnMarkedDirty(Node* node)
     viewDirty_ = true;
 }
 
+void Camera::UpdateProjection() const
+{
+    // Start from a zero matrix in case it was custom previously
+    projection_ = Matrix4::ZERO;
+
+    if (!orthographic_)
+    {
+        float h = (1.0f / tanf(fov_ * M_DEGTORAD * 0.5f)) * zoom_;
+        float w = h / aspectRatio_;
+        float q = farClip_ / (farClip_ - nearClip_);
+        float r = -q * nearClip_;
+
+        projection_.m00_ = w;
+        projection_.m02_ = projectionOffset_.x_ * 2.0f;
+        projection_.m11_ = h;
+        projection_.m12_ = projectionOffset_.y_ * 2.0f;
+        projection_.m22_ = q;
+        projection_.m23_ = r;
+        projection_.m32_ = 1.0f;
+        projNearClip_ = nearClip_;
+        projFarClip_ = farClip_;
+    }
+    else
+    {
+        float h = (1.0f / (orthoSize_ * 0.5f)) * zoom_;
+        float w = h / aspectRatio_;
+        float q = 1.0f / farClip_;
+        float r = 0.0f;
+
+        projection_.m00_ = w;
+        projection_.m03_ = projectionOffset_.x_ * 2.0f;
+        projection_.m11_ = h;
+        projection_.m13_ = projectionOffset_.y_ * 2.0f;
+        projection_.m22_ = q;
+        projection_.m23_ = r;
+        projection_.m33_ = 1.0f;
+        // Near clip does not affect depth accuracy in ortho projection, so let it stay 0 to avoid problems with shader depth parameters
+        projNearClip_ = 0.0f;
+        projFarClip_ = farClip_;
+    }
+
+    projectionDirty_ = false;
+    customProjection_ = false;
+}
+
 }

+ 26 - 12
Source/Atomic/Graphics/Camera.h

@@ -94,11 +94,16 @@ public:
     void SetClipPlane(const Plane& plane);
     /// Set vertical flipping mode. Called internally by View to resolve OpenGL / Direct3D9 rendertarget sampling differences.
     void SetFlipVertical(bool enable);
+    /// Set custom projection matrix, which should be specified in D3D convention with depth range 0 - 1. Disables auto aspect ratio.
+    /** Change any of the standard view parameters (FOV, far clip, zoom etc.) to revert to the standard projection. 
+        Note that the custom projection is not serialized or replicated through the network.
+     */
+    void SetProjection(const Matrix4& projection);
 
-    /// Return far clip distance.
-    float GetFarClip() const { return farClip_; }
+    /// Return far clip distance. If a custom projection matrix is in use, is calculated from it instead of the value assigned with SetFarClip().
+    float GetFarClip() const;
 
-    /// Return near clip distance.
+    /// Return near clip distance. If a custom projection matrix is in use, is calculated from it instead of the value assigned with SetNearClip().
     float GetNearClip() const;
 
     /// Return vertical field of view in degrees.
@@ -133,10 +138,10 @@ public:
 
     /// Return frustum in world space.
     const Frustum& GetFrustum() const;
-    /// Return API-specific projection matrix.
-    const Matrix4& GetProjection() const;
-    /// Return either API-specific or API-independent (D3D convention) projection matrix.
-    Matrix4 GetProjection(bool apiSpecific) const;
+    /// Return projection matrix. It's in D3D convention with depth range 0 - 1.
+    Matrix4 GetProjection() const;
+    /// Return projection matrix converted to API-specific format for use as a shader parameter.
+    Matrix4 GetGPUProjection() const;
     /// Return view matrix.
     const Matrix3x4& GetView() const;
     /// Return frustum near and far sizes.
@@ -149,11 +154,11 @@ public:
     Frustum GetViewSpaceFrustum() const;
     /// Return split frustum in view space.
     Frustum GetViewSpaceSplitFrustum(float nearClip, float farClip) const;
-    /// Return ray corresponding to normalized screen coordinates (0.0 - 1.0), with origin on the near clip plane.
+    /// Return ray corresponding to normalized screen coordinates (0 - 1), with origin on the near clip plane.
     Ray GetScreenRay(float x, float y) const;
-    /// Convert a world space point to normalized screen coordinates (0.0 - 1.0).
+    /// Convert a world space point to normalized screen coordinates (0 - 1).
     Vector2 WorldToScreenPoint(const Vector3& worldPos) const;
-    /// Convert normalized screen coordinates (0.0 - 1.0) and distance along view Z axis (in Z coordinate) to a world space point. The distance can not be closer than the near clip plane.
+    /// Convert normalized screen coordinates (0 - 1) and distance along view Z axis (in Z coordinate) to a world space point. The distance can not be closer than the near clip plane.
     /** Note that a HitDistance() from the camera screen ray is not the same as distance along the view Z axis, as under a perspective projection the ray is likely to not be Z-aligned.
      */
     Vector3 ScreenToWorldPoint(const Vector3& screenPos) const;
@@ -186,7 +191,7 @@ public:
     /// Return a scene node's LOD scaled distance.
     float GetLodDistance(float distance, float scale, float bias) const;
     /// Return a world rotation for facing a camera on certain axes based on the existing world rotation.
-    Quaternion GetFaceCameraRotation(const Vector3& position, const Quaternion& rotation, FaceCameraMode mode);
+    Quaternion GetFaceCameraRotation(const Vector3& position, const Quaternion& rotation, FaceCameraMode mode, float minAngle = 0.0f);
     /// Get effective world transform for matrix and frustum calculations including reflection but excluding node scaling.
     Matrix3x4 GetEffectiveWorldTransform() const;
     /// Return if projection parameters are valid for rendering and raycasting.
@@ -212,11 +217,14 @@ protected:
     virtual void OnMarkedDirty(Node* node);
 
 private:
+    /// Recalculate projection matrix.
+    void UpdateProjection() const;
+
     /// Cached view matrix.
     mutable Matrix3x4 view_;
     /// Cached projection matrix.
     mutable Matrix4 projection_;
-    /// Cached frustum.
+    /// Cached world space frustum.
     mutable Frustum frustum_;
     /// View matrix dirty flag.
     mutable bool viewDirty_;
@@ -226,6 +234,10 @@ private:
     mutable bool frustumDirty_;
     /// Orthographic mode flag.
     bool orthographic_;
+    /// Cached actual near clip distance.
+    mutable float projNearClip_;
+    /// Cached actual far clip distance.
+    mutable float projFarClip_;
     /// Near clip distance.
     float nearClip_;
     /// Far clip distance.
@@ -262,6 +274,8 @@ private:
     bool useReflection_;
     /// Use custom clip plane flag.
     bool useClipping_;
+    /// Use custom projection matrix flag. Used internally.
+    mutable bool customProjection_;
 };
 
 }

+ 2 - 1
Source/Atomic/Graphics/DebugRenderer.cpp

@@ -81,6 +81,7 @@ void DebugRenderer::SetView(Camera* camera)
 
     view_ = camera->GetView();
     projection_ = camera->GetProjection();
+    gpuProjection_ = camera->GetGPUProjection();
     frustum_ = camera->GetFrustum();
 }
 
@@ -510,7 +511,7 @@ void DebugRenderer::Render()
     graphics->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
     graphics->SetShaderParameter(VSP_VIEW, view_);
     graphics->SetShaderParameter(VSP_VIEWINV, view_.Inverse());
-    graphics->SetShaderParameter(VSP_VIEWPROJ, projection_ * view_);
+    graphics->SetShaderParameter(VSP_VIEWPROJ, gpuProjection_ * view_);
     graphics->SetShaderParameter(PSP_MATDIFFCOLOR, Color(1.0f, 1.0f, 1.0f, 1.0f));
     graphics->SetVertexBuffer(vertexBuffer_);
 

+ 2 - 0
Source/Atomic/Graphics/DebugRenderer.h

@@ -185,6 +185,8 @@ private:
     Matrix3x4 view_;
     /// Projection transform.
     Matrix4 projection_;
+    /// Projection transform in API-specific format.
+    Matrix4 gpuProjection_;
     /// View frustum.
     Frustum frustum_;
     /// Vertex buffer.

+ 16 - 11
Source/Atomic/Graphics/Graphics.h

@@ -188,12 +188,12 @@ public:
     void SetTexture(unsigned index, Texture* texture);
     /// Bind texture unit 0 for update. Called by Texture. Used only on OpenGL.
     void SetTextureForUpdate(Texture* texture);
-    /// Set default texture filtering mode.
-    void SetDefaultTextureFilterMode(TextureFilterMode mode);
-    /// Set texture anisotropy.
-    void SetTextureAnisotropy(unsigned level);
     /// Dirty texture parameters of all textures (when global settings change.)
     void SetTextureParametersDirty();
+    /// Set default texture filtering mode. Called by Renderer before rendering.
+    void SetDefaultTextureFilterMode(TextureFilterMode mode);
+    /// Set default texture anisotropy level. Called by Renderer before rendering.
+    void SetDefaultTextureAnisotropy(unsigned level);
     /// Reset all rendertargets, depth-stencil surface and viewport.
     void ResetRenderTargets();
     /// Reset specific rendertarget.
@@ -210,8 +210,8 @@ public:
     void SetDepthStencil(Texture2D* texture);
     /// Set viewport.
     void SetViewport(const IntRect& rect);
-    /// Set blending mode.
-    void SetBlendMode(BlendMode mode);
+    /// Set blending and alpha-to-coverage modes. Alpha-to-coverage is not supported on Direct3D9.
+    void SetBlendMode(BlendMode mode, bool alphaToCoverage = false);
     /// Set color write on/off.
     void SetColorWrite(bool enable);
     /// Set hardware culling mode.
@@ -384,6 +384,9 @@ public:
     /// Return default texture filtering mode.
     TextureFilterMode GetDefaultTextureFilterMode() const { return defaultTextureFilterMode_; }
 
+    /// Return default texture max. anisotropy level.
+    unsigned GetDefaultTextureAnisotropy() const { return defaultTextureAnisotropy_; }
+
     /// Return current rendertarget by index.
     RenderSurface* GetRenderTarget(unsigned index) const;
 
@@ -393,12 +396,12 @@ public:
     /// Return the viewport coordinates.
     IntRect GetViewport() const { return viewport_; }
 
-    /// Return texture anisotropy.
-    unsigned GetTextureAnisotropy() const { return textureAnisotropy_; }
-
     /// Return blending mode.
     BlendMode GetBlendMode() const { return blendMode_; }
 
+    /// Return whether alpha-to-coverage is enabled.
+    bool GetAlphaToCoverage() const { return alphaToCoverage_; }
+
     /// Return whether color write is enabled.
     bool GetColorWrite() const { return colorWrite_; }
 
@@ -699,10 +702,12 @@ private:
     IntRect viewport_;
     /// Default texture filtering mode.
     TextureFilterMode defaultTextureFilterMode_;
-    /// Texture anisotropy level.
-    unsigned textureAnisotropy_;
+    /// Default texture max. anisotropy level.
+    unsigned defaultTextureAnisotropy_;
     /// Blending mode.
     BlendMode blendMode_;
+    /// Alpha-to-coverage enable.
+    bool alphaToCoverage_;
     /// Color write enable.
     bool colorWrite_;
     /// Hardware culling mode.

+ 3 - 2
Source/Atomic/Graphics/GraphicsDefs.h

@@ -352,10 +352,11 @@ enum FaceCameraMode
     FC_ROTATE_Y,
     FC_LOOKAT_XYZ,
     FC_LOOKAT_Y,
-    FC_DIRECTION
+    FC_LOOKAT_MIXED,
+    FC_DIRECTION,
 };
 
-/// Shadow type
+/// Shadow type.
 enum ShadowQuality
 {
     SHADOWQUALITY_SIMPLE_16BIT = 0,

+ 66 - 1
Source/Atomic/Graphics/Light.cpp

@@ -55,7 +55,9 @@ static const float DEFAULT_SHADOWFADESTART = 0.8f;
 static const float DEFAULT_SHADOWQUANTIZE = 0.5f;
 static const float DEFAULT_SHADOWMINVIEW = 3.0f;
 static const float DEFAULT_SHADOWNEARFARRATIO = 0.002f;
+static const float DEFAULT_SHADOWMAXEXTRUSION = 1000.0f;
 static const float DEFAULT_SHADOWSPLIT = 1000.0f;
+static const float DEFAULT_TEMPERATURE = 6590.0f;
 
 static const char* typeNames[] =
 {
@@ -92,6 +94,7 @@ Light::Light(Context* context) :
     shadowCascade_(CascadeParameters(DEFAULT_SHADOWSPLIT, 0.0f, 0.0f, 0.0f, DEFAULT_SHADOWFADESTART)),
     shadowFocus_(FocusParameters(true, true, true, DEFAULT_SHADOWQUANTIZE, DEFAULT_SHADOWMINVIEW)),
     lightQueue_(0),
+    temperature_(6590.0f),
     specularIntensity_(DEFAULT_SPECULARINTENSITY),
     brightness_(DEFAULT_BRIGHTNESS),
     range_(DEFAULT_RANGE),
@@ -102,7 +105,9 @@ Light::Light(Context* context) :
     shadowIntensity_(0.0f),
     shadowResolution_(1.0f),
     shadowNearFarRatio_(DEFAULT_SHADOWNEARFARRATIO),
-    perVertex_(false)
+    shadowMaxExtrusion_(DEFAULT_SHADOWMAXEXTRUSION),
+    perVertex_(false),
+    usePhysicalValues_(false)
 {
 }
 
@@ -120,6 +125,8 @@ void Light::RegisterObject(Context* context)
     ATOMIC_ACCESSOR_ATTRIBUTE("Specular Intensity", GetSpecularIntensity, SetSpecularIntensity, float, DEFAULT_SPECULARINTENSITY,
         AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Brightness Multiplier", GetBrightness, SetBrightness, float, DEFAULT_BRIGHTNESS, AM_DEFAULT);
+    ATOMIC_ACCESSOR_ATTRIBUTE("Temperature", GetTemperature, SetTemperature, float, DEFAULT_TEMPERATURE, AM_DEFAULT);
+    ATOMIC_ATTRIBUTE("Use Physical Values", bool, usePhysicalValues_, false, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Range", GetRange, SetRange, float, DEFAULT_RANGE, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Spot FOV", GetFov, SetFov, float, DEFAULT_LIGHT_FOV, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Spot Aspect Ratio", GetAspectRatio, SetAspectRatio, float, 1.0f, AM_DEFAULT);
@@ -148,6 +155,7 @@ void Light::RegisterObject(Context* context)
     ATOMIC_ATTRIBUTE("Depth Slope Bias", float, shadowBias_.slopeScaledBias_, DEFAULT_SLOPESCALEDBIAS, AM_DEFAULT);
     ATOMIC_ATTRIBUTE("Normal Offset", float, shadowBias_.normalOffset_, DEFAULT_NORMALOFFSET, AM_DEFAULT);
     ATOMIC_ATTRIBUTE("Near/Farclip Ratio", float, shadowNearFarRatio_, DEFAULT_SHADOWNEARFARRATIO, AM_DEFAULT);
+    ATOMIC_ACCESSOR_ATTRIBUTE("Max Extrusion", GetShadowMaxExtrusion, SetShadowMaxExtrusion, float, DEFAULT_SHADOWMAXEXTRUSION, AM_DEFAULT);
     ATOMIC_ATTRIBUTE("View Mask", int, viewMask_, DEFAULT_VIEWMASK, AM_DEFAULT);
     ATOMIC_ATTRIBUTE("Light Mask", int, lightMask_, DEFAULT_LIGHTMASK, AM_DEFAULT);
 }
@@ -290,6 +298,18 @@ void Light::SetColor(const Color& color)
     MarkNetworkUpdate();
 }
 
+void Light::SetTemperature(float temperature)
+{
+    temperature_ = Clamp(temperature, 1000.0f, 10000.0f);
+    MarkNetworkUpdate();
+}
+
+void Light::SetUsePhysicalValues(bool enable)
+{
+    usePhysicalValues_ = enable;
+    MarkNetworkUpdate();
+}
+
 void Light::SetSpecularIntensity(float intensity)
 {
     specularIntensity_ = Max(intensity, 0.0f);
@@ -329,6 +349,12 @@ void Light::SetShadowNearFarRatio(float nearFarRatio)
     MarkNetworkUpdate();
 }
 
+void Light::SetShadowMaxExtrusion(float extrusion)
+{
+    shadowMaxExtrusion_ = Max(extrusion, 0.0f);
+    MarkNetworkUpdate();
+}
+
 void Light::SetFadeDistance(float distance)
 {
     fadeDistance_ = Max(distance, 0.0f);
@@ -386,6 +412,45 @@ void Light::SetShapeTexture(Texture* texture)
     MarkNetworkUpdate();
 }
 
+Color Light::GetColorFromTemperature() const
+{
+    // Approximate Planckian locus in CIE 1960 UCS
+    float u = (0.860117757f + 1.54118254e-4f * temperature_ + 1.28641212e-7f * temperature_ * temperature_) /
+        (1.0f + 8.42420235e-4f * temperature_ + 7.08145163e-7f * temperature_ * temperature_);
+    float v = (0.317398726f + 4.22806245e-5f * temperature_ + 4.20481691e-8f * temperature_ * temperature_) /
+        (1.0f - 2.89741816e-5f * temperature_ + 1.61456053e-7f * temperature_ * temperature_);
+
+    float x = 3.0f * u / (2.0f * u - 8.0f * v + 4.0f);
+    float y = 2.0f * v / (2.0f * u - 8.0f * v + 4.0f);
+    float z = 1.0f - x - y;
+
+    float y_ = 1.0f;
+    float x_ = y_ / y * x;
+    float z_ = y_ / y * z;
+
+    float red = 3.2404542f * x_ + -1.5371385f * y_ + -0.4985314f * z_;
+    float green = -0.9692660f * x_ + 1.8760108f * y_ + 0.0415560f * z_;
+    float blue = 0.0556434f * x_ + -0.2040259f * y_ + 1.0572252f * z_;
+
+    return Color(red, green, blue);
+}
+
+Color Light::GetEffectiveColor() const
+{
+    if (usePhysicalValues_)
+    {
+        // Light color in kelvin.
+        Color tempColor = GetColorFromTemperature();
+        // Light brightness in lumens
+        float energy = (brightness_ * 4.0f * M_PI) * 16.0f / (100.0f * 100.0f) / M_PI;
+        return Color(tempColor.r_ * color_.r_ * energy, tempColor.g_ * color_.g_ * energy, tempColor.b_ * color_.b_ * energy, 1.0f);
+    }
+    else
+    {
+        return Color(color_ * brightness_, 1.0f);
+    }
+}
+
 Frustum Light::GetFrustum() const
 {
     // Note: frustum is unaffected by node or parent scale

+ 30 - 6
Source/Atomic/Graphics/Light.h

@@ -50,7 +50,7 @@ static const unsigned MAX_CASCADE_SPLITS = 4;
 static const unsigned MAX_CASCADE_SPLITS = 1;
 #endif
 
-/// Shadow depth bias parameters.
+/// Depth bias parameters. Used both by lights (for shadow mapping) and materials.
 struct ATOMIC_API BiasParameters
 {
     /// Construct undefined.
@@ -178,9 +178,13 @@ public:
     void SetPerVertex(bool enable);
     /// Set color.
     void SetColor(const Color& color);
+    /// Set temperature of the light in Kelvin. Modulates the light color when "use physical values" is enabled.
+    void SetTemperature(float temperature);
+    /// Set use physical light values.
+    void SetUsePhysicalValues(bool enable);
     /// Set specular intensity. Zero disables specular calculations.
     void SetSpecularIntensity(float intensity);
-    /// Set light brightness multiplier. Both the color and specular intensity are multiplied with this to get final values for rendering.
+    /// Set light brightness multiplier. Both the color and specular intensity are multiplied with this. When "use physical values" is enabled, the value is specified in lumens.
     void SetBrightness(float brightness);
     /// Set range.
     void SetRange(float range);
@@ -202,8 +206,10 @@ public:
     void SetShadowIntensity(float intensity);
     /// Set shadow resolution between 0.25 - 1.0. Determines the shadow map to use.
     void SetShadowResolution(float resolution);
-    /// Set shadow camera near/far clip distance ratio.
+    /// Set shadow camera near/far clip distance ratio for spot and point lights. Does not affect directional lights, since they are orthographic and have near clip 0.
     void SetShadowNearFarRatio(float nearFarRatio);
+    /// Set maximum shadow extrusion for directional lights. The actual extrusion will be the smaller of this and camera far clip. Default 1000.
+    void SetShadowMaxExtrusion(float extrusion);
     /// Set range attenuation texture.
     void SetRampTexture(Texture* texture);
     /// Set spotlight attenuation texture.
@@ -218,14 +224,23 @@ public:
     /// Return color.
     const Color& GetColor() const { return color_; }
 
+    /// Return the temperature of the light in Kelvin.
+    float GetTemperature() const { return temperature_; }
+
+    /// Return if light uses temperature and brightness in lumens.
+    bool GetUsePhysicalValues() const { return usePhysicalValues_; }
+
+    /// Return the color value of the temperature in Kelvin.
+    Color GetColorFromTemperature() const;
+
     /// Return specular intensity.
     float GetSpecularIntensity() const { return specularIntensity_; }
 
-    /// Return brightness multiplier.
+    /// Return brightness multiplier. Specified in lumens when "use physical values" is enabled.
     float GetBrightness() const { return brightness_; }
 
-    /// Return effective color, multiplied by brightness. Do not multiply the alpha so that can compare against the default black color to detect a light with no effect.
-    Color GetEffectiveColor() const { return Color(color_ * brightness_, 1.0f); }
+    /// Return effective color, multiplied by brightness and affected by temperature when "use physical values" is enabled. Alpha is always 1 so that can compare against the default black color to detect a light with no effect.
+    Color GetEffectiveColor() const;
 
     /// Return effective specular intensity, multiplied by absolute value of brightness.
     float GetEffectiveSpecularIntensity() const { return specularIntensity_ * Abs(brightness_); }
@@ -263,6 +278,9 @@ public:
     /// Return shadow camera near/far clip distance ratio.
     float GetShadowNearFarRatio() const { return shadowNearFarRatio_; }
 
+    /// Return maximum shadow extrusion distance for directional lights.
+    float GetShadowMaxExtrusion() const { return shadowMaxExtrusion_; }
+
     /// Return range attenuation texture.
     Texture* GetRampTexture() const { return rampTexture_; }
 
@@ -319,6 +337,8 @@ private:
     LightType lightType_;
     /// Color.
     Color color_;
+    /// Light Temperature.
+    float temperature_;
     /// Shadow depth bias parameters.
     BiasParameters shadowBias_;
     /// Directional light cascaded shadow parameters.
@@ -353,8 +373,12 @@ private:
     float shadowResolution_;
     /// Shadow camera near/far clip distance ratio.
     float shadowNearFarRatio_;
+    /// Directional shadow max. extrusion distance.
+    float shadowMaxExtrusion_;
     /// Per-vertex lighting flag.
     bool perVertex_;
+    /// Use physical light values flag.
+    bool usePhysicalValues_;
 };
 
 inline bool CompareLights(Light* lhs, Light* rhs)

+ 135 - 23
Source/Atomic/Graphics/Material.cpp

@@ -173,6 +173,7 @@ TechniqueEntry::TechniqueEntry() :
 
 TechniqueEntry::TechniqueEntry(Technique* tech, unsigned qualityLevel, float lodDistance) :
     technique_(tech),
+    original_(tech),
     qualityLevel_(qualityLevel),
     lodDistance_(lodDistance)
 {
@@ -429,6 +430,13 @@ bool Material::Load(const XMLElement& source)
 
     ResourceCache* cache = GetSubsystem<ResourceCache>();
 
+    XMLElement shaderElem = source.GetChild("shader");
+    if (shaderElem)
+    {
+        vertexShaderDefines_ = shaderElem.GetAttribute("vsdefines");
+        pixelShaderDefines_ = shaderElem.GetAttribute("psdefines");
+    }
+
     XMLElement techniqueElem = source.GetChild("technique");
     techniques_.Clear();
 
@@ -438,7 +446,7 @@ bool Material::Load(const XMLElement& source)
         if (tech)
         {
             TechniqueEntry newTechnique;
-            newTechnique.technique_ = tech;
+            newTechnique.technique_ = newTechnique.original_ = tech;
             if (techniqueElem.HasAttribute("quality"))
                 newTechnique.qualityLevel_ = techniqueElem.GetInt("quality");
             if (techniqueElem.HasAttribute("loddistance"))
@@ -450,6 +458,7 @@ bool Material::Load(const XMLElement& source)
     }
 
     SortTechniques();
+    ApplyShaderDefines();
 
     XMLElement textureElem = source.GetChild("texture");
     while (textureElem)
@@ -539,13 +548,20 @@ bool Material::Load(const XMLElement& source)
     if (depthBiasElem)
         SetDepthBias(BiasParameters(depthBiasElem.GetFloat("constant"), depthBiasElem.GetFloat("slopescaled")));
 
+    XMLElement alphaToCoverageElem = source.GetChild("alphatocoverage");
+    if (alphaToCoverageElem)
+        SetAlphaToCoverage(alphaToCoverageElem.GetBool("enable"));
+
     XMLElement renderOrderElem = source.GetChild("renderorder");
     if (renderOrderElem)
         SetRenderOrder((unsigned char)renderOrderElem.GetUInt("value"));
 
+    XMLElement occlusionElem = source.GetChild("occlusion");
+    if (occlusionElem)
+        SetOcclusion(occlusionElem.GetBool("enable"));
+
     RefreshShaderParameterHash();
     RefreshMemoryUse();
-    CheckOcclusion();
     return true;
 }
 
@@ -561,6 +577,13 @@ bool Material::Load(const JSONValue& source)
 
     ResourceCache* cache = GetSubsystem<ResourceCache>();
 
+    const JSONValue& shaderVal = source.Get("shader");
+    if (!shaderVal.IsNull())
+    {
+        vertexShaderDefines_ = shaderVal.Get("vsdefines").GetString();
+        pixelShaderDefines_ = shaderVal.Get("psdefines").GetString();
+    }
+
     // Load techniques
     JSONArray techniquesArray = source.Get("techniques").GetArray();
     techniques_.Clear();
@@ -573,7 +596,7 @@ bool Material::Load(const JSONValue& source)
         if (tech)
         {
             TechniqueEntry newTechnique;
-            newTechnique.technique_ = tech;
+            newTechnique.technique_ = newTechnique.original_ = tech;
             JSONValue qualityVal = techVal.Get("quality");
             if (!qualityVal.IsNull())
                 newTechnique.qualityLevel_ = qualityVal.GetInt();
@@ -585,6 +608,7 @@ bool Material::Load(const JSONValue& source)
     }
 
     SortTechniques();
+    ApplyShaderDefines();
 
     // Load textures
     JSONObject textureObject = source.Get("textures").GetObject();
@@ -681,13 +705,20 @@ bool Material::Load(const JSONValue& source)
     if (!depthBiasVal.IsNull())
         SetDepthBias(BiasParameters(depthBiasVal.Get("constant").GetFloat(), depthBiasVal.Get("slopescaled").GetFloat()));
 
+    JSONValue alphaToCoverageVal = source.Get("alphatocoverage");
+    if (!alphaToCoverageVal.IsNull())
+        SetAlphaToCoverage(alphaToCoverageVal.GetBool());
+
     JSONValue renderOrderVal = source.Get("renderorder");
     if (!renderOrderVal.IsNull())
-        SetRenderOrder((unsigned char)renderOrderVal.Get("value").GetUInt());
+        SetRenderOrder((unsigned char)renderOrderVal.GetUInt());
+
+    JSONValue occlusionVal = source.Get("occlusion");
+    if (!occlusionVal.IsNull())
+        SetOcclusion(occlusionVal.GetBool());
 
     RefreshShaderParameterHash();
     RefreshMemoryUse();
-    CheckOcclusion();
     return true;
 }
 
@@ -724,6 +755,16 @@ bool Material::Save(XMLElement& dest) const
         }
     }
 
+    // Write shader compile defines
+    if (!vertexShaderDefines_.Empty() || !pixelShaderDefines_.Empty())
+    {
+        XMLElement shaderElem = dest.CreateChild("shader");
+        if (!vertexShaderDefines_.Empty())
+            shaderElem.SetString("vsdefines", vertexShaderDefines_);
+        if (!pixelShaderDefines_.Empty())
+            shaderElem.SetString("psdefines", pixelShaderDefines_);
+    }
+
     // Write shader parameters
     for (HashMap<StringHash, MaterialShaderParameter>::ConstIterator j = shaderParameters_.Begin();
          j != shaderParameters_.End(); ++j)
@@ -769,10 +810,18 @@ bool Material::Save(XMLElement& dest) const
     depthBiasElem.SetFloat("constant", depthBias_.constantBias_);
     depthBiasElem.SetFloat("slopescaled", depthBias_.slopeScaledBias_);
 
+    // Write alpha-to-coverage
+    XMLElement alphaToCoverageElem = dest.CreateChild("alphatocoverage");
+    alphaToCoverageElem.SetBool("enable", alphaToCoverage_);
+
     // Write render order
     XMLElement renderOrderElem = dest.CreateChild("renderorder");
     renderOrderElem.SetUInt("value", renderOrder_);
 
+    // Write occlusion
+    XMLElement occlusionElem = dest.CreateChild("occlusion");
+    occlusionElem.SetBool("enable", occlusion_);
+
     return true;
 }
 
@@ -805,6 +854,17 @@ bool Material::Save(JSONValue& dest) const
     }
     dest.Set("textures", texturesValue);
 
+    // Write shader compile defines
+    if (!vertexShaderDefines_.Empty() || !pixelShaderDefines_.Empty())
+    {
+        JSONValue shaderVal;
+        if (!vertexShaderDefines_.Empty())
+            shaderVal.Set("vsdefines", vertexShaderDefines_);
+        if (!pixelShaderDefines_.Empty())
+            shaderVal.Set("psdefines", pixelShaderDefines_);
+        dest.Set("shader", shaderVal);
+    }
+
     // Write shader parameters
     JSONValue shaderParamsVal;
     for (HashMap<StringHash, MaterialShaderParameter>::ConstIterator j = shaderParameters_.Begin();
@@ -849,10 +909,17 @@ bool Material::Save(JSONValue& dest) const
     JSONValue depthBiasValue;
     depthBiasValue.Set("constant", depthBias_.constantBias_);
     depthBiasValue.Set("slopescaled", depthBias_.slopeScaledBias_);
+    dest.Set("depthbias", depthBiasValue);
+
+    // Write alpha-to-coverage
+    dest.Set("alphatocoverage", alphaToCoverage_);
 
     // Write render order
     dest.Set("renderorder", (unsigned) renderOrder_);
 
+    // Write occlusion
+    dest.Set("occlusion", occlusion_);
+
     return true;
 }
 
@@ -871,14 +938,39 @@ void Material::SetTechnique(unsigned index, Technique* tech, unsigned qualityLev
         return;
 
     techniques_[index] = TechniqueEntry(tech, qualityLevel, lodDistance);
-    CheckOcclusion();
+    ApplyShaderDefines(index);
+}
+
+void Material::SetVertexShaderDefines(const String& defines)
+{
+    if (defines != vertexShaderDefines_)
+    {
+        vertexShaderDefines_ = defines;
+        ApplyShaderDefines();
+    }
+}
+
+void Material::SetPixelShaderDefines(const String& defines)
+{
+    if (defines != pixelShaderDefines_)
+    {
+        pixelShaderDefines_ = defines;
+        ApplyShaderDefines();
+    }
 }
 
 void Material::SetShaderParameter(const String& name, const Variant& value)
 {
     MaterialShaderParameter newParam;
     newParam.name_ = name;
-    newParam.value_ = value;
+    if (value.GetType() != VAR_DOUBLE)
+        newParam.value_ = value;
+    else
+    {
+        // Lua scripts may end up creating Double variants, which are unsuitable for shader data. Convert if necessary.
+        newParam.value_ = value.GetFloat();
+    }
+
     StringHash nameHash(name);
     shaderParameters_[nameHash] = newParam;
 
@@ -1015,11 +1107,21 @@ void Material::SetDepthBias(const BiasParameters& parameters)
     depthBias_.Validate();
 }
 
+void Material::SetAlphaToCoverage(bool enable)
+{
+    alphaToCoverage_ = enable;
+}
+
 void Material::SetRenderOrder(unsigned char order)
 {
     renderOrder_ = order;
 }
 
+void Material::SetOcclusion(bool enable)
+{
+    occlusion_ = enable;
+}
+
 void Material::SetScene(Scene* scene)
 {
     UnsubscribeFromEvent(E_UPDATE);
@@ -1057,9 +1159,13 @@ SharedPtr<Material> Material::Clone(const String& cloneName) const
 
     ret->SetName(cloneName);
     ret->techniques_ = techniques_;
+    ret->vertexShaderDefines_ = vertexShaderDefines_;
+    ret->pixelShaderDefines_ = pixelShaderDefines_;
     ret->shaderParameters_ = shaderParameters_;
     ret->shaderParameterHash_ = shaderParameterHash_;
     ret->textures_ = textures_;
+    ret->depthBias_ = depthBias_;
+    ret->alphaToCoverage_ = alphaToCoverage_;
     ret->occlusion_ = occlusion_;
     ret->specular_ = specular_;
     ret->cullMode_ = cullMode_;
@@ -1146,28 +1252,15 @@ Variant Material::ParseShaderParameterValue(const String& value)
         return ToVectorVariant(valueTrimmed);
 }
 
-void Material::CheckOcclusion()
-{
-    // Determine occlusion by checking the base pass of each technique
-    occlusion_ = false;
-    for (unsigned i = 0; i < techniques_.Size(); ++i)
-    {
-        Technique* tech = techniques_[i].technique_;
-        if (tech)
-        {
-            Pass* pass = tech->GetPass("base");
-            if (pass && pass->GetDepthWrite() && !pass->GetAlphaMask())
-                occlusion_ = true;
-        }
-    }
-}
-
 void Material::ResetToDefaults()
 {
     // Needs to be a no-op when async loading, as this does a GetResource() which is not allowed from worker threads
     if (!Thread::IsMainThread())
         return;
 
+    vertexShaderDefines_.Clear();
+    pixelShaderDefines_.Clear();
+
     SetNumTechniques(1);
     Renderer* renderer = GetSubsystem<Renderer>();
     SetTechnique(0, renderer ? renderer->GetDefaultTechnique() :
@@ -1192,6 +1285,7 @@ void Material::ResetToDefaults()
     fillMode_ = FILL_SOLID;
     depthBias_ = BiasParameters(0.0f, 0.0f);
     renderOrder_ = DEFAULT_RENDER_ORDER;
+    occlusion_ = true;
 
     RefreshShaderParameterHash();
     RefreshMemoryUse();
@@ -1270,6 +1364,24 @@ void Material::HandleAttributeAnimationUpdate(StringHash eventType, VariantMap&
         SetShaderParameterAnimation(finishedNames[i], 0);
 }
 
+void Material::ApplyShaderDefines(unsigned index)
+{
+    if (index == M_MAX_UNSIGNED)
+    {
+        for (unsigned i = 0; i < techniques_.Size(); ++i)
+            ApplyShaderDefines(i);
+        return;
+    }
+
+    if (index >= techniques_.Size() || !techniques_[index].original_)
+        return;
+
+    if (vertexShaderDefines_.Empty() && pixelShaderDefines_.Empty())
+        techniques_[index].technique_ = techniques_[index].original_;
+    else
+        techniques_[index].technique_ = techniques_[index].original_->CloneWithDefines(vertexShaderDefines_, pixelShaderDefines_);
+}
+
 // ATOMIC BEGIN
 const char** Material::GetTextureUnitNames()
 {

+ 29 - 5
Source/Atomic/Graphics/Material.h

@@ -64,6 +64,8 @@ struct TechniqueEntry
 
     /// Technique.
     SharedPtr<Technique> technique_;
+    /// Original technique, in case the material adds shader compilation defines. The modified clones are requested from it.
+    SharedPtr<Technique> original_;
     /// Quality level.
     int qualityLevel_;
     /// LOD distance.
@@ -134,6 +136,10 @@ public:
     void SetNumTechniques(unsigned num);
     /// Set technique.
     void SetTechnique(unsigned index, Technique* tech, unsigned qualityLevel = 0, float lodDistance = 0.0f);
+    /// Set additional vertex shader defines. Separate multiple defines with spaces. Setting defines at the material level causes technique(s) to be cloned as necessary.
+    void SetVertexShaderDefines(const String& defines);
+    /// Set additional pixel shader defines. Separate multiple defines with spaces. Setting defines at the material level causes technique(s) to be cloned as necessary.
+    void SetPixelShaderDefines(const String& defines);
     /// Set shader parameter.
     void SetShaderParameter(const String& name, const Variant& value);
     /// Set shader parameter animation.
@@ -155,10 +161,14 @@ public:
     void SetShadowCullMode(CullMode mode);
     /// Set polygon fill mode. Interacts with the camera's fill mode setting so that the "least filled" mode will be used.
     void SetFillMode(FillMode mode);
-    /// Set depth bias.
+    /// Set depth bias parameters for depth write and compare. Note that the normal offset parameter is not used and will not be saved, as it affects only shadow map sampling during light rendering.
     void SetDepthBias(const BiasParameters& parameters);
+    /// Set alpha-to-coverage mode on all passes.
+    void SetAlphaToCoverage(bool enable);
     /// Set 8-bit render order within pass. Default 128. Lower values will render earlier and higher values later, taking precedence over e.g. state and distance sorting.
     void SetRenderOrder(unsigned char order);
+    /// Set whether to use in occlusion rendering. Default true.
+    void SetOcclusion(bool enable);
     /// Associate the material with a scene to ensure that shader parameter animation happens in sync with scene update, respecting the scene time scale. If no scene is set, the global update events will be used.
     void SetScene(Scene* scene);
     /// Remove shader parameter.
@@ -190,6 +200,11 @@ public:
     /// Return all textures.
     const HashMap<TextureUnit, SharedPtr<Texture> >& GetTextures() const { return textures_; }
 
+    /// Return additional vertex shader defines.
+    const String& GetVertexShaderDefines() const { return vertexShaderDefines_; }
+    /// Return additional pixel shader defines.
+    const String& GetPixelShaderDefines() const { return pixelShaderDefines_; }
+
     /// Return shader parameter.
     const Variant& GetShaderParameter(const String& name) const;
     /// Return shader parameter animation.
@@ -214,6 +229,9 @@ public:
     /// Return depth bias.
     const BiasParameters& GetDepthBias() const { return depthBias_; }
 
+    /// Return alpha-to-coverage mode.
+    bool GetAlphaToCoverage() const { return alphaToCoverage_; }
+
     /// Return render order.
     unsigned char GetRenderOrder() const { return renderOrder_; }
     
@@ -243,19 +261,19 @@ public:
     // ATOMIC END
 
 private:
-    /// Helper function for loading JSON files
+    /// Helper function for loading JSON files.
     bool BeginLoadJSON(Deserializer& source);
-    /// Helper function for loading XML files
+    /// Helper function for loading XML files.
     bool BeginLoadXML(Deserializer& source);
 
-    /// Re-evaluate occlusion rendering.
-    void CheckOcclusion();
     /// Reset to defaults.
     void ResetToDefaults();
     /// Recalculate shader parameter hash.
     void RefreshShaderParameterHash();
     /// Recalculate the memory used by the material.
     void RefreshMemoryUse();
+    /// Reapply shader defines to technique index. By default reapply all.
+    void ApplyShaderDefines(unsigned index = M_MAX_UNSIGNED);
     /// Return shader parameter animation info.
     ShaderParameterAnimationInfo* GetShaderParameterAnimationInfo(const String& name) const;
     /// Update whether should be subscribed to scene or global update events for shader parameter animation.
@@ -271,6 +289,10 @@ private:
     HashMap<StringHash, MaterialShaderParameter> shaderParameters_;
     /// %Shader parameters animation infos.
     HashMap<StringHash, SharedPtr<ShaderParameterAnimationInfo> > shaderParameterAnimationInfos_;
+    /// Vertex shader defines.
+    String vertexShaderDefines_;
+    /// Pixel shader defines.
+    String pixelShaderDefines_;
     /// Normal culling mode.
     CullMode cullMode_;
     /// Culling mode for shadow rendering.
@@ -285,6 +307,8 @@ private:
     unsigned auxViewFrameNumber_;
     /// Shader parameter hash value.
     unsigned shaderParameterHash_;
+    /// Alpha-to-coverage mode.
+    bool alphaToCoverage_;
     /// Render occlusion flag.
     bool occlusion_;
     /// Specular lighting flag.

+ 1 - 1
Source/Atomic/Graphics/OcclusionBuffer.cpp

@@ -125,7 +125,7 @@ void OcclusionBuffer::SetView(Camera* camera)
         return;
 
     view_ = camera->GetView();
-    projection_ = camera->GetProjection(false);
+    projection_ = camera->GetProjection();
     viewProj_ = projection_ * view_;
     nearClip_ = camera->GetNearClip();
     farClip_ = camera->GetFarClip();

+ 28 - 11
Source/Atomic/Graphics/OpenGL/OGLGraphics.cpp

@@ -255,6 +255,7 @@ Graphics::Graphics(Context* context_) :
     shadowMapFormat_(GL_DEPTH_COMPONENT16),
     hiresShadowMapFormat_(GL_DEPTH_COMPONENT24),
     defaultTextureFilterMode_(FILTER_TRILINEAR),
+    defaultTextureAnisotropy_(4),
     shaderPath_("Shaders/GLSL/"),
     shaderExtension_(".glsl"),
     orientations_("LandscapeLeft LandscapeRight"),
@@ -602,8 +603,15 @@ bool Graphics::TakeScreenShot(Image* destImage)
 
     ResetRenderTargets();
 
+#ifndef GL_ES_VERSION_2_0
     destImage->SetSize(width_, height_, 3);
     glReadPixels(0, 0, width_, height_, GL_RGB, GL_UNSIGNED_BYTE, destImage->GetData());
+#else
+    // Use RGBA format on OpenGL ES, as otherwise (at least on Android) the produced image is all black
+    destImage->SetSize(width_, height_, 4);
+    glReadPixels(0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, destImage->GetData());
+#endif
+
     // On OpenGL we need to flip the image vertically after reading
     destImage->FlipVertical();
 
@@ -1507,11 +1515,13 @@ void Graphics::SetDefaultTextureFilterMode(TextureFilterMode mode)
     }
 }
 
-void Graphics::SetTextureAnisotropy(unsigned level)
+void Graphics::SetDefaultTextureAnisotropy(unsigned level)
 {
-    if (level != textureAnisotropy_)
+    level = Max(level, 1U);
+    
+    if (level != defaultTextureAnisotropy_)
     {
-        textureAnisotropy_ = level;
+        defaultTextureAnisotropy_ = level;
         SetTextureParametersDirty();
     }
 }
@@ -1648,7 +1658,7 @@ void Graphics::SetViewport(const IntRect& rect)
     SetScissorTest(false);
 }
 
-void Graphics::SetBlendMode(BlendMode mode)
+void Graphics::SetBlendMode(BlendMode mode, bool alphaToCoverage)
 {
     if (mode != blendMode_)
     {
@@ -1663,6 +1673,16 @@ void Graphics::SetBlendMode(BlendMode mode)
 
         blendMode_ = mode;
     }
+
+    if (alphaToCoverage != alphaToCoverage_)
+    {
+        if (alphaToCoverage)
+            glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+        else
+            glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+
+        alphaToCoverage_ = alphaToCoverage;
+    }
 }
 
 void Graphics::SetColorWrite(bool enable)
@@ -1703,9 +1723,8 @@ void Graphics::SetDepthBias(float constantBias, float slopeScaledBias)
         if (slopeScaledBias != 0.0f)
         {
             // OpenGL constant bias is unreliable and dependant on depth buffer bitdepth, apply in the projection matrix instead
-            float adjustedSlopeScaledBias = slopeScaledBias + 1.0f;
             glEnable(GL_POLYGON_OFFSET_FILL);
-            glPolygonOffset(adjustedSlopeScaledBias, 0.0f);
+            glPolygonOffset(slopeScaledBias, 0.0f);
         }
         else
             glDisable(GL_POLYGON_OFFSET_FILL);
@@ -2430,7 +2449,6 @@ void Graphics::Restore()
 void Graphics::MarkFBODirty()
 {
     impl_->fboDirty_ = true;
-
 }
 
 void Graphics::SetVBO(unsigned object)
@@ -2727,9 +2745,8 @@ void Graphics::CheckFeatureSupport()
     }
 #endif
 
-// Consider OpenGL shadows always hardware sampled, if supported at all
-hardwareShadowSupport_ = shadowMapFormat_ != 0;
-
+    // Consider OpenGL shadows always hardware sampled, if supported at all
+    hardwareShadowSupport_ = shadowMapFormat_ != 0;
 }
 
 void Graphics::PrepareDraw()
@@ -3070,7 +3087,7 @@ void Graphics::ResetCachedState()
     vertexShader_ = 0;
     pixelShader_ = 0;
     blendMode_ = BLEND_REPLACE;
-    textureAnisotropy_ = 1;
+    alphaToCoverage_ = false;
     colorWrite_ = true;
     cullMode_ = CULL_NONE;
     constantDepthBias_ = 0.0f;

+ 2 - 1
Source/Atomic/Graphics/OpenGL/OGLTexture.cpp

@@ -133,8 +133,9 @@ void Texture::UpdateParameters()
     // Anisotropy
     if (graphics_->GetAnisotropySupport())
     {
+        unsigned maxAnisotropy = anisotropy_ ? anisotropy_ : graphics_->GetDefaultTextureAnisotropy();
         glTexParameterf(target_, GL_TEXTURE_MAX_ANISOTROPY_EXT,
-            filterMode == FILTER_ANISOTROPIC ? (float)graphics_->GetTextureAnisotropy() : 1.0f);
+            filterMode == FILTER_ANISOTROPIC ? (float)maxAnisotropy : 1.0f);
     }
 
     // Shadow compare

+ 23 - 5
Source/Atomic/Graphics/ParticleEmitter.cpp

@@ -41,6 +41,8 @@ extern const char* GEOMETRY_CATEGORY;
 extern const char* faceCameraModeNames[];
 static const unsigned MAX_PARTICLES_IN_FRAME = 100;
 
+extern const char* autoRemoveModeNames[];
+
 ParticleEmitter::ParticleEmitter(Context* context) :
     BillboardSet(context),
     periodTimer_(0.0f),
@@ -50,7 +52,8 @@ ParticleEmitter::ParticleEmitter(Context* context) :
     emitting_(true),
     needUpdate_(false),
     serializeParticles_(true),
-    sendFinishEvent_(true)
+    sendFinishedEvent_(true),
+    autoRemove_(REMOVE_DISABLED)
 {
     SetNumParticles(DEFAULT_NUM_PARTICLES);
 }
@@ -74,6 +77,7 @@ void ParticleEmitter::RegisterObject(Context* context)
     ATOMIC_ATTRIBUTE("Is Emitting", bool, emitting_, true, AM_FILE);
     ATOMIC_ATTRIBUTE("Period Timer", float, periodTimer_, 0.0f, AM_FILE | AM_NOEDIT);
     ATOMIC_ATTRIBUTE("Emission Timer", float, emissionTimer_, 0.0f, AM_FILE | AM_NOEDIT);
+    ATOMIC_ENUM_ATTRIBUTE("Autoremove Mode", autoRemove_, autoRemoveModeNames, REMOVE_DISABLED, AM_DEFAULT);
     ATOMIC_COPY_BASE_ATTRIBUTES(Drawable);
     ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Particles", GetParticlesAttr, SetParticlesAttr, VariantVector, Variant::emptyVariantVector,
         AM_FILE | AM_NOEDIT);
@@ -128,7 +132,7 @@ void ParticleEmitter::Update(const FrameInfo& frame)
         if (inactiveTime && periodTimer_ >= inactiveTime)
         {
             emitting_ = true;
-            sendFinishEvent_ = true;
+            sendFinishedEvent_ = true;
             periodTimer_ -= inactiveTime;
         }
         // If emitter has an indefinite stop interval, keep period timer reset to allow restarting emission in the editor
@@ -295,7 +299,7 @@ void ParticleEmitter::SetEmitting(bool enable)
     if (enable != emitting_)
     {
         emitting_ = enable;
-        sendFinishEvent_ = enable;
+        sendFinishedEvent_ = enable;
         periodTimer_ = 0.0f;
         // Note: network update does not need to be marked as this is a file only attribute
     }
@@ -307,6 +311,12 @@ void ParticleEmitter::SetSerializeParticles(bool enable)
     // Note: network update does not need to be marked as this is a file only attribute
 }
 
+void ParticleEmitter::SetAutoRemoveMode(AutoRemoveMode mode)
+{
+    autoRemove_ = mode;
+    MarkNetworkUpdate();
+}
+
 void ParticleEmitter::ResetEmissionTimer()
 {
     emissionTimer_ = 0.0f;
@@ -538,7 +548,7 @@ void ParticleEmitter::HandleScenePostUpdate(StringHash eventType, VariantMap& ev
         MarkForUpdate();
     }
 
-    if (node_ && !emitting_ && sendFinishEvent_)
+    if (node_ && !emitting_ && sendFinishedEvent_)
     {
         // Send finished event only once all billboards are gone
         bool hasEnabledBillboards = false;
@@ -554,7 +564,10 @@ void ParticleEmitter::HandleScenePostUpdate(StringHash eventType, VariantMap& ev
 
         if (!hasEnabledBillboards)
         {
-            sendFinishEvent_ = false;
+            sendFinishedEvent_ = false;
+
+            // Make a weak pointer to self to check for destruction during event handling
+            WeakPtr<ParticleEmitter> self(this);
 
             using namespace ParticleEffectFinished;
 
@@ -563,6 +576,11 @@ void ParticleEmitter::HandleScenePostUpdate(StringHash eventType, VariantMap& ev
             eventData[P_EFFECT] = effect_;
 
             node_->SendEvent(E_PARTICLEEFFECTFINISHED, eventData);
+
+            if (self.Expired())
+                return;
+
+            DoAutoRemove(autoRemove_);
         }
     }
 }

+ 8 - 1
Source/Atomic/Graphics/ParticleEmitter.h

@@ -76,6 +76,8 @@ public:
     void SetEmitting(bool enable);
     /// Set whether particles should be serialized. Default true, set false to reduce scene file size.
     void SetSerializeParticles(bool enable);
+    //// Set to remove either the emitter component or its owner node from the scene automatically on particle effect completion. Disabled by default.
+    void SetAutoRemoveMode(AutoRemoveMode mode);
     /// Reset the emission period timer.
     void ResetEmissionTimer();
     /// Remove all current particles.
@@ -97,6 +99,9 @@ public:
     /// Return whether particles are to be serialized.
     bool GetSerializeParticles() const { return serializeParticles_; }
 
+    /// Return automatic removal mode on particle effect completion.
+    AutoRemoveMode GetAutoRemoveMode() const { return autoRemove_; }
+
     /// Set particles effect attribute.
     void SetEffectAttr(const ResourceRef& value);
     /// Set particles effect attribute.
@@ -142,7 +147,9 @@ private:
     /// Serialize particles flag.
     bool serializeParticles_;
     /// Ready to send effect finish event flag.
-    bool sendFinishEvent_;
+    bool sendFinishedEvent_;
+    /// Automatic removal mode.
+    AutoRemoveMode autoRemove_;
 };
 
 }

+ 11 - 8
Source/Atomic/Graphics/Renderer.cpp

@@ -708,7 +708,7 @@ void Renderer::Render()
         SetIndirectionTextureData();
 
     graphics_->SetDefaultTextureFilterMode(textureFilterMode_);
-    graphics_->SetTextureAnisotropy((unsigned)textureAnisotropy_);
+    graphics_->SetDefaultTextureAnisotropy((unsigned)textureAnisotropy_);
 
     // If no views that render to the backbuffer, clear the screen so that e.g. the UI is not rendered on top of previous frame
     bool hasBackbufferViews = false;
@@ -1400,7 +1400,7 @@ void Renderer::OptimizeLightByStencil(Light* light, Camera* camera)
 
         Geometry* geometry = GetLightGeometry(light);
         const Matrix3x4& view = camera->GetView();
-        const Matrix4& projection = camera->GetProjection();
+        const Matrix4& projection = camera->GetGPUProjection();
         Vector3 cameraPos = camera->GetNode()->GetWorldPosition();
         float lightDist;
 
@@ -1665,6 +1665,9 @@ void Renderer::LoadPassShaders(Pass* pass)
         extraShaderDefines = " VSM_SHADOW ";
     }
 
+    String vsDefines = pass->GetEffectiveVertexShaderDefines();
+    String psDefines = pass->GetEffectivePixelShaderDefines();
+
     if (pass->GetLightingMode() == LIGHTING_PERPIXEL)
     {
         // Load forward pixel lit variations
@@ -1677,7 +1680,7 @@ void Renderer::LoadPassShaders(Pass* pass)
             unsigned l = j % MAX_LIGHT_VS_VARIATIONS;
 
             vertexShaders[j] = graphics_->GetShader(VS, pass->GetVertexShader(),
-                pass->GetVertexShaderDefines() + extraShaderDefines + lightVSVariations[l] + geometryVSVariations[g]);
+                vsDefines + extraShaderDefines + lightVSVariations[l] + geometryVSVariations[g]);
         }
         for (unsigned j = 0; j < MAX_LIGHT_PS_VARIATIONS * 2; ++j)
         {
@@ -1687,12 +1690,12 @@ void Renderer::LoadPassShaders(Pass* pass)
             if (l & LPS_SHADOW)
             {
                 pixelShaders[j] = graphics_->GetShader(PS, pass->GetPixelShader(),
-                    pass->GetPixelShaderDefines() + extraShaderDefines + lightPSVariations[l] + GetShadowVariations() +
+                    psDefines + extraShaderDefines + lightPSVariations[l] + GetShadowVariations() +
                     heightFogVariations[h]);
             }
             else
                 pixelShaders[j] = graphics_->GetShader(PS, pass->GetPixelShader(),
-                    pass->GetPixelShaderDefines() + extraShaderDefines + lightPSVariations[l] + heightFogVariations[h]);
+                    psDefines + extraShaderDefines + lightPSVariations[l] + heightFogVariations[h]);
         }
     }
     else
@@ -1706,7 +1709,7 @@ void Renderer::LoadPassShaders(Pass* pass)
                 unsigned g = j / MAX_VERTEXLIGHT_VS_VARIATIONS;
                 unsigned l = j % MAX_VERTEXLIGHT_VS_VARIATIONS;
                 vertexShaders[j] = graphics_->GetShader(VS, pass->GetVertexShader(),
-                    pass->GetVertexShaderDefines() + extraShaderDefines + vertexLightVSVariations[l] + geometryVSVariations[g]);
+                    vsDefines + extraShaderDefines + vertexLightVSVariations[l] + geometryVSVariations[g]);
             }
         }
         else
@@ -1715,7 +1718,7 @@ void Renderer::LoadPassShaders(Pass* pass)
             for (unsigned j = 0; j < MAX_GEOMETRYTYPES; ++j)
             {
                 vertexShaders[j] = graphics_->GetShader(VS, pass->GetVertexShader(),
-                    pass->GetVertexShaderDefines() + extraShaderDefines + geometryVSVariations[j]);
+                    vsDefines + extraShaderDefines + geometryVSVariations[j]);
             }
         }
 
@@ -1723,7 +1726,7 @@ void Renderer::LoadPassShaders(Pass* pass)
         for (unsigned j = 0; j < 2; ++j)
         {
             pixelShaders[j] =
-                graphics_->GetShader(PS, pass->GetPixelShader(), pass->GetPixelShaderDefines() + extraShaderDefines + heightFogVariations[j]);
+                graphics_->GetShader(PS, pass->GetPixelShader(), psDefines + extraShaderDefines + heightFogVariations[j]);
         }
     }
 

Some files were not shown because too many files changed in this diff