/src/libreoffice/svx/source/customshapes/EnhancedCustomShape3d.cxx
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include "EnhancedCustomShape3d.hxx" |
21 | | #include <o3tl/unit_conversion.hxx> |
22 | | #include <svx/deflt3d.hxx> |
23 | | #include <svx/svdmodel.hxx> |
24 | | #include <tools/poly.hxx> |
25 | | #include <svx/svditer.hxx> |
26 | | #include <svx/svdobj.hxx> |
27 | | #include <svx/svdoashp.hxx> |
28 | | #include <svl/itemset.hxx> |
29 | | #include <svl/whiter.hxx> |
30 | | #include <svx/xfillit0.hxx> |
31 | | #include <svx/xlineit0.hxx> |
32 | | #include <svx/xsflclit.hxx> |
33 | | #include <svx/xbtmpit.hxx> |
34 | | #include <svx/xflclit.hxx> |
35 | | #include <svx/svdopath.hxx> |
36 | | #include <svx/svddef.hxx> |
37 | | #include <svx/svx3ditems.hxx> |
38 | | #include <extrud3d.hxx> |
39 | | #include <svx/xflbmtit.hxx> |
40 | | #include <svx/xlnclit.hxx> |
41 | | #include <svx/sdasitm.hxx> |
42 | | #include <svx/scene3d.hxx> |
43 | | #include <com/sun/star/drawing/Position3D.hpp> |
44 | | #include <com/sun/star/drawing/Direction3D.hpp> |
45 | | #include <com/sun/star/drawing/NormalsKind.hpp> |
46 | | #include <com/sun/star/drawing/ShadeMode.hpp> |
47 | | #include <svx/sdr/properties/properties.hxx> |
48 | | #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp> |
49 | | #include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp> |
50 | | #include <com/sun/star/drawing/ProjectionMode.hpp> |
51 | | #include <basegfx/color/bcolor.hxx> |
52 | | #include <basegfx/polygon/b2dpolypolygontools.hxx> |
53 | | #include <basegfx/polygon/b3dpolygon.hxx> |
54 | | #include <basegfx/range/b2drange.hxx> |
55 | | #include <sdr/primitive2d/sdrattributecreator.hxx> |
56 | | #include <drawinglayer/attribute/sdrlineattribute.hxx> |
57 | | #include <drawinglayer/attribute/sdrlinestartendattribute.hxx> |
58 | | #include <svx/xlnwtit.hxx> |
59 | | #include <svx/xlntrit.hxx> |
60 | | #include <svx/xfltrit.hxx> |
61 | | #include <comphelper/configuration.hxx> |
62 | | |
63 | | using namespace com::sun::star; |
64 | | using namespace com::sun::star::uno; |
65 | | |
66 | | namespace { |
67 | | |
68 | | void GetOrigin( const SdrCustomShapeGeometryItem& rItem, double& rOriginX, double& rOriginY ) |
69 | 500 | { |
70 | 500 | css::drawing::EnhancedCustomShapeParameterPair aOriginParaPair; |
71 | 500 | const Any* pAny = rItem.GetPropertyValueByName( u"Extrusion"_ustr, u"Origin"_ustr ); |
72 | 500 | if ( ! ( pAny && ( *pAny >>= aOriginParaPair ) && ( aOriginParaPair.First.Value >>= rOriginX ) && ( aOriginParaPair.Second.Value >>= rOriginY ) ) ) |
73 | 46 | { |
74 | 46 | rOriginX = 0.50; |
75 | 46 | rOriginY =-0.50; |
76 | 46 | } |
77 | 500 | } |
78 | | |
79 | | void GetRotateAngle( const SdrCustomShapeGeometryItem& rItem, double& rAngleX, double& rAngleY ) |
80 | 3.19k | { |
81 | 3.19k | css::drawing::EnhancedCustomShapeParameterPair aRotateAngleParaPair; |
82 | 3.19k | const Any* pAny = rItem.GetPropertyValueByName( u"Extrusion"_ustr, u"RotateAngle"_ustr ); |
83 | 3.19k | if ( ! ( pAny && ( *pAny >>= aRotateAngleParaPair ) && ( aRotateAngleParaPair.First.Value >>= rAngleX ) && ( aRotateAngleParaPair.Second.Value >>= rAngleY ) ) ) |
84 | 3.03k | { |
85 | 3.03k | rAngleX = 0.0; |
86 | 3.03k | rAngleY = 0.0; |
87 | 3.03k | } |
88 | 3.19k | rAngleX = basegfx::deg2rad(rAngleX); |
89 | 3.19k | rAngleY = basegfx::deg2rad(rAngleY); |
90 | 3.19k | } |
91 | | |
92 | | void GetSkew( const SdrCustomShapeGeometryItem& rItem, double& rSkewAmount, double& rSkewAngle ) |
93 | 2.69k | { |
94 | 2.69k | css::drawing::EnhancedCustomShapeParameterPair aSkewParaPair; |
95 | 2.69k | const Any* pAny = rItem.GetPropertyValueByName( u"Extrusion"_ustr, u"Skew"_ustr ); |
96 | 2.69k | if ( ! ( pAny && ( *pAny >>= aSkewParaPair ) && ( aSkewParaPair.First.Value >>= rSkewAmount ) && ( aSkewParaPair.Second.Value >>= rSkewAngle ) ) ) |
97 | 0 | { |
98 | 0 | rSkewAmount = 50; |
99 | | // ODF default is 45, but older ODF documents expect -135 as default. For intermediate |
100 | | // solution see tdf#141301 and tdf#141127. |
101 | | // MS Office default -135 is set in msdffimp.cxx to make import independent from setting here. |
102 | 0 | rSkewAngle = -135; |
103 | 0 | } |
104 | 2.69k | rSkewAngle = basegfx::deg2rad(rSkewAngle); |
105 | 2.69k | } |
106 | | |
107 | | void GetExtrusionDepth( const SdrCustomShapeGeometryItem& rItem, const double* pMap, double& rBackwardDepth, double& rForwardDepth ) |
108 | 3.19k | { |
109 | 3.19k | css::drawing::EnhancedCustomShapeParameterPair aDepthParaPair; |
110 | 3.19k | double fDepth = 0, fFraction = 0; |
111 | 3.19k | const Any* pAny = rItem.GetPropertyValueByName( u"Extrusion"_ustr, u"Depth"_ustr ); |
112 | 3.19k | if ( pAny && ( *pAny >>= aDepthParaPair ) && ( aDepthParaPair.First.Value >>= fDepth ) && ( aDepthParaPair.Second.Value >>= fFraction ) ) |
113 | 685 | { |
114 | 685 | rForwardDepth = fDepth * fFraction; |
115 | 685 | rBackwardDepth = fDepth - rForwardDepth; |
116 | 685 | } |
117 | 2.50k | else |
118 | 2.50k | { |
119 | 2.50k | rBackwardDepth = 1270; |
120 | 2.50k | rForwardDepth = 0; |
121 | 2.50k | } |
122 | 3.19k | if ( pMap ) |
123 | 0 | { |
124 | 0 | double fMap = *pMap; |
125 | 0 | rBackwardDepth *= fMap; |
126 | 0 | rForwardDepth *= fMap; |
127 | 0 | } |
128 | 3.19k | } |
129 | | |
130 | | double GetDouble( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName, double fDefault ) |
131 | 19.1k | { |
132 | 19.1k | double fRetValue = fDefault; |
133 | 19.1k | const Any* pAny = rItem.GetPropertyValueByName( u"Extrusion"_ustr, rPropertyName ); |
134 | 19.1k | if ( pAny ) |
135 | 13.1k | *pAny >>= fRetValue; |
136 | 19.1k | return fRetValue; |
137 | 19.1k | } |
138 | | |
139 | | drawing::ShadeMode GetShadeMode( const SdrCustomShapeGeometryItem& rItem, const drawing::ShadeMode eDefault ) |
140 | 3.19k | { |
141 | 3.19k | drawing::ShadeMode eRet( eDefault ); |
142 | 3.19k | const Any* pAny = rItem.GetPropertyValueByName( u"Extrusion"_ustr, u"ShadeMode"_ustr ); |
143 | 3.19k | if ( pAny ) |
144 | 135 | { |
145 | 135 | if (!(*pAny >>= eRet)) |
146 | 0 | { |
147 | 0 | sal_Int32 nEnum = 0; |
148 | 0 | if(*pAny >>= nEnum) |
149 | 0 | { |
150 | 0 | eRet = static_cast<drawing::ShadeMode>(nEnum); |
151 | 0 | } |
152 | 0 | } |
153 | 135 | } |
154 | 3.19k | return eRet; |
155 | 3.19k | } |
156 | | |
157 | | bool GetBool( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName, const bool bDefault ) |
158 | 60.9k | { |
159 | 60.9k | bool bRetValue = bDefault; |
160 | 60.9k | const Any* pAny = rItem.GetPropertyValueByName( u"Extrusion"_ustr, rPropertyName ); |
161 | 60.9k | if ( pAny ) |
162 | 19.1k | *pAny >>= bRetValue; |
163 | 60.9k | return bRetValue; |
164 | 60.9k | } |
165 | | |
166 | | drawing::Position3D GetPosition3D( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName, |
167 | | const drawing::Position3D& rDefault, const double* pMap ) |
168 | 500 | { |
169 | 500 | drawing::Position3D aRetValue( rDefault ); |
170 | 500 | const Any* pAny = rItem.GetPropertyValueByName( u"Extrusion"_ustr, rPropertyName ); |
171 | 500 | if ( pAny ) |
172 | 500 | *pAny >>= aRetValue; |
173 | 500 | if ( pMap ) |
174 | 0 | { |
175 | 0 | aRetValue.PositionX *= *pMap; |
176 | 0 | aRetValue.PositionY *= *pMap; |
177 | 0 | aRetValue.PositionZ *= *pMap; |
178 | 0 | } |
179 | 500 | return aRetValue; |
180 | 500 | } |
181 | | |
182 | | drawing::Direction3D GetDirection3D( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName, const drawing::Direction3D& rDefault ) |
183 | 9.57k | { |
184 | 9.57k | drawing::Direction3D aRetValue( rDefault ); |
185 | 9.57k | const Any* pAny = rItem.GetPropertyValueByName( u"Extrusion"_ustr, rPropertyName ); |
186 | 9.57k | if ( pAny ) |
187 | 952 | *pAny >>= aRetValue; |
188 | 9.57k | return aRetValue; |
189 | 9.57k | } |
190 | | |
191 | | sal_Int16 GetMetalType(const SdrCustomShapeGeometryItem& rItem, const sal_Int16 eDefault) |
192 | 3.19k | { |
193 | 3.19k | sal_Int16 aRetValue(eDefault); |
194 | 3.19k | const Any* pAny = rItem.GetPropertyValueByName(u"Extrusion"_ustr, u"MetalType"_ustr); |
195 | 3.19k | if (pAny) |
196 | 3.19k | *pAny >>= aRetValue; |
197 | 3.19k | return aRetValue; |
198 | 3.19k | } |
199 | | |
200 | | // Calculates the light directions for the additional lights, which are used to emulate soft |
201 | | // lights of MS Office. Method needs to be documented in the Wiki |
202 | | // https://wiki.documentfoundation.org/Development/ODF_Implementer_Notes in part |
203 | | // List_of_LibreOffice_ODF_implementation-defined_items |
204 | | // The method expects vector rLight to be normalized and results normalized vectors. |
205 | | void lcl_SoftLightsDirection(const basegfx::B3DVector& rLight, basegfx::B3DVector& rSoftUp, |
206 | | basegfx::B3DVector& rSoftDown, basegfx::B3DVector& rSoftRight, |
207 | | basegfx::B3DVector& rSoftLeft) |
208 | 3.16k | { |
209 | 3.16k | constexpr double fAngle = basegfx::deg2rad(60); // angle between regular light and soft light |
210 | | |
211 | | // We first create directions around (0|0|1) and then rotate them to the light position. |
212 | 3.16k | rSoftUp = basegfx::B3DVector(0.0, sin(fAngle), cos(fAngle)); |
213 | 3.16k | rSoftDown = basegfx::B3DVector(0.0, -sin(fAngle), cos(fAngle)); |
214 | 3.16k | rSoftRight = basegfx::B3DVector(sin(fAngle), 0.0, cos(fAngle)); |
215 | 3.16k | rSoftLeft = basegfx::B3DVector(-sin(fAngle), 0.0, cos(fAngle)); |
216 | | |
217 | 3.16k | basegfx::B3DHomMatrix aRotateMat; |
218 | 3.16k | aRotateMat.rotate(0.0, 0.0, M_PI_4); |
219 | 3.16k | if (rLight.getX() == 0.0 && rLight.getZ() == 0.0) |
220 | 0 | { |
221 | | // Special case with light from top or bottom |
222 | 0 | if (rLight.getY() >= 0.0) |
223 | 0 | aRotateMat.rotate(-M_PI_2, 0.0, 0.0); |
224 | 0 | else |
225 | 0 | aRotateMat.rotate(M_PI_2, 0.0, 0.0); |
226 | 0 | } |
227 | 3.16k | else |
228 | 3.16k | { |
229 | | // Azimuth from z-axis to x-axis. (0|0|1) to (1|0|0) is 90deg. |
230 | 3.16k | double fAzimuth = atan2(rLight.getX(), rLight.getZ()); |
231 | | // Elevation from xz-plane to y-axis. (0|0|1) to (0|1|0) is 90deg. |
232 | 3.16k | double fElevation = atan2(rLight.getY(), std::hypot(rLight.getX(), rLight.getZ())); |
233 | 3.16k | aRotateMat.rotate(-fElevation, fAzimuth, 0.0); |
234 | 3.16k | } |
235 | | |
236 | 3.16k | rSoftUp = aRotateMat * rSoftUp; |
237 | 3.16k | rSoftDown = aRotateMat * rSoftDown; |
238 | 3.16k | rSoftRight = aRotateMat * rSoftRight; |
239 | 3.16k | rSoftLeft = aRotateMat * rSoftLeft; |
240 | 3.16k | } |
241 | | } |
242 | | |
243 | | rtl::Reference<SdrObject> EnhancedCustomShape3d::Create3DObject( |
244 | | const SdrObject* pShape2d, |
245 | | const SdrObjCustomShape& rSdrObjCustomShape) |
246 | 44.9k | { |
247 | 44.9k | rtl::Reference<SdrObject> pRet; |
248 | 44.9k | const SdrCustomShapeGeometryItem& rGeometryItem(rSdrObjCustomShape.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY)); |
249 | 44.9k | double fMap(1.0), *pMap = nullptr; |
250 | | |
251 | 44.9k | if ( rSdrObjCustomShape.getSdrModelFromSdrObject().GetScaleUnit() != MapUnit::Map100thMM ) |
252 | 0 | { |
253 | 0 | DBG_ASSERT( rSdrObjCustomShape.getSdrModelFromSdrObject().GetScaleUnit() == MapUnit::MapTwip, "EnhancedCustomShape3d::Current MapMode is Unsupported" ); |
254 | | // But we could use MapToO3tlUnit from <tools/UnitConversion> ... ? |
255 | 0 | fMap *= o3tl::convert(1.0, o3tl::Length::mm100, o3tl::Length::twip); |
256 | 0 | pMap = &fMap; |
257 | 0 | } |
258 | | |
259 | 44.9k | if ( GetBool( rGeometryItem, u"Extrusion"_ustr, false ) ) |
260 | 3.19k | { |
261 | 3.19k | bool bIsMirroredX(rSdrObjCustomShape.IsMirroredX()); |
262 | 3.19k | bool bIsMirroredY(rSdrObjCustomShape.IsMirroredY()); |
263 | 3.19k | tools::Rectangle aSnapRect(rSdrObjCustomShape.GetLogicRect()); |
264 | 3.19k | Degree100 nObjectRotation(rSdrObjCustomShape.GetRotateAngle()); |
265 | 3.19k | if ( nObjectRotation ) |
266 | 1.14k | { |
267 | 1.14k | double a = toRadians(36000_deg100 - nObjectRotation); |
268 | 1.14k | tools::Long dx = aSnapRect.Right() - aSnapRect.Left(); |
269 | 1.14k | tools::Long dy = aSnapRect.Bottom()- aSnapRect.Top(); |
270 | 1.14k | Point aP( aSnapRect.TopLeft() ); |
271 | 1.14k | RotatePoint( aP, rSdrObjCustomShape.GetSnapRect().Center(), sin( a ), cos( a ) ); |
272 | 1.14k | aSnapRect.SetLeft( aP.X() ); |
273 | 1.14k | aSnapRect.SetTop( aP.Y() ); |
274 | 1.14k | aSnapRect.SetRight( aSnapRect.Left() + dx ); |
275 | 1.14k | aSnapRect.SetBottom( aSnapRect.Top() + dy ); |
276 | 1.14k | } |
277 | 3.19k | Point aCenter( aSnapRect.Center() ); |
278 | | |
279 | 3.19k | SfxItemSet aSet( rSdrObjCustomShape.GetMergedItemSet() ); |
280 | | |
281 | | // tdf#146360 If the ItemSet of the source SdrObject has a parent |
282 | | // (which means it has a StyleSheet), we need to do some old-style |
283 | | // 'BurnInStyleSheetAttributes' action. |
284 | | // That means to set all Items which are set in the StyleSheet |
285 | | // directly in the ItemSet. |
286 | | // This is okay here since the 3D SdrObjects created are |
287 | | // placeholders that get rendered, but never reach the |
288 | | // surface/the user. If attributes for the source SdrObject |
289 | | // change, these will be recreated. |
290 | | // The problem is that while "aSet" still has a ptr to the style's |
291 | | // ItemSet, this gets lost at the ItemSet of the SdrObject when |
292 | | // an ItemSet gets set at the 3D SdrObject, like in diverse |
293 | | // SetMergedItemSet calls below. This leads to fetching the wrong |
294 | | // (default) FillBitmap in the calls p3DObj->GetMergedItem below |
295 | | // (which is 32x32 white, that's what you see without the fix). |
296 | | // This could also be fixed (tried it) by either |
297 | | // - using rSdrObjCustomShape.GetMergedItem |
298 | | // - setting the StyleSheet at 3D SdrObjects ASAP (done at caller) |
299 | | // but both solutions contain the risk to not find all places, so |
300 | | // it's just more safe to merge the StyleSheet attributes to the |
301 | | // ItemSet used for the whole creation. |
302 | 3.19k | if(nullptr != aSet.GetParent()) |
303 | 0 | { |
304 | 0 | SfxWhichIter aIter(aSet); |
305 | 0 | sal_uInt16 nWhich(aIter.FirstWhich()); |
306 | 0 | const SfxPoolItem *pItem(nullptr); |
307 | |
|
308 | 0 | while(nWhich) |
309 | 0 | { |
310 | | // this may look at 1st look like doing nothing, but it converts |
311 | | // items set in parent/style to SfxItemState::SET items in the |
312 | | // ItemSet (see AttributeProperties::ForceStyleToHardAttributes()) |
313 | 0 | if(SfxItemState::SET == aSet.GetItemState(nWhich, true, &pItem)) |
314 | 0 | { |
315 | 0 | aSet.Put(*pItem); |
316 | 0 | } |
317 | |
|
318 | 0 | nWhich = aIter.NextWhich(); |
319 | 0 | } |
320 | |
|
321 | 0 | aSet.SetParent(nullptr); |
322 | 0 | } |
323 | | |
324 | | //SJ: vertical writing is not required, by removing this item no outliner is created |
325 | 3.19k | aSet.ClearItem( SDRATTR_TEXTDIRECTION ); |
326 | | |
327 | | // #i105323# For 3D AutoShapes, the shadow attribute has to be applied to each |
328 | | // created visualisation helper model shape individually. The shadow itself |
329 | | // will then be rendered from the 3D renderer correctly for the whole 3D scene |
330 | | // (and thus behind all objects of which the visualisation may be built). So, |
331 | | // do NOT remove it from the ItemSet here. |
332 | | // aSet.ClearItem(SDRATTR_SHADOW); |
333 | | |
334 | 3.19k | std::vector< E3dCompoundObject* > aPlaceholderObjectList; |
335 | | |
336 | 3.19k | double fExtrusionBackward, fExtrusionForward; |
337 | 3.19k | GetExtrusionDepth( rGeometryItem, pMap, fExtrusionBackward, fExtrusionForward ); |
338 | 3.19k | double fDepth = fExtrusionBackward + fExtrusionForward; |
339 | 3.19k | if ( fDepth < 1.0 ) |
340 | 7 | fDepth = 1.0; |
341 | | |
342 | 3.19k | drawing::ProjectionMode eProjectionMode( drawing::ProjectionMode_PARALLEL ); |
343 | 3.19k | const Any* pAny = rGeometryItem.GetPropertyValueByName( u"Extrusion"_ustr, u"ProjectionMode"_ustr ); |
344 | 3.19k | if (pAny) |
345 | 3.19k | { |
346 | 3.19k | if(!(*pAny >>= eProjectionMode)) |
347 | 0 | { |
348 | 0 | sal_Int32 nEnum = 0; |
349 | 0 | if(*pAny >>= nEnum) |
350 | 0 | { |
351 | 0 | eProjectionMode = static_cast<drawing::ProjectionMode>(nEnum); |
352 | 0 | } |
353 | 0 | } |
354 | 3.19k | } |
355 | | // pShape2d Convert in scenes which include 3D Objects |
356 | 3.19k | E3dDefaultAttributes a3DDefaultAttr; |
357 | 3.19k | a3DDefaultAttr.SetDefaultLatheCharacterMode( true ); |
358 | 3.19k | a3DDefaultAttr.SetDefaultExtrudeCharacterMode( true ); |
359 | | |
360 | 3.19k | rtl::Reference<E3dScene> pScene = new E3dScene(rSdrObjCustomShape.getSdrModelFromSdrObject()); |
361 | | |
362 | 3.19k | bool bSceneHasObjects ( false ); |
363 | 3.19k | bool bUseTwoFillStyles( false ); |
364 | | |
365 | 3.19k | drawing::ShadeMode eShadeMode( GetShadeMode( rGeometryItem, drawing::ShadeMode_FLAT ) ); |
366 | 3.19k | bool bUseExtrusionColor = GetBool( rGeometryItem, u"Color"_ustr, false ); |
367 | | |
368 | 3.19k | drawing::FillStyle eFillStyle( aSet.Get(XATTR_FILLSTYLE).GetValue() ); |
369 | 3.19k | pScene->GetProperties().SetObjectItem( Svx3DShadeModeItem(static_cast<sal_uInt16>(eShadeMode))); |
370 | 3.19k | aSet.Put( makeSvx3DPercentDiagonalItem( 0 ) ); |
371 | 3.19k | aSet.Put( Svx3DTextureModeItem( 1 ) ); |
372 | | // SPECIFIC needed for ShadeMode_SMOOTH and ShadeMode_PHONG, otherwise FLAT is faster. |
373 | 3.19k | if (eShadeMode == drawing::ShadeMode_SMOOTH || eShadeMode == drawing::ShadeMode_PHONG) |
374 | 0 | aSet.Put( Svx3DNormalsKindItem(static_cast<sal_uInt16>(drawing::NormalsKind_SPECIFIC))); |
375 | 3.19k | else |
376 | 3.19k | aSet.Put( Svx3DNormalsKindItem(static_cast<sal_uInt16>(drawing::NormalsKind_FLAT))); |
377 | | |
378 | 3.19k | if ( eShadeMode == drawing::ShadeMode_DRAFT ) |
379 | 0 | { |
380 | 0 | aSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) ); |
381 | 0 | aSet.Put( XFillStyleItem ( drawing::FillStyle_NONE ) ); |
382 | 0 | aSet.Put( makeSvx3DDoubleSidedItem( true ) ); |
383 | 0 | } |
384 | 3.19k | else |
385 | 3.19k | { |
386 | 3.19k | aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) ); |
387 | 3.19k | if ( eFillStyle == drawing::FillStyle_NONE ) |
388 | 1.45k | aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) ); |
389 | 1.73k | else if ( ( eFillStyle == drawing::FillStyle_BITMAP ) || ( eFillStyle == drawing::FillStyle_GRADIENT ) || bUseExtrusionColor ) |
390 | 683 | bUseTwoFillStyles = true; |
391 | | |
392 | | // If shapes are mirrored once (mirroring two times correct geometry again) |
393 | | // double-sided at the object and two-sided-lighting at the scene need to be set. |
394 | | |
395 | | // #i122777# Also use double sided for two fill styles since there several 3d objects get |
396 | | // created with a depth of 0; one of them is the backside which needs double-sided to |
397 | | // get visible |
398 | 3.19k | if(bUseTwoFillStyles || (bIsMirroredX && !bIsMirroredY) || (!bIsMirroredX && bIsMirroredY)) |
399 | 2.06k | { |
400 | 2.06k | aSet.Put( makeSvx3DDoubleSidedItem( true ) ); |
401 | 2.06k | pScene->GetProperties().SetObjectItem( makeSvx3DTwoSidedLightingItem( true ) ); |
402 | 2.06k | } |
403 | 3.19k | } |
404 | | |
405 | 3.19k | tools::Rectangle aBoundRect2d; |
406 | 3.19k | basegfx::B2DPolyPolygon aTotalPolyPoly; |
407 | 3.19k | SdrObjListIter aIter( *pShape2d, SdrIterMode::DeepNoGroups ); |
408 | 3.19k | const bool bMultipleSubObjects(aIter.Count() > 1); |
409 | 3.19k | const bool bFuzzing(comphelper::IsFuzzing()); |
410 | | |
411 | 11.3k | while( aIter.IsMore() ) |
412 | 8.11k | { |
413 | 8.11k | const SdrObject* pNext = aIter.Next(); |
414 | 8.11k | bool bIsPlaceholderObject = (pNext->GetMergedItem( XATTR_FILLSTYLE ).GetValue() == drawing::FillStyle_NONE ) |
415 | 8.11k | && (pNext->GetMergedItem( XATTR_LINESTYLE ).GetValue() == drawing::LineStyle_NONE ); |
416 | 8.11k | basegfx::B2DPolyPolygon aPolyPoly; |
417 | 8.11k | SfxItemSet aLocalSet(aSet); |
418 | 8.11k | drawing::FillStyle aLocalFillStyle(eFillStyle); |
419 | | |
420 | 8.11k | if ( auto pPathObj = dynamic_cast<const SdrPathObj*>(pNext) ) |
421 | 8.10k | { |
422 | 8.10k | const SfxItemSet& rSet = pNext->GetMergedItemSet(); |
423 | 8.10k | bool bNeedToConvertToContour(false); |
424 | | |
425 | | // do conversion only for single line objects; for all others a fill and a |
426 | | // line object get created. When we have fill, we want no line. That line has |
427 | | // always been there, but since it was never converted to contour, it kept |
428 | | // invisible (all this 'hidden' logic should be migrated to primitives). |
429 | 8.10k | if(!bMultipleSubObjects) |
430 | 1.77k | { |
431 | 1.77k | const drawing::FillStyle eStyle(rSet.Get(XATTR_FILLSTYLE).GetValue()); |
432 | | |
433 | 1.77k | if(drawing::FillStyle_NONE == eStyle) |
434 | 1.46k | { |
435 | 1.46k | const drawinglayer::attribute::SdrLineAttribute aLine( |
436 | 1.46k | drawinglayer::primitive2d::createNewSdrLineAttribute(rSet)); |
437 | | |
438 | 1.46k | bNeedToConvertToContour = (0.0 < aLine.getWidth() || 0.0 != aLine.getFullDotDashLen()); |
439 | | |
440 | 1.46k | if(!bNeedToConvertToContour && !aLine.isDefault()) |
441 | 0 | { |
442 | 0 | const drawinglayer::attribute::SdrLineStartEndAttribute aLineStartEnd( |
443 | 0 | drawinglayer::primitive2d::createNewSdrLineStartEndAttribute(rSet, aLine.getWidth())); |
444 | |
|
445 | 0 | if((aLineStartEnd.getStartWidth() && aLineStartEnd.isStartActive()) |
446 | 0 | || (aLineStartEnd.getEndWidth() && aLineStartEnd.isEndActive())) |
447 | 0 | { |
448 | 0 | bNeedToConvertToContour = true; |
449 | 0 | } |
450 | 0 | } |
451 | 1.46k | } |
452 | 1.77k | } |
453 | | |
454 | 8.10k | if (bNeedToConvertToContour && !bFuzzing) |
455 | 0 | { |
456 | 0 | rtl::Reference<SdrObject> pNewObj = pNext->ConvertToContourObj(const_cast< SdrObject* >(pNext)); |
457 | 0 | SdrPathObj* pNewPathObj = dynamic_cast< SdrPathObj* >(pNewObj.get()); |
458 | |
|
459 | 0 | if(pNewPathObj) |
460 | 0 | { |
461 | 0 | aPolyPoly = pNewPathObj->GetPathPoly(); |
462 | |
|
463 | 0 | if(aPolyPoly.isClosed()) |
464 | 0 | { |
465 | | // correct item properties from line to fill style |
466 | 0 | if(eShadeMode == drawing::ShadeMode_DRAFT) |
467 | 0 | { |
468 | | // for draft, create wireframe with fixed line width |
469 | 0 | aLocalSet.Put(XLineStyleItem(drawing::LineStyle_SOLID)); |
470 | 0 | aLocalSet.Put(XLineWidthItem(40)); |
471 | 0 | aLocalFillStyle = drawing::FillStyle_NONE; |
472 | 0 | } |
473 | 0 | else |
474 | 0 | { |
475 | | // switch from line to fill, copy line attr to fill attr (color, transparence) |
476 | 0 | aLocalSet.Put(XLineWidthItem(0)); |
477 | 0 | aLocalSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); |
478 | 0 | aLocalSet.Put(XFillColorItem(OUString(), aLocalSet.Get(XATTR_LINECOLOR).GetColorValue())); |
479 | 0 | aLocalSet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); |
480 | 0 | aLocalSet.Put(XFillTransparenceItem(aLocalSet.Get(XATTR_LINETRANSPARENCE).GetValue())); |
481 | 0 | aLocalFillStyle = drawing::FillStyle_SOLID; |
482 | 0 | } |
483 | 0 | } |
484 | 0 | else |
485 | 0 | { |
486 | | // correct item properties to hairlines |
487 | 0 | aLocalSet.Put(XLineWidthItem(0)); |
488 | 0 | aLocalSet.Put(XLineStyleItem(drawing::LineStyle_SOLID)); |
489 | 0 | } |
490 | 0 | } |
491 | 0 | } |
492 | 8.10k | else |
493 | 8.10k | { |
494 | 8.10k | aPolyPoly = pPathObj->GetPathPoly(); |
495 | 8.10k | } |
496 | 8.10k | } |
497 | 1 | else |
498 | 1 | { |
499 | 1 | rtl::Reference<SdrObject> pNewObj = pNext->ConvertToPolyObj( false, false ); |
500 | 1 | SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pNewObj.get() ); |
501 | 1 | if ( pPath ) |
502 | 1 | aPolyPoly = pPath->GetPathPoly(); |
503 | 1 | } |
504 | | |
505 | 8.11k | if( aPolyPoly.count() ) |
506 | 8.11k | { |
507 | 8.11k | if(aPolyPoly.areControlPointsUsed()) |
508 | 2.52k | { |
509 | 2.52k | aPolyPoly = basegfx::utils::adaptiveSubdivideByAngle(aPolyPoly); |
510 | 2.52k | } |
511 | | |
512 | 8.11k | const basegfx::B2DRange aTempRange(basegfx::utils::getRange(aPolyPoly)); |
513 | 8.11k | const tools::Rectangle aBoundRect(basegfx::fround<tools::Long>(aTempRange.getMinX()), basegfx::fround<tools::Long>(aTempRange.getMinY()), basegfx::fround<tools::Long>(aTempRange.getMaxX()), basegfx::fround<tools::Long>(aTempRange.getMaxY())); |
514 | 8.11k | aTotalPolyPoly.append(aPolyPoly); |
515 | 8.11k | aBoundRect2d.Union( aBoundRect ); |
516 | | |
517 | | // #i122777# depth 0 is okay for planes when using double-sided |
518 | 8.11k | rtl::Reference<E3dCompoundObject> p3DObj = new E3dExtrudeObj( |
519 | 8.11k | rSdrObjCustomShape.getSdrModelFromSdrObject(), |
520 | 8.11k | a3DDefaultAttr, |
521 | 8.11k | aPolyPoly, |
522 | 8.11k | bUseTwoFillStyles ? 0 : fDepth ); |
523 | | |
524 | 8.11k | p3DObj->NbcSetLayer( pShape2d->GetLayer() ); |
525 | 8.11k | p3DObj->SetMergedItemSet( aLocalSet ); |
526 | | |
527 | 8.11k | if ( bIsPlaceholderObject ) |
528 | 1.02k | aPlaceholderObjectList.push_back( p3DObj.get() ); |
529 | 7.09k | else if ( bUseTwoFillStyles ) |
530 | 1.15k | { |
531 | 1.15k | BitmapEx aFillBmp; |
532 | 1.15k | bool bFillBmpTile = p3DObj->GetMergedItem( XATTR_FILLBMP_TILE ).GetValue(); |
533 | 1.15k | if ( bFillBmpTile ) |
534 | 1.01k | { |
535 | 1.01k | const XFillBitmapItem& rBmpItm = p3DObj->GetMergedItem(XATTR_FILLBITMAP); |
536 | 1.01k | aFillBmp = rBmpItm.GetGraphicObject().GetGraphic().GetBitmapEx(); |
537 | | |
538 | | // #i122777# old adaptation of FillStyle bitmap size to 5-times the original size; this is not needed |
539 | | // anymore and was used in old times to male the fill look better when converting to 3D. Removed |
540 | | // from regular 3D objects for some time, also needs to be removed from CustomShapes |
541 | | |
542 | | //Size aLogicalSize = aFillBmp.GetPrefSize(); |
543 | | //if ( aFillBmp.GetPrefMapMode() == MapUnit::MapPixel ) |
544 | | // aLogicalSize = Application::GetDefaultDevice()->PixelToLogic( aLogicalSize, MapUnit::Map100thMM ); |
545 | | //else |
546 | | // aLogicalSize = OutputDevice::LogicToLogic( aLogicalSize, aFillBmp.GetPrefMapMode(), MapUnit::Map100thMM ); |
547 | | //aLogicalSize.Width() *= 5; ;// :-( nice scaling, look at engine3d/obj3d.cxx |
548 | | //aLogicalSize.Height() *= 5; |
549 | | //aFillBmp.SetPrefSize( aLogicalSize ); |
550 | | //aFillBmp.SetPrefMapMode( MapUnit::Map100thMM ); |
551 | | //p3DObj->SetMergedItem(XFillBitmapItem(String(), Graphic(aFillBmp))); |
552 | 1.01k | } |
553 | 132 | else |
554 | 132 | { |
555 | 132 | if ( aSnapRect != aBoundRect && aSnapRect.GetWidth() > 0 && aSnapRect.GetHeight() > 0) |
556 | 132 | { |
557 | 132 | const XFillBitmapItem& rBmpItm = p3DObj->GetMergedItem(XATTR_FILLBITMAP); |
558 | 132 | aFillBmp = rBmpItm.GetGraphicObject().GetGraphic().GetBitmapEx(); |
559 | 132 | Size aBmpSize( aFillBmp.GetSizePixel() ); |
560 | 132 | double fXScale = static_cast<double>(aBoundRect.GetWidth()) / static_cast<double>(aSnapRect.GetWidth()); |
561 | 132 | double fYScale = static_cast<double>(aBoundRect.GetHeight()) / static_cast<double>(aSnapRect.GetHeight()); |
562 | | |
563 | 132 | Point aPt( static_cast<sal_Int32>( static_cast<double>( aBoundRect.Left() - aSnapRect.Left() )* static_cast<double>(aBmpSize.Width()) / static_cast<double>(aSnapRect.GetWidth()) ), |
564 | 132 | static_cast<sal_Int32>( static_cast<double>( aBoundRect.Top() - aSnapRect.Top() ) * static_cast<double>(aBmpSize.Height()) / static_cast<double>(aSnapRect.GetHeight()) ) ); |
565 | 132 | Size aSize( static_cast<sal_Int32>( aBmpSize.Width() * fXScale ), |
566 | 132 | static_cast<sal_Int32>( aBmpSize.Height() * fYScale ) ); |
567 | 132 | tools::Rectangle aCropRect( aPt, aSize ); |
568 | 132 | aFillBmp.Crop( aCropRect ); |
569 | 132 | p3DObj->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aFillBmp))); |
570 | 132 | } |
571 | 132 | } |
572 | 1.15k | pScene->InsertObject( p3DObj.get() ); |
573 | 1.15k | p3DObj = new E3dExtrudeObj( |
574 | 1.15k | rSdrObjCustomShape.getSdrModelFromSdrObject(), |
575 | 1.15k | a3DDefaultAttr, |
576 | 1.15k | aPolyPoly, |
577 | 1.15k | fDepth); |
578 | 1.15k | p3DObj->NbcSetLayer( pShape2d->GetLayer() ); |
579 | 1.15k | p3DObj->SetMergedItemSet( aLocalSet ); |
580 | 1.15k | if ( bUseExtrusionColor ) |
581 | 953 | p3DObj->SetMergedItem( XFillColorItem( u""_ustr, rSdrObjCustomShape.GetMergedItem( XATTR_SECONDARYFILLCOLOR ).GetColorValue() ) ); |
582 | 1.15k | p3DObj->SetMergedItem( XFillStyleItem( drawing::FillStyle_SOLID ) ); |
583 | 1.15k | p3DObj->SetMergedItem( Svx3DCloseFrontItem( false ) ); |
584 | 1.15k | p3DObj->SetMergedItem( Svx3DCloseBackItem( false ) ); |
585 | 1.15k | pScene->InsertObject( p3DObj.get() ); |
586 | | |
587 | | // #i122777# depth 0 is okay for planes when using double-sided |
588 | 1.15k | p3DObj = new E3dExtrudeObj( |
589 | 1.15k | rSdrObjCustomShape.getSdrModelFromSdrObject(), |
590 | 1.15k | a3DDefaultAttr, |
591 | 1.15k | std::move(aPolyPoly), |
592 | 1.15k | 0); |
593 | | |
594 | 1.15k | p3DObj->NbcSetLayer( pShape2d->GetLayer() ); |
595 | 1.15k | p3DObj->SetMergedItemSet( aLocalSet ); |
596 | | |
597 | 1.15k | basegfx::B3DHomMatrix aFrontTransform( p3DObj->GetTransform() ); |
598 | 1.15k | aFrontTransform.translate( 0.0, 0.0, fDepth ); |
599 | 1.15k | p3DObj->NbcSetTransform( aFrontTransform ); |
600 | | |
601 | 1.15k | if ( ( aLocalFillStyle == drawing::FillStyle_BITMAP ) && !aFillBmp.IsEmpty() ) |
602 | 134 | { |
603 | 134 | p3DObj->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aFillBmp))); |
604 | 134 | } |
605 | 1.15k | } |
606 | 5.94k | else if ( aLocalFillStyle == drawing::FillStyle_NONE ) |
607 | 2.08k | { |
608 | 2.08k | const XLineColorItem& rLineColor = p3DObj->GetMergedItem( XATTR_LINECOLOR ); |
609 | 2.08k | p3DObj->SetMergedItem( XFillColorItem( u""_ustr, rLineColor.GetColorValue() ) ); |
610 | 2.08k | p3DObj->SetMergedItem( makeSvx3DDoubleSidedItem( true ) ); |
611 | 2.08k | p3DObj->SetMergedItem( Svx3DCloseFrontItem( false ) ); |
612 | 2.08k | p3DObj->SetMergedItem( Svx3DCloseBackItem( false ) ); |
613 | 2.08k | } |
614 | 8.11k | pScene->InsertObject( p3DObj.get() ); |
615 | 8.11k | bSceneHasObjects = true; |
616 | 8.11k | } |
617 | 8.11k | } |
618 | | |
619 | 3.19k | if ( bSceneHasObjects ) // is the SdrObject properly converted |
620 | 3.19k | { |
621 | | // then we can change the return value |
622 | 3.19k | pRet = pScene; |
623 | | |
624 | | // Camera settings, Perspective ... |
625 | 3.19k | Camera3D rCamera = pScene->GetCamera(); |
626 | 3.19k | pScene->NbcSetSnapRect( aSnapRect ); |
627 | | |
628 | | // InitScene replacement |
629 | 3.19k | double fW = aBoundRect2d.getOpenWidth(); |
630 | 3.19k | double fH = aBoundRect2d.getOpenHeight(); |
631 | 3.19k | rCamera.SetAutoAdjustProjection( false ); |
632 | 3.19k | rCamera.SetViewWindow( -fW / 2, - fH / 2, fW, fH); |
633 | 3.19k | basegfx::B3DPoint aLookAt( 0.0, 0.0, 0.0 ); |
634 | 3.19k | basegfx::B3DPoint aCamPos( 0.0, 0.0, 100.0 ); |
635 | 3.19k | rCamera.SetPosAndLookAt( aCamPos, aLookAt ); |
636 | 3.19k | rCamera.SetFocalLength( 1.0 ); |
637 | 3.19k | ProjectionType eProjectionType( eProjectionMode == drawing::ProjectionMode_PARALLEL ? ProjectionType::Parallel : ProjectionType::Perspective ); |
638 | 3.19k | rCamera.SetProjection( eProjectionType ); |
639 | 3.19k | pScene->SetCamera( rCamera ); |
640 | 3.19k | pScene->SetBoundAndSnapRectsDirty(); |
641 | | |
642 | 3.19k | basegfx::B3DHomMatrix aNewTransform( pScene->GetTransform() ); |
643 | 3.19k | basegfx::B2DHomMatrix aPolyPolyTransform; |
644 | | // Apply flip and z-rotation to scene transformation (y up). At same time transform |
645 | | // aTotalPolyPoly (y down) which will be used for 2D boundRect of shape having 2D |
646 | | // transformations applied. |
647 | | |
648 | | // API values use shape center as origin. Move scene so, that shape center is origin. |
649 | 3.19k | aNewTransform.translate( -aCenter.X(), aCenter.Y(), -fExtrusionBackward); |
650 | 3.19k | aPolyPolyTransform.translate(-aCenter.X(), -aCenter.Y()); |
651 | | |
652 | 3.19k | double fZRotate(basegfx::deg2rad(rSdrObjCustomShape.GetObjectRotation())); |
653 | 3.19k | if ( fZRotate != 0.0 ) |
654 | 1.14k | { |
655 | 1.14k | aNewTransform.rotate( 0.0, 0.0, fZRotate ); |
656 | 1.14k | aPolyPolyTransform.rotate(-fZRotate); |
657 | 1.14k | } |
658 | 3.19k | if ( bIsMirroredX ) |
659 | 831 | { |
660 | 831 | aNewTransform.scale( -1.0, 1, 1 ); |
661 | 831 | aPolyPolyTransform.scale(-1.0, 1); |
662 | 831 | } |
663 | 3.19k | if ( bIsMirroredY ) |
664 | 1.60k | { |
665 | 1.60k | aNewTransform.scale( 1, -1.0, 1 ); |
666 | 1.60k | aPolyPolyTransform.scale(1, -1.0); |
667 | 1.60k | } |
668 | 3.19k | aPolyPolyTransform.translate(aCenter.X(), aCenter.Y()); |
669 | 3.19k | aTotalPolyPoly.transform(aPolyPolyTransform); |
670 | | |
671 | | // x- and y-rotation have an own rotation center. x- and y-value of rotation center are |
672 | | // fractions of shape size, z-value is in Hmm in property. Shape center is (0 0 0). |
673 | | // Values in property are in custom shape extrusion space with y-axis down. |
674 | 3.19k | double fXRotate, fYRotate; |
675 | 3.19k | GetRotateAngle( rGeometryItem, fXRotate, fYRotate ); |
676 | 3.19k | drawing::Direction3D aRotationCenterDefault( 0, 0, 0 ); |
677 | 3.19k | drawing::Direction3D aRotationCenter( GetDirection3D( rGeometryItem, u"RotationCenter"_ustr, aRotationCenterDefault ) ); |
678 | 3.19k | aRotationCenter.DirectionX *= aSnapRect.getOpenWidth(); |
679 | 3.19k | aRotationCenter.DirectionY *= aSnapRect.getOpenHeight(); |
680 | 3.19k | if (pMap) |
681 | 0 | { |
682 | 0 | aRotationCenter.DirectionZ *= *pMap; |
683 | 0 | } |
684 | 3.19k | aNewTransform.translate( -aRotationCenter.DirectionX, aRotationCenter.DirectionY, -aRotationCenter.DirectionZ ); |
685 | 3.19k | if( fYRotate != 0.0 ) |
686 | 158 | aNewTransform.rotate( 0.0, -fYRotate, 0.0 ); |
687 | 3.19k | if( fXRotate != 0.0 ) |
688 | 145 | aNewTransform.rotate( -fXRotate, 0.0, 0.0 ); |
689 | 3.19k | aNewTransform.translate(aRotationCenter.DirectionX, -aRotationCenter.DirectionY, aRotationCenter.DirectionZ); |
690 | | |
691 | | // oblique parallel projection is done by shearing the object, not by moving the camera |
692 | 3.19k | if (eProjectionMode == drawing::ProjectionMode_PARALLEL) |
693 | 2.69k | { |
694 | 2.69k | double fSkew, fAlpha; |
695 | 2.69k | GetSkew( rGeometryItem, fSkew, fAlpha ); |
696 | 2.69k | if ( fSkew != 0.0 ) |
697 | 2.68k | { |
698 | 2.68k | double fInvTanBeta( fSkew / 100.0 ); |
699 | 2.68k | if(fInvTanBeta) |
700 | 2.68k | { |
701 | 2.68k | aNewTransform.shearXY( |
702 | 2.68k | fInvTanBeta * cos(fAlpha), |
703 | 2.68k | fInvTanBeta * sin(fAlpha)); |
704 | 2.68k | } |
705 | 2.68k | } |
706 | 2.69k | } |
707 | | |
708 | 3.19k | pScene->NbcSetTransform( aNewTransform ); |
709 | | |
710 | | // These values are used later again, so declare them outside the if-statement. They will |
711 | | // contain the absolute values of ViewPoint in 3D scene coordinate system, y-axis up. |
712 | 3.19k | double fViewPointX = 0; // dummy values |
713 | 3.19k | double fViewPointY = 0; |
714 | 3.19k | double fViewPointZ = 25000; |
715 | 3.19k | if (eProjectionMode == drawing::ProjectionMode_PERSPECTIVE) |
716 | 500 | { |
717 | 500 | double fOriginX, fOriginY; |
718 | | // Calculate BoundRect of shape, including flip and z-rotation, from aTotalPolyPoly. |
719 | 500 | tools::Rectangle aBoundAfter2DTransform; // aBoundAfter2DTransform has y-axis down. |
720 | 500 | basegfx::B2DRange aTotalPolyPolyRange(aTotalPolyPoly.getB2DRange()); |
721 | 500 | aBoundAfter2DTransform.SetLeft(aTotalPolyPolyRange.getMinX()); |
722 | 500 | aBoundAfter2DTransform.SetTop(aTotalPolyPolyRange.getMinY()); |
723 | 500 | aBoundAfter2DTransform.SetRight(aTotalPolyPolyRange.getMaxX()); |
724 | 500 | aBoundAfter2DTransform.SetBottom(aTotalPolyPolyRange.getMaxY()); |
725 | | |
726 | | // Property "Origin" in API is relative to bounding box of shape after 2D |
727 | | // transformations. Range is [-0.5;0.5] with center of bounding box as 0. |
728 | | // Resolve "Origin" fractions to length |
729 | 500 | GetOrigin( rGeometryItem, fOriginX, fOriginY ); |
730 | 500 | fOriginX *= aBoundAfter2DTransform.GetWidth(); |
731 | 500 | fOriginY *= aBoundAfter2DTransform.GetHeight(); |
732 | | // Resolve length to absolute value for 3D |
733 | 500 | fOriginX += aBoundAfter2DTransform.Center().X(); |
734 | 500 | fOriginY += aBoundAfter2DTransform.Center().Y(); |
735 | 500 | fOriginY = - fOriginY; |
736 | | // Scene is translated so that shape center is origin of coordinate system. |
737 | | // Translate point "Origin" too. |
738 | 500 | fOriginX -= aCenter.X(); |
739 | 500 | fOriginY -= -aCenter.Y(); |
740 | | // API ViewPoint values are relative to point "Origin" and have y-axis down. |
741 | | // ToDo: These default ViewPoint values are used as default by MS Office. But ODF |
742 | | // default is (3500, -3500, 25000), details in tdf#146192. |
743 | 500 | drawing::Position3D aViewPointDefault( 3472, -3472, 25000 ); |
744 | 500 | drawing::Position3D aViewPoint( GetPosition3D( rGeometryItem, u"ViewPoint"_ustr, aViewPointDefault, pMap ) ); |
745 | 500 | fViewPointX = aViewPoint.PositionX + fOriginX; |
746 | 500 | fViewPointY = - aViewPoint.PositionY + fOriginY; |
747 | 500 | fViewPointZ = aViewPoint.PositionZ; |
748 | 500 | } |
749 | | |
750 | | // now set correct camera position |
751 | 3.19k | if (eProjectionMode == drawing::ProjectionMode_PARALLEL) |
752 | 2.69k | { |
753 | 2.69k | basegfx::B3DPoint _aLookAt( 0.0, 0.0, 0.0 ); |
754 | 2.69k | basegfx::B3DPoint _aNewCamPos( 0.0, 0.0, 25000.0 ); |
755 | 2.69k | rCamera.SetPosAndLookAt( _aNewCamPos, _aLookAt ); |
756 | 2.69k | pScene->SetCamera( rCamera ); |
757 | 2.69k | } |
758 | 500 | else |
759 | 500 | { |
760 | 500 | basegfx::B3DPoint _aLookAt(fViewPointX, fViewPointY, 0.0); |
761 | 500 | basegfx::B3DPoint aNewCamPos(fViewPointX, fViewPointY, fViewPointZ); |
762 | 500 | rCamera.SetPosAndLookAt( aNewCamPos, _aLookAt ); |
763 | 500 | pScene->SetCamera( rCamera ); |
764 | 500 | } |
765 | | |
766 | | // NbcSetTransform has not updated the scene 2D rectangles. |
767 | | // Idea: Get a bound volume as polygon from bound rectangle of shape without 2D |
768 | | // transformations. Calculate its projection to the XY-plane. Then calculate the bounding |
769 | | // rectangle of the projection and convert this rectangle back to absolute 2D coordinates. |
770 | | // Set that as 2D rectangle of the scene. |
771 | 3.19k | const tools::Polygon aPolygon(aBoundRect2d); // y-up |
772 | 3.19k | basegfx::B3DPolygon aPolygonBoundVolume; // y-down, scene coordinates |
773 | 15.9k | for (sal_uInt16 i = 0; i < 4; i++ ) |
774 | 12.7k | { |
775 | 12.7k | aPolygonBoundVolume.append(basegfx::B3DPoint(aPolygon[i].X(), -aPolygon[i].Y(), 0)); |
776 | 12.7k | } |
777 | 15.9k | for (sal_uInt16 i = 0; i < 4; i++ ) |
778 | 12.7k | { |
779 | 12.7k | aPolygonBoundVolume.append(basegfx::B3DPoint(aPolygon[i].X(), -aPolygon[i].Y(), fDepth)); |
780 | 12.7k | } |
781 | 3.19k | aPolygonBoundVolume.transform(aNewTransform); |
782 | | |
783 | | // projection |
784 | 3.19k | tools::Polygon a2DProjectionResult(8); // in fact 3D points with z=0 |
785 | 28.7k | for (sal_uInt16 i = 0; i < 8; i++ ) |
786 | 25.5k | { |
787 | 25.5k | const basegfx::B3DPoint aPoint3D(aPolygonBoundVolume.getB3DPoint(i)); |
788 | | |
789 | 25.5k | if (eProjectionMode == drawing::ProjectionMode_PARALLEL) |
790 | 21.5k | { |
791 | 21.5k | a2DProjectionResult[i].setX(aPoint3D.getX()); |
792 | 21.5k | a2DProjectionResult[i].setY(aPoint3D.getY()); |
793 | 21.5k | } |
794 | 4.00k | else |
795 | 4.00k | { |
796 | | // skip point if line from viewpoint to point is parallel to xy-plane |
797 | 4.00k | if (double fDiv = aPoint3D.getZ() - fViewPointZ; fDiv != 0.0) |
798 | 3.98k | { |
799 | 3.98k | double f = (- fViewPointZ) / fDiv; |
800 | 3.98k | double fX = (aPoint3D.getX() - fViewPointX) * f + fViewPointX; |
801 | 3.98k | double fY = (aPoint3D.getY() - fViewPointY) * f + fViewPointY;; |
802 | 3.98k | a2DProjectionResult[i].setX(static_cast<sal_Int32>(fX)); |
803 | 3.98k | a2DProjectionResult[i].setY(static_cast<sal_Int32>(fY)); |
804 | 3.98k | } |
805 | 4.00k | } |
806 | 25.5k | } |
807 | | // Convert to y-axis down |
808 | 28.7k | for (sal_uInt16 i = 0; i < 8; i++ ) |
809 | 25.5k | { |
810 | 25.5k | a2DProjectionResult[i].setY(- a2DProjectionResult[i].Y()); |
811 | 25.5k | } |
812 | | // Shift back to shape center |
813 | 3.19k | a2DProjectionResult.Translate(aCenter); |
814 | | |
815 | 3.19k | pScene->SetLogicRect(a2DProjectionResult.GetBoundRect()); |
816 | | |
817 | | |
818 | | // light and material |
819 | | |
820 | | // "LightFace" has nothing corresponding in 3D rendering engine. |
821 | | /* bool bLightFace = */ GetBool(rGeometryItem, u"LightFace"_ustr, true); // default in ODF |
822 | | |
823 | | // Light directions |
824 | | |
825 | 3.19k | drawing::Direction3D aFirstLightDirectionDefault(50000.0, 0.0, 10000.0); |
826 | 3.19k | drawing::Direction3D aFirstLightDirection(GetDirection3D( rGeometryItem, u"FirstLightDirection"_ustr, aFirstLightDirectionDefault)); |
827 | 3.19k | if (aFirstLightDirection.DirectionX == 0.0 && aFirstLightDirection.DirectionY == 0.0 |
828 | 3.19k | && aFirstLightDirection.DirectionZ == 0.0) |
829 | 0 | aFirstLightDirection.DirectionZ = 1.0; |
830 | 3.19k | basegfx::B3DVector aLight1Vector(aFirstLightDirection.DirectionX, -aFirstLightDirection.DirectionY, aFirstLightDirection.DirectionZ); |
831 | 3.19k | aLight1Vector.normalize(); |
832 | | |
833 | 3.19k | drawing::Direction3D aSecondLightDirectionDefault(-50000.0, 0.0, 10000.0); |
834 | 3.19k | drawing::Direction3D aSecondLightDirection(GetDirection3D( rGeometryItem, u"SecondLightDirection"_ustr, aSecondLightDirectionDefault)); |
835 | 3.19k | if (aSecondLightDirection.DirectionX == 0.0 && aSecondLightDirection.DirectionY == 0.0 |
836 | 3.19k | && aSecondLightDirection.DirectionZ == 0.0) |
837 | 0 | aSecondLightDirection.DirectionZ = 1.0; |
838 | 3.19k | basegfx::B3DVector aLight2Vector(aSecondLightDirection.DirectionX, -aSecondLightDirection.DirectionY, aSecondLightDirection.DirectionZ); |
839 | 3.19k | aLight2Vector.normalize(); |
840 | | |
841 | | // tdf#160421 a single flip inverts the light directions currently (March 2024). So invert |
842 | | // their directions here for rendering. |
843 | 3.19k | if (bIsMirroredX != bIsMirroredY) |
844 | 1.65k | { |
845 | 1.65k | aLight1Vector *= -1.0; |
846 | 1.65k | aLight2Vector *= -1.0; |
847 | 1.65k | } |
848 | | |
849 | | // Light Intensity |
850 | | |
851 | | // For "FirstLight" the 3D-Scene light "1" is regularly used. In case of surface "Matte" |
852 | | // the light 4 is used instead. For "SecondLight" the 3D-Scene light "2" is regularly used. |
853 | | // In case first or second light is not harsh, the lights 5 to 8 are used in addition |
854 | | // to get a soft light appearance. |
855 | | // The 3D-Scene light "3" is currently not used. |
856 | | |
857 | | // ODF default 66%. MS Office default 38000/65536=0.579 is set in import filter. |
858 | 3.19k | double fLight1Intensity = GetDouble(rGeometryItem, u"FirstLightLevel"_ustr, 66) / 100.0; |
859 | | // ODF and MS Office have both default 'true'. |
860 | 3.19k | bool bFirstLightHarsh = GetBool(rGeometryItem, u"FirstLightHarsh"_ustr, true); |
861 | | // ODF default 66%. MS Office default 38000/65536=0.579 is set in import filter |
862 | 3.19k | double fLight2Intensity = GetDouble(rGeometryItem, u"SecondLightLevel"_ustr, 66) / 100.0; |
863 | | // ODF has default 'true'. MS Office default 'false' is set in import. |
864 | 3.19k | bool bSecondLightHarsh = GetBool(rGeometryItem, u"SecondLightHarsh"_ustr, true); |
865 | | |
866 | | // ODF default 33%. MS Office default 20000/65536=0.305 is set in import filter. |
867 | 3.19k | double fAmbientIntensity = GetDouble(rGeometryItem, u"Brightness"_ustr, 33) / 100.0; |
868 | | |
869 | 3.19k | double fLight1IntensityForSpecular(fLight1Intensity); // remember original value |
870 | 3.19k | if (!bFirstLightHarsh || !bSecondLightHarsh) // might need softing lights |
871 | 3.16k | { |
872 | 3.16k | bool bNeedSoftLights(false); // catch case of lights with zero intensity. |
873 | 3.16k | basegfx::B3DVector aLight5Vector; |
874 | 3.16k | basegfx::B3DVector aLight6Vector; |
875 | 3.16k | basegfx::B3DVector aLight7Vector; |
876 | 3.16k | basegfx::B3DVector aLight8Vector; |
877 | | // The needed light intensities depend on the angle between regular light and |
878 | | // additional lights, currently for 60deg. |
879 | 3.16k | Color aHoriSoftLightColor; |
880 | 3.16k | Color aVertSoftLightColor; |
881 | | |
882 | 3.16k | if (!bSecondLightHarsh && fLight2Intensity > 0.0 |
883 | 3.16k | && (bFirstLightHarsh || fLight1Intensity == 0.0)) // only second light soft |
884 | 3.14k | { |
885 | | // That is default for shapes generated in the UI, for LO and MS Office as well. |
886 | 3.14k | bNeedSoftLights = true; |
887 | 3.14k | double fLight2SoftIntensity = fLight2Intensity * 0.40; |
888 | 3.14k | aHoriSoftLightColor = Color(basegfx::BColor(fLight2SoftIntensity).clamp()); |
889 | 3.14k | aVertSoftLightColor = aHoriSoftLightColor; |
890 | 3.14k | fLight2Intensity *= 0.2; |
891 | | |
892 | 3.14k | lcl_SoftLightsDirection(aLight2Vector, aLight5Vector, aLight6Vector, |
893 | 3.14k | aLight7Vector, aLight8Vector); |
894 | 3.14k | } |
895 | 22 | else if (!bFirstLightHarsh && fLight1Intensity > 0.0 |
896 | 22 | && (bSecondLightHarsh || fLight2Intensity == 0.0)) // only first light soft |
897 | 15 | { |
898 | 15 | bNeedSoftLights = true; |
899 | 15 | double fLight1SoftIntensity = fLight1Intensity * 0.40; |
900 | 15 | aHoriSoftLightColor = Color(basegfx::BColor(fLight1SoftIntensity).clamp()); |
901 | 15 | aVertSoftLightColor = aHoriSoftLightColor; |
902 | 15 | fLight1Intensity *= 0.2; |
903 | | |
904 | 15 | lcl_SoftLightsDirection(aLight1Vector, aLight5Vector, aLight6Vector, |
905 | 15 | aLight7Vector, aLight8Vector); |
906 | 15 | } |
907 | 7 | else if (!bFirstLightHarsh && fLight1Intensity > 0.0 && !bSecondLightHarsh |
908 | 7 | && fLight2Intensity > 0.0) // both lights soft |
909 | 4 | { |
910 | 4 | bNeedSoftLights = true; |
911 | | // We do not hat enough lights. We use two soft lights for FirstLight and two for |
912 | | // SecondLight and double intensity. |
913 | 4 | double fLight1SoftIntensity = fLight1Intensity * 0.8; |
914 | 4 | fLight1Intensity *= 0.4; |
915 | 4 | aHoriSoftLightColor = Color(basegfx::BColor(fLight1SoftIntensity).clamp()); |
916 | 4 | basegfx::B3DVector aDummy1, aDummy2; |
917 | 4 | lcl_SoftLightsDirection(aLight1Vector, aDummy1, aDummy2, aLight7Vector, |
918 | 4 | aLight8Vector); |
919 | | |
920 | 4 | double fLight2SoftIntensity = fLight2Intensity * 0.8; |
921 | 4 | aVertSoftLightColor = Color(basegfx::BColor(fLight2SoftIntensity).clamp()); |
922 | 4 | fLight2Intensity *= 0.4; |
923 | 4 | lcl_SoftLightsDirection(aLight2Vector, aLight5Vector, aLight6Vector, aDummy1, |
924 | 4 | aDummy2); |
925 | 4 | } |
926 | | |
927 | 3.16k | if (bNeedSoftLights) |
928 | 3.15k | { |
929 | 3.15k | pScene->GetProperties().SetObjectItem( |
930 | 3.15k | makeSvx3DLightDirection5Item(aLight5Vector)); |
931 | 3.15k | pScene->GetProperties().SetObjectItem( |
932 | 3.15k | makeSvx3DLightcolor5Item(aVertSoftLightColor)); |
933 | 3.15k | pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff5Item(true)); |
934 | 3.15k | pScene->GetProperties().SetObjectItem( |
935 | 3.15k | makeSvx3DLightDirection6Item(aLight6Vector)); |
936 | 3.15k | pScene->GetProperties().SetObjectItem( |
937 | 3.15k | makeSvx3DLightcolor6Item(aVertSoftLightColor)); |
938 | 3.15k | pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff6Item(true)); |
939 | 3.15k | pScene->GetProperties().SetObjectItem( |
940 | 3.15k | makeSvx3DLightDirection7Item(aLight7Vector)); |
941 | 3.15k | pScene->GetProperties().SetObjectItem( |
942 | 3.15k | makeSvx3DLightcolor7Item(aHoriSoftLightColor)); |
943 | 3.15k | pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff7Item(true)); |
944 | 3.15k | pScene->GetProperties().SetObjectItem( |
945 | 3.15k | makeSvx3DLightDirection8Item(aLight8Vector)); |
946 | 3.15k | pScene->GetProperties().SetObjectItem( |
947 | 3.15k | makeSvx3DLightcolor8Item(aHoriSoftLightColor)); |
948 | 3.15k | pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff8Item(true)); |
949 | 3.15k | } |
950 | 3.16k | } |
951 | | |
952 | | // ToDo: MSO seems to add half of the surplus to ambient color. ODF restricts value to <1. |
953 | 3.19k | if (fLight1Intensity > 1.0) |
954 | 21 | { |
955 | 21 | fAmbientIntensity += (fLight1Intensity - 1.0) / 2.0; |
956 | 21 | } |
957 | | |
958 | | // ToDo: How to handle fAmbientIntensity larger 1.0 ? Perhaps lighten object color? |
959 | | |
960 | | // Now set the regularly 3D-scene light attributes. |
961 | 3.19k | Color aAmbientColor(basegfx::BColor(fAmbientIntensity).clamp()); |
962 | 3.19k | pScene->GetProperties().SetObjectItem(makeSvx3DAmbientcolorItem(aAmbientColor)); |
963 | | |
964 | 3.19k | pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection1Item(aLight1Vector)); |
965 | 3.19k | pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff1Item(fLight1Intensity > 0.0)); |
966 | 3.19k | Color aLight1Color(basegfx::BColor(fLight1Intensity).clamp()); |
967 | 3.19k | pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor1Item(aLight1Color)); |
968 | | |
969 | 3.19k | pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection2Item(aLight2Vector)); |
970 | 3.19k | pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff2Item(fLight2Intensity > 0.0)); |
971 | 3.19k | Color aLight2Color(basegfx::BColor(fLight2Intensity).clamp()); |
972 | 3.19k | pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor2Item(aLight2Color)); |
973 | | |
974 | | // Object reactions on light |
975 | | // Diffusion, Specular-Color and -Intensity are object properties, not scene properties. |
976 | | // Surface flag "Metal" is an object property too. |
977 | | |
978 | | // Property "Diffusion" would correspond to style attribute "drd3:diffuse-color". |
979 | | // But that is not implemented. We cannot ignore the attribute because MS Office sets |
980 | | // attribute c3DDiffuseAmt to 43712 (Type Fixed 16.16, approx 66,9%) instead of MSO |
981 | | // default 65536 (100%), if the user sets surface 'Metal' in the UI of MS Office. |
982 | | // We will change the material color of the 3D object as ersatz. |
983 | | // ODF data type is percent with default 0%. MSO default is set in import filter. |
984 | 3.19k | double fDiffusion = GetDouble(rGeometryItem, u"Diffusion"_ustr, 0.0) / 100.0; |
985 | | |
986 | | // ODF standard specifies for value true: "the specular color for the shading of an |
987 | | // extruded shape is gray (red, green and blue values of 200) instead of white and 15% is |
988 | | // added to the specularity." |
989 | | // Neither 'specularity' nor 'specular color' is clearly defined in the standard. ODF term |
990 | | // 'specularity' seems to correspond to UI field 'Specular Intensity' for 3D scenes. |
991 | | // MS Office uses current material color in case 'Metal' is set. To detect, whether |
992 | | // rendering similar to MS Office has to be used the property 'MetalType' is used. It is |
993 | | // set on import and in the extrusion bar. |
994 | 3.19k | bool bMetal = GetBool(rGeometryItem, u"Metal"_ustr, false); |
995 | 3.19k | sal_Int16 eMetalType( |
996 | 3.19k | GetMetalType(rGeometryItem, drawing::EnhancedCustomShapeMetalType::MetalODF)); |
997 | 3.19k | bool bMetalMSCompatible |
998 | 3.19k | = eMetalType == drawing::EnhancedCustomShapeMetalType::MetalMSCompatible; |
999 | | |
1000 | | // Property "Specularity" corresponds to 3D object style attribute dr3d:specular-color. |
1001 | 3.19k | double fSpecularity = GetDouble(rGeometryItem, u"Specularity"_ustr, 0) / 100.0; |
1002 | | |
1003 | 3.19k | if (bMetal && !bMetalMSCompatible) |
1004 | 0 | { |
1005 | 0 | fSpecularity *= 200.0 / 255.0; |
1006 | 0 | } |
1007 | | |
1008 | | // MS Office seems to render as if 'Specular Color' = Specularity * Light1Intensity. |
1009 | 3.19k | double fShadingFactor = fLight1IntensityForSpecular * fSpecularity; |
1010 | 3.19k | Color aSpecularCol(basegfx::BColor(fShadingFactor).clamp()); |
1011 | | // In case of bMetalMSCompatible the color will be recalculated in the below loop. |
1012 | | |
1013 | | // Shininess ODF default 50 (unit %). MS Office default 5, import filter makes *10. |
1014 | | // Shininess corresponds to "Specular Intensity" with the nonlinear relationship |
1015 | | // "Specular Intensity" = 2^c3DShininess = 2^("Shininess" / 10) |
1016 | 3.19k | double fShininess = GetDouble(rGeometryItem, u"Shininess"_ustr, 50) / 10.0; |
1017 | 3.19k | fShininess = std::clamp<double>(pow(2, fShininess), 0.0, 100.0); |
1018 | 3.19k | sal_uInt16 nIntensity = static_cast<sal_uInt16>(basegfx::fround(fShininess)); |
1019 | 3.19k | if (bMetal && !bMetalMSCompatible) |
1020 | 0 | { |
1021 | 0 | nIntensity += 15; // as specified in ODF |
1022 | 0 | nIntensity = std::clamp<sal_uInt16>(nIntensity, 0, 100); |
1023 | 0 | } |
1024 | | |
1025 | 3.19k | SdrObjListIter aSceneIter(*pScene, SdrIterMode::DeepNoGroups); |
1026 | 13.6k | while (aSceneIter.IsMore()) |
1027 | 10.4k | { |
1028 | 10.4k | const SdrObject* pNext = aSceneIter.Next(); |
1029 | | |
1030 | | // Change material color as ersatz for missing style attribute "drd3:diffuse-color". |
1031 | | // For this ersatz we exclude case fDiffusion == 0.0, because for older documents this |
1032 | | // attribute is not written out to draw:extrusion-diffusion and ODF default 0 would |
1033 | | // produce black objects. |
1034 | 10.4k | const Color& rMatColor |
1035 | 10.4k | = pNext->GetProperties().GetItem(XATTR_FILLCOLOR).GetColorValue(); |
1036 | 10.4k | Color aOldMatColor(rMatColor); |
1037 | 10.4k | if (fDiffusion > 0.0 && !basegfx::fTools::equalZero(fDiffusion) |
1038 | 10.4k | && !basegfx::fTools::equal(fDiffusion, 1.0)) |
1039 | 2.53k | { |
1040 | | // Occurs e.g. with MS surface preset 'Metal'. |
1041 | 2.53k | sal_uInt16 nHue; |
1042 | 2.53k | sal_uInt16 nSaturation; |
1043 | 2.53k | sal_uInt16 nBrightness; |
1044 | 2.53k | rMatColor.RGBtoHSB(nHue, nSaturation, nBrightness); |
1045 | 2.53k | nBrightness |
1046 | 2.53k | = static_cast<sal_uInt16>(static_cast<double>(nBrightness) * fDiffusion); |
1047 | 2.53k | nBrightness = std::clamp<sal_uInt16>(nBrightness, 0, 100); |
1048 | 2.53k | Color aNewMatColor = Color::HSBtoRGB(nHue, nSaturation, nBrightness); |
1049 | 2.53k | pNext->GetProperties().SetObjectItem(XFillColorItem(u""_ustr, aNewMatColor)); |
1050 | 2.53k | } |
1051 | | |
1052 | | // Using material color instead of gray in case of MS Office compatible rendering. |
1053 | 10.4k | if (bMetal && bMetalMSCompatible) |
1054 | 9.56k | { |
1055 | 9.56k | sal_uInt16 nHue; |
1056 | 9.56k | sal_uInt16 nSaturation; |
1057 | 9.56k | sal_uInt16 nBrightness; |
1058 | 9.56k | aOldMatColor.RGBtoHSB(nHue, nSaturation, nBrightness); |
1059 | 9.56k | nBrightness = static_cast<sal_uInt16>(static_cast<double>(nBrightness) |
1060 | 9.56k | * fShadingFactor); |
1061 | 9.56k | nBrightness = std::clamp<sal_uInt16>(nBrightness, 0, 100); |
1062 | 9.56k | aSpecularCol = Color::HSBtoRGB(nHue, nSaturation, nBrightness); |
1063 | 9.56k | } |
1064 | | |
1065 | 10.4k | pNext->GetProperties().SetObjectItem(makeSvx3DMaterialSpecularItem(aSpecularCol)); |
1066 | 10.4k | pNext->GetProperties().SetObjectItem( |
1067 | 10.4k | makeSvx3DMaterialSpecularIntensityItem(nIntensity)); |
1068 | 10.4k | } |
1069 | | |
1070 | | // fSpecularity = 0 is used to indicate surface preset "Matte". |
1071 | 3.19k | if (basegfx::fTools::equalZero(fSpecularity)) |
1072 | 2.89k | { |
1073 | | // First light in LO 3D engine is always specular, all other lights are never specular. |
1074 | | // We copy light1 values to light4 and use it instead of light1 in the 3D scene. |
1075 | 2.89k | pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff1Item(false)); |
1076 | 2.89k | pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff4Item(true)); |
1077 | 2.89k | pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor4Item(aLight1Color)); |
1078 | 2.89k | pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection4Item(aLight1Vector)); |
1079 | 2.89k | } |
1080 | | |
1081 | | // removing placeholder objects |
1082 | 3.19k | for (E3dCompoundObject* pTemp : aPlaceholderObjectList) |
1083 | 1.02k | { |
1084 | 1.02k | pScene->RemoveObject( pTemp->GetOrdNum() ); |
1085 | 1.02k | } |
1086 | 3.19k | } |
1087 | 3.19k | } |
1088 | 44.9k | return pRet; |
1089 | 44.9k | } |
1090 | | |
1091 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |