ソースを参照

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 年 前
コミット
347fe0815d
48 ファイル変更417 行追加291 行削除
  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" />