diff options
Diffstat (limited to 'tools/source/generic/b3dtrans.cxx')
-rw-r--r-- | tools/source/generic/b3dtrans.cxx | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/tools/source/generic/b3dtrans.cxx b/tools/source/generic/b3dtrans.cxx new file mode 100644 index 000000000..b8e29be31 --- /dev/null +++ b/tools/source/generic/b3dtrans.cxx @@ -0,0 +1,459 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <tools/b3dtrans.hxx> + +#include <osl/diagnose.h> + + // Near and far clipping planes +static constexpr double gfNearBound = 0.001; +static constexpr double gfFarBound = 1.001; + + +// B3dTransformationSet -------------------------------------------------------- +// Transformations for all 3D output + +B3dTransformationSet::B3dTransformationSet() +{ + Reset(); +} + +B3dTransformationSet::~B3dTransformationSet() +{ +} + +void B3dTransformationSet::Orientation(basegfx::B3DHomMatrix& rTarget, const basegfx::B3DPoint& aVRP, basegfx::B3DVector aVPN, basegfx::B3DVector aVUP) +{ + rTarget.translate( -aVRP.getX(), -aVRP.getY(), -aVRP.getZ()); + aVUP.normalize(); + aVPN.normalize(); + basegfx::B3DVector aRx(aVUP); + basegfx::B3DVector aRy(aVPN); + aRx = aRx.getPerpendicular(aRy); + aRx.normalize(); + aRy = aRy.getPerpendicular(aRx); + aRy.normalize(); + basegfx::B3DHomMatrix aTemp; + aTemp.set(0, 0, aRx.getX()); + aTemp.set(0, 1, aRx.getY()); + aTemp.set(0, 2, aRx.getZ()); + aTemp.set(1, 0, aRy.getX()); + aTemp.set(1, 1, aRy.getY()); + aTemp.set(1, 2, aRy.getZ()); + aTemp.set(2, 0, aVPN.getX()); + aTemp.set(2, 1, aVPN.getY()); + aTemp.set(2, 2, aVPN.getZ()); + rTarget *= aTemp; +} + +void B3dTransformationSet::Frustum(basegfx::B3DHomMatrix& rTarget, double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar) +{ + if(!(fNear > 0.0)) + { + fNear = 0.001; + } + if(!(fFar > 0.0)) + { + fFar = 1.0; + } + if(fNear == fFar) + { + fFar = fNear + 1.0; + } + if(fLeft == fRight) + { + fLeft -= 1.0; + fRight += 1.0; + } + if(fTop == fBottom) + { + fBottom -= 1.0; + fTop += 1.0; + } + basegfx::B3DHomMatrix aTemp; + + aTemp.set(0, 0, 2.0 * fNear / (fRight - fLeft)); + aTemp.set(1, 1, 2.0 * fNear / (fTop - fBottom)); + aTemp.set(0, 2, (fRight + fLeft) / (fRight - fLeft)); + aTemp.set(1, 2, (fTop + fBottom) / (fTop - fBottom)); + aTemp.set(2, 2, -1.0 * ((fFar + fNear) / (fFar - fNear))); + aTemp.set(3, 2, -1.0); + aTemp.set(2, 3, -1.0 * ((2.0 * fFar * fNear) / (fFar - fNear))); + aTemp.set(3, 3, 0.0); + + rTarget *= aTemp; +} + +void B3dTransformationSet::Ortho(basegfx::B3DHomMatrix& rTarget, + double fLeft, double fRight, double fBottom, double fTop, + double fNear, double fFar) +{ + if(fNear == fFar) + { + OSL_FAIL("Near and far clipping plane in Ortho definition are identical"); + fFar = fNear + 1.0; + } + if(fLeft == fRight) + { + OSL_FAIL("Left and right in Ortho definition are identical"); + fLeft -= 1.0; + fRight += 1.0; + } + if(fTop == fBottom) + { + OSL_FAIL("Top and bottom in Ortho definition are identical"); + fBottom -= 1.0; + fTop += 1.0; + } + basegfx::B3DHomMatrix aTemp; + + aTemp.set(0, 0, 2.0 / (fRight - fLeft)); + aTemp.set(1, 1, 2.0 / (fTop - fBottom)); + aTemp.set(2, 2, -1.0 * (2.0 / (fFar - fNear))); + aTemp.set(0, 3, -1.0 * ((fRight + fLeft) / (fRight - fLeft))); + aTemp.set(1, 3, -1.0 * ((fTop + fBottom) / (fTop - fBottom))); + aTemp.set(2, 3, -1.0 * ((fFar + fNear) / (fFar - fNear))); + + rTarget *= aTemp; +} + +/// reset values +void B3dTransformationSet::Reset() +{ + // Reset matrices to identity matrices + maObjectTrans.identity(); + PostSetObjectTrans(); + + Orientation(maOrientation); + PostSetOrientation(); + + maTexture.identity(); + + mfLeftBound = mfBottomBound = -1.0; + mfRightBound = mfTopBound = 1.0; + + mfRatio = 0.0; + + maViewportRectangle = tools::Rectangle(-1, -1, 2, 2); + maVisibleRectangle = maViewportRectangle; + + mbPerspective = true; + + mbProjectionValid = false; + + CalcViewport(); +} + +/// Object transformation +void B3dTransformationSet::PostSetObjectTrans() +{ + // Assign and compute inverse + maInvObjectTrans = maObjectTrans; + maInvObjectTrans.invert(); +} + +void B3dTransformationSet::SetOrientation(const basegfx::B3DPoint& rVRP, const basegfx::B3DVector& rVPN, const basegfx::B3DVector& rVUP) +{ + maOrientation.identity(); + Orientation(maOrientation, rVRP, rVPN, rVUP); + + PostSetOrientation(); +} + +void B3dTransformationSet::PostSetOrientation() +{ + // Assign and compute inverse + maInvOrientation = maOrientation; + maInvOrientation.invert(); +} + +/// Projections for transformations +void B3dTransformationSet::SetProjection(const basegfx::B3DHomMatrix& mProject) +{ + maProjection = mProject; + PostSetProjection(); +} + +const basegfx::B3DHomMatrix& B3dTransformationSet::GetProjection() +{ + if(!mbProjectionValid) + CalcViewport(); + return maProjection; +} + +void B3dTransformationSet::PostSetProjection() +{ + // Assign and compute inverse + maInvProjection = GetProjection(); + maInvProjection.invert(); +} + +/// Transformations for viewport +void B3dTransformationSet::CalcViewport() +{ + // Parameters for projection + double fLeft(mfLeftBound); + double fRight(mfRightBound); + double fBottom(mfBottomBound); + double fTop(mfTopBound); + + // Adjust projection to aspect ratio, if set + if(GetRatio() != 0.0) + { + // Compute current aspect ratio of boundaries + double fBoundWidth = static_cast<double>(maViewportRectangle.GetWidth() + 1); + double fBoundHeight = static_cast<double>(maViewportRectangle.GetHeight() + 1); + double fActRatio = 1; + double fFactor; + + if(fBoundWidth != 0.0) + fActRatio = fBoundHeight / fBoundWidth; + // FIXME else in this case has a lot of problems, should this return. + + // scale down larger part + if(fActRatio > mfRatio) + { + // scale down Y + fFactor = fActRatio; + fTop *= fFactor; + fBottom *= fFactor; + } + else + { + // scale down X + fFactor = 1.0 / fActRatio; + fRight *= fFactor; + fLeft *= fFactor; + } + } + + // Do projection and object areas overlap? + maSetBound = maViewportRectangle; + + // Reset projection with new values + basegfx::B3DHomMatrix aNewProjection; + + // #i36281# + // OpenGL needs a little more rough additional size to not let + // the front face vanish. Changed from SMALL_DVALUE to 0.000001, + // which is 1/10000th, comared with 1/tenth of a million from SMALL_DVALUE. + const double fDistPart((gfFarBound - gfNearBound) * 0.0001); + + // To avoid critical clipping, set Near & Far generously + if(mbPerspective) + { + Frustum(aNewProjection, fLeft, fRight, fBottom, fTop, gfNearBound - fDistPart, gfFarBound + fDistPart); + } + else + { + Ortho(aNewProjection, fLeft, fRight, fBottom, fTop, gfNearBound - fDistPart, gfFarBound + fDistPart); + } + + // Set to true to guarantee loop termination + mbProjectionValid = true; + + // set new projection + SetProjection(aNewProjection); + + // fill parameters for ViewportTransformation + // Translation + maTranslate.setX(static_cast<double>(maSetBound.Left()) + ((maSetBound.GetWidth() - 1) / 2.0)); + maTranslate.setY(static_cast<double>(maSetBound.Top()) + ((maSetBound.GetHeight() - 1) / 2.0)); + maTranslate.setZ(ZBUFFER_DEPTH_RANGE / 2.0); + + // Scaling + maScale.setX((maSetBound.GetWidth() - 1) / 2.0); + maScale.setY((maSetBound.GetHeight() - 1) / -2.0); + maScale.setZ(ZBUFFER_DEPTH_RANGE / 2.0); +} + +void B3dTransformationSet::SetRatio(double fNew) +{ + if(mfRatio != fNew) + { + mfRatio = fNew; + mbProjectionValid = false; + } +} + +void B3dTransformationSet::SetDeviceRectangle(double fL, double fR, double fB, double fT) +{ + if(fL != mfLeftBound || fR != mfRightBound || fB != mfBottomBound || fT != mfTopBound) + { + mfLeftBound = fL; + mfRightBound = fR; + mfBottomBound = fB; + mfTopBound = fT; + + mbProjectionValid = false; + + // Broadcast changes + DeviceRectangleChange(); + } +} + +void B3dTransformationSet::DeviceRectangleChange() +{ +} + +void B3dTransformationSet::SetPerspective(bool bNew) +{ + if(mbPerspective != bNew) + { + mbPerspective = bNew; + mbProjectionValid = false; + } +} + +void B3dTransformationSet::SetViewportRectangle(tools::Rectangle const & rRect, tools::Rectangle const & rVisible) +{ + if(rRect != maViewportRectangle || rVisible != maVisibleRectangle) + { + maViewportRectangle = rRect; + maVisibleRectangle = rVisible; + + mbProjectionValid = false; + } +} + +// direct access to various transformations + +basegfx::B3DPoint B3dTransformationSet::WorldToEyeCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= maOrientation; + return aVec; +} + +basegfx::B3DPoint B3dTransformationSet::EyeToWorldCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= maInvOrientation; + return aVec; +} + +// B3dViewport ----------------------------------------------------------------- + +B3dViewport::B3dViewport() +: B3dTransformationSet(), + aVRP(0, 0, 0), + aVPN(0, 0, 1), + aVUV(0, 1, 0) +{ + CalcOrientation(); +} + +B3dViewport::~B3dViewport() +{ +} + +void B3dViewport::SetVUV(const basegfx::B3DVector& rNewVUV) +{ + aVUV = rNewVUV; + CalcOrientation(); +} + +void B3dViewport::SetViewportValues( + const basegfx::B3DPoint& rNewVRP, + const basegfx::B3DVector& rNewVPN, + const basegfx::B3DVector& rNewVUV) +{ + aVRP = rNewVRP; + aVPN = rNewVPN; + aVUV = rNewVUV; + CalcOrientation(); +} + +void B3dViewport::CalcOrientation() +{ + SetOrientation(aVRP, aVPN, aVUV); +} + +// B3dCamera ------------------------------------------------------------------- + +B3dCamera::B3dCamera( + const basegfx::B3DPoint& rPos, const basegfx::B3DVector& rLkAt, + double fFocLen, double fBnkAng) +: B3dViewport(), + aPosition(rPos), + aLookAt(rLkAt), + fFocalLength(fFocLen), + fBankAngle(fBnkAng) +{ + CalcNewViewportValues(); +} + +B3dCamera::~B3dCamera() +{ +} + +void B3dCamera::DeviceRectangleChange() +{ + // call parent + B3dViewport::DeviceRectangleChange(); + + // react to changes + CalcNewViewportValues(); +} + +void B3dCamera::CalcNewViewportValues() +{ + basegfx::B3DVector aViewVector(aPosition - aLookAt); + basegfx::B3DVector aNewVPN(aViewVector); + + basegfx::B3DVector aNewVUV(0.0, 1.0, 0.0); + if(aNewVPN.getLength() < aNewVPN.getY()) + aNewVUV.setX(0.5); + + aNewVUV.normalize(); + aNewVPN.normalize(); + + basegfx::B3DVector aNewToTheRight = aNewVPN.getPerpendicular(aNewVUV); + aNewToTheRight.normalize(); + aNewVUV = aNewToTheRight.getPerpendicular(aNewVPN); + aNewVUV.normalize(); + + SetViewportValues(aPosition, aNewVPN, aNewVUV); + CalcFocalLength(); + + if(fBankAngle != 0.0) + { + basegfx::B3DHomMatrix aRotMat; + aRotMat.rotate(0.0, 0.0, fBankAngle); + basegfx::B3DVector aUp(0.0, 1.0, 0.0); + aUp *= aRotMat; + aUp = EyeToWorldCoor(aUp); + aUp.normalize(); + SetVUV(aUp); + } +} + +void B3dCamera::CalcFocalLength() +{ + double fWidth = GetDeviceRectangleWidth(); + + // Adjust focal length based on given position + basegfx::B3DPoint aOldPosition = WorldToEyeCoor({}); + if(fWidth != 0.0) + fFocalLength = aOldPosition.getZ() / fWidth * 35.0; + if(fFocalLength < 5.0) + fFocalLength = 5.0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |