Browse Source

Added more default materials.
Re-added fast square root & fast inverse square root, and fast variations of normalizing vectors & quaternions.
Skinning & instancing vertex shaders are now chosen automatically without the material having to specify them.
Refactored Button logic.

Lasse Öörni 15 years ago
parent
commit
347fe0815d
48 changed files with 417 additions and 291 deletions
  1. 1 4
      Bin/Data/Materials/Default.xml
  2. 8 0
      Bin/Data/Materials/DefaultAlpha.xml
  3. 1 4
      Bin/Data/Materials/DefaultDiff.xml
  4. 8 0
      Bin/Data/Materials/DefaultDiffAlpha.xml
  5. 12 0
      Bin/Data/Materials/DefaultDiffAlphaMask.xml
  6. 0 15
      Bin/Data/Materials/DefaultDiffInstanced.xml
  7. 12 0
      Bin/Data/Materials/DefaultDiffNormal.xml
  8. 12 0
      Bin/Data/Materials/DefaultDiffNormalAlphaMask.xml
  9. 0 14
      Bin/Data/Materials/DefaultDiffSkinned.xml
  10. 0 15
      Bin/Data/Materials/DefaultInstanced.xml
  11. 0 15
      Bin/Data/Materials/DefaultSkinned.xml
  12. 1 1
      Bin/Data/Materials/Jack.xml
  13. 0 3
      Bin/Data/Materials/JackStatic.xml
  14. 1 1
      Bin/Data/Materials/Mushroom.xml
  15. 0 7
      Bin/Data/Materials/MushroomInstanced.xml
  16. 0 7
      Bin/Data/Materials/MushroomStatic.xml
  17. 1 1
      Bin/Data/Materials/Ninja.xml
  18. 1 13
      Bin/Data/Materials/Test.xml
  19. 2 1
      Bin/Data/UI/UI.xml
  20. 1 1
      Engine/Engine/RegisterEngine.cpp
  21. 4 5
      Engine/Engine/RegisterUI.cpp
  22. 33 1
      Engine/Math/MathDefs.h
  23. 23 0
      Engine/Math/Quaternion.h
  24. 21 0
      Engine/Math/Vector2.h
  25. 22 0
      Engine/Math/Vector3.h
  26. 2 2
      Engine/Physics/RigidBody.cpp
  27. 2 0
      Engine/Renderer/AnimatedModel.h
  28. 102 72
      Engine/Renderer/AnimationState.cpp
  29. 2 2
      Engine/Renderer/AnimationState.h
  30. 1 1
      Engine/Renderer/Batch.cpp
  31. 1 1
      Engine/Renderer/Camera.cpp
  32. 1 1
      Engine/Renderer/DeferredView.cpp
  33. 1 1
      Engine/Renderer/ForwardView.cpp
  34. 2 0
      Engine/Renderer/GeometryNode.h
  35. 2 0
      Engine/Renderer/InstancedModel.h
  36. 57 34
      Engine/Renderer/Pipeline.cpp
  37. 9 0
      Engine/Renderer/RendererDefs.h
  38. 2 2
      Engine/Renderer/View.cpp
  39. 1 1
      Engine/Renderer/VolumeNode.cpp
  40. 42 39
      Engine/UI/Button.cpp
  41. 14 15
      Engine/UI/Button.h
  42. 1 1
      Engine/UI/UI.cpp
  43. 1 1
      Engine/UI/UIElement.cpp
  44. 1 1
      Engine/UI/UIElement.h
  45. 3 3
      Examples/Test/Application.cpp
  46. 2 2
      SourceAssets/Shaders/Deferred/GBuffer.xml
  47. 3 3
      SourceAssets/Shaders/Forward.xml
  48. 1 1
      SourceAssets/Shaders/Prepass/GBuffer.xml

+ 1 - 4
Bin/Data/Materials/Default.xml

@@ -2,14 +2,11 @@
     <technique>
         <parameter name="MatSpecProperties" value="0.5 16" />
         <pass name="deferred" vs="Deferred/GBuffer" ps="Deferred/GBuffer" />
-
         <pass name="prepass" vs="Prepass/GBuffer" ps="Prepass/GBuffer" />
         <pass name="material" vs="Prepass/Material" ps="Prepass/Material" depthwrite="false" depthtest="equal" />
-        
         <pass name="ambient" vs="Forward" ps="Forward_Ambient" />
         <pass name="light" vs="Forward" ps="Forward" depthwrite="false" depthtest="equal" blend="add" />
-        <pass name="negative" vs="Forward" ps="Forward" depthwrite="false" depthtest="equal" blend="multiply" />
-        
+        <pass name="negative" vs="Forward" ps="Forward" depthwrite="false" depthtest="equal" blend="multiply" />       
         <pass name="shadow" vs="Shadow" ps="Shadow" />
     </technique>
 </material>

+ 8 - 0
Bin/Data/Materials/DefaultAlpha.xml

@@ -0,0 +1,8 @@
+<material>
+    <technique>
+        <parameter name="MatSpecProperties" value="0.5 16" />
+        <pass name="ambient" vs="Forward" ps="Forward_Ambient" depthwrite="false" blend="alpha" />
+        <pass name="light" vs="Forward" ps="Forward" depthwrite="false" blend="addalpha" />
+        <pass name="negative" vs="Forward" ps="Forward" depthwrite="false" blend="multiply" />
+    </technique>
+</material>

+ 1 - 4
Bin/Data/Materials/DefaultDiff.xml

@@ -2,14 +2,11 @@
     <technique>
         <parameter name="MatSpecProperties" value="0.5 16" />
         <pass name="deferred" vs="Deferred/GBuffer" ps="Deferred/GBuffer_Diff" />
-
         <pass name="prepass" vs="Prepass/GBuffer" ps="Prepass/GBuffer" />
         <pass name="material" vs="Prepass/Material" ps="Prepass/Material_Diff" depthwrite="false" depthtest="equal" />
-        
         <pass name="ambient" vs="Forward" ps="Forward_DiffAmbient" />
         <pass name="light" vs="Forward" ps="Forward_Diff" depthwrite="false" depthtest="equal" blend="add" />
-        <pass name="negative" vs="Forward" ps="Forward_Diff" depthwrite="false" depthtest="equal" blend="multiply" />
-        
+        <pass name="negative" vs="Forward" ps="Forward_Diff" depthwrite="false" depthtest="equal" blend="multiply" />     
         <pass name="shadow" vs="Shadow" ps="Shadow" />
     </technique>
 </material>

+ 8 - 0
Bin/Data/Materials/DefaultDiffAlpha.xml

@@ -0,0 +1,8 @@
+<material>
+    <technique>
+        <parameter name="MatSpecProperties" value="0.5 16" />
+        <pass name="ambient" vs="Forward" ps="Forward_DiffAmbient" depthwrite="false" blend="alpha" />
+        <pass name="light" vs="Forward" ps="Forward_Diff" depthwrite="false" blend="addalpha" />
+        <pass name="negative" vs="Forward" ps="Forward_Diff" depthwrite="false" blend="multiply" />
+    </technique>
+</material>

+ 12 - 0
Bin/Data/Materials/DefaultDiffAlphaMask.xml

@@ -0,0 +1,12 @@
+<material>
+    <technique>
+        <parameter name="MatSpecProperties" value="0.5 16" />
+        <pass name="deferred" vs="Deferred/GBuffer" ps="Deferred/GBuffer_DiffMask" alphamask="true" />
+        <pass name="prepass" vs="Prepass/GBuffer" ps="Prepass/GBuffer_Mask" alphamask="true" />
+        <pass name="material" vs="Prepass/Material" ps="Prepass/Material_DiffMask" alphamask="true" depthwrite="false" depthtest="equal" />
+        <pass name="ambient" vs="Forward" ps="Forward_DiffAmbient" alphatest="true" />
+        <pass name="light" vs="Forward" ps="Forward_Diff" alphatest="true" depthwrite="false" depthtest="equal" blend="add" />
+        <pass name="negative" vs="Forward" ps="Forward_Diff" alphatest="true" depthwrite="false" depthtest="equal" blend="multiply" />  
+        <pass name="shadow" vs="Shadow" ps="Shadow_Mask" alphamask="true" />
+    </technique>
+</material>

