1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org
6 
7 Copyright (c) 2000-2014 Torus Knot Software Ltd
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice shall be included in
16 all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 THE SOFTWARE.
25 -----------------------------------------------------------------------------
26 */
27 
28 #include "OgreShaderPrecompiledHeaders.h"
29 
30 #ifdef RTSHADER_SYSTEM_BUILD_EXT_SHADERS
31 #define HS_DATA_BIND_NAME "HS_SRS_DATA"
32 
33 
34 namespace Ogre {
35 
36 namespace RTShader {
37 
38 /************************************************************************/
39 /*                                                                      */
40 /************************************************************************/
DualQuaternionSkinning()41 DualQuaternionSkinning::DualQuaternionSkinning() : HardwareSkinningTechnique()
42 {
43 }
44 
45 //-----------------------------------------------------------------------
resolveParameters(ProgramSet * programSet)46 bool DualQuaternionSkinning::resolveParameters(ProgramSet* programSet)
47 {
48     Program* vsProgram = programSet->getCpuProgram(GPT_VERTEX_PROGRAM);
49     Function* vsMain = vsProgram->getEntryPointFunction();
50 
51     //if needed mark this vertex program as hardware skinned
52     if (mDoBoneCalculations == true)
53     {
54         vsProgram->setSkeletalAnimationIncluded(true);
55     }
56 
57     //
58     // get the parameters we need whether we are doing bone calculations or not
59     //
60     // Note: in order to be consistent we will always output position, normal,
61     // tangent and binormal in both object and world space. And output position
62     // in projective space to cover the responsibility of the transform stage
63 
64     //input param
65     mParamInPosition = vsMain->resolveInputParameter(Parameter::SPC_POSITION_OBJECT_SPACE);
66     mParamInNormal = vsMain->resolveInputParameter(Parameter::SPC_NORMAL_OBJECT_SPACE);
67     //mParamInBiNormal = vsMain->resolveInputParameter(Parameter::SPS_BINORMAL, 0, Parameter::SPC_BINORMAL_OBJECT_SPACE, GCT_FLOAT3);
68     //mParamInTangent = vsMain->resolveInputParameter(Parameter::SPS_TANGENT, 0, Parameter::SPC_TANGENT_OBJECT_SPACE, GCT_FLOAT3);
69 
70     //local param
71     mParamLocalBlendPosition = vsMain->resolveLocalParameter("BlendedPosition", GCT_FLOAT3);
72     mParamLocalNormalWorld = vsMain->resolveLocalParameter(Parameter::SPC_NORMAL_WORLD_SPACE);
73     //mParamLocalTangentWorld = vsMain->resolveLocalParameter(Parameter::SPS_TANGENT, 0, Parameter::SPC_TANGENT_WORLD_SPACE, GCT_FLOAT3);
74     //mParamLocalBinormalWorld = vsMain->resolveLocalParameter(Parameter::SPS_BINORMAL, 0, Parameter::SPC_BINORMAL_WORLD_SPACE, GCT_FLOAT3);
75 
76     //output param
77     mParamOutPositionProj = vsMain->resolveOutputParameter(Parameter::SPC_POSITION_PROJECTIVE_SPACE);
78 
79     //check if parameter retrival went well
80     bool isValid =
81         (mParamInPosition.get() != NULL) &&
82         (mParamInNormal.get() != NULL) &&
83         //(mParamInBiNormal.get() != NULL) &&
84         //(mParamInTangent.get() != NULL) &&
85         (mParamLocalNormalWorld.get() != NULL) &&
86         //(mParamLocalTangentWorld.get() != NULL) &&
87         //(mParamLocalBinormalWorld.get() != NULL) &&
88         (mParamOutPositionProj.get() != NULL);
89 
90 
91     if (mDoBoneCalculations == true)
92     {
93         //input parameters
94         mParamInNormal = vsMain->resolveInputParameter(Parameter::SPC_NORMAL_OBJECT_SPACE);
95         //mParamInBiNormal = vsMain->resolveInputParameter(Parameter::SPS_BINORMAL, 0, Parameter::SPC_BINORMAL_OBJECT_SPACE, GCT_FLOAT3);
96         //mParamInTangent = vsMain->resolveInputParameter(Parameter::SPS_TANGENT, 0, Parameter::SPC_TANGENT_OBJECT_SPACE, GCT_FLOAT3);
97         mParamInIndices = vsMain->resolveInputParameter(Parameter::SPC_BLEND_INDICES);
98         mParamInWeights = vsMain->resolveInputParameter(Parameter::SPC_BLEND_WEIGHTS);
99         //ACT_WORLD_DUALQUATERNION_ARRAY_2x4 is an array of float4s, so there are two indices for each bone (required by Cg)
100         mParamInWorldMatrices = vsProgram->resolveAutoParameterInt(GpuProgramParameters::ACT_WORLD_DUALQUATERNION_ARRAY_2x4, GCT_FLOAT4, 0, mBoneCount * 2);
101         mParamInInvWorldMatrix = vsProgram->resolveParameter(GpuProgramParameters::ACT_INVERSE_WORLD_MATRIX);
102         mParamInViewProjMatrix = vsProgram->resolveParameter(GpuProgramParameters::ACT_VIEWPROJ_MATRIX);
103 
104         mParamTempWorldMatrix = vsMain->resolveLocalParameter("worldMatrix", GCT_MATRIX_2X4);
105         mParamBlendDQ = vsMain->resolveLocalParameter("blendDQ", GCT_MATRIX_2X4);
106         mParamInitialDQ = vsMain->resolveLocalParameter("initialDQ", GCT_MATRIX_2X4);
107         mParamIndex1 = vsMain->resolveLocalParameter("index1", GCT_FLOAT1);
108         mParamIndex2 = vsMain->resolveLocalParameter("index2", GCT_FLOAT1);
109 
110         if(mScalingShearingSupport)
111         {
112             if (ShaderGenerator::getSingleton().getTargetLanguage() == "hlsl")
113             {
114                 //set hlsl shader to use row-major matrices instead of column-major.
115                 //it enables the use of auto-bound 3x4 matrices in generated hlsl shader.
116                 vsProgram->setUseColumnMajorMatrices(false);
117             }
118 
119             mParamInScaleShearMatrices = vsProgram->resolveParameter(GpuProgramParameters::ACT_WORLD_SCALE_SHEAR_MATRIX_ARRAY_3x4, mBoneCount);
120             mParamBlendS = vsMain->resolveLocalParameter("blendS", GCT_MATRIX_3X4);
121             mParamTempFloat3x3 = vsMain->resolveLocalParameter("TempVal3x3", GCT_MATRIX_3X3);
122             mParamTempFloat3x4 = vsMain->resolveLocalParameter("TempVal3x4", GCT_MATRIX_3X4);
123         }
124 
125         mParamTempFloat2x4 = vsMain->resolveLocalParameter("TempVal2x4", GCT_MATRIX_2X4);
126         mParamTempFloat4 = vsMain->resolveLocalParameter("TempVal4", GCT_FLOAT4);
127         mParamTempFloat3 = vsMain->resolveLocalParameter("TempVal3", GCT_FLOAT3);
128 
129         //check if parameter retrival went well
130         isValid &=
131             (mParamInIndices.get() != NULL) &&
132             (mParamInWeights.get() != NULL) &&
133             (mParamInWorldMatrices.get() != NULL) &&
134             (mParamInViewProjMatrix.get() != NULL) &&
135             (mParamInInvWorldMatrix.get() != NULL) &&
136             (mParamBlendDQ.get() != NULL) &&
137             (mParamInitialDQ.get() != NULL) &&
138             (mParamIndex1.get() != NULL) &&
139             (mParamIndex2.get() != NULL) &&
140 
141             (!mScalingShearingSupport || (mScalingShearingSupport &&
142             (mParamInScaleShearMatrices.get() != NULL &&
143             mParamBlendS.get() != NULL &&
144             mParamTempFloat3x3.get() != NULL &&
145             mParamTempFloat3x4.get() != NULL))) &&
146 
147             (mParamTempFloat2x4.get() != NULL) &&
148             (mParamTempFloat4.get() != NULL) &&
149             (mParamTempFloat3.get() != NULL);
150     }
151     else
152     {
153         mParamInWorldMatrix = vsProgram->resolveParameter(GpuProgramParameters::ACT_WORLD_MATRIX);
154         mParamInWorldViewProjMatrix = vsProgram->resolveParameter(GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
155 
156         //check if parameter retrival went well
157         isValid &=
158             (mParamInWorldMatrix.get() != NULL) &&
159             (mParamInWorldViewProjMatrix.get() != NULL);
160     }
161     return isValid;
162 }
163 
164 //-----------------------------------------------------------------------
resolveDependencies(ProgramSet * programSet)165 bool DualQuaternionSkinning::resolveDependencies(ProgramSet* programSet)
166 {
167     Program* vsProgram = programSet->getCpuProgram(GPT_VERTEX_PROGRAM);
168     vsProgram->addDependency(FFP_LIB_COMMON);
169     vsProgram->addDependency(FFP_LIB_TRANSFORM);
170     vsProgram->addDependency(SGX_LIB_DUAL_QUATERNION);
171 
172     return true;
173 }
174 
175 //-----------------------------------------------------------------------
addFunctionInvocations(ProgramSet * programSet)176 bool DualQuaternionSkinning::addFunctionInvocations(ProgramSet* programSet)
177 {
178     Program* vsProgram = programSet->getCpuProgram(GPT_VERTEX_PROGRAM);
179     Function* vsMain = vsProgram->getEntryPointFunction();
180 
181     //add functions to calculate position data in world, object and projective space
182     addPositionCalculations(vsMain);
183 
184     //add functions to calculate normal and normal related data in world and object space
185     addNormalRelatedCalculations(vsMain, mParamInNormal, mParamLocalNormalWorld);
186     //addNormalRelatedCalculations(vsMain, mParamInTangent, mParamLocalTangentWorld, internalCounter);
187     //addNormalRelatedCalculations(vsMain, mParamInBiNormal, mParamLocalBinormalWorld, internalCounter);
188 
189     return true;
190 }
191 
192 //-----------------------------------------------------------------------
addPositionCalculations(Function * vsMain)193 void DualQuaternionSkinning::addPositionCalculations(Function* vsMain)
194 {
195     auto stage = vsMain->getStage(FFP_VS_TRANSFORM);
196 
197     if (mDoBoneCalculations == true)
198     {
199         if(mScalingShearingSupport)
200         {
201             //Construct a scaling and shearing matrix based on the blend weights
202             for(int i = 0 ; i < getWeightCount() ; ++i)
203             {
204                 //Assign the local param based on the current index of the scaling and shearing matrices
205                 stage.assign({In(mParamInScaleShearMatrices), At(mParamInIndices).mask(indexToMask(i)),
206                               Out(mParamTempFloat3x4)});
207 
208                 //Calculate the resultant scaling and shearing matrix based on the weights given
209                 addIndexedPositionWeight(vsMain, i, mParamTempFloat3x4, mParamTempFloat3x4, mParamBlendS);
210             }
211 
212             //Transform the position based by the scaling and shearing matrix
213             stage.callFunction(FFP_FUNC_TRANSFORM, mParamBlendS, In(mParamInPosition).xyz(), mParamLocalBlendPosition);
214         }
215         else
216         {
217             //Assign the input position to the local blended position
218             stage.assign(In(mParamInPosition).xyz(), mParamLocalBlendPosition);
219         }
220 
221         //Set functions to calculate world position
222         for(int i = 0 ; i < getWeightCount() ; ++i)
223         {
224             //Set the index of the matrix
225             stage.assign(In(mParamInIndices).mask(indexToMask(i)), mParamIndex1);
226 
227             //Multiply the index by 2
228             stage.callFunction(FFP_FUNC_MODULATE, mParamIndex1, 2, mParamIndex1);
229 
230             //Add 1 to the index and assign as the second row's index
231             stage.callFunction(FFP_FUNC_ADD, mParamIndex1, 1, mParamIndex2);
232 
233             //Build the dual quaternion matrix
234             stage.callFunction(SGX_FUNC_BUILD_DUAL_QUATERNION_MATRIX,
235                                {In(mParamInWorldMatrices), At(mParamIndex1), In(mParamInWorldMatrices),
236                                 At(mParamIndex2), Out(mParamTempFloat2x4)});
237 
238             //Adjust the podalities of the dual quaternions
239             if(mCorrectAntipodalityHandling)
240             {
241                 adjustForCorrectAntipodality(vsMain, i, mParamTempFloat2x4);
242             }
243 
244             //Calculate the resultant dual quaternion based on the weights given
245             addIndexedPositionWeight(vsMain, i, mParamTempFloat2x4, mParamTempFloat2x4, mParamBlendDQ);
246         }
247 
248         //Normalize the dual quaternion
249         stage.callFunction(SGX_FUNC_NORMALIZE_DUAL_QUATERNION, mParamBlendDQ);
250 
251         //Calculate the blend position
252         stage.callFunction(SGX_FUNC_CALCULATE_BLEND_POSITION, mParamLocalBlendPosition, mParamBlendDQ, mParamTempFloat4);
253 
254         //Update from object to projective space
255         stage.callFunction(FFP_FUNC_TRANSFORM, mParamInViewProjMatrix, mParamTempFloat4, mParamOutPositionProj);
256     }
257     else
258     {
259         //update from object to projective space
260         stage.callFunction(FFP_FUNC_TRANSFORM, mParamInWorldViewProjMatrix, mParamInPosition, mParamOutPositionProj);
261     }
262 }
263 
264 //-----------------------------------------------------------------------
addNormalRelatedCalculations(Function * vsMain,ParameterPtr & pNormalRelatedParam,ParameterPtr & pNormalWorldRelatedParam)265 void DualQuaternionSkinning::addNormalRelatedCalculations(Function* vsMain,
266                                 ParameterPtr& pNormalRelatedParam,
267                                 ParameterPtr& pNormalWorldRelatedParam)
268 {
269     auto stage = vsMain->getStage(FFP_VS_TRANSFORM);
270 
271     if (mDoBoneCalculations == true)
272     {
273         if(mScalingShearingSupport)
274         {
275             //Calculate the adjoint transpose of the blended scaling and shearing matrix
276             stage.callFunction(SGX_FUNC_ADJOINT_TRANSPOSE_MATRIX, mParamBlendS, mParamTempFloat3x3);
277             //Transform the normal by the adjoint transpose of the blended scaling and shearing matrix
278             stage.callFunction(FFP_FUNC_TRANSFORM, mParamTempFloat3x3, pNormalRelatedParam, pNormalRelatedParam);
279             //Need to normalize again after transforming the normal
280             stage.callFunction(FFP_FUNC_NORMALIZE, pNormalRelatedParam);
281         }
282         //Transform the normal according to the dual quaternion
283         stage.callFunction(SGX_FUNC_CALCULATE_BLEND_NORMAL, pNormalRelatedParam, mParamBlendDQ, pNormalWorldRelatedParam);
284         //update back the original position relative to the object
285         stage.callFunction(FFP_FUNC_TRANSFORM, mParamInInvWorldMatrix, pNormalWorldRelatedParam, pNormalRelatedParam);
286     }
287     else
288     {
289         //update from object to world space
290         stage.callFunction(FFP_FUNC_TRANSFORM, mParamInWorldMatrix, pNormalRelatedParam, pNormalWorldRelatedParam);
291     }
292 }
293 
294 //-----------------------------------------------------------------------
adjustForCorrectAntipodality(Function * vsMain,int index,const ParameterPtr & pTempWorldMatrix)295 void DualQuaternionSkinning::adjustForCorrectAntipodality(Function* vsMain,
296                                 int index, const ParameterPtr& pTempWorldMatrix)
297 {
298     auto stage = vsMain->getStage(FFP_VS_TRANSFORM);
299 
300     //Antipodality doesn't need to be adjusted for dq0 on itself (used as the basis of antipodality calculations)
301     if(index > 0)
302     {
303         //This is the base dual quaternion dq0, which the antipodality calculations are based on
304         stage.callFunction(SGX_FUNC_ANTIPODALITY_ADJUSTMENT, mParamInitialDQ, mParamTempFloat2x4, pTempWorldMatrix);
305     }
306     else if(index == 0)
307     {
308         //Set the first dual quaternion as the initial dq
309         stage.assign(mParamTempFloat2x4, mParamInitialDQ);
310     }
311 }
312 
313 //-----------------------------------------------------------------------
addIndexedPositionWeight(Function * vsMain,int index,ParameterPtr & pWorldMatrix,ParameterPtr & pPositionTempParameter,ParameterPtr & pPositionRelatedOutputParam)314 void DualQuaternionSkinning::addIndexedPositionWeight(Function* vsMain, int index,
315                                 ParameterPtr& pWorldMatrix, ParameterPtr& pPositionTempParameter,
316                                 ParameterPtr& pPositionRelatedOutputParam)
317 {
318     auto stage = vsMain->getStage(FFP_VS_TRANSFORM);
319 
320     //multiply position with world matrix and put into temporary param
321     stage.callFunction(SGX_FUNC_BLEND_WEIGHT, In(mParamInWeights).mask(indexToMask(index)), pWorldMatrix,
322                        pPositionTempParameter);
323 
324     //check if on first iteration
325     if (index == 0)
326     {
327         //set the local param as the value of the world param
328         stage.assign(pPositionTempParameter, pPositionRelatedOutputParam);
329     }
330     else
331     {
332         //add the local param as the value of the world param
333         stage.callFunction(FFP_FUNC_ADD, pPositionTempParameter, pPositionRelatedOutputParam,
334                            pPositionRelatedOutputParam);
335     }
336 }
337 
338 }
339 }
340 
341 #endif
342 
343 
344 
345