浏览代码

Added instancing to the OpenGL renderer, requires the ARB_instanced_arrays extension.

Lasse Öörni 12 年之前
父节点
当前提交
8b28c12ae6

+ 1 - 0
Bin/CoreData/Shaders/GLSL/Basic.xml

@@ -4,6 +4,7 @@
         <option name="VCol" define="VERTEXCOLOR" />
         <option name="VCol" define="VERTEXCOLOR" />
         <variation name="" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
         <variation name="Skinned" define="SKINNED" />
+        <variation name="Instanced" define="INSTANCED" />
         <variation name="Billboard" define="BILLBOARD" />
         <variation name="Billboard" define="BILLBOARD" />
     </shader>
     </shader>
     <shader type="ps">
     <shader type="ps">

+ 1 - 0
Bin/CoreData/Shaders/GLSL/Depth.xml

@@ -2,6 +2,7 @@
     <shader type="vs">
     <shader type="vs">
         <variation name="" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
         <variation name="Skinned" define="SKINNED" />
+        <variation name="Instanced" define="INSTANCED" />
     </shader>
     </shader>
     <shader type="ps">
     <shader type="ps">
         <option name="AlphaMask" define="ALPHAMASK" />
         <option name="AlphaMask" define="ALPHAMASK" />

+ 1 - 0
Bin/CoreData/Shaders/GLSL/LitParticle.xml

@@ -21,6 +21,7 @@
         <option name="" /> <!-- Dummy option to separate the two variation groups -->
         <option name="" /> <!-- Dummy option to separate the two variation groups -->
         <variation name="" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
         <variation name="Skinned" define="SKINNED" />
+        <variation name="Instanced" define="INSTANCED" />
         <variation name="Billboard" define="BILLBOARD" />
         <variation name="Billboard" define="BILLBOARD" />
     </shader>
     </shader>
     <shader type="ps">
     <shader type="ps">

+ 1 - 0
Bin/CoreData/Shaders/GLSL/LitSolid.xml

@@ -24,6 +24,7 @@
         <option name="Shadow" define="SHADOW" require="PERPIXEL" />
         <option name="Shadow" define="SHADOW" require="PERPIXEL" />
         <variation name="" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
         <variation name="Skinned" define="SKINNED" />
+        <variation name="Instanced" define="INSTANCED" />
         <variation name="Billboard" define="BILLBOARD" />
         <variation name="Billboard" define="BILLBOARD" />
     </shader>
     </shader>
     <shader type="ps">
     <shader type="ps">

+ 1 - 0
Bin/CoreData/Shaders/GLSL/Shadow.xml

@@ -2,6 +2,7 @@
     <shader type="vs">
     <shader type="vs">
         <variation name="" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
         <variation name="Skinned" define="SKINNED" />
+        <variation name="Instanced" define="INSTANCED" />
     </shader>
     </shader>
     <shader type="ps">
     <shader type="ps">
         <option name="AlphaMask" define="ALPHAMASK" />
         <option name="AlphaMask" define="ALPHAMASK" />

+ 3 - 1
Bin/CoreData/Shaders/GLSL/TerrainBlend.xml

@@ -19,6 +19,8 @@
         </variation>
         </variation>
         <option name="Spec" define="SPECULAR" require="PERPIXEL" />
         <option name="Spec" define="SPECULAR" require="PERPIXEL" />
         <option name="Shadow" define="SHADOW" require="PERPIXEL" />
         <option name="Shadow" define="SHADOW" require="PERPIXEL" />
+        <variation name="" />
+        <variation name="Instanced" define="INSTANCED" />
     </shader>
     </shader>
     <shader type="ps">
     <shader type="ps">
         <option name="Ambient" define="AMBIENT" require="PERPIXEL" />
         <option name="Ambient" define="AMBIENT" require="PERPIXEL" />
@@ -36,7 +38,7 @@
             <define name="PERPIXEL" />
             <define name="PERPIXEL" />
         </variation>
         </variation>
         <variation name="Prepass" define="PREPASS" />
         <variation name="Prepass" define="PREPASS" />
-        <variation name="Material" define="MATERIAL" exclude="Normal" />
+        <variation name="Material" define="MATERIAL" />
         <variation name="Deferred" define="DEFERRED" />
         <variation name="Deferred" define="DEFERRED" />
         <option name="Mask" define="CUBEMASK" require="POINTLIGHT" />
         <option name="Mask" define="CUBEMASK" require="POINTLIGHT" />
         <option name="Spec" define="SPECULAR" require="PERPIXEL" />
         <option name="Spec" define="SPECULAR" require="PERPIXEL" />

+ 27 - 8
Bin/CoreData/Shaders/GLSL/Transform.vert

@@ -8,7 +8,13 @@ attribute vec4 iBlendWeights;
 attribute vec4 iBlendIndices;
 attribute vec4 iBlendIndices;
 attribute vec3 iCubeTexCoord;
 attribute vec3 iCubeTexCoord;
 attribute vec4 iCubeTexCoord2;
 attribute vec4 iCubeTexCoord2;
+#ifndef GL_ES
+    attribute vec4 iInstanceMatrix1;
+    attribute vec4 iInstanceMatrix2;
+    attribute vec4 iInstanceMatrix3;
+#endif
 
 
+#ifdef SKINNED
 mat4 GetSkinMatrix(vec4 blendWeights, vec4 blendIndices)
 mat4 GetSkinMatrix(vec4 blendWeights, vec4 blendIndices)
 {
 {
     ivec4 idx = ivec4(blendIndices) * 3;
     ivec4 idx = ivec4(blendIndices) * 3;
@@ -18,6 +24,15 @@ mat4 GetSkinMatrix(vec4 blendWeights, vec4 blendIndices)
         mat4(cSkinMatrices[idx.z], cSkinMatrices[idx.z + 1], cSkinMatrices[idx.z + 2], lastColumn) * blendWeights.z +
         mat4(cSkinMatrices[idx.z], cSkinMatrices[idx.z + 1], cSkinMatrices[idx.z + 2], lastColumn) * blendWeights.z +
         mat4(cSkinMatrices[idx.w], cSkinMatrices[idx.w + 1], cSkinMatrices[idx.w + 2], lastColumn) * blendWeights.w;
         mat4(cSkinMatrices[idx.w], cSkinMatrices[idx.w + 1], cSkinMatrices[idx.w + 2], lastColumn) * blendWeights.w;
 }
 }
+#endif
+
+#ifdef INSTANCED
+mat4 GetInstanceMatrix()
+{
+    const vec4 lastColumn = vec4(0.0, 0.0, 0.0, 1.0);
+    return mat4(iInstanceMatrix1, iInstanceMatrix2, iInstanceMatrix3, lastColumn);
+}
+#endif
 
 
 mat3 GetNormalMatrix(mat4 modelMatrix)
 mat3 GetNormalMatrix(mat4 modelMatrix)
 {
 {
@@ -54,18 +69,22 @@ vec3 GetBillboardNormal()
     return vec3(-cCameraRot[2][0], -cCameraRot[2][1], -cCameraRot[2][2]);
     return vec3(-cCameraRot[2][0], -cCameraRot[2][1], -cCameraRot[2][2]);
 }
 }
 
 
-#ifdef SKINNED
+// Note: the skinning/instancing model matrix is a transpose, so the matrix multiply order must be swapped
+// (see GetWorldPos(), GetWorldNormal() and GetWorldTangent() below)
+#if defined(SKINNED)
     #define iModelMatrix GetSkinMatrix(iBlendWeights, iBlendIndices)
     #define iModelMatrix GetSkinMatrix(iBlendWeights, iBlendIndices)
