ソースを参照

Configure amount of allowed bones for hardware skinning based on the rendering API. Allow 128 bones on Direct3D 11 & OpenGL 3. Add -mb (maxbones) parameter to AssetImporter & OgreImporter. When using OpenGL 3, allow deferred rendering to use different MRT formats without forcing an RGBA substitute rendertarget.

Lasse Öörni 10 年 前
コミット
b8c7c59eca

+ 5 - 1
Docs/Reference.dox

@@ -1127,9 +1127,11 @@ uniform sampler2D sDetailMap2;
 uniform sampler2D sDetailMap3;
 \endcode
 
+The maximum number of bones supported for hardware skinning depends on the graphics API and is relayed to the shader code in the MAXBONES compilation define. Typically the maximum is 64, but is reduced to 32 on the Raspberry PI, and increased to 128 on Direct3D 11 & OpenGL 3. See also \ref Graphics::GetMaxBones "GetMaxBones()".
+
 \section Shaders_API API differences
 
-Direct3D9 and Direct3D11 share the same HLSL shader code, and likewise OpenGL 2, OpenGL 3, OpenGL ES 2 and WebGL share the same GLSL code. Macros and some conditional code are used to hide the API differences where possible. 
+Direct3D9 and Direct3D11 share the same HLSL shader code, and likewise OpenGL 2, OpenGL 3, OpenGL ES 2 and WebGL share the same GLSL code. Macros and some conditional code are used to hide the API differences where possible.
 
 When HLSL shaders are compiled for Direct3D11, the define D3D11 is present, and the following details need to be observed:
 
@@ -2467,6 +2469,7 @@ Options:
 -ns         Do not create subdirectories for resources
 -nz         Do not create a zone and a directional light (scene mode only)
 -nf         Do not fix infacing normals
+-mb <x>     Maximum number of bones per submesh. Default 64
 -p <path>   Set path for scene resources. Default is output file path
 -r <name>   Use the named scene node as root node\n"
 -f <freq>   Animation tick frequency to use if unspecified. Default 4800
@@ -2504,6 +2507,7 @@ Options:
 -r      Output only rotations from animations
 -s      Split each submesh into own vertex buffer
 -t      Generate tangents
+-mb <x> Maximum number of bones per submesh, default 64
 \endverbatim
 
 Note: outputting only bone rotations may help when using an animation in a different model, but if bone position changes have been used for effect, the animation may become less lively. Unpredictable mutilations might result from using an animation in a model not originally intended for, as Urho3D does not specifically attempt to retarget animations.

+ 15 - 6
Source/Tools/AssetImporter/AssetImporter.cpp

@@ -122,6 +122,7 @@ bool noOverwriteMaterial_ = false;
 bool noOverwriteTexture_ = false;
 bool noOverwriteNewerTexture_ = false;
 bool checkUniqueModel_ = true;
+unsigned maxBones_ = 64;
 Vector<String> nonSkinningBoneIncludes_;
 Vector<String> nonSkinningBoneExcludes_;
 
@@ -231,6 +232,7 @@ void Run(const Vector<String>& arguments)
             "-nz         Do not create a zone and a directional light (scene mode only)\n"
             "-nf         Do not fix infacing normals\n"
             "-ne         Do not save empty nodes (scene mode only)\n"
+            "-mb <x>     Maximum number of bones per submesh. Default 64\n"
             "-p <path>   Set path for scene resources. Default is output file path\n"
             "-r <name>   Use the named scene node as root node\n"
             "-f <freq>   Animation tick frequency to use if unspecified. Default 4800\n"
@@ -338,6 +340,13 @@ void Run(const Vector<String>& arguments)
                     break;
                 }
             }
+            else if (argument == "mb" && !value.Empty())
+            {
+                maxBones_ = ToUInt(value);
+                if (maxBones_ < 1)
+                    maxBones_ = 1;
+                ++i;
+            }
             else if (argument == "p" && !value.Empty())
             {
                 resourcePath_ = AddTrailingSlash(value);
@@ -905,7 +914,7 @@ void BuildAndSaveModel(OutModel& model)
         outModel->SetNumGeometryLodLevels(destGeomIndex, 1);
         outModel->SetGeometry(destGeomIndex, 0, geom);
         outModel->SetGeometryCenter(destGeomIndex, center);
-        if (model.bones_.Size() > MAX_SKIN_MATRICES)
+        if (model.bones_.Size() > maxBones_)
             allBoneMappings.Push(boneMappings);
         
         startVertexOffset += mesh->mNumVertices;
@@ -966,7 +975,7 @@ void BuildAndSaveModel(OutModel& model)
         }
         
         outModel->SetSkeleton(skeleton);
-        if (model.bones_.Size() > MAX_SKIN_MATRICES)
+        if (model.bones_.Size() > maxBones_)
             outModel->SetGeometryBoneMappings(allBoneMappings);
     }
     
@@ -1892,13 +1901,13 @@ void GetBlendData(OutModel& model, aiMesh* mesh, PODVector<unsigned>& boneMappin
     boneMappings.Clear();
     
     // If model has more bones than can fit vertex shader parameters, write the per-geometry mappings
-    if (model.bones_.Size() > MAX_SKIN_MATRICES)
+    if (model.bones_.Size() > maxBones_)
     {
-        if (mesh->mNumBones > MAX_SKIN_MATRICES)
+        if (mesh->mNumBones > maxBones_)
         {
             ErrorExit(
-                "Geometry (submesh) has over 64 bone influences. Try splitting to more submeshes\n"
-                "that each stay at 64 bones or below."
+                "Geometry (submesh) has over " + String(maxBones_) + " bone influences. Try splitting to more submeshes\n"
+                "that each stay at " + String(maxBones_) + " bones or below."
             );
         }
         boneMappings.Resize(mesh->mNumBones);

+ 13 - 3
Source/Tools/OgreImporter/OgreImporter.cpp

@@ -28,6 +28,7 @@
 #include <Urho3D/Container/HashSet.h>
 #include <Urho3D/Core/ProcessUtils.h>
 #include <Urho3D/Container/Sort.h>
+#include <Urho3D/Core/StringUtils.h>
 #include <Urho3D/Graphics/Tangent.h>
 #include <Urho3D/Resource/XMLFile.h>
 
@@ -55,6 +56,7 @@ Vector<ModelBone> bones_;
 Vector<ModelMorph> morphs_;
 Vector<String> materialNames_;
 BoundingBox boundingBox_;
+unsigned maxBones_ = 64;
 unsigned numSubMeshes_ = 0;
 bool useOneBuffer_ = true;
 
@@ -94,6 +96,7 @@ void Run(const Vector<String>& arguments)
             "-r      Output only rotations from animations\n"
             "-s      Split each submesh into own vertex buffer\n"
             "-t      Generate tangents\n"
+            "-mb <x> Maximum number of bones per submesh, default 64\n"
         );
     }
     
@@ -133,6 +136,13 @@ void Run(const Vector<String>& arguments)
                     }
                     break;
                 }
+                else if (argument == "mb" && i < arguments.Size() - 1)
+                {
+                    maxBones_ = ToUInt(arguments[i + 1]);
+                    if (maxBones_ < 1)
+                        maxBones_ = 1;
+                    ++i;
+                }
             }
         }
     }
@@ -500,7 +510,7 @@ void LoadMesh(const String& inputFileName, bool generateTangents, bool splitSubM
                 bool sorted = false;
                 
                 // If amount of bones is larger than supported by HW skinning, must remap per submesh
-                if (bones_.Size() > MAX_SKIN_MATRICES)
+                if (bones_.Size() > maxBones_)
                 {
                     HashMap<unsigned, unsigned> usedBoneMap;
                     unsigned remapIndex = 0;
@@ -524,8 +534,8 @@ void LoadMesh(const String& inputFileName, bool generateTangents, bool splitSubM
                     }
                     
                     // If still too many bones in one subgeometry, error
-                    if (usedBoneMap.Size() > MAX_SKIN_MATRICES)
-                        ErrorExit("Too many bones in submesh " + String(subMeshIndex + 1));
+                    if (usedBoneMap.Size() > maxBones_)
+                        ErrorExit("Too many bones (limit " + String(maxBones_) + ") in submesh " + String(subMeshIndex + 1));
                     
                     // Write mapping of vertex buffer bone indices to original bone indices
                     subGeometryLodLevel.boneMapping_.Resize(usedBoneMap.Size());

+ 1 - 1
Source/Urho3D/Graphics/DecalSet.cpp

@@ -913,7 +913,7 @@ bool DecalSet::GetBones(Drawable* target, unsigned batchIndex, const float* blen
 
             if (!found)
             {
-                if (bones_.Size() >= MAX_SKIN_MATRICES)
+                if (bones_.Size() >= Graphics::GetMaxBones())
                 {
                     LOGWARNING("Maximum skinned decal bone count reached");
                     return false;

+ 2 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Graphics.h

@@ -420,6 +420,8 @@ public:
     static unsigned GetFormat(const String& formatName);
     /// Return UV offset required for pixel perfect rendering.
     static const Vector2& GetPixelUVOffset() { return pixelUVOffset; }
+    /// Return maximum number of supported bones for skinning.
+    static unsigned GetMaxBones() { return 128; }
 
 private:
     /// Create the application window.

+ 2 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11ShaderVariation.cpp

@@ -252,6 +252,8 @@ bool ShaderVariation::Compile()
         profile = "ps_4_0";
         flags |= D3DCOMPILE_PREFER_FLOW_CONTROL;
     }
+
+    defines.Push("MAXBONES=" + String(Graphics::GetMaxBones()));
     
     // Collect defines into macros
     Vector<String> defineValues;

+ 2 - 0
Source/Urho3D/Graphics/Direct3D9/D3D9Graphics.h

@@ -414,6 +414,8 @@ public:
     static unsigned GetFormat(const String& formatName);
     /// Return UV offset required for pixel perfect rendering.
     static const Vector2& GetPixelUVOffset() { return pixelUVOffset; }
+    /// Return maximum number of supported bones for skinning.
+    static unsigned GetMaxBones() { return 64; }
 
 private:
     /// Set vertex buffer stream frequency.

+ 2 - 0
Source/Urho3D/Graphics/Direct3D9/D3D9ShaderVariation.cpp

@@ -241,6 +241,8 @@ bool ShaderVariation::Compile(PODVector<unsigned>& byteCode)
         profile = "ps_3_0";
         flags |= D3DCOMPILE_PREFER_FLOW_CONTROL;
     }
+
+    defines.Push("MAXBONES=" + String(Graphics::GetMaxBones()));
     
     // Collect defines into macros
     Vector<String> defineValues;

+ 0 - 1
Source/Urho3D/Graphics/GraphicsDefs.h

@@ -358,7 +358,6 @@ static const unsigned NO_ELEMENT = 0xffffffff;
 
 static const int MAX_RENDERTARGETS = 4;
 static const int MAX_VERTEX_STREAMS = 4;
-static const int MAX_SKIN_MATRICES = 64;
 static const int MAX_CONSTANT_REGISTERS = 256;
 
 static const int BITS_PER_COMPONENT = 8;

+ 9 - 0
Source/Urho3D/Graphics/OpenGL/OGLGraphics.cpp

@@ -2086,6 +2086,15 @@ unsigned Graphics::GetFormat(CompressedFormat format) const
     }
 }
 
+unsigned Graphics::GetMaxBones()
+{
+    #ifdef RPI
+    return 32;
+    #else
+    return gl3Support ? 128 : 64;
+    #endif
+}
+
 ShaderVariation* Graphics::GetShader(ShaderType type, const String& name, const String& defines) const
 {
     return GetShader(type, name.CString(), defines.CString());

+ 2 - 0
Source/Urho3D/Graphics/OpenGL/OGLGraphics.h

@@ -439,6 +439,8 @@ public:
     static unsigned GetFormat(const String& formatName);
     /// Return UV offset required for pixel perfect rendering.
     static const Vector2& GetPixelUVOffset() { return pixelUVOffset; }
+    /// Return maximum number of supported bones for skinning.
+    static unsigned GetMaxBones();
     /// Return whether is using an OpenGL 3 context.
     static bool GetGL3Support() { return gl3Support; }
 

+ 4 - 1
Source/Urho3D/Graphics/OpenGL/OGLShaderVariation.cpp

@@ -127,7 +127,10 @@ bool ShaderVariation::Create()
 
     // Distinguish between VS and PS compile in case the shader code wants to include/omit different things
     shaderCode += type_ == VS ? "#define COMPILEVS\n" : "#define COMPILEPS\n";
-    
+
+    // Add define for the maximum number of supported bones
+    shaderCode += "#define MAXBONES " + String(Graphics::GetMaxBones()) + "\n";
+
     // Prepend the defines to the shader code
     Vector<String> defineVec = defines_.Split(' ');
     for (unsigned i = 0; i < defineVec.Size(); ++i)

+ 4 - 3
Source/Urho3D/Graphics/View.cpp

@@ -1865,9 +1865,10 @@ void View::AllocateScreenBuffers()
 
     #ifdef URHO3D_OPENGL
     // Due to FBO limitations, in OpenGL deferred modes need to render to texture first and then blit to the backbuffer
-    // Also, if rendering to a texture with full deferred rendering, it must be RGBA to comply with the rest of the buffers.
-    if ((deferred_ && !renderTarget_) || (deferredAmbient_ && renderTarget_ && renderTarget_->GetParentTexture()->GetFormat() !=
-        Graphics::GetRGBAFormat()))
+    // Also, if rendering to a texture with full deferred rendering, it must be RGBA to comply with the rest of the buffers,
+    // unless using OpenGL 3
+    if ((deferred_ && !renderTarget_) || (!Graphics::GetGL3Support() && deferredAmbient_ && renderTarget_ && 
+        renderTarget_->GetParentTexture()->GetFormat() != Graphics::GetRGBAFormat()))
         needSubstitute = true;
     // Also need substitute if rendering to backbuffer using a custom (readable) depth buffer
     if (!renderTarget_ && !needSubstitute)

+ 1 - 0
Source/Urho3D/LuaScript/pkgs/Graphics/Graphics.pkg

@@ -72,6 +72,7 @@ class Graphics : public Object
     static unsigned GetDepthStencilFormat();
     static unsigned GetReadableDepthFormat();
     static unsigned GetFormat(const String formatName);
+    static unsigned GetMaxBones();
 
     tolua_readonly tolua_property__is_set bool initialized;
     tolua_property__get_set String windowTitle;

+ 2 - 0
Source/Urho3D/Script/GraphicsAPI.cpp

@@ -483,9 +483,11 @@ static void RegisterTextures(asIScriptEngine* engine)
     engine->RegisterGlobalFunction("uint GetRGFloat32Format()", asFUNCTION(Graphics::GetRGFloat32Format), asCALL_CDECL);
     engine->RegisterGlobalFunction("uint GetFloat16Format()", asFUNCTION(Graphics::GetFloat16Format), asCALL_CDECL);
     engine->RegisterGlobalFunction("uint GetFloat32Format()", asFUNCTION(Graphics::GetFloat32Format), asCALL_CDECL);
+    engine->RegisterGlobalFunction("uint GetLinearDepthFormat()", asFUNCTION(Graphics::GetLinearDepthFormat), asCALL_CDECL);
     engine->RegisterGlobalFunction("uint GetDepthStencilFormat()", asFUNCTION(Graphics::GetDepthStencilFormat), asCALL_CDECL);
     engine->RegisterGlobalFunction("uint GetReadableDepthFormat()", asFUNCTION(Graphics::GetReadableDepthFormat), asCALL_CDECL);
     engine->RegisterGlobalFunction("uint GetFormat(const String&in)", asFUNCTIONPR(Graphics::GetFormat, (const String&), unsigned), asCALL_CDECL);
+    engine->RegisterGlobalFunction("uint GetMaxBones()", asFUNCTION(Graphics::GetMaxBones), asCALL_CDECL);
 }
 
 static Material* MaterialClone(const String& cloneName, Material* ptr)

+ 2 - 6
bin/CoreData/Shaders/GLSL/Uniforms.glsl

@@ -33,11 +33,7 @@ uniform mat4 cZone;
     uniform mat4 cLightMatrices[2];
 #endif
 #ifdef SKINNED
-    #ifdef RPI
-        uniform vec4 cSkinMatrices[32*3];
-    #else
-        uniform vec4 cSkinMatrices[64*3];
-    #endif
+    uniform vec4 cSkinMatrices[MAXBONES*3];
 #endif
 #ifdef NUMVERTEXLIGHTS
     uniform vec4 cVertexLights[4*3];
@@ -138,7 +134,7 @@ uniform ObjectVS
     mat3 cBillboardRot;
 #endif
 #ifdef SKINNED
-    uniform vec4 cSkinMatrices[64*3];
+    uniform vec4 cSkinMatrices[MAXBONES*3];
 #endif
 };
 

+ 2 - 2
bin/CoreData/Shaders/HLSL/Uniforms.hlsl

@@ -27,7 +27,7 @@ uniform float4 cUOffset;
 uniform float4 cVOffset;
 uniform float4x3 cZone;
 #ifdef SKINNED
-    uniform float4x3 cSkinMatrices[64];
+    uniform float4x3 cSkinMatrices[MAXBONES];
 #endif
 #ifdef NUMVERTEXLIGHTS
     uniform float4 cVertexLights[4*3];
@@ -123,7 +123,7 @@ cbuffer ObjectVS : register(b5)
     float3x3 cBillboardRot;
 #endif
 #ifdef SKINNED
-    uniform float4x3 cSkinMatrices[64];
+    uniform float4x3 cSkinMatrices[MAXBONES];
 #endif
 }
 #endif