From 6968753783c8ad691982d6f3c0893a35dc4d95c4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 19 Dec 2017 17:40:34 +0100 Subject: [PATCH 01/53] Added G factor in PBR shading and removed evalPBRShadingGloss --- libraries/render-utils/src/LightingModel.slh | 92 +++++++++----------- 1 file changed, 39 insertions(+), 53 deletions(-) diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 521c4894dc..64e819cf38 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -121,86 +121,72 @@ float fresnelSchlickScalar(float fresnelScalar, vec3 lightDir, vec3 halfDir) { return (exponential) + fresnelScalar * (1.0 - exponential); } -float specularDistribution(float roughness, vec3 normal, vec3 halfDir) { - float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0); -// float gloss2 = pow(0.001 + roughness, 4); - float gloss2 = (0.001 + roughness); - gloss2 *= gloss2; // pow 2 - gloss2 *= gloss2; // pow 4 - float denom = (ndoth * ndoth*(gloss2 - 1.0) + 1.0); - float power = gloss2 / (3.14159 * denom * denom); +float specularDistribution(float roughness, vec3 normal, vec3 halfDir, vec3 eyeDir, float ndotl) { + // See https://www.khronos.org/assets/uploads/developers/library/2017-web3d/glTF-2.0-Launch_Jun17.pdf + // for details of equations, especially page 20 + float ndoth = dot(halfDir, normal); + float ndotv = dot(eyeDir, normal); + vec3 nd = clamp(vec3(ndoth, ndotv, ndotl), vec3(0.0), vec3(1.0)); + vec3 nd2 = nd * nd; + float roughness2 = (0.001 + roughness*0.999); + roughness2 *= roughness2; // pow 2 + float roughness4 = roughness2; + roughness4 *= roughness4; // pow 4 + float denom = (nd2.x * (roughness2 - 1.0) + 1.0); + denom *= denom; + // Add geometric factors G1(n,l) and G1(n,v) + float oneMinusRoughness4 = 1.0-roughness4; + vec2 invG = nd.yz + sqrt(vec2(roughness4)+nd2.yz*oneMinusRoughness4); + denom *= invG.x * invG.y; + // Don't divide by PI as diffuse isn't either. The real PBR formula normalizes + // by PI on both but if we decide to not do it, it is just as if we + // multiplied all our lights by PI + //denom *= 3.1415926; + float power = roughness4 / denom; return power; } -float specularDistributionGloss(float gloss2, vec3 normal, vec3 halfDir) { - float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0); -// float gloss2 = pow(0.001 + roughness, 4); - float denom = (ndoth * ndoth*(gloss2 - 1.0) + 1.0); - float power = gloss2 / (3.14159 * denom * denom); - return power; -} - + // Frag Shading returns the diffuse amount as W and the specular rgb as xyz vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + // Incident angle attenuation + float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); // Specular Lighting vec3 halfDir = normalize(fragEyeDir + fragLightDir); vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir); - vec3 specular = fresnelColor * power * diffuse; + float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + vec3 specular = fresnelColor * power * angleAttenuation; - return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x)); + return vec4(specular, (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x)); } // Frag Shading returns the diffuse amount as W and the specular rgb as xyz vec4 evalPBRShadingDielectric(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, float fresnel) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + // Incident angle attenuation + float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); // Specular Lighting vec3 halfDir = normalize(fragEyeDir + fragLightDir); float fresnelScalar = fresnelSchlickScalar(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir); - vec3 specular = vec3(fresnelScalar) * power * diffuse; + float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + vec3 specular = vec3(fresnelScalar) * power * angleAttenuation; - return vec4(specular, diffuse * (1.0 - fresnelScalar)); + return vec4(specular, angleAttenuation * (1.0 - fresnelScalar)); } vec4 evalPBRShadingMetallic(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, vec3 fresnel) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + // Incident angle attenuation + float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); // Specular Lighting vec3 halfDir = normalize(fragEyeDir + fragLightDir); vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir); - vec3 specular = fresnelColor * power * diffuse; + float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + vec3 specular = fresnelColor * power * angleAttenuation; return vec4(specular, 0.f); } -// Frag Shading returns the diffuse amount as W and the specular rgb as xyz -vec4 evalPBRShadingGloss(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float gloss2) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); - - // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistributionGloss(gloss2, fragNormal, halfDir); - vec3 specular = fresnelColor * power * diffuse; - - return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x)); -} - <@endfunc@> @@ -244,7 +230,7 @@ void evalFragShading(out vec3 diffuse, out vec3 specular, diffuse *= specularBrdf.y; specular = vec3(specularBrdf.x); } else { - vec4 shading = evalPBRShadingGloss(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); + vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); diffuse = vec3(shading.w); specular = shading.xyz; } @@ -274,7 +260,7 @@ void evalFragShadingGloss(out vec3 diffuse, out vec3 specular, vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float gloss, vec3 albedo ) { - vec4 shading = evalPBRShadingGloss(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, gloss); + vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, gloss); diffuse = vec3(shading.w); diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled()); specular = shading.xyz; From 0e666ce671c024b2bfc6b2332c7e9d710b72535d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 20 Dec 2017 09:34:53 +0100 Subject: [PATCH 02/53] Metallic value is now continuous and materials can be linearly blended between pure dielectric and pure metal --- .../render-utils/src/DeferredBufferRead.slh | 23 +++++++------------ libraries/render-utils/src/LightingModel.slh | 2 +- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/libraries/render-utils/src/DeferredBufferRead.slh b/libraries/render-utils/src/DeferredBufferRead.slh index fbca241bb9..5667f63d13 100644 --- a/libraries/render-utils/src/DeferredBufferRead.slh +++ b/libraries/render-utils/src/DeferredBufferRead.slh @@ -46,6 +46,12 @@ struct DeferredFragment { float depthVal; }; +vec3 getFresnelF0(float metallic, vec3 metalF0) { + // Enable continuous metallness value by lerping between dielectric + // and metal fresnel F0 value based on the "metallic" parameter + return mix(vec3(0.03), metalF0, metallic); +} + DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) { vec4 normalVal; vec4 diffuseVal; @@ -73,13 +79,7 @@ DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) { frag.scattering = specularVal.x; } - if (frag.metallic <= 0.5) { - frag.metallic = 0.0; - frag.fresnel = vec3(0.03); // Default Di-electric fresnel value - } else { - frag.fresnel = vec3(diffuseVal.xyz); - frag.metallic = 1.0; - } + frag.fresnel = getFresnelF0(frag.metallic, diffuseVal.xyz); return frag; } @@ -106,14 +106,7 @@ DeferredFragment unpackDeferredFragmentNoPositionNoAmbient(vec2 texcoord) { //frag.emissive = specularVal.xyz; frag.obscurance = 1.0; - - if (frag.metallic <= 0.5) { - frag.metallic = 0.0; - frag.fresnel = vec3(0.03); // Default Di-electric fresnel value - } else { - frag.fresnel = vec3(diffuseVal.xyz); - frag.metallic = 1.0; - } + frag.fresnel = getFresnelF0(frag.metallic, diffuseVal.xyz); return frag; } diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 64e819cf38..d77e19f13a 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -128,7 +128,7 @@ float specularDistribution(float roughness, vec3 normal, vec3 halfDir, vec3 eyeD float ndotv = dot(eyeDir, normal); vec3 nd = clamp(vec3(ndoth, ndotv, ndotl), vec3(0.0), vec3(1.0)); vec3 nd2 = nd * nd; - float roughness2 = (0.001 + roughness*0.999); + float roughness2 = mix(0.001, 1.0, roughness); roughness2 *= roughness2; // pow 2 float roughness4 = roughness2; roughness4 *= roughness4; // pow 4 From e8b88cd3f845dcd9ce5624f406ad6058b69c9564 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 20 Dec 2017 11:14:07 +0100 Subject: [PATCH 03/53] Specular lighting comming from ambient sphere map is now multiplied by PI to be at the same level as normal light specular --- libraries/render-utils/src/LightAmbient.slh | 29 ++++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 5f74b46d3e..f3f1411394 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -22,8 +22,10 @@ vec4 evalSkyboxLight(vec3 direction, float lod) { <@func declareEvalAmbientSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@> -vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float gloss) { - return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5.0); +vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float gloss) { + float f = pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5.0); + return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * f; +// return mix(fresnelColor, vec3(1.0), f); } <@if supportAmbientMap@> @@ -31,7 +33,7 @@ vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float <@endif@> vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 fragNormal, float roughness) { - vec3 direction = -reflect(fragEyeDir, fragNormal); + vec3 lightDir = -reflect(fragEyeDir, fragNormal); vec3 specularLight; <@if supportIfAmbientMapElseAmbientSphere@> if (getLightHasAmbientMap(ambient)) @@ -40,7 +42,13 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f { float levels = getLightAmbientMapNumMips(ambient); float lod = min(((roughness)* levels), levels); - specularLight = evalSkyboxLight(direction, lod).xyz; + specularLight = evalSkyboxLight(lightDir, lod).xyz; + // We multiply specular by Pi to match specular / diffuse normalization in + // LightingModel.slh where diffuse and specular arent divided by Pi (when they should, rigourously + // speaking, based on physical equations). The spherical harmonics evaluation, on + // the other hand, seems to be Pi times stronger than usual. So all in all, everything + // should be at the right level now. + specularLight *= 3.1415926; } <@endif@> <@if supportIfAmbientMapElseAmbientSphere@> @@ -48,11 +56,11 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f <@endif@> <@if supportAmbientSphere@> { - specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), direction).xyz; + specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz; } <@endif@> - return specularLight; + return specularLight; } <@endfunc@> @@ -67,14 +75,14 @@ float curvatureAO(in float k) { <@endif@> void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambient, vec3 eyeDir, vec3 normal, - float roughness, float metallic, vec3 fresnel, vec3 albedo, float obscurance + float roughness, float metallic, vec3 fresnelF0, vec3 albedo, float obscurance <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature <@endif@> ) { // Fresnel - vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, eyeDir, normal, 1.0 - roughness); + vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, eyeDir, normal, 1.0 - roughness); // Diffuse from ambient diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; @@ -107,8 +115,9 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie diffuse *= albedo; } - diffuse *= lightEnergy * isDiffuseEnabled() * isAmbientEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isAmbientEnabled(); + lightEnergy *= isAmbientEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); } <@endfunc@> From 053bd2ba9826a13311219e30cb3ede9214783310 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 21 Dec 2017 17:41:36 +0100 Subject: [PATCH 04/53] Created SurfaceData structure to centralize computation of frequently used shader temporary values --- .../render-utils/src/DeferredBufferRead.slh | 3 + .../render-utils/src/DeferredGlobalLight.slh | 24 ++- .../render-utils/src/ForwardGlobalLight.slh | 18 +- libraries/render-utils/src/LightAmbient.slh | 25 +-- .../render-utils/src/LightDirectional.slh | 11 +- libraries/render-utils/src/LightPoint.slh | 11 +- libraries/render-utils/src/LightSpot.slh | 10 +- libraries/render-utils/src/LightingModel.slh | 166 +++++++++++------- .../render-utils/src/SubsurfaceScattering.slh | 32 ---- .../render-utils/src/local_lights_shading.slf | 17 +- .../render-utils/src/model_translucent.slf | 8 +- .../src/model_translucent_fade.slf | 8 +- libraries/render-utils/src/overlay3D.slf | 4 +- .../src/overlay3D_translucent.slf | 4 +- 14 files changed, 180 insertions(+), 161 deletions(-) diff --git a/libraries/render-utils/src/DeferredBufferRead.slh b/libraries/render-utils/src/DeferredBufferRead.slh index 5667f63d13..3cbed3fcef 100644 --- a/libraries/render-utils/src/DeferredBufferRead.slh +++ b/libraries/render-utils/src/DeferredBufferRead.slh @@ -46,11 +46,14 @@ struct DeferredFragment { float depthVal; }; +<@if not GETFRESNEL0@> +<@def GETFRESNEL0@> vec3 getFresnelF0(float metallic, vec3 metalF0) { // Enable continuous metallness value by lerping between dielectric // and metal fresnel F0 value based on the "metallic" parameter return mix(vec3(0.03), metalF0, metallic); } +<@endif@> DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) { vec4 normalVal; diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index de2d41be6b..ad4a9a3006 100644 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -65,10 +65,12 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -79,7 +81,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -110,10 +112,12 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu ) { <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -123,7 +127,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -174,19 +178,21 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) { <$prepareGlobalLight()$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + color += emissive * isEmissiveEnabled(); // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance); + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance); color += ambientDiffuse; color += ambientSpecular / opacity; // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; @@ -199,19 +205,21 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze( { <$prepareGlobalLight()$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + color += emissive * isEmissiveEnabled(); // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance); + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance); color += ambientDiffuse; color += ambientSpecular / opacity; // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; diff --git a/libraries/render-utils/src/ForwardGlobalLight.slh b/libraries/render-utils/src/ForwardGlobalLight.slh index aba0498ef5..3f77f85b51 100644 --- a/libraries/render-utils/src/ForwardGlobalLight.slh +++ b/libraries/render-utils/src/ForwardGlobalLight.slh @@ -65,10 +65,12 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -79,7 +81,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -109,10 +111,12 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu ) { <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -124,7 +128,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -173,19 +177,21 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) { <$prepareGlobalLight()$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + color += emissive * isEmissiveEnabled(); // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance); + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance); color += ambientDiffuse; color += ambientSpecular / opacity; // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index f3f1411394..e466702a34 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -22,8 +22,9 @@ vec4 evalSkyboxLight(vec3 direction, float lod) { <@func declareEvalAmbientSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@> -vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float gloss) { - float f = pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5.0); +vec3 fresnelSchlickAmbient(vec3 fresnelColor, SurfaceData surface) { + float gloss = 1.0 - surface.roughness; + float f = pow(1.0 - surface.ldoth, 5.0); return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * f; // return mix(fresnelColor, vec3(1.0), f); } @@ -32,8 +33,8 @@ vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, floa <$declareSkyboxMap()$> <@endif@> -vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 fragNormal, float roughness) { - vec3 lightDir = -reflect(fragEyeDir, fragNormal); +vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { + vec3 lightDir = -reflect(surface.eyeDir, surface.normal); vec3 specularLight; <@if supportIfAmbientMapElseAmbientSphere@> if (getLightHasAmbientMap(ambient)) @@ -41,8 +42,8 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f <@if supportAmbientMap@> { float levels = getLightAmbientMapNumMips(ambient); - float lod = min(((roughness)* levels), levels); - specularLight = evalSkyboxLight(lightDir, lod).xyz; + float lod = min(((surface.roughness)* levels), levels); + specularLight = evalSkyboxLight(surface.lightDir, lod).xyz; // We multiply specular by Pi to match specular / diffuse normalization in // LightingModel.slh where diffuse and specular arent divided by Pi (when they should, rigourously // speaking, based on physical equations). The spherical harmonics evaluation, on @@ -56,7 +57,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f <@endif@> <@if supportAmbientSphere@> { - specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz; + specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), surface.lightDir).xyz; } <@endif@> @@ -74,21 +75,21 @@ float curvatureAO(in float k) { } <@endif@> -void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambient, vec3 eyeDir, vec3 normal, - float roughness, float metallic, vec3 fresnelF0, vec3 albedo, float obscurance +void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambient, SurfaceData surface, + float metallic, vec3 fresnelF0, vec3 albedo, float obscurance <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature <@endif@> ) { // Fresnel - vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, eyeDir, normal, 1.0 - roughness); + vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, surface); // Diffuse from ambient - diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; + diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), surface.normal).xyz; // Specular highlight from ambient - specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness) * ambientFresnel; + specular = evalAmbientSpecularIrradiance(ambient, surface) * ambientFresnel; <@if supportScattering@> if (scattering * isScatteringEnabled() > 0.0) { diff --git a/libraries/render-utils/src/LightDirectional.slh b/libraries/render-utils/src/LightDirectional.slh index 749709c600..b6e1720a2c 100644 --- a/libraries/render-utils/src/LightDirectional.slh +++ b/libraries/render-utils/src/LightDirectional.slh @@ -12,7 +12,7 @@ <@func declareLightingDirectional(supportScattering)@> void evalLightingDirectional(out vec3 diffuse, out vec3 specular, vec3 lightDir, vec3 lightIrradiance, - vec3 eyeDir, vec3 normal, float roughness, + SurfaceData surface, float metallic, vec3 fresnel, vec3 albedo, float shadow <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature @@ -22,14 +22,17 @@ void evalLightingDirectional(out vec3 diffuse, out vec3 specular, vec3 lightDir, // Attenuation vec3 lightEnergy = shadow * lightIrradiance; - evalFragShading(diffuse, specular, normal, -lightDir, eyeDir, metallic, fresnel, roughness, albedo + updateSurfaceDataWithLight(surface, -lightDir); + + evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); - diffuse *= lightEnergy * isDiffuseEnabled() * isDirectionalEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isDirectionalEnabled(); + lightEnergy *= isDirectionalEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); } <@endfunc@> diff --git a/libraries/render-utils/src/LightPoint.slh b/libraries/render-utils/src/LightPoint.slh index 7e389e11f6..91a1260fcc 100644 --- a/libraries/render-utils/src/LightPoint.slh +++ b/libraries/render-utils/src/LightPoint.slh @@ -12,7 +12,7 @@ <@func declareLightingPoint(supportScattering)@> void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light, - vec4 fragLightDirLen, vec3 fragEyeDir, vec3 normal, float roughness, + vec4 fragLightDirLen, SurfaceData surface, float metallic, vec3 fresnel, vec3 albedo, float shadow <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature @@ -23,19 +23,22 @@ void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light, float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); + // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); vec3 lightEnergy = radialAttenuation * shadow * getLightIrradiance(light); // Eval shading - evalFragShading(diffuse, specular, normal, fragLightDir, fragEyeDir, metallic, fresnel, roughness, albedo + evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); - diffuse *= lightEnergy * isDiffuseEnabled() * isPointEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isPointEnabled(); + lightEnergy *= isPointEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); if (isShowLightContour() > 0.0) { // Show edge diff --git a/libraries/render-utils/src/LightSpot.slh b/libraries/render-utils/src/LightSpot.slh index 8627dae0eb..73c5bd9559 100644 --- a/libraries/render-utils/src/LightSpot.slh +++ b/libraries/render-utils/src/LightSpot.slh @@ -12,7 +12,7 @@ <@func declareLightingSpot(supportScattering)@> void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light, - vec4 fragLightDirLen, float cosSpotAngle, vec3 fragEyeDir, vec3 normal, float roughness, + vec4 fragLightDirLen, float cosSpotAngle, SurfaceData surface, float metallic, vec3 fresnel, vec3 albedo, float shadow <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature @@ -23,6 +23,7 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light, float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); @@ -30,14 +31,15 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light, vec3 lightEnergy = angularAttenuation * radialAttenuation * shadow *getLightIrradiance(light); // Eval shading - evalFragShading(diffuse, specular, normal, fragLightDir, fragEyeDir, metallic, fresnel, roughness, albedo + evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); - diffuse *= lightEnergy * isDiffuseEnabled() * isSpotEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isSpotEnabled(); + lightEnergy *= isSpotEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); if (isShowLightContour() > 0.0) { // Show edges diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index d77e19f13a..27b5327b09 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -77,6 +77,30 @@ float isWireframeEnabled() { <@endfunc@> <$declareLightingModel()$> +struct SurfaceData { + vec3 normal; + vec3 eyeDir; + vec3 lightDir; + vec3 halfDir; + float roughness; + float roughness2; + float roughness4; + float ndotv; + float ndotl; + float ndoth; + float ldoth; + float smithInvG1NdotV; +}; + +<@if not GETFRESNEL0@> +<@def GETFRESNEL0@> +vec3 getFresnelF0(float metallic, vec3 metalF0) { + // Enable continuous metallness value by lerping between dielectric + // and metal fresnel F0 value based on the "metallic" parameter + return mix(vec3(0.03), metalF0, metallic); +} +<@endif@> + <@func declareBeckmannSpecular()@> uniform sampler2D scatteringSpecularBeckmann; @@ -85,17 +109,13 @@ float fetchSpecularBeckmann(float ndoth, float roughness) { return pow(2.0 * texture(scatteringSpecularBeckmann, vec2(ndoth, roughness)).r, 10.0); } -vec2 skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) { +vec2 skinSpecular(SurfaceData surface, float intensity) { vec2 result = vec2(0.0, 1.0); - float ndotl = dot(N, L); - if (ndotl > 0.0) { - vec3 h = L + V; - vec3 H = normalize(h); - float ndoth = dot(N, H); - float PH = fetchSpecularBeckmann(ndoth, roughness); - float F = fresnelSchlickScalar(0.028, H, V); - float frSpec = max(PH * F / dot(h, h), 0.0); - result.x = ndotl * intensity * frSpec; + if (surface.ndotl > 0.0) { + float PH = fetchSpecularBeckmann(surface.ndoth, surface.roughness); + float F = fresnelSchlickScalar(0.028, surface); + float frSpec = max(PH * F / dot(surface.halfDir, surface.halfDir), 0.0); + result.x = surface.ndotl * intensity * frSpec; result.y -= F; } @@ -105,83 +125,105 @@ vec2 skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) { <@func declareEvalPBRShading()@> -vec3 fresnelSchlickColor(vec3 fresnelColor, vec3 lightDir, vec3 halfDir) { - float base = 1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0); +float evalSmithInvG1(float roughness4, float ndotd) { + return ndotd + sqrt(roughness4+ndotd*ndotd*(1.0-roughness4)); +} + +SurfaceData initSurfaceData(float roughness, vec3 normal, vec3 eyeDir) { + SurfaceData surface; + surface.eyeDir = eyeDir; + surface.normal = normal; + surface.lightDir = vec3(0,0,0); + surface.halfDir = vec3(0,0,0); + surface.roughness = mix(0.001, 1.0, roughness); + surface.roughness2 = surface.roughness * surface.roughness; + surface.roughness4 = surface.roughness2 * surface.roughness2; + surface.ndotv = clamp(dot(normal, eyeDir), 0.0, 1.0); + surface.ndoth = 0.0; + surface.ndotl = 0.0; + surface.ldoth = 0.0; + surface.smithInvG1NdotV = evalSmithInvG1(surface.roughness4, surface.ndotv); + return surface; +} + +void updateSurfaceDataWithLight(inout SurfaceData surface, vec3 lightDir) { + surface.lightDir = lightDir; + surface.halfDir = normalize(surface.eyeDir + lightDir); + vec3 dots; + dots.x = dot(surface.normal, surface.halfDir); + dots.y = dot(surface.normal, surface.lightDir); + dots.z = dot(surface.halfDir, surface.lightDir); + dots = clamp(dots, vec3(0), vec3(1)); + surface.ndoth = dots.x; + surface.ndotl = dots.y; + surface.ldoth = dots.z; +} + +vec3 fresnelSchlickColor(vec3 fresnelColor, SurfaceData surface) { + float base = 1.0 - surface.ldoth; //float exponential = pow(base, 5.0); float base2 = base * base; float exponential = base * base2 * base2; return vec3(exponential) + fresnelColor * (1.0 - exponential); } -float fresnelSchlickScalar(float fresnelScalar, vec3 lightDir, vec3 halfDir) { - float base = 1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0); +float fresnelSchlickScalar(float fresnelScalar, SurfaceData surface) { + float base = 1.0 - surface.ldoth; //float exponential = pow(base, 5.0); float base2 = base * base; float exponential = base * base2 * base2; return (exponential) + fresnelScalar * (1.0 - exponential); } -float specularDistribution(float roughness, vec3 normal, vec3 halfDir, vec3 eyeDir, float ndotl) { +float specularDistribution(SurfaceData surface) { // See https://www.khronos.org/assets/uploads/developers/library/2017-web3d/glTF-2.0-Launch_Jun17.pdf // for details of equations, especially page 20 - float ndoth = dot(halfDir, normal); - float ndotv = dot(eyeDir, normal); - vec3 nd = clamp(vec3(ndoth, ndotv, ndotl), vec3(0.0), vec3(1.0)); - vec3 nd2 = nd * nd; - float roughness2 = mix(0.001, 1.0, roughness); - roughness2 *= roughness2; // pow 2 - float roughness4 = roughness2; - roughness4 *= roughness4; // pow 4 - float denom = (nd2.x * (roughness2 - 1.0) + 1.0); + float denom = (surface.ndoth*surface.ndoth * (surface.roughness2 - 1.0) + 1.0); denom *= denom; // Add geometric factors G1(n,l) and G1(n,v) - float oneMinusRoughness4 = 1.0-roughness4; - vec2 invG = nd.yz + sqrt(vec2(roughness4)+nd2.yz*oneMinusRoughness4); - denom *= invG.x * invG.y; + float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl); + denom *= surface.smithInvG1NdotV * smithInvG1NdotL; // Don't divide by PI as diffuse isn't either. The real PBR formula normalizes // by PI on both but if we decide to not do it, it is just as if we // multiplied all our lights by PI //denom *= 3.1415926; - float power = roughness4 / denom; + float power = surface.roughness4 / denom; return power; } // Frag Shading returns the diffuse amount as W and the specular rgb as xyz -vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) { +vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) { // Incident angle attenuation - float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + float angleAttenuation = surface.ndotl; // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + vec3 fresnelColor = fresnelSchlickColor(fresnel, surface); + float power = specularDistribution(surface); vec3 specular = fresnelColor * power * angleAttenuation; return vec4(specular, (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x)); } // Frag Shading returns the diffuse amount as W and the specular rgb as xyz -vec4 evalPBRShadingDielectric(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, float fresnel) { +vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) { // Incident angle attenuation - float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + float angleAttenuation = surface.ndotl; // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - float fresnelScalar = fresnelSchlickScalar(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + float fresnelScalar = fresnelSchlickScalar(fresnel, surface); + float power = specularDistribution(surface); vec3 specular = vec3(fresnelScalar) * power * angleAttenuation; return vec4(specular, angleAttenuation * (1.0 - fresnelScalar)); } -vec4 evalPBRShadingMetallic(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, vec3 fresnel) { +vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) { // Incident angle attenuation - float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + float angleAttenuation = surface.ndotl; // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + vec3 fresnelColor = fresnelSchlickColor(fresnel, surface); + float power = specularDistribution(surface); vec3 specular = fresnelColor * power * angleAttenuation; return vec4(specular, 0.f); @@ -193,15 +235,9 @@ vec4 evalPBRShadingMetallic(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, <$declareEvalPBRShading()$> -// Return xyz the specular/reflection component and w the diffuse component -//vec4 evalFragShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) { -// return evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); -//} - void evalFragShading(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float roughness, vec3 albedo) { - vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo) { + vec4 shading = evalPBRShading(metallic, fresnel, surface); diffuse = vec3(shading.w); if (isAlbedoEnabled() > 0.0) { diffuse *= albedo; @@ -215,22 +251,19 @@ void evalFragShading(out vec3 diffuse, out vec3 specular, void evalFragShading(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float roughness, vec3 albedo, + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature) { if (scattering * isScatteringEnabled() > 0.0) { - vec3 brdf = evalSkinBRDF(fragLightDir, fragNormal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); - float NdotL = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); - diffuse = mix(vec3(NdotL), brdf, scattering); + vec3 brdf = evalSkinBRDF(surface.lightDir, surface.normal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); + diffuse = mix(vec3(surface.ndotl), brdf, scattering); // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec2 specularBrdf = skinSpecular(fragNormal, fragLightDir, fragEyeDir, roughness, 1.0); + vec2 specularBrdf = skinSpecular(surface, 1.0); diffuse *= specularBrdf.y; specular = vec3(specularBrdf.x); } else { - vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); + vec4 shading = evalPBRShading(metallic, fresnel, surface); diffuse = vec3(shading.w); specular = shading.xyz; } @@ -239,17 +272,15 @@ void evalFragShading(out vec3 diffuse, out vec3 specular, void evalFragShadingScattering(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float roughness, vec3 albedo + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo ,float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature ) { - vec3 brdf = evalSkinBRDF(fragLightDir, fragNormal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); - float NdotL = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + vec3 brdf = evalSkinBRDF(surface.lightDir, surface.normal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); + float NdotL = surface.ndotl; diffuse = mix(vec3(NdotL), brdf, scattering); // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec2 specularBrdf = skinSpecular(fragNormal, fragLightDir, fragEyeDir, roughness, 1.0); + vec2 specularBrdf = skinSpecular(surface, 1.0); diffuse *= specularBrdf.y; specular = vec3(specularBrdf.x); @@ -257,10 +288,9 @@ void evalFragShadingScattering(out vec3 diffuse, out vec3 specular, } void evalFragShadingGloss(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float gloss, vec3 albedo + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo ) { - vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, gloss); + vec4 shading = evalPBRShading(metallic, fresnel, surface); diffuse = vec3(shading.w); diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled()); specular = shading.xyz; diff --git a/libraries/render-utils/src/SubsurfaceScattering.slh b/libraries/render-utils/src/SubsurfaceScattering.slh index 201ec2291a..233dfd7a0c 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.slh +++ b/libraries/render-utils/src/SubsurfaceScattering.slh @@ -63,38 +63,6 @@ vec3 scatter(float r) { <@endfunc@> -<@func declareSkinSpecularLighting()@> - -uniform sampler2D scatteringSpecularBeckmann; - -float fetchSpecularBeckmann(float ndoth, float roughness) { - return pow( 2.0 * texture(scatteringSpecularBeckmann, vec2(ndoth, roughness)).r, 10.0); -} - -float fresnelReflectance(vec3 H, vec3 V, float Fo) { - float base = 1.0 - dot(V, H); - float exponential = pow(base, 5.0); - return exponential + Fo * (1.0 - exponential); -} - -float skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) { - float result = 0.0; - float ndotl = dot(N, L); - if (ndotl > 0.0) { - vec3 h = L + V; - vec3 H = normalize(h); - float ndoth = dot(N, H); - float PH = fetchSpecularBeckmann(ndoth, roughness); - float F = fresnelReflectance(H, V, 0.028); - float frSpec = max(PH * F / dot(h, h), 0.0); - result = ndotl * intensity * frSpec; - } - - return result; -} - -<@endfunc@> - <@func declareSubsurfaceScatteringIntegrate(NumIntegrationSteps)@> diff --git a/libraries/render-utils/src/local_lights_shading.slf b/libraries/render-utils/src/local_lights_shading.slf index c6310cb079..a935a8cb89 100644 --- a/libraries/render-utils/src/local_lights_shading.slf +++ b/libraries/render-utils/src/local_lights_shading.slf @@ -84,9 +84,8 @@ void main(void) { // Frag to eye vec vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0); vec3 fragEyeDir = normalize(fragEyeVector.xyz); - - // Compute the rougness into gloss2 once: - float fragGloss2 = pow(frag.roughness + 0.001, 4.0); + + SurfaceData surface = initSurfaceData(frag.roughness, frag.normal, fragEyeDir); bool withScattering = (frag.scattering * isScatteringEnabled() > 0.0); int numLightTouching = 0; @@ -119,16 +118,18 @@ void main(void) { float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); + // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); vec3 lightEnergy = radialAttenuation * getLightIrradiance(light); // Eval shading if (withScattering) { - evalFragShadingScattering(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, frag.roughness, frag.albedo + evalFragShadingScattering(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo ,frag.scattering, midNormalCurvature, lowNormalCurvature ); } else { - evalFragShadingGloss(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, fragGloss2, frag.albedo); + evalFragShadingGloss(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo); } diffuse *= lightEnergy * isDiffuseEnabled(); @@ -173,6 +174,8 @@ void main(void) { float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); + // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); float angularAttenuation = lightIrradiance_evalLightSpotAttenuation(light.irradiance, cosSpotAngle); @@ -180,10 +183,10 @@ void main(void) { // Eval shading if (withScattering) { - evalFragShadingScattering(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, frag.roughness, frag.albedo + evalFragShadingScattering(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo ,frag.scattering, midNormalCurvature, lowNormalCurvature ); } else { - evalFragShadingGloss(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, fragGloss2, frag.albedo); + evalFragShadingGloss(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo); } diffuse *= lightEnergy * isDiffuseEnabled(); diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 38f162fdc3..7e64c5ab8b 100644 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -50,13 +50,7 @@ void main(void) { <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; diff --git a/libraries/render-utils/src/model_translucent_fade.slf b/libraries/render-utils/src/model_translucent_fade.slf index 9d5477304c..0d5a452fed 100644 --- a/libraries/render-utils/src/model_translucent_fade.slf +++ b/libraries/render-utils/src/model_translucent_fade.slf @@ -60,13 +60,7 @@ void main(void) { <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; diff --git a/libraries/render-utils/src/overlay3D.slf b/libraries/render-utils/src/overlay3D.slf index 0cb3340845..b58dd5afac 100644 --- a/libraries/render-utils/src/overlay3D.slf +++ b/libraries/render-utils/src/overlay3D.slf @@ -40,12 +40,14 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a vec3 fragEyeDir; <$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$> + SurfaceData surface = initSurfaceData(roughness, normal, fragEyeDir); + vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(ambient); // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse * isDiffuseEnabled() * isDirectionalEnabled(); color += directionalSpecular * isSpecularEnabled() * isDirectionalEnabled(); diff --git a/libraries/render-utils/src/overlay3D_translucent.slf b/libraries/render-utils/src/overlay3D_translucent.slf index 9bdac2d21f..72bd0d740e 100644 --- a/libraries/render-utils/src/overlay3D_translucent.slf +++ b/libraries/render-utils/src/overlay3D_translucent.slf @@ -40,12 +40,14 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a vec3 fragEyeDir; <$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$> + SurfaceData surface = initSurfaceData(roughness, normal, fragEyeDir); + vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(ambient); // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; From cb4d78ce5ce7d21638480973ac0d000accdfaedf Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 21 Dec 2017 18:47:18 +0100 Subject: [PATCH 05/53] Fixed bug in specular ambient --- libraries/render-utils/src/LightAmbient.slh | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index e466702a34..1c13de3221 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -22,11 +22,9 @@ vec4 evalSkyboxLight(vec3 direction, float lod) { <@func declareEvalAmbientSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@> -vec3 fresnelSchlickAmbient(vec3 fresnelColor, SurfaceData surface) { - float gloss = 1.0 - surface.roughness; - float f = pow(1.0 - surface.ldoth, 5.0); +vec3 fresnelSchlickAmbient(vec3 fresnelColor, float ndotd, float gloss) { + float f = pow(1.0 - ndotd, 5.0); return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * f; -// return mix(fresnelColor, vec3(1.0), f); } <@if supportAmbientMap@> @@ -43,7 +41,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { { float levels = getLightAmbientMapNumMips(ambient); float lod = min(((surface.roughness)* levels), levels); - specularLight = evalSkyboxLight(surface.lightDir, lod).xyz; + specularLight = evalSkyboxLight(lightDir, lod).xyz; // We multiply specular by Pi to match specular / diffuse normalization in // LightingModel.slh where diffuse and specular arent divided by Pi (when they should, rigourously // speaking, based on physical equations). The spherical harmonics evaluation, on @@ -57,7 +55,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { <@endif@> <@if supportAmbientSphere@> { - specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), surface.lightDir).xyz; + specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz; } <@endif@> @@ -83,7 +81,7 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie ) { // Fresnel - vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, surface); + vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, surface.ndotv, 1.0-surface.roughness); // Diffuse from ambient diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), surface.normal).xyz; From a6d5e33ecaae18250e05c158d8b3d118a5c71adf Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 22 Dec 2017 10:39:45 +0100 Subject: [PATCH 06/53] Fixed levels between specular, diffuse, ambient specular, ambiend diffuse and background sky --- libraries/render-utils/src/LightAmbient.slh | 16 +++++++--------- libraries/render-utils/src/LightingModel.slh | 16 ++++++++++------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 1c13de3221..8c6dbb85a2 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -22,9 +22,10 @@ vec4 evalSkyboxLight(vec3 direction, float lod) { <@func declareEvalAmbientSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@> -vec3 fresnelSchlickAmbient(vec3 fresnelColor, float ndotd, float gloss) { +vec3 fresnelSchlickAmbient(vec3 fresnelColor, float ndotd, float gloss) { float f = pow(1.0 - ndotd, 5.0); return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * f; +// return fresnelColor + (vec3(1.0) - fresnelColor) * f; } <@if supportAmbientMap@> @@ -40,14 +41,10 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { <@if supportAmbientMap@> { float levels = getLightAmbientMapNumMips(ambient); - float lod = min(((surface.roughness)* levels), levels); + float m = 12.0 / (1.0+5.0*surface.roughness); + float lod = levels - m; + lod = max(lod, 0); specularLight = evalSkyboxLight(lightDir, lod).xyz; - // We multiply specular by Pi to match specular / diffuse normalization in - // LightingModel.slh where diffuse and specular arent divided by Pi (when they should, rigourously - // speaking, based on physical equations). The spherical harmonics evaluation, on - // the other hand, seems to be Pi times stronger than usual. So all in all, everything - // should be at the right level now. - specularLight *= 3.1415926; } <@endif@> <@if supportIfAmbientMapElseAmbientSphere@> @@ -56,6 +53,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { <@if supportAmbientSphere@> { specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz; + specularLight /= 3.1415926; } <@endif@> @@ -99,7 +97,7 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie // Diffuse from ambient diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lowNormalCurvature.xyz).xyz; - + diffuse /= 3.1415926; specular = vec3(0.0); } <@endif@> diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 27b5327b09..8a6c03d423 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -183,10 +183,7 @@ float specularDistribution(SurfaceData surface) { // Add geometric factors G1(n,l) and G1(n,v) float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl); denom *= surface.smithInvG1NdotV * smithInvG1NdotL; - // Don't divide by PI as diffuse isn't either. The real PBR formula normalizes - // by PI on both but if we decide to not do it, it is just as if we - // multiplied all our lights by PI - //denom *= 3.1415926; + // Don't divide by PI as it will be done later float power = surface.roughness4 / denom; return power; } @@ -200,8 +197,11 @@ vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) { vec3 fresnelColor = fresnelSchlickColor(fresnel, surface); float power = specularDistribution(surface); vec3 specular = fresnelColor * power * angleAttenuation; + float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x); - return vec4(specular, (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x)); + specular /= 3.1415926; + diffuse /= 3.1415926; + return vec4(specular, diffuse); } // Frag Shading returns the diffuse amount as W and the specular rgb as xyz @@ -213,8 +213,11 @@ vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) { float fresnelScalar = fresnelSchlickScalar(fresnel, surface); float power = specularDistribution(surface); vec3 specular = vec3(fresnelScalar) * power * angleAttenuation; + float diffuse = angleAttenuation * (1.0 - fresnelScalar); - return vec4(specular, angleAttenuation * (1.0 - fresnelScalar)); + specular /= 3.1415926; + diffuse /= 3.1415926; + return vec4(specular, diffuse); } vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) { @@ -226,6 +229,7 @@ vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) { float power = specularDistribution(surface); vec3 specular = fresnelColor * power * angleAttenuation; + specular /= 3.1415926; return vec4(specular, 0.f); } From 93ba9ad3b19c0e6fa90ff17cd736e4a939152930 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 26 Dec 2017 15:43:23 +0100 Subject: [PATCH 07/53] Adjusted roughness aspect on specular reflection of sky box to more closely match Unity's PBR look especially on high roughness values --- libraries/render-utils/src/LightAmbient.slh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 8c6dbb85a2..409b8b0587 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -41,7 +41,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { <@if supportAmbientMap@> { float levels = getLightAmbientMapNumMips(ambient); - float m = 12.0 / (1.0+5.0*surface.roughness); + float m = 12.0 / (1.0+11.0*surface.roughness); float lod = levels - m; lod = max(lod, 0); specularLight = evalSkyboxLight(lightDir, lod).xyz; From dcfeed9b2cb99713c4d6adb982674d27409a9e43 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 27 Dec 2017 15:52:17 +0100 Subject: [PATCH 08/53] Switched to using getFresnelF0 function in overlay3D model shaders --- libraries/render-utils/src/overlay3D_model.slf | 8 +------- .../render-utils/src/overlay3D_model_translucent.slf | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/libraries/render-utils/src/overlay3D_model.slf b/libraries/render-utils/src/overlay3D_model.slf index bb0d84a513..ea61c2bb75 100644 --- a/libraries/render-utils/src/overlay3D_model.slf +++ b/libraries/render-utils/src/overlay3D_model.slf @@ -46,13 +46,7 @@ void main(void) { albedo *= _color; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; diff --git a/libraries/render-utils/src/overlay3D_model_translucent.slf b/libraries/render-utils/src/overlay3D_model_translucent.slf index b26e70f465..b6ae8eb75e 100644 --- a/libraries/render-utils/src/overlay3D_model_translucent.slf +++ b/libraries/render-utils/src/overlay3D_model_translucent.slf @@ -44,13 +44,7 @@ void main(void) { albedo *= _color; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; From 565875e823893528184f37649deeaac27b28abd6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 15 Dec 2017 15:23:10 -0800 Subject: [PATCH 09/53] WIP first attempt at dual quat skinning --- libraries/animation/src/Rig.cpp | 8 + libraries/animation/src/Rig.h | 1 + .../render-utils/src/CauterizedModel.cpp | 49 +++++++ .../render-utils/src/MeshPartPayload.cpp | 6 +- libraries/render-utils/src/Model.cpp | 25 ++++ libraries/render-utils/src/Skinning.slh | 112 ++++++++++---- libraries/shared/src/DualQuaternion.cpp | 81 ++++++++++ libraries/shared/src/DualQuaternion.h | 58 ++++++++ tests/shared/src/DualQuaternionTests.cpp | 138 ++++++++++++++++++ tests/shared/src/DualQuaternionTests.h | 26 ++++ 10 files changed, 475 insertions(+), 29 deletions(-) create mode 100644 libraries/shared/src/DualQuaternion.cpp create mode 100644 libraries/shared/src/DualQuaternion.h create mode 100644 tests/shared/src/DualQuaternionTests.cpp create mode 100644 tests/shared/src/DualQuaternionTests.h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 44745c5c2d..34a3207f47 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1731,6 +1731,14 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { } } +AnimPose Rig::getJointPose(int jointIndex) const { + if (isIndexValid(jointIndex)) { + return _internalPoseSet._absolutePoses[jointIndex]; + } else { + return AnimPose::identity; + } +} + void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { const AnimPose geometryToRigPose(_geometryToRigTransform); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 1ec4d9527f..7d7b55f4a8 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -159,6 +159,7 @@ public: // rig space glm::mat4 getJointTransform(int jointIndex) const; + AnimPose getJointPose(int jointIndex) const; // Start or stop animations as needed. void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index dbb82ab638..36b06b1035 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -9,6 +9,7 @@ #include "CauterizedModel.h" #include +#include #include "AbstractViewStateInterface.h" #include "MeshPartPayload.h" @@ -109,30 +110,78 @@ void CauterizedModel::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); + + /* AJT: TODO REMOVE auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + */ + // AJT: TODO OPTOMIZE + AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); + AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + + // pack scale rotation and translation into a mat4. + state.clusterMatrices[j][0].x = result.scale().x; + state.clusterMatrices[j][0].y = result.scale().y; + state.clusterMatrices[j][0].z = result.scale().z; + + DualQuaternion dq(result.rot(), result.trans()); + state.clusterMatrices[j][1].x = dq.real().x; + state.clusterMatrices[j][1].y = dq.real().y; + state.clusterMatrices[j][1].z = dq.real().z; + state.clusterMatrices[j][1].w = dq.real().w; + state.clusterMatrices[j][2].x = dq.imag().x; + state.clusterMatrices[j][2].y = dq.imag().y; + state.clusterMatrices[j][2].z = dq.imag().z; + state.clusterMatrices[j][2].w = dq.imag().w; } } // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { + static const glm::mat4 zeroScale( glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; + auto cauterizePose = AnimPose(cauterizeMatrix); for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); + + // AJT: TODO REMOVE: + /* auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointMatrix = cauterizeMatrix; } glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + */ + + auto jointPose = _rig.getJointPose(cluster.jointIndex); + if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { + jointPose = cauterizePose; + } + AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + + // pack scale rotation and translation into a mat4. + state.clusterMatrices[j][0].x = result.scale().x; + state.clusterMatrices[j][0].y = result.scale().y; + state.clusterMatrices[j][0].z = result.scale().z; + + DualQuaternion dq(result.rot(), result.trans()); + state.clusterMatrices[j][1].x = dq.real().x; + state.clusterMatrices[j][1].y = dq.real().y; + state.clusterMatrices[j][1].z = dq.real().z; + state.clusterMatrices[j][1].w = dq.real().w; + state.clusterMatrices[j][2].x = dq.imag().x; + state.clusterMatrices[j][2].y = dq.imag().y; + state.clusterMatrices[j][2].z = dq.imag().z; + state.clusterMatrices[j][2].w = dq.imag().w; } } } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 2a59c7d3c5..eb3866df21 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -12,6 +12,7 @@ #include "MeshPartPayload.h" #include +#include #include "DeferredLightingEffect.h" @@ -330,7 +331,10 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in updateTransform(transform, offsetTransform); Transform renderTransform = transform; if (state.clusterMatrices.size() == 1) { - renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); + // convert form dual quaternion representation to a Transform. + glm::vec3 scale = glm::vec3(state.clusterMatrices[0][0]); + DualQuaternion dq(state.clusterMatrices[0][1], state.clusterMatrices[0][2]); + renderTransform = transform.worldTransform(Transform(dq.getRotation(), scale, dq.getTranslation())); } updateTransformForSkinnedMesh(renderTransform, transform); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b91f4dd405..6ff86516fc 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "AbstractViewStateInterface.h" #include "MeshPartPayload.h" @@ -1176,8 +1177,32 @@ void Model::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); + + // AJT: TODO REMOVE + /* auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + */ + AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); + + // AJT: TODO: store inverseBindMatrix as an animpose + // AJT: TODO: optimize AnimPose::operator* + AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + + // pack scale rotation and translation into a mat4. + state.clusterMatrices[j][0].x = result.scale().x; + state.clusterMatrices[j][0].y = result.scale().y; + state.clusterMatrices[j][0].z = result.scale().z; + + DualQuaternion dq(result.rot(), result.trans()); + state.clusterMatrices[j][1].x = dq.real().x; + state.clusterMatrices[j][1].y = dq.real().y; + state.clusterMatrices[j][1].z = dq.real().z; + state.clusterMatrices[j][1].w = dq.real().w; + state.clusterMatrices[j][2].x = dq.imag().x; + state.clusterMatrices[j][2].y = dq.imag().y; + state.clusterMatrices[j][2].z = dq.imag().z; + state.clusterMatrices[j][2].w = dq.imag().w; } } diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 2d1f010029..10dc4042b2 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -18,52 +18,108 @@ layout(std140) uniform skinClusterBuffer { mat4 clusterMatrices[MAX_CLUSTERS]; }; -void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); +vec4 quatConj(vec4 v) { + return vec4(-v.x, -v.y, -v.z, v.w); +} +vec4 quatMul(vec4 q1, vec4 q2) { + return vec4( q1.x * q2.w + q1.y * q2.z - q1.z * q2.y + q1.w * q2.x, + -q1.x * q2.z + q1.y * q2.w + q1.z * q2.x + q1.w * q2.y, + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w + q1.w * q2.z, + -q1.x * q2.x - q1.y * q2.y - q1.z * q2.z + q1.w * q2.w); +} + +vec3 rotateByQuat(vec4 q, vec3 p) { + return vec3(quatMul(quatMul(q, vec4(p.x, p.y, p.z, 0.0)), quatConj(q))); +} + +void dqMul(vec4 lhsReal, vec4 lhsImag, vec4 rhsReal, vec4 rhsImag, out vec4 realOut, out vec4 imagOut) { + realOut = quatMul(lhsReal, rhsReal); + imagOut = quatMul(lhsReal, rhsImag) + quatMul(lhsImag, rhsReal); +} + +void blendClusters(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scaleOut, out vec4 rotOut, out vec3 posOut) { + vec3 scale = vec3(0.0, 0.0, 0.0); + vec4 dqReal = vec4(0.0, 0.0, 0.0, 0.0); + vec4 dqImag = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; + + vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 dqr = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec4 dqi = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + + scale += s * clusterWeight; + dqReal += dqr * clusterWeight; + dqImag += dqi * clusterWeight; } - skinnedPosition = newPosition; + scaleOut = scale; + + float dqLen = length(dqReal); + dqReal *= 1.0 / dqLen; + dqImag *= 1.0 / dqLen; + + rotOut = dqReal; + + vec4 invReal = quatConj(dqReal); + posOut.xyz = 2.0 * quatMul(dqImag, invReal).xyz; +} + +void blendClusters_rigid(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scaleOut, out vec4 rotOut, out vec3 posOut) { + float maxWeight = 0.0; + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 dqr = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec4 dqi = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + + if (clusterWeight > maxWeight) { + maxWeight = clusterWeight; + scaleOut = s; + rotOut = dqr; + vec4 invReal = quatConj(dqr); + posOut = 2.0 * quatMul(dqi, invReal).xyz; + } + } +} + +void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + + vec3 scale, pos; + vec4 rot; + blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); + + skinnedPosition.xyz = rotateByQuat(rot, (vec3(inPosition) * scale)) + pos; + skinnedPosition.w = 1; } void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, out vec4 skinnedPosition, out vec3 skinnedNormal) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - } + vec3 scale, pos; + vec4 rot; + blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); - skinnedPosition = newPosition; - skinnedNormal = newNormal.xyz; + skinnedNormal.xyz = rotateByQuat(rot, inNormal * scale); } void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newTangent = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - newTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; - } + vec3 scale, pos; + vec4 rot; + blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); - skinnedPosition = newPosition; - skinnedNormal = newNormal.xyz; - skinnedTangent = newTangent.xyz; + skinnedPosition.xyz = rotateByQuat(rot, (vec3(inPosition) * scale)) + pos; + skinnedPosition.w = 1; + + skinnedNormal = rotateByQuat(rot, inNormal * scale); + skinnedTangent = rotateByQuat(rot, inTangent * scale); } -<@endif@> \ No newline at end of file +<@endif@> diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp new file mode 100644 index 0000000000..3b5aaf6d6d --- /dev/null +++ b/libraries/shared/src/DualQuaternion.cpp @@ -0,0 +1,81 @@ +// +// DualQuaternion.cpp +// +// Created by Anthony J. Thibault on Dec 13th 2017. +// Copyright (c) 2017 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "DualQuaternion.h" +#include "GLMHelpers.h" + +// delegating constructor +DualQuaternion::DualQuaternion(const glm::mat4& m) : DualQuaternion(glmExtractRotation(m), extractTranslation(m)) { +} + +DualQuaternion::DualQuaternion(const glm::quat& real, const glm::quat& imag) : _real(real), _imag(imag) { +} + +DualQuaternion::DualQuaternion(const glm::vec4& real, const glm::vec4& imag) : + _real(real.w, real.x, real.y, real.z), + _imag(imag.w, imag.x, imag.y, imag.z) { +} + +DualQuaternion::DualQuaternion(const glm::quat& rotation, const glm::vec3& translation) { + _real = rotation; + _imag = glm::quat(0, 0.5f * translation.x, 0.5f * translation.y, 0.5f * translation.z) * rotation; +} + +DualQuaternion DualQuaternion::operator*(const DualQuaternion& rhs) const { + return DualQuaternion(_real * rhs._real, _real * rhs._imag + _imag * rhs._real); +} + +DualQuaternion DualQuaternion::operator*(float scalar) const { + return DualQuaternion(_real * scalar, _imag * scalar); +} + +DualQuaternion DualQuaternion::operator+(const DualQuaternion& rhs) const { + return DualQuaternion(_real + rhs._real, _imag + rhs._imag); +} + +glm::vec3 DualQuaternion::xformPoint(const glm::vec3& rhs) const { + DualQuaternion v(glm::quat(), glm::quat(0.0f, rhs.x, rhs.y, rhs.z)); + DualQuaternion dualConj(glm::conjugate(_real), -glm::conjugate(_imag)); + DualQuaternion result = *this * v * dualConj; + return vec3(result._imag.x, result._imag.y, result._imag.z); +} + +glm::quat DualQuaternion::getRotation() const { + return _real; +} + +glm::vec3 DualQuaternion::getTranslation() const { + glm::quat result = 2.0f * (_imag * glm::inverse(_real)); + return glm::vec3(result.x, result.y, result.z); +} + + +glm::vec3 DualQuaternion::xformVector(const glm::vec3& rhs) const { + return _real * rhs; +} + +DualQuaternion DualQuaternion::inverse() const { + glm::quat invReal = glm::inverse(_real); + return DualQuaternion(invReal, - invReal * _imag * invReal); +} + +DualQuaternion DualQuaternion::conjugate() const { + return DualQuaternion(glm::conjugate(_real), glm::conjugate(_imag)); +} + +float DualQuaternion::length() const { + DualQuaternion result = *this * conjugate(); + return sqrtf(result._real.w); +} + +DualQuaternion DualQuaternion::normalize() const { + float invLen = 1.0f / length(); + return *this * invLen; +} diff --git a/libraries/shared/src/DualQuaternion.h b/libraries/shared/src/DualQuaternion.h new file mode 100644 index 0000000000..ed8cdf54ff --- /dev/null +++ b/libraries/shared/src/DualQuaternion.h @@ -0,0 +1,58 @@ +// +// DualQuaternion.h +// +// Created by Anthony J. Thibault on Dec 13th 2017. +// Copyright (c) 2017 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#ifndef hifi_DualQuaternion +#define hifi_DualQuaternion + +#include +#include +#include +#include + +class DualQuaternion { +public: + explicit DualQuaternion(const glm::mat4& m); + DualQuaternion(const glm::quat& real, const glm::quat& imag); + DualQuaternion(const glm::quat& rotation, const glm::vec3& translation); + DualQuaternion(const glm::vec4& real, const glm::vec4& imag); + DualQuaternion operator*(const DualQuaternion& rhs) const; + DualQuaternion operator*(float scalar) const; + DualQuaternion operator+(const DualQuaternion& rhs) const; + + const glm::quat& real() const { return _real; } + glm::quat& real() { return _real; } + + const glm::quat& imag() const { return _imag; } + glm::quat& imag() { return _imag; } + + glm::quat getRotation() const; + glm::vec3 getTranslation() const; + + glm::vec3 xformPoint(const glm::vec3& rhs) const; + glm::vec3 xformVector(const glm::vec3& rhs) const; + + DualQuaternion inverse() const; + DualQuaternion conjugate() const; + float length() const; + DualQuaternion normalize() const; + +protected: + friend QDebug operator<<(QDebug debug, const DualQuaternion& pose); + glm::quat _real; + glm::quat _imag; +}; + +inline QDebug operator<<(QDebug debug, const DualQuaternion& dq) { + debug << "AnimPose, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), imag = (" << dq._imag.x << dq._imag.y << dq._imag.z << dq._imag.w << ")"; + return debug; +} + +#endif diff --git a/tests/shared/src/DualQuaternionTests.cpp b/tests/shared/src/DualQuaternionTests.cpp new file mode 100644 index 0000000000..8187ed78bd --- /dev/null +++ b/tests/shared/src/DualQuaternionTests.cpp @@ -0,0 +1,138 @@ +// +// DualQuaternionTests.cpp +// tests/shared/src +// +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "DualQuaternionTests.h" + +#include +#include +#include +#include + +#include <../GLMTestUtils.h> +#include <../QTestExtensions.h> + +QTEST_MAIN(DualQuaternionTests) + +static void quatComp(const glm::quat& q1, const glm::quat& q2) { + QCOMPARE_WITH_ABS_ERROR(q1.x, q2.x, EPSILON); + QCOMPARE_WITH_ABS_ERROR(q1.y, q2.y, EPSILON); + QCOMPARE_WITH_ABS_ERROR(q1.z, q2.z, EPSILON); + QCOMPARE_WITH_ABS_ERROR(q1.w, q2.w, EPSILON); +} + +void DualQuaternionTests::ctor() { + glm::quat real = angleAxis(PI / 2.0f, Vectors::UNIT_Y); + glm::quat imag(0.0f, 1.0f, 2.0f, 3.0f); + + DualQuaternion dq(real, imag); + quatComp(real, dq.real()); + quatComp(imag, dq.imag()); + + glm::quat rotation = angleAxis(PI / 3.0f, Vectors::UNIT_X); + glm::vec3 translation(1.0, 2.0f, 3.0f); + dq = DualQuaternion(rotation, translation); + quatComp(rotation, dq.getRotation()); + QCOMPARE_WITH_ABS_ERROR(translation, dq.getTranslation(), EPSILON); + + rotation = angleAxis(-2.0f * PI / 7.0f, Vectors::UNIT_Z); + translation = glm::vec3(-1.0, 12.0f, 2.0f); + glm::mat4 m = createMatFromQuatAndPos(rotation, translation); + dq = DualQuaternion(m); + quatComp(rotation, dq.getRotation()); + QCOMPARE_WITH_ABS_ERROR(translation, dq.getTranslation(), EPSILON); +} + +void DualQuaternionTests::mult() { + + glm::quat rotation = angleAxis(PI / 3.0f, Vectors::UNIT_X); + glm::vec3 translation(1.0, 2.0f, 3.0f); + glm::mat4 m1 = createMatFromQuatAndPos(rotation, translation); + DualQuaternion dq1(m1); + + rotation = angleAxis(-2.0f * PI / 7.0f, Vectors::UNIT_Z); + translation = glm::vec3(-1.0, 12.0f, 2.0f); + glm::mat4 m2 = createMatFromQuatAndPos(rotation, translation); + DualQuaternion dq2(m2); + + DualQuaternion dq3 = dq1 * dq2; + glm::mat4 m3 = m1 * m2; + + rotation = glmExtractRotation(m3); + translation = extractTranslation(m3); + + quatComp(rotation, dq3.getRotation()); + QCOMPARE_WITH_ABS_ERROR(translation, dq3.getTranslation(), EPSILON); +} + +void DualQuaternionTests::xform() { + + glm::quat rotation = angleAxis(PI / 3.0f, Vectors::UNIT_X); + glm::vec3 translation(1.0, 2.0f, 3.0f); + glm::mat4 m1 = createMatFromQuatAndPos(rotation, translation); + DualQuaternion dq1(m1); + + rotation = angleAxis(-2.0f * PI / 7.0f, Vectors::UNIT_Z); + translation = glm::vec3(-1.0, 12.0f, 2.0f); + glm::mat4 m2 = createMatFromQuatAndPos(rotation, translation); + DualQuaternion dq2(m2); + + DualQuaternion dq3 = dq1 * dq2; + glm::mat4 m3 = m1 * m2; + + glm::vec3 p(1.0f, 2.0f, 3.0f); + + glm::vec3 p1 = transformPoint(m3, p); + glm::vec3 p2 = dq3.xformPoint(p); + + QCOMPARE_WITH_ABS_ERROR(p1, p2, 0.001f); + + p1 = transformVectorFast(m3, p); + p2 = dq3.xformVector(p); + + QCOMPARE_WITH_ABS_ERROR(p1, p2, 0.001f); +} + +void DualQuaternionTests::trans() { + glm::vec3 t1 = glm::vec3(); + DualQuaternion dq1(Quaternions::IDENTITY, t1); + glm::vec3 t2 = glm::vec3(1.0f, 2.0f, 3.0f); + DualQuaternion dq2(angleAxis(PI / 3.0f, Vectors::UNIT_X), t2); + glm::vec3 t3 = glm::vec3(3.0f, 2.0f, 1.0f); + DualQuaternion dq3(angleAxis(PI / 5.0f, Vectors::UNIT_Y), t3); + + QCOMPARE_WITH_ABS_ERROR(t1, dq1.getTranslation(), 0.001f); + QCOMPARE_WITH_ABS_ERROR(t2, dq2.getTranslation(), 0.001f); + QCOMPARE_WITH_ABS_ERROR(t3, dq3.getTranslation(), 0.001f); +} + +// Dual Quaternion Linear Blending test +void DualQuaternionTests::dlb() { + DualQuaternion dq1(Quaternions::IDENTITY, glm::vec3()); + DualQuaternion dq2(angleAxis(PI / 2.0f, Vectors::UNIT_X), glm::vec3(0.0f, 1.0f, 0.0f)); + + qDebug() << "dq1 =" << dq1; + qDebug() << "dq1.length = " << dq1.length(); + qDebug() << "dq2 =" << dq2; + qDebug() << "dq2.length = " << dq2.length(); + + // linear blend between dq1 and dq2 + DualQuaternion dq3 = dq1 * 0.5f + dq2 * 0.5f; + DualQuaternion dq4 = dq3.normalize(); + + qDebug() << "dq3 =" << dq3; + qDebug() << "dq3.length = " << dq3.length(); + qDebug() << "dq3.trans =" << dq3.getTranslation(); + + qDebug() << "dq4 =" << dq4; + qDebug() << "dq4.length = " << dq4.length(); + qDebug() << "dq4.trans =" << dq4.getTranslation(); +} diff --git a/tests/shared/src/DualQuaternionTests.h b/tests/shared/src/DualQuaternionTests.h new file mode 100644 index 0000000000..988973b689 --- /dev/null +++ b/tests/shared/src/DualQuaternionTests.h @@ -0,0 +1,26 @@ +// +// DualQuaternionTests.h +// tests/shared/src +// +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DualQuaternionTests_h +#define hifi_DualQuaternionTests_h + +#include + +class DualQuaternionTests : public QObject { + Q_OBJECT +private slots: + void ctor(); + void mult(); + void xform(); + void trans(); + void dlb(); +}; + +#endif // hifi_DualQuaternionTests_h From 532c8a23f98f8704ecc32f8769c47d508acfe036 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 15 Dec 2017 15:50:22 -0800 Subject: [PATCH 10/53] tests --- tests/shared/src/DualQuaternionTests.cpp | 25 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/tests/shared/src/DualQuaternionTests.cpp b/tests/shared/src/DualQuaternionTests.cpp index 8187ed78bd..1c34bfbee7 100644 --- a/tests/shared/src/DualQuaternionTests.cpp +++ b/tests/shared/src/DualQuaternionTests.cpp @@ -118,21 +118,30 @@ void DualQuaternionTests::trans() { void DualQuaternionTests::dlb() { DualQuaternion dq1(Quaternions::IDENTITY, glm::vec3()); DualQuaternion dq2(angleAxis(PI / 2.0f, Vectors::UNIT_X), glm::vec3(0.0f, 1.0f, 0.0f)); + DualQuaternion dq2Alt(-angleAxis(PI / 2.0f, Vectors::UNIT_X), glm::vec3(0.0f, 1.0f, 0.0f)); qDebug() << "dq1 =" << dq1; - qDebug() << "dq1.length = " << dq1.length(); qDebug() << "dq2 =" << dq2; - qDebug() << "dq2.length = " << dq2.length(); // linear blend between dq1 and dq2 DualQuaternion dq3 = dq1 * 0.5f + dq2 * 0.5f; - DualQuaternion dq4 = dq3.normalize(); + + // alternate linear blend between dq1 and dq2 + DualQuaternion dq4 = dq1 * 0.5 + dq2Alt * 0.5f; qDebug() << "dq3 =" << dq3; - qDebug() << "dq3.length = " << dq3.length(); - qDebug() << "dq3.trans =" << dq3.getTranslation(); - qDebug() << "dq4 =" << dq4; - qDebug() << "dq4.length = " << dq4.length(); - qDebug() << "dq4.trans =" << dq4.getTranslation(); + + glm::vec3 p1(0.0f, 0.5f, -0.5f); + glm::vec3 p2(0.0f, 0.5f, 0.5f); + + glm::vec3 p3 = dq3.xformPoint(p1); + glm::vec3 p4 = dq3.xformPoint(p2); + glm::vec3 p5 = dq4.xformPoint(p1); + glm::vec3 p6 = dq4.xformPoint(p2); + + qDebug() << "p3 =" << p3; + qDebug() << "p4 =" << p4; + qDebug() << "p5 =" << p5; + qDebug() << "p6 =" << p6; } From 23affb570b1be336a2404ea08fc2df3a78cdbf7c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 18 Dec 2017 15:26:02 -0800 Subject: [PATCH 11/53] WIP checkpoint, root transforms for shapes and non-animated fbx files are working. --- .../render-utils/src/CauterizedModel.cpp | 72 ++++++++++++------ .../render-utils/src/MeshPartPayload.cpp | 26 ++++++- libraries/render-utils/src/Model.cpp | 39 ++++++---- libraries/render-utils/src/Model.h | 11 +++ libraries/render-utils/src/Skinning.slh | 76 ++++++++++++++++++- .../render-utils/src/SoftAttachmentModel.cpp | 29 +++++++ 6 files changed, 207 insertions(+), 46 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 36b06b1035..cba716312a 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -16,7 +16,6 @@ #include "CauterizedMeshPartPayload.h" #include "RenderUtilsLogging.h" - CauterizedModel::CauterizedModel(QObject* parent) : Model(parent) { } @@ -111,11 +110,13 @@ void CauterizedModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - /* AJT: TODO REMOVE + /* AJT: TODO REMOVE */ +#ifdef SKIN_MATRIX + SKIN_ASSERT(false); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); - */ - // AJT: TODO OPTOMIZE +#endif +#ifdef SKIN_COMP AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); @@ -124,15 +125,15 @@ void CauterizedModel::updateClusterMatrices() { state.clusterMatrices[j][0].y = result.scale().y; state.clusterMatrices[j][0].z = result.scale().z; - DualQuaternion dq(result.rot(), result.trans()); - state.clusterMatrices[j][1].x = dq.real().x; - state.clusterMatrices[j][1].y = dq.real().y; - state.clusterMatrices[j][1].z = dq.real().z; - state.clusterMatrices[j][1].w = dq.real().w; - state.clusterMatrices[j][2].x = dq.imag().x; - state.clusterMatrices[j][2].y = dq.imag().y; - state.clusterMatrices[j][2].z = dq.imag().z; - state.clusterMatrices[j][2].w = dq.imag().w; + state.clusterMatrices[j][1].x = result.rot().x; + state.clusterMatrices[j][1].y = result.rot().y; + state.clusterMatrices[j][1].z = result.rot().z; + state.clusterMatrices[j][1].w = result.rot().w; + + state.clusterMatrices[j][2].x = result.trans().x; + state.clusterMatrices[j][2].y = result.trans().y; + state.clusterMatrices[j][2].z = result.trans().z; +#endif } } @@ -153,15 +154,16 @@ void CauterizedModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); +#ifdef SKIN_MATRIX + SKIN_ASSERT(false); // AJT: TODO REMOVE: - /* auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointMatrix = cauterizeMatrix; } glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); - */ - +#endif +#ifdef SKIN_COMP auto jointPose = _rig.getJointPose(cluster.jointIndex); if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointPose = cauterizePose; @@ -173,15 +175,15 @@ void CauterizedModel::updateClusterMatrices() { state.clusterMatrices[j][0].y = result.scale().y; state.clusterMatrices[j][0].z = result.scale().z; - DualQuaternion dq(result.rot(), result.trans()); - state.clusterMatrices[j][1].x = dq.real().x; - state.clusterMatrices[j][1].y = dq.real().y; - state.clusterMatrices[j][1].z = dq.real().z; - state.clusterMatrices[j][1].w = dq.real().w; - state.clusterMatrices[j][2].x = dq.imag().x; - state.clusterMatrices[j][2].y = dq.imag().y; - state.clusterMatrices[j][2].z = dq.imag().z; - state.clusterMatrices[j][2].w = dq.imag().w; + state.clusterMatrices[j][1].x = result.rot().x; + state.clusterMatrices[j][1].y = result.rot().y; + state.clusterMatrices[j][1].z = result.rot().z; + state.clusterMatrices[j][1].w = result.rot().w; + + state.clusterMatrices[j][2].x = result.trans().x; + state.clusterMatrices[j][2].y = result.trans().y; + state.clusterMatrices[j][2].z = result.trans().z; +#endif } } } @@ -249,13 +251,33 @@ void CauterizedModel::updateRenderItems() { Transform renderTransform = modelTransform; if (clusterMatrices.size() == 1) { +#ifdef SKIN_MATRIX + SKIN_ASSERT(false); renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0])); +#endif +#ifdef SKIN_COMP + glm::vec3 scale(clusterMatrices[0][0]); + glm::quat rot(clusterMatrices[0][1].w, clusterMatrices[0][1].x, clusterMatrices[0][1].y, clusterMatrices[0][1].z); + glm::vec3 trans(clusterMatrices[0][2]); + glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); + renderTransform = modelTransform.worldTransform(Transform(m)); +#endif } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); renderTransform = modelTransform; if (clusterMatricesCauterized.size() == 1) { +#ifdef SKIN_MATRIX + SKIN_ASSERT(false); renderTransform = modelTransform.worldTransform(Transform(clusterMatricesCauterized[0])); +#endif +#ifdef SKIN_COMP + glm::vec3 scale(clusterMatricesCauterized[0][0]); + glm::quat rot(clusterMatricesCauterized[0][1].w, clusterMatricesCauterized[0][1].x, clusterMatricesCauterized[0][1].y, clusterMatricesCauterized[0][1].z); + glm::vec3 trans(clusterMatricesCauterized[0][2]); + glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); + renderTransform = modelTransform.worldTransform(Transform(m)); +#endif } data.updateTransformForCauterizedMesh(renderTransform); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index eb3866df21..fcf6a61fb5 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -331,10 +331,17 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in updateTransform(transform, offsetTransform); Transform renderTransform = transform; if (state.clusterMatrices.size() == 1) { - // convert form dual quaternion representation to a Transform. - glm::vec3 scale = glm::vec3(state.clusterMatrices[0][0]); - DualQuaternion dq(state.clusterMatrices[0][1], state.clusterMatrices[0][2]); - renderTransform = transform.worldTransform(Transform(dq.getRotation(), scale, dq.getTranslation())); +#ifdef SKIN_MATRIX + SKIN_ASSERT(false); + renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); +#endif +#ifdef SKIN_COMP + glm::vec3 scale(state.clusterMatrices[0][0]); + glm::quat rot(state.clusterMatrices[0][1].w, state.clusterMatrices[0][1].x, state.clusterMatrices[0][1].y, state.clusterMatrices[0][1].z); + glm::vec3 trans(state.clusterMatrices[0][2]); + glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); + renderTransform = transform.worldTransform(Transform(m)); +#endif } updateTransformForSkinnedMesh(renderTransform, transform); @@ -549,7 +556,18 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector maxWeight) { + maxWeight = clusterWeight; + scaleOut = s; + rotOut = r; + posOut = t; + } + } +} + void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { vec3 scale, pos; @@ -121,5 +143,55 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v skinnedTangent = rotateByQuat(rot, inTangent * scale); } +// ORIGINAL +/* +void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + } + + skinnedPosition = newPosition; +} + +void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, + out vec4 skinnedPosition, out vec3 skinnedNormal) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; + } + + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; +} + +void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, + out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newTangent = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; + newTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; + } + + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; + skinnedTangent = newTangent.xyz; +} +*/ + <@endif@> diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 63991f9422..7d49a8cda1 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -50,6 +50,7 @@ void SoftAttachmentModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); +#ifdef SKIN_MATRIX // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); glm::mat4 jointMatrix; @@ -58,7 +59,35 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } + SKIN_ASSERT(false); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); +#endif +#ifdef SKIN_COMP + // TODO: cache these look-ups as an optimization + int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); + AnimPose jointPose; + if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { + jointPose = _rigOverride.getJointPose(jointIndexOverride); + } else { + jointPose = _rig.getJointPose(cluster.jointIndex); + } + AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + + // pack scale rotation and translation into a mat4. + state.clusterMatrices[j][0].x = result.scale().x; + state.clusterMatrices[j][0].y = result.scale().y; + state.clusterMatrices[j][0].z = result.scale().z; + + state.clusterMatrices[j][1].x = result.rot().x; + state.clusterMatrices[j][1].y = result.rot().y; + state.clusterMatrices[j][1].z = result.rot().z; + state.clusterMatrices[j][1].w = result.rot().w; + + state.clusterMatrices[j][2].x = result.trans().x; + state.clusterMatrices[j][2].y = result.trans().y; + state.clusterMatrices[j][2].z = result.trans().z; +#endif + } } From 515d13a4c15297cf1f75820aa59aba72cf3c0e40 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 18 Dec 2017 17:51:14 -0800 Subject: [PATCH 12/53] WIP checkpoint --- interface/src/avatar/MyAvatar.cpp | 7 ++- libraries/animation/src/AnimPose.cpp | 23 +++++++++ libraries/animation/src/AnimPose.h | 2 + .../render-utils/src/CauterizedModel.cpp | 50 ++++++++++++++++++- 4 files changed, 79 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 02a1959a95..0fd7f7f012 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1820,7 +1820,8 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { if (_skeletonModel->isLoaded() && !_skeletonModel->getRig().getAnimNode()) { initHeadBones(); - _skeletonModel->setCauterizeBoneSet(_headBoneSet); + // AJT HACK DISABLE CAUTERIZE + //_skeletonModel->setCauterizeBoneSet(_headBoneSet); _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); initAnimGraph(); _isAnimatingScale = true; @@ -1912,7 +1913,9 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { // toggle using the cauterizedBones depending on where the camera is and the rendering pass type. const bool shouldDrawHead = shouldRenderHead(renderArgs); if (shouldDrawHead != _prevShouldDrawHead) { - _skeletonModel->setEnableCauterization(!shouldDrawHead); + // AJT: DISABLE CAUTER + // _skeletonModel->setEnableCauterization(!shouldDrawHead); + _skeletonModel->setEnableCauterization(false); for (int i = 0; i < _attachmentData.size(); i++) { if (_attachmentData[i].jointName.compare("Head", Qt::CaseInsensitive) == 0 || diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index 470bbab8b6..29c4c46d6e 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -69,6 +69,27 @@ AnimPose AnimPose::mirror() const { return AnimPose(_scale, glm::quat(_rot.w, _rot.x, -_rot.y, -_rot.z), glm::vec3(-_trans.x, _trans.y, _trans.z)); } +bool AnimPose::fuzzyEqual(const AnimPose& rhs) const { + const float SCALE_EPSILON = 0.00001f; + const float ROT_EPSILON = 0.00001f; + const float TRANS_EPSILON = 0.001f; + if ((fabsf(rhs._scale.x - _scale.x) < SCALE_EPSILON) && + (fabsf(rhs._scale.y - _scale.y) < SCALE_EPSILON) && + (fabsf(rhs._scale.z - _scale.z) < SCALE_EPSILON)) { + if ((fabsf(rhs._rot.x - _rot.x) < ROT_EPSILON) && + (fabsf(rhs._rot.y - _rot.y) < ROT_EPSILON) && + (fabsf(rhs._rot.z - _rot.z) < ROT_EPSILON) && + (fabsf(rhs._rot.w - _rot.w) < ROT_EPSILON)) { + if ((fabsf(rhs._trans.x - _trans.x) < TRANS_EPSILON) && + (fabsf(rhs._trans.y - _trans.y) < TRANS_EPSILON) && + (fabsf(rhs._trans.z - _trans.z) < TRANS_EPSILON)) { + return true; + } + } + } + return false; +} + AnimPose::operator glm::mat4() const { glm::vec3 xAxis = _rot * glm::vec3(_scale.x, 0.0f, 0.0f); glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale.y, 0.0f); @@ -76,3 +97,5 @@ AnimPose::operator glm::mat4() const { return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f)); } + + diff --git a/libraries/animation/src/AnimPose.h b/libraries/animation/src/AnimPose.h index 2df3d1f2e4..0f29bac7ce 100644 --- a/libraries/animation/src/AnimPose.h +++ b/libraries/animation/src/AnimPose.h @@ -46,6 +46,8 @@ public: const glm::vec3& trans() const { return _trans; } glm::vec3& trans() { return _trans; } + bool fuzzyEqual(const AnimPose& rhs) const; + private: friend QDebug operator<<(QDebug debug, const AnimPose& pose); glm::vec3 _scale { 1.0f }; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index cba716312a..282aedf180 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -104,6 +104,12 @@ void CauterizedModel::updateClusterMatrices() { _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); + bool debug = false; + + if (debug) { + qDebug() << "AJT: CauterizedModel::updateClusterMatrices(), url =" << _url; + } + for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -117,8 +123,14 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); #endif #ifdef SKIN_COMP + + if (debug) { + qDebug() << "AJT: _meshState[" << i << "], cluster[" << j << "]"; + } + AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + result.rot() = glm::normalize(result.rot()); // pack scale rotation and translation into a mat4. state.clusterMatrices[j][0].x = result.scale().x; @@ -133,6 +145,18 @@ void CauterizedModel::updateClusterMatrices() { state.clusterMatrices[j][2].x = result.trans().x; state.clusterMatrices[j][2].y = result.trans().y; state.clusterMatrices[j][2].z = result.trans().z; + + // AJT REMOVE + if (debug) { + glm::mat4 jointMatrix = _rig.getJointTransform(cluster.jointIndex); + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + qDebug() << "AJT: m =" << m; + qDebug() << "AJT: (AnimPose)m =" << AnimPose(m); + qDebug() << "AJT: result =" << result; + qDebug() << "AJT: (mat4)result =" << (glm::mat4)result; + SKIN_ASSERT(result.fuzzyEqual(AnimPose(m))); + } #endif } } @@ -146,7 +170,6 @@ void CauterizedModel::updateClusterMatrices() { glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; - auto cauterizePose = AnimPose(cauterizeMatrix); for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; @@ -164,11 +187,19 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); #endif #ifdef SKIN_COMP + + if (debug) { + qDebug() << "AJT: CAUTERIZED _meshState[" << i << "], cluster[" << j << "]"; + } + auto jointPose = _rig.getJointPose(cluster.jointIndex); + /* if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointPose = cauterizePose; } + */ AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + result.rot() = glm::normalize(result.rot()); // pack scale rotation and translation into a mat4. state.clusterMatrices[j][0].x = result.scale().x; @@ -183,6 +214,23 @@ void CauterizedModel::updateClusterMatrices() { state.clusterMatrices[j][2].x = result.trans().x; state.clusterMatrices[j][2].y = result.trans().y; state.clusterMatrices[j][2].z = result.trans().z; + + // AJT REMOVE + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); + /* + if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { + jointMatrix = cauterizeMatrix; + } + */ + if (debug) { + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + qDebug() << "AJT: m =" << m; + qDebug() << "AJT: (AnimPose)m =" << AnimPose(m); + qDebug() << "AJT: result =" << result; + qDebug() << "AJT: (mat4)result =" << (glm::mat4)result; + SKIN_ASSERT(result.fuzzyEqual(AnimPose(m))); + } #endif } } From 18113d824c3f8c8340c6affc67c8bbf997728452 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 19 Dec 2017 15:57:06 -0800 Subject: [PATCH 13/53] Created Model::TransformComponents class --- .../src/CauterizedMeshPartPayload.cpp | 30 +++- .../src/CauterizedMeshPartPayload.h | 7 +- .../render-utils/src/CauterizedModel.cpp | 139 ++++------------- .../render-utils/src/MeshPartPayload.cpp | 83 ++++++----- libraries/render-utils/src/MeshPartPayload.h | 10 +- libraries/render-utils/src/Model.cpp | 53 ++----- libraries/render-utils/src/Model.h | 45 +++++- libraries/render-utils/src/Skinning.slh | 140 ++++++++---------- .../render-utils/src/SoftAttachmentModel.cpp | 34 +---- 9 files changed, 237 insertions(+), 304 deletions(-) diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index 396cd13508..c64c98b271 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -20,19 +20,35 @@ using namespace render; CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} -void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterMatrices, const std::vector& cauterizedClusterMatrices) { - ModelMeshPartPayload::updateClusterBuffer(clusterMatrices); +#ifdef SKIN_COMP +void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { + ModelMeshPartPayload::updateClusterBuffer(clusterTransforms); - if (cauterizedClusterMatrices.size() > 1) { + if (cauterizedClusterTransforms.size() > 1) { if (!_cauterizedClusterBuffer) { - _cauterizedClusterBuffer = std::make_shared(cauterizedClusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) cauterizedClusterMatrices.data()); + _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(Model::TransformComponents), + (const gpu::Byte*) cauterizedClusterTransforms.data()); } else { - _cauterizedClusterBuffer->setSubData(0, cauterizedClusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) cauterizedClusterMatrices.data()); + _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(Model::TransformComponents), + (const gpu::Byte*) cauterizedClusterTransforms.data()); } } } +#else +void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { + ModelMeshPartPayload::updateClusterBuffer(clusterTransforms); + + if (cauterizedClusterTransforms.size() > 1) { + if (!_cauterizedClusterBuffer) { + _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(glm::mat4), + (const gpu::Byte*) cauterizedClusterTransforms.data()); + } else { + _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(glm::mat4), + (const gpu::Byte*) cauterizedClusterTransforms.data()); + } + } +} +#endif void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform& renderTransform) { _cauterizedTransform = renderTransform; diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 44eddc6e31..1ee77c300f 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -14,8 +14,11 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); - - void updateClusterBuffer(const std::vector& clusterMatrices, const std::vector& cauterizedClusterMatrices); +#ifdef SKIN_COMP + void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); +#else + void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); +#endif void updateTransformForCauterizedMesh(const Transform& renderTransform); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 282aedf180..606e1f0351 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -35,7 +35,7 @@ bool CauterizedModel::updateGeometry() { const FBXGeometry& fbxGeometry = getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { Model::MeshState state; - state.clusterMatrices.resize(mesh.clusters.size()); + state.clusterTransforms.resize(mesh.clusters.size()); _cauterizeMeshStates.append(state); } } @@ -116,47 +116,14 @@ void CauterizedModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - /* AJT: TODO REMOVE */ -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); -#endif #ifdef SKIN_COMP - - if (debug) { - qDebug() << "AJT: _meshState[" << i << "], cluster[" << j << "]"; - } - - AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); - AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); - result.rot() = glm::normalize(result.rot()); - - // pack scale rotation and translation into a mat4. - state.clusterMatrices[j][0].x = result.scale().x; - state.clusterMatrices[j][0].y = result.scale().y; - state.clusterMatrices[j][0].z = result.scale().z; - - state.clusterMatrices[j][1].x = result.rot().x; - state.clusterMatrices[j][1].y = result.rot().y; - state.clusterMatrices[j][1].z = result.rot().z; - state.clusterMatrices[j][1].w = result.rot().w; - - state.clusterMatrices[j][2].x = result.trans().x; - state.clusterMatrices[j][2].y = result.trans().y; - state.clusterMatrices[j][2].z = result.trans().z; - - // AJT REMOVE - if (debug) { - glm::mat4 jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - qDebug() << "AJT: m =" << m; - qDebug() << "AJT: (AnimPose)m =" << AnimPose(m); - qDebug() << "AJT: result =" << result; - qDebug() << "AJT: (mat4)result =" << (glm::mat4)result; - SKIN_ASSERT(result.fuzzyEqual(AnimPose(m))); - } + // AJT: TODO: optimize + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + state.clusterTransforms[j] = Model::TransformComponents(m); +#else + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } } @@ -177,60 +144,18 @@ void CauterizedModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - // AJT: TODO REMOVE: auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointMatrix = cauterizeMatrix; } - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); -#endif + #ifdef SKIN_COMP - - if (debug) { - qDebug() << "AJT: CAUTERIZED _meshState[" << i << "], cluster[" << j << "]"; - } - - auto jointPose = _rig.getJointPose(cluster.jointIndex); - /* - if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { - jointPose = cauterizePose; - } - */ - AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); - result.rot() = glm::normalize(result.rot()); - - // pack scale rotation and translation into a mat4. - state.clusterMatrices[j][0].x = result.scale().x; - state.clusterMatrices[j][0].y = result.scale().y; - state.clusterMatrices[j][0].z = result.scale().z; - - state.clusterMatrices[j][1].x = result.rot().x; - state.clusterMatrices[j][1].y = result.rot().y; - state.clusterMatrices[j][1].z = result.rot().z; - state.clusterMatrices[j][1].w = result.rot().w; - - state.clusterMatrices[j][2].x = result.trans().x; - state.clusterMatrices[j][2].y = result.trans().y; - state.clusterMatrices[j][2].z = result.trans().z; - - // AJT REMOVE - auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - /* - if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { - jointMatrix = cauterizeMatrix; - } - */ - if (debug) { - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - qDebug() << "AJT: m =" << m; - qDebug() << "AJT: (AnimPose)m =" << AnimPose(m); - qDebug() << "AJT: result =" << result; - qDebug() << "AJT: (mat4)result =" << (glm::mat4)result; - SKIN_ASSERT(result.fuzzyEqual(AnimPose(m))); - } + // AJT: TODO: optimize + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + state.clusterTransforms[j] = Model::TransformComponents(m); +#else + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } } @@ -288,43 +213,31 @@ void CauterizedModel::updateRenderItems() { auto itemID = self->_modelMeshRenderItemIDs[i]; auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; - auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices); - auto clusterMatricesCauterized(self->getCauterizeMeshState(meshIndex).clusterMatrices); + auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms); + auto clusterTransformsCauterized(self->getCauterizeMeshState(meshIndex).clusterTransforms); bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); - transaction.updateItem(itemID, [modelTransform, clusterMatrices, clusterMatricesCauterized, invalidatePayloadShapeKey, + transaction.updateItem(itemID, [modelTransform, clusterTransforms, clusterTransformsCauterized, invalidatePayloadShapeKey, isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, enableCauterization](CauterizedMeshPartPayload& data) { - data.updateClusterBuffer(clusterMatrices, clusterMatricesCauterized); + data.updateClusterBuffer(clusterTransforms, clusterTransformsCauterized); Transform renderTransform = modelTransform; - if (clusterMatrices.size() == 1) { -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0])); -#endif + if (clusterTransforms.size() == 1) { #ifdef SKIN_COMP - glm::vec3 scale(clusterMatrices[0][0]); - glm::quat rot(clusterMatrices[0][1].w, clusterMatrices[0][1].x, clusterMatrices[0][1].y, clusterMatrices[0][1].z); - glm::vec3 trans(clusterMatrices[0][2]); - glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); - renderTransform = modelTransform.worldTransform(Transform(m)); + renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); +#else + renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); #endif } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); renderTransform = modelTransform; - if (clusterMatricesCauterized.size() == 1) { -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - renderTransform = modelTransform.worldTransform(Transform(clusterMatricesCauterized[0])); -#endif + if (clusterTransformsCauterized.size() == 1) { #ifdef SKIN_COMP - glm::vec3 scale(clusterMatricesCauterized[0][0]); - glm::quat rot(clusterMatricesCauterized[0][1].w, clusterMatricesCauterized[0][1].x, clusterMatricesCauterized[0][1].y, clusterMatricesCauterized[0][1].z); - glm::vec3 trans(clusterMatricesCauterized[0][2]); - glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); - renderTransform = modelTransform.worldTransform(Transform(m)); + renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0].getMatrix())); +#else + renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0])); #endif } data.updateTransformForCauterizedMesh(renderTransform); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index fcf6a61fb5..3056dbc728 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -326,22 +326,17 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in const Model::MeshState& state = model->getMeshState(_meshIndex); updateMeshPart(modelMesh, partIndex); - computeAdjustedLocalBound(state.clusterMatrices); + computeAdjustedLocalBound(state.clusterTransforms); updateTransform(transform, offsetTransform); Transform renderTransform = transform; - if (state.clusterMatrices.size() == 1) { -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); -#endif + if (state.clusterTransforms.size() == 1) { #ifdef SKIN_COMP - glm::vec3 scale(state.clusterMatrices[0][0]); - glm::quat rot(state.clusterMatrices[0][1].w, state.clusterMatrices[0][1].x, state.clusterMatrices[0][1].y, state.clusterMatrices[0][1].z); - glm::vec3 trans(state.clusterMatrices[0][2]); - glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); - renderTransform = transform.worldTransform(Transform(m)); + renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0].getMatrix())); +#else + renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0])); #endif + } updateTransformForSkinnedMesh(renderTransform, transform); @@ -371,20 +366,36 @@ void ModelMeshPartPayload::notifyLocationChanged() { } - -void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterMatrices) { +#ifdef SKIN_COMP +void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { // Once computed the cluster matrices, update the buffer(s) - if (clusterMatrices.size() > 1) { + if (clusterTransforms.size() > 1) { if (!_clusterBuffer) { - _clusterBuffer = std::make_shared(clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) clusterMatrices.data()); + _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(Model::TransformComponents), + (const gpu::Byte*) clusterTransforms.data()); } else { - _clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) clusterMatrices.data()); + _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(Model::TransformComponents), + (const gpu::Byte*) clusterTransforms.data()); } } } +#else +void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { + // Once computed the cluster matrices, update the buffer(s) + if (clusterTransforms.size() > 1) { + if (!_clusterBuffer) { + _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(glm::mat4), + (const gpu::Byte*) clusterTransforms.data()); + } + else { + _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(glm::mat4), + (const gpu::Byte*) clusterTransforms.data()); + } + } +} +#endif + void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform) { _transform = renderTransform; @@ -550,23 +561,27 @@ void ModelMeshPartPayload::render(RenderArgs* args) { args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } -void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterMatrices) { - _adjustedLocalBound = _localBound; - if (clusterMatrices.size() > 0) { - _adjustedLocalBound.transform(clusterMatrices[0]); - for (int i = 1; i < (int)clusterMatrices.size(); ++i) { - AABox clusterBound = _localBound; -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - clusterBound.transform(clusterMatrices[i]); -#endif #ifdef SKIN_COMP - // AJT: FIXME: TODO: SLOW AS SHIT - glm::vec3 scale(clusterMatrices[i][0]); - glm::quat rot(clusterMatrices[i][1].w, clusterMatrices[i][1].x, clusterMatrices[i][1].y, clusterMatrices[i][1].z); - glm::vec3 trans(clusterMatrices[i][2]); - glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); - clusterBound.transform(m); +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { +#else +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { +#endif + _adjustedLocalBound = _localBound; + if (clusterTransforms.size() > 0) { +#ifdef SKIN_COMP + // AJT: TODO: optimize + _adjustedLocalBound.transform(clusterTransforms[0].getMatrix()); +#else + _adjustedLocalBound.transform(clusterTransforms[0]); +#endif + + for (int i = 1; i < (int)clusterTransforms.size(); ++i) { + AABox clusterBound = _localBound; +#ifdef SKIN_COMP + // AJT: TODO: optimize + clusterBound.transform(clusterTransforms[i].getMatrix()); +#else + clusterBound.transform(clusterTransforms[i]); #endif _adjustedLocalBound += clusterBound; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index fb55883101..989a653b2f 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -87,7 +87,11 @@ public: typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; - void updateClusterBuffer(const std::vector& clusterMatrices); +#ifdef SKIN_COMP + void updateClusterBuffer(const std::vector& clusterTransforms); +#else + void updateClusterBuffer(const std::vector& clusterTransforms); +#endif void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform); // Render Item interface @@ -104,7 +108,11 @@ public: void bindMesh(gpu::Batch& batch) override; void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; +#ifdef SKIN_COMP + void computeAdjustedLocalBound(const std::vector& clusterTransforms); +#else void computeAdjustedLocalBound(const std::vector& clusterMatrices); +#endif gpu::BufferPointer _clusterBuffer; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 36d26dea7b..0f014032a3 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -266,25 +266,20 @@ void Model::updateRenderItems() { auto itemID = self->_modelMeshRenderItemIDs[i]; auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; - auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices); + auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms); bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); - transaction.updateItem(itemID, [modelTransform, clusterMatrices, invalidatePayloadShapeKey, + transaction.updateItem(itemID, [modelTransform, clusterTransforms, invalidatePayloadShapeKey, isWireframe, isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.updateClusterBuffer(clusterMatrices); + data.updateClusterBuffer(clusterTransforms); + Transform renderTransform = modelTransform; - if (clusterMatrices.size() == 1) { -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0])); -#endif + if (clusterTransforms.size() == 1) { #ifdef SKIN_COMP - glm::vec3 scale(clusterMatrices[0][0]); - glm::quat rot(clusterMatrices[0][1].w, clusterMatrices[0][1].x, clusterMatrices[0][1].y, clusterMatrices[0][1].z); - glm::vec3 trans(clusterMatrices[0][2]); - glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); - renderTransform = modelTransform.worldTransform(Transform(m)); + renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); +#else + renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); #endif } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); @@ -338,7 +333,7 @@ bool Model::updateGeometry() { const FBXGeometry& fbxGeometry = getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; - state.clusterMatrices.resize(mesh.clusters.size()); + state.clusterTransforms.resize(mesh.clusters.size()); _meshStates.push_back(state); // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index @@ -1169,7 +1164,7 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { void Model::computeMeshPartLocalBounds() { for (auto& part : _modelMeshRenderItems) { const Model::MeshState& state = _meshStates.at(part->_meshIndex); - part->computeAdjustedLocalBound(state.clusterMatrices); + part->computeAdjustedLocalBound(state.clusterTransforms); } } @@ -1187,30 +1182,14 @@ void Model::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - - // AJT: TODO FIXME -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); -#endif #ifdef SKIN_COMP - AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); - AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); - - // pack scale rotation and translation into a mat4. - state.clusterMatrices[j][0].x = result.scale().x; - state.clusterMatrices[j][0].y = result.scale().y; - state.clusterMatrices[j][0].z = result.scale().z; - - state.clusterMatrices[j][1].x = result.rot().x; - state.clusterMatrices[j][1].y = result.rot().y; - state.clusterMatrices[j][1].z = result.rot().z; - state.clusterMatrices[j][1].w = result.rot().w; - - state.clusterMatrices[j][2].x = result.trans().x; - state.clusterMatrices[j][2].y = result.trans().y; - state.clusterMatrices[j][2].z = result.trans().z; + // AJT: TODO: optimize + glm::mat4 mat; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, mat); + state.clusterTransforms[j] = TransformComponents(mat); +#else + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index b798d77ab9..e8f11a421b 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -43,8 +43,6 @@ } \ } while(false) -//#define SKIN_MATRIX -//#define SKIN_DUAL_QUAT #define SKIN_COMP class AbstractViewStateInterface; @@ -257,9 +255,50 @@ public: int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; } bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; } + class TransformComponents { + public: + TransformComponents() {} + TransformComponents(const glm::mat4& m) { + AnimPose p(m); + _scale.x = p.scale().x; + _scale.y = p.scale().y; + _scale.z = p.scale().z; + _rot = p.rot(); + _trans.x = p.trans().x; + _trans.y = p.trans().y; + _trans.z = p.trans().z; + } + + TransformComponents(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) { + _scale.x = scale.x; + _scale.y = scale.y; + _scale.z = scale.z; + _rot = rot; + _trans.x = trans.x; + _trans.y = trans.y; + _trans.z = trans.z; + } + + glm::vec3 getScale() const { return glm::vec3(_scale); } + glm::quat getRot() const { return _rot; } + glm::vec3 getTrans() const { return glm::vec3(_trans); } + glm::mat4 getMatrix() const { return createMatFromScaleQuatAndPos(getScale(), getRot(), getTrans()); }; + + protected: + glm::vec4 _scale { 1.0f, 1.0f, 1.0f, 0.0f }; + glm::quat _rot { 1.0f, 0.0f, 0.0f, 0.0f }; + glm::vec4 _trans { 0.0f, 0.0f, 0.0f, 0.0f }; + glm::vec4 _padding { 0.0f, 0.0f, 0.0f, 0.0f }; + }; + class MeshState { public: - std::vector clusterMatrices; +#ifdef SKIN_COMP + std::vector clusterTransforms; +#else + std::vector clusterTransforms; +#endif + }; const MeshState& getMeshState(int index) { return _meshStates.at(index); } diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 371efef145..63bb6ba46e 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -38,60 +38,11 @@ void dqMul(vec4 lhsReal, vec4 lhsImag, vec4 rhsReal, vec4 rhsImag, out vec4 real imagOut = quatMul(lhsReal, rhsImag) + quatMul(lhsImag, rhsReal); } -// dual quat blend -void blendClusters_dual_quat(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scaleOut, out vec4 rotOut, out vec3 posOut) { - vec3 scale = vec3(0.0, 0.0, 0.0); - vec4 dqReal = vec4(0.0, 0.0, 0.0, 0.0); - vec4 dqImag = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; +void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec3 tAccum = vec3(0.0, 0.0, 0.0); - vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 dqr = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec4 dqi = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); - - scale += s * clusterWeight; - dqReal += dqr * clusterWeight; - dqImag += dqi * clusterWeight; - } - - scaleOut = scale; - - float dqLen = length(dqReal); - dqReal *= 1.0 / dqLen; - dqImag *= 1.0 / dqLen; - - rotOut = dqReal; - - vec4 invReal = quatConj(dqReal); - posOut.xyz = 2.0 * quatMul(dqImag, invReal).xyz; -} - -// rigid dual quat blend -void blendClusters_rigid_dual_quat(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scaleOut, out vec4 rotOut, out vec3 posOut) { - float maxWeight = 0.0; - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - - vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 dqr = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec4 dqi = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); - - if (clusterWeight > maxWeight) { - maxWeight = clusterWeight; - scaleOut = s; - rotOut = dqr; - vec4 invReal = quatConj(dqr); - posOut = 2.0 * quatMul(dqi, invReal).xyz; - } - } -} - -// rigid componentwise blend -void blendClusters(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scaleOut, out vec4 rotOut, out vec3 posOut) { - float maxWeight = 0.0; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; @@ -100,51 +51,83 @@ void blendClusters(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scal vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); - if (clusterWeight > maxWeight) { - maxWeight = clusterWeight; - scaleOut = s; - rotOut = r; - posOut = t; + if (dot(r, rAccum) < 0) { + r = -r; } + + sAccum += s * clusterWeight; + rAccum += r * clusterWeight; + tAccum += t * clusterWeight; } -} -void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + rAccum = normalize(rAccum); - vec3 scale, pos; - vec4 rot; - blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); - - skinnedPosition.xyz = rotateByQuat(rot, (vec3(inPosition) * scale)) + pos; - skinnedPosition.w = 1; + skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); } void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, out vec4 skinnedPosition, out vec3 skinnedNormal) { - vec3 scale, pos; - vec4 rot; - blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec3 tAccum = vec3(0.0, 0.0, 0.0); - skinnedNormal.xyz = rotateByQuat(rot, inNormal * scale); + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); + + if (dot(r, rAccum) < 0) { + r = -r; + } + + sAccum += s * clusterWeight; + rAccum += r * clusterWeight; + tAccum += t * clusterWeight; + } + + rAccum = normalize(rAccum); + + skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); + skinnedNormal = rotateByQuat(rAccum, inNormal); } void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { - vec3 scale, pos; - vec4 rot; - blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec3 tAccum = vec3(0.0, 0.0, 0.0); - skinnedPosition.xyz = rotateByQuat(rot, (vec3(inPosition) * scale)) + pos; - skinnedPosition.w = 1; + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; - skinnedNormal = rotateByQuat(rot, inNormal * scale); - skinnedTangent = rotateByQuat(rot, inTangent * scale); + vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); + + if (dot(r, rAccum) < 0) { + r = -r; + } + + sAccum += s * clusterWeight; + rAccum += r * clusterWeight; + tAccum += t * clusterWeight; + } + + rAccum = normalize(rAccum); + + skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); + skinnedNormal = rotateByQuat(rAccum, inNormal); + skinnedTangent = rotateByQuat(rAccum, inTangent); } -// ORIGINAL /* +// ORIGINAL void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); @@ -193,5 +176,4 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v } */ - <@endif@> diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 7d49a8cda1..8fa609b241 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -50,7 +50,6 @@ void SoftAttachmentModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); -#ifdef SKIN_MATRIX // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); glm::mat4 jointMatrix; @@ -59,35 +58,14 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } - SKIN_ASSERT(false); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); -#endif #ifdef SKIN_COMP - // TODO: cache these look-ups as an optimization - int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); - AnimPose jointPose; - if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { - jointPose = _rigOverride.getJointPose(jointIndexOverride); - } else { - jointPose = _rig.getJointPose(cluster.jointIndex); - } - AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); - - // pack scale rotation and translation into a mat4. - state.clusterMatrices[j][0].x = result.scale().x; - state.clusterMatrices[j][0].y = result.scale().y; - state.clusterMatrices[j][0].z = result.scale().z; - - state.clusterMatrices[j][1].x = result.rot().x; - state.clusterMatrices[j][1].y = result.rot().y; - state.clusterMatrices[j][1].z = result.rot().z; - state.clusterMatrices[j][1].w = result.rot().w; - - state.clusterMatrices[j][2].x = result.trans().x; - state.clusterMatrices[j][2].y = result.trans().y; - state.clusterMatrices[j][2].z = result.trans().z; + // AJT: TODO: Optimize + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + state.clusterTransforms[j] = Model::TransformComponents(m); +#else + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif - } } From 3896b31a727728c04835c020e87a9380a07dfe22 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 21 Dec 2017 14:20:41 -0800 Subject: [PATCH 14/53] Line numbers for shader when there are compilation errors --- libraries/gl/src/gl/GLShaders.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libraries/gl/src/gl/GLShaders.cpp b/libraries/gl/src/gl/GLShaders.cpp index 8ef0198676..017c92b71c 100644 --- a/libraries/gl/src/gl/GLShaders.cpp +++ b/libraries/gl/src/gl/GLShaders.cpp @@ -63,12 +63,17 @@ namespace gl { } */ - qCWarning(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:"; + qCCritical(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:"; + int lineNumber = 0; for (auto s : srcstr) { - qCWarning(glLogging) << s; + QString str(s); + QStringList lines = str.split("\n"); + for (auto& line : lines) { + qCCritical(glLogging).noquote() << QString("%1: %2").arg(lineNumber++, 5, 10, QChar('0')).arg(line); + } } - qCWarning(glLogging) << "GLShader::compileShader - errors:"; - qCWarning(glLogging) << temp; + qCCritical(glLogging) << "GLShader::compileShader - errors:"; + qCCritical(glLogging) << temp; error = std::string(temp); delete[] temp; From 8bdddf721153a401344187352cb36945c47e4a0e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 21 Dec 2017 14:22:22 -0800 Subject: [PATCH 15/53] Full Dual Quaternion support --- .../src/CauterizedMeshPartPayload.cpp | 22 +-- .../src/CauterizedMeshPartPayload.h | 11 +- .../render-utils/src/CauterizedModel.cpp | 22 ++- .../render-utils/src/MeshPartPayload.cpp | 36 +--- libraries/render-utils/src/MeshPartPayload.h | 17 +- libraries/render-utils/src/Model.cpp | 9 +- libraries/render-utils/src/Model.h | 36 +++- libraries/render-utils/src/Skinning.slh | 176 ++++++++++++++++++ .../render-utils/src/SoftAttachmentModel.cpp | 7 +- libraries/shared/src/DualQuaternion.cpp | 3 + libraries/shared/src/DualQuaternion.h | 1 + 11 files changed, 267 insertions(+), 73 deletions(-) diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index c64c98b271..969be6cdd7 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -20,35 +20,19 @@ using namespace render; CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} -#ifdef SKIN_COMP -void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { +void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { ModelMeshPartPayload::updateClusterBuffer(clusterTransforms); if (cauterizedClusterTransforms.size() > 1) { if (!_cauterizedClusterBuffer) { - _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(Model::TransformComponents), + _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(TransformType), (const gpu::Byte*) cauterizedClusterTransforms.data()); } else { - _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(Model::TransformComponents), + _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(TransformType), (const gpu::Byte*) cauterizedClusterTransforms.data()); } } } -#else -void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { - ModelMeshPartPayload::updateClusterBuffer(clusterTransforms); - - if (cauterizedClusterTransforms.size() > 1) { - if (!_cauterizedClusterBuffer) { - _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(glm::mat4), - (const gpu::Byte*) cauterizedClusterTransforms.data()); - } else { - _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(glm::mat4), - (const gpu::Byte*) cauterizedClusterTransforms.data()); - } - } -} -#endif void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform& renderTransform) { _cauterizedTransform = renderTransform; diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 1ee77c300f..5dceb3debd 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -14,12 +14,17 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); -#ifdef SKIN_COMP - void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); + +#if defined(SKIN_COMP) + using TransformType = Model::TransformComponents; +#elif defined(SKIN_DQ) + using TransformType = Model::TransformDualQuaternion; #else - void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); + using TransformType = glm::mat4; #endif + void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); + void updateTransformForCauterizedMesh(const Transform& renderTransform); void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 606e1f0351..cf6aebc952 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -117,11 +117,16 @@ void CauterizedModel::updateClusterMatrices() { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); -#ifdef SKIN_COMP - // AJT: TODO: optimize +#if defined(SKIN_COMP) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + AnimPose p(m); state.clusterTransforms[j] = Model::TransformComponents(m); +#elif defined(SKIN_DQ) + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + AnimPose p(m); + state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif @@ -149,11 +154,16 @@ void CauterizedModel::updateClusterMatrices() { jointMatrix = cauterizeMatrix; } -#ifdef SKIN_COMP - // AJT: TODO: optimize +#if defined(SKIN_COMP) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + AnimPose p(m); state.clusterTransforms[j] = Model::TransformComponents(m); +#elif defined(SKIN_DQ) + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + AnimPose p(m); + state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif @@ -224,7 +234,7 @@ void CauterizedModel::updateRenderItems() { Transform renderTransform = modelTransform; if (clusterTransforms.size() == 1) { -#ifdef SKIN_COMP +#if defined(SKIN_COMP) || defined(SKIN_DQ) renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); @@ -234,7 +244,7 @@ void CauterizedModel::updateRenderItems() { renderTransform = modelTransform; if (clusterTransformsCauterized.size() == 1) { -#ifdef SKIN_COMP +#if defined(SKIN_COMP) || defined(SKIN_DQ) renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0].getMatrix())); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0])); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 3056dbc728..a303c0b0d8 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -331,7 +331,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in updateTransform(transform, offsetTransform); Transform renderTransform = transform; if (state.clusterTransforms.size() == 1) { -#ifdef SKIN_COMP +#if defined(SKIN_COMP) || defined(SKIN_DQ) renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0].getMatrix())); #else renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0])); @@ -366,26 +366,11 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -#ifdef SKIN_COMP -void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { +void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { // Once computed the cluster matrices, update the buffer(s) if (clusterTransforms.size() > 1) { if (!_clusterBuffer) { - _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(Model::TransformComponents), - (const gpu::Byte*) clusterTransforms.data()); - } - else { - _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(Model::TransformComponents), - (const gpu::Byte*) clusterTransforms.data()); - } - } -} -#else -void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { - // Once computed the cluster matrices, update the buffer(s) - if (clusterTransforms.size() > 1) { - if (!_clusterBuffer) { - _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(glm::mat4), + _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(TransformType), (const gpu::Byte*) clusterTransforms.data()); } else { @@ -394,8 +379,6 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clu } } } -#endif - void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform) { _transform = renderTransform; @@ -561,15 +544,11 @@ void ModelMeshPartPayload::render(RenderArgs* args) { args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } -#ifdef SKIN_COMP -void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { -#else -void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { -#endif + +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { _adjustedLocalBound = _localBound; if (clusterTransforms.size() > 0) { -#ifdef SKIN_COMP - // AJT: TODO: optimize +#if defined(SKIN_COMP) || defined(SKIN_DQ) _adjustedLocalBound.transform(clusterTransforms[0].getMatrix()); #else _adjustedLocalBound.transform(clusterTransforms[0]); @@ -577,8 +556,7 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms); + +#if defined(SKIN_COMP) + using TransformType = Model::TransformComponents; +#elif defined(SKIN_DQ) + using TransformType = Model::TransformDualQuaternion; #else - void updateClusterBuffer(const std::vector& clusterTransforms); + using TransformType = glm::mat4; #endif + + void updateClusterBuffer(const std::vector& clusterTransforms); void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform); // Render Item interface @@ -108,11 +113,7 @@ public: void bindMesh(gpu::Batch& batch) override; void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; -#ifdef SKIN_COMP - void computeAdjustedLocalBound(const std::vector& clusterTransforms); -#else - void computeAdjustedLocalBound(const std::vector& clusterMatrices); -#endif + void computeAdjustedLocalBound(const std::vector& clusterTransforms); gpu::BufferPointer _clusterBuffer; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 0f014032a3..6eb9662c46 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -276,7 +276,7 @@ void Model::updateRenderItems() { Transform renderTransform = modelTransform; if (clusterTransforms.size() == 1) { -#ifdef SKIN_COMP +#if defined(SKIN_COMP) || defined(SKIN_DQ) renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); @@ -1183,11 +1183,14 @@ void Model::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); -#ifdef SKIN_COMP - // AJT: TODO: optimize +#if defined(SKIN_COMP) glm::mat4 mat; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, mat); state.clusterTransforms[j] = TransformComponents(mat); +#elif defined(SKIN_DQ) + glm::mat4 mat; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, mat); + state.clusterTransforms[j] = TransformDualQuaternion(mat); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index e8f11a421b..f869634dd2 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -30,6 +30,7 @@ #include #include #include +#include #include "GeometryCache.h" #include "TextureCache.h" @@ -43,7 +44,7 @@ } \ } while(false) -#define SKIN_COMP +#define SKIN_DQ class AbstractViewStateInterface; class QScriptEngine; @@ -255,6 +256,7 @@ public: int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; } bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; } +#if defined(SKIN_COMP) class TransformComponents { public: TransformComponents() {} @@ -290,15 +292,43 @@ public: glm::vec4 _trans { 0.0f, 0.0f, 0.0f, 0.0f }; glm::vec4 _padding { 0.0f, 0.0f, 0.0f, 0.0f }; }; +#elif defined(SKIN_DQ) + class TransformDualQuaternion { + public: + TransformDualQuaternion() {} + TransformDualQuaternion(const glm::mat4& m) { + AnimPose p(m); + _scale.x = p.scale().x; + _scale.y = p.scale().y; + _scale.z = p.scale().z; + _dq = DualQuaternion(p.rot(), p.trans()); + } + TransformDualQuaternion(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) { + _scale.x = scale.x; + _scale.y = scale.y; + _scale.z = scale.z; + _dq = DualQuaternion(rot, trans); + } + glm::vec3 getScale() const { return glm::vec3(_scale); } + glm::quat getRot() const { return _dq.getRotation(); } + glm::vec3 getTrans() const { return _dq.getTranslation(); } + glm::mat4 getMatrix() const { return createMatFromScaleQuatAndPos(getScale(), getRot(), getTrans()); }; + protected: + glm::vec4 _scale { 1.0f, 1.0f, 1.0f, 0.0f }; + DualQuaternion _dq; + glm::vec4 _padding; + }; +#endif class MeshState { public: -#ifdef SKIN_COMP +#if defined(SKIN_COMP) std::vector clusterTransforms; +#elif defined(SKIN_DQ) + std::vector clusterTransforms; #else std::vector clusterTransforms; #endif - }; const MeshState& getMeshState(int index) { return _meshStates.at(index); } diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 63bb6ba46e..17c3a26079 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -38,6 +38,181 @@ void dqMul(vec4 lhsReal, vec4 lhsImag, vec4 rhsReal, vec4 rhsImag, out vec4 real imagOut = quatMul(lhsReal, rhsImag) + quatMul(lhsImag, rhsReal); } +void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 scale = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + + sAccum += scale * clusterWeight; + rAccum += real * clusterWeight; + iAccum += imag * clusterWeight; + } + + float norm = length(rAccum); + rAccum /= norm; + iAccum /= norm; + + float xe = iAccum.x; + float ye = iAccum.y; + float ze = iAccum.z; + float we = iAccum.w; + + float x0 = rAccum.x; + float y0 = rAccum.y; + float z0 = rAccum.z; + float w0 = rAccum.w; + + float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); + float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); + float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); + + vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, + 2.0 * x0 * y0 + 2.0 * w0 * z0, + 2.0 * x0 * z0 - 2.0 * w0 * y0, + 0.0); + vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, + 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, + 2.0 * y0 * z0 + 2.0 * w0 * x0, + 0.0); + vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, + 2.0 * y0 * z0 - 2.0 * w0 * x0, + 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, + 0.0); + vec4 col3 = vec4(t0, t1, t2, 1.0); + + mat4 m = mat4(col0, col1, col2, col3); + + skinnedPosition = m * (vec4(sAccum, 1) * inPosition); +} + +void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, + out vec4 skinnedPosition, out vec3 skinnedNormal) { + + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 scale = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + + sAccum += scale * clusterWeight; + rAccum += real * clusterWeight; + iAccum += imag * clusterWeight; + } + + float norm = length(rAccum); + rAccum /= norm; + iAccum /= norm; + + float xe = iAccum.x; + float ye = iAccum.y; + float ze = iAccum.z; + float we = iAccum.w; + + float x0 = rAccum.x; + float y0 = rAccum.y; + float z0 = rAccum.z; + float w0 = rAccum.w; + + float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); + float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); + float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); + + vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, + 2.0 * x0 * y0 + 2.0 * w0 * z0, + 2.0 * x0 * z0 - 2.0 * w0 * y0, + 0.0); + vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, + 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, + 2.0 * y0 * z0 + 2.0 * w0 * x0, + 0.0); + vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, + 2.0 * y0 * z0 - 2.0 * w0 * x0, + 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, + 0.0); + vec4 col3 = vec4(t0, t1, t2, 1.0); + + mat4 m = mat4(col0, col1, col2, col3); + + skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + skinnedNormal = vec3(m * vec4(inNormal, 0)); +} + +void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, + out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { + + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 scale = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + + sAccum += scale * clusterWeight; + rAccum += real * clusterWeight; + iAccum += imag * clusterWeight; + } + + float norm = length(rAccum); + rAccum /= norm; + iAccum /= norm; + + float xe = iAccum.x; + float ye = iAccum.y; + float ze = iAccum.z; + float we = iAccum.w; + + float x0 = rAccum.x; + float y0 = rAccum.y; + float z0 = rAccum.z; + float w0 = rAccum.w; + + float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); + float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); + float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); + + vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, + 2.0 * x0 * y0 + 2.0 * w0 * z0, + 2.0 * x0 * z0 - 2.0 * w0 * y0, + 0.0); + vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, + 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, + 2.0 * y0 * z0 + 2.0 * w0 * x0, + 0.0); + vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, + 2.0 * y0 * z0 - 2.0 * w0 * x0, + 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, + 0.0); + vec4 col3 = vec4(t0, t1, t2, 1.0); + + mat4 m = mat4(col0, col1, col2, col3); + + skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + skinnedNormal = vec3(m * vec4(inNormal, 0)); + skinnedTangent = vec3(m * vec4(inTangent, 0)); +} + +// SKIN_COMP +/* void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); @@ -125,6 +300,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v skinnedNormal = rotateByQuat(rAccum, inNormal); skinnedTangent = rotateByQuat(rAccum, inTangent); } +*/ /* // ORIGINAL diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 8fa609b241..7b76f89184 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -58,11 +58,14 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } -#ifdef SKIN_COMP - // AJT: TODO: Optimize +#if defined(SKIN_COMP) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformComponents(m); +#elif defined(SKIN_DQ) + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp index 3b5aaf6d6d..e9b1a65635 100644 --- a/libraries/shared/src/DualQuaternion.cpp +++ b/libraries/shared/src/DualQuaternion.cpp @@ -12,6 +12,9 @@ #include "GLMHelpers.h" // delegating constructor +DualQuaternion::DualQuaternion() : _real(1.0f, 0.0f, 0.0f, 0.0), _imag(0.0f, 0.0f, 0.0f, 0.0f) { +} + DualQuaternion::DualQuaternion(const glm::mat4& m) : DualQuaternion(glmExtractRotation(m), extractTranslation(m)) { } diff --git a/libraries/shared/src/DualQuaternion.h b/libraries/shared/src/DualQuaternion.h index ed8cdf54ff..061317bde9 100644 --- a/libraries/shared/src/DualQuaternion.h +++ b/libraries/shared/src/DualQuaternion.h @@ -19,6 +19,7 @@ class DualQuaternion { public: + DualQuaternion(); explicit DualQuaternion(const glm::mat4& m); DualQuaternion(const glm::quat& real, const glm::quat& imag); DualQuaternion(const glm::quat& rotation, const glm::vec3& translation); From 5bb0b06061a97633f052e5368b2c218b2f0c342f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 21 Dec 2017 18:15:30 -0800 Subject: [PATCH 16/53] WIP: Dual Quaternion compensation for spinning the right way. --- .../render-utils/src/CauterizedModel.cpp | 51 ++++++++++++++++++- libraries/render-utils/src/Model.h | 2 +- libraries/render-utils/src/Skinning.slh | 42 ++++++++++++--- libraries/shared/src/DualQuaternion.cpp | 17 +++++-- libraries/shared/src/DualQuaternion.h | 3 ++ 5 files changed, 104 insertions(+), 11 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index cf6aebc952..5be93304ab 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -113,7 +113,12 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); +#if defined(SKIN_DQ) + // HACK: FOR DQ go thru reverse order! + for (int j = mesh.clusters.size() - 1; j >= 0; j--) { +#else for (int j = 0; j < mesh.clusters.size(); j++) { +#endif const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); @@ -127,6 +132,25 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); + + // AJT: HACK n^2!!!! fix me, find parent clusterTransform. + int parentIndex = _rig.getJointParentIndex(cluster.jointIndex); + int parentClusterIndex = -1; + // scan for parent! + for (int ii = mesh.clusters.size() - 1; ii > j; ii--) { + if (mesh.clusters[ii].jointIndex == parentIndex) { + parentClusterIndex = ii; + break; + } + } + + // ensure that we have the same polarity as our parent! + if (parentClusterIndex >= 0) { + //if (state.clusterTransforms[parentClusterIndex]._dq.dot(state.clusterTransforms[j]._dq) < 0.0f) { + if (glm::dot(state.clusterTransforms[parentClusterIndex]._dq.real(), state.clusterTransforms[j]._dq.real()) < 0.0f) { + state.clusterTransforms[j]._dq = -state.clusterTransforms[j]._dq; + } + } #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif @@ -146,7 +170,13 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); - for (int j = 0; j < mesh.clusters.size(); j++) { + +#if defined(SKIN_DQ) + // HACK: FOR DQ go thru reverse order! + for (int j = mesh.clusters.size() - 1; j >= 0; j--) { +#else + for (int j = 0; j < mesh.clusters.size(); j++) { +#endif const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); @@ -164,6 +194,25 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); + + // AJT: HACK n^2!!!! fix me, find parent clusterTransform. + int parentIndex = _rig.getJointParentIndex(cluster.jointIndex); + int parentClusterIndex = -1; + // scan for parent! + for (int ii = mesh.clusters.size() - 1; ii > j; ii--) { + if (mesh.clusters[ii].jointIndex == parentIndex) { + parentClusterIndex = ii; + break; + } + } + + // ensure that we have the same polarity as our parent! + if (parentClusterIndex >= 0) { + //if (state.clusterTransforms[parentClusterIndex]._dq.dot(state.clusterTransforms[j]._dq) < 0.0f) { + if (glm::dot(state.clusterTransforms[parentClusterIndex]._dq.real(), state.clusterTransforms[j]._dq.real()) < 0.0f) { + state.clusterTransforms[j]._dq = -state.clusterTransforms[j]._dq; + } + } #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index f869634dd2..cb211a1011 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -313,7 +313,7 @@ public: glm::quat getRot() const { return _dq.getRotation(); } glm::vec3 getTrans() const { return _dq.getTranslation(); } glm::mat4 getMatrix() const { return createMatFromScaleQuatAndPos(getScale(), getRot(), getTrans()); }; - protected: + public: // AJT: TODO FIX ME. glm::vec4 _scale { 1.0f, 1.0f, 1.0f, 0.0f }; DualQuaternion _dq; glm::vec4 _padding; diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 17c3a26079..bdba724d43 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -43,6 +43,7 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 prevR = vec4(0.0, 0.0, 0.0, 1.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; @@ -52,9 +53,18 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + float dqClusterWeight = clusterWeight; + if (i == 0) { + prevR = real; + } else { + if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; + } + } + sAccum += scale * clusterWeight; - rAccum += real * clusterWeight; - iAccum += imag * clusterWeight; + rAccum += real * dqClusterWeight; + iAccum += imag * dqClusterWeight; } float norm = length(rAccum); @@ -100,6 +110,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 prevR = vec4(0.0, 0.0, 0.0, 1.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; @@ -109,9 +120,18 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + float dqClusterWeight = clusterWeight; + if (i == 0) { + prevR = real; + } else { + if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; + } + } + sAccum += scale * clusterWeight; - rAccum += real * clusterWeight; - iAccum += imag * clusterWeight; + rAccum += real * dqClusterWeight; + iAccum += imag * dqClusterWeight; } float norm = length(rAccum); @@ -158,6 +178,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 prevR = vec4(0.0, 0.0, 0.0, 1.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; @@ -167,9 +188,18 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + float dqClusterWeight = clusterWeight; + if (i == 0) { + prevR = real; + } else { + if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; + } + } + sAccum += scale * clusterWeight; - rAccum += real * clusterWeight; - iAccum += imag * clusterWeight; + rAccum += real * dqClusterWeight; + iAccum += imag * dqClusterWeight; } float norm = length(rAccum); diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp index e9b1a65635..44d7356463 100644 --- a/libraries/shared/src/DualQuaternion.cpp +++ b/libraries/shared/src/DualQuaternion.cpp @@ -59,26 +59,37 @@ glm::vec3 DualQuaternion::getTranslation() const { return glm::vec3(result.x, result.y, result.z); } - glm::vec3 DualQuaternion::xformVector(const glm::vec3& rhs) const { return _real * rhs; } +// AJT: UNTESTED DualQuaternion DualQuaternion::inverse() const { glm::quat invReal = glm::inverse(_real); return DualQuaternion(invReal, - invReal * _imag * invReal); } +// AJT: UNTESTED DualQuaternion DualQuaternion::conjugate() const { return DualQuaternion(glm::conjugate(_real), glm::conjugate(_imag)); } +// AJT: UNTESTED float DualQuaternion::length() const { - DualQuaternion result = *this * conjugate(); - return sqrtf(result._real.w); + float dot = this->dot(*this); + return sqrtf(dot); } DualQuaternion DualQuaternion::normalize() const { float invLen = 1.0f / length(); return *this * invLen; } + +float DualQuaternion::dot(const DualQuaternion& rhs) const { + DualQuaternion result = *this * conjugate(); + return result._real.w; +} + +DualQuaternion DualQuaternion::operator-() const { + return DualQuaternion(-_real, -_imag); +} diff --git a/libraries/shared/src/DualQuaternion.h b/libraries/shared/src/DualQuaternion.h index 061317bde9..508570a930 100644 --- a/libraries/shared/src/DualQuaternion.h +++ b/libraries/shared/src/DualQuaternion.h @@ -44,6 +44,8 @@ public: DualQuaternion conjugate() const; float length() const; DualQuaternion normalize() const; + float dot(const DualQuaternion& rhs) const; + DualQuaternion operator-() const; protected: friend QDebug operator<<(QDebug debug, const DualQuaternion& pose); @@ -51,6 +53,7 @@ protected: glm::quat _imag; }; + inline QDebug operator<<(QDebug debug, const DualQuaternion& dq) { debug << "AnimPose, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), imag = (" << dq._imag.x << dq._imag.y << dq._imag.z << dq._imag.w << ")"; return debug; From b69edceb4f9fbfdfba8f1ef6f3e34e3366610056 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 22 Dec 2017 10:54:48 -0800 Subject: [PATCH 17/53] Fixed rotation polarity and weights are now 16 bit. --- libraries/fbx/src/FBX.h | 2 +- libraries/fbx/src/FBXReader.cpp | 4 +- libraries/fbx/src/FBXReader_Mesh.cpp | 4 +- .../render-utils/src/CauterizedModel.cpp | 50 +------------------ libraries/render-utils/src/Skinning.slh | 18 +++---- 5 files changed, 12 insertions(+), 66 deletions(-) diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 7d3328a2dd..2ec847104c 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -224,7 +224,7 @@ public: QVector texCoords; QVector texCoords1; QVector clusterIndices; - QVector clusterWeights; + QVector clusterWeights; QVector originalIndices; QVector clusters; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index e4fea00a34..b1db5992e7 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1727,9 +1727,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } if (totalWeight > 0.0f) { const float ALMOST_HALF = 0.499f; - float weightScalingFactor = (float)(UINT8_MAX) / totalWeight; + float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { - extracted.mesh.clusterWeights[k] = (uint8_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF); + extracted.mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF); } } } diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index c64cbcc90d..f76435c216 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -583,7 +583,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // we need 16 bits instead of just 8 for clusterIndices clusterIndicesSize *= 2; } - int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t); + int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint16_t); int normalsOffset = 0; int tangentsOffset = normalsOffset + normalsSize; @@ -662,7 +662,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { if (clusterWeightsSize) { mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, model::BufferView(attribBuffer, clusterWeightsOffset, clusterWeightsSize, - gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::XYZW))); + gpu::Element(gpu::VEC4, gpu::NUINT16, gpu::XYZW))); } diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 5be93304ab..7bac720b86 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -113,12 +113,7 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); -#if defined(SKIN_DQ) - // HACK: FOR DQ go thru reverse order! - for (int j = mesh.clusters.size() - 1; j >= 0; j--) { -#else for (int j = 0; j < mesh.clusters.size(); j++) { -#endif const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); @@ -132,25 +127,6 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); - - // AJT: HACK n^2!!!! fix me, find parent clusterTransform. - int parentIndex = _rig.getJointParentIndex(cluster.jointIndex); - int parentClusterIndex = -1; - // scan for parent! - for (int ii = mesh.clusters.size() - 1; ii > j; ii--) { - if (mesh.clusters[ii].jointIndex == parentIndex) { - parentClusterIndex = ii; - break; - } - } - - // ensure that we have the same polarity as our parent! - if (parentClusterIndex >= 0) { - //if (state.clusterTransforms[parentClusterIndex]._dq.dot(state.clusterTransforms[j]._dq) < 0.0f) { - if (glm::dot(state.clusterTransforms[parentClusterIndex]._dq.real(), state.clusterTransforms[j]._dq.real()) < 0.0f) { - state.clusterTransforms[j]._dq = -state.clusterTransforms[j]._dq; - } - } #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif @@ -171,12 +147,7 @@ void CauterizedModel::updateClusterMatrices() { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); -#if defined(SKIN_DQ) - // HACK: FOR DQ go thru reverse order! - for (int j = mesh.clusters.size() - 1; j >= 0; j--) { -#else - for (int j = 0; j < mesh.clusters.size(); j++) { -#endif + for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); @@ -194,25 +165,6 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); - - // AJT: HACK n^2!!!! fix me, find parent clusterTransform. - int parentIndex = _rig.getJointParentIndex(cluster.jointIndex); - int parentClusterIndex = -1; - // scan for parent! - for (int ii = mesh.clusters.size() - 1; ii > j; ii--) { - if (mesh.clusters[ii].jointIndex == parentIndex) { - parentClusterIndex = ii; - break; - } - } - - // ensure that we have the same polarity as our parent! - if (parentClusterIndex >= 0) { - //if (state.clusterTransforms[parentClusterIndex]._dq.dot(state.clusterTransforms[j]._dq) < 0.0f) { - if (glm::dot(state.clusterTransforms[parentClusterIndex]._dq.real(), state.clusterTransforms[j]._dq.real()) < 0.0f) { - state.clusterTransforms[j]._dq = -state.clusterTransforms[j]._dq; - } - } #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index bdba724d43..c11455a575 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -56,10 +56,8 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio float dqClusterWeight = clusterWeight; if (i == 0) { prevR = real; - } else { - if (dot(prevR, real) < 0) { - dqClusterWeight = -clusterWeight; - } + } else if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; } sAccum += scale * clusterWeight; @@ -123,10 +121,8 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP float dqClusterWeight = clusterWeight; if (i == 0) { prevR = real; - } else { - if (dot(prevR, real) < 0) { - dqClusterWeight = -clusterWeight; - } + } else if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; } sAccum += scale * clusterWeight; @@ -191,10 +187,8 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v float dqClusterWeight = clusterWeight; if (i == 0) { prevR = real; - } else { - if (dot(prevR, real) < 0) { - dqClusterWeight = -clusterWeight; - } + } else if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; } sAccum += scale * clusterWeight; From e86fd4f9929b51d64db1e767f1fa8406a750e4da Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 22 Dec 2017 12:23:17 -0800 Subject: [PATCH 18/53] Removed SKIN_COMP define, re-enabled cauterization --- interface/src/avatar/MyAvatar.cpp | 7 +- .../src/CauterizedMeshPartPayload.h | 4 +- .../render-utils/src/CauterizedModel.cpp | 30 ++----- .../render-utils/src/MeshPartPayload.cpp | 6 +- libraries/render-utils/src/MeshPartPayload.h | 4 +- libraries/render-utils/src/Model.cpp | 8 +- libraries/render-utils/src/Model.h | 43 +-------- libraries/render-utils/src/Skinning.slh | 89 ++++--------------- .../render-utils/src/SoftAttachmentModel.cpp | 6 +- libraries/shared/src/DualQuaternion.cpp | 3 - 10 files changed, 38 insertions(+), 162 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0fd7f7f012..02a1959a95 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1820,8 +1820,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { if (_skeletonModel->isLoaded() && !_skeletonModel->getRig().getAnimNode()) { initHeadBones(); - // AJT HACK DISABLE CAUTERIZE - //_skeletonModel->setCauterizeBoneSet(_headBoneSet); + _skeletonModel->setCauterizeBoneSet(_headBoneSet); _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); initAnimGraph(); _isAnimatingScale = true; @@ -1913,9 +1912,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { // toggle using the cauterizedBones depending on where the camera is and the rendering pass type. const bool shouldDrawHead = shouldRenderHead(renderArgs); if (shouldDrawHead != _prevShouldDrawHead) { - // AJT: DISABLE CAUTER - // _skeletonModel->setEnableCauterization(!shouldDrawHead); - _skeletonModel->setEnableCauterization(false); + _skeletonModel->setEnableCauterization(!shouldDrawHead); for (int i = 0; i < _attachmentData.size(); i++) { if (_attachmentData[i].jointName.compare("Head", Qt::CaseInsensitive) == 0 || diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 5dceb3debd..2337632047 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -15,9 +15,7 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); -#if defined(SKIN_COMP) - using TransformType = Model::TransformComponents; -#elif defined(SKIN_DQ) +#if defined(SKIN_DQ) using TransformType = Model::TransformDualQuaternion; #else using TransformType = glm::mat4; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 7bac720b86..3ffacb8836 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -104,12 +104,6 @@ void CauterizedModel::updateClusterMatrices() { _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); - bool debug = false; - - if (debug) { - qDebug() << "AJT: CauterizedModel::updateClusterMatrices(), url =" << _url; - } - for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -117,12 +111,7 @@ void CauterizedModel::updateClusterMatrices() { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); -#if defined(SKIN_COMP) - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - AnimPose p(m); - state.clusterTransforms[j] = Model::TransformComponents(m); -#elif defined(SKIN_DQ) +#if defined(SKIN_DQ) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); @@ -137,9 +126,9 @@ void CauterizedModel::updateClusterMatrices() { if (!_cauterizeBoneSet.empty()) { static const glm::mat4 zeroScale( - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0001f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0001f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0001f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; @@ -155,12 +144,7 @@ void CauterizedModel::updateClusterMatrices() { jointMatrix = cauterizeMatrix; } -#if defined(SKIN_COMP) - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - AnimPose p(m); - state.clusterTransforms[j] = Model::TransformComponents(m); -#elif defined(SKIN_DQ) +#if defined(SKIN_DQ) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); @@ -235,7 +219,7 @@ void CauterizedModel::updateRenderItems() { Transform renderTransform = modelTransform; if (clusterTransforms.size() == 1) { -#if defined(SKIN_COMP) || defined(SKIN_DQ) +#if defined(SKIN_DQ) renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); @@ -245,7 +229,7 @@ void CauterizedModel::updateRenderItems() { renderTransform = modelTransform; if (clusterTransformsCauterized.size() == 1) { -#if defined(SKIN_COMP) || defined(SKIN_DQ) +#if defined(SKIN_DQ) renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0].getMatrix())); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0])); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index a303c0b0d8..481d4574ab 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -331,7 +331,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in updateTransform(transform, offsetTransform); Transform renderTransform = transform; if (state.clusterTransforms.size() == 1) { -#if defined(SKIN_COMP) || defined(SKIN_DQ) +#if defined(SKIN_DQ) renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0].getMatrix())); #else renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0])); @@ -548,7 +548,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { _adjustedLocalBound = _localBound; if (clusterTransforms.size() > 0) { -#if defined(SKIN_COMP) || defined(SKIN_DQ) +#if defined(SKIN_DQ) _adjustedLocalBound.transform(clusterTransforms[0].getMatrix()); #else _adjustedLocalBound.transform(clusterTransforms[0]); @@ -556,7 +556,7 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector clusterTransforms; -#elif defined(SKIN_DQ) +#if defined(SKIN_DQ) std::vector clusterTransforms; #else std::vector clusterTransforms; diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index c11455a575..b90db26d9f 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -43,20 +43,19 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 prevR = vec4(0.0, 0.0, 0.0, 1.0); + vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - vec3 scale = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + vec3 scale = vec3(clusterMatrix[0]); + vec4 real = clusterMatrix[1]; + vec4 imag = clusterMatrix[2]; + // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; - if (i == 0) { - prevR = real; - } else if (dot(prevR, real) < 0) { + if (dot(real, polarityReference) < 0) { dqClusterWeight = -clusterWeight; } @@ -108,20 +107,19 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 prevR = vec4(0.0, 0.0, 0.0, 1.0); + vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - vec3 scale = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + vec3 scale = vec3(clusterMatrix[0]); + vec4 real = clusterMatrix[1]; + vec4 imag = clusterMatrix[2]; + // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; - if (i == 0) { - prevR = real; - } else if (dot(prevR, real) < 0) { + if (dot(real, polarityReference) < 0) { dqClusterWeight = -clusterWeight; } @@ -174,20 +172,19 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 prevR = vec4(0.0, 0.0, 0.0, 1.0); + vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - vec3 scale = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + vec3 scale = vec3(clusterMatrix[0]); + vec4 real = clusterMatrix[1]; + vec4 imag = clusterMatrix[2]; + // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; - if (i == 0) { - prevR = real; - } else if (dot(prevR, real) < 0) { + if (dot(real, polarityReference) < 0) { dqClusterWeight = -clusterWeight; } @@ -326,54 +323,4 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v } */ -/* -// ORIGINAL -void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - } - - skinnedPosition = newPosition; -} - -void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, - out vec4 skinnedPosition, out vec3 skinnedNormal) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); - - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - } - - skinnedPosition = newPosition; - skinnedNormal = newNormal.xyz; -} - -void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, - out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newTangent = vec4(0.0, 0.0, 0.0, 0.0); - - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - newTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; - } - - skinnedPosition = newPosition; - skinnedNormal = newNormal.xyz; - skinnedTangent = newTangent.xyz; -} -*/ - <@endif@> diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 7b76f89184..58b5eea222 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -58,11 +58,7 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } -#if defined(SKIN_COMP) - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - state.clusterTransforms[j] = Model::TransformComponents(m); -#elif defined(SKIN_DQ) +#if defined(SKIN_DQ) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp index 44d7356463..e2a9dc9bb2 100644 --- a/libraries/shared/src/DualQuaternion.cpp +++ b/libraries/shared/src/DualQuaternion.cpp @@ -63,18 +63,15 @@ glm::vec3 DualQuaternion::xformVector(const glm::vec3& rhs) const { return _real * rhs; } -// AJT: UNTESTED DualQuaternion DualQuaternion::inverse() const { glm::quat invReal = glm::inverse(_real); return DualQuaternion(invReal, - invReal * _imag * invReal); } -// AJT: UNTESTED DualQuaternion DualQuaternion::conjugate() const { return DualQuaternion(glm::conjugate(_real), glm::conjugate(_imag)); } -// AJT: UNTESTED float DualQuaternion::length() const { float dot = this->dot(*this); return sqrtf(dot); From bcd813ac62342712c6b3d4f16661317f44b58910 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 22 Dec 2017 15:24:54 -0800 Subject: [PATCH 19/53] Optimizations and cleanup --- .../render-utils/src/CauterizedModel.cpp | 14 +- .../render-utils/src/MeshPartPayload.cpp | 15 +- libraries/render-utils/src/Model.cpp | 6 +- libraries/render-utils/src/Model.h | 16 +- libraries/render-utils/src/Skinning.slh | 242 +++++------------- .../render-utils/src/SoftAttachmentModel.cpp | 11 +- libraries/shared/src/Transform.h | 2 +- 7 files changed, 113 insertions(+), 193 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 3ffacb8836..86deff095b 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -112,9 +112,9 @@ void CauterizedModel::updateClusterMatrices() { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) + // AJT: TODO: optimize glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); @@ -145,9 +145,9 @@ void CauterizedModel::updateClusterMatrices() { } #if defined(SKIN_DQ) + // AJT: TODO: optimize glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); @@ -220,7 +220,10 @@ void CauterizedModel::updateRenderItems() { Transform renderTransform = modelTransform; if (clusterTransforms.size() == 1) { #if defined(SKIN_DQ) - renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); + Transform transform(clusterTransforms[0].getRotation(), + clusterTransforms[0].getScale(), + clusterTransforms[0].getTranslation()); + renderTransform = modelTransform.worldTransform(transform); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); #endif @@ -230,7 +233,10 @@ void CauterizedModel::updateRenderItems() { renderTransform = modelTransform; if (clusterTransformsCauterized.size() == 1) { #if defined(SKIN_DQ) - renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0].getMatrix())); + Transform transform(clusterTransforms[0].getRotation(), + clusterTransforms[0].getScale(), + clusterTransforms[0].getTranslation()); + renderTransform = modelTransform.worldTransform(Transform(transform)); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0])); #endif diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 481d4574ab..e3bbfc1c03 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -332,7 +332,10 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in Transform renderTransform = transform; if (state.clusterTransforms.size() == 1) { #if defined(SKIN_DQ) - renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0].getMatrix())); + Transform transform(state.clusterTransforms[0].getRotation(), + state.clusterTransforms[0].getScale(), + state.clusterTransforms[0].getTranslation()); + renderTransform = transform.worldTransform(Transform(transform)); #else renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0])); #endif @@ -549,7 +552,10 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector 0) { #if defined(SKIN_DQ) - _adjustedLocalBound.transform(clusterTransforms[0].getMatrix()); + Transform rootTransform(clusterTransforms[0].getRotation(), + clusterTransforms[0].getScale(), + clusterTransforms[0].getTranslation()); + _adjustedLocalBound.transform(rootTransform); #else _adjustedLocalBound.transform(clusterTransforms[0]); #endif @@ -557,7 +563,10 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector <@def SKINNING_SLH@> +// Use dual quaternion skinning +// Must match #define SKIN_DQ in Model.h +<@def SKIN_DQ@> + const int MAX_CLUSTERS = 128; const int INDICES_PER_VERTEX = 4; @@ -18,33 +22,46 @@ layout(std140) uniform skinClusterBuffer { mat4 clusterMatrices[MAX_CLUSTERS]; }; -vec4 quatConj(vec4 v) { - return vec4(-v.x, -v.y, -v.z, v.w); -} - -vec4 quatMul(vec4 q1, vec4 q2) { - return vec4( q1.x * q2.w + q1.y * q2.z - q1.z * q2.y + q1.w * q2.x, - -q1.x * q2.z + q1.y * q2.w + q1.z * q2.x + q1.w * q2.y, - q1.x * q2.y - q1.y * q2.x + q1.z * q2.w + q1.w * q2.z, - -q1.x * q2.x - q1.y * q2.y - q1.z * q2.z + q1.w * q2.w); -} - -vec3 rotateByQuat(vec4 q, vec3 p) { - return vec3(quatMul(quatMul(q, vec4(p.x, p.y, p.z, 0.0)), quatConj(q))); -} - -void dqMul(vec4 lhsReal, vec4 lhsImag, vec4 rhsReal, vec4 rhsImag, out vec4 realOut, out vec4 imagOut) { - realOut = quatMul(lhsReal, rhsReal); - imagOut = quatMul(lhsReal, rhsImag) + quatMul(lhsImag, rhsReal); +<@if SKIN_DQ@> + +mat4 dualQuatToMat4(vec4 real, vec4 imag) { + float twoRealXSq = 2.0 * real.x * real.x; + float twoRealYSq = 2.0 * real.y * real.y; + float twoRealZSq = 2.0 * real.z * real.z; + float twoRealXY = 2.0 * real.x * real.y; + float twoRealXZ = 2.0 * real.x * real.z; + float twoRealXW = 2.0 * real.x * real.w; + float twoRealZW = 2.0 * real.z * real.w; + float twoRealYZ = 2.0 * real.y * real.z; + float twoRealYW = 2.0 * real.y * real.w; + vec4 col0 = vec4(1.0 - twoRealYSq - twoRealZSq, + twoRealXY + twoRealZW, + twoRealXZ - twoRealYW, + 0.0); + vec4 col1 = vec4(twoRealXY - twoRealZW, + 1 - twoRealXSq - twoRealZSq, + twoRealYZ + twoRealXW, + 0.0); + vec4 col2 = vec4(twoRealXZ + twoRealYW, + twoRealYZ - twoRealXW, + 1 - twoRealXSq - twoRealYSq, + 0.0); + vec4 col3 = vec4(2.0 * (-imag.w * real.x + imag.x * real.w - imag.y * real.z + imag.z * real.y), + 2.0 * (-imag.w * real.y + imag.x * real.z + imag.y * real.w - imag.z * real.x), + 2.0 * (-imag.w * real.z - imag.x * real.y + imag.y * real.x + imag.z * real.w), + 1.0); + + return mat4(col0, col1, col2, col3); } +// dual quaternion linear blending void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + // linearly blend scale and dual quaternion components vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; - for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; @@ -64,46 +81,20 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio iAccum += imag * dqClusterWeight; } + // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; iAccum /= norm; - float xe = iAccum.x; - float ye = iAccum.y; - float ze = iAccum.z; - float we = iAccum.w; - - float x0 = rAccum.x; - float y0 = rAccum.y; - float z0 = rAccum.z; - float w0 = rAccum.w; - - float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); - float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); - float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); - - vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, - 2.0 * x0 * y0 + 2.0 * w0 * z0, - 2.0 * x0 * z0 - 2.0 * w0 * y0, - 0.0); - vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, - 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, - 2.0 * y0 * z0 + 2.0 * w0 * x0, - 0.0); - vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, - 2.0 * y0 * z0 - 2.0 * w0 * x0, - 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, - 0.0); - vec4 col3 = vec4(t0, t1, t2, 1.0); - - mat4 m = mat4(col0, col1, col2, col3); - + // conversion from dual quaternion to 4x4 matrix. + mat4 m = dualQuatToMat4(rAccum, iAccum); skinnedPosition = m * (vec4(sAccum, 1) * inPosition); } void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, out vec4 skinnedPosition, out vec3 skinnedNormal) { + // linearly blend scale and dual quaternion components vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); @@ -128,40 +119,13 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP iAccum += imag * dqClusterWeight; } + // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; iAccum /= norm; - float xe = iAccum.x; - float ye = iAccum.y; - float ze = iAccum.z; - float we = iAccum.w; - - float x0 = rAccum.x; - float y0 = rAccum.y; - float z0 = rAccum.z; - float w0 = rAccum.w; - - float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); - float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); - float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); - - vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, - 2.0 * x0 * y0 + 2.0 * w0 * z0, - 2.0 * x0 * z0 - 2.0 * w0 * y0, - 0.0); - vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, - 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, - 2.0 * y0 * z0 + 2.0 * w0 * x0, - 0.0); - vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, - 2.0 * y0 * z0 - 2.0 * w0 * x0, - 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, - 0.0); - vec4 col3 = vec4(t0, t1, t2, 1.0); - - mat4 m = mat4(col0, col1, col2, col3); - + // conversion from dual quaternion to 4x4 matrix. + mat4 m = dualQuatToMat4(rAccum, iAccum); skinnedPosition = m * (vec4(sAccum, 1) * inPosition); skinnedNormal = vec3(m * vec4(inNormal, 0)); } @@ -169,6 +133,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { + // linearly blend scale and dual quaternion components vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); @@ -193,134 +158,67 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v iAccum += imag * dqClusterWeight; } + // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; iAccum /= norm; - float xe = iAccum.x; - float ye = iAccum.y; - float ze = iAccum.z; - float we = iAccum.w; - - float x0 = rAccum.x; - float y0 = rAccum.y; - float z0 = rAccum.z; - float w0 = rAccum.w; - - float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); - float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); - float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); - - vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, - 2.0 * x0 * y0 + 2.0 * w0 * z0, - 2.0 * x0 * z0 - 2.0 * w0 * y0, - 0.0); - vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, - 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, - 2.0 * y0 * z0 + 2.0 * w0 * x0, - 0.0); - vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, - 2.0 * y0 * z0 - 2.0 * w0 * x0, - 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, - 0.0); - vec4 col3 = vec4(t0, t1, t2, 1.0); - - mat4 m = mat4(col0, col1, col2, col3); - + // conversion from dual quaternion to 4x4 matrix. + mat4 m = dualQuatToMat4(rAccum, iAccum); skinnedPosition = m * (vec4(sAccum, 1) * inPosition); skinnedNormal = vec3(m * vec4(inNormal, 0)); skinnedTangent = vec3(m * vec4(inTangent, 0)); } -// SKIN_COMP -/* +<@else@> // SKIN_DQ + void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { - vec3 sAccum = vec3(0.0, 0.0, 0.0); - vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec3 tAccum = vec3(0.0, 0.0, 0.0); + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - - vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); - - if (dot(r, rAccum) < 0) { - r = -r; - } - - sAccum += s * clusterWeight; - rAccum += r * clusterWeight; - tAccum += t * clusterWeight; + newPosition += clusterMatrix * inPosition * clusterWeight; } - rAccum = normalize(rAccum); - - skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); + skinnedPosition = newPosition; } void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, out vec4 skinnedPosition, out vec3 skinnedNormal) { - - vec3 sAccum = vec3(0.0, 0.0, 0.0); - vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec3 tAccum = vec3(0.0, 0.0, 0.0); + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - - vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); - - if (dot(r, rAccum) < 0) { - r = -r; - } - - sAccum += s * clusterWeight; - rAccum += r * clusterWeight; - tAccum += t * clusterWeight; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; } - rAccum = normalize(rAccum); - - skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); - skinnedNormal = rotateByQuat(rAccum, inNormal); + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; } void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { - - vec3 sAccum = vec3(0.0, 0.0, 0.0); - vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec3 tAccum = vec3(0.0, 0.0, 0.0); + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newTangent = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - - vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); - - if (dot(r, rAccum) < 0) { - r = -r; - } - - sAccum += s * clusterWeight; - rAccum += r * clusterWeight; - tAccum += t * clusterWeight; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; + newTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; } - rAccum = normalize(rAccum); - - skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); - skinnedNormal = rotateByQuat(rAccum, inNormal); - skinnedTangent = rotateByQuat(rAccum, inTangent); + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; + skinnedTangent = newTangent.xyz; } -*/ -<@endif@> +<@endif@> // if SKIN_DQ + +<@endif@> // if not SKINNING_SLH diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 58b5eea222..fe74dc3c87 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -52,17 +52,26 @@ void SoftAttachmentModel::updateClusterMatrices() { // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); +#if defined(SKIN_DQ) glm::mat4 jointMatrix; if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } -#if defined(SKIN_DQ) + + // AJT: TODO: OPTIMIZE glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else + glm::mat4 jointMatrix; + if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { + jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); + } else { + jointMatrix = _rig.getJointTransform(cluster.jointIndex); + } + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 7a39314f4d..90bfc1aaa6 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -58,7 +58,7 @@ public: _rotation(rotation), _scale(scale), _translation(translation), - _flags(FLAG_CACHE_INVALID_BITSET) // invalid cache + _flags(0xf) // FLAG_TRANSLATION | FLAG_ROTATION | FLAG_SCALING | FLAG_NON_UNIFORM { if (!isValidScale(_scale)) { _scale = Vec3(1.0f); From aacf2d489fabd4e2186052caec26afc29f21d115 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 3 Jan 2018 13:21:47 -0800 Subject: [PATCH 20/53] removed comments --- libraries/render-utils/src/CauterizedModel.cpp | 2 -- libraries/render-utils/src/Model.cpp | 2 +- libraries/render-utils/src/SoftAttachmentModel.cpp | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 86deff095b..b8596af07f 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -112,7 +112,6 @@ void CauterizedModel::updateClusterMatrices() { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) - // AJT: TODO: optimize glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); @@ -145,7 +144,6 @@ void CauterizedModel::updateClusterMatrices() { } #if defined(SKIN_DQ) - // AJT: TODO: optimize glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 5ca9fc2bc4..b2459d27e3 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1178,6 +1178,7 @@ void Model::updateClusterMatrices() { if (!_needsUpdateClusterMatrices || !isLoaded()) { return; } + _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); for (int i = 0; i < (int) _meshStates.size(); i++) { @@ -1187,7 +1188,6 @@ void Model::updateClusterMatrices() { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) - // AJT: TODO: optimize glm::mat4 mat; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, mat); state.clusterTransforms[j] = TransformDualQuaternion(mat); diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index fe74dc3c87..0d0db7cbe3 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -60,7 +60,6 @@ void SoftAttachmentModel::updateClusterMatrices() { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } - // AJT: TODO: OPTIMIZE glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); From b8c88fca3b0e28be59dab4a3f26fb8294465120c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 4 Jan 2018 13:36:15 -0800 Subject: [PATCH 21/53] Dual Quaternion skinning optimization Reduce expensive conversion from quat -> matrix -> quat, by keeping the inverseBindPose in a Transform instance instead of a mat4. --- libraries/fbx/src/FBX.h | 1 + libraries/fbx/src/FBXReader.cpp | 1 + .../render-utils/src/CauterizedModel.cpp | 37 +++++++++++-------- libraries/render-utils/src/Model.cpp | 9 +++-- libraries/render-utils/src/Model.h | 4 ++ 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 2ec847104c..6f771a702a 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -115,6 +115,7 @@ public: int jointIndex; glm::mat4 inverseBindMatrix; + Transform inverseBindTransform; }; const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index b1db5992e7..c52647802e 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1613,6 +1613,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS fbxCluster.jointIndex = 0; } fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; + fbxCluster.inverseBindTransform = Transform(fbxCluster.inverseBindMatrix); extracted.mesh.clusters.append(fbxCluster); // override the bind rotation with the transform link diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index b8596af07f..e3f26a43d8 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -109,13 +109,14 @@ void CauterizedModel::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - - auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - state.clusterTransforms[j] = Model::TransformDualQuaternion(m); + auto jointPose = _rig.getJointPose(cluster.jointIndex); + Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); #else + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } @@ -123,14 +124,17 @@ void CauterizedModel::updateClusterMatrices() { // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { - +#if defined(SKIN_DQ) + AnimPose cauterizePose = _rig.getJointPose(geometry.neckJointIndex); + cauterizePose.scale() = glm::vec3(0.0001f, 0.0001f, 0.0001f); +#else static const glm::mat4 zeroScale( glm::vec4(0.0001f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0001f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0001f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; - +#endif for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -138,18 +142,19 @@ void CauterizedModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { - jointMatrix = cauterizeMatrix; - } - + if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { + // not cauterized so just copy the value from the non-cauterized version. + state.clusterTransforms[j] = _meshStates[i].clusterTransforms[j]; + } else { #if defined(SKIN_DQ) - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - state.clusterTransforms[j] = Model::TransformDualQuaternion(m); + Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); #else - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); + glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif + } } } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b2459d27e3..2fc4a778d9 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1188,10 +1188,13 @@ void Model::updateClusterMatrices() { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) - glm::mat4 mat; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, mat); - state.clusterTransforms[j] = TransformDualQuaternion(mat); + auto jointPose = _rig.getJointPose(cluster.jointIndex); + Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); #else + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 1461213c6f..d13a76847a 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -268,6 +268,10 @@ public: _scale.z = scale.z; _dq = DualQuaternion(rot, trans); } + TransformDualQuaternion(const Transform& transform) { + _scale = glm::vec4(transform.getScale(), 0.0f); + _dq = DualQuaternion(transform.getRotation(), transform.getTranslation()); + } glm::vec3 getScale() const { return glm::vec3(_scale); } glm::quat getRotation() const { return _dq.getRotation(); } glm::vec3 getTranslation() const { return _dq.getTranslation(); } From fac0982c67ddcc5089cec483bbaf93cdd489e589 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 4 Jan 2018 16:37:12 -0800 Subject: [PATCH 22/53] need to use sizeof(TransformType), not sizeof(mat4) --- libraries/render-utils/src/MeshPartPayload.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index e3bbfc1c03..1907be9b42 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -377,7 +377,7 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector& (const gpu::Byte*) clusterTransforms.data()); } else { - _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(glm::mat4), + _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(TransformType), (const gpu::Byte*) clusterTransforms.data()); } } From 51ab38f48415c1001b48c122ae4127232fe26940 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 4 Jan 2018 17:32:22 -0800 Subject: [PATCH 23/53] warning fix --- libraries/render-utils/src/Model.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2fc4a778d9..31e23f7660 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1186,7 +1186,6 @@ void Model::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); From c904302057ac5d827849963413854c532757ad23 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 8 Jan 2018 15:23:39 +0100 Subject: [PATCH 24/53] Added back multiplication of specular for point / directional / spot lighting by PI as Naty Hoffman recommends. Limited texture LOD of ambient map by LOD computed for filtering to prevent aliasing --- libraries/render-utils/src/LightAmbient.slh | 4 +++- libraries/render-utils/src/LightingModel.slh | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 409b8b0587..3decdbcf7c 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -16,6 +16,9 @@ uniform samplerCube skyboxMap; vec4 evalSkyboxLight(vec3 direction, float lod) { // textureQueryLevels is not available until #430, so we require explicit lod // float mipmapLevel = lod * textureQueryLevels(skyboxMap); + float filterLod = textureQueryLod(skyboxMap, direction); + // Keep texture filtering LOD as limit to prevent aliasing on specular reflection + lod = max(lod, filterLod); return textureLod(skyboxMap, direction, lod); } <@endfunc@> @@ -53,7 +56,6 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { <@if supportAmbientSphere@> { specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz; - specularLight /= 3.1415926; } <@endif@> diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 8a6c03d423..279a010cf4 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -199,8 +199,12 @@ vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) { vec3 specular = fresnelColor * power * angleAttenuation; float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x); - specular /= 3.1415926; diffuse /= 3.1415926; + // Diffuse is divided by PI but specular isn't because an infinitesimal volume light source + // has a multiplier of PI, says Naty Hoffman. + // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf + // page 23 paragraph "Punctual light sources") + return vec4(specular, diffuse); } @@ -215,8 +219,11 @@ vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) { vec3 specular = vec3(fresnelScalar) * power * angleAttenuation; float diffuse = angleAttenuation * (1.0 - fresnelScalar); - specular /= 3.1415926; diffuse /= 3.1415926; + // Diffuse is divided by PI but specular isn't because an infinitesimal volume light source + // has a multiplier of PI, says Naty Hoffman. + // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf + // page 23 paragraph "Punctual light sources") return vec4(specular, diffuse); } @@ -229,7 +236,10 @@ vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) { float power = specularDistribution(surface); vec3 specular = fresnelColor * power * angleAttenuation; - specular /= 3.1415926; + // Specular isn't divided by PI because an infinitesimal volume light source + // has a multiplier of PI, says Naty Hoffman. + // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf + // page 23 paragraph "Punctual light sources") return vec4(specular, 0.f); } From d08f94a74d09368bb19a7b4f5fe56f88d74f5e98 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 8 Jan 2018 13:19:54 -0800 Subject: [PATCH 25/53] Code review feedback * Removed AnimPose::fuzzyEqual * Fixed DualQuaternion ctor --- libraries/animation/src/AnimPose.cpp | 21 --------------------- libraries/animation/src/AnimPose.h | 2 -- libraries/shared/src/DualQuaternion.cpp | 2 +- 3 files changed, 1 insertion(+), 24 deletions(-) diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index 29c4c46d6e..a0b8fba1da 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -69,27 +69,6 @@ AnimPose AnimPose::mirror() const { return AnimPose(_scale, glm::quat(_rot.w, _rot.x, -_rot.y, -_rot.z), glm::vec3(-_trans.x, _trans.y, _trans.z)); } -bool AnimPose::fuzzyEqual(const AnimPose& rhs) const { - const float SCALE_EPSILON = 0.00001f; - const float ROT_EPSILON = 0.00001f; - const float TRANS_EPSILON = 0.001f; - if ((fabsf(rhs._scale.x - _scale.x) < SCALE_EPSILON) && - (fabsf(rhs._scale.y - _scale.y) < SCALE_EPSILON) && - (fabsf(rhs._scale.z - _scale.z) < SCALE_EPSILON)) { - if ((fabsf(rhs._rot.x - _rot.x) < ROT_EPSILON) && - (fabsf(rhs._rot.y - _rot.y) < ROT_EPSILON) && - (fabsf(rhs._rot.z - _rot.z) < ROT_EPSILON) && - (fabsf(rhs._rot.w - _rot.w) < ROT_EPSILON)) { - if ((fabsf(rhs._trans.x - _trans.x) < TRANS_EPSILON) && - (fabsf(rhs._trans.y - _trans.y) < TRANS_EPSILON) && - (fabsf(rhs._trans.z - _trans.z) < TRANS_EPSILON)) { - return true; - } - } - } - return false; -} - AnimPose::operator glm::mat4() const { glm::vec3 xAxis = _rot * glm::vec3(_scale.x, 0.0f, 0.0f); glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale.y, 0.0f); diff --git a/libraries/animation/src/AnimPose.h b/libraries/animation/src/AnimPose.h index 0f29bac7ce..2df3d1f2e4 100644 --- a/libraries/animation/src/AnimPose.h +++ b/libraries/animation/src/AnimPose.h @@ -46,8 +46,6 @@ public: const glm::vec3& trans() const { return _trans; } glm::vec3& trans() { return _trans; } - bool fuzzyEqual(const AnimPose& rhs) const; - private: friend QDebug operator<<(QDebug debug, const AnimPose& pose); glm::vec3 _scale { 1.0f }; diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp index e2a9dc9bb2..03628f8aba 100644 --- a/libraries/shared/src/DualQuaternion.cpp +++ b/libraries/shared/src/DualQuaternion.cpp @@ -28,7 +28,7 @@ DualQuaternion::DualQuaternion(const glm::vec4& real, const glm::vec4& imag) : DualQuaternion::DualQuaternion(const glm::quat& rotation, const glm::vec3& translation) { _real = rotation; - _imag = glm::quat(0, 0.5f * translation.x, 0.5f * translation.y, 0.5f * translation.z) * rotation; + _imag = glm::quat(0.0f, 0.5f * translation.x, 0.5f * translation.y, 0.5f * translation.z) * rotation; } DualQuaternion DualQuaternion::operator*(const DualQuaternion& rhs) const { From fbea22e0f0a00a99e0db008e3823aadebebbd083 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 8 Jan 2018 18:21:23 -0800 Subject: [PATCH 26/53] Renamed imag component to dual, using the correct nomenclature Also removed incomplete test from unit tests. --- libraries/render-utils/src/Skinning.slh | 38 ++++++++++++------------ libraries/shared/src/DualQuaternion.cpp | 28 ++++++++--------- libraries/shared/src/DualQuaternion.h | 8 ++--- tests/shared/src/DualQuaternionTests.cpp | 38 ++---------------------- tests/shared/src/DualQuaternionTests.h | 1 - 5 files changed, 40 insertions(+), 73 deletions(-) diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index ddcdd2c71f..49d0df3d2c 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -24,7 +24,7 @@ layout(std140) uniform skinClusterBuffer { <@if SKIN_DQ@> -mat4 dualQuatToMat4(vec4 real, vec4 imag) { +mat4 dualQuatToMat4(vec4 real, vec4 dual) { float twoRealXSq = 2.0 * real.x * real.x; float twoRealYSq = 2.0 * real.y * real.y; float twoRealZSq = 2.0 * real.z * real.z; @@ -46,9 +46,9 @@ mat4 dualQuatToMat4(vec4 real, vec4 imag) { twoRealYZ - twoRealXW, 1 - twoRealXSq - twoRealYSq, 0.0); - vec4 col3 = vec4(2.0 * (-imag.w * real.x + imag.x * real.w - imag.y * real.z + imag.z * real.y), - 2.0 * (-imag.w * real.y + imag.x * real.z + imag.y * real.w - imag.z * real.x), - 2.0 * (-imag.w * real.z - imag.x * real.y + imag.y * real.x + imag.z * real.w), + vec4 col3 = vec4(2.0 * (-dual.w * real.x + dual.x * real.w - dual.y * real.z + dual.z * real.y), + 2.0 * (-dual.w * real.y + dual.x * real.z + dual.y * real.w - dual.z * real.x), + 2.0 * (-dual.w * real.z - dual.x * real.y + dual.y * real.x + dual.z * real.w), 1.0); return mat4(col0, col1, col2, col3); @@ -60,7 +60,7 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio // linearly blend scale and dual quaternion components vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; @@ -68,7 +68,7 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio vec3 scale = vec3(clusterMatrix[0]); vec4 real = clusterMatrix[1]; - vec4 imag = clusterMatrix[2]; + vec4 dual = clusterMatrix[2]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -78,16 +78,16 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; - iAccum += imag * dqClusterWeight; + dAccum += dual * dqClusterWeight; } // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; - iAccum /= norm; + dAccum /= norm; // conversion from dual quaternion to 4x4 matrix. - mat4 m = dualQuatToMat4(rAccum, iAccum); + mat4 m = dualQuatToMat4(rAccum, dAccum); skinnedPosition = m * (vec4(sAccum, 1) * inPosition); } @@ -97,7 +97,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP // linearly blend scale and dual quaternion components vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { @@ -106,7 +106,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP vec3 scale = vec3(clusterMatrix[0]); vec4 real = clusterMatrix[1]; - vec4 imag = clusterMatrix[2]; + vec4 dual = clusterMatrix[2]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -116,16 +116,16 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; - iAccum += imag * dqClusterWeight; + dAccum += dual * dqClusterWeight; } // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; - iAccum /= norm; + dAccum /= norm; // conversion from dual quaternion to 4x4 matrix. - mat4 m = dualQuatToMat4(rAccum, iAccum); + mat4 m = dualQuatToMat4(rAccum, dAccum); skinnedPosition = m * (vec4(sAccum, 1) * inPosition); skinnedNormal = vec3(m * vec4(inNormal, 0)); } @@ -136,7 +136,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v // linearly blend scale and dual quaternion components vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { @@ -145,7 +145,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v vec3 scale = vec3(clusterMatrix[0]); vec4 real = clusterMatrix[1]; - vec4 imag = clusterMatrix[2]; + vec4 dual = clusterMatrix[2]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -155,16 +155,16 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; - iAccum += imag * dqClusterWeight; + dAccum += dual * dqClusterWeight; } // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; - iAccum /= norm; + dAccum /= norm; // conversion from dual quaternion to 4x4 matrix. - mat4 m = dualQuatToMat4(rAccum, iAccum); + mat4 m = dualQuatToMat4(rAccum, dAccum); skinnedPosition = m * (vec4(sAccum, 1) * inPosition); skinnedNormal = vec3(m * vec4(inNormal, 0)); skinnedTangent = vec3(m * vec4(inTangent, 0)); diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp index 03628f8aba..2accbed2a9 100644 --- a/libraries/shared/src/DualQuaternion.cpp +++ b/libraries/shared/src/DualQuaternion.cpp @@ -12,42 +12,42 @@ #include "GLMHelpers.h" // delegating constructor -DualQuaternion::DualQuaternion() : _real(1.0f, 0.0f, 0.0f, 0.0), _imag(0.0f, 0.0f, 0.0f, 0.0f) { +DualQuaternion::DualQuaternion() : _real(1.0f, 0.0f, 0.0f, 0.0), _dual(0.0f, 0.0f, 0.0f, 0.0f) { } DualQuaternion::DualQuaternion(const glm::mat4& m) : DualQuaternion(glmExtractRotation(m), extractTranslation(m)) { } -DualQuaternion::DualQuaternion(const glm::quat& real, const glm::quat& imag) : _real(real), _imag(imag) { +DualQuaternion::DualQuaternion(const glm::quat& real, const glm::quat& dual) : _real(real), _dual(dual) { } -DualQuaternion::DualQuaternion(const glm::vec4& real, const glm::vec4& imag) : +DualQuaternion::DualQuaternion(const glm::vec4& real, const glm::vec4& dual) : _real(real.w, real.x, real.y, real.z), - _imag(imag.w, imag.x, imag.y, imag.z) { + _dual(dual.w, dual.x, dual.y, dual.z) { } DualQuaternion::DualQuaternion(const glm::quat& rotation, const glm::vec3& translation) { _real = rotation; - _imag = glm::quat(0.0f, 0.5f * translation.x, 0.5f * translation.y, 0.5f * translation.z) * rotation; + _dual = glm::quat(0.0f, 0.5f * translation.x, 0.5f * translation.y, 0.5f * translation.z) * rotation; } DualQuaternion DualQuaternion::operator*(const DualQuaternion& rhs) const { - return DualQuaternion(_real * rhs._real, _real * rhs._imag + _imag * rhs._real); + return DualQuaternion(_real * rhs._real, _real * rhs._dual + _dual * rhs._real); } DualQuaternion DualQuaternion::operator*(float scalar) const { - return DualQuaternion(_real * scalar, _imag * scalar); + return DualQuaternion(_real * scalar, _dual * scalar); } DualQuaternion DualQuaternion::operator+(const DualQuaternion& rhs) const { - return DualQuaternion(_real + rhs._real, _imag + rhs._imag); + return DualQuaternion(_real + rhs._real, _dual + rhs._dual); } glm::vec3 DualQuaternion::xformPoint(const glm::vec3& rhs) const { DualQuaternion v(glm::quat(), glm::quat(0.0f, rhs.x, rhs.y, rhs.z)); - DualQuaternion dualConj(glm::conjugate(_real), -glm::conjugate(_imag)); + DualQuaternion dualConj(glm::conjugate(_real), -glm::conjugate(_dual)); DualQuaternion result = *this * v * dualConj; - return vec3(result._imag.x, result._imag.y, result._imag.z); + return vec3(result._dual.x, result._dual.y, result._dual.z); } glm::quat DualQuaternion::getRotation() const { @@ -55,7 +55,7 @@ glm::quat DualQuaternion::getRotation() const { } glm::vec3 DualQuaternion::getTranslation() const { - glm::quat result = 2.0f * (_imag * glm::inverse(_real)); + glm::quat result = 2.0f * (_dual * glm::inverse(_real)); return glm::vec3(result.x, result.y, result.z); } @@ -65,11 +65,11 @@ glm::vec3 DualQuaternion::xformVector(const glm::vec3& rhs) const { DualQuaternion DualQuaternion::inverse() const { glm::quat invReal = glm::inverse(_real); - return DualQuaternion(invReal, - invReal * _imag * invReal); + return DualQuaternion(invReal, - invReal * _dual * invReal); } DualQuaternion DualQuaternion::conjugate() const { - return DualQuaternion(glm::conjugate(_real), glm::conjugate(_imag)); + return DualQuaternion(glm::conjugate(_real), glm::conjugate(_dual)); } float DualQuaternion::length() const { @@ -88,5 +88,5 @@ float DualQuaternion::dot(const DualQuaternion& rhs) const { } DualQuaternion DualQuaternion::operator-() const { - return DualQuaternion(-_real, -_imag); + return DualQuaternion(-_real, -_dual); } diff --git a/libraries/shared/src/DualQuaternion.h b/libraries/shared/src/DualQuaternion.h index 508570a930..709c089fdc 100644 --- a/libraries/shared/src/DualQuaternion.h +++ b/libraries/shared/src/DualQuaternion.h @@ -31,8 +31,8 @@ public: const glm::quat& real() const { return _real; } glm::quat& real() { return _real; } - const glm::quat& imag() const { return _imag; } - glm::quat& imag() { return _imag; } + const glm::quat& dual() const { return _dual; } + glm::quat& dual() { return _dual; } glm::quat getRotation() const; glm::vec3 getTranslation() const; @@ -50,12 +50,12 @@ public: protected: friend QDebug operator<<(QDebug debug, const DualQuaternion& pose); glm::quat _real; - glm::quat _imag; + glm::quat _dual; }; inline QDebug operator<<(QDebug debug, const DualQuaternion& dq) { - debug << "AnimPose, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), imag = (" << dq._imag.x << dq._imag.y << dq._imag.z << dq._imag.w << ")"; + debug << "AnimPose, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), dual = (" << dq._dual.x << dq._dual.y << dq._dual.z << dq._dual.w << ")"; return debug; } diff --git a/tests/shared/src/DualQuaternionTests.cpp b/tests/shared/src/DualQuaternionTests.cpp index 1c34bfbee7..fe14d9d166 100644 --- a/tests/shared/src/DualQuaternionTests.cpp +++ b/tests/shared/src/DualQuaternionTests.cpp @@ -31,11 +31,11 @@ static void quatComp(const glm::quat& q1, const glm::quat& q2) { void DualQuaternionTests::ctor() { glm::quat real = angleAxis(PI / 2.0f, Vectors::UNIT_Y); - glm::quat imag(0.0f, 1.0f, 2.0f, 3.0f); + glm::quat dual(0.0f, 1.0f, 2.0f, 3.0f); - DualQuaternion dq(real, imag); + DualQuaternion dq(real, dual); quatComp(real, dq.real()); - quatComp(imag, dq.imag()); + quatComp(dual, dq.dual()); glm::quat rotation = angleAxis(PI / 3.0f, Vectors::UNIT_X); glm::vec3 translation(1.0, 2.0f, 3.0f); @@ -113,35 +113,3 @@ void DualQuaternionTests::trans() { QCOMPARE_WITH_ABS_ERROR(t2, dq2.getTranslation(), 0.001f); QCOMPARE_WITH_ABS_ERROR(t3, dq3.getTranslation(), 0.001f); } - -// Dual Quaternion Linear Blending test -void DualQuaternionTests::dlb() { - DualQuaternion dq1(Quaternions::IDENTITY, glm::vec3()); - DualQuaternion dq2(angleAxis(PI / 2.0f, Vectors::UNIT_X), glm::vec3(0.0f, 1.0f, 0.0f)); - DualQuaternion dq2Alt(-angleAxis(PI / 2.0f, Vectors::UNIT_X), glm::vec3(0.0f, 1.0f, 0.0f)); - - qDebug() << "dq1 =" << dq1; - qDebug() << "dq2 =" << dq2; - - // linear blend between dq1 and dq2 - DualQuaternion dq3 = dq1 * 0.5f + dq2 * 0.5f; - - // alternate linear blend between dq1 and dq2 - DualQuaternion dq4 = dq1 * 0.5 + dq2Alt * 0.5f; - - qDebug() << "dq3 =" << dq3; - qDebug() << "dq4 =" << dq4; - - glm::vec3 p1(0.0f, 0.5f, -0.5f); - glm::vec3 p2(0.0f, 0.5f, 0.5f); - - glm::vec3 p3 = dq3.xformPoint(p1); - glm::vec3 p4 = dq3.xformPoint(p2); - glm::vec3 p5 = dq4.xformPoint(p1); - glm::vec3 p6 = dq4.xformPoint(p2); - - qDebug() << "p3 =" << p3; - qDebug() << "p4 =" << p4; - qDebug() << "p5 =" << p5; - qDebug() << "p6 =" << p6; -} diff --git a/tests/shared/src/DualQuaternionTests.h b/tests/shared/src/DualQuaternionTests.h index 988973b689..aa4b40cfd6 100644 --- a/tests/shared/src/DualQuaternionTests.h +++ b/tests/shared/src/DualQuaternionTests.h @@ -20,7 +20,6 @@ private slots: void mult(); void xform(); void trans(); - void dlb(); }; #endif // hifi_DualQuaternionTests_h From ad2a0310608816b7e0de13c1ae69f9bdc8a5f08d Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 9 Jan 2018 16:44:48 -0800 Subject: [PATCH 27/53] Fix bug in audio-mixer audio packet parsing. Codec string was being read as channel flag. --- assignment-client/src/audio/AudioMixerClientData.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 49453c6fc6..b7974c88fc 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -275,9 +275,13 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { if (micStreamIt == _audioStreams.end()) { // we don't have a mic stream yet, so add it - // read the channel flag to see if our stream is stereo or not + // hop past the sequence number that leads the packet message.seek(sizeof(quint16)); + // pull the codec string from the packet + auto codecString = message.readString(); + + // read the channel flag to see if our stream is stereo or not quint8 channelFlag; message.readPrimitive(&channelFlag); @@ -285,7 +289,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames()); avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO); - qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName; + qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "channels:" << (channelFlag + 1); connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioMixerClientData::handleMismatchAudioFormat); From c363a9281e3e1e373a3dff9bfa2765b0bcf4d81e Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 9 Jan 2018 16:54:14 -0800 Subject: [PATCH 28/53] Fix another bug in audio-mixer audio packet parsing. For SilentAudioFrame packets, numSamples was being read as channel flag. --- .../src/audio/AudioMixerClientData.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index b7974c88fc..394224b8f4 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -281,15 +281,21 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { // pull the codec string from the packet auto codecString = message.readString(); - // read the channel flag to see if our stream is stereo or not - quint8 channelFlag; - message.readPrimitive(&channelFlag); - - bool isStereo = channelFlag == 1; + // determine if the stream is stereo or not + bool isStereo; + if (packetType == PacketType::SilentAudioFrame) { + quint16 numSilentSamples; + message.readPrimitive(&numSilentSamples); + isStereo = numSilentSamples == AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; + } else { + quint8 channelFlag; + message.readPrimitive(&channelFlag); + isStereo = channelFlag == 1; + } auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames()); avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO); - qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "channels:" << (channelFlag + 1); + qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "channels:" << (isStereo ? 2 : 1); connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioMixerClientData::handleMismatchAudioFormat); From 2960ad845c0ddf0b0904d3e3c4b366c0b8dc607e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 10 Jan 2018 10:55:45 +0100 Subject: [PATCH 29/53] Fixed weird rendering bug. --- libraries/render-utils/src/LightAmbient.slh | 2 +- libraries/render-utils/src/LightingModel.slh | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 3decdbcf7c..eb565d60e4 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -16,7 +16,7 @@ uniform samplerCube skyboxMap; vec4 evalSkyboxLight(vec3 direction, float lod) { // textureQueryLevels is not available until #430, so we require explicit lod // float mipmapLevel = lod * textureQueryLevels(skyboxMap); - float filterLod = textureQueryLod(skyboxMap, direction); + float filterLod = textureQueryLod(skyboxMap, direction).x; // Keep texture filtering LOD as limit to prevent aliasing on specular reflection lod = max(lod, filterLod); return textureLod(skyboxMap, direction, lod); diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 279a010cf4..d96c565b81 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -133,16 +133,19 @@ SurfaceData initSurfaceData(float roughness, vec3 normal, vec3 eyeDir) { SurfaceData surface; surface.eyeDir = eyeDir; surface.normal = normal; - surface.lightDir = vec3(0,0,0); - surface.halfDir = vec3(0,0,0); surface.roughness = mix(0.001, 1.0, roughness); surface.roughness2 = surface.roughness * surface.roughness; surface.roughness4 = surface.roughness2 * surface.roughness2; surface.ndotv = clamp(dot(normal, eyeDir), 0.0, 1.0); + surface.smithInvG1NdotV = evalSmithInvG1(surface.roughness4, surface.ndotv); + + // These values will be set when we know the light direction, in updateSurfaceDataWithLight surface.ndoth = 0.0; surface.ndotl = 0.0; surface.ldoth = 0.0; - surface.smithInvG1NdotV = evalSmithInvG1(surface.roughness4, surface.ndotv); + surface.lightDir = vec3(0,0,1); + surface.halfDir = vec3(0,0,1); + return surface; } From 660032d8fb848322798b0fb8aeacd21c132be48b Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 10 Jan 2018 06:54:27 -0800 Subject: [PATCH 30/53] Fix bug in how emitAudioPacket() determines mono/stereo. Compressed bytes were counted as if audio samples. --- assignment-client/src/Agent.cpp | 2 +- libraries/audio-client/src/AudioClient.cpp | 2 +- libraries/audio/src/AbstractAudioInterface.cpp | 8 +++----- libraries/audio/src/AbstractAudioInterface.h | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 2fc905c6fd..d1c2c6597b 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -439,7 +439,7 @@ void Agent::executeScript() { encodedBuffer = audio; } - AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, + AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, false, audioTransform, scriptedAvatar->getWorldPosition(), glm::vec3(0), packetType, _selectedCodecName); }); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index af86499101..e688b69266 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1079,7 +1079,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { encodedBuffer = audioBuffer; } - emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, + emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, _isStereoInput, audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, packetType, _selectedCodecName); _stats.sentPacket(); diff --git a/libraries/audio/src/AbstractAudioInterface.cpp b/libraries/audio/src/AbstractAudioInterface.cpp index 4def97596f..376ecddd34 100644 --- a/libraries/audio/src/AbstractAudioInterface.cpp +++ b/libraries/audio/src/AbstractAudioInterface.cpp @@ -19,7 +19,7 @@ #include "AudioConstants.h" -void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, +void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, bool isStereo, const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale, PacketType packetType, QString codecName) { static std::mutex _mutex; @@ -30,9 +30,6 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes Locker lock(_mutex); auto audioPacket = NLPacket::create(packetType); - // FIXME - this is not a good way to determine stereoness with codecs.... - quint8 isStereo = bytes == AudioConstants::NETWORK_FRAME_BYTES_STEREO ? 1 : 0; - // write sequence number auto sequence = sequenceNumber++; audioPacket->writePrimitive(sequence); @@ -48,7 +45,8 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes audioPacket->writePrimitive(numSilentSamples); } else { // set the mono/stereo byte - audioPacket->writePrimitive(isStereo); + quint8 channelFlag = isStereo ? 1 : 0; + audioPacket->writePrimitive(channelFlag); } // pack the three float positions diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index 8b48b55206..37731c31f7 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -29,7 +29,7 @@ class AbstractAudioInterface : public QObject { public: AbstractAudioInterface(QObject* parent = 0) : QObject(parent) {}; - static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, + static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, bool isStereo, const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale, PacketType packetType, QString codecName = QString("")); From 6a2e3cc27250e01eacdcc06bfe45c1f4451d38ca Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 10 Jan 2018 15:14:18 -0800 Subject: [PATCH 31/53] Enable stereo codecs for AvatarAudioStream --- assignment-client/src/audio/AudioMixerClientData.cpp | 11 ++++++----- libraries/audio-client/src/AudioClient.cpp | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 394224b8f4..f6298ce1b9 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -283,7 +283,8 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { // determine if the stream is stereo or not bool isStereo; - if (packetType == PacketType::SilentAudioFrame) { + if (packetType == PacketType::SilentAudioFrame + || packetType == PacketType::ReplicatedSilentAudioFrame) { quint16 numSilentSamples; message.readPrimitive(&numSilentSamples); isStereo = numSilentSamples == AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; @@ -294,8 +295,8 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { } auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames()); - avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO); - qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "channels:" << (isStereo ? 2 : 1); + avatarAudioStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO); + qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo; connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioMixerClientData::handleMismatchAudioFormat); @@ -334,7 +335,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { #if INJECTORS_SUPPORT_CODECS injectorStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO); - qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName; + qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName << "isStereo:" << isStereo; #endif auto emplaced = _audioStreams.emplace( @@ -577,7 +578,7 @@ void AudioMixerClientData::setupCodec(CodecPluginPointer codec, const QString& c auto avatarAudioStream = getAvatarAudioStream(); if (avatarAudioStream) { - avatarAudioStream->setupCodec(codec, codecName, AudioConstants::MONO); + avatarAudioStream->setupCodec(codec, codecName, avatarAudioStream->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); } #if INJECTORS_SUPPORT_CODECS diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index e688b69266..2e347f88df 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -782,7 +782,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) { _selectedCodecName = selectedCodecName; - qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName; + qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput; // release any old codec encoder/decoder first... if (_codec && _encoder) { @@ -797,7 +797,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) { if (_selectedCodecName == plugin->getName()) { _codec = plugin; _receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO); - _encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO); + _encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get(); break; } From 7ba42f0e76ec8269b5b6cd7a35deb625c09a870b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 11 Jan 2018 15:52:56 +1300 Subject: [PATCH 32/53] Update JSDoc per recent changes in Window API --- interface/src/scripting/WindowScriptingInterface.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index af9c03a218..6d7a10c580 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -42,7 +42,7 @@ void CustomPromptResultFromScriptValue(const QScriptValue& object, CustomPromptR * @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other * chrome), in pixels. Read-only. * @property {number} innerHeight - The height of the drawable area of the Interface window (i.e., without borders or other - * chrome) plus the height of the menu bar, in pixels. Read-only. + * chrome), in pixels. Read-only. * @property {object} location - Provides facilities for working with your current metaverse location. See {@link location}. * @property {number} x - The x coordinate of the top left corner of the Interface window on the display. Read-only. * @property {number} y - The y coordinate of the top left corner of the Interface window on the display. Read-only. @@ -555,7 +555,7 @@ signals: /**jsdoc * Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot} with - * includeAnimated = false. + * includeAnimated = false or {@link Window.takeSecondaryCameraSnapshot|takeSecondaryCameraSnapshot}. * @function Window.stillSnapshotTaken * @param {string} pathStillSnapshot - The path and name of the snapshot image file. * @param {boolean} notify - The value of the notify parameter that {@link Window.takeSnapshot|takeSnapshot} From a543d90090968a0457d7b9e65eaf21ad69fb92d3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 11 Jan 2018 15:53:45 +1300 Subject: [PATCH 33/53] Miscellaneous fixes --- interface/src/scripting/WindowScriptingInterface.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 6d7a10c580..bfad5644bf 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -301,7 +301,7 @@ public slots: /**jsdoc * Get Interface's build number. * @function Window.checkVersion - * @returns {string} - Interface's build number. + * @returns {string} Interface's build number. */ QString checkVersion(); @@ -327,7 +327,7 @@ public slots: * full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the * dimensions is adjusted in order to match the aspect ratio. * @example Using the snapshot function and signals. - * function onStillSnapshottaken(path, notify) { + * function onStillSnapshotTaken(path, notify) { * print("Still snapshot taken: " + path); * print("Notify: " + notify); * } @@ -340,7 +340,7 @@ public slots: * print("Animated snapshot taken: " + animatedPath); * } * - * Window.stillSnapshotTaken.connect(onStillSnapshottaken); + * Window.stillSnapshotTaken.connect(onStillSnapshotTaken); * Window.processingGifStarted.connect(onProcessingGifStarted); * Window.processingGifCompleted.connect(onProcessingGifCompleted); * From c39771d995bee4180cf639b74976566ab8294219 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 11 Jan 2018 11:50:23 -0800 Subject: [PATCH 34/53] do not delete wallet file when things go wrong, and remove obsolete reset machinery --- .../qml/hifi/commerce/wallet/Help.qml | 32 ------------------- interface/src/commerce/Ledger.cpp | 7 ---- interface/src/commerce/Ledger.h | 1 - interface/src/commerce/QmlCommerce.cpp | 12 ------- interface/src/commerce/QmlCommerce.h | 2 -- interface/src/commerce/Wallet.cpp | 26 ++++----------- interface/src/commerce/Wallet.h | 2 -- 7 files changed, 7 insertions(+), 75 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 409833df98..f0f123f6c0 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -25,7 +25,6 @@ Item { id: root; property string keyFilePath; - property bool showDebugButtons: true; Connections { target: Commerce; @@ -55,37 +54,6 @@ Item { // Style color: hifi.colors.blueHighlight; } - HifiControlsUit.Button { - id: clearCachedPassphraseButton; - visible: root.showDebugButtons; - color: hifi.buttons.black; - colorScheme: hifi.colorSchemes.dark; - anchors.top: parent.top; - anchors.left: helpTitleText.right; - anchors.leftMargin: 20; - height: 40; - width: 150; - text: "DBG: Clear Pass"; - onClicked: { - Commerce.setPassphrase(""); - sendSignalToWallet({method: 'passphraseReset'}); - } - } - HifiControlsUit.Button { - id: resetButton; - visible: root.showDebugButtons; - color: hifi.buttons.red; - colorScheme: hifi.colorSchemes.dark; - anchors.top: clearCachedPassphraseButton.top; - anchors.left: clearCachedPassphraseButton.right; - height: 40; - width: 150; - text: "DBG: RST Wallet"; - onClicked: { - Commerce.reset(); - sendSignalToWallet({method: 'walletReset'}); - } - } ListModel { id: helpModel; diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 51658ddef8..15830636f0 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -189,13 +189,6 @@ void Ledger::history(const QStringList& keys, const int& pageNumber) { keysQuery("history", "historySuccess", "historyFailure", params); } -// The api/failResponse is called just for the side effect of logging. -void Ledger::resetSuccess(QNetworkReply& reply) { apiResponse("reset", reply); } -void Ledger::resetFailure(QNetworkReply& reply) { failResponse("reset", reply); } -void Ledger::reset() { - send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, QJsonObject()); -} - void Ledger::accountSuccess(QNetworkReply& reply) { // lets set the appropriate stuff in the wallet now auto wallet = DependencyManager::get(); diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 5d90aa0808..19699a69d6 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -31,7 +31,6 @@ public: void inventory(const QStringList& keys); void history(const QStringList& keys, const int& pageNumber); void account(); - void reset(); void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false); void certificateInfo(const QString& certificateId); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 320c7e041c..58548eb3ef 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -128,18 +128,6 @@ void QmlCommerce::generateKeyPair() { getWalletAuthenticatedStatus(); } -void QmlCommerce::reset() { - auto ledger = DependencyManager::get(); - auto wallet = DependencyManager::get(); - ledger->reset(); - wallet->reset(); -} - -void QmlCommerce::resetLocalWalletOnly() { - auto wallet = DependencyManager::get(); - wallet->reset(); -} - void QmlCommerce::account() { auto ledger = DependencyManager::get(); ledger->account(); diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index f2e6c82021..c04ede86c4 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -62,8 +62,6 @@ protected: Q_INVOKABLE void inventory(); Q_INVOKABLE void history(const int& pageNumber); Q_INVOKABLE void generateKeyPair(); - Q_INVOKABLE void reset(); - Q_INVOKABLE void resetLocalWalletOnly(); Q_INVOKABLE void account(); Q_INVOKABLE void certificateInfo(const QString& certificateId); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index c3c91e82a8..8b73042ada 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -144,15 +144,13 @@ bool writeKeys(const char* filename, EC_KEY* keys) { if ((fp = fopen(filename, "wt"))) { if (!PEM_write_EC_PUBKEY(fp, keys)) { fclose(fp); - qCDebug(commerce) << "failed to write public key"; - QFile(QString(filename)).remove(); + qCCritical(commerce) << "failed to write public key"; return retval; } if (!PEM_write_ECPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { fclose(fp); - qCDebug(commerce) << "failed to write private key"; - QFile(QString(filename)).remove(); + qCCritical(commerce) << "failed to write private key"; return retval; } @@ -168,7 +166,8 @@ bool writeKeys(const char* filename, EC_KEY* keys) { QPair generateECKeypair() { EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); - QPair retval; + QPair retval{}; + EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE); if (!EC_KEY_generate_key(keyPair)) { qCDebug(commerce) << "Error generating EC Keypair -" << ERR_get_error(); @@ -517,6 +516,9 @@ bool Wallet::generateKeyPair() { qCInfo(commerce) << "Generating keypair."; auto keyPair = generateECKeypair(); + if (!keyPair.first) { + return false; + } writeBackupInstructions(); @@ -653,20 +655,6 @@ QString Wallet::getKeyFilePath() { } } -void Wallet::reset() { - _publicKeys.clear(); - - delete _securityImage; - _securityImage = nullptr; - - // tell the provider we got nothing - updateImageProvider(); - _passphrase->clear(); - - - QFile keyFile(keyFilePath()); - keyFile.remove(); -} bool Wallet::writeWallet(const QString& newPassphrase) { EC_KEY* keys = readKeys(keyFilePath().toStdString().c_str()); if (keys) { diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index ed145df451..fe3a9f1d5f 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -49,8 +49,6 @@ public: bool walletIsAuthenticatedWithPassphrase(); bool changePassphrase(const QString& newPassphrase); - void reset(); - void getWalletStatus(); enum WalletStatus { WALLET_STATUS_NOT_LOGGED_IN = 0, From d225f803d0c2bc70abd4939b9efa474318053081 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Thu, 11 Jan 2018 12:28:44 -0800 Subject: [PATCH 35/53] Corrected default values. --- libraries/entities/src/EntityItemProperties.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index d0dabbf5b6..e334e5f669 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -185,9 +185,9 @@ public: DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE); DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME); - DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED); - DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED); - DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED); + DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); // This is the default mode for zone creation DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); From 2987d21f661db6d1852c22ddc4329e8dad915d3e Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 11 Jan 2018 13:15:34 -0800 Subject: [PATCH 36/53] remove some more --- interface/src/commerce/Ledger.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 19699a69d6..318006b645 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -65,8 +65,6 @@ public slots: void inventoryFailure(QNetworkReply& reply); void historySuccess(QNetworkReply& reply); void historyFailure(QNetworkReply& reply); - void resetSuccess(QNetworkReply& reply); - void resetFailure(QNetworkReply& reply); void accountSuccess(QNetworkReply& reply); void accountFailure(QNetworkReply& reply); void updateLocationSuccess(QNetworkReply& reply); From 4bd09fd9afbd7e45d216cd0f182dfc51a73cc2ea Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Thu, 11 Jan 2018 14:53:58 -0800 Subject: [PATCH 37/53] Corrected default values - for reading legacy content. --- libraries/entities/src/EntityTree.cpp | 38 +++++++++----------- libraries/networking/src/udt/PacketHeaders.h | 2 +- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ae243623f6..50455ae6be 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -39,11 +39,9 @@ #include "EntityEditFilters.h" #include "EntityDynamicFactoryInterface.h" - static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50; const float EntityTree::DEFAULT_MAX_TMP_ENTITY_LIFETIME = 60 * 60; // 1 hour - // combines the ray cast arguments into a single object class RayArgs { public: @@ -2281,28 +2279,25 @@ bool EntityTree::readFromMap(QVariantMap& map) { properties.setOwningAvatarID(myNodeID); } - // TEMPORARY fix for older content not containing these fields in the zones - if (properties.getType() == EntityTypes::EntityType::Zone) { - if (!entityMap.contains("keyLightMode")) { - properties.setKeyLightMode(COMPONENT_MODE_ENABLED); + // Fix for older content not containing these fields in the zones + int contentVersion = map["Version"].toInt(); + bool needsConversion = (contentVersion < (int)EntityVersion::ZoneLightInheritModes); + if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) { + // The background should be enabled if the mode is skybox + // Note that if the values are default then they are not stored in the JSON file + if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "skybox")) { + properties.setSkyboxMode(COMPONENT_MODE_ENABLED); + } else { + properties.setSkyboxMode(COMPONENT_MODE_INHERIT); } - if (!entityMap.contains("skyboxMode")) { - if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "nothing")) { - properties.setSkyboxMode(COMPONENT_MODE_INHERIT); - } else { - // Either the background mode field is missing (shouldn't happen) or the background mode is "skybox" - properties.setSkyboxMode(COMPONENT_MODE_ENABLED); + // The legacy version had no keylight/ambient modes - these are always on + properties.setKeyLightMode(COMPONENT_MODE_ENABLED); + properties.setAmbientLightMode(COMPONENT_MODE_ENABLED); - // Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour - if (properties.getAmbientLight().getAmbientURL() == "") { - properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL()); - } - } - } - - if (!entityMap.contains("ambientLightMode")) { - properties.setAmbientLightMode(COMPONENT_MODE_ENABLED); + // Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour + if (properties.getAmbientLight().getAmbientURL() == "") { + properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL()); } } @@ -2312,6 +2307,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { success = false; } } + return success; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 7eafbbccf5..deddeb4153 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -202,7 +202,7 @@ enum class EntityVersion : PacketVersion { HazeEffect, StaticCertJsonVersionOne, OwnershipChallengeFix, - ZoneLightInheritModes, + ZoneLightInheritModes = 82, ZoneStageRemoved }; From 311e95e0d0bbfed4d84324aa30d505c245f4ff06 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Thu, 11 Jan 2018 14:57:22 -0800 Subject: [PATCH 38/53] Corrected default values - for reading legacy content. --- libraries/entities/src/EntityItemProperties.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index e334e5f669..c3d04dc7ad 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -188,8 +188,6 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - - // This is the default mode for zone creation DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); From 11e918681aa622b6f939100771739a194691962b Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 12 Jan 2018 02:08:10 +0300 Subject: [PATCH 39/53] =?UTF-8?q?FB11241=20Changing=20options=20in=20menu?= =?UTF-8?q?=20causes=20the=20title=20=E2=80=9CMenu=E2=80=9D=20to=20change?= =?UTF-8?q?=20to=20=E2=80=9Croot=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit note: fix is based on removing duplicated top-level menu entry from stack view --- interface/resources/qml/hifi/tablet/TabletMenuStack.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml index ff653b6457..4fa29de9a3 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml @@ -22,7 +22,6 @@ Item { anchors.fill: parent id: d objectName: "stack" - initialItem: topMenu property var menuStack: [] property var topMenu: null; From 467a7eaf2dbc47245daf49299a014f8d5a68fda0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 11 Jan 2018 15:40:53 -0800 Subject: [PATCH 40/53] Fix for elbow glitches The pole vector constraint calculation within the IK system would sometimes compute the incorrect rotations. This would be visible as an instantaneous snap of the elbow joint as the bicep was curled. When applying pole vector constraints, there needs to be two methods of determining the current orientation of the elbow joint. One for when the arm/elbow joint is bent, and one for when the arm/elbow is straight. Previously, the way we would switch between these two solutions could cause a large rotation delta to accur between very small angles. Now we use the more accurate method (1) more often, and we smoothly blend between the solutions as the joint gets straighter. --- .../animation/src/AnimInverseKinematics.cpp | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index d17fbebf3a..15c7cfbb57 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -591,19 +591,31 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const glm::vec3 d = basePose.trans() - topPose.trans(); float dLen = glm::length(d); if (dLen > EPSILON) { + glm::vec3 dUnit = d / dLen; glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector()); + + // if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector. + // however if mid joint angle is in between the two blend between both solutions. + vec3 u = normalize(basePose.trans() - midPose.trans()); + vec3 v = normalize(topPose.trans() - midPose.trans()); + + const float LERP_THRESHOLD = 3.05433f; // 175 deg + const float BENT_THRESHOLD = 2.96706f; // 170 deg + + float jointAngle = acos(dot(u, v)); + if (jointAngle < BENT_THRESHOLD) { + glm::vec3 midPoint = topPose.trans() + d * 0.5f; + e = normalize(midPose.trans() - midPoint); + } else if (jointAngle < LERP_THRESHOLD) { + glm::vec3 midPoint = topPose.trans() + d * 0.5f; + float alpha = (jointAngle - LERP_THRESHOLD) / (BENT_THRESHOLD - LERP_THRESHOLD); + e = lerp(e, normalize(midPose.trans() - midPoint), alpha); + } + glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit; float eProjLen = glm::length(eProj); - const float MIN_EPROJ_LEN = 0.5f; - if (eProjLen < MIN_EPROJ_LEN) { - glm::vec3 midPoint = topPose.trans() + d * 0.5f; - e = midPose.trans() - midPoint; - eProj = e - glm::dot(e, dUnit) * dUnit; - eProjLen = glm::length(eProj); - } - glm::vec3 p = target.getPoleVector(); glm::vec3 pProj = p - glm::dot(p, dUnit) * dUnit; float pProjLen = glm::length(pProj); @@ -636,15 +648,31 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const glm::vec3 dUnit = d / dLen; glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector()); + + + // if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector. + // however if mid joint angle is in between the two blend between both solutions. + vec3 u = normalize(basePose.trans() - midPose.trans()); + vec3 v = normalize(topPose.trans() - midPose.trans()); + + const float LERP_THRESHOLD = 3.05433f; // 175 deg + const float BENT_THRESHOLD = 2.96706f; // 170 deg + + float jointAngle = acos(dot(u, v)); + glm::vec4 eColor = RED; + if (jointAngle < BENT_THRESHOLD) { + glm::vec3 midPoint = topPose.trans() + d * 0.5f; + e = normalize(midPose.trans() - midPoint); + eColor = GREEN; + } else if (jointAngle < LERP_THRESHOLD) { + glm::vec3 midPoint = topPose.trans() + d * 0.5f; + float alpha = (jointAngle - LERP_THRESHOLD) / (BENT_THRESHOLD - LERP_THRESHOLD); + e = lerp(e, normalize(midPose.trans() - midPoint), alpha); + eColor = YELLOW; + } + glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit; float eProjLen = glm::length(eProj); - const float MIN_EPROJ_LEN = 0.5f; - if (eProjLen < MIN_EPROJ_LEN) { - glm::vec3 midPoint = topPose.trans() + d * 0.5f; - e = midPose.trans() - midPoint; - eProj = e - glm::dot(e, dUnit) * dUnit; - eProjLen = glm::length(eProj); - } glm::vec3 p = target.getPoleVector(); const float PROJ_VECTOR_LEN = 10.0f; @@ -655,7 +683,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const YELLOW); DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint), geomToWorldPose.xformPoint(midPoint + PROJ_VECTOR_LEN * glm::normalize(e)), - RED); + eColor); DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint), geomToWorldPose.xformPoint(midPoint + POLE_VECTOR_LEN * glm::normalize(p)), BLUE); From 7c5085bd24d470f8f8f3f9d03091dc875d4aeb0e Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 11 Jan 2018 16:10:50 -0800 Subject: [PATCH 41/53] Handle stereo changes while active, by restarting the codec on both ends --- assignment-client/src/audio/AudioMixerClientData.cpp | 1 + assignment-client/src/audio/AvatarAudioStream.cpp | 10 ++++++++++ libraries/audio-client/src/AudioClient.cpp | 11 ++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index f6298ce1b9..2560f43337 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -579,6 +579,7 @@ void AudioMixerClientData::setupCodec(CodecPluginPointer codec, const QString& c auto avatarAudioStream = getAvatarAudioStream(); if (avatarAudioStream) { avatarAudioStream->setupCodec(codec, codecName, avatarAudioStream->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); + qCDebug(audio) << "setting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << avatarAudioStream->isStereo(); } #if INJECTORS_SUPPORT_CODECS diff --git a/assignment-client/src/audio/AvatarAudioStream.cpp b/assignment-client/src/audio/AvatarAudioStream.cpp index 1e0c3ed9e6..42495b4dd0 100644 --- a/assignment-client/src/audio/AvatarAudioStream.cpp +++ b/assignment-client/src/audio/AvatarAudioStream.cpp @@ -11,6 +11,7 @@ #include +#include "AudioLogging.h" #include "AvatarAudioStream.h" AvatarAudioStream::AvatarAudioStream(bool isStereo, int numStaticJitterFrames) : @@ -41,6 +42,15 @@ int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray& _ringBuffer.resizeForFrameSize(isStereo ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + // restart the codec + if (_codec) { + if (_decoder) { + _codec->releaseDecoder(_decoder); + } + _decoder = _codec->createDecoder(AudioConstants::SAMPLE_RATE, isStereo ? AudioConstants::STEREO : AudioConstants::MONO); + } + qCDebug(audio) << "resetting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo; + _isStereo = isStereo; } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 2e347f88df..c274f53dca 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1382,7 +1382,16 @@ void AudioClient::setIsStereoInput(bool isStereoInput) { _desiredInputFormat.setChannelCount(1); } - // change in channel count for desired input format, restart the input device + // restart the codec + if (_codec) { + if (_encoder) { + _codec->releaseEncoder(_encoder); + } + _encoder = _codec->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); + } + qCDebug(audioclient) << "Reset Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput; + + // restart the input device switchInputToAudioDevice(_inputDeviceInfo); } } From 86cfeac95c4561ebfef13462ba79082771ace832 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Thu, 11 Jan 2018 16:39:44 -0800 Subject: [PATCH 42/53] Moved variable setting to outside of loop. --- libraries/entities/src/EntityTree.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 50455ae6be..b39d88bd46 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2235,6 +2235,10 @@ bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer } bool EntityTree::readFromMap(QVariantMap& map) { + // These are needed to deal with older content (before adding inheritance modes) + int contentVersion = map["Version"].toInt(); + bool needsConversion = (contentVersion < (int)EntityVersion::ZoneLightInheritModes); + // map will have a top-level list keyed as "Entities". This will be extracted // and iterated over. Each member of this list is converted to a QVariantMap, then // to a QScriptValue, and then to EntityItemProperties. These properties are used @@ -2280,8 +2284,6 @@ bool EntityTree::readFromMap(QVariantMap& map) { } // Fix for older content not containing these fields in the zones - int contentVersion = map["Version"].toInt(); - bool needsConversion = (contentVersion < (int)EntityVersion::ZoneLightInheritModes); if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) { // The background should be enabled if the mode is skybox // Note that if the values are default then they are not stored in the JSON file From 983e1fe0759e914de44f1d18f99f658b04afb249 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 11 Jan 2018 17:57:58 -0800 Subject: [PATCH 43/53] warning fixes: removed unused variable. --- libraries/animation/src/AnimInverseKinematics.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 15c7cfbb57..7bf250a32c 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -649,7 +649,6 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const glm::vec3 dUnit = d / dLen; glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector()); - // if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector. // however if mid joint angle is in between the two blend between both solutions. vec3 u = normalize(basePose.trans() - midPose.trans()); @@ -671,9 +670,6 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const eColor = YELLOW; } - glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit; - float eProjLen = glm::length(eProj); - glm::vec3 p = target.getPoleVector(); const float PROJ_VECTOR_LEN = 10.0f; const float POLE_VECTOR_LEN = 100.0f; From db6e70cb249351833591773ee7a457558bfcd8bb Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 11 Jan 2018 18:14:16 -0800 Subject: [PATCH 44/53] Fix bug when calling switchInputToAudioDevice() with the active device. Pass QAudioDeviceInfo by value to avoid getting clobbered. --- libraries/audio-client/src/AudioClient.cpp | 2 +- libraries/audio-client/src/AudioClient.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index c274f53dca..513ccb00e9 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1427,7 +1427,7 @@ void AudioClient::outputFormatChanged() { _receivedAudioStream.outputFormatChanged(_outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); } -bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo, bool isShutdownRequest) { +bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) { qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]"; bool supportedFormat = false; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 0ceb9c4dc3..c8db742ca2 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -378,7 +378,7 @@ private: void handleLocalEchoAndReverb(QByteArray& inputByteArray); - bool switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo, bool isShutdownRequest = false); + bool switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false); bool switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest = false); // Callback acceleration dependent calculations From dfc8c86571397146c3dd53db06ed7fe9586f8bc2 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 11 Jan 2018 18:22:34 -0800 Subject: [PATCH 45/53] Fix bug when calling switchOutputToAudioDevice() with the active device. Pass QAudioDeviceInfo by value to avoid getting clobbered. --- libraries/audio-client/src/AudioClient.cpp | 2 +- libraries/audio-client/src/AudioClient.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 513ccb00e9..579910d9f7 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1610,7 +1610,7 @@ void AudioClient::outputNotify() { } } -bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest) { +bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) { qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; bool supportedFormat = false; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index c8db742ca2..0643b8e52a 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -379,7 +379,7 @@ private: void handleLocalEchoAndReverb(QByteArray& inputByteArray); bool switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false); - bool switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest = false); + bool switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest = false); // Callback acceleration dependent calculations int calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const; From a1f25bf49c3f60b41fa91095250f4310ed1f137c Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Thu, 11 Jan 2018 19:35:13 -0800 Subject: [PATCH 46/53] Moved variable setting to outside of loop. Added copy of ambient URL. --- libraries/entities/src/EntityTree.cpp | 15 +++++++++------ libraries/networking/src/udt/PacketHeaders.h | 2 ++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b39d88bd46..b7b560290e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2285,22 +2285,25 @@ bool EntityTree::readFromMap(QVariantMap& map) { // Fix for older content not containing these fields in the zones if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) { + // The ambient URL has been moved from "keyLight" to "ambientLight" + properties.getAmbientLight().setAmbientURL(entityMap["ambientURL"].toString()); + // The background should be enabled if the mode is skybox // Note that if the values are default then they are not stored in the JSON file if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "skybox")) { properties.setSkyboxMode(COMPONENT_MODE_ENABLED); - } else { + + // Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour + if (properties.getAmbientLight().getAmbientURL() == "") { + properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL()); + } + } else { properties.setSkyboxMode(COMPONENT_MODE_INHERIT); } // The legacy version had no keylight/ambient modes - these are always on properties.setKeyLightMode(COMPONENT_MODE_ENABLED); properties.setAmbientLightMode(COMPONENT_MODE_ENABLED); - - // Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour - if (properties.getAmbientLight().getAmbientURL() == "") { - properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL()); - } } EntityItemPointer entity = addEntity(entityItemID, properties); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index deddeb4153..acc1ff01db 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -196,6 +196,8 @@ void sendWrongProtocolVersionsSignature(bool sendWrongVersion); /// for debuggin uint qHash(const PacketType& key, uint seed); QDebug operator<<(QDebug debug, const PacketType& type); +// Due to the different legacy behaviour, we need special processing for domains that were created before +// the zone inheritance modes were added. These have version numbers up to 80 enum class EntityVersion : PacketVersion { StrokeColorProperty = 0, HasDynamicOwnershipTests, From 50a030b68b8d8f06a42be7e4f5ca91cb478febe6 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Fri, 12 Jan 2018 00:22:06 -0800 Subject: [PATCH 47/53] Corrected copy of ambient URL. --- libraries/entities/src/EntityTree.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b7b560290e..907426c922 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2242,7 +2242,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { // map will have a top-level list keyed as "Entities". This will be extracted // and iterated over. Each member of this list is converted to a QVariantMap, then // to a QScriptValue, and then to EntityItemProperties. These properties are used - // to add the new entity to the EnitytTree. + // to add the new entity to the EntityTree. QVariantList entitiesQList = map["Entities"].toList(); QScriptEngine scriptEngine; @@ -2286,7 +2286,10 @@ bool EntityTree::readFromMap(QVariantMap& map) { // Fix for older content not containing these fields in the zones if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) { // The ambient URL has been moved from "keyLight" to "ambientLight" - properties.getAmbientLight().setAmbientURL(entityMap["ambientURL"].toString()); + if (entityMap.contains("keyLight")) { + QVariantMap keyLightObject = entityMap["keyLight"].toMap(); + properties.getAmbientLight().setAmbientURL(keyLightObject["ambientURL"].toString()); + } // The background should be enabled if the mode is skybox // Note that if the values are default then they are not stored in the JSON file From bfd18e3696678e71f97dedd62ca4724ac9baa351 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 12 Jan 2018 09:07:50 -0800 Subject: [PATCH 48/53] unused variable warning fix --- libraries/animation/src/AnimInverseKinematics.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 7bf250a32c..c8388c3b12 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -646,7 +646,6 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const AnimPose geomToWorldPose = AnimPose(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix()); - glm::vec3 dUnit = d / dLen; glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector()); // if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector. From 6920e576137e8f03e0a1b4b1ad57e581aac37c54 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 12 Jan 2018 13:31:58 -0800 Subject: [PATCH 49/53] Fix 'Browse Avatars' button from Avatar Settings --- .../qml/dialogs/preferences/AvatarBrowser.qml | 85 -------------- .../dialogs/preferences/AvatarPreference.qml | 18 +-- .../preferences/TabletAvatarBrowser.qml | 111 ------------------ interface/src/Application.cpp | 9 ++ interface/src/Application.h | 1 + 5 files changed, 11 insertions(+), 213 deletions(-) delete mode 100644 interface/resources/qml/dialogs/preferences/AvatarBrowser.qml delete mode 100644 interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml diff --git a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml b/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml deleted file mode 100644 index 5949adffca..0000000000 --- a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml +++ /dev/null @@ -1,85 +0,0 @@ -// -// AvatarBrowser.qml -// -// Created by Bradley Austin Davis on 30 Aug 2015 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtWebChannel 1.0 -import QtWebEngine 1.2 - -import "../../windows" -import "../../controls-uit" -import "../../styles-uit" - -Window { - id: root - HifiConstants { id: hifi } - width: 900; height: 700 - resizable: true - modality: Qt.ApplicationModal - - Item { - anchors.fill: parent - - property bool keyboardEnabled: false - property bool keyboardRaised: true - property bool punctuationMode: false - - BaseWebView { - id: webview - url: Account.metaverseServerURL + "/marketplace?category=avatars" - focus: true - - anchors { - top: parent.top - left: parent.left - right: parent.right - bottom: keyboard.top - } - - // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // Detect when may want to raise and lower keyboard. - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - } - } - - Keyboard { - id: keyboard - raised: parent.keyboardEnabled && parent.keyboardRaised - numeric: parent.punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } - - Component.onCompleted: { - keyboardEnabled = HMD.active; - } - } -} diff --git a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml index b27827d9d7..0efc3776b3 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml @@ -99,25 +99,9 @@ Preference { leftMargin: dataTextField.acceptableInput ? hifi.dimensions.contentSpacing.x : 0 } onClicked: { - if (typeof desktop !== "undefined") { - // Load dialog via OffscreenUi so that JavaScript EventBridge is available. - root.browser = OffscreenUi.load("dialogs/preferences/AvatarBrowser.qml"); - root.browser.windowDestroyed.connect(function(){ - root.browser = null; - }); - } else { - root.browser = tabletAvatarBrowserBuilder.createObject(tabletRoot); - - // Make dialog modal. - tabletRoot.openModal = root.browser; - } + ApplicationInterface.loadAvatarBrowser(); } } - Component { - id: tabletAvatarBrowserBuilder; - TabletAvatarBrowser { } - } - } } diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml deleted file mode 100644 index 2ea12f1d3d..0000000000 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml +++ /dev/null @@ -1,111 +0,0 @@ -// -// TabletAvatarBrowser.qml -// -// Created by David Rowe on 14 Mar 2017 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtWebChannel 1.0 -import QtWebEngine 1.2 - -import "../../../../windows" -import "../../../../controls-uit" -import "../../../../styles-uit" - -Item { - id: root - objectName: "ModelBrowserDialog" - - property string title: "Attachment Model" - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - anchors.fill: parent - - BaseWebView { - id: webview - url: (Account.metaverseServerURL + "/marketplace?category=avatars") - focus: true - - anchors { - top: parent.top - left: parent.left - right: parent.right - bottom: footer.top - } - - // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // Detect when may want to raise and lower keyboard. - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - } - } - - Rectangle { - id: footer - height: 40 - - anchors { - left: parent.left - right: parent.right - bottom: keyboard.top - } - - color: hifi.colors.baseGray - - Row { - anchors { - verticalCenter: parent.verticalCenter - right: parent.right - rightMargin: hifi.dimensions.contentMargin.x - } - - Button { - text: "Cancel" - color: hifi.buttons.white - onClicked: root.destroy(); - } - } - } - - Keyboard { - id: keyboard - - raised: parent.keyboardEnabled && parent.keyboardRaised - numeric: parent.punctuationMode - - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } - - Component.onCompleted: { - keyboardEnabled = HMD.active; - } -} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f9a994124b..e081e80360 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6807,6 +6807,15 @@ void Application::loadAddAvatarBookmarkDialog() const { avatarBookmarks->addBookmark(); } +void Application::loadAvatarBrowser() const { + auto tablet = dynamic_cast(DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")); + // construct the url to the marketplace item + QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars"; + QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js"; + tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH); + DependencyManager::get()->openTablet(); +} + void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) { postLambdaEvent([notify, includeAnimated, aspectRatio, this] { // Get a screenshot and save it diff --git a/interface/src/Application.h b/interface/src/Application.h index b01e998a21..effb35caee 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -309,6 +309,7 @@ public slots: void toggleEntityScriptServerLogDialog(); Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); Q_INVOKABLE void loadAddAvatarBookmarkDialog() const; + Q_INVOKABLE void loadAvatarBrowser() const; Q_INVOKABLE SharedSoundPointer getSampleSound() const; void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const; From 522007af9dbd93511375e9d32aa16d607ee40258 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 12 Jan 2018 14:58:14 -0800 Subject: [PATCH 50/53] Fix social media sharing buttons on Snapshot app --- interface/resources/qml/controls/TabletWebScreen.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml index 501e321f0d..bb037ad478 100644 --- a/interface/resources/qml/controls/TabletWebScreen.qml +++ b/interface/resources/qml/controls/TabletWebScreen.qml @@ -41,9 +41,9 @@ Item { onNewViewRequestedCallback: { // desktop is not defined for web-entities or tablet if (typeof desktop !== "undefined") { - desktop.openBrowserWindow(request, profile); + desktop.openBrowserWindow(request, webViewCoreProfile); } else { - tabletRoot.openBrowserWindow(request, profile); + tabletRoot.openBrowserWindow(request, webViewCoreProfile); } } From 8e4bd2d66240390d84d32b5b36e9938de3b7106b Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 12 Jan 2018 17:11:02 -0800 Subject: [PATCH 51/53] fix asset browser for tablet --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e081e80360..449b014c13 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6178,7 +6178,7 @@ void Application::showAssetServerWidget(QString filePath) { if (!hmd->getShouldShowTablet() && !isHMDMode()) { DependencyManager::get()->show(url, "AssetServer", startUpload); } else { - static const QUrl url("hifi/dialogs/TabletAssetServer.qml"); + static const QUrl url("../dialogs/TabletAssetServer.qml"); tablet->pushOntoStack(url); } } From 3e7d30366125d8ec004ac57c400680c7ca65b2b9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 15 Jan 2018 09:18:22 -0800 Subject: [PATCH 52/53] export atp GetMappingRequest to js --- .../src/AssetScriptingInterface.cpp | 24 ++++++++++++++++++- .../src/AssetScriptingInterface.h | 18 +++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 25e8c0dcf3..1c5a7ff7ba 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -16,9 +16,11 @@ #include #include #include -#include #include +#include "ScriptEngineLogging.h" + + AssetScriptingInterface::AssetScriptingInterface(QScriptEngine* engine) : _engine(engine) { @@ -53,10 +55,30 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValu setMappingRequest->start(); } +void AssetScriptingInterface::getMapping(QString path, QScriptValue callback) { + auto request = DependencyManager::get()->createGetMappingRequest(path); + QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable { + auto result = request->getError(); + if (callback.isFunction()) { + if (result == GetMappingRequest::NotFound) { + QScriptValueList args { "" }; + callback.call(_engine->currentContext()->thisObject(), args); + } else if (result == GetMappingRequest::NoError) { + QScriptValueList args { request->getHash() }; + callback.call(_engine->currentContext()->thisObject(), args); + } else { + qCDebug(scriptengine) << "error -- " << request->getError() << " -- " << request->getErrorString(); + } + request->deleteLater(); + } + }); + request->start(); +} void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) { if (!urlString.startsWith(ATP_SCHEME)) { + qCDebug(scriptengine) << "AssetScriptingInterface::downloadData url must be of form atp:"; return; } diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 2812be65f9..0964a4f5e2 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -75,7 +75,23 @@ public: * @param {string} error */ Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback); - + + /**jsdoc + * Look up a path to hash mapping within the connected domain's asset server + * @function Assets.getMapping + * @static + * @param path {string} + * @param callback {Assets~getMappingCallback} + */ + + /**jsdoc + * Called when getMapping is complete + * @callback Assets~getMappingCallback + * @param {string} assetID + */ + Q_INVOKABLE void getMapping(QString path, QScriptValue callback); + + Q_INVOKABLE void setBakingEnabled(QString path, bool enabled, QScriptValue callback); #if (PR_BUILD || DEV_BUILD) From 76048f3c308d7ca39712c18093c39a16e6eecdc2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 15 Jan 2018 10:48:12 -0800 Subject: [PATCH 53/53] error handling --- libraries/script-engine/src/AssetScriptingInterface.cpp | 6 ++++-- libraries/script-engine/src/AssetScriptingInterface.h | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 1c5a7ff7ba..e0e04a1e25 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -61,13 +61,15 @@ void AssetScriptingInterface::getMapping(QString path, QScriptValue callback) { auto result = request->getError(); if (callback.isFunction()) { if (result == GetMappingRequest::NotFound) { - QScriptValueList args { "" }; + QScriptValueList args { "", true }; callback.call(_engine->currentContext()->thisObject(), args); } else if (result == GetMappingRequest::NoError) { - QScriptValueList args { request->getHash() }; + QScriptValueList args { request->getHash(), true }; callback.call(_engine->currentContext()->thisObject(), args); } else { qCDebug(scriptengine) << "error -- " << request->getError() << " -- " << request->getErrorString(); + QScriptValueList args { "", false }; + callback.call(_engine->currentContext()->thisObject(), args); } request->deleteLater(); } diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 0964a4f5e2..dded2ef21d 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -85,9 +85,10 @@ public: */ /**jsdoc - * Called when getMapping is complete + * Called when getMapping is complete. * @callback Assets~getMappingCallback - * @param {string} assetID + * @param assetID {string} hash value if found, else an empty string + * @param success {boolean} false for errors other than "not found", else true */ Q_INVOKABLE void getMapping(QString path, QScriptValue callback);