+#elif defined(INSTANCED)
+    #define iModelMatrix GetInstanceMatrix();
 #else
 #else
     #define iModelMatrix cModel
     #define iModelMatrix cModel
 #endif
 #endif
 
 
 vec3 GetWorldPos(mat4 modelMatrix)
 vec3 GetWorldPos(mat4 modelMatrix)
 {
 {
-    #if defined(BILLBOARD)
-        return GetBillboardPos(iPos, iTexCoord2, modelMatrix);
-    #elif defined(SKINNED)
+    #if defined(SKINNED) || defined(INSTANCED)
         return (iPos * modelMatrix).xyz;
         return (iPos * modelMatrix).xyz;
+    #elif defined(BILLBOARD)
+        return GetBillboardPos(iPos, iTexCoord2, modelMatrix);
     #else
     #else
         return (modelMatrix * iPos).xyz;
         return (modelMatrix * iPos).xyz;
     #endif
     #endif
@@ -73,10 +92,10 @@ vec3 GetWorldPos(mat4 modelMatrix)
 
 
 vec3 GetWorldNormal(mat4 modelMatrix)
 vec3 GetWorldNormal(mat4 modelMatrix)
 {
 {
-    #if defined(BILLBOARD)
-        return GetBillboardNormal();
-    #elif defined(SKINNED)
+    #if defined(SKINNED) || defined(INSTANCED)
         return normalize(iNormal * GetNormalMatrix(modelMatrix));
         return normalize(iNormal * GetNormalMatrix(modelMatrix));
+    #elif defined(BILLBOARD)
+        return GetBillboardNormal();
     #else
     #else
         return normalize(GetNormalMatrix(modelMatrix) * iNormal);
         return normalize(GetNormalMatrix(modelMatrix) * iNormal);
     #endif
     #endif
@@ -85,7 +104,7 @@ vec3 GetWorldNormal(mat4 modelMatrix)
 vec3 GetWorldTangent(mat4 modelMatrix)
 vec3 GetWorldTangent(mat4 modelMatrix)
 {   
 {   
     mat3 normalMatrix = GetNormalMatrix(modelMatrix);
     mat3 normalMatrix = GetNormalMatrix(modelMatrix);
-    #ifdef SKINNED
+    #if defined(SKINNED) || defined(INSTANCED)
         return normalize(iTangent.xyz * normalMatrix);
         return normalize(iTangent.xyz * normalMatrix);
     #else
     #else
         return normalize(normalMatrix * iTangent.xyz);
         return normalize(normalMatrix * iTangent.xyz);

+ 1 - 0
Bin/CoreData/Shaders/GLSL/Unlit.xml

@@ -3,6 +3,7 @@
         <option name="VCol" define="VERTEXCOLOR" />
         <option name="VCol" define="VERTEXCOLOR" />
         <variation name="" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
         <variation name="Skinned" define="SKINNED" />
+        <variation name="Instanced" define="INSTANCED" />
         <variation name="Billboard" define="BILLBOARD" />
         <variation name="Billboard" define="BILLBOARD" />
     </shader>
     </shader>
     <shader type="ps">
     <shader type="ps">

+ 2 - 28
Bin/CoreData/Shaders/GLSL/Vegetation.xml

@@ -20,33 +20,7 @@
         </variation>
         </variation>
         <option name="Spec" define="SPECULAR" require="PERPIXEL" />
         <option name="Spec" define="SPECULAR" require="PERPIXEL" />
         <option name="Shadow" define="SHADOW" require="PERPIXEL" />
         <option name="Shadow" define="SHADOW" require="PERPIXEL" />
-    </shader>
-    <shader type="ps">
-        <option name="Diff" define="DIFFMAP" />
-        <option name="Normal" define="NORMALMAP" require="DIFFMAP" />
-        <option name="Packed" define="PACKEDNORMAL" require="NORMALMAP" />
-        <option name="SpecMap" define="SPECMAP" require="DIFFMAP" />
-        <option name="AlphaMask" define="ALPHAMASK" require="DIFFMAP" />
-        <option name="Ambient" define="AMBIENT" require="PERPIXEL" />
-        <variation name="" define="AMBIENT" />
-        <variation name="Dir">
-            <define name="DIRLIGHT" />
-            <define name="PERPIXEL" />
-        </variation>
-        <variation name="Spot">
-            <define name="SPOTLIGHT" />
-            <define name="PERPIXEL" />
-        </variation>
-        <variation name="Point">
-            <define name="POINTLIGHT" />
-            <define name="PERPIXEL" />
-        </variation>
-        <variation name="Prepass" define="PREPASS" />
-        <variation name="Material" define="MATERIAL" />
-        <variation name="Deferred" define="DEFERRED" />
-        <option name="Mask" define="CUBEMASK" require="POINTLIGHT" />
-        <option name="Spec" define="SPECULAR" require="PERPIXEL" />
-        <option name="Shadow" define="SHADOW" require="PERPIXEL" />
-        <option name="LQ" define="LQSHADOW" require="SHADOW" />
+        <variation name="" />
+        <variation name="Instanced" define="INSTANCED" />
     </shader>
     </shader>
 </shaders>
 </shaders>

+ 3 - 3
Bin/CoreData/Shaders/GLSL/VegetationDepth.xml

@@ -1,6 +1,6 @@
 <shaders>
 <shaders>
-    <shader type="vs" />
-    <shader type="ps">
-        <option name="AlphaMask" define="ALPHAMASK" />
+    <shader type="vs">
+        <variation name="" />
+        <variation name="Instanced" define="INSTANCED" />
     </shader>
     </shader>
 </shaders>
 </shaders>

+ 3 - 3
Bin/CoreData/Shaders/GLSL/VegetationShadow.xml

@@ -1,6 +1,6 @@
 <shaders>
 <shaders>
-    <shader type="vs" />
-    <shader type="ps">
-        <option name="AlphaMask" define="ALPHAMASK" />
+    <shader type="vs">
+        <variation name="" />
+        <variation name="Instanced" define="INSTANCED" />
     </shader>
     </shader>
 </shaders>
 </shaders>

+ 2 - 2
CMakeLists.txt

@@ -45,10 +45,10 @@ add_definitions (-DENABLE_LOGGING)
 # If not on MSVC, enable use of OpenGL instead of Direct3D9 (either not compiling on Windows or
 # If not on MSVC, enable use of OpenGL instead of Direct3D9 (either not compiling on Windows or
 # with a compiler that may not have an up-to-date DirectX SDK). This can also be unconditionally
 # with a compiler that may not have an up-to-date DirectX SDK). This can also be unconditionally
 # set, but Windows graphics card drivers are usually better optimized for Direct3D.
 # set, but Windows graphics card drivers are usually better optimized for Direct3D.
-if (NOT MSVC)
+#if (NOT MSVC)
     set (USE_OPENGL 1)
     set (USE_OPENGL 1)
     add_definitions (-DUSE_OPENGL)
     add_definitions (-DUSE_OPENGL)
-endif ()
+#endif ()
 
 
 # If not on Windows, enable Unix mode for kNet library.
 # If not on Windows, enable Unix mode for kNet library.
 if (NOT WIN32)
 if (NOT WIN32)

+ 3 - 4
Docs/Reference.dox

@@ -486,6 +486,7 @@ When setting the initial screen mode, Graphics does a few checks:
 
 
 - For Direct3D9, the supported shader model is checked. 2 is minimum, but 3 will be used if available. SM2 can be forced by calling \ref Graphics::SetForceSM2 "SetForceSM2()" before setting the initial screen mode.
 - For Direct3D9, the supported shader model is checked. 2 is minimum, but 3 will be used if available. SM2 can be forced by calling \ref Graphics::SetForceSM2 "SetForceSM2()" before setting the initial screen mode.
 - For OpenGL, version 2.0 with EXT_framebuffer_object, EXT_packed_depth_stencil and EXT_texture_filter_anisotropic extensions is checked for.
 - For OpenGL, version 2.0 with EXT_framebuffer_object, EXT_packed_depth_stencil and EXT_texture_filter_anisotropic extensions is checked for.
+- Is hardware instancing supported? This requires shader model 3 on Direct3D9 and the ARB_instanced_arrays extension on OpenGL.
 - Are hardware shadow maps supported? Both ATI & NVIDIA style shadow maps can be used. If neither are available, no shadows will be rendered.
 - Are hardware shadow maps supported? Both ATI & NVIDIA style shadow maps can be used. If neither are available, no shadows will be rendered.
 - Are light pre-pass and deferred rendering modes supported? These require sufficient multiple rendertarget support, and R32F texture format support.
 - Are light pre-pass and deferred rendering modes supported? These require sufficient multiple rendertarget support, and R32F texture format support.
 
 
@@ -543,7 +544,7 @@ The following techniques will be used to reduce the amount of CPU and GPU work w
 
 
 - Software rasterized occlusion: after the octree has been queried for visible objects, the objects that are marked as occluders are rendered on the CPU to a small hierarchical-depth buffer, and it will be used to test the non-occluders for visibility. Use \ref Renderer::SetMaxOccluderTriangles "SetMaxOccluderTriangles()" and \ref Renderer::SetOccluderSizeThreshold "SetOccluderSizeThreshold()" to configure the occlusion rendering.
 - Software rasterized occlusion: after the octree has been queried for visible objects, the objects that are marked as occluders are rendered on the CPU to a small hierarchical-depth buffer, and it will be used to test the non-occluders for visibility. Use \ref Renderer::SetMaxOccluderTriangles "SetMaxOccluderTriangles()" and \ref Renderer::SetOccluderSizeThreshold "SetOccluderSizeThreshold()" to configure the occlusion rendering.
 
 
-- Hardware instancing (Direct3D9 SM3 only): rendering operations with the same geometry, material and light will be grouped together and performed as one draw call. Objects with a large amount of triangles will not be rendered as instanced, as that could actually be detrimental to performance. Use \ref Renderer::SetMaxInstanceTriangles "SetMaxInstanceTriangles()" to set the threshold. Note that even when instancing is not available, or the triangle count of objects is too large, they still benefit from the grouping, as render state only needs to be set once before rendering each group, reducing the CPU cost.
+- Hardware instancing: rendering operations with the same geometry, material and light will be grouped together and performed as one draw call. Objects with a large amount of triangles will not be rendered as instanced, as that could actually be detrimental to performance. Use \ref Renderer::SetMaxInstanceTriangles "SetMaxInstanceTriangles()" to set the threshold. Note that even when instancing is not available, or the triangle count of objects is too large, they still benefit from the grouping, as render state only needs to be set once before rendering each group, reducing the CPU cost.
 
 
 - %Light stencil masking: in forward rendering, before objects lit by a spot or point light are re-rendered additively, the light's bounding shape is rendered to the stencil buffer to ensure pixels outside the light range are not processed.
 - %Light stencil masking: in forward rendering, before objects lit by a spot or point light are re-rendered additively, the light's bounding shape is rendered to the stencil buffer to ensure pixels outside the light range are not processed.
 
 
@@ -631,8 +632,6 @@ These differences need to be observed when using the low-level rendering functio
 
 
 - %Shader resources are stored in different locations depending on the API: Bin/CoreData/Shaders/HLSL for Direct3D9, and Bin/CoreData/Shaders/GLSL for OpenGL.
 - %Shader resources are stored in different locations depending on the API: Bin/CoreData/Shaders/HLSL for Direct3D9, and Bin/CoreData/Shaders/GLSL for OpenGL.
 
 
-- At least for now, instancing is not supported for OpenGL. It still benefits from the instance group rendering loop, which only changes the model transform for each object with the same material and light, instead of setting the whole renderstate.
-
 - To ensure similar UV addressing for render-to-texture viewports on both APIs, on OpenGL texture viewports will be rendered upside down.
 - To ensure similar UV addressing for render-to-texture viewports on both APIs, on OpenGL texture viewports will be rendered upside down.
 
 
 OpenGL ES 2.0 has further limitations:
 OpenGL ES 2.0 has further limitations:
@@ -794,7 +793,7 @@ For both variations and options, a name is required, and one or more defines to
 </variation>
 </variation>
 \endcode
 \endcode
 
 
-A variation or option can "require" other defines, for example instancing requires the SM3 define which tells that we are compiling for %Shader %Model 3. Like defines, requires can be listed either as an attribute (if only one) or as a child element (if many.)
+A variation or option can "require" other defines, for example in HLSL shaders instancing requires the SM3 define which tells that we are compiling for %Shader %Model 3. Like defines, requires can be listed either as an attribute (if only one) or as a child element (if many.)
 
 
 Additionally, an option can "include" or "exclude" other options. Use this mechanism instead of variations if complex dependencies between options is required, instead of simple exclusion. Again, includes or excludes can be listed either as attributes or child elements.
 Additionally, an option can "include" or "exclude" other options. Use this mechanism instead of variations if complex dependencies between options is required, instead of simple exclusion. Again, includes or excludes can be listed either as attributes or child elements.
 
 

+ 1 - 0
Docs/ScriptAPI.dox

@@ -2990,6 +2990,7 @@ Properties:<br>
 - uint numPrimitives (readonly)
 - uint numPrimitives (readonly)
 - uint numBatches (readonly)
 - uint numBatches (readonly)
 - bool sm3Support (readonly)
 - bool sm3Support (readonly)
+- bool instancingSupport (readonly)
 - bool lightPrepassSupport (readonly)
 - bool lightPrepassSupport (readonly)
 - bool deferredSupport (readonly)
 - bool deferredSupport (readonly)
 - bool hardwareShadowSupport (readonly)
 - bool hardwareShadowSupport (readonly)

+ 1 - 0
Engine/Engine/GraphicsAPI.cpp

@@ -1018,6 +1018,7 @@ static void RegisterGraphics(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Graphics", "uint get_numPrimitives() const", asMETHOD(Graphics, GetNumPrimitives), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "uint get_numPrimitives() const", asMETHOD(Graphics, GetNumPrimitives), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "uint get_numBatches() const", asMETHOD(Graphics, GetNumBatches), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "uint get_numBatches() const", asMETHOD(Graphics, GetNumBatches), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_sm3Support() const", asMETHOD(Graphics, GetSM3Support), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_sm3Support() const", asMETHOD(Graphics, GetSM3Support), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Graphics", "bool get_instancingSupport() const", asMETHOD(Graphics, GetInstancingSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_lightPrepassSupport() const", asMETHOD(Graphics, GetLightPrepassSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_lightPrepassSupport() const", asMETHOD(Graphics, GetLightPrepassSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_deferredSupport() const", asMETHOD(Graphics, GetDeferredSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_deferredSupport() const", asMETHOD(Graphics, GetDeferredSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_hardwareShadowSupport() const", asMETHOD(Graphics, GetHardwareShadowSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_hardwareShadowSupport() const", asMETHOD(Graphics, GetHardwareShadowSupport), asCALL_THISCALL);

+ 2 - 0
Engine/Graphics/Direct3D9/D3D9Graphics.h

@@ -238,6 +238,8 @@ public:
     unsigned GetHiresShadowMapFormat() const { return hiresShadowMapFormat_; }
     unsigned GetHiresShadowMapFormat() const { return hiresShadowMapFormat_; }
     /// Return whether Shader Model 3 is supported.
     /// Return whether Shader Model 3 is supported.
     bool GetSM3Support() const { return hasSM3_; }
     bool GetSM3Support() const { return hasSM3_; }
+    /// Return whether hardware instancing is supported.
+    bool GetInstancingSupport() const { return hasSM3_; }
     /// Return whether light pre-pass rendering is supported.
     /// Return whether light pre-pass rendering is supported.
     bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
     bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
     /// Return whether deferred rendering is supported.
     /// Return whether deferred rendering is supported.

+ 71 - 7
Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -162,6 +162,7 @@ Graphics::Graphics(Context* context_) :
     vsync_(false),
     vsync_(false),
     tripleBuffer_(false),
     tripleBuffer_(false),
     sRGB_(false),
     sRGB_(false),
+    instancingSupport_(false),
     lightPrepassSupport_(false),
     lightPrepassSupport_(false),
     deferredSupport_(false),
     deferredSupport_(false),
     anisotropySupport_(false),
     anisotropySupport_(false),
@@ -379,11 +380,21 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool resizable, b
                 Release(true, true);
                 Release(true, true);
                 return false;
                 return false;
             }
             }
-        
+            
+            instancingSupport_ = GLEW_ARB_instanced_arrays != 0;
             dxtTextureSupport_ = GLEW_EXT_texture_compression_s3tc != 0;
             dxtTextureSupport_ = GLEW_EXT_texture_compression_s3tc != 0;
             anisotropySupport_ = GLEW_EXT_texture_filter_anisotropic != 0;
             anisotropySupport_ = GLEW_EXT_texture_filter_anisotropic != 0;
             sRGBSupport_ = GLEW_EXT_texture_sRGB != 0;
             sRGBSupport_ = GLEW_EXT_texture_sRGB != 0;
             sRGBWriteSupport_ = GLEW_EXT_framebuffer_sRGB != 0;
             sRGBWriteSupport_ = GLEW_EXT_framebuffer_sRGB != 0;
+            
+            // Set up instancing divisors if supported
+            if (instancingSupport_)
+            {
+                glVertexAttribDivisorARB(ELEMENT_INSTANCEMATRIX1, 1);
+                glVertexAttribDivisorARB(ELEMENT_INSTANCEMATRIX2, 1);
+                glVertexAttribDivisorARB(ELEMENT_INSTANCEMATRIX3, 1);
+            }
+            
             #else
             #else
             dxtTextureSupport_ = CheckExtension(extensions, "EXT_texture_compression_dxt1");
             dxtTextureSupport_ = CheckExtension(extensions, "EXT_texture_compression_dxt1");
             etcTextureSupport_ = CheckExtension(extensions, "OES_compressed_ETC1_RGB8_texture");
             etcTextureSupport_ = CheckExtension(extensions, "OES_compressed_ETC1_RGB8_texture");
@@ -677,6 +688,50 @@ void Graphics::Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount
 
 
 void Graphics::DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount, unsigned instanceCount)
 void Graphics::DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount, unsigned instanceCount)
 {
 {
+    #ifndef GL_ES_VERSION_2_0
+    if (!indexCount || !indexBuffer_ || !indexBuffer_->GetGPUObject() || !instancingSupport_)
+        return;
+    
+    if (impl_->fboDirty_)
+        CommitFramebuffer();
+    
+    unsigned primitiveCount = 0;
+    unsigned indexSize = indexBuffer_->GetIndexSize();
+    
+    switch (type)
+    {
+    case TRIANGLE_LIST:
+        primitiveCount = indexCount / 3;
+        if (indexSize == sizeof(unsigned short))
+        {
+            glDrawElementsInstancedARB(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, (const GLvoid*)(indexStart * indexSize),
+                instanceCount);
+        }
+        else
+        {
+            glDrawElementsInstancedARB(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, (const GLvoid*)(indexStart * indexSize),
+                instanceCount);
+        }
+        break;
+        
+    case LINE_LIST:
+        primitiveCount = indexCount / 2;
+        if (indexSize == sizeof(unsigned short))
+        {
+            glDrawElementsInstancedARB(GL_LINES, indexCount, GL_UNSIGNED_SHORT, (const GLvoid*)(indexStart * indexSize),
+                instanceCount);
+        }
+        else
+        {
+            glDrawElementsInstancedARB(GL_LINES, indexCount, GL_UNSIGNED_INT, (const GLvoid*)(indexStart * indexSize),
+                instanceCount);
+        }
+        break;
+    }
+    
+    numPrimitives_ += instanceCount * primitiveCount;
+    ++numBatches_;
+    #endif
 }
 }
 
 
 void Graphics::SetVertexBuffer(VertexBuffer* buffer)
 void Graphics::SetVertexBuffer(VertexBuffer* buffer)
@@ -720,7 +775,7 @@ bool Graphics::SetVertexBuffers(const Vector<VertexBuffer*>& buffers, const PODV
         }
         }
         
         
         // If buffer and element mask have stayed the same, skip to the next buffer
         // If buffer and element mask have stayed the same, skip to the next buffer
-        if (buffer == vertexBuffers_[i] && elementMask == elementMasks_[i] && !changed)
+        if (buffer == vertexBuffers_[i] && elementMask == elementMasks_[i] && instanceOffset == lastInstanceOffset_ && !changed)
         {
         {
             newAttributes |= elementMask;
             newAttributes |= elementMask;
             continue;
             continue;
@@ -754,9 +809,11 @@ bool Graphics::SetVertexBuffers(const Vector<VertexBuffer*>& buffers, const PODV
                     impl_->enabledAttributes_ |= elementBit;
                     impl_->enabledAttributes_ |= elementBit;
                 }
                 }
                 
                 
-                // Set the attribute pointer
+                // Set the attribute pointer. Add instance offset for the instance matrix pointers
+                unsigned offset = j >= ELEMENT_INSTANCEMATRIX1 ? instanceOffset * vertexSize : 0;
                 glVertexAttribPointer(attrIndex, VertexBuffer::elementComponents[j], VertexBuffer::elementType[j],
                 glVertexAttribPointer(attrIndex, VertexBuffer::elementComponents[j], VertexBuffer::elementType[j],
-                    VertexBuffer::elementNormalize[j], vertexSize, (const GLvoid*)(buffer->GetElementOffset((VertexElement)j)));
+                    VertexBuffer::elementNormalize[j], vertexSize, (const GLvoid*)(buffer->GetElementOffset((VertexElement)j)
+                    + offset));
             }
             }
         }
         }
     }
     }
