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