+ 0 - 15
Bin/Data/Materials/DefaultDiffInstanced.xml

@@ -1,15 +0,0 @@
-<material>
-    <technique>
-        <parameter name="MatSpecProperties" value="0.5 16" />
-        <pass name="deferred" vs="Deferred/GBuffer_Instanced" ps="Deferred/GBuffer_Diff" />
-
-        <pass name="prepass" vs="Prepass/GBuffer_Instanced" ps="Prepass/GBuffer" />
-        <pass name="material" vs="Prepass/Material_Instanced" ps="Prepass/Material_Diff" depthwrite="false" depthtest="equal" />
-        
-        <pass name="ambient" vs="Forward_Instanced" ps="Forward_DiffAmbient" />
-        <pass name="light" vs="Forward_Instanced" ps="Forward_Diff" depthwrite="false" depthtest="equal" blend="add" />
-        <pass name="negative" vs="Forward_Instanced" ps="Forward_Diff" depthwrite="false" depthtest="equal" blend="multiply" />
-        
-        <pass name="shadow" vs="Shadow_Instanced" ps="Shadow" />
-    </technique>
-</material>

+ 12 - 0
Bin/Data/Materials/DefaultDiffNormal.xml

@@ -0,0 +1,12 @@
+<material>
+    <technique>
+        <parameter name="MatSpecProperties" value="0.5 16" />
+        <pass name="deferred" vs="Deferred/GBuffer_Normal" ps="Deferred/GBuffer_DiffNormal" />
+        <pass name="prepass" vs="Prepass/GBuffer_Normal" ps="Prepass/GBuffer_Normal" />
+        <pass name="material" vs="Prepass/Material" ps="Prepass/Material_Diff" depthwrite="false" depthtest="equal" />
+        <pass name="ambient" vs="Forward" ps="Forward_DiffAmbient" />
+        <pass name="light" vs="Forward_Normal" ps="Forward_DiffNormal" depthwrite="false" depthtest="equal" blend="add" />
+        <pass name="negative" vs="Forward" ps="Forward_Diff" depthwrite="false" depthtest="equal" blend="multiply" />       
+        <pass name="shadow" vs="Shadow" ps="Shadow" />
+    </technique>
+</material>

+ 12 - 0
Bin/Data/Materials/DefaultDiffNormalAlphaMask.xml

@@ -0,0 +1,12 @@
+<material>
+    <technique>
+        <parameter name="MatSpecProperties" value="0.5 16" />
+        <pass name="deferred" vs="Deferred/GBuffer_Normal" ps="Deferred/GBuffer_DiffNormalMask" alphamask="true" />
+        <pass name="prepass" vs="Prepass/GBuffer_Normal" ps="Prepass/GBuffer_NormalMask" alphamask="true" />
+        <pass name="material" vs="Prepass/Material" ps="Prepass/Material_DiffMask" alphamask="true" depthwrite="false" depthtest="equal" />
+        <pass name="ambient" vs="Forward" ps="Forward_DiffAmbient" alphatest="true" />
+        <pass name="light" vs="Forward_Normal" ps="Forward_DiffNormal" alphatest="true" depthwrite="false" depthtest="equal" blend="add" />
+        <pass name="negative" vs="Forward" ps="Forward_Diff" alphatest="true" depthwrite="false" depthtest="equal" blend="multiply" />
+        <pass name="shadow" vs="Shadow" ps="Shadow_Mask" alphamask="true" />
+    </technique>
+</material>

+ 0 - 14
Bin/Data/Materials/DefaultDiffSkinned.xml

@@ -1,14 +0,0 @@
-<material>
-    <technique>
-        <parameter name="MatSpecProperties" value="0.5 16" />
-        <pass name="deferred" vs="Deferred/GBuffer_Skinned" ps="Deferred/GBuffer_Diff" />
-
-        <pass name="prepass" vs="Prepass/GBuffer_Skinned" ps="Prepass/GBuffer" />
-        <pass name="material" vs="Prepass/Material_Skinned" ps="Prepass/Material_Diff" depthwrite="false" depthtest="equal" />
-
-        <pass name="ambient" vs="Forward_Skinned" ps="Forward_DiffAmbient" />
-        <pass name="light" vs="Forward_Skinned" ps="Forward_Diff" depthwrite="false" depthtest="equal" blend="add" />
-        <pass name="negative" vs="Forward_Skinned" ps="Forward_Diff" depthwrite="false" depthtest="equal" blend="multiply" />
-        <pass name="shadow" vs="Shadow_Skinned" ps="Shadow" />
-    </technique>
-</material>

+ 0 - 15
Bin/Data/Materials/DefaultInstanced.xml

@@ -1,15 +0,0 @@
-<material>
-    <technique>
-        <parameter name="MatSpecProperties" value="0.5 16" />
-        <pass name="deferred" vs="Deferred/GBuffer_Instanced" ps="Deferred/GBuffer" />
-
-        <pass name="prepass" vs="Prepass/GBuffer_Instanced" ps="Prepass/GBuffer" />
-        <pass name="material" vs="Prepass/Material_Instanced" ps="Prepass/Material" depthwrite="false" depthtest="equal" />
-
-        <pass name="ambient" vs="Forward_Instanced" ps="Forward_Ambient" />
-        <pass name="light" vs="Forward_Instanced" ps="Forward" depthwrite="false" depthtest="equal" blend="add" />
-        <pass name="negative" vs="Forward_Instanced" ps="Forward" depthwrite="false" depthtest="equal" blend="multiply" />
-        
-        <pass name="shadow" vs="Shadow_Instanced" ps="Shadow" />
-    </technique>
-</material>

+ 0 - 15
Bin/Data/Materials/DefaultSkinned.xml

@@ -1,15 +0,0 @@
-<material>
-    <technique>
-        <parameter name="MatSpecProperties" value="0.5 16" />
-        <pass name="deferred" vs="Deferred/GBuffer_Skinned" ps="Deferred/GBuffer" />
-
-        <pass name="prepass" vs="Prepass/GBuffer_Skinned" ps="Prepass/GBuffer" />
-        <pass name="material" vs="Prepass/Material_Skinned" ps="Prepass/Material" depthwrite="false" depthtest="equal" />
-
-        <pass name="ambient" vs="Forward_Skinned" ps="Forward_Ambient" />
-        <pass name="light" vs="Forward_Skinned" ps="Forward" depthwrite="false" depthtest="equal" blend="add" />
-        <pass name="negative" vs="Forward_Skinned" ps="Forward" depthwrite="false" depthtest="equal" blend="multiply" />
-
-        <pass name="shadow" vs="Shadow_Skinned" ps="Shadow" />
-    </technique>
-</material>

+ 1 - 1
Bin/Data/Materials/Jack.xml

@@ -1,3 +1,3 @@
 <material>
-    <base name="Materials/DefaultSkinned.xml" />
+    <base name="Materials/Default.xml" />
 </material>

+ 0 - 3
Bin/Data/Materials/JackStatic.xml

@@ -1,3 +0,0 @@
-<material>
-    <base name="Materials/Default.xml" />
-</material>

+ 1 - 1
Bin/Data/Materials/Mushroom.xml

@@ -1,5 +1,5 @@
 <material>
-    <base name="Materials/DefaultDiffSkinned.xml" />
+    <base name="Materials/DefaultDiff.xml" />
     <technique>
         <texture unit="diffuse" name="Textures/Mushroom.dds" />
         <parameter name="MatSpecProperties" value="0.1 16" />

+ 0 - 7
Bin/Data/Materials/MushroomInstanced.xml

@@ -1,7 +0,0 @@
-<material>
-    <base name="Materials/DefaultDiffInstanced.xml" />
-    <technique>
-        <texture unit="diffuse" name="Textures/Mushroom.dds" />
-        <parameter name="MatSpecProperties" value="0.1 16" />
-    </technique>
-</material>