@@ -764,6 +821,8 @@ bool Graphics::SetVertexBuffers(const Vector<VertexBuffer*>& buffers, const PODV
     if (!changed)
     if (!changed)
         return true;
         return true;
     
     
+    lastInstanceOffset_ = instanceOffset;
+    
     // Now check which vertex attributes should be disabled
     // Now check which vertex attributes should be disabled
     unsigned disableAttributes = impl_->enabledAttributes_ & (~newAttributes);
     unsigned disableAttributes = impl_->enabledAttributes_ & (~newAttributes);
     unsigned disableIndex = 0;
     unsigned disableIndex = 0;
@@ -814,7 +873,7 @@ bool Graphics::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers,
         }
         }
         
         
         // If buffer and element mask have stayed the same, skip to the next buffer
         // If buffer and element mask have stayed the same, skip to the next buffer
-        if (buffer == vertexBuffers_[i] && elementMask == elementMasks_[i] && !changed)
+        if (buffer == vertexBuffers_[i] && elementMask == elementMasks_[i] && instanceOffset == lastInstanceOffset_ && !changed)
         {
         {
             newAttributes |= elementMask;
             newAttributes |= elementMask;
             continue;
             continue;
@@ -848,9 +907,11 @@ bool Graphics::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers,
                     impl_->enabledAttributes_ |= elementBit;
                     impl_->enabledAttributes_ |= elementBit;
                 }
                 }
                 
                 
