Line data Source code
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 <tools/b3dtrans.hxx>
21 :
22 : // B3dTransformationSet --------------------------------------------------------
23 : // Transformations for all 3D output
24 :
25 0 : B3dTransformationSet::B3dTransformationSet()
26 : {
27 0 : Reset();
28 0 : }
29 :
30 0 : B3dTransformationSet::~B3dTransformationSet()
31 : {
32 0 : }
33 :
34 0 : void B3dTransformationSet::Orientation(basegfx::B3DHomMatrix& rTarget, basegfx::B3DPoint aVRP, basegfx::B3DVector aVPN, basegfx::B3DVector aVUP)
35 : {
36 0 : rTarget.translate( -aVRP.getX(), -aVRP.getY(), -aVRP.getZ());
37 0 : aVUP.normalize();
38 0 : aVPN.normalize();
39 0 : basegfx::B3DVector aRx(aVUP);
40 0 : basegfx::B3DVector aRy(aVPN);
41 0 : aRx = aRx.getPerpendicular(aRy);
42 0 : aRx.normalize();
43 0 : aRy = aRy.getPerpendicular(aRx);
44 0 : aRy.normalize();
45 0 : basegfx::B3DHomMatrix aTemp;
46 0 : aTemp.set(0, 0, aRx.getX());
47 0 : aTemp.set(0, 1, aRx.getY());
48 0 : aTemp.set(0, 2, aRx.getZ());
49 0 : aTemp.set(1, 0, aRy.getX());
50 0 : aTemp.set(1, 1, aRy.getY());
51 0 : aTemp.set(1, 2, aRy.getZ());
52 0 : aTemp.set(2, 0, aVPN.getX());
53 0 : aTemp.set(2, 1, aVPN.getY());
54 0 : aTemp.set(2, 2, aVPN.getZ());
55 0 : rTarget *= aTemp;
56 0 : }
57 :
58 0 : void B3dTransformationSet::Frustum(basegfx::B3DHomMatrix& rTarget, double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar)
59 : {
60 0 : if(!(fNear > 0.0))
61 : {
62 0 : fNear = 0.001;
63 : }
64 0 : if(!(fFar > 0.0))
65 : {
66 0 : fFar = 1.0;
67 : }
68 0 : if(fNear == fFar)
69 : {
70 0 : fFar = fNear + 1.0;
71 : }
72 0 : if(fLeft == fRight)
73 : {
74 0 : fLeft -= 1.0;
75 0 : fRight += 1.0;
76 : }
77 0 : if(fTop == fBottom)
78 : {
79 0 : fBottom -= 1.0;
80 0 : fTop += 1.0;
81 : }
82 0 : basegfx::B3DHomMatrix aTemp;
83 :
84 0 : aTemp.set(0, 0, 2.0 * fNear / (fRight - fLeft));
85 0 : aTemp.set(1, 1, 2.0 * fNear / (fTop - fBottom));
86 0 : aTemp.set(0, 2, (fRight + fLeft) / (fRight - fLeft));
87 0 : aTemp.set(1, 2, (fTop + fBottom) / (fTop - fBottom));
88 0 : aTemp.set(2, 2, -1.0 * ((fFar + fNear) / (fFar - fNear)));
89 0 : aTemp.set(3, 2, -1.0);
90 0 : aTemp.set(2, 3, -1.0 * ((2.0 * fFar * fNear) / (fFar - fNear)));
91 0 : aTemp.set(3, 3, 0.0);
92 :
93 0 : rTarget *= aTemp;
94 0 : }
95 :
96 0 : void B3dTransformationSet::Ortho(basegfx::B3DHomMatrix& rTarget,
97 : double fLeft, double fRight, double fBottom, double fTop,
98 : double fNear, double fFar)
99 : {
100 0 : if(fNear == fFar)
101 : {
102 : OSL_FAIL("Near and far clipping plane in Ortho definition are identical");
103 0 : fFar = fNear + 1.0;
104 : }
105 0 : if(fLeft == fRight)
106 : {
107 : OSL_FAIL("Left and right in Ortho definition are identical");
108 0 : fLeft -= 1.0;
109 0 : fRight += 1.0;
110 : }
111 0 : if(fTop == fBottom)
112 : {
113 : OSL_FAIL("Top and bottom in Ortho definition are identical");
114 0 : fBottom -= 1.0;
115 0 : fTop += 1.0;
116 : }
117 0 : basegfx::B3DHomMatrix aTemp;
118 :
119 0 : aTemp.set(0, 0, 2.0 / (fRight - fLeft));
120 0 : aTemp.set(1, 1, 2.0 / (fTop - fBottom));
121 0 : aTemp.set(2, 2, -1.0 * (2.0 / (fFar - fNear)));
122 0 : aTemp.set(0, 3, -1.0 * ((fRight + fLeft) / (fRight - fLeft)));
123 0 : aTemp.set(1, 3, -1.0 * ((fTop + fBottom) / (fTop - fBottom)));
124 0 : aTemp.set(2, 3, -1.0 * ((fFar + fNear) / (fFar - fNear)));
125 :
126 0 : rTarget *= aTemp;
127 0 : }
128 :
129 : /// reset values
130 0 : void B3dTransformationSet::Reset()
131 : {
132 : // Reset matrices to identity matrices
133 0 : maObjectTrans.identity();
134 0 : PostSetObjectTrans();
135 :
136 0 : Orientation(maOrientation);
137 0 : PostSetOrientation();
138 :
139 0 : maTexture.identity();
140 :
141 0 : mfLeftBound = mfBottomBound = -1.0;
142 0 : mfRightBound = mfTopBound = 1.0;
143 0 : mfNearBound = 0.001;
144 0 : mfFarBound = 1.001;
145 :
146 0 : meRatio = Base3DRatioGrow;
147 0 : mfRatio = 0.0;
148 :
149 0 : maViewportRectangle = Rectangle(-1, -1, 2, 2);
150 0 : maVisibleRectangle = maViewportRectangle;
151 :
152 0 : mbPerspective = sal_True;
153 :
154 0 : mbProjectionValid = sal_False;
155 0 : mbObjectToDeviceValid = sal_False;
156 0 : mbWorldToViewValid = sal_False;
157 :
158 0 : CalcViewport();
159 0 : }
160 :
161 : /// Object transformation
162 0 : void B3dTransformationSet::PostSetObjectTrans()
163 : {
164 : // Assign and compute inverse
165 0 : maInvObjectTrans = maObjectTrans;
166 0 : maInvObjectTrans.invert();
167 0 : }
168 :
169 0 : void B3dTransformationSet::SetOrientation( basegfx::B3DPoint aVRP, basegfx::B3DVector aVPN, basegfx::B3DVector aVUP)
170 : {
171 0 : maOrientation.identity();
172 0 : Orientation(maOrientation, aVRP, aVPN, aVUP);
173 :
174 0 : mbInvTransObjectToEyeValid = sal_False;
175 0 : mbObjectToDeviceValid = sal_False;
176 0 : mbWorldToViewValid = sal_False;
177 :
178 0 : PostSetOrientation();
179 0 : }
180 :
181 0 : void B3dTransformationSet::PostSetOrientation()
182 : {
183 : // Assign and compute inverse
184 0 : maInvOrientation = maOrientation;
185 0 : maInvOrientation.invert();
186 0 : }
187 :
188 : /// Projections for transformations
189 0 : void B3dTransformationSet::SetProjection(const basegfx::B3DHomMatrix& mProject)
190 : {
191 0 : maProjection = mProject;
192 0 : PostSetProjection();
193 0 : }
194 :
195 0 : const basegfx::B3DHomMatrix& B3dTransformationSet::GetProjection()
196 : {
197 0 : if(!mbProjectionValid)
198 0 : CalcViewport();
199 0 : return maProjection;
200 : }
201 :
202 0 : void B3dTransformationSet::PostSetProjection()
203 : {
204 : // Assign and comptue inverse
205 0 : maInvProjection = GetProjection();
206 0 : maInvProjection.invert();
207 :
208 : // invalidate dependent matrices
209 0 : mbObjectToDeviceValid = sal_False;
210 0 : mbWorldToViewValid = sal_False;
211 0 : }
212 :
213 : /// Transformations for viewport
214 0 : void B3dTransformationSet::CalcViewport()
215 : {
216 : // Parameters for projection
217 0 : double fLeft(mfLeftBound);
218 0 : double fRight(mfRightBound);
219 0 : double fBottom(mfBottomBound);
220 0 : double fTop(mfTopBound);
221 :
222 : // Adjust projection to aspect ratio, if set
223 0 : if(GetRatio() != 0.0)
224 : {
225 : // Compute current aspect ratio of boundaries
226 0 : double fBoundWidth = (double)(maViewportRectangle.GetWidth() + 1);
227 0 : double fBoundHeight = (double)(maViewportRectangle.GetHeight() + 1);
228 0 : double fActRatio = 1;
229 : double fFactor;
230 :
231 0 : if(fBoundWidth != 0.0)
232 0 : fActRatio = fBoundHeight / fBoundWidth;
233 : // FIXME else in this case has a lot of problems, should this return.
234 :
235 0 : switch(meRatio)
236 : {
237 : case Base3DRatioShrink :
238 : {
239 : // Dilate smaller part
240 0 : if(fActRatio > mfRatio)
241 : {
242 : // enlarge X
243 0 : fFactor = 1.0 / fActRatio;
244 0 : fRight *= fFactor;
245 0 : fLeft *= fFactor;
246 : }
247 : else
248 : {
249 : // enlarge Y
250 0 : fFactor = fActRatio;
251 0 : fTop *= fFactor;
252 0 : fBottom *= fFactor;
253 : }
254 0 : break;
255 : }
256 : case Base3DRatioGrow :
257 : {
258 : // scale down larger part
259 0 : if(fActRatio > mfRatio)
260 : {
261 : // scale down Y
262 0 : fFactor = fActRatio;
263 0 : fTop *= fFactor;
264 0 : fBottom *= fFactor;
265 : }
266 : else
267 : {
268 : // scale down X
269 0 : fFactor = 1.0 / fActRatio;
270 0 : fRight *= fFactor;
271 0 : fLeft *= fFactor;
272 : }
273 0 : break;
274 : }
275 : case Base3DRatioMiddle :
276 : {
277 : // averaging
278 0 : fFactor = ((1.0 / fActRatio) + 1.0) / 2.0;
279 0 : fRight *= fFactor;
280 0 : fLeft *= fFactor;
281 0 : fFactor = (fActRatio + 1.0) / 2.0;
282 0 : fTop *= fFactor;
283 0 : fBottom *= fFactor;
284 0 : break;
285 : }
286 : }
287 : }
288 :
289 : // Do projection and object areas overlap?
290 0 : maSetBound = maViewportRectangle;
291 :
292 : // Reset projection with new values
293 0 : basegfx::B3DHomMatrix aNewProjection;
294 :
295 : // #i36281#
296 : // OpenGL needs a little more rough additional size to not let
297 : // the front face vanish. Changed from SMALL_DVALUE to 0.000001,
298 : // which is 1/10000th, comared with 1/tenth of a million from SMALL_DVALUE.
299 0 : const double fDistPart((mfFarBound - mfNearBound) * 0.0001);
300 :
301 : // To avoid critical clipping, set Near & Far generously
302 0 : if(mbPerspective)
303 : {
304 0 : Frustum(aNewProjection, fLeft, fRight, fBottom, fTop, mfNearBound - fDistPart, mfFarBound + fDistPart);
305 : }
306 : else
307 : {
308 0 : Ortho(aNewProjection, fLeft, fRight, fBottom, fTop, mfNearBound - fDistPart, mfFarBound + fDistPart);
309 : }
310 :
311 : // Set to true to guarantee loop termination
312 0 : mbProjectionValid = sal_True;
313 :
314 : // set new projection
315 0 : SetProjection(aNewProjection);
316 :
317 : // fill parameters for ViewportTransformation
318 : // Translation
319 0 : maTranslate.setX((double)maSetBound.Left() + ((maSetBound.GetWidth() - 1L) / 2.0));
320 0 : maTranslate.setY((double)maSetBound.Top() + ((maSetBound.GetHeight() - 1L) / 2.0));
321 0 : maTranslate.setZ(ZBUFFER_DEPTH_RANGE / 2.0);
322 :
323 : // Scaling
324 0 : maScale.setX((maSetBound.GetWidth() - 1L) / 2.0);
325 0 : maScale.setY((maSetBound.GetHeight() - 1L) / -2.0);
326 0 : maScale.setZ(ZBUFFER_DEPTH_RANGE / 2.0);
327 :
328 : // React to change of viewport
329 0 : PostSetViewport();
330 0 : }
331 :
332 0 : void B3dTransformationSet::SetRatio(double fNew)
333 : {
334 0 : if(mfRatio != fNew)
335 : {
336 0 : mfRatio = fNew;
337 0 : mbProjectionValid = sal_False;
338 0 : mbObjectToDeviceValid = sal_False;
339 0 : mbWorldToViewValid = sal_False;
340 : }
341 0 : }
342 :
343 0 : void B3dTransformationSet::SetDeviceRectangle(double fL, double fR, double fB, double fT,
344 : sal_Bool bBroadCastChange)
345 : {
346 0 : if(fL != mfLeftBound || fR != mfRightBound || fB != mfBottomBound || fT != mfTopBound)
347 : {
348 0 : mfLeftBound = fL;
349 0 : mfRightBound = fR;
350 0 : mfBottomBound = fB;
351 0 : mfTopBound = fT;
352 :
353 0 : mbProjectionValid = sal_False;
354 0 : mbObjectToDeviceValid = sal_False;
355 0 : mbWorldToViewValid = sal_False;
356 :
357 : // Broadcast changes
358 0 : if(bBroadCastChange)
359 0 : DeviceRectangleChange();
360 : }
361 0 : }
362 :
363 0 : void B3dTransformationSet::DeviceRectangleChange()
364 : {
365 0 : }
366 :
367 0 : void B3dTransformationSet::SetPerspective(sal_Bool bNew)
368 : {
369 0 : if(mbPerspective != bNew)
370 : {
371 0 : mbPerspective = bNew;
372 0 : mbProjectionValid = sal_False;
373 0 : mbObjectToDeviceValid = sal_False;
374 0 : mbWorldToViewValid = sal_False;
375 : }
376 0 : }
377 :
378 0 : void B3dTransformationSet::SetViewportRectangle(Rectangle& rRect, Rectangle& rVisible)
379 : {
380 0 : if(rRect != maViewportRectangle || rVisible != maVisibleRectangle)
381 : {
382 0 : maViewportRectangle = rRect;
383 0 : maVisibleRectangle = rVisible;
384 :
385 0 : mbProjectionValid = sal_False;
386 0 : mbObjectToDeviceValid = sal_False;
387 0 : mbWorldToViewValid = sal_False;
388 : }
389 0 : }
390 :
391 0 : void B3dTransformationSet::PostSetViewport()
392 : {
393 0 : }
394 :
395 : // direct access to various transformations
396 :
397 0 : const basegfx::B3DPoint B3dTransformationSet::WorldToEyeCoor(const basegfx::B3DPoint& rVec)
398 : {
399 0 : basegfx::B3DPoint aVec(rVec);
400 0 : aVec *= GetOrientation();
401 0 : return aVec;
402 : }
403 :
404 0 : const basegfx::B3DPoint B3dTransformationSet::EyeToWorldCoor(const basegfx::B3DPoint& rVec)
405 : {
406 0 : basegfx::B3DPoint aVec(rVec);
407 0 : aVec *= GetInvOrientation();
408 0 : return aVec;
409 : }
410 :
411 : // B3dViewport -----------------------------------------------------------------
412 :
413 0 : B3dViewport::B3dViewport()
414 : : B3dTransformationSet(),
415 : aVRP(0, 0, 0),
416 : aVPN(0, 0, 1),
417 0 : aVUV(0, 1, 0)
418 : {
419 0 : CalcOrientation();
420 0 : }
421 :
422 0 : B3dViewport::~B3dViewport()
423 : {
424 0 : }
425 :
426 0 : void B3dViewport::SetVUV(const basegfx::B3DVector& rNewVUV)
427 : {
428 0 : aVUV = rNewVUV;
429 0 : CalcOrientation();
430 0 : }
431 :
432 0 : void B3dViewport::SetViewportValues(
433 : const basegfx::B3DPoint& rNewVRP,
434 : const basegfx::B3DVector& rNewVPN,
435 : const basegfx::B3DVector& rNewVUV)
436 : {
437 0 : aVRP = rNewVRP;
438 0 : aVPN = rNewVPN;
439 0 : aVUV = rNewVUV;
440 0 : CalcOrientation();
441 0 : }
442 :
443 0 : void B3dViewport::CalcOrientation()
444 : {
445 0 : SetOrientation(aVRP, aVPN, aVUV);
446 0 : }
447 :
448 : // B3dCamera -------------------------------------------------------------------
449 :
450 0 : B3dCamera::B3dCamera(
451 : const basegfx::B3DPoint& rPos, const basegfx::B3DVector& rLkAt,
452 : double fFocLen, double fBnkAng, sal_Bool bUseFocLen)
453 : : B3dViewport(),
454 : aPosition(rPos),
455 : aCorrectedPosition(rPos),
456 : aLookAt(rLkAt),
457 : fFocalLength(fFocLen),
458 : fBankAngle(fBnkAng),
459 0 : bUseFocalLength(bUseFocLen)
460 : {
461 0 : CalcNewViewportValues();
462 0 : }
463 :
464 0 : B3dCamera::~B3dCamera()
465 : {
466 0 : }
467 :
468 0 : void B3dCamera::DeviceRectangleChange()
469 : {
470 : // call parent
471 0 : B3dViewport::DeviceRectangleChange();
472 :
473 : // react to changes
474 0 : CalcNewViewportValues();
475 0 : }
476 :
477 0 : void B3dCamera::CalcNewViewportValues()
478 : {
479 0 : basegfx::B3DVector aViewVector(aPosition - aLookAt);
480 0 : basegfx::B3DVector aNewVPN(aViewVector);
481 :
482 0 : basegfx::B3DVector aNewVUV(0.0, 1.0, 0.0);
483 0 : if(aNewVPN.getLength() < aNewVPN.getY())
484 0 : aNewVUV.setX(0.5);
485 :
486 0 : aNewVUV.normalize();
487 0 : aNewVPN.normalize();
488 :
489 0 : basegfx::B3DVector aNewToTheRight = aNewVPN;
490 0 : aNewToTheRight = aNewToTheRight.getPerpendicular(aNewVUV);
491 0 : aNewToTheRight.normalize();
492 0 : aNewVUV = aNewToTheRight.getPerpendicular(aNewVPN);
493 0 : aNewVUV.normalize();
494 :
495 0 : SetViewportValues(aPosition, aNewVPN, aNewVUV);
496 0 : if(CalcFocalLength())
497 0 : SetViewportValues(aCorrectedPosition, aNewVPN, aNewVUV);
498 :
499 0 : if(fBankAngle != 0.0)
500 : {
501 0 : basegfx::B3DHomMatrix aRotMat;
502 0 : aRotMat.rotate(0.0, 0.0, fBankAngle);
503 0 : basegfx::B3DVector aUp(0.0, 1.0, 0.0);
504 0 : aUp *= aRotMat;
505 0 : aUp = EyeToWorldCoor(aUp);
506 0 : aUp.normalize();
507 0 : SetVUV(aUp);
508 0 : }
509 0 : }
510 :
511 0 : sal_Bool B3dCamera::CalcFocalLength()
512 : {
513 0 : double fWidth = GetDeviceRectangleWidth();
514 0 : sal_Bool bRetval = sal_False;
515 :
516 0 : if(bUseFocalLength)
517 : {
518 : // Update position if focal length changes
519 0 : aCorrectedPosition = basegfx::B3DPoint(0.0, 0.0, fFocalLength * fWidth / 35.0);
520 0 : aCorrectedPosition = EyeToWorldCoor(aCorrectedPosition);
521 0 : bRetval = sal_True;
522 : }
523 : else
524 : {
525 : // Adjust focal length based on given position
526 0 : basegfx::B3DPoint aOldPosition;
527 0 : aOldPosition = WorldToEyeCoor(aOldPosition);
528 0 : if(fWidth != 0.0)
529 0 : fFocalLength = aOldPosition.getZ() / fWidth * 35.0;
530 0 : if(fFocalLength < 5.0)
531 0 : fFocalLength = 5.0;
532 : }
533 0 : return bRetval;
534 : }
535 :
536 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|