+ 0 - 7
Bin/Data/Materials/MushroomStatic.xml

@@ -1,7 +0,0 @@
-<material>
-    <base name="Materials/DefaultDiff.xml" />
-    <technique>
-        <texture unit="diffuse" name="Textures/Mushroom.dds" />
-        <parameter name="MatSpecProperties" value="0.1 16" />
-    </technique>
-</material>

+ 1 - 1
Bin/Data/Materials/Ninja.xml

@@ -1,5 +1,5 @@
 <material>
-    <base name="Materials/DefaultDiffSkinned.xml" />
+    <base name="Materials/DefaultDiff.xml" />
     <technique>
         <texture unit="diffuse" name="Textures/Ninja.dds" />
         <parameter name="MatSpecProperties" value="0 0" />

+ 1 - 13
Bin/Data/Materials/Test.xml

@@ -1,31 +1,19 @@
 <material>
+    <base name="Materials/DefaultDiffNormal.xml" />
     <technique quality="1">
         <texture unit="diffuse" name="Textures/Diffuse.dds" />
         <texture unit="normal" name="Textures/Normal.dds" />
         <parameter name="MatSpecProperties" value="0.5 16" />
-        <pass name="deferred" vs="Deferred/GBuffer_Normal" ps="Deferred/GBuffer_DiffNormal" />
-
-        <pass name="prepass" vs="Prepass/GBuffer_Normal" ps="Prepass/GBuffer_Normal" />
-        <pass name="material" vs="Prepass/Material" ps="Prepass/Material_Diff" depthwrite="false" depthtest="equal" />
-
-        <pass name="ambient" vs="Forward" ps="Forward_DiffAmbient" />
-        <pass name="light" vs="Forward_Normal" ps="Forward_DiffNormal" depthwrite="false" depthtest="equal" blend="add" />
-        <pass name="negative" vs="Forward" ps="Forward_Diff" depthwrite="false" depthtest="equal" blend="multiply" />
-
-        <pass name="shadow" vs="Shadow" ps="Shadow" />
     </technique>
     <technique quality="0">
         <texture unit="diffuse" name="Textures/Diffuse.dds" />
         <parameter name="MatSpecProperties" value="0.5 16" />
         <pass name="deferred" vs="Deferred/GBuffer" ps="Deferred/GBuffer_Diff" />
-
         <pass name="prepass" vs="Prepass/GBuffer" ps="Prepass/GBuffer" />
         <pass name="material" vs="Prepass/Material" ps="Prepass/Material_Diff" depthwrite="false" depthtest="equal" />
-
         <pass name="ambient" vs="Forward" ps="Forward_DiffAmbient" />
         <pass name="light" vs="Forward" ps="Forward_Diff" depthwrite="false" depthtest="equal" blend="add" />
         <pass name="negative" vs="Forward" ps="Forward_Diff" depthwrite="false" depthtest="equal" blend="multiply" />
-
         <pass name="shadow" vs="Shadow" ps="Shadow" />
     </technique>
 </material>

+ 2 - 1
Bin/Data/UI/UI.xml

@@ -11,6 +11,7 @@
         <hoverrect value="32 0 48 16" />
         <pressedrect value="48 0 64 16" />
         <border value="5 5 5 5" />
+        <labeloffset value="0 0" />
     </element>
     <element name="Window">
         <texture name="Textures/UI.png" />
@@ -21,6 +22,6 @@
         <resizable enable="true" />
     </element>
     <element name="Text">
-        <font name="Fonts/Cour.ttf" size="10" />
+        <font name="Cour.ttf" size="10" />
     </element>
 </elements>

+ 1 - 1
Engine/Engine/RegisterEngine.cpp

@@ -115,7 +115,7 @@ static void registerConnection(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Connection", "const Vector3& getPosition() const", asMETHOD(Connection, getPosition), asCALL_THISCALL);
     
     // Register Variant getPtr() for Connection
-    engine->RegisterObjectMethod("Connection", "Connection@+ getConnection() const", asFUNCTION(getVariantPtr<Connection>), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("Variant", "Connection@+ getConnection() const", asFUNCTION(getVariantPtr<Connection>), asCALL_CDECL_OBJLAST);
     
     engine->RegisterObjectType("NetUpdateInfo", sizeof(NetUpdateInfo), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C);
     engine->RegisterObjectBehaviour("NetUpdateInfo", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructNetUpdateInfo), asCALL_CDECL_OBJLAST);

+ 4 - 5
Engine/Engine/RegisterUI.cpp

@@ -71,7 +71,7 @@ static void registerUIElement(asIScriptEngine* engine)
     engine->RegisterObjectMethod("UIElement", "void loadParameters(XMLFile@+, const string& in)", asFUNCTION(UIElementLoadParameters), asCALL_CDECL_OBJLAST);
     
     // Register Variant getPtr() for UIElement
-    engine->RegisterObjectMethod("UIElement", "UIElement@+ getUIElement() const", asFUNCTION(getVariantPtr<UIElement>), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("Variant", "UIElement@+ getUIElement() const", asFUNCTION(getVariantPtr<UIElement>), asCALL_CDECL_OBJLAST);
 }
 
 static void registerText(asIScriptEngine* engine)
@@ -121,15 +121,14 @@ static void registerButton(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Button", "void setHoverRect(int, int, int, int)", asMETHODPR(Button, setHoverRect, (int, int, int, int), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Button", "void setPressedRect(const IntRect& in)", asMETHODPR(Button, setPressedRect, (const IntRect&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Button", "void setPressedRect(int, int, int, int)", asMETHODPR(Button, setPressedRect, (int, int, int, int), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "void setHoverDelay(float)", asMETHOD(Button, setHoverDelay), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "void setPresseddelay(float)", asMETHOD(Button, setPressedDelay), asCALL_THISCALL);
     engine->RegisterObjectMethod("Button", "void setLabel(UIElement@+)", asMETHOD(Button, setLabel), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Button", "void setLabelOffset(const IntVector2& in)", asMETHODPR(Button, setLabelOffset, (const IntVector2&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Button", "void setLabelOffset(int, int)", asMETHODPR(Button, setLabelOffset, (int, int), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Button", "const IntRect& getInactiveRect() const", asMETHOD(Button, getInactiveRect), asCALL_THISCALL);
     engine->RegisterObjectMethod("Button", "const IntRect& getHoverRect() const", asMETHOD(Button, getHoverRect), asCALL_THISCALL);
     engine->RegisterObjectMethod("Button", "const IntRect& getPressedRect() const", asMETHOD(Button, getPressedRect), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "float getHoverDelay() const", asMETHOD(Button, getHoverDelay), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "float getPressedDelay() const", asMETHOD(Button, getPressedDelay), asCALL_THISCALL);
     engine->RegisterObjectMethod("Button", "UIElement@+ getLabel() const", asMETHOD(Button, getLabel), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Button", "const IntVector2& getLabelOffset() const", asMETHOD(Button, getLabelOffset), asCALL_THISCALL);
     registerRefCasts<UIElement, Button>(engine, "UIElement", "Button");
 }
 

+ 33 - 1
Engine/Math/MathDefs.h

@@ -36,7 +36,7 @@ static const int M_MAX_INT = 0x7fffffff;
 static const unsigned M_MIN_UNSIGNED = 0x00000000;
 static const unsigned M_MAX_UNSIGNED = 0xffffffff;
 
-static const float M_EPSILON = 0.0000001f;
+static const float M_EPSILON = 0.000001f;
 static const float M_MIN_NEARCLIP = 0.01f;
 static const float M_MAX_FOV = 160.0f;
 static const float M_LARGE_VALUE = 100000000.0f;
@@ -130,4 +130,36 @@ inline bool isPowerOfTwo(unsigned value)
     return value == 1;
 }
 
+//! Fast square root
+inline float fastSqrt(float x)
+{
+    union
+    {
+        float f;
+        int i;
+    } u;
+    
+    u.f = x;
+    u.i -= 1 << 23;
+    u.i >>= 1;
+    u.i += 1 << 29;
+    return u.f;
+}
+
+//! Fast inverse square root
+inline float fastInvSqrt(float x)
+{
+    union
+    {
+        float f;
+        int i;
+    } u;
+    
+    float xHalf = 0.5f * x;
+    u.f = x;
+    u.i = 0x5f3759df - (u.i >> 1);
+    x = u.f * (1.5f - xHalf * u.f * u.f);
+    return x;
+}
+
 #endif // MATH_MATHDEFS_H

+ 23 - 0
Engine/Math/Quaternion.h

@@ -167,6 +167,16 @@ public:
         return len;
     }
     
+    //! Normalize to unit length using fast inverse square root
+    void normalizeFast()
+    {
+        float invLen = fastInvSqrt(mW * mW + mX * mX + mY * mY + mZ * mZ);
+        mW *= invLen;
+        mX *= invLen;
+        mY *= invLen;
+        mZ *= invLen;
+    }
+    
     //! Set from an angle (in degrees) and axis
     void fromAngleAxis(float angle, const Vector3& axis);
     //! Set from Euler angles (in degrees)
@@ -185,6 +195,13 @@ public:
         return *this * invLen;
     }
     
+    //! Return normalized to unit length, using fast inverse square root
+    Quaternion getNormalizedFast() const
+    {
+        float invLen = fastInvSqrt(mW * mW + mX * mX + mY * mY + mZ * mZ);
+        return *this * invLen;
+    }
+    
     //! Return inverse
     Quaternion getInverse() const
     {
@@ -219,6 +236,12 @@ public:
         return (*this * (1.0f - t) + rhs * t).getNormalized();
     }
     
+    //! Normalized interpolation with another quaternion, using fast inverse square root
+    Quaternion nlerpFast(const Quaternion& rhs, float t) const
+    {
+        return (*this * (1.0f - t) + rhs * t).getNormalizedFast();
+    }
+    
     //! Return Euler angles (in degrees)
     void getEulerAngles(float& angleX, float& angleY, float& angleZ) const;
     //! Return yaw angle in degrees

+ 21 - 0
Engine/Math/Vector2.h

@@ -187,12 +187,26 @@ public:
         return len;
     }
     
+    //! Normalize to unit length using fast inverse square root
+    void normalizeFast()
+    {
+        float invLen = fastInvSqrt(mX * mX + mY * mY);
+        mX *= invLen;
+        mY *= invLen;
+    }
+    
     //! Return length
     float getLength() const
     {
         return sqrtf(mX * mX + mY * mY);
     }
     
+    //! Return length using fast square root
+    float getLengthFast() const
+    {
+        return fastSqrt(mX * mX + mY * mY);
+    }
+    
     //! Return squared length
     float getLengthSquared() const
     {
@@ -228,6 +242,13 @@ public:
         return *this * invLen;
     }
     
+    //! Return normalized to unit length using fast inverse square root
+    Vector2 getNormalizedFast() const
+    {
+        float invLen = fastInvSqrt(mX * mX + mY * mY);
+        return *this * invLen;
+    }
+    
     //! Return float data
     const float* getData() const { return &mX; }
     

+ 22 - 0
Engine/Math/Vector3.h

@@ -206,12 +206,27 @@ public:
         return len;
     }
     
+    //! Normalize to unit length using fast inverse square root
+    void normalizeFast()
+    {
+        float invLen = fastInvSqrt(mX * mX + mY * mY + mZ * mZ);
+        mX *= invLen;
+        mY *= invLen;
+        mZ *= invLen;
+    }
+    
     //! Return length
     float getLength() const
     {
         return sqrtf(mX * mX + mY * mY + mZ * mZ);
     }
     
+    //! Return length using fast square root
+    float getLengthFast() const
+    {
+        return fastSqrt(mX * mX + mY * mY + mZ * mZ);
+    }
+    
     //! Return squared length
     float getLengthSquared() const
     {
@@ -257,6 +272,13 @@ public:
         return *this * invLen;
     }
     
+    //! Return normalized to unit length using fast inverse square root
+    Vector3 getNormalizedFast() const
+    {
+        float invLen = fastInvSqrt(mX * mX + mY * mY + mZ * mZ);
+        return *this * invLen;
+    }
+    
     //! Return float data
     const float* getData() const { return &mX; }
     

+ 2 - 2
Engine/Physics/RigidBody.cpp

@@ -372,8 +372,8 @@ void RigidBody::readNetUpdate(Deserializer& source, ResourceCache* cache, const
     if (bits & (1 | 2 | 64 | 128))
     {
         // If the last network update was stationary, forcibly disable the body so it will not start drifting
-        bool active = (getLinearVelocity().getLength() > getLinearRestThreshold()) ||
-            (getAngularVelocity().getLength() > getAngularRestThreshold());
+        bool active = (getLinearVelocity().getLengthFast() > getLinearRestThreshold()) ||
+            (getAngularVelocity().getLengthFast() > getAngularRestThreshold());
         
         if (!active)
         {

+ 2 - 0
Engine/Renderer/AnimatedModel.h

@@ -69,6 +69,8 @@ public:
     virtual void updateDistance(const FrameInfo& frame);
     //! Prepare geometry for rendering
     virtual void updateGeometry(const FrameInfo& frame, Renderer* renderer);
+    //! Return geometry type, determines vertex shader variation
+    virtual GeometryType getGeometryType() { return GEOM_SKINNED; }
     //! Return vertex shader parameter
     virtual bool getVertexShaderParameter(unsigned batchIndex, VSParameter parameter, const float** data, unsigned* count);
     //! Draw debug geometry

+ 102 - 72
Engine/Renderer/AnimationState.cpp

@@ -278,117 +278,147 @@ void AnimationState::apply()
     if (!isEnabled())
         return;
     
-    for (std::map<unsigned, Bone*>::const_iterator i = mTrackToBoneMap.begin(); i != mTrackToBoneMap.end(); ++i)
+    // Check first if full weight or blending
+    if (mWeight == 1.0f)
     {
-        const AnimationTrack* track = mAnimation->getTrack(i->first);
-        Bone* bone = i->second;
-        
-        if ((bone->isAnimationEnabled()) && (track->mKeyFrames.size()))
+        for (std::map<unsigned, Bone*>::const_iterator i = mTrackToBoneMap.begin(); i != mTrackToBoneMap.end(); ++i)
         {
+            const AnimationTrack* track = mAnimation->getTrack(i->first);
+            Bone* bone = i->second;
+            
+            if ((!bone->isAnimationEnabled()) || (!track->mKeyFrames.size()))
+                continue;
+            
             unsigned& frame = mLastKeyFrame[i->first];
             track->getKeyFrameIndex(mTime, frame);
             
             // Check if next frame to interpolate to is valid, or if wrapping is needed (looping animation only)
             unsigned nextFrame = frame + 1;
             bool interpolate = true;
-            if (!mLooped)
+            if (nextFrame >= track->mKeyFrames.size())
             {
-                if (nextFrame >= track->mKeyFrames.size())
+                if (!mLooped)
                 {
                     nextFrame = frame;
                     interpolate = false;
                 }
-            }
-            else
-            {
-                if (nextFrame >= track->mKeyFrames.size())
+                else
                     nextFrame = 0;
             }
             
             const AnimationKeyFrame* keyFrame = &track->mKeyFrames[frame];
-            const AnimationKeyFrame* nextKeyFrame = &track->mKeyFrames[nextFrame];
-            float timeInterval = nextKeyFrame->mTime - keyFrame->mTime;
-            if (timeInterval < 0.0f)
-                timeInterval += mAnimation->getLength();
-            
             unsigned char channelMask = track->mChannelMask;
             
             if (!interpolate)
             {
                 // No interpolation, full weight
-                if (mWeight == 1.0f)
+                if (channelMask & CHANNEL_POSITION)
+                    bone->setPosition(keyFrame->mPosition);
+                if (channelMask & CHANNEL_ROTATION)
+                    bone->setRotation(keyFrame->mRotation);
+                if (channelMask & CHANNEL_SCALE)
+                    bone->setScale(keyFrame->mScale);
+            }
+            else
+            {
+                const AnimationKeyFrame* nextKeyFrame = &track->mKeyFrames[nextFrame];
+                float timeInterval = nextKeyFrame->mTime - keyFrame->mTime;
+                if (timeInterval < 0.0f)
+                    timeInterval += mAnimation->getLength();
+                float t = clamp((mTime - keyFrame->mTime) / timeInterval, 0.0f, 1.0f);
+                
+                // Interpolation, full weight
+                if (channelMask & CHANNEL_POSITION)
+                    bone->setPosition(keyFrame->mPosition.lerp(nextKeyFrame->mPosition, t));
+                if (channelMask & CHANNEL_ROTATION)
                 {
-                    if (channelMask & CHANNEL_POSITION)
-                        bone->setPosition(keyFrame->mPosition);
-                    if (channelMask & CHANNEL_ROTATION)
-                        bone->setRotation(keyFrame->mRotation);
-                    if (channelMask & CHANNEL_SCALE)
-                        bone->setScale(keyFrame->mScale);
+                    if (!mUseNlerp)
+                        bone->setRotation(keyFrame->mRotation.slerp(nextKeyFrame->mRotation, t));
+                    else
+                        bone->setRotation(keyFrame->mRotation.nlerpFast(nextKeyFrame->mRotation, t));
+                }
+                if (channelMask & CHANNEL_SCALE)
+                    bone->setScale(keyFrame->mScale.lerp(nextKeyFrame->mScale, t));
+            }
+        }
+    }
+    else
+    {
+        for (std::map<unsigned, Bone*>::const_iterator i = mTrackToBoneMap.begin(); i != mTrackToBoneMap.end(); ++i)
+        {
+            const AnimationTrack* track = mAnimation->getTrack(i->first);
+            Bone* bone = i->second;
+            
+            if ((!bone->isAnimationEnabled()) || (!track->mKeyFrames.size()))
+                continue;
+            
+            unsigned& frame = mLastKeyFrame[i->first];
+            track->getKeyFrameIndex(mTime, frame);
+            
+            // Check if next frame to interpolate to is valid, or if wrapping is needed (looping animation only)
+            unsigned nextFrame = frame + 1;
+            bool interpolate = true;
+            if (nextFrame >= track->mKeyFrames.size())
+            {
+                if (!mLooped)
+                {
+                    nextFrame = frame;
+                    interpolate = false;
                 }
-                // No interpolation, blend between old transform & animation
                 else
+                    nextFrame = 0;
+            }
+            
+            const AnimationKeyFrame* keyFrame = &track->mKeyFrames[frame];
+            unsigned char channelMask = track->mChannelMask;
+            
+            if (!interpolate)
+            {
+                // No interpolation, blend between old transform & animation
+                if (channelMask & CHANNEL_POSITION)
+                    bone->setPosition(bone->getPosition().lerp(keyFrame->mPosition, mWeight));
+                if (channelMask & CHANNEL_ROTATION)
                 {
-                    if (channelMask & CHANNEL_POSITION)
-                        bone->setPosition(bone->getPosition().lerp(keyFrame->mPosition, mWeight));
-                    if (channelMask & CHANNEL_ROTATION)
-                    {
-                        if (!mUseNlerp)
-                            bone->setRotation(bone->getRotation().slerp(keyFrame->mRotation, mWeight));
-                        else
-                            bone->setRotation(bone->getRotation().nlerp(keyFrame->mRotation, mWeight));
-                    }
-                    if (channelMask & CHANNEL_SCALE)
-                        bone->setScale(bone->getScale().lerp(keyFrame->mScale, mWeight));
+                    if (!mUseNlerp)
+                        bone->setRotation(bone->getRotation().slerp(keyFrame->mRotation, mWeight));
+                    else
+                        bone->setRotation(bone->getRotation().nlerpFast(keyFrame->mRotation, mWeight));
                 }
+                if (channelMask & CHANNEL_SCALE)
+                    bone->setScale(bone->getScale().lerp(keyFrame->mScale, mWeight));
             }
             else
             {
-                float t = 1.0f;
-                if (timeInterval > 0.0f)
-                    t = (mTime - keyFrame->mTime) / timeInterval;
+                const AnimationKeyFrame* nextKeyFrame = &track->mKeyFrames[nextFrame];
+                float timeInterval = nextKeyFrame->mTime - keyFrame->mTime;
+                if (timeInterval < 0.0f)
+                    timeInterval += mAnimation->getLength();
+                float t = clamp((mTime - keyFrame->mTime) / timeInterval, 0.0f, 1.0f);
                 
-                // Interpolation, full weight
-                if (mWeight == 1.0f)
+                // Interpolation, blend between old transform & animation
+                if (channelMask & CHANNEL_POSITION)
                 {
-                    if (channelMask & CHANNEL_POSITION)
-                        bone->setPosition(keyFrame->mPosition.lerp(nextKeyFrame->mPosition, t));
-                    if (channelMask & CHANNEL_ROTATION)
-                    {
-                        if (!mUseNlerp)
-                            bone->setRotation(keyFrame->mRotation.slerp(nextKeyFrame->mRotation, t));
-                        else
-                            bone->setRotation(keyFrame->mRotation.nlerp(nextKeyFrame->mRotation, t));
-                    }
-                    if (channelMask & CHANNEL_SCALE)
-                        bone->setScale(keyFrame->mScale.lerp(nextKeyFrame->mScale, t));
+                    bone->setPosition(bone->getPosition().lerp(
+                        keyFrame->mPosition.lerp(nextKeyFrame->mPosition, t), mWeight));
                 }
-                // Interpolation, blend between old transform & animation
-                else
+                if (channelMask & CHANNEL_ROTATION)
                 {
-                    if (channelMask & CHANNEL_POSITION)
+                    if (!mUseNlerp)
                     {
-                        bone->setPosition(bone->getPosition().lerp(
-                            keyFrame->mPosition.lerp(nextKeyFrame->mPosition, t), mWeight));
+                        bone->setRotation(bone->getRotation().slerp(
+                            keyFrame->mRotation.slerp(nextKeyFrame->mRotation, t), mWeight));
                     }
-                    if (channelMask & CHANNEL_ROTATION)
+                    else
                     {
-                        if (!mUseNlerp)
-                        {
-                            bone->setRotation(bone->getRotation().slerp(
-                                keyFrame->mRotation.slerp(nextKeyFrame->mRotation, t), mWeight));
-                        }
-                        else
-                        {
-                            bone->setRotation(bone->getRotation().nlerp(
-                                keyFrame->mRotation.nlerp(nextKeyFrame->mRotation, t), mWeight));
-                        }
-                    }
-                    if (channelMask & CHANNEL_SCALE)
-                    {
-                        bone->setScale(bone->getScale().lerp(
-                            keyFrame->mScale.lerp(nextKeyFrame->mScale, t), mWeight));
+                        bone->setRotation(bone->getRotation().nlerpFast(
+                            keyFrame->mRotation.nlerpFast(nextKeyFrame->mRotation, t), mWeight));
                     }
                 }
+                if (channelMask & CHANNEL_SCALE)
+                {
+                    bone->setScale(bone->getScale().lerp(
+                        keyFrame->mScale.lerp(nextKeyFrame->mScale, t), mWeight));
+                }
             }
         }
     }

+ 2 - 2
Engine/Renderer/AnimationState.h

@@ -70,7 +70,7 @@ public:
     void addTime(float delta);
     //! Set blending priority
     void setPriority(int priority);
-    //! Set nlerp use instead of slerp, default false
+    //! Set to use nlerp instead of slerp for rotation, default false
     void setUseNlerp(bool enable);
     
     //! Return animation
@@ -87,7 +87,7 @@ public:
     float getTime() const;
     //! Return blending priority
     int getPriority() const { return mPriority; }
-    //! Return whether using nlerp
+    //! Return whether using nlerp for rotation
     bool getUseNlerp() const { return mUseNlerp; }
     //! Return whether network client smoothing active
     bool isInterpolating() const { return mInterpolationFlags != 0; }

+ 1 - 1
Engine/Renderer/Batch.cpp

@@ -45,7 +45,7 @@ void Batch::calculateSortKey(bool stateHasPriority, bool frontToBack)
         distance = 65535 - distance;
     
     // Shaders
-    unsigned short shaders = mVertexShader->getHash() + mPixelShader->getHash();
+    unsigned short shaders = (mVertexShader ? mVertexShader->getHash() : 0) + (mPixelShader ? mPixelShader->getHash() : 0);
     
     // Material technique (determines textures and shader parameters)
     unsigned short technique = *((unsigned short*)&mTechnique);

+ 1 - 1
Engine/Renderer/Camera.cpp

@@ -469,7 +469,7 @@ Vector3 Camera::getUpVector()
 float Camera::getDistance(const Vector3& worldPos)
 {
     if (!mOrthographic)
-        return (worldPos - getWorldPosition()).getLength();
+        return (worldPos - getWorldPosition()).getLengthFast();
     else
         return fabsf((getInverseWorldTransform() * worldPos).mZ);
 }

+ 1 - 1
Engine/Renderer/DeferredView.cpp

@@ -212,7 +212,7 @@ void View::getBatchesDeferred()
                         Batch shadowBatch;
                         shadowBatch.mNode = node;
                         shadowBatch.mCamera = &shadowCamera;
-                        shadowBatch.mDistance = (node->getWorldPosition() - shadowCamera.getPosition()).getLength();
+                        shadowBatch.mDistance = (node->getWorldPosition() - shadowCamera.getPosition()).getLengthFast();
                         shadowBatch.mGeometry = geom;
                         shadowBatch.mBatchIndex = l;
                         shadowBatch.mForwardLight = sSplitLights[j];

+ 1 - 1
Engine/Renderer/ForwardView.cpp

@@ -160,7 +160,7 @@ void View::getBatchesForward()
                         Batch shadowBatch;
                         shadowBatch.mNode = node;
                         shadowBatch.mCamera = &shadowCamera;
-                        shadowBatch.mDistance = (node->getWorldPosition() - shadowCamera.getPosition()).getLength();
+                        shadowBatch.mDistance = (node->getWorldPosition() - shadowCamera.getPosition()).getLengthFast();
                         shadowBatch.mGeometry = geom;
                         shadowBatch.mBatchIndex = l;
                         shadowBatch.mForwardLight = sSplitLights[j];

+ 2 - 0
Engine/Renderer/GeometryNode.h

@@ -56,6 +56,8 @@ public:
     virtual void updateDistance(const FrameInfo& frame);
     //! Prepare geometry for rendering
     virtual void updateGeometry(const FrameInfo& frame, Renderer* renderer) = 0;
+    //! Return geometry type, determines vertex shader variation
+    virtual GeometryType getGeometryType() { return GEOM_STATIC; }
     //! Return number of batches
     virtual unsigned getNumBatches() = 0;
     //! Return geometry by batch index

+ 2 - 0
Engine/Renderer/InstancedModel.h

@@ -74,6 +74,8 @@ public:
     virtual void updateDistance(const FrameInfo& frame);
     //! Prepare geometry for rendering
     virtual void updateGeometry(const FrameInfo& frame, Renderer* renderer);
+    //! Return geometry type, determines vertex shader variation
+    virtual GeometryType getGeometryType() { return GEOM_INSTANCED; }
     //! Return number of batches
     virtual unsigned getNumBatches();
     //! Return geometry by batch index

+ 57 - 34
Engine/Renderer/Pipeline.cpp

@@ -168,6 +168,13 @@ static const unsigned short spotLightIndexData[] =
     7, 6, 5
 };
 
+static const std::string geometryVSVariations[] =
+{
+    "",
+    "Skinned",
+    "Instanced"
+};
+
 static const std::string gBufferPSVariations[] =
 {
     "",
@@ -497,14 +504,20 @@ VertexShader* Pipeline::getVertexShader(const std::string& name) const
 {
     // Check for extra underscore with no variations and remove
     std::string fullName = replace(mShaderPath + name + mVSFormat, "_.", ".");
-    return mCache->getResource<VertexShader>(fullName);
+    if (mCache->exists(fullName))
+        return mCache->getResource<VertexShader>(fullName);
+    else
+        return 0;
 }
 
 PixelShader* Pipeline::getPixelShader(const std::string& name) const
 {
     // Check for extra underscore with no variations and remove
     std::string fullName = replace(mShaderPath + name + mPSFormat, "_.", ".");
-    return mCache->getResource<PixelShader>(fullName);
+    if (mCache->exists(fullName))
+        return mCache->getResource<PixelShader>(fullName);
+    else
+        return 0;
 }
 
 unsigned Pipeline::getNumGeometries(bool allViews) const
@@ -682,42 +695,36 @@ Texture2D* Pipeline::getShadowMap(float resolution)
 
 void Pipeline::setBatchShaders(Batch& batch, MaterialTechnique* technique, MaterialPass* pass, bool allowShadows)
 {
+    static std::set<Material*> errorDisplayed;
+    
+    batch.mTechnique = technique;
+    batch.mPass = pass;
+    
     // Check if shaders are unloaded or need reloading
     std::vector<SharedPtr<VertexShader> >& vertexShaders = pass->getVertexShaders();
     std::vector<SharedPtr<PixelShader> >& pixelShaders = pass->getPixelShaders();
-    
     if ((!vertexShaders.size()) || (!pixelShaders.size()) || (technique->getShadersLoadedFrameNumber() !=
         mShadersChangedFrameNumber))
     {
         // First release all previous shaders, then load
         technique->releaseShaders();
         loadMaterialShaders(technique);
-        
-        // Make sure that shaders for this pass are loaded now
-        if ((!vertexShaders.size()) || (!pixelShaders.size()))
-        {
-            // Do not log error, as it would result in a lot of spam
-            batch.mVertexShader = 0;
-            batch.mPixelShader = 0;
-            batch.mTechnique = 0;
-            batch.mPass = 0;
-            return;
-        }
     }
     
-    batch.mTechnique = technique;
-    batch.mPass = pass;
-    
-    // Recognize light / vertex light pass from the amount of shaders
-    if ((vertexShaders.size() == 1) && (pixelShaders.size() == 1))
+    // Make sure shaders are loaded now
+    if ((vertexShaders.size()) && (pixelShaders.size()))
     {
-        batch.mVertexShader = vertexShaders[0];
-        batch.mPixelShader = pixelShaders[0];
-    }
-    else
-    {
-        // Light pass
-        if (pixelShaders.size() > 1)
+        // Recognize light pass from the amount of shaders
+        if (pixelShaders.size() == 1)
+        {
+            unsigned vsi = 0;
+            if (batch.mNode->getNodeFlags() & NODE_GEOMETRY)
+                vsi = static_cast<GeometryNode*>(batch.mNode)->getGeometryType();
+            
+            batch.mVertexShader = vertexShaders[vsi];
+            batch.mPixelShader = pixelShaders[0];
+        }
+        else
         {
             Light* light = batch.mForwardLight;
             if (!light)
@@ -730,6 +737,8 @@ void Pipeline::setBatchShaders(Batch& batch, MaterialTechnique* technique, Mater
             
             unsigned vsi = 0;
             unsigned psi = 0;
+            if (batch.mNode->getNodeFlags() & NODE_GEOMETRY)
+                vsi = static_cast<GeometryNode*>(batch.mNode)->getGeometryType() * MAX_LIGHT_VS_VARIATIONS;
             
             // Negative lights have no specular or shadows
             if (!light->isNegative())
@@ -762,6 +771,17 @@ void Pipeline::setBatchShaders(Batch& batch, MaterialTechnique* technique, Mater
             batch.mPixelShader = pixelShaders[psi];
         }
     }
+    
+    // Log error if shaders could not be assigned, but only once per material
+    if ((!batch.mVertexShader) || (!batch.mPixelShader))
+    {
+        Material* parentMat = technique->getParent();
+        if (errorDisplayed.find(parentMat) == errorDisplayed.end())
+        {
+            errorDisplayed.insert(parentMat);
+            LOGERROR("Material " + parentMat->getName() + " has missing shaders");
+        }
+    }
 }
 
 void Pipeline::setLightVolumeShaders(Batch& batch)
@@ -939,21 +959,24 @@ void Pipeline::loadMaterialPassShaders(MaterialTechnique* technique, PassType pa
     switch (i->first)
     {
     default:
-        vertexShaders.resize(1);
+        vertexShaders.resize(MAX_GEOMETRYTYPES);
         pixelShaders.resize(1);
-        vertexShaders[0] = getVertexShader(vertexShaderName);
+        for (unsigned j = 0; j < MAX_GEOMETRYTYPES; ++j)
+            vertexShaders[j] = getVertexShader(vertexShaderName + geometryVSVariations[j]);
         pixelShaders[0] = getPixelShader(pixelShaderName);
         break;
         
     case PASS_LIGHT:
     case PASS_NEGATIVE:
-        vertexShaders.resize(MAX_LIGHT_VS_VARIATIONS);
+        vertexShaders.resize(MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS);
         pixelShaders.resize(MAX_LIGHT_PS_VARIATIONS);
         
-        for (unsigned j = 0; j < MAX_LIGHT_VS_VARIATIONS; ++j)
+        for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS; ++j)
         {
-            if ((!(j & LVS_SHADOW)) || (allowShadows))
-                vertexShaders[j] = getVertexShader(vertexShaderName + lightVSVariations[j]);
+            unsigned g = j / MAX_LIGHT_VS_VARIATIONS;
+            unsigned l = j % MAX_LIGHT_VS_VARIATIONS;
+            if ((!(l & LVS_SHADOW)) || (allowShadows))
+                vertexShaders[j] = getVertexShader(vertexShaderName + lightVSVariations[l] + geometryVSVariations[g]);
             else
                 vertexShaders[j].reset();
         }
@@ -1137,7 +1160,7 @@ void Pipeline::setupLightBatch(Batch& batch)
     light->overrideTransforms(0, *batch.mCamera, &model, &view);
     
     float lightExtent = light->getVolumeExtent();
-    float lightViewDist = (light->getWorldPosition() - batch.mCamera->getWorldPosition()).getLength();
+    float lightViewDist = (light->getWorldPosition() - batch.mCamera->getWorldPosition()).getLengthFast();
     
     mRenderer->setAlphaTest(false);
     mRenderer->setBlendMode(light->isNegative() ? BLEND_MULTIPLY : BLEND_ADD);
@@ -1267,7 +1290,7 @@ void Pipeline::drawSplitLightToStencil(Camera& camera, Light* light, bool clear)
             light->overrideTransforms(0, camera, &model, &view);
             
             float lightExtent = light->getVolumeExtent();
-            float lightViewDist = (light->getWorldPosition() - camera.getWorldPosition()).getLength();
+            float lightViewDist = (light->getWorldPosition() - camera.getWorldPosition()).getLengthFast();
             bool drawBackFaces = lightViewDist < (lightExtent + camera.getNearClip());
             
             mRenderer->setAlphaTest(false);

+ 9 - 0
Engine/Renderer/RendererDefs.h

@@ -39,6 +39,15 @@ enum PrimitiveType
     LINE_LIST
 };
 
+//! Geometry type
+enum GeometryType
+{
+    GEOM_STATIC = 0,
+    GEOM_SKINNED,
+    GEOM_INSTANCED,
+    MAX_GEOMETRYTYPES
+};
+
 //! Blending mode
 enum BlendMode
 {

+ 2 - 2
Engine/Renderer/View.cpp

@@ -361,7 +361,7 @@ void View::updateOccluders(std::vector<GeometryNode*>& occluders, Camera& camera
         
         // Check that occluder is big enough on the screen
         const BoundingBox& box = node->getWorldBoundingBox();
-        float diagonal = (box.mMax - box.mMin).getLength();
+        float diagonal = (box.mMax - box.mMin).getLengthFast();
         float compare;
         if (!camera.isOrthographic())
             compare = diagonal * halfViewSize / node->getDistance();
@@ -804,7 +804,7 @@ bool View::isShadowCasterVisible(GeometryNode* geom, BoundingBox lightViewBox, c
         Ray extrusionRay(center, center.getNormalized());
         
         float extrusionDistance = shadowCamera.getFarClip();
-        float originalDistance = clamp(center.getLength(), M_EPSILON, extrusionDistance);
+        float originalDistance = clamp(center.getLengthFast(), M_EPSILON, extrusionDistance);
         
         // Because of the perspective, the bounding box must also grow when it is extruded to the distance
         float sizeFactor = extrusionDistance / originalDistance;

+ 1 - 1
Engine/Renderer/VolumeNode.cpp

@@ -282,7 +282,7 @@ void VolumeNode::setLightMask(unsigned mask)
 float VolumeNode::calculateDrawDistance(const Camera& camera, float minScreenSize)
 {
     const BoundingBox& box = getWorldBoundingBox();
-    float diagonal = (box.mMax - box.mMin).getLength();
+    float diagonal = (box.mMax - box.mMin).getLengthFast();
     float halfViewSize = camera.getHalfViewSize();
     
     return diagonal * halfViewSize / minScreenSize;

+ 42 - 39
Engine/UI/Button.cpp

@@ -35,8 +35,7 @@ Button::Button(const std::string& name) :
     mInactiveRect(0, 0, 0, 0),
     mHoverRect(0, 0, 0, 0),
     mPressedRect(0, 0, 0, 0),
-    mHoverDelay(0.0f),
-    mPressedDelay(0.1f),
+    mLabelOffset(0, 0),
     mState(BUTTON_INACTIVE),
     mHoveringThisFrame(false)
 {
@@ -58,31 +57,16 @@ XMLElement Button::loadParameters(XMLFile* file, const std::string& elementName,
         setHoverRect(paramElem.getChildElement("hoverrect").getIntRect("value"));
     if (paramElem.hasChildElement("pressedrect"))
         setPressedRect(paramElem.getChildElement("pressedrect").getIntRect("value"));
-    if (paramElem.hasChildElement("hoverdelay"))
-        setHoverDelay(paramElem.getChildElement("hoverdelay").getFloat("value"));
-    if (paramElem.hasChildElement("presseddelay"))
-        setPressedDelay(paramElem.getChildElement("presseddelay").getFloat("value"));
+    if (paramElem.hasChildElement("labeloffset"))
+        setLabelOffset(paramElem.getChildElement("labeloffset").getIntVector2("value"));
     
     return paramElem;
 }
 
 void Button::update(float timeStep)
 {
-    if ((mState != BUTTON_INACTIVE) && (mStateTime <= 0.0f))
-    {
-        if (!mHoveringThisFrame)
-        {
-            mState = BUTTON_INACTIVE;
-            mStateTime = 0.0f;
-        }
-        else
-        {
-            mState = BUTTON_HOVER;
-            mStateTime = mHoverDelay;
-        }
-    }
-    else
-        mStateTime -= timeStep;
+    if (!mHoveringThisFrame)
+        setState(BUTTON_INACTIVE);
     
     mHoveringThisFrame = false;
 }
@@ -107,13 +91,12 @@ void Button::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quad
     BorderImage::getBatches(batches, quads, currentScissor);
 }
 
-void Button::onHover(const IntVector2& position, const IntVector2& screenPosition)
+void Button::onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
 {
-    if (mState != BUTTON_PRESSED)
-    {
-        mState = BUTTON_HOVER;
-        mStateTime = mHoverDelay;
-    }
+    if (buttons & MOUSEB_LEFT)
+        setState(BUTTON_PRESSED);
+    else
+        setState(BUTTON_HOVER);
     
     mHoveringThisFrame = true;
 }
@@ -122,8 +105,8 @@ void Button::onClick(const IntVector2& position, const IntVector2& screenPositio
 {
     if (buttons & MOUSEB_LEFT)
     {
-        mState = BUTTON_PRESSED;
-        mStateTime = mPressedDelay;
+        setState(BUTTON_PRESSED);
+        mHoveringThisFrame = true;
         
         using namespace Pressed;
         
@@ -163,16 +146,6 @@ void Button::setPressedRect(int left, int top, int right, int bottom)
     mPressedRect = IntRect(left, top, right, bottom);
 }
 
-void Button::setHoverDelay(float delay)
-{
-    mHoverDelay = delay;
-}
-
-void Button::setPressedDelay(float delay)
-{
-    mPressedDelay = delay;
-}
-
 void Button::setLabel(UIElement* label)
 {
     removeChild(mLabel);
@@ -182,6 +155,36 @@ void Button::setLabel(UIElement* label)
         addChild(mLabel);
         // Center the label element on the button forcibly
         mLabel->setAlignment(HA_CENTER, VA_CENTER);
+        updateLabelOffset();
+    }
+}
+
+void Button::setLabelOffset(const IntVector2& offset)
+{
+    mLabelOffset = offset;
+}
+
+void Button::setLabelOffset(int x, int y)
+{
+    mLabelOffset = IntVector2(x, y);
+}
+
+void Button::setState(ButtonState state)
+{
+    if (state != mState)
+    {
+        mState = state;
+        updateLabelOffset();
     }
 }
 
+void Button::updateLabelOffset()
+{
+    if (!mLabel)
+        return;
+    
+    if (mState == BUTTON_PRESSED)
+        mLabel->setPosition(mLabelOffset);
+    else
+        mLabel->setPosition(IntVector2::sZero);
+}

+ 14 - 15
Engine/UI/Button.h

@@ -50,7 +50,7 @@ public:
     //! Return UI rendering batches
     virtual void getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor);
     //! React to mouse hover
-    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition);
+    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
     //! React to mouse click
     virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
     
@@ -66,12 +66,12 @@ public:
     void setPressedRect(const IntRect& rect);
     //! Set pressed image rectangle
     void setPressedRect(int left, int top, int right, int bottom);
-    //! Set hovering image delay
-    void setHoverDelay(float delay);
-    //! Set pressed image delay
-    void setPressedDelay(float delay);
     //! Set optional label UI element
     void setLabel(UIElement* label);
+    //! Set label offset on press
+    void setLabelOffset(const IntVector2& offset);
+    //! Set label offset on press
+    void setLabelOffset(int x, int y);
     
     //! Return inactive image rectangle
     const IntRect& getInactiveRect() const { return mInactiveRect; }
@@ -79,14 +79,17 @@ public:
     const IntRect& getHoverRect() const { return mHoverRect; }
     //! Return pressed image rectangle
     const IntRect& getPressedRect() const { return mPressedRect; }
-    //! Return hovering image delay
-    float getHoverDelay() const { return mHoverDelay; }
-    //! Return pressed image delay
-    float getPressedDelay() const { return mPressedDelay; }
     //! Return label UI element
     UIElement* getLabel() const { return mLabel; }
+    //! Return label offset on press
+    const IntVector2& getLabelOffset() const { return mLabelOffset; }
     
 protected:
+    //! Set new state
+    void setState(ButtonState state);
+    //! Set offset of label depending on button press state
+    void updateLabelOffset();
+    
     //! Label UI element
     SharedPtr<UIElement> mLabel;
     //! Inactive image rectangle
@@ -95,12 +98,8 @@ protected:
     IntRect mHoverRect;
     //! Pressed image rectangle
     IntRect mPressedRect;
-    //! Hovering image delay
-    float mHoverDelay;
-    //! Pressed image delay
-    float mPressedDelay;
-    //! State timer
-    float mStateTime;
+    //! Label offset on press
+    IntVector2 mLabelOffset;
     //! Current state
     ButtonState mState;
     //! Hovering flag

+ 1 - 1
Engine/UI/UI.cpp

@@ -167,7 +167,7 @@ void UI::update(float timeStep)
         IntVector2 pos = mCursor->getPosition();
         UIElement* element = getElementAt(pos);
         if (element)
-            element->onHover(element->screenToElement(pos), pos);
+            element->onHover(element->screenToElement(pos), pos, mMouseButtons);
     }
     
     {

+ 1 - 1
Engine/UI/UIElement.cpp

@@ -231,7 +231,7 @@ void UIElement::onChar(unsigned key)
 {
 }
 
-void UIElement::onHover(const IntVector2& position, const IntVector2& screenPosition)
+void UIElement::onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
 {
 }
 

+ 1 - 1
Engine/UI/UIElement.h

@@ -80,7 +80,7 @@ public:
     virtual float getDerivedOpacity();
     
     //! React to mouse hover
-    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition);
+    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
     //! React to mouse click
     virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
     //! React to mouse drag start

+ 3 - 3
Examples/Test/Application.cpp

@@ -200,7 +200,7 @@ void Application::init()
     
     XMLFile* uiSetup = mCache->getResource<XMLFile>("UI/UI.xml");
     
-    SharedPtr<Cursor> cursor(new Cursor("Cursor"));
+    Cursor* cursor = new Cursor("Cursor");
     cursor->loadParameters(uiSetup, "Cursor", mCache);
     cursor->setPosition(renderer->getWidth() / 2, renderer->getHeight() / 2);
     ui->setCursor(cursor);
@@ -340,7 +340,7 @@ void Application::createScene()
         
         StaticModel* object = new StaticModel(octree);
         object->setModel(mCache->getResource<Model>("Models/Mushroom.mdl"));
-        object->setMaterial(mCache->getResource<Material>("Materials/MushroomStatic.xml"));
+        object->setMaterial(mCache->getResource<Material>("Materials/Mushroom.xml"));
         object->setCastShadows(true);
         object->setOccluder(true);
         body->addChild(object);
@@ -355,7 +355,7 @@ void Application::createScene()
     {
         InstancedModel* instanced = new InstancedModel(octree);
         instanced->setModel(mCache->getResource<Model>("Models/Mushroom.mdl"));
-        instanced->setMaterial(mCache->getResource<Material>("Materials/MushroomInstanced.xml"));
+        instanced->setMaterial(mCache->getResource<Material>("Materials/Mushroom.xml"));
         instanced->setPosition(Vector3(random() * 160.0f - 80.0f, 0.0f, random() * 160.0f - 80.0f));
         instanced->setCastShadows(true);
         instanced->setNumInstances(50);

+ 2 - 2
SourceAssets/Shaders/Deferred/GBuffer.xml

@@ -1,10 +1,10 @@
 <shaders>
     <shader name="GBuffer" type="vs">
+        <option name="Normal" define="NORMALMAP" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
         <variation name="Instanced" define="INSTANCED" />
-        <option name="Normal" define="NORMALMAP" />
-    </shader>    
+    </shader>
     <shader name="GBuffer" type="ps">
         <option name="Diff" define="DIFFMAP" />
         <option name="Normal" define="NORMALMAP" />

+ 3 - 3
SourceAssets/Shaders/Forward.xml

@@ -1,8 +1,5 @@
 <shaders>
     <shader name="Forward" type="vs">
-        <variation name="" />
-        <variation name="Skinned" define="SKINNED" />
-        <variation name="Instanced" define="INSTANCED" />
         <option name="Unlit" define="UNLIT">
             <exclude name="Normal" />
             <exclude name="Spot" />
@@ -11,6 +8,9 @@
         <option name="Normal" define="NORMALMAP" />
         <option name="Spot" define="SPOTLIGHT" />
         <option name="Shadow" define="SHADOW" />
+        <variation name="" />
+        <variation name="Skinned" define="SKINNED" />
+        <variation name="Instanced" define="INSTANCED" />
     </shader>
     <shader name="Forward" type="ps">
         <option name="Diff" define="DIFFMAP" />

+ 1 - 1
SourceAssets/Shaders/Prepass/GBuffer.xml

@@ -1,9 +1,9 @@
 <shaders>
     <shader name="GBuffer" type="vs">
+        <option name="Normal" define="NORMALMAP" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
         <variation name="Instanced" define="INSTANCED" />
-        <option name="Normal" define="NORMALMAP" />
     </shader>
     <shader name="GBuffer" type="ps">
         <option name="Normal" define="NORMALMAP" />