-                // Set the attribute pointer
+                // Set the attribute pointer. Add instance offset for the instance matrix pointers
+                unsigned offset = j >= ELEMENT_INSTANCEMATRIX1 ? instanceOffset * vertexSize : 0;
                 glVertexAttribPointer(attrIndex, VertexBuffer::elementComponents[j], VertexBuffer::elementType[j],
                 glVertexAttribPointer(attrIndex, VertexBuffer::elementComponents[j], VertexBuffer::elementType[j],
-                    VertexBuffer::elementNormalize[j], vertexSize, (const GLvoid*)(buffer->GetElementOffset((VertexElement)j)));
+                    VertexBuffer::elementNormalize[j], vertexSize, (const GLvoid*)(buffer->GetElementOffset((VertexElement)j)
+                    + offset));
             }
             }
         }
         }
     }
     }
@@ -858,6 +919,8 @@ bool Graphics::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers,
     if (!changed)
     if (!changed)
         return true;
         return true;
     
     
+    lastInstanceOffset_ = instanceOffset;
+    
     // Now check which vertex attributes should be disabled
     // Now check which vertex attributes should be disabled
     unsigned disableAttributes = impl_->enabledAttributes_ & (~newAttributes);
     unsigned disableAttributes = impl_->enabledAttributes_ & (~newAttributes);
     unsigned disableIndex = 0;
     unsigned disableIndex = 0;
@@ -2543,6 +2606,7 @@ void Graphics::ResetCachedState()
     stencilRef_ = 0;
     stencilRef_ = 0;
     stencilCompareMask_ = M_MAX_UNSIGNED;
     stencilCompareMask_ = M_MAX_UNSIGNED;
     stencilWriteMask_ = M_MAX_UNSIGNED;
     stencilWriteMask_ = M_MAX_UNSIGNED;
+    lastInstanceOffset_ = 0;
     impl_->activeTexture_ = 0;
     impl_->activeTexture_ = 0;
     impl_->enabledAttributes_ = 0;
     impl_->enabledAttributes_ = 0;
     impl_->boundFbo_ = impl_->systemFbo_;
     impl_->boundFbo_ = impl_->systemFbo_;

+ 9 - 3
Engine/Graphics/OpenGL/OGLGraphics.h

@@ -112,7 +112,7 @@ public:
     void Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount);
     void Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount);
     /// Draw indexed geometry.
     /// Draw indexed geometry.
     void Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount);
     void Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount);
-    /// Draw indexed, instanced geometry. No-op on OpenGL.
+    /// Draw indexed, instanced geometry.
     void DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount, unsigned instanceCount);
     void DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount, unsigned instanceCount);
     /// Set vertex buffer.
     /// Set vertex buffer.
     void SetVertexBuffer(VertexBuffer* buffer);
     void SetVertexBuffer(VertexBuffer* buffer);
