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