/* -*- 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 #include // 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(maViewportRectangle.GetWidth() + 1); double fBoundHeight = static_cast(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(maSetBound.Left()) + ((maSetBound.GetWidth() - 1) / 2.0)); maTranslate.setY(static_cast(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 const basegfx::B3DPoint B3dTransformationSet::WorldToEyeCoor(const basegfx::B3DPoint& rVec) { basegfx::B3DPoint aVec(rVec); aVec *= maOrientation; return aVec; } const 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: */