@@ -247,6 +247,8 @@ public:
     unsigned GetHiresShadowMapFormat() const { return hiresShadowMapFormat_; }
     unsigned GetHiresShadowMapFormat() const { return hiresShadowMapFormat_; }
     /// Return whether Shader Model 3 is supported. Always false on OpenGL.
     /// Return whether Shader Model 3 is supported. Always false on OpenGL.
     bool GetSM3Support() const { return false; }
     bool GetSM3Support() const { return false; }
+    /// Return whether hardware instancing is supported.
+    bool GetInstancingSupport() const { return instancingSupport_; }
     /// Return whether light pre-pass rendering is supported.
     /// Return whether light pre-pass rendering is supported.
     bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
     bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
     /// Return whether deferred rendering is supported.
     /// Return whether deferred rendering is supported.
@@ -255,8 +257,8 @@ public:
     bool GetAnisotropySupport() const { return anisotropySupport_; }
     bool GetAnisotropySupport() const { return anisotropySupport_; }
     /// Return whether shadow map depth compare is done in hardware. Always true on OpenGL.
     /// Return whether shadow map depth compare is done in hardware. Always true on OpenGL.
     bool GetHardwareShadowSupport() const { return true; }
     bool GetHardwareShadowSupport() const { return true; }
-    /// Return whether stream offset is supported. Always false on OpenGL.
-    bool GetStreamOffsetSupport() const { return false; }
+    /// Return whether stream offset is supported. Always true on OpenGL.
+    bool GetStreamOffsetSupport() const { return true; }
     /// Return whether sRGB conversion on texture sampling is supported.
     /// Return whether sRGB conversion on texture sampling is supported.
     bool GetSRGBSupport() const { return sRGBSupport_; }
     bool GetSRGBSupport() const { return sRGBSupport_; }
     /// Return whether sRGB conversion on rendertarget writing is supported.
     /// Return whether sRGB conversion on rendertarget writing is supported.
@@ -428,6 +430,8 @@ private:
     bool tripleBuffer_;
     bool tripleBuffer_;
     /// sRGB conversion on write flag for the main window.
     /// sRGB conversion on write flag for the main window.
     bool sRGB_;
     bool sRGB_;
+    /// Instancing support flag.
+    bool instancingSupport_;
     /// Light prepass support flag.
     /// Light prepass support flag.
     bool lightPrepassSupport_;
     bool lightPrepassSupport_;
     /// Deferred rendering support flag.
     /// Deferred rendering support flag.
@@ -524,6 +528,8 @@ private:
     unsigned stencilCompareMask_;
     unsigned stencilCompareMask_;
     /// Stencil write bitmask.
     /// Stencil write bitmask.
     unsigned stencilWriteMask_;
     unsigned stencilWriteMask_;
+    /// Last used instance data offset.
+    unsigned lastInstanceOffset_;
     /// Default texture filtering mode.
     /// Default texture filtering mode.
     TextureFilterMode defaultTextureFilterMode_;
     TextureFilterMode defaultTextureFilterMode_;
     /// Map for additional depth textures, to emulate Direct3D9 ability to mix render texture and backbuffer rendering.
     /// Map for additional depth textures, to emulate Direct3D9 ability to mix render texture and backbuffer rendering.

+ 6 - 0
Engine/Graphics/OpenGL/OGLShaderProgram.cpp

@@ -105,6 +105,12 @@ bool ShaderProgram::Link()
     glBindAttribLocation(object_, 7, "iBlendIndices");
     glBindAttribLocation(object_, 7, "iBlendIndices");
     glBindAttribLocation(object_, 8, "iCubeTexCoord");
     glBindAttribLocation(object_, 8, "iCubeTexCoord");
     glBindAttribLocation(object_, 9, "iCubeTexCoord2");
     glBindAttribLocation(object_, 9, "iCubeTexCoord2");
+    #ifndef GL_ES_VERSION_2_0
+    glBindAttribLocation(object_, 10, "iInstanceMatrix1");
+    glBindAttribLocation(object_, 11, "iInstanceMatrix2");
+    glBindAttribLocation(object_, 12, "iInstanceMatrix3");
+    #endif
+    
     glAttachShader(object_, vertexShader_->GetGPUObject());
     glAttachShader(object_, vertexShader_->GetGPUObject());
     glAttachShader(object_, pixelShader_->GetGPUObject());
     glAttachShader(object_, pixelShader_->GetGPUObject());
     glLinkProgram(object_);
     glLinkProgram(object_);

+ 1 - 1
Engine/Graphics/Renderer.cpp

@@ -1732,7 +1732,7 @@ void Renderer::SetIndirectionTextureData()
 void Renderer::CreateInstancingBuffer()
 void Renderer::CreateInstancingBuffer()
 {
 {
     // Do not create buffer if instancing not supported
     // Do not create buffer if instancing not supported
-    if (!graphics_->GetSM3Support())
+    if (!graphics_->GetInstancingSupport())
     {
     {
         instancingBuffer_.Reset();
         instancingBuffer_.Reset();
         dynamicInstancing_ = false;
         dynamicInstancing_ = false;