svx/source/customshapes/EnhancedCustomShape3d.cxx |   70 +++++++++++++-----
 svx/source/toolbars/extrusionbar.cxx              |   84 ++++++++++++++--------
 2 files changed, 107 insertions(+), 47 deletions(-)

New commits:
commit 70a4cb766ed356bc17433ac1673e977bb0bf3d2f
Author:     Regina Henschel <rb.hensc...@t-online.de>
AuthorDate: Thu Nov 4 19:30:56 2021 +0100
Commit:     Regina Henschel <rb.hensc...@t-online.de>
CommitDate: Sat Nov 6 15:35:46 2021 +0100

    tdf#140321 enable 'matte', 'metal' extrusion surface
    
    The patch makes extrusion surface presets 'matte', 'plastic' and 'metal'
    look different from another. That has been broken from the beginning of
    custom shape extrusion in OOo2.
    To get 'matte' ODF conform, property first-light-harsh is enable.
    To keep smooth rendering set by PowerPoint, forcing shade mode 'flat'
    is removed.
    
    For more details about the change see comment in bug tdf#140321.
    
    Change-Id: Idd1e0af19ea3f7e604da7f10330c6f9e640d08f8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/124720
    Tested-by: Jenkins
    Reviewed-by: Regina Henschel <rb.hensc...@t-online.de>

diff --git a/svx/source/customshapes/EnhancedCustomShape3d.cxx 
b/svx/source/customshapes/EnhancedCustomShape3d.cxx
index 05bb45f4a9cf..7b847c381835 100644
--- a/svx/source/customshapes/EnhancedCustomShape3d.cxx
+++ b/svx/source/customshapes/EnhancedCustomShape3d.cxx
@@ -41,6 +41,7 @@
 #include <svx/scene3d.hxx>
 #include <com/sun/star/drawing/Position3D.hpp>
 #include <com/sun/star/drawing/Direction3D.hpp>
+#include <com/sun/star/drawing/NormalsKind.hpp>
 #include <com/sun/star/drawing/ShadeMode.hpp>
 #include <svx/sdr/properties/properties.hxx>
 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
@@ -327,10 +328,14 @@ SdrObject* EnhancedCustomShape3d::Create3DObject(
         bool bUseExtrusionColor = GetBool( rGeometryItem, "Color", false );
 
         drawing::FillStyle eFillStyle( aSet.Get(XATTR_FILLSTYLE).GetValue() );
-        pScene->GetProperties().SetObjectItem( Svx3DShadeModeItem( 0 ) );
+        pScene->GetProperties().SetObjectItem( 
Svx3DShadeModeItem(static_cast<sal_uInt16>(eShadeMode)));
         aSet.Put( makeSvx3DPercentDiagonalItem( 0 ) );
         aSet.Put( Svx3DTextureModeItem( 1 ) );
-        aSet.Put( Svx3DNormalsKindItem( 1 ) );
+         // SPECIFIC needed for ShadeMode_SMOOTH and ShadeMode_PHONG, 
otherwise FLAT is faster
+        if (eShadeMode == drawing::ShadeMode_SMOOTH || eShadeMode == 
drawing::ShadeMode_PHONG)
+            aSet.Put( 
Svx3DNormalsKindItem(static_cast<sal_uInt16>(drawing::NormalsKind_SPECIFIC)));
+        else
+            aSet.Put( 
Svx3DNormalsKindItem(static_cast<sal_uInt16>(drawing::NormalsKind_FLAT)));
 
         if ( eShadeMode == drawing::ShadeMode_DRAFT )
         {
@@ -656,7 +661,6 @@ SdrObject* EnhancedCustomShape3d::Create3DObject(
 
             // light
 
-            double fAmbientIntensity = GetDouble( rGeometryItem, "Brightness", 
22178.0 / 655.36 ) / 100.0;
 
             drawing::Direction3D aFirstLightDirectionDefault( 50000, 0, 10000 
);
             drawing::Direction3D aFirstLightDirection( GetDirection3D( 
rGeometryItem, "FirstLightDirection", aFirstLightDirectionDefault ) );
@@ -665,7 +669,7 @@ SdrObject* EnhancedCustomShape3d::Create3DObject(
 
             double fLightIntensity = GetDouble( rGeometryItem, 
"FirstLightLevel", 43712.0 / 655.36 ) / 100.0;
 
-            /* sal_Bool bFirstLightHarsh = */ GetBool( rGeometryItem, 
"FirstLightHarsh", false );
+            bool bFirstLightHarsh = GetBool( rGeometryItem, "FirstLightHarsh", 
true );
 
             drawing::Direction3D aSecondLightDirectionDefault( -50000, 0, 
10000 );
             drawing::Direction3D aSecondLightDirection( GetDirection3D( 
rGeometryItem, "SecondLightDirection", aSecondLightDirectionDefault ) );
@@ -677,6 +681,21 @@ SdrObject* EnhancedCustomShape3d::Create3DObject(
             /* sal_Bool bLight2Harsh = */ GetBool( rGeometryItem, 
"SecondLightHarsh", false );
             /* sal_Bool bLightFace = */ GetBool( rGeometryItem, "LightFace", 
false );
 
+            double fAmbientIntensity = GetDouble( rGeometryItem, "Brightness", 
22178.0 / 655.36 ) / 100.0;
+            bool bMetal = GetBool( rGeometryItem, "Metal", false );
+
+            // Currently needed for import from binar MS Office.
+            // ToDo: Create a solution in the filters.
+            // MS Office adds black to diffuse and ambient color in case of 
metal. Use an
+            // approximating ersatz.
+            if (bMetal)
+            {
+                fAmbientIntensity -= 0.15; // Estimated value. Adapt it if 
necessary.
+                fAmbientIntensity = std::clamp(fAmbientIntensity, 0.0, 1.0);
+                fLight2Intensity -= 0.15;
+                fLight2Intensity = std::clamp(fLight2Intensity, 0.0, 1.0);
+            }
+
             sal_uInt16 nAmbientColor = static_cast<sal_uInt16>( 
fAmbientIntensity * 255.0 );
             if ( nAmbientColor > 255 )
                 nAmbientColor = 255;
@@ -699,6 +718,9 @@ SdrObject* EnhancedCustomShape3d::Create3DObject(
             pScene->GetProperties().SetObjectItem( makeSvx3DLightcolor2Item( 
aAmbientSpot2Color ) );
             pScene->GetProperties().SetObjectItem( 
makeSvx3DLightDirection2Item( aSpotLight2 ) );
 
+            // Currently needed for import from binary MS Office.
+            // ToDo: Create a solution in the filters.
+            // Binary MS Office creates brighter shapes than our 3D engine 
with same values.
             sal_uInt8 nSpotLight3 = 70;
             basegfx::B3DVector aSpotLight3( 0.0, 0.0, 1.0 );
             pScene->GetProperties().SetObjectItem( makeSvx3DLightOnOff3Item( 
true ) );
@@ -706,23 +728,37 @@ SdrObject* EnhancedCustomShape3d::Create3DObject(
             pScene->GetProperties().SetObjectItem( makeSvx3DLightcolor3Item( 
aAmbientSpot3Color ) );
             pScene->GetProperties().SetObjectItem( 
makeSvx3DLightDirection3Item( aSpotLight3 ) );
 
-            double fSpecular = GetDouble( rGeometryItem, "Specularity", 0 ) / 
100;
-            bool bMetal = GetBool( rGeometryItem, "Metal", false );
-
-            Color aSpecularCol( 225,225,225 );
+            double fSpecular = GetDouble( rGeometryItem, "Specularity", 0 );
+            // ODF specifies 'white', OOXML uses shape fill color in some 
presets
+            Color aSpecularCol(255, 255, 255);
             if ( bMetal )
             {
+                // values as specified in ODF
                 aSpecularCol = Color( 200, 200, 200 );
-                fSpecular += 0.15;
+                fSpecular += 15.0;
+            }
+            sal_Int32 nIntensity = 100 - static_cast<sal_Int32>(fSpecular);
+            nIntensity = std::clamp<sal_Int32>(nIntensity, 0, 100);
+
+            // specularity is an object property, not a scene property
+            SdrObjListIter aSceneIter( *pScene, SdrIterMode::DeepNoGroups );
+            while (aSceneIter.IsMore())
+            {
+                const SdrObject* pNext = aSceneIter.Next();
+                
pNext->GetProperties().SetObjectItem(makeSvx3DMaterialSpecularItem(aSpecularCol));
+                
pNext->GetProperties().SetObjectItem(makeSvx3DMaterialSpecularIntensityItem(static_cast<sal_uInt16>(nIntensity)));
+            }
+
+            // fSpecular = 0 is used to indicate surface preset "matte".
+            if (!bFirstLightHarsh || basegfx::fTools::equalZero(fSpecular, 
0.0001))
+            {
+                // First light in LO 3D engine is always specular, all other 
lights are never specular.
+                // We copy light1 values to light4 and use it instead of 
light1 in the 3D scene.
+                
pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff1Item(false));
+                
pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff4Item(true));
+                
pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor4Item(aAmbientSpot1Color));
+                
pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection4Item(aSpotLight1));
             }
-            sal_Int32 nIntensity = static_cast<sal_Int32>(fSpecular) * 100;
-            if ( nIntensity > 100 )
-                nIntensity = 100;
-            else if ( nIntensity < 0 )
-                nIntensity = 0;
-            nIntensity = 100 - nIntensity;
-            pScene->GetProperties().SetObjectItem( 
makeSvx3DMaterialSpecularItem( aSpecularCol ) );
-            pScene->GetProperties().SetObjectItem( 
makeSvx3DMaterialSpecularIntensityItem( static_cast<sal_uInt16>(nIntensity) ) );
 
             pScene->SetLogicRect(
                 CalculateNewSnapRect(
diff --git a/svx/source/toolbars/extrusionbar.cxx 
b/svx/source/toolbars/extrusionbar.cxx
index 90af6561d6ff..659af238d77e 100644
--- a/svx/source/toolbars/extrusionbar.cxx
+++ b/svx/source/toolbars/extrusionbar.cxx
@@ -40,6 +40,7 @@
 #include <svx/sdasitm.hxx>
 #include <svl/intitem.hxx>
 #include <rtl/math.hxx>
+#include <basegfx/numeric/ftools.hxx>
 
 #include <svx/extrusionbar.hxx>
 #include <extrusiondepthdialog.hxx>
@@ -321,25 +322,54 @@ static void impl_execute( SfxRequest const & rReq, 
SdrCustomShapeGeometryItem& r
         {
             sal_Int32 nSurface = 
rReq.GetArgs()->GetItem<SfxInt32Item>(SID_EXTRUSION_SURFACE)->GetValue();
 
-            ShadeMode eShadeMode( ShadeMode_FLAT );
-            bool bMetal = false;
-            double fSpecularity = 0;
-            double fDiffusion = 0;
+            // Set ShadeMode only when changing from or to wireframe, 
otherwise keep existing value.
+            ShadeMode eOldShadeMode(ShadeMode_FLAT);
+            css::uno::Any* pAny = 
rGeometryItem.GetPropertyValueByName(sExtrusion, u"ShadeMode");
+            if (pAny)
+                *pAny >>= eOldShadeMode;
+            ShadeMode eShadeMode(eOldShadeMode);
+            switch (nSurface)
+            {
+                case 0: // wireframe
+                    eShadeMode = ShadeMode_DRAFT;
+                    break;
+                case 1: // matte
+                case 2: // plastic
+                case 3: // metal
+                    if (eOldShadeMode == ShadeMode_DRAFT)
+                        eShadeMode = ShadeMode_FLAT; // ODF default
+                    break;
+            }
 
+            bool bMetal = nSurface == 3;
+
+            // ODF has no dedicated property for 'surface'. MS Office binary 
format uses attribute
+            // c3DSpecularAmt to distinguish between 'matte' (=0) and 
'plastic'.
+            // From point of ODF, using not harsh light has similar effect.
+            double fOldSpecularity = 0.0;
+            pAny = rGeometryItem.GetPropertyValueByName(sExtrusion, 
u"Specularity");
+            if (pAny)
+                *pAny >>= fOldSpecularity;
+            double fSpecularity = fOldSpecularity;
+            bool bOldIsFirstLightHarsh = true;
+            pAny = rGeometryItem.GetPropertyValueByName(sExtrusion, 
u"FirstLightHarsh");
+            if (pAny)
+                *pAny >>= bOldIsFirstLightHarsh;
+            bool bIsFirstLightHarsh = bOldIsFirstLightHarsh;
             switch( nSurface )
             {
             case 0: // wireframe
-                eShadeMode = ShadeMode_DRAFT;
                 break;
             case 1: // matte
+                fSpecularity = 0.0;
+                bIsFirstLightHarsh = false;
                 break;
             case 2: // plastic
-                fSpecularity = 122.0;
-                break;
             case 3: // metal
-                bMetal = true;
-                fSpecularity = 122.0;
-                fDiffusion = 122.0;
+                if (basegfx::fTools::equalZero(fOldSpecularity, 0.0001))
+                    fSpecularity = 80; // estimated value, can be changed if 
necessary
+                if (!bOldIsFirstLightHarsh)
+                    bIsFirstLightHarsh = true;
                 break;
             }
 
@@ -352,13 +382,19 @@ static void impl_execute( SfxRequest const & rReq, 
SdrCustomShapeGeometryItem& r
             aPropValue.Value <<= bMetal;
             rGeometryItem.SetPropertyValue( sExtrusion,  aPropValue );
 
-            aPropValue.Name = "Specularity";
-            aPropValue.Value <<= fSpecularity;
-            rGeometryItem.SetPropertyValue( sExtrusion,  aPropValue );
+            if (!basegfx::fTools::equalZero(fOldSpecularity - fSpecularity, 
0.0001))
+            {
+                aPropValue.Name = "Specularity";
+                aPropValue.Value <<= fSpecularity;
+                rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
+            }
 
-            aPropValue.Name = "Diffusion";
-            aPropValue.Value <<= fDiffusion;
-            rGeometryItem.SetPropertyValue( sExtrusion,  aPropValue );
+            if (bOldIsFirstLightHarsh != bIsFirstLightHarsh)
+            {
+                aPropValue.Name = "FirstLightHarsh";
+                aPropValue.Value <<= bIsFirstLightHarsh;
+                rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
+            }
         }
     }
     break;
@@ -369,7 +405,6 @@ static void impl_execute( SfxRequest const & rReq, 
SdrCustomShapeGeometryItem& r
             sal_Int32 nLevel = 
rReq.GetArgs()->GetItem<SfxInt32Item>(SID_EXTRUSION_LIGHTING_INTENSITY)->GetValue();
 
             double fBrightness;
-            bool bHarsh2 = false;
             double fLevel1;
             double fLevel2;
 
@@ -377,19 +412,16 @@ static void impl_execute( SfxRequest const & rReq, 
SdrCustomShapeGeometryItem& r
             {
             case 0: // bright
                 fBrightness = 34.0;
-                bHarsh2 = false;
                 fLevel1 = 66.0;
                 fLevel2 = 66.0;
                 break;
             case 1: // normal
                 fBrightness = 15.0;
-                bHarsh2 = false;
                 fLevel1 = 67.0;
                 fLevel2 = 37.0;
                 break;
             case 2: // dim
                 fBrightness = 6.0;
-                bHarsh2 = true;
                 fLevel1 = 79.0;
                 fLevel2 = 21.0;
                 break;
@@ -400,16 +432,8 @@ static void impl_execute( SfxRequest const & rReq, 
SdrCustomShapeGeometryItem& r
             aPropValue.Value <<= fBrightness;
             rGeometryItem.SetPropertyValue( sExtrusion,  aPropValue );
 
-            aPropValue.Name = "LightFace";
-            aPropValue.Value <<= true;
-            rGeometryItem.SetPropertyValue( sExtrusion,  aPropValue );
-
-            aPropValue.Name = "FirstLightHarsh";
-            aPropValue.Value <<= true;
-            rGeometryItem.SetPropertyValue( sExtrusion,  aPropValue );
-
             aPropValue.Name = "SecondLightHarsh";
-            aPropValue.Value <<= bHarsh2;
+            aPropValue.Value <<= false;
             rGeometryItem.SetPropertyValue( sExtrusion,  aPropValue );
 
             aPropValue.Name = "FirstLightLevel";
@@ -849,7 +873,7 @@ static void getExtrusionSurfaceState( SdrView const * 
pSdrView, SfxItemSet& rSet
             if( pAny )
                 *pAny >>= eShadeMode;
 
-            if( eShadeMode == ShadeMode_FLAT )
+            if (eShadeMode != ShadeMode_DRAFT)
             {
                 bool bMetal = false;
                 pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, 
"Metal" );

Reply via email to