diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /slideshow/source/engine/opengl | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | slideshow/source/engine/opengl/Operation.cxx | 225 | ||||
-rw-r--r-- | slideshow/source/engine/opengl/Operation.hxx | 266 | ||||
-rw-r--r-- | slideshow/source/engine/opengl/TransitionImpl.cxx | 2292 | ||||
-rw-r--r-- | slideshow/source/engine/opengl/TransitionImpl.hxx | 393 | ||||
-rw-r--r-- | slideshow/source/engine/opengl/TransitionerImpl.cxx | 1346 | ||||
-rw-r--r-- | slideshow/source/engine/opengl/ogltrans.component | 26 |
6 files changed, 4548 insertions, 0 deletions
diff --git a/slideshow/source/engine/opengl/Operation.cxx b/slideshow/source/engine/opengl/Operation.cxx new file mode 100644 index 000000000..ec83d6b74 --- /dev/null +++ b/slideshow/source/engine/opengl/Operation.cxx @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2015 by Collabora, Ltd. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <sal/config.h> + +#include "Operation.hxx" + +#include <basegfx/numeric/ftools.hxx> + +#include <glm/gtc/matrix_transform.hpp> +#include <glm/gtc/type_ptr.hpp> + +SRotate::SRotate(const glm::vec3& Axis, const glm::vec3& Origin, + double Angle, bool bInter, double T0, double T1): + Operation(bInter, T0, T1), + axis(Axis), + origin(Origin), + angle(basegfx::deg2rad(Angle)) +{ +} + +SScale::SScale(const glm::vec3& Scale, const glm::vec3& Origin, + bool bInter, double T0, double T1): + Operation(bInter, T0, T1), + scale(Scale), + origin(Origin) +{ +} + +RotateAndScaleDepthByWidth::RotateAndScaleDepthByWidth(const glm::vec3& Axis, + const glm::vec3& Origin, double Angle, bool bScale, bool bInter, double T0, double T1): + Operation(bInter, T0, T1), + axis(Axis), + origin(Origin), + angle(basegfx::deg2rad(Angle)), + scale(bScale) +{ +} + +RotateAndScaleDepthByHeight::RotateAndScaleDepthByHeight(const glm::vec3& Axis, + const glm::vec3& Origin, double Angle, bool bScale, bool bInter, double T0, double T1): + Operation(bInter, T0, T1), + axis(Axis), + origin(Origin), + angle(basegfx::deg2rad(Angle)), + scale(bScale) +{ +} + + +STranslate::STranslate(const glm::vec3& Vector, bool bInter, double T0, double T1): + Operation(bInter, T0, T1), + vector(Vector) +{ +} + +std::shared_ptr<SRotate> +makeSRotate(const glm::vec3& Axis,const glm::vec3& Origin,double Angle,bool bInter, double T0, double T1) +{ + return std::make_shared<SRotate>(Axis, Origin, Angle, bInter, T0, T1); +} + +std::shared_ptr<SScale> +makeSScale(const glm::vec3& Scale, const glm::vec3& Origin,bool bInter, double T0, double T1) +{ + return std::make_shared<SScale>(Scale, Origin, bInter, T0, T1); +} + +std::shared_ptr<STranslate> +makeSTranslate(const glm::vec3& Vector,bool bInter, double T0, double T1) +{ + return std::make_shared<STranslate>(Vector, bInter, T0, T1); +} + +std::shared_ptr<SEllipseTranslate> +makeSEllipseTranslate(double dWidth, double dHeight, double dStartPosition, double dEndPosition, bool bInter, double T0, double T1) +{ + return std::make_shared<SEllipseTranslate>(dWidth, dHeight, dStartPosition, dEndPosition, bInter, T0, T1); +} + +std::shared_ptr<RotateAndScaleDepthByWidth> +makeRotateAndScaleDepthByWidth(const glm::vec3& Axis,const glm::vec3& Origin,double Angle, bool bScale, bool bInter, double T0, double T1) +{ + return std::make_shared<RotateAndScaleDepthByWidth>(Axis, Origin, Angle, bScale, bInter, T0, T1); +} + +std::shared_ptr<RotateAndScaleDepthByHeight> +makeRotateAndScaleDepthByHeight(const glm::vec3& Axis,const glm::vec3& Origin,double Angle,bool bScale, bool bInter, double T0, double T1) +{ + return std::make_shared<RotateAndScaleDepthByHeight>(Axis, Origin, Angle, bScale, bInter, T0, T1); +} + +static double intervalInter(double t, double T0, double T1) +{ + return ( t - T0 ) / ( T1 - T0 ); +} + +void STranslate::interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const +{ + if(t <= mnT0) + return; + if(!mbInterpolate || t > mnT1) + t = mnT1; + t = intervalInter(t,mnT0,mnT1); + matrix = glm::translate(matrix, glm::vec3(SlideWidthScale*t*vector.x, SlideHeightScale*t*vector.y, t*vector.z)); +} + +void SRotate::interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const +{ + if(t <= mnT0) + return; + if(!mbInterpolate || t > mnT1) + t = mnT1; + t = intervalInter(t,mnT0,mnT1); + glm::vec3 translation_vector(SlideWidthScale*origin.x, SlideHeightScale*origin.y, origin.z); + glm::vec3 scale_vector(SlideWidthScale * SlideWidthScale, SlideHeightScale * SlideHeightScale, 1); + matrix = glm::translate(matrix, translation_vector); + matrix = glm::scale(matrix, scale_vector); + matrix = glm::rotate(matrix, static_cast<float>(t*angle), axis); + matrix = glm::scale(matrix, 1.f / scale_vector); + matrix = glm::translate(matrix, -translation_vector); +} + +void SScale::interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const +{ + if(t <= mnT0) + return; + if(!mbInterpolate || t > mnT1) + t = mnT1; + t = intervalInter(t,mnT0,mnT1); + glm::vec3 translation_vector(SlideWidthScale*origin.x, SlideHeightScale*origin.y, origin.z); + matrix = glm::translate(matrix, translation_vector); + matrix = glm::scale(matrix, static_cast<float>(1 - t) + static_cast<float>(t) * scale); + matrix = glm::translate(matrix, -translation_vector); +} + +void RotateAndScaleDepthByWidth::interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const +{ + if(t <= mnT0) + return; + if(!mbInterpolate || t > mnT1) + t = mnT1; + t = intervalInter(t,mnT0,mnT1); + glm::vec3 translation_vector(SlideWidthScale*origin.x, SlideHeightScale*origin.y, SlideWidthScale*origin.z); + glm::vec3 scale_vector(SlideWidthScale * SlideWidthScale, SlideHeightScale * SlideHeightScale, 1); + matrix = glm::translate(matrix, translation_vector); + if (scale) + matrix = glm::scale(matrix, scale_vector); + matrix = glm::rotate(matrix, static_cast<float>(t*angle), axis); + if (scale) + matrix = glm::scale(matrix, 1.f / scale_vector); + matrix = glm::translate(matrix, -translation_vector); +} + +void RotateAndScaleDepthByHeight::interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const +{ + if(t <= mnT0) + return; + if(!mbInterpolate || t > mnT1) + t = mnT1; + t = intervalInter(t,mnT0,mnT1); + glm::vec3 translation_vector(SlideWidthScale*origin.x, SlideHeightScale*origin.y, SlideHeightScale*origin.z); + glm::vec3 scale_vector(SlideWidthScale * SlideWidthScale, SlideHeightScale * SlideHeightScale, 1); + matrix = glm::translate(matrix, translation_vector); + if (scale) + matrix = glm::scale(matrix, scale_vector); + matrix = glm::rotate(matrix, static_cast<float>(t*angle), axis); + if (scale) + matrix = glm::scale(matrix, 1.f / scale_vector); + matrix = glm::translate(matrix, -translation_vector); +} + +SEllipseTranslate::SEllipseTranslate(double dWidth, double dHeight, double dStartPosition, + double dEndPosition, bool bInter, double T0, double T1): + Operation(bInter, T0, T1) +{ + width = dWidth; + height = dHeight; + startPosition = dStartPosition; + endPosition = dEndPosition; +} + +void SEllipseTranslate::interpolate(glm::mat4& matrix, double t, double /* SlideWidthScale */, double /* SlideHeightScale */) const +{ + if(t <= mnT0) + return; + if(!mbInterpolate || t > mnT1) + t = mnT1; + t = intervalInter(t,mnT0,mnT1); + + double a1, a2, x, y; + a1 = startPosition*2*M_PI; + a2 = (startPosition + t*(endPosition - startPosition))*2*M_PI; + x = width*(cos (a2) - cos (a1))/2; + y = height*(sin (a2) - sin (a1))/2; + + matrix = glm::translate(matrix, glm::vec3(x, 0, y)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/opengl/Operation.hxx b/slideshow/source/engine/opengl/Operation.hxx new file mode 100644 index 000000000..0ee818f24 --- /dev/null +++ b/slideshow/source/engine/opengl/Operation.hxx @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2015 by Collabora, Ltd. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef INCLUDED_OGLTRANS_OPERATIONS_HXX_ +#define INCLUDED_OGLTRANS_OPERATIONS_HXX_ + +#include <config_lgpl.h> +#include <glm/gtc/type_ptr.hpp> + +#include <memory> + +/** This class is to be derived to make any operation (transform) you may need in order to construct your transitions +*/ +class Operation +{ +public: + virtual ~Operation(){} + Operation(const Operation&) = delete; + Operation& operator=(const Operation&) = delete; + +protected: + /** Should this operation be interpolated . If TRUE, the transform will smoothly move from making no difference from t = 0.0 to mnT0 to being completely transformed from t = mnT1 to 1. If FALSE, the transform will be ineffectual from t = 0 to mnT0, and completely transformed from t = mnT0 to 1. + */ + bool mbInterpolate; + + /** time to begin the transformation + */ + double mnT0; + + /** time to finish the transformation + */ + double mnT1; +public: + /** this is the function that is called to give the Operation to OpenGL. + + @param t + time from t = 0 to t = 1 + + @param SlideWidthScale + width of slide divided by width of window + + @param SlideHeightScale + height of slide divided by height of window + + */ + virtual void interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const = 0; + +protected: + Operation(bool bInterpolate, double nT0, double nT1): + mbInterpolate(bInterpolate), mnT0(nT0), mnT1(nT1){} +}; + +/** this class is a generic CounterClockWise(CCW) rotation with an axis angle +*/ +class SRotate: public Operation +{ +public: + virtual void interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const override; + + /** Constructor + + @param Axis + axis to rotate about + + @param Origin + position that rotation axis runs through + + @param Angle + angle in degrees of CCW rotation + + @param bInter + see Operation + + @param T0 + transformation starting time + + @param T1 + transformation ending time + + */ + SRotate(const glm::vec3& Axis, const glm::vec3& Origin, double Angle, + bool bInter, double T0, double T1); +private: + /** axis to rotate CCW about + */ + glm::vec3 axis; + + /** position that rotation axis runs through + */ + glm::vec3 origin; + + /** angle in degrees of CCW rotation + */ + double angle; +}; + +std::shared_ptr<SRotate> +makeSRotate(const glm::vec3& Axis, const glm::vec3& Origin, double Angle, + bool bInter, double T0, double T1); + +/** scaling transformation +*/ +class SScale: public Operation +{ +public: + virtual void interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const override; + + /** Constructor + + @param Scale + amount to scale by + + @param Origin + position that rotation axis runs through + + @param bInter + see Operation + + @param T0 + transformation starting time + + @param T1 + transformation ending time + + */ + SScale(const glm::vec3& Scale, const glm::vec3& Origin,bool bInter, double T0, double T1); +private: + glm::vec3 scale; + glm::vec3 origin; +}; + +std::shared_ptr<SScale> +makeSScale(const glm::vec3& Scale, const glm::vec3& Origin,bool bInter, double T0, double T1); + +/** translation transformation +*/ +class STranslate: public Operation +{ +public: + virtual void interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const override; + + /** Constructor + + @param Vector + vector to translate + + @param bInter + see Operation + + @param T0 + transformation starting time + + @param T1 + transformation ending time + + */ + STranslate(const glm::vec3& Vector,bool bInter, double T0, double T1); +private: + /** vector to translate by + */ + glm::vec3 vector; +}; + +std::shared_ptr<STranslate> +makeSTranslate(const glm::vec3& Vector,bool bInter, double T0, double T1); + +/** translation transformation +*/ +class SEllipseTranslate: public Operation +{ +public: + virtual void interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const override; + + /** Constructor + + @param Vector + vector to translate + + @param bInter + see Operation + + @param T0 + transformation starting time + + @param T1 + transformation ending time + + */ + SEllipseTranslate(double dWidth, double dHeight, double dStartPosition, double dEndPosition, bool bInter, double T0, double T1); +private: + /** width and length of the ellipse + */ + double width, height; + + /** start and end position on the ellipse <0,1> + */ + double startPosition; + double endPosition; +}; + +std::shared_ptr<SEllipseTranslate> +makeSEllipseTranslate(double dWidth, double dHeight, double dStartPosition, double dEndPosition, bool bInter, double T0, double T1); + +/** Same as SRotate, except the depth is scaled by the width of the slide divided by the width of the window. +*/ +class RotateAndScaleDepthByWidth: public Operation +{ +public: + virtual void interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const override; + + RotateAndScaleDepthByWidth(const glm::vec3& Axis,const glm::vec3& Origin,double Angle, bool bScale, bool bInter, double T0, double T1); +private: + glm::vec3 axis; + glm::vec3 origin; + double angle; + bool scale; +}; + +std::shared_ptr<RotateAndScaleDepthByWidth> +makeRotateAndScaleDepthByWidth(const glm::vec3& Axis,const glm::vec3& Origin,double Angle, bool bScale, bool bInter, double T0, double T1); + +/** Same as SRotate, except the depth is scaled by the width of the slide divided by the height of the window. +*/ +class RotateAndScaleDepthByHeight: public Operation +{ +public: + virtual void interpolate(glm::mat4& matrix, double t, double SlideWidthScale, double SlideHeightScale) const override; + + RotateAndScaleDepthByHeight(const glm::vec3& Axis,const glm::vec3& Origin,double Angle, bool bScale, bool bInter, double T0, double T1); +private: + glm::vec3 axis; + glm::vec3 origin; + double angle; + bool scale; +}; + +std::shared_ptr<RotateAndScaleDepthByHeight> +makeRotateAndScaleDepthByHeight(const glm::vec3& Axis,const glm::vec3& Origin,double Angle, bool bScale, bool bInter, double T0, double T1); + +#endif // INCLUDED_SLIDESHOW_OPERATIONS_HXX_ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/opengl/TransitionImpl.cxx b/slideshow/source/engine/opengl/TransitionImpl.cxx new file mode 100644 index 000000000..ba43acddc --- /dev/null +++ b/slideshow/source/engine/opengl/TransitionImpl.cxx @@ -0,0 +1,2292 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <glm/gtc/matrix_transform.hpp> +#include <glm/gtc/type_ptr.hpp> +#include <vcl/opengl/OpenGLHelper.hxx> +#include <vcl/opengl/OpenGLContext.hxx> +#include <sal/log.hxx> + +#include <algorithm> +#include <array> + +#include <comphelper/random.hxx> + +#include "Operation.hxx" +#include "TransitionImpl.hxx" +#include <cmath> + +TransitionScene::TransitionScene(TransitionScene const& rOther) + : maLeavingSlidePrimitives(rOther.maLeavingSlidePrimitives) + , maEnteringSlidePrimitives(rOther.maEnteringSlidePrimitives) + , maOverallOperations(rOther.maOverallOperations) + , maSceneObjects(rOther.maSceneObjects) +{ +} + +TransitionScene& TransitionScene::operator=(const TransitionScene& rOther) +{ + TransitionScene aTmp(rOther); + swap(aTmp); + return *this; +} + +void TransitionScene::swap(TransitionScene& rOther) +{ + using std::swap; + + swap(maLeavingSlidePrimitives, rOther.maLeavingSlidePrimitives); + swap(maEnteringSlidePrimitives, rOther.maEnteringSlidePrimitives); + swap(maOverallOperations, rOther.maOverallOperations); + swap(maSceneObjects, rOther.maSceneObjects); +} + +OGLTransitionImpl::~OGLTransitionImpl() +{ +} + +void OGLTransitionImpl::uploadModelViewProjectionMatrices() +{ + double EyePos(10.0); + double const RealF(1.0); + double const RealN(-1.0); + double const RealL(-1.0); + double RealR(1.0); + double const RealB(-1.0); + double RealT(1.0); + double ClipN(EyePos+5.0*RealN); + double ClipF(EyePos+15.0*RealF); + double ClipL(RealL*8.0); + double ClipR(RealR*8.0); + double ClipB(RealB*8.0); + double ClipT(RealT*8.0); + + glm::mat4 projection = glm::frustum<float>(ClipL, ClipR, ClipB, ClipT, ClipN, ClipF); + //This scaling is to take the plane with BottomLeftCorner(-1,-1,0) and TopRightCorner(1,1,0) and map it to the screen after the perspective division. + glm::vec3 scale(1.0 / (((RealR * 2.0 * ClipN) / (EyePos * (ClipR - ClipL))) - ((ClipR + ClipL) / (ClipR - ClipL))), + 1.0 / (((RealT * 2.0 * ClipN) / (EyePos * (ClipT - ClipB))) - ((ClipT + ClipB) / (ClipT - ClipB))), + 1.0); + projection = glm::scale(projection, scale); + glm::mat4 modelview = glm::translate(glm::mat4(), glm::vec3(0, 0, -EyePos)); + + GLint location = glGetUniformLocation( m_nProgramObject, "u_projectionMatrix" ); + if( location != -1 ) { + glUniformMatrix4fv(location, 1, false, glm::value_ptr(projection)); + CHECK_GL_ERROR(); + } + + location = glGetUniformLocation( m_nProgramObject, "u_modelViewMatrix" ); + if( location != -1 ) { + glUniformMatrix4fv(location, 1, false, glm::value_ptr(modelview)); + CHECK_GL_ERROR(); + } +} + +static std::vector<int> uploadPrimitives(const Primitives_t& primitives) +{ + int size = 0; + for (const Primitive& primitive: primitives) + size += primitive.getVerticesByteSize(); + + CHECK_GL_ERROR(); + glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_STATIC_DRAW); + CHECK_GL_ERROR(); + Vertex *buf = static_cast<Vertex*>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); + + std::vector<int> indices; + int last_pos = 0; + for (const Primitive& primitive: primitives) { + indices.push_back(last_pos); + int num = primitive.writeVertices(buf); + buf += num; + last_pos += num; + } + + CHECK_GL_ERROR(); + glUnmapBuffer(GL_ARRAY_BUFFER); + CHECK_GL_ERROR(); + return indices; +} + +bool OGLTransitionImpl::prepare( sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, OpenGLContext *pContext ) +{ + m_nProgramObject = makeShader(); + if (!m_nProgramObject) + return false; + + CHECK_GL_ERROR(); + glUseProgram( m_nProgramObject ); + CHECK_GL_ERROR(); + + const SceneObjects_t& rSceneObjects(maScene.getSceneObjects()); + for(size_t i(0); i != rSceneObjects.size(); ++i) { + rSceneObjects[i]->prepare(m_nProgramObject); + } + + GLint location = glGetUniformLocation( m_nProgramObject, "leavingSlideTexture" ); + if( location != -1 ) { + glUniform1i( location, 0 ); // texture unit 0 + CHECK_GL_ERROR(); + } + + location = glGetUniformLocation( m_nProgramObject, "enteringSlideTexture" ); + if( location != -1 ) { + glUniform1i( location, 2 ); // texture unit 2 + CHECK_GL_ERROR(); + } + + uploadModelViewProjectionMatrices(); + + m_nPrimitiveTransformLocation = glGetUniformLocation( m_nProgramObject, "u_primitiveTransformMatrix" ); + m_nSceneTransformLocation = glGetUniformLocation( m_nProgramObject, "u_sceneTransformMatrix" ); + m_nOperationsTransformLocation = glGetUniformLocation( m_nProgramObject, "u_operationsTransformMatrix" ); + m_nTimeLocation = glGetUniformLocation( m_nProgramObject, "time" ); + + glGenVertexArrays(1, &m_nVertexArrayObject); + glBindVertexArray(m_nVertexArrayObject); + + glGenBuffers(1, &m_nVertexBufferObject); + glBindBuffer(GL_ARRAY_BUFFER, m_nVertexBufferObject); + + // In practice both leaving and entering slides share the same primitives. + m_nFirstIndices = uploadPrimitives(getScene().getLeavingSlide()); + + // Attribute bindings + m_nPositionLocation = glGetAttribLocation(m_nProgramObject, "a_position"); + if (m_nPositionLocation != -1) { + glEnableVertexAttribArray(m_nPositionLocation); + glVertexAttribPointer( m_nPositionLocation, 3, GL_FLOAT, false, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, position)) ); + CHECK_GL_ERROR(); + } + + m_nNormalLocation = glGetAttribLocation(m_nProgramObject, "a_normal"); + if (m_nNormalLocation != -1) { + glEnableVertexAttribArray(m_nNormalLocation); + glVertexAttribPointer( m_nNormalLocation, 3, GL_FLOAT, false, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, normal)) ); + CHECK_GL_ERROR(); + } + + m_nTexCoordLocation = glGetAttribLocation(m_nProgramObject, "a_texCoord"); + if (m_nTexCoordLocation != -1) { + glEnableVertexAttribArray(m_nTexCoordLocation); + glVertexAttribPointer( m_nTexCoordLocation, 2, GL_FLOAT, false, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, texcoord)) ); + CHECK_GL_ERROR(); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + CHECK_GL_ERROR(); + + prepareTransition( glLeavingSlideTex, glEnteringSlideTex, pContext ); + return true; +} + +void OGLTransitionImpl::finish() +{ + const SceneObjects_t& rSceneObjects(maScene.getSceneObjects()); + for(size_t i(0); i != rSceneObjects.size(); ++i) { + rSceneObjects[i]->finish(); + } + + finishTransition(); + + CHECK_GL_ERROR(); + if( m_nProgramObject ) { + glDeleteBuffers(1, &m_nVertexBufferObject); + m_nVertexBufferObject = 0; + glDeleteVertexArrays(1, &m_nVertexArrayObject); + m_nVertexArrayObject = 0; + glDeleteProgram( m_nProgramObject ); + m_nProgramObject = 0; + } + CHECK_GL_ERROR(); +} + +void OGLTransitionImpl::prepare( double, double ) +{ +} + +void OGLTransitionImpl::cleanup() +{ +} + +void OGLTransitionImpl::prepareTransition( sal_Int32, sal_Int32, OpenGLContext* ) +{ +} + +void OGLTransitionImpl::finishTransition() +{ +} + +void OGLTransitionImpl::displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, double SlideWidthScale, double SlideHeightScale, OpenGLContext * ) +{ + CHECK_GL_ERROR(); + applyOverallOperations( nTime, SlideWidthScale, SlideHeightScale ); + + glUniform1f( m_nTimeLocation, nTime ); + + glActiveTexture( GL_TEXTURE2 ); + glBindTexture( GL_TEXTURE_2D, glEnteringSlideTex ); + glActiveTexture( GL_TEXTURE0 ); + + displaySlide( nTime, glLeavingSlideTex, getScene().getLeavingSlide(), SlideWidthScale, SlideHeightScale ); + CHECK_GL_ERROR(); +} + +void OGLTransitionImpl::display( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, + double SlideWidth, double SlideHeight, double DispWidth, double DispHeight, OpenGLContext *pContext ) +{ + const double SlideWidthScale = SlideWidth/DispWidth; + const double SlideHeightScale = SlideHeight/DispHeight; + + CHECK_GL_ERROR(); + glBindVertexArray(m_nVertexArrayObject); + prepare( SlideWidth, SlideHeight ); + + CHECK_GL_ERROR(); + displaySlides_( nTime, glLeavingSlideTex, glEnteringSlideTex, SlideWidthScale, SlideHeightScale, pContext ); + CHECK_GL_ERROR(); + displayScene( nTime, SlideWidth, SlideHeight, DispWidth, DispHeight ); + CHECK_GL_ERROR(); +} + +void OGLTransitionImpl::applyOverallOperations( double nTime, double SlideWidthScale, double SlideHeightScale ) +{ + const Operations_t& rOverallOperations(maScene.getOperations()); + glm::mat4 matrix; + for(size_t i(0); i != rOverallOperations.size(); ++i) + rOverallOperations[i]->interpolate(matrix, nTime, SlideWidthScale, SlideHeightScale); + CHECK_GL_ERROR(); + if (m_nOperationsTransformLocation != -1) { + glUniformMatrix4fv(m_nOperationsTransformLocation, 1, false, glm::value_ptr(matrix)); + CHECK_GL_ERROR(); + } +} + +static void displayPrimitives(const Primitives_t& primitives, GLint primitiveTransformLocation, double nTime, double WidthScale, double HeightScale, std::vector<int>::const_iterator first) +{ + for (const Primitive& primitive: primitives) + primitive.display(primitiveTransformLocation, nTime, WidthScale, HeightScale, *first++); +} + +void +OGLTransitionImpl::displaySlide( + const double nTime, + const sal_Int32 glSlideTex, const Primitives_t& primitives, + double SlideWidthScale, double SlideHeightScale ) +{ + CHECK_GL_ERROR(); + glBindTexture(GL_TEXTURE_2D, glSlideTex); + CHECK_GL_ERROR(); + if (m_nSceneTransformLocation != -1) { + glUniformMatrix4fv(m_nSceneTransformLocation, 1, false, glm::value_ptr(glm::mat4())); + CHECK_GL_ERROR(); + } + displayPrimitives(primitives, m_nPrimitiveTransformLocation, nTime, SlideWidthScale, SlideHeightScale, m_nFirstIndices.cbegin()); + CHECK_GL_ERROR(); +} + +void +OGLTransitionImpl::displayUnbufferedSlide( + const double nTime, + const sal_Int32 glSlideTex, const Primitives_t& primitives, + double SlideWidthScale, double SlideHeightScale ) +{ + CHECK_GL_ERROR(); + glBindTexture(GL_TEXTURE_2D, glSlideTex); + CHECK_GL_ERROR(); + glBindVertexArray(0); + CHECK_GL_ERROR(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + CHECK_GL_ERROR(); + if (m_nSceneTransformLocation != -1) { + glUniformMatrix4fv(m_nSceneTransformLocation, 1, false, glm::value_ptr(glm::mat4())); + CHECK_GL_ERROR(); + } + for (const Primitive& primitive: primitives) + primitive.display(m_nPrimitiveTransformLocation, nTime, SlideWidthScale, SlideHeightScale); + CHECK_GL_ERROR(); + glBindVertexArray(m_nVertexArrayObject); + CHECK_GL_ERROR(); + glBindBuffer(GL_ARRAY_BUFFER, m_nVertexBufferObject); + CHECK_GL_ERROR(); +} + +void OGLTransitionImpl::displayScene( double nTime, double SlideWidth, double SlideHeight, double DispWidth, double DispHeight ) +{ + const SceneObjects_t& rSceneObjects(maScene.getSceneObjects()); + CHECK_GL_ERROR(); + for(size_t i(0); i != rSceneObjects.size(); ++i) + rSceneObjects[i]->display(m_nSceneTransformLocation, m_nPrimitiveTransformLocation, nTime, SlideWidth, SlideHeight, DispWidth, DispHeight); + CHECK_GL_ERROR(); +} + +void Primitive::display(GLint primitiveTransformLocation, double nTime, double WidthScale, double HeightScale) const +{ + glm::mat4 matrix; + applyOperations( matrix, nTime, WidthScale, HeightScale ); + + CHECK_GL_ERROR(); + if (primitiveTransformLocation != -1) { + glUniformMatrix4fv(primitiveTransformLocation, 1, false, glm::value_ptr(matrix)); + CHECK_GL_ERROR(); + } + + GLuint nVertexArrayObject; + glGenVertexArrays(1, &nVertexArrayObject); + CHECK_GL_ERROR(); + glBindVertexArray(nVertexArrayObject); + CHECK_GL_ERROR(); + + GLuint nBuffer; + glGenBuffers(1, &nBuffer); + CHECK_GL_ERROR(); + glBindBuffer(GL_ARRAY_BUFFER, nBuffer); + CHECK_GL_ERROR(); + glBufferData(GL_ARRAY_BUFFER, getVerticesByteSize(), Vertices.data(), GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + CHECK_GL_ERROR(); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr); + CHECK_GL_ERROR(); + glDrawArrays( GL_TRIANGLES, 0, Vertices.size() ); + CHECK_GL_ERROR(); + + glDeleteBuffers(1, &nBuffer); + CHECK_GL_ERROR(); + + glDeleteVertexArrays(1, &nVertexArrayObject); + CHECK_GL_ERROR(); +} + +void Primitive::display(GLint primitiveTransformLocation, double nTime, double WidthScale, double HeightScale, int first) const +{ + glm::mat4 matrix; + applyOperations( matrix, nTime, WidthScale, HeightScale ); + + CHECK_GL_ERROR(); + if (primitiveTransformLocation != -1) { + glUniformMatrix4fv(primitiveTransformLocation, 1, false, glm::value_ptr(matrix)); + CHECK_GL_ERROR(); + } + glDrawArrays( GL_TRIANGLES, first, Vertices.size() ); + + CHECK_GL_ERROR(); +} + +void Primitive::applyOperations(glm::mat4& matrix, double nTime, double WidthScale, double HeightScale) const +{ + for(const auto & rOperation : Operations) + rOperation->interpolate(matrix, nTime, WidthScale, HeightScale); + matrix = glm::scale(matrix, glm::vec3(WidthScale, HeightScale, 1)); +} + +void SceneObject::display(GLint sceneTransformLocation, GLint primitiveTransformLocation, double nTime, double /* SlideWidth */, double /* SlideHeight */, double DispWidth, double DispHeight ) const +{ + // fixme: allow various model spaces, now we make it so that + // it is regular -1,-1 to 1,1, where the whole display fits in + glm::mat4 matrix; + if (DispHeight > DispWidth) + matrix = glm::scale(matrix, glm::vec3(DispHeight/DispWidth, 1, 1)); + else + matrix = glm::scale(matrix, glm::vec3(1, DispWidth/DispHeight, 1)); + CHECK_GL_ERROR(); + if (sceneTransformLocation != -1) { + glUniformMatrix4fv(sceneTransformLocation, 1, false, glm::value_ptr(matrix)); + CHECK_GL_ERROR(); + } + displayPrimitives(maPrimitives, primitiveTransformLocation, nTime, 1, 1, maFirstIndices.cbegin()); + CHECK_GL_ERROR(); +} + +void SceneObject::pushPrimitive(const Primitive &p) +{ + maPrimitives.push_back(p); +} + +SceneObject::SceneObject() + : maPrimitives() +{ +} + +SceneObject::~SceneObject() +{ +} + +namespace +{ + +class Iris : public SceneObject +{ +public: + Iris() = default; + + virtual void prepare(GLuint program) override; + virtual void display(GLint sceneTransformLocation, GLint primitiveTransformLocation, double nTime, double SlideWidth, double SlideHeight, double DispWidth, double DispHeight) const override; + virtual void finish() override; + +private: + GLuint maTexture = 0; + GLuint maBuffer = 0; + GLuint maVertexArray = 0; +}; + +void Iris::display(GLint sceneTransformLocation, GLint primitiveTransformLocation, double nTime, double SlideWidth, double SlideHeight, double DispWidth, double DispHeight ) const +{ + glBindVertexArray(maVertexArray); + CHECK_GL_ERROR(); + glBindTexture(GL_TEXTURE_2D, maTexture); + CHECK_GL_ERROR(); + SceneObject::display(sceneTransformLocation, primitiveTransformLocation, nTime, SlideWidth, SlideHeight, DispWidth, DispHeight); +} + +void Iris::prepare(GLuint program) +{ + CHECK_GL_ERROR(); + static const GLubyte img[3] = { 80, 80, 80 }; + + glGenTextures(1, &maTexture); + glBindTexture(GL_TEXTURE_2D, maTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, img); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + CHECK_GL_ERROR(); + + glGenVertexArrays(1, &maVertexArray); + glBindVertexArray(maVertexArray); + + glGenBuffers(1, &maBuffer); + glBindBuffer(GL_ARRAY_BUFFER, maBuffer); + maFirstIndices = uploadPrimitives(maPrimitives); + + // Attribute bindings + GLint location = glGetAttribLocation(program, "a_position"); + if (location != -1) { + glEnableVertexAttribArray(location); + glVertexAttribPointer( location, 3, GL_FLOAT, false, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, position)) ); + CHECK_GL_ERROR(); + } + + location = glGetAttribLocation(program, "a_normal"); + if (location != -1) { + glEnableVertexAttribArray(location); + glVertexAttribPointer( location, 3, GL_FLOAT, false, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, normal)) ); + CHECK_GL_ERROR(); + } + + location = glGetAttribLocation(program, "a_texCoord"); + if (location != -1) { + glEnableVertexAttribArray(location); + glVertexAttribPointer( location, 2, GL_FLOAT, false, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, texcoord)) ); + CHECK_GL_ERROR(); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void Iris::finish() +{ + CHECK_GL_ERROR(); + glDeleteBuffers(1, &maBuffer); + CHECK_GL_ERROR(); + glDeleteVertexArrays(1, &maVertexArray); + CHECK_GL_ERROR(); + glDeleteTextures(1, &maTexture); + CHECK_GL_ERROR(); +} + +} + +namespace +{ + +class ReflectionTransition : public OGLTransitionImpl +{ +public: + ReflectionTransition(const TransitionScene& rScene, const TransitionSettings& rSettings) + : OGLTransitionImpl(rScene, rSettings) + {} + +private: + virtual GLuint makeShader() const override; + virtual void displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, double SlideWidthScale, double SlideHeightScale, OpenGLContext *pContext ) override; + + virtual void prepareTransition( sal_Int32, sal_Int32, OpenGLContext* ) override { + glDisable(GL_CULL_FACE); + } + + virtual void finishTransition() override { + glEnable(GL_CULL_FACE); + } +}; + +GLuint ReflectionTransition::makeShader() const +{ + return OpenGLHelper::LoadShaders( "reflectionVertexShader", "reflectionFragmentShader" ); +} + +void ReflectionTransition::displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, + double SlideWidthScale, double SlideHeightScale, OpenGLContext * ) +{ + CHECK_GL_ERROR(); + applyOverallOperations( nTime, SlideWidthScale, SlideHeightScale ); + + sal_Int32 texture; + Primitives_t slide; + if (nTime < 0.5) { + texture = glLeavingSlideTex; + slide = getScene().getLeavingSlide(); + } else { + texture = glEnteringSlideTex; + slide = getScene().getEnteringSlide(); + } + + displaySlide( nTime, texture, slide, SlideWidthScale, SlideHeightScale ); + CHECK_GL_ERROR(); +} + +std::shared_ptr<OGLTransitionImpl> +makeReflectionTransition( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + Operations_t&& rOverallOperations, + const TransitionSettings& rSettings) +{ + return std::make_shared<ReflectionTransition>( + TransitionScene(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives), std::move(rOverallOperations), SceneObjects_t()), + rSettings); +} + +} + +namespace +{ + +class SimpleTransition : public OGLTransitionImpl +{ +public: + SimpleTransition(const TransitionScene& rScene, const TransitionSettings& rSettings) + : OGLTransitionImpl(rScene, rSettings) + { + } + +private: + virtual GLuint makeShader() const override; + + virtual void displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, double SlideWidthScale, double SlideHeightScale, OpenGLContext *pContext ) override; +}; + +GLuint SimpleTransition::makeShader() const +{ + return OpenGLHelper::LoadShaders( "basicVertexShader", "basicFragmentShader" ); +} + +void SimpleTransition::displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, + double SlideWidthScale, double SlideHeightScale, OpenGLContext * ) +{ + CHECK_GL_ERROR(); + applyOverallOperations( nTime, SlideWidthScale, SlideHeightScale ); + + CHECK_GL_ERROR(); + displaySlide( nTime, glLeavingSlideTex, getScene().getLeavingSlide(), SlideWidthScale, SlideHeightScale ); + displaySlide( nTime, glEnteringSlideTex, getScene().getEnteringSlide(), SlideWidthScale, SlideHeightScale ); + CHECK_GL_ERROR(); +} + +std::shared_ptr<OGLTransitionImpl> +makeSimpleTransition( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + Operations_t&& rOverallOperations, + SceneObjects_t&& rSceneObjects, + const TransitionSettings& rSettings) +{ + return std::make_shared<SimpleTransition>( + TransitionScene(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives), + std::move(rOverallOperations), std::move(rSceneObjects)), + rSettings); +} + +std::shared_ptr<OGLTransitionImpl> +makeSimpleTransition( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + Operations_t&& rOverallOperations, + const TransitionSettings& rSettings = TransitionSettings()) +{ + return makeSimpleTransition(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives), + std::move(rOverallOperations), SceneObjects_t(), rSettings); +} + +std::shared_ptr<OGLTransitionImpl> +makeSimpleTransition( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + SceneObjects_t&& rSceneObjects, + const TransitionSettings& rSettings) +{ + return makeSimpleTransition(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives), + Operations_t(), std::move(rSceneObjects), rSettings); +} + +std::shared_ptr<OGLTransitionImpl> +makeSimpleTransition( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + const TransitionSettings& rSettings = TransitionSettings()) +{ + return makeSimpleTransition(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives), + Operations_t(), SceneObjects_t(), rSettings); +} + +} + +std::shared_ptr<OGLTransitionImpl> makeOutsideCubeFaceToLeft() +{ + Primitive Slide; + + Slide.pushTriangle(glm::vec2(0,0),glm::vec2(1,0),glm::vec2(0,1)); + Slide.pushTriangle(glm::vec2(1,0),glm::vec2(0,1),glm::vec2(1,1)); + + Primitives_t aLeavingPrimitives; + aLeavingPrimitives.push_back(Slide); + + Slide.Operations.push_back(makeRotateAndScaleDepthByWidth(glm::vec3(0,1,0),glm::vec3(0,0,-1),90,false,false,0.0,1.0)); + + Primitives_t aEnteringPrimitives; + aEnteringPrimitives.push_back(Slide); + + Operations_t aOperations; + aOperations.push_back(makeRotateAndScaleDepthByWidth(glm::vec3(0,1,0),glm::vec3(0,0,-1),-90,false,true,0.0,1.0)); + + return makeSimpleTransition(std::move(aLeavingPrimitives), std::move(aEnteringPrimitives), std::move(aOperations)); +} + +std::shared_ptr<OGLTransitionImpl> makeInsideCubeFaceToLeft() +{ + Primitive Slide; + + Slide.pushTriangle(glm::vec2(0,0),glm::vec2(1,0),glm::vec2(0,1)); + Slide.pushTriangle(glm::vec2(1,0),glm::vec2(0,1),glm::vec2(1,1)); + + Primitives_t aLeavingPrimitives; + aLeavingPrimitives.push_back(Slide); + + Slide.Operations.push_back(makeRotateAndScaleDepthByWidth(glm::vec3(0,1,0),glm::vec3(0,0,1),-90,false,false,0.0,1.0)); + + Primitives_t aEnteringPrimitives; + aEnteringPrimitives.push_back(Slide); + + Operations_t aOperations; + aOperations.push_back(makeRotateAndScaleDepthByWidth(glm::vec3(0,1,0),glm::vec3(0,0,1),90,false,true,0.0,1.0)); + + return makeSimpleTransition(std::move(aLeavingPrimitives), std::move(aEnteringPrimitives), std::move(aOperations)); +} + +std::shared_ptr<OGLTransitionImpl> makeFallLeaving() +{ + Primitive Slide; + + Slide.pushTriangle(glm::vec2(0,0),glm::vec2(1,0),glm::vec2(0,1)); + Slide.pushTriangle(glm::vec2(1,0),glm::vec2(0,1),glm::vec2(1,1)); + + Primitives_t aEnteringPrimitives; + aEnteringPrimitives.push_back(Slide); + + Slide.Operations.push_back(makeRotateAndScaleDepthByWidth(glm::vec3(1,0,0),glm::vec3(0,-1,0), 90,true,true,0.0,1.0)); + Primitives_t aLeavingPrimitives; + aLeavingPrimitives.push_back(Slide); + + TransitionSettings aSettings; + aSettings.mbUseMipMapEntering = false; + + return makeSimpleTransition(std::move(aLeavingPrimitives), std::move(aEnteringPrimitives), aSettings); +} + +std::shared_ptr<OGLTransitionImpl> makeTurnAround() +{ + Primitive Slide; + TransitionSettings aSettings; + + Slide.pushTriangle(glm::vec2(0,0),glm::vec2(1,0),glm::vec2(0,1)); + Slide.pushTriangle(glm::vec2(1,0),glm::vec2(0,1),glm::vec2(1,1)); + Primitives_t aLeavingPrimitives; + aLeavingPrimitives.push_back(Slide); + + Slide.Operations.push_back(makeSScale(glm::vec3(1, -1, 1), glm::vec3(0, -1.02, 0), false, -1, 0)); + aLeavingPrimitives.push_back(Slide); + + Slide.Operations.clear(); + Slide.Operations.push_back(makeRotateAndScaleDepthByWidth(glm::vec3(0,1,0),glm::vec3(0,0,0),-180,true,false,0.0,1.0)); + Primitives_t aEnteringPrimitives; + aEnteringPrimitives.push_back(Slide); + + Slide.Operations.push_back(makeSScale(glm::vec3(1, -1, 1), glm::vec3(0, -1.02, 0), false, -1, 0)); + aEnteringPrimitives.push_back(Slide); + + Operations_t aOperations; + aOperations.push_back(makeSTranslate(glm::vec3(0, 0, -1.5),true, 0, 0.5)); + aOperations.push_back(makeSTranslate(glm::vec3(0, 0, 1.5), true, 0.5, 1)); + aOperations.push_back(makeRotateAndScaleDepthByWidth(glm::vec3(0, 1, 0),glm::vec3(0, 0, 0), -180, true, true, 0.0, 1.0)); + + return makeReflectionTransition(std::move(aLeavingPrimitives), std::move(aEnteringPrimitives), std::move(aOperations), aSettings); +} + +std::shared_ptr<OGLTransitionImpl> makeTurnDown() +{ + Primitive Slide; + + Slide.pushTriangle(glm::vec2(0,0),glm::vec2(1,0),glm::vec2(0,1)); + Slide.pushTriangle(glm::vec2(1,0),glm::vec2(0,1),glm::vec2(1,1)); + Primitives_t aLeavingPrimitives; + aLeavingPrimitives.push_back(Slide); + + Slide.Operations.push_back(makeSTranslate(glm::vec3(0, 0, 0.0001), false, -1.0, 0.0)); + Slide.Operations.push_back(makeSRotate (glm::vec3(0, 0, 1), glm::vec3(-1, 1, 0), -90, true, 0.0, 1.0)); + Slide.Operations.push_back(makeSRotate (glm::vec3(0, 0, 1), glm::vec3(-1, 1, 0), 90, false, -1.0, 0.0)); + Primitives_t aEnteringPrimitives; + aEnteringPrimitives.push_back(Slide); + + TransitionSettings aSettings; + aSettings.mbUseMipMapLeaving = false; + + return makeSimpleTransition(std::move(aLeavingPrimitives), std::move(aEnteringPrimitives), aSettings); +} + +std::shared_ptr<OGLTransitionImpl> makeIris() +{ + Primitive Slide; + + Slide.pushTriangle (glm::vec2 (0,0), glm::vec2 (1,0), glm::vec2 (0,1)); + Slide.pushTriangle (glm::vec2 (1,0), glm::vec2 (0,1), glm::vec2 (1,1)); + Primitives_t aEnteringPrimitives; + aEnteringPrimitives.push_back (Slide); + + Slide.Operations.push_back (makeSTranslate (glm::vec3 (0, 0, 0.000001), false, -1, 0)); + Slide.Operations.push_back (makeSTranslate (glm::vec3 (0, 0, -0.000002), false, 0.5, 1)); + Primitives_t aLeavingPrimitives; + aLeavingPrimitives.push_back (Slide); + + + Primitive irisPart; + int i, nSteps = 24, nParts = 7; + double t = 1.0/nSteps, lx = 1, ly = 0, of=2.2, f=1.42; + + for (i=1; i<=nSteps; i++) { + double x = cos ((3*2*M_PI*t)/nParts); + double y = -sin ((3*2*M_PI*t)/nParts); + double cx = (f*x + 1)/2; + double cy = (f*y + 1)/2; + double lcx = (f*lx + 1)/2; + double lcy = (f*ly + 1)/2; + double cxo = (of*x + 1)/2; + double cyo = (of*y + 1)/2; + double lcxo = (of*lx + 1)/2; + double lcyo = (of*ly + 1)/2; + irisPart.pushTriangle (glm::vec2 (lcx, lcy), + glm::vec2 (lcxo, lcyo), + glm::vec2 (cx, cy)); + irisPart.pushTriangle (glm::vec2 (cx, cy), + glm::vec2 (lcxo, lcyo), + glm::vec2 (cxo, cyo)); + lx = x; + ly = y; + t += 1.0/nSteps; + } + + std::shared_ptr<Iris> pIris = std::make_shared<Iris>(); + double angle = 87; + + for (i = 0; i < nParts; i++) { + irisPart.Operations.clear (); + double rx, ry; + + rx = cos ((2*M_PI*i)/nParts); + ry = sin ((2*M_PI*i)/nParts); + irisPart.Operations.push_back (makeSRotate (glm::vec3(0, 0, 1), glm::vec3(rx, ry, 0), angle, true, 0.0, 0.5)); + irisPart.Operations.push_back (makeSRotate (glm::vec3(0, 0, 1), glm::vec3(rx, ry, 0), -angle, true, 0.5, 1)); + if (i > 0) { + irisPart.Operations.push_back (makeSTranslate (glm::vec3(rx, ry, 0), false, -1, 0)); + irisPart.Operations.push_back (makeSRotate (glm::vec3(0, 0, 1), glm::vec3(0, 0, 0), i*360.0/nParts, false, -1, 0)); + irisPart.Operations.push_back (makeSTranslate (glm::vec3(-1, 0, 0), false, -1, 0)); + } + irisPart.Operations.push_back(makeSTranslate(glm::vec3(0, 0, 1), false, -2, 0.0)); + irisPart.Operations.push_back (makeSRotate (glm::vec3(1, .5, 0), glm::vec3(1, 0, 0), -30, false, -1, 0)); + pIris->pushPrimitive (irisPart); + } + + SceneObjects_t aSceneObjects; + aSceneObjects.push_back (pIris); + + TransitionSettings aSettings; + aSettings.mbUseMipMapLeaving = aSettings.mbUseMipMapEntering = false; + + return makeSimpleTransition(std::move(aLeavingPrimitives), std::move(aEnteringPrimitives), std::move(aSceneObjects), aSettings); +} + +namespace +{ + +class RochadeTransition : public ReflectionTransition +{ +public: + RochadeTransition(const TransitionScene& rScene, const TransitionSettings& rSettings) + : ReflectionTransition(rScene, rSettings) + {} + +private: + virtual void displaySlides_(double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, double SlideWidthScale, double SlideHeightScale, OpenGLContext *pContext) override; +}; + +void RochadeTransition::displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, double SlideWidthScale, double SlideHeightScale, OpenGLContext * ) +{ + applyOverallOperations( nTime, SlideWidthScale, SlideHeightScale ); + + if( nTime > .5) { + displaySlide( nTime, glLeavingSlideTex, getScene().getLeavingSlide(), SlideWidthScale, SlideHeightScale ); + displaySlide( nTime, glEnteringSlideTex, getScene().getEnteringSlide(), SlideWidthScale, SlideHeightScale ); + } else { + displaySlide( nTime, glEnteringSlideTex, getScene().getEnteringSlide(), SlideWidthScale, SlideHeightScale ); + displaySlide( nTime, glLeavingSlideTex, getScene().getLeavingSlide(), SlideWidthScale, SlideHeightScale ); + } +} + +std::shared_ptr<OGLTransitionImpl> +makeRochadeTransition( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + const TransitionSettings& rSettings) +{ + return std::make_shared<RochadeTransition>( + TransitionScene(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives)), + rSettings) + ; + +} +} + +std::shared_ptr<OGLTransitionImpl> makeRochade() +{ + Primitive Slide; + TransitionSettings aSettings; + + double w, h; + + w = 2.2; + h = 10; + + Slide.pushTriangle(glm::vec2(0,0),glm::vec2(1,0),glm::vec2(0,1)); + Slide.pushTriangle(glm::vec2(1,0),glm::vec2(0,1),glm::vec2(1,1)); + + Slide.Operations.push_back(makeSEllipseTranslate(w, h, 0.25, -0.25, true, 0, 1)); + Slide.Operations.push_back(makeRotateAndScaleDepthByWidth(glm::vec3(0,1,0),glm::vec3(0,0,0), -45, true, true, 0, 1)); + Primitives_t aLeavingSlide; + aLeavingSlide.push_back(Slide); + + Slide.Operations.push_back(makeSScale(glm::vec3(1, -1, 1), glm::vec3(0, -1.02, 0), false, -1, 0)); + aLeavingSlide.push_back(Slide); + + Slide.Operations.clear(); + Slide.Operations.push_back(makeSEllipseTranslate(w, h, 0.75, 0.25, true, 0, 1)); + Slide.Operations.push_back(makeSTranslate(glm::vec3(0, 0, -h), false, -1, 0)); + Slide.Operations.push_back(makeRotateAndScaleDepthByWidth(glm::vec3(0,1,0),glm::vec3(0,0,0), -45, true, true, 0, 1)); + Slide.Operations.push_back(makeRotateAndScaleDepthByWidth(glm::vec3(0,1,0),glm::vec3(0,0,0), 45, true, false, -1, 0)); + Primitives_t aEnteringSlide; + aEnteringSlide.push_back(Slide); + + Slide.Operations.push_back(makeSScale(glm::vec3(1, -1, 1), glm::vec3(0, -1.02, 0), false, -1, 0)); + aEnteringSlide.push_back(Slide); + + return makeRochadeTransition(std::move(aLeavingSlide), std::move(aEnteringSlide), aSettings); +} + +static double randFromNeg1to1() +{ + return comphelper::rng::uniform_real_distribution(-1.0, std::nextafter(1.0, DBL_MAX)); +} + +// TODO(Q3): extract to basegfx +static glm::vec3 randNormVectorInXYPlane() +{ + glm::vec3 toReturn(randFromNeg1to1(),randFromNeg1to1(),0.0); + return glm::normalize(toReturn); +} + +template<typename T> +static T clamp(const T& rIn) +{ + return glm::clamp(rIn, T(-1.0), T(1.0)); +} + +std::shared_ptr<OGLTransitionImpl> makeRevolvingCircles( sal_uInt16 nCircles , sal_uInt16 nPointsOnCircles ) +{ + double dAngle(2*M_PI/static_cast<double>( nPointsOnCircles )); + if(nCircles < 2 || nPointsOnCircles < 4) + return makeNByMTileFlip(1,1); + float Radius(1.0/static_cast<double>( nCircles )); + float dRadius(Radius); + float LastRadius(0.0); + float NextRadius(2*Radius); + + /// now we know there is at least two circles + /// the first will always be a full circle + /// the last will always be the outer shell of the slide with a circle hole + + //add the full circle + std::vector<glm::vec2> unScaledTexCoords; + float TempAngle(0.0); + for(unsigned int Point(0); Point < nPointsOnCircles; ++Point) + { + unScaledTexCoords.emplace_back( cos(TempAngle - M_PI_2) , sin(TempAngle- M_PI_2) ); + + TempAngle += dAngle; + } + + Primitives_t aLeavingSlide; + Primitives_t aEnteringSlide; + { + Primitive EnteringSlide; + Primitive LeavingSlide; + for(int Point(0); Point + 1 < nPointsOnCircles; ++Point) + { + EnteringSlide.pushTriangle( glm::vec2( 0.5 , 0.5 ) , Radius * unScaledTexCoords[ Point + 1 ] / 2.0f + glm::vec2( 0.5 , 0.5 ) , Radius * unScaledTexCoords[ Point ] / 2.0f + glm::vec2( 0.5 , 0.5 ) ); + LeavingSlide.pushTriangle( glm::vec2( 0.5 , 0.5 ) , Radius * unScaledTexCoords[ Point + 1 ] / 2.0f + glm::vec2( 0.5 , 0.5 ) , Radius * unScaledTexCoords[ Point ] / 2.0f + glm::vec2( 0.5, 0.5) ); + } + EnteringSlide.pushTriangle( glm::vec2(0.5,0.5) , Radius * unScaledTexCoords[ 0 ] / 2.0f + glm::vec2( 0.5 , 0.5 ) , Radius * unScaledTexCoords[ nPointsOnCircles - 1 ] / 2.0f + glm::vec2( 0.5 , 0.5 ) ); + LeavingSlide.pushTriangle( glm::vec2(0.5,0.5) , Radius*unScaledTexCoords[0]/2.0f + glm::vec2(0.5,0.5) , Radius*unScaledTexCoords[nPointsOnCircles - 1]/2.0f + glm::vec2(0.5,0.5) ); + + glm::vec3 axis(randNormVectorInXYPlane()); + EnteringSlide.Operations.push_back( makeSRotate( axis , glm::vec3(0,0,0) , 180, true, Radius/2.0 , (NextRadius + 1)/2.0 ) ); + LeavingSlide.Operations.push_back( makeSRotate( axis , glm::vec3(0,0,0) , 180, true, Radius/2.0 , (NextRadius + 1)/2.0 ) ); + EnteringSlide.Operations.push_back( makeSRotate( axis , glm::vec3(0,0,0) , -180, false,0.0,1.0) ); + + aEnteringSlide.push_back(EnteringSlide); + aLeavingSlide.push_back(LeavingSlide); + LastRadius = Radius; + Radius = NextRadius; + NextRadius += dRadius; + } + + for(int i(1); i < nCircles - 1; ++i) + { + Primitive LeavingSlide; + Primitive EnteringSlide; + for(int Side(0); Side < nPointsOnCircles - 1; ++Side) + { + EnteringSlide.pushTriangle(Radius*unScaledTexCoords[Side]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side + 1]/2.0f + glm::vec2(0.5,0.5) ); + EnteringSlide.pushTriangle(Radius*unScaledTexCoords[Side]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side + 1]/2.0f + glm::vec2(0.5,0.5) , Radius*unScaledTexCoords[Side + 1]/2.0f + glm::vec2(0.5,0.5) ); + + LeavingSlide.pushTriangle(Radius*unScaledTexCoords[Side]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side + 1]/2.0f + glm::vec2(0.5,0.5) ); + LeavingSlide.pushTriangle(Radius*unScaledTexCoords[Side]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side + 1]/2.0f + glm::vec2(0.5,0.5) , Radius*unScaledTexCoords[Side + 1]/2.0f + glm::vec2(0.5,0.5) ); + } + + EnteringSlide.pushTriangle(Radius*unScaledTexCoords[nPointsOnCircles - 1]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[nPointsOnCircles - 1]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[0]/2.0f + glm::vec2(0.5,0.5) ); + EnteringSlide.pushTriangle(Radius*unScaledTexCoords[nPointsOnCircles - 1]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[0]/2.0f + glm::vec2(0.5,0.5) , Radius*unScaledTexCoords[0]/2.0f + glm::vec2(0.5,0.5) ); + + LeavingSlide.pushTriangle(Radius*unScaledTexCoords[nPointsOnCircles - 1]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[nPointsOnCircles - 1]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[0]/2.0f + glm::vec2(0.5,0.5) ); + LeavingSlide.pushTriangle(Radius*unScaledTexCoords[nPointsOnCircles - 1]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[0]/2.0f + glm::vec2(0.5,0.5) , Radius*unScaledTexCoords[0]/2.0f + glm::vec2(0.5,0.5) ); + + glm::vec3 axis(randNormVectorInXYPlane()); + EnteringSlide.Operations.push_back( makeSRotate( axis , glm::vec3(0,0,0) , 180, true, Radius/2.0 , (NextRadius + 1)/2.0 ) ); + LeavingSlide.Operations.push_back( makeSRotate( axis , glm::vec3(0,0,0) , 180, true, Radius/2.0 , (NextRadius + 1)/2.0 ) ); + EnteringSlide.Operations.push_back( makeSRotate( axis , glm::vec3(0,0,0) , -180, false,0.0,1.0) ); + + aEnteringSlide.push_back(EnteringSlide); + aLeavingSlide.push_back(LeavingSlide); + + LastRadius = Radius; + Radius = NextRadius; + NextRadius += dRadius; + } + { + Radius = sqrt(2.0); + Primitive LeavingSlide; + Primitive EnteringSlide; + for(int Side(0); Side < nPointsOnCircles - 1; ++Side) + { + + EnteringSlide.pushTriangle(clamp(Radius*unScaledTexCoords[Side])/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side + 1]/2.0f + glm::vec2(0.5,0.5) ); + EnteringSlide.pushTriangle(clamp(Radius*unScaledTexCoords[Side])/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side + 1]/2.0f + glm::vec2(0.5,0.5) , clamp(Radius*unScaledTexCoords[Side + 1])/2.0f + glm::vec2(0.5,0.5) ); + + LeavingSlide.pushTriangle(clamp(Radius*unScaledTexCoords[Side])/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side + 1]/2.0f + glm::vec2(0.5,0.5) ); + LeavingSlide.pushTriangle(clamp(Radius*unScaledTexCoords[Side])/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[Side + 1]/2.0f + glm::vec2(0.5,0.5) , clamp(Radius*unScaledTexCoords[Side + 1])/2.0f + glm::vec2(0.5,0.5) ); + } + + EnteringSlide.pushTriangle(clamp(Radius*unScaledTexCoords[nPointsOnCircles - 1])/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[nPointsOnCircles - 1]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[0]/2.0f + glm::vec2(0.5,0.5) ); + EnteringSlide.pushTriangle(clamp(Radius*unScaledTexCoords[nPointsOnCircles - 1])/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[0]/2.0f + glm::vec2(0.5,0.5) , clamp(Radius*unScaledTexCoords[0])/2.0f + glm::vec2(0.5,0.5) ); + + LeavingSlide.pushTriangle(clamp(Radius*unScaledTexCoords[nPointsOnCircles - 1])/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[nPointsOnCircles - 1]/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[0]/2.0f + glm::vec2(0.5,0.5) ); + LeavingSlide.pushTriangle(clamp(Radius*unScaledTexCoords[nPointsOnCircles - 1])/2.0f + glm::vec2(0.5,0.5) , LastRadius*unScaledTexCoords[0]/2.0f + glm::vec2(0.5,0.5) , clamp(Radius*unScaledTexCoords[0])/2.0f + glm::vec2(0.5,0.5) ); + + glm::vec3 axis(randNormVectorInXYPlane()); + EnteringSlide.Operations.push_back( makeSRotate( axis , glm::vec3(0,0,0) , 180, true, (LastRadius + dRadius)/2.0 , 1.0 ) ); + LeavingSlide.Operations.push_back( makeSRotate( axis , glm::vec3(0,0,0) , 180, true, (LastRadius + dRadius)/2.0 , 1.0 ) ); + EnteringSlide.Operations.push_back( makeSRotate( axis , glm::vec3(0,0,0) , -180, false,0.0,1.0) ); + + aEnteringSlide.push_back(EnteringSlide); + aLeavingSlide.push_back(LeavingSlide); + } + + return makeSimpleTransition(std::move(aLeavingSlide), std::move(aEnteringSlide)); +} + +std::shared_ptr<OGLTransitionImpl> makeHelix( sal_uInt16 nRows ) +{ + double invN(1.0/static_cast<double>(nRows)); + double iDn = 0.0; + double iPDn = invN; + Primitives_t aLeavingSlide; + Primitives_t aEnteringSlide; + for(unsigned int i(0); i < nRows; ++i) + { + Primitive Tile; + + Tile.pushTriangle(glm::vec2( 1.0 , iDn ) , glm::vec2( 0.0 , iDn ) , glm::vec2( 0.0 , iPDn )); + + Tile.pushTriangle(glm::vec2( 1.0 , iPDn ) , glm::vec2( 1.0 , iDn ) , glm::vec2( 0.0 , iPDn )); + + Tile.Operations.push_back( makeSRotate( glm::vec3( 0 , 1 , 0 ) , ( Tile.getVertex(1) + Tile.getVertex(3) )/2.0f , 180 , + true, std::min(std::max(static_cast<double>(i - nRows/2.0)*invN/2.0,0.0),1.0), + std::min(std::max(static_cast<double>(i + nRows/2.0)*invN/2.0,0.0),1.0) ) ); + + aLeavingSlide.push_back(Tile); + + Tile.Operations.push_back( makeSRotate( glm::vec3( 0 , 1 , 0 ) , ( Tile.getVertex(1) + Tile.getVertex(3) )/2.0f , -180 , false,0.0,1.0) ); + + aEnteringSlide.push_back(Tile); + + iDn += invN; + iPDn += invN; + } + + return makeSimpleTransition(std::move(aLeavingSlide), std::move(aEnteringSlide)); +} + +static float fdiv(int a, int b) +{ + return static_cast<float>(a)/b; +} + +static glm::vec2 vec(float x, float y, float nx, float ny) +{ + x = x < 0.0 ? 0.0 : x; + x = std::min(x, nx); + y = y < 0.0 ? 0.0 : y; + y = std::min(y, ny); + return glm::vec2(fdiv(x, nx), fdiv(y, ny)); +} + +std::shared_ptr<OGLTransitionImpl> makeNByMTileFlip( sal_uInt16 n, sal_uInt16 m ) +{ + Primitives_t aLeavingSlide; + Primitives_t aEnteringSlide; + + for (int x = 0; x < n; x++) + { + for (int y = 0; y < n; y++) + { + Primitive aTile; + glm::vec2 x11 = vec(x, y, n, m); + glm::vec2 x12 = vec(x, y+1, n, m); + glm::vec2 x21 = vec(x+1, y, n, m); + glm::vec2 x22 = vec(x+1, y+1, n, m); + + aTile.pushTriangle(x21, x11, x12); + aTile.pushTriangle(x22, x21, x12); + + aTile.Operations.push_back(makeSRotate( glm::vec3(0 , 1, 0), (aTile.getVertex(1) + aTile.getVertex(3)) / 2.0f, 180 , true, x11.x * x11.y / 2.0f , ((x22.x * x22.y) + 1.0f) / 2.0f)); + aLeavingSlide.push_back(aTile); + + aTile.Operations.push_back(makeSRotate( glm::vec3(0 , 1, 0), (aTile.getVertex(1) + aTile.getVertex(3)) / 2.0f, -180, false, x11.x * x11.y / 2.0f , ((x22.x * x22.y) + 1.0f) / 2.0f)); + aEnteringSlide.push_back(aTile); + } + } + + return makeSimpleTransition(std::move(aLeavingSlide), std::move(aEnteringSlide)); +} + +Primitive& Primitive::operator=(const Primitive& rvalue) +{ + Primitive aTmp(rvalue); + swap(aTmp); + return *this; +} + +Primitive::Primitive(const Primitive& rvalue) + : Operations(rvalue.Operations) + , Vertices(rvalue.Vertices) +{ +} + +void Primitive::swap(Primitive& rOther) +{ + using std::swap; + + swap(Operations, rOther.Operations); + swap(Vertices, rOther.Vertices); +} + +void Primitive::pushTriangle(const glm::vec2& SlideLocation0,const glm::vec2& SlideLocation1,const glm::vec2& SlideLocation2) +{ + std::vector<glm::vec3> Verts; + std::vector<glm::vec2> Texs; + Verts.reserve(3); + Texs.reserve(3); + + Verts.emplace_back( 2*SlideLocation0.x - 1, -2*SlideLocation0.y + 1 , 0.0 ); + Verts.emplace_back( 2*SlideLocation1.x - 1, -2*SlideLocation1.y + 1 , 0.0 ); + Verts.emplace_back( 2*SlideLocation2.x - 1, -2*SlideLocation2.y + 1 , 0.0 ); + + //figure out if they're facing the correct way, and make them face the correct way. + glm::vec3 Normal( glm::cross( Verts[0] - Verts[1] , Verts[1] - Verts[2] ) ); + if(Normal.z >= 0.0)//if the normal is facing us + { + Texs.push_back(SlideLocation0); + Texs.push_back(SlideLocation1); + Texs.push_back(SlideLocation2); + } + else // if the normal is facing away from us, make it face us + { + Texs.push_back(SlideLocation0); + Texs.push_back(SlideLocation2); + Texs.push_back(SlideLocation1); + Verts.clear(); + Verts.emplace_back( 2*SlideLocation0.x - 1, -2*SlideLocation0.y + 1 , 0.0 ); + Verts.emplace_back( 2*SlideLocation2.x - 1, -2*SlideLocation2.y + 1 , 0.0 ); + Verts.emplace_back( 2*SlideLocation1.x - 1, -2*SlideLocation1.y + 1 , 0.0 ); + } + + Vertices.push_back({Verts[0], glm::vec3(0, 0, 1), Texs[0]}); //all normals always face the screen when untransformed. + Vertices.push_back({Verts[1], glm::vec3(0, 0, 1), Texs[1]}); //all normals always face the screen when untransformed. + Vertices.push_back({Verts[2], glm::vec3(0, 0, 1), Texs[2]}); //all normals always face the screen when untransformed. +} + +namespace +{ + +class DiamondTransition : public SimpleTransition +{ +public: + DiamondTransition(const TransitionScene& rScene, const TransitionSettings& rSettings) + : SimpleTransition(rScene, rSettings) + {} + +private: + virtual void displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, double SlideWidthScale, double SlideHeightScale, OpenGLContext *pContext ) override; +}; + +Primitives_t makeLeavingSlide(double nTime) +{ + Primitive Slide2; + if( nTime >= 0.5 ) { + double m = 1 - nTime; + + Slide2.pushTriangle (glm::vec2 (0,0), glm::vec2 (m,0), glm::vec2 (0,m)); + Slide2.pushTriangle (glm::vec2 (nTime,0), glm::vec2 (1,0), glm::vec2 (1,m)); + Slide2.pushTriangle (glm::vec2 (1,nTime), glm::vec2 (1,1), glm::vec2 (nTime,1)); + Slide2.pushTriangle (glm::vec2 (0,nTime), glm::vec2 (m,1), glm::vec2 (0,1)); + } else { + double l = 0.5 - nTime; + double h = 0.5 + nTime; + + Slide2.pushTriangle (glm::vec2 (0,0), glm::vec2 (1,0), glm::vec2 (0.5,l)); + Slide2.pushTriangle (glm::vec2 (0.5,l), glm::vec2 (1,0), glm::vec2 (h,0.5)); + Slide2.pushTriangle (glm::vec2 (1,0), glm::vec2 (1,1), glm::vec2 (h,0.5)); + Slide2.pushTriangle (glm::vec2 (h,0.5), glm::vec2 (1,1), glm::vec2 (0.5,h)); + Slide2.pushTriangle (glm::vec2 (0.5,h), glm::vec2 (1,1), glm::vec2 (0,1)); + Slide2.pushTriangle (glm::vec2 (l,0.5), glm::vec2 (0.5,h), glm::vec2 (0,1)); + Slide2.pushTriangle (glm::vec2 (0,0), glm::vec2 (l,0.5), glm::vec2 (0,1)); + Slide2.pushTriangle (glm::vec2 (0,0), glm::vec2 (0.5,l), glm::vec2 (l,0.5)); + } + Slide2.Operations.push_back (makeSTranslate (glm::vec3 (0, 0, 0.00000001), false, -1, 0)); + Primitives_t aLeavingSlidePrimitives; + aLeavingSlidePrimitives.push_back (Slide2); + + return aLeavingSlidePrimitives; +} + +void DiamondTransition::displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, + double SlideWidthScale, double SlideHeightScale, OpenGLContext * ) +{ + CHECK_GL_ERROR(); + applyOverallOperations( nTime, SlideWidthScale, SlideHeightScale ); + + CHECK_GL_ERROR(); + displayUnbufferedSlide( nTime, glLeavingSlideTex, makeLeavingSlide(nTime), SlideWidthScale, SlideHeightScale ); + displaySlide( nTime, glEnteringSlideTex, getScene().getEnteringSlide(), SlideWidthScale, SlideHeightScale ); + CHECK_GL_ERROR(); +} + +std::shared_ptr<OGLTransitionImpl> +makeDiamondTransition(const TransitionSettings& rSettings) +{ + Primitive Slide1; + Slide1.pushTriangle (glm::vec2 (0,0), glm::vec2 (1,0), glm::vec2 (0,1)); + Slide1.pushTriangle (glm::vec2 (1,0), glm::vec2 (0,1), glm::vec2 (1,1)); + Primitives_t aEnteringSlidePrimitives; + aEnteringSlidePrimitives.push_back (Slide1); + Primitives_t aLeavingSlidePrimitives; + aLeavingSlidePrimitives.push_back (Slide1); + return std::make_shared<DiamondTransition>(TransitionScene(std::move(aLeavingSlidePrimitives), std::move(aEnteringSlidePrimitives)), rSettings); +} + +} + +std::shared_ptr<OGLTransitionImpl> makeDiamond() +{ + TransitionSettings aSettings; + aSettings.mbUseMipMapLeaving = aSettings.mbUseMipMapEntering = false; + + return makeDiamondTransition(aSettings); +} + +std::shared_ptr<OGLTransitionImpl> makeVenetianBlinds( bool vertical, int parts ) +{ + static double t30 = tan( M_PI/6.0 ); + double ln = 0; + double p = 1.0/parts; + + Primitives_t aLeavingSlide; + Primitives_t aEnteringSlide; + for( int i=0; i<parts; i++ ) { + Primitive Slide; + double n = (i + 1)/static_cast<double>(parts); + if( vertical ) { + Slide.pushTriangle (glm::vec2 (ln,0), glm::vec2 (n,0), glm::vec2 (ln,1)); + Slide.pushTriangle (glm::vec2 (n,0), glm::vec2 (ln,1), glm::vec2 (n,1)); + Slide.Operations.push_back(makeRotateAndScaleDepthByWidth(glm::vec3(0, 1, 0), glm::vec3(n + ln - 1, 0, -t30*p), -120, true, true, 0.0, 1.0)); + } else { + Slide.pushTriangle (glm::vec2 (0,ln), glm::vec2 (1,ln), glm::vec2 (0,n)); + Slide.pushTriangle (glm::vec2 (1,ln), glm::vec2 (0,n), glm::vec2 (1,n)); + Slide.Operations.push_back(makeRotateAndScaleDepthByHeight(glm::vec3(1, 0, 0), glm::vec3(0, 1 - n - ln, -t30*p), -120, true, true, 0.0, 1.0)); + } + aLeavingSlide.push_back (Slide); + + if( vertical ) { + Slide.Operations.push_back(makeSRotate(glm::vec3(0, 1, 0), glm::vec3(2*n - 1, 0, 0), -60, false, -1, 0)); + Slide.Operations.push_back(makeSRotate(glm::vec3(0, 1, 0), glm::vec3(n + ln - 1, 0, 0), 180, false, -1, 0)); + } else { + Slide.Operations.push_back(makeSRotate(glm::vec3(1, 0, 0), glm::vec3(0, 1 - 2*n, 0), -60, false, -1, 0)); + Slide.Operations.push_back(makeSRotate(glm::vec3(1, 0, 0), glm::vec3(0, 1 - n - ln, 0), 180, false, -1, 0)); + } + aEnteringSlide.push_back (Slide); + ln = n; + } + + return makeSimpleTransition(std::move(aLeavingSlide), std::move(aEnteringSlide)); +} + +namespace +{ + +class FadeSmoothlyTransition : public OGLTransitionImpl +{ +public: + FadeSmoothlyTransition(const TransitionScene& rScene, const TransitionSettings& rSettings) + : OGLTransitionImpl(rScene, rSettings) + {} + +private: + virtual GLuint makeShader() const override; +}; + +GLuint FadeSmoothlyTransition::makeShader() const +{ + return OpenGLHelper::LoadShaders( "basicVertexShader", "fadeFragmentShader" ); +} + +std::shared_ptr<OGLTransitionImpl> +makeFadeSmoothlyTransition( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + const TransitionSettings& rSettings) +{ + return std::make_shared<FadeSmoothlyTransition>( + TransitionScene(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives)), + rSettings) + ; +} + +} + +std::shared_ptr<OGLTransitionImpl> makeFadeSmoothly() +{ + Primitive Slide; + + Slide.pushTriangle (glm::vec2 (0,0), glm::vec2 (1,0), glm::vec2 (0,1)); + Slide.pushTriangle (glm::vec2 (1,0), glm::vec2 (0,1), glm::vec2 (1,1)); + Primitives_t aLeavingSlide; + aLeavingSlide.push_back (Slide); + Primitives_t aEnteringSlide; + aEnteringSlide.push_back (Slide); + + TransitionSettings aSettings; + aSettings.mbUseMipMapLeaving = aSettings.mbUseMipMapEntering = false; + + return makeFadeSmoothlyTransition(std::move(aLeavingSlide), std::move(aEnteringSlide), aSettings); +} + +namespace +{ + +class FadeThroughColorTransition : public OGLTransitionImpl +{ +public: + FadeThroughColorTransition(const TransitionScene& rScene, const TransitionSettings& rSettings, bool white) + : OGLTransitionImpl(rScene, rSettings), useWhite( white ) + {} + +private: + virtual GLuint makeShader() const override; + bool useWhite; +}; + +GLuint FadeThroughColorTransition::makeShader() const +{ + return OpenGLHelper::LoadShaders( "basicVertexShader", "fadeBlackFragmentShader", + useWhite ? "#define use_white" : "", "" ); +} + +std::shared_ptr<OGLTransitionImpl> +makeFadeThroughColorTransition( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + const TransitionSettings& rSettings, + bool white) +{ + return std::make_shared<FadeThroughColorTransition>( + TransitionScene(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives)), + rSettings, white) + ; +} + +} + +std::shared_ptr<OGLTransitionImpl> makeFadeThroughColor( bool white ) +{ + Primitive Slide; + + Slide.pushTriangle (glm::vec2 (0,0), glm::vec2 (1,0), glm::vec2 (0,1)); + Slide.pushTriangle (glm::vec2 (1,0), glm::vec2 (0,1), glm::vec2 (1,1)); + Primitives_t aLeavingSlide; + aLeavingSlide.push_back (Slide); + Primitives_t aEnteringSlide; + aEnteringSlide.push_back (Slide); + + TransitionSettings aSettings; + aSettings.mbUseMipMapLeaving = aSettings.mbUseMipMapEntering = false; + + return makeFadeThroughColorTransition(std::move(aLeavingSlide), std::move(aEnteringSlide), aSettings, white); +} + +namespace +{ + +class PermTextureTransition : public OGLTransitionImpl +{ +protected: + PermTextureTransition(const TransitionScene& rScene, const TransitionSettings& rSettings) + : OGLTransitionImpl(rScene, rSettings) + , m_nHelperTexture(0) + {} + + virtual void finishTransition() override; + virtual void prepareTransition( sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, OpenGLContext *pContext ) override; + +private: + /** various data */ + GLuint m_nHelperTexture; +}; + +void PermTextureTransition::finishTransition() +{ + CHECK_GL_ERROR(); + if ( m_nHelperTexture ) + { + glDeleteTextures( 1, &m_nHelperTexture ); + m_nHelperTexture = 0; + } + CHECK_GL_ERROR(); +} + +constexpr auto permutation2D = []() constexpr { + int permutation256 [256]= { + 215, 100, 200, 204, 233, 50, 85, 196, + 71, 141, 122, 160, 93, 131, 243, 234, + 162, 183, 36, 155, 4, 62, 35, 205, + 40, 102, 33, 27, 255, 55, 214, 156, + 75, 163, 134, 126, 249, 74, 197, 228, + 72, 90, 206, 235, 17, 22, 49, 169, + 227, 89, 16, 5, 117, 60, 248, 230, + 217, 68, 138, 96, 194, 170, 136, 10, + 112, 238, 184, 189, 176, 42, 225, 212, + 84, 58, 175, 244, 150, 168, 219, 236, + 101, 208, 123, 37, 164, 110, 158, 201, + 78, 114, 57, 48, 70, 142, 106, 43, + 232, 26, 32, 252, 239, 98, 191, 94, + 59, 149, 39, 187, 203, 190, 19, 13, + 133, 45, 61, 247, 23, 34, 20, 52, + 118, 209, 146, 193, 222, 18, 1, 152, + 46, 41, 91, 148, 115, 25, 135, 77, + 254, 147, 224, 161, 9, 213, 223, 250, + 231, 251, 127, 166, 63, 179, 81, 130, + 139, 28, 120, 151, 241, 86, 111, 0, + 88, 153, 172, 182, 159, 105, 178, 47, + 51, 167, 65, 66, 92, 73, 198, 211, + 245, 195, 31, 220, 140, 76, 221, 186, + 154, 185, 56, 83, 38, 165, 109, 67, + 124, 226, 132, 53, 229, 29, 12, 181, + 121, 24, 207, 199, 177, 113, 30, 80, + 3, 97, 188, 79, 216, 173, 8, 145, + 87, 128, 180, 237, 240, 137, 125, 104, + 15, 242, 119, 246, 103, 143, 95, 144, + 2, 44, 69, 157, 192, 174, 14, 54, + 218, 82, 64, 210, 11, 6, 129, 21, + 116, 171, 99, 202, 7, 107, 253, 108 + }; + std::array<unsigned char, 256 * 256> a{}; + for (int y = 0; y < 256; y++) + for (int x = 0; x < 256; x++) + a[x + y * 256] = permutation256[(y + permutation256[x]) & 0xff]; + return a; +}(); + +void initPermTexture(GLuint *texID) +{ + CHECK_GL_ERROR(); + glGenTextures(1, texID); + glBindTexture(GL_TEXTURE_2D, *texID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RED, GL_UNSIGNED_BYTE, + permutation2D.data()); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + CHECK_GL_ERROR(); +} + +void PermTextureTransition::prepareTransition( sal_Int32, sal_Int32, OpenGLContext* ) +{ + CHECK_GL_ERROR(); + GLint location = glGetUniformLocation( m_nProgramObject, "permTexture" ); + if( location != -1 ) { + glActiveTexture(GL_TEXTURE1); + CHECK_GL_ERROR(); + if( !m_nHelperTexture ) + initPermTexture( &m_nHelperTexture ); + + glActiveTexture(GL_TEXTURE0); + CHECK_GL_ERROR(); + + glUniform1i( location, 1 ); // texture unit 1 + CHECK_GL_ERROR(); + } + CHECK_GL_ERROR(); +} + +} + +namespace +{ + +class StaticNoiseTransition : public PermTextureTransition +{ +public: + StaticNoiseTransition(const TransitionScene& rScene, const TransitionSettings& rSettings) + : PermTextureTransition(rScene, rSettings) + {} + +private: + virtual GLuint makeShader() const override; +}; + +GLuint StaticNoiseTransition::makeShader() const +{ + return OpenGLHelper::LoadShaders( "basicVertexShader", "staticFragmentShader" ); +} + +std::shared_ptr<OGLTransitionImpl> +makeStaticNoiseTransition( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + const TransitionSettings& rSettings) +{ + return std::make_shared<StaticNoiseTransition>( + TransitionScene(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives)), + rSettings) + ; +} + +} + +std::shared_ptr<OGLTransitionImpl> makeStatic() +{ + Primitive Slide; + + Slide.pushTriangle (glm::vec2 (0,0), glm::vec2 (1,0), glm::vec2 (0,1)); + Slide.pushTriangle (glm::vec2 (1,0), glm::vec2 (0,1), glm::vec2 (1,1)); + Primitives_t aLeavingSlide; + aLeavingSlide.push_back (Slide); + Primitives_t aEnteringSlide; + aEnteringSlide.push_back (Slide); + + TransitionSettings aSettings; + aSettings.mbUseMipMapLeaving = aSettings.mbUseMipMapEntering = false; + + return makeStaticNoiseTransition(std::move(aLeavingSlide), std::move(aEnteringSlide), aSettings); +} + +namespace +{ + +class DissolveTransition : public PermTextureTransition +{ +public: + DissolveTransition(const TransitionScene& rScene, const TransitionSettings& rSettings) + : PermTextureTransition(rScene, rSettings) + {} + +private: + virtual GLuint makeShader() const override; +}; + +GLuint DissolveTransition::makeShader() const +{ + return OpenGLHelper::LoadShaders( "basicVertexShader", "dissolveFragmentShader" ); +} + +std::shared_ptr<OGLTransitionImpl> +makeDissolveTransition( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + const TransitionSettings& rSettings) +{ + return std::make_shared<DissolveTransition>( + TransitionScene(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives)), + rSettings) + ; +} + +} + +std::shared_ptr<OGLTransitionImpl> makeDissolve() +{ + Primitive Slide; + + Slide.pushTriangle (glm::vec2 (0,0), glm::vec2 (1,0), glm::vec2 (0,1)); + Slide.pushTriangle (glm::vec2 (1,0), glm::vec2 (0,1), glm::vec2 (1,1)); + Primitives_t aLeavingSlide; + aLeavingSlide.push_back (Slide); + Primitives_t aEnteringSlide; + aEnteringSlide.push_back (Slide); + + TransitionSettings aSettings; + aSettings.mbUseMipMapLeaving = aSettings.mbUseMipMapEntering = false; + + return makeDissolveTransition(std::move(aLeavingSlide), std::move(aEnteringSlide), aSettings); +} + +namespace +{ + +class VortexTransition : public PermTextureTransition +{ +public: + VortexTransition(const TransitionScene& rScene, const TransitionSettings& rSettings, int nNX, int nNY) + : PermTextureTransition(rScene, rSettings) + , maNumTiles(nNX,nNY) + { + mvTileInfo.resize(6*maNumTiles.x*maNumTiles.y); + mnFramebuffers[0] = 0; + mnFramebuffers[1] = 0; + mnDepthTextures[0] = 0; + mnDepthTextures[1] = 0; + } + +private: + virtual void finishTransition() override; + virtual GLuint makeShader() const override; + virtual void prepareTransition( sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, OpenGLContext *pContext ) override; + virtual void displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, double SlideWidthScale, double SlideHeightScale, OpenGLContext *pContext ) override; + + GLint mnSlideLocation = -1; + GLint mnTileInfoLocation = -1; + GLuint mnTileInfoBuffer = 0u; + GLint mnShadowLocation = -1; + std::array<GLuint, 2> mnFramebuffers; + std::array<GLuint, 2> mnDepthTextures; + + glm::ivec2 maNumTiles; + + std::vector<GLfloat> mvTileInfo; +}; + +void VortexTransition::finishTransition() +{ + PermTextureTransition::finishTransition(); + + CHECK_GL_ERROR(); + glDeleteTextures(2, mnDepthTextures.data()); + mnDepthTextures = {0u, 0u}; + CHECK_GL_ERROR(); + glDeleteFramebuffers(2, mnFramebuffers.data()); + mnFramebuffers = {0u, 0u}; + glDeleteBuffers(1, &mnTileInfoBuffer); + mnTileInfoBuffer = 0u; + mnSlideLocation = -1; + mnTileInfoLocation = -1; + mnShadowLocation = -1; + CHECK_GL_ERROR(); +} + +GLuint VortexTransition::makeShader() const +{ + return OpenGLHelper::LoadShaders( "vortexVertexShader", "vortexFragmentShader", "vortexGeometryShader" ); +} + +glm::mat4 lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up) { + glm::vec3 f = glm::normalize(center - eye); + glm::vec3 u = glm::normalize(up); + glm::vec3 s = glm::normalize(glm::cross(f, u)); + u = glm::cross(s, f); + + return glm::mat4(s.x, u.x, -f.x, 0, + s.y, u.y, -f.y, 0, + s.z, u.z, -f.z, 0, + -glm::dot(s, eye), -glm::dot(u, eye), glm::dot(f, eye), 1); +} + +void VortexTransition::prepareTransition( sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, OpenGLContext *pContext ) +{ + CHECK_GL_ERROR(); + PermTextureTransition::prepareTransition( glLeavingSlideTex, glEnteringSlideTex, pContext ); + CHECK_GL_ERROR(); + + mnSlideLocation = glGetUniformLocation(m_nProgramObject, "slide"); + mnTileInfoLocation = glGetAttribLocation(m_nProgramObject, "tileInfo"); + GLint nNumTilesLocation = glGetUniformLocation(m_nProgramObject, "numTiles"); + mnShadowLocation = glGetUniformLocation(m_nProgramObject, "shadow"); + GLint nOrthoProjectionMatrix = glGetUniformLocation(m_nProgramObject, "orthoProjectionMatrix"); + GLint nOrthoViewMatrix = glGetUniformLocation(m_nProgramObject, "orthoViewMatrix"); + GLint location = glGetUniformLocation(m_nProgramObject, "leavingShadowTexture"); + glUniform1i(location, 2); + location = glGetUniformLocation(m_nProgramObject, "enteringShadowTexture"); + glUniform1i(location, 3); + CHECK_GL_ERROR(); + + glUniform2iv(nNumTilesLocation, 1, glm::value_ptr(maNumTiles)); + CHECK_GL_ERROR(); + + glGenBuffers(1, &mnTileInfoBuffer); + CHECK_GL_ERROR(); + + // We store the (x,y) indexes of the tile each vertex belongs to in a float, so they must fit. + assert(maNumTiles.x < 256); + assert(maNumTiles.y < 256); + + // Two triangles, i.e. six vertices, per tile + { + int n = 0; + for (int x = 0; x < maNumTiles.x; x++) + { + for (int y = 0; y < maNumTiles.y; y++) + { + for (int v = 0; v < 6; v++) + { + mvTileInfo[n] = x + (y << 8) + (v << 16); + n++; + } + } + } + } + + glBindBuffer(GL_ARRAY_BUFFER, mnTileInfoBuffer); + CHECK_GL_ERROR(); + glEnableVertexAttribArray(mnTileInfoLocation); + CHECK_GL_ERROR(); + glVertexAttribPointer(mnTileInfoLocation, 1, GL_FLOAT, GL_FALSE, 0, nullptr); + CHECK_GL_ERROR(); + glBufferData(GL_ARRAY_BUFFER, mvTileInfo.size()*sizeof(GLfloat), mvTileInfo.data(), GL_STATIC_DRAW); + CHECK_GL_ERROR(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + CHECK_GL_ERROR(); + + double EyePos(10.0); + double const RealF(1.0); + double const RealN(-1.0); + double const RealL(-2.0); + double RealR(2.0); + double const RealB(-2.0); + double RealT(2.0); + double ClipN(EyePos+5.0*RealN); + double ClipF(EyePos+15.0*RealF); + double ClipL(RealL*8.0); + double ClipR(RealR*8.0); + double ClipB(RealB*8.0); + double ClipT(RealT*8.0); + + glm::mat4 projection = glm::ortho<float>(ClipL, ClipR, ClipB, ClipT, ClipN, ClipF); + //This scaling is to take the plane with BottomLeftCorner(-1,-1,0) and TopRightCorner(1,1,0) and map it to the screen after the perspective division. + glm::vec3 scale(1.0 / (((RealR * 2.0 * ClipN) / (EyePos * (ClipR - ClipL))) - ((ClipR + ClipL) / (ClipR - ClipL))), + 1.0 / (((RealT * 2.0 * ClipN) / (EyePos * (ClipT - ClipB))) - ((ClipT + ClipB) / (ClipT - ClipB))), + 1.0); + projection = glm::scale(projection, scale); + glUniformMatrix4fv(nOrthoProjectionMatrix, 1, false, glm::value_ptr(projection)); + + glm::mat4 view = lookAt(glm::vec3(-1, 1, EyePos), glm::vec3(-0.5, 0.5, 0), glm::vec3(0, 1, 0)); + glUniformMatrix4fv(nOrthoViewMatrix, 1, false, glm::value_ptr(view)); + + // Generate the framebuffers and textures for the shadows. + glGenTextures(2, mnDepthTextures.data()); + glGenFramebuffers(2, mnFramebuffers.data()); + + for (int i : {0, 1}) { + glBindTexture(GL_TEXTURE_2D, mnDepthTextures[i]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, 2048, 2048, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindFramebuffer(GL_FRAMEBUFFER, mnFramebuffers[i]); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, mnDepthTextures[i], 0); + glDrawBuffer(GL_NONE); // No color buffer is drawn to. + + // Always check that our framebuffer is ok + if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + SAL_WARN("slideshow.opengl", "Wrong framebuffer!"); + return; + } + } + + pContext->restoreDefaultFramebuffer(); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture( GL_TEXTURE2 ); + glBindTexture( GL_TEXTURE_2D, mnDepthTextures[0] ); + glActiveTexture( GL_TEXTURE3 ); + glBindTexture( GL_TEXTURE_2D, mnDepthTextures[1] ); + glActiveTexture( GL_TEXTURE0 ); +} + +void VortexTransition::displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, double SlideWidthScale, double SlideHeightScale, OpenGLContext * pContext ) +{ + CHECK_GL_ERROR(); + applyOverallOperations( nTime, SlideWidthScale, SlideHeightScale ); + glUniform1f( m_nTimeLocation, nTime ); + glUniform1f( mnShadowLocation, 1.0 ); + + std::array<GLint, 4> viewport; + glGetIntegerv(GL_VIEWPORT, viewport.data()); + glViewport(0, 0, 2048, 2048); + + glBindFramebuffer(GL_FRAMEBUFFER, mnFramebuffers[0]); + glClear(GL_DEPTH_BUFFER_BIT); + glUniform1f( mnSlideLocation, 0.0 ); + displaySlide( nTime, glLeavingSlideTex, getScene().getLeavingSlide(), SlideWidthScale, SlideHeightScale ); + + glBindFramebuffer(GL_FRAMEBUFFER, mnFramebuffers[1]); + glClear(GL_DEPTH_BUFFER_BIT); + glUniform1f( mnSlideLocation, 1.0 ); + displaySlide( nTime, glEnteringSlideTex, getScene().getEnteringSlide(), SlideWidthScale, SlideHeightScale ); + + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + pContext->restoreDefaultFramebuffer(); + glUniform1f( mnShadowLocation, 0.0 ); + glUniform1f( mnSlideLocation, 0.0 ); + displaySlide( nTime, glLeavingSlideTex, getScene().getLeavingSlide(), SlideWidthScale, SlideHeightScale ); + glUniform1f( mnSlideLocation, 1.0 ); + displaySlide( nTime, glEnteringSlideTex, getScene().getEnteringSlide(), SlideWidthScale, SlideHeightScale ); + CHECK_GL_ERROR(); +} + +std::shared_ptr<OGLTransitionImpl> +makeVortexTransition(Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + const TransitionSettings& rSettings, + int NX, + int NY) +{ + return std::make_shared<VortexTransition>(TransitionScene(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives)), + rSettings, + NX, NY); +} + +} + +std::shared_ptr<OGLTransitionImpl> makeVortex() +{ + const int NX = 96, NY = 96; + Primitive Slide; + + for (int x = 0; x < NX; x++) + { + for (int y = 0; y < NY; y++) + { + Slide.pushTriangle (glm::vec2 (fdiv(x,NX),fdiv(y,NY)), glm::vec2 (fdiv(x+1,NX),fdiv(y,NY)), glm::vec2 (fdiv(x,NX),fdiv(y+1,NY))); + Slide.pushTriangle (glm::vec2 (fdiv(x+1,NX),fdiv(y,NY)), glm::vec2 (fdiv(x,NX),fdiv(y+1,NY)), glm::vec2 (fdiv(x+1,NX),fdiv(y+1,NY))); + } + } + Primitives_t aLeavingSlide; + aLeavingSlide.push_back (Slide); + Primitives_t aEnteringSlide; + aEnteringSlide.push_back (Slide); + + TransitionSettings aSettings; + aSettings.mbUseMipMapLeaving = aSettings.mbUseMipMapEntering = false; + aSettings.mnRequiredGLVersion = 3.2f; + + return makeVortexTransition(std::move(aLeavingSlide), std::move(aEnteringSlide), aSettings, NX, NY); +} + +namespace +{ + +class RippleTransition : public OGLTransitionImpl +{ +public: + RippleTransition(const TransitionScene& rScene, const TransitionSettings& rSettings, const glm::vec2& rCenter) + : OGLTransitionImpl(rScene, rSettings), + maCenter(rCenter) + { + } + +private: + virtual GLuint makeShader() const override; + virtual void prepareTransition( sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, OpenGLContext *pContext ) override; + virtual void prepare( double SlideWidth, double SlideHeight ) override; + + glm::vec2 maCenter; + GLint maSlideRatioLocation = -1; +}; + +GLuint RippleTransition::makeShader() const +{ + return OpenGLHelper::LoadShaders( "basicVertexShader", "rippleFragmentShader" ); +} + +void RippleTransition::prepareTransition( sal_Int32, sal_Int32, OpenGLContext* ) +{ + GLint nCenterLocation = glGetUniformLocation(m_nProgramObject, "center"); + CHECK_GL_ERROR(); + + glUniform2fv(nCenterLocation, 1, glm::value_ptr(maCenter)); + CHECK_GL_ERROR(); + + maSlideRatioLocation = glGetUniformLocation(m_nProgramObject, "slideRatio"); + CHECK_GL_ERROR(); +} + +void RippleTransition::prepare( double SlideWidth, double SlideHeight ) +{ + if( maSlideRatioLocation != -1 ) + glUniform1f( maSlideRatioLocation, SlideWidth / SlideHeight ); +} + +std::shared_ptr<OGLTransitionImpl> +makeRippleTransition(Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + const TransitionSettings& rSettings) +{ + // The center point should be adjustable by the user, but we have no way to do that in the UI + return std::make_shared<RippleTransition>(TransitionScene(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives)), + rSettings, + glm::vec2(0.5, 0.5)); +} + +} + +std::shared_ptr<OGLTransitionImpl> makeRipple() +{ + Primitive Slide; + + Slide.pushTriangle (glm::vec2 (0,0), glm::vec2 (1,0), glm::vec2 (0,1)); + Slide.pushTriangle (glm::vec2 (1,0), glm::vec2 (0,1), glm::vec2 (1,1)); + + Primitives_t aLeavingSlide; + aLeavingSlide.push_back (Slide); + + Primitives_t aEnteringSlide; + aEnteringSlide.push_back (Slide); + + TransitionSettings aSettings; + aSettings.mbUseMipMapLeaving = aSettings.mbUseMipMapEntering = false; + + return makeRippleTransition(std::move(aLeavingSlide), std::move(aEnteringSlide), aSettings); +} + +static void createHexagon(Primitive& aHexagon, const int x, const int y, const int NX, const int NY) +{ + if (y % 4 == 0) + { + aHexagon.pushTriangle(vec(x-1, y-1, NX, NY), vec(x, y-2, NX, NY), vec(x, y+0.5, NX, NY)); + aHexagon.pushTriangle(vec(x, y-2, NX, NY), vec(x+1, y-1, NX, NY), vec(x, y+0.5, NX, NY)); + aHexagon.pushTriangle(vec(x+1, y-1, NX, NY), vec(x+1, y, NX, NY), vec(x, y+0.5, NX, NY)); + aHexagon.pushTriangle(vec(x+1, y, NX, NY), vec(x, y+1, NX, NY), vec(x, y+0.5, NX, NY)); + aHexagon.pushTriangle(vec(x, y+1, NX, NY), vec(x-1, y, NX, NY), vec(x, y+0.5, NX, NY)); + aHexagon.pushTriangle(vec(x-1, y, NX, NY), vec(x-1, y-1, NX, NY), vec(x, y+0.5, NX, NY)); + } + else + { + aHexagon.pushTriangle(vec(x-2, y-1, NX, NY), vec(x-1, y-2, NX, NY), vec(x, y+0.5, NX, NY)); + aHexagon.pushTriangle(vec(x-1, y-2, NX, NY), vec(x, y-1, NX, NY), vec(x, y+0.5, NX, NY)); + aHexagon.pushTriangle(vec(x, y-1, NX, NY), vec(x, y, NX, NY), vec(x, y+0.5, NX, NY)); + aHexagon.pushTriangle(vec(x, y, NX, NY), vec(x-1, y+1, NX, NY), vec(x, y+0.5, NX, NY)); + aHexagon.pushTriangle(vec(x-1, y+1, NX, NY), vec(x-2, y, NX, NY), vec(x, y+0.5, NX, NY)); + aHexagon.pushTriangle(vec(x-2, y, NX, NY), vec(x-2, y-1, NX, NY), vec(x, y+0.5, NX, NY)); + } +} + +namespace +{ + +class GlitterTransition : public PermTextureTransition +{ +public: + GlitterTransition(const TransitionScene& rScene, const TransitionSettings& rSettings) + : PermTextureTransition(rScene, rSettings) + { + } + +private: + virtual GLuint makeShader() const override; + virtual void prepareTransition( sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, OpenGLContext *pContext ) override; + virtual void cleanup() override; + + GLuint maBuffer = 0; +}; + +GLuint GlitterTransition::makeShader() const +{ + return OpenGLHelper::LoadShaders( "glitterVertexShader", "glitterFragmentShader" ); +} + +struct ThreeFloats +{ + GLfloat x, y, z; +}; + +void GlitterTransition::prepareTransition( sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, OpenGLContext *pContext ) +{ + CHECK_GL_ERROR(); + PermTextureTransition::prepareTransition( glLeavingSlideTex, glEnteringSlideTex, pContext ); + CHECK_GL_ERROR(); + + GLint nNumTilesLocation = glGetUniformLocation(m_nProgramObject, "numTiles"); + if (nNumTilesLocation != -1) { + glUniform2iv(nNumTilesLocation, 1, glm::value_ptr(glm::ivec2(41, 41 * 4 / 3))); + CHECK_GL_ERROR(); + } + + glGenBuffers(1, &maBuffer); + glBindBuffer(GL_ARRAY_BUFFER, maBuffer); + + // Upload the center of each hexagon. + const Primitive& primitive = getScene().getLeavingSlide()[0]; + std::vector<ThreeFloats> vertices; + for (int i = 2; i < primitive.getVerticesCount(); i += 18) { + const glm::vec3& center = primitive.getVertex(i); + for (int j = 0; j < 18; ++j) + vertices.push_back({center.x, center.y, center.z}); + } + glBufferData(GL_ARRAY_BUFFER, vertices.size() * 3 * sizeof(GLfloat), vertices.data(), GL_STATIC_DRAW); + + GLint location = glGetAttribLocation(m_nProgramObject, "center"); + if (location != -1) { + glEnableVertexAttribArray(location); + glVertexAttribPointer( location, 3, GL_FLOAT, false, 0, nullptr ); + CHECK_GL_ERROR(); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void GlitterTransition::cleanup() +{ + CHECK_GL_ERROR(); + glDeleteBuffers(1, &maBuffer); + CHECK_GL_ERROR(); +} + +std::shared_ptr<OGLTransitionImpl> +makeGlitterTransition(Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + const TransitionSettings& rSettings) +{ + return std::make_shared<GlitterTransition>(TransitionScene(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives)), + rSettings); +} + +} + +std::shared_ptr<OGLTransitionImpl> makeGlitter() +{ + const int NX = 80; + const int NY = NX * 4 / 3; + + Primitives_t aSlide; + Primitives_t aEmptySlide; + Primitive aHexagon; + + for (int y = 0; y < NY+2; y+=2) + for (int x = 0; x < NX+2; x+=2) + createHexagon(aHexagon, x, y, NX, NY); + + aSlide.push_back(aHexagon); + + return makeGlitterTransition(std::move(aSlide), std::move(aEmptySlide), TransitionSettings()); +} + +namespace +{ + +class HoneycombTransition : public PermTextureTransition +{ +public: + HoneycombTransition(const TransitionScene& rScene, const TransitionSettings& rSettings) + : PermTextureTransition(rScene, rSettings) + { + mnDepthTextures[0] = 0; + mnDepthTextures[1] = 0; + } + +private: + virtual void finishTransition() override; + virtual GLuint makeShader() const override; + virtual void prepareTransition( sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, OpenGLContext *pContext ) override; + virtual void displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, double SlideWidthScale, double SlideHeightScale, OpenGLContext *pContext ) override; + + GLint maHexagonSizeLocation = -1; + GLint maSelectedTextureLocation = -1; + GLint mnShadowLocation = -1; + GLuint mnFramebuffer = 0u; + std::array<GLuint, 2> mnDepthTextures; +}; + +void HoneycombTransition::finishTransition() +{ + PermTextureTransition::finishTransition(); + + CHECK_GL_ERROR(); + glActiveTexture( GL_TEXTURE2 ); + glBindTexture( GL_TEXTURE_2D, 0 ); + glActiveTexture( GL_TEXTURE3 ); + glBindTexture( GL_TEXTURE_2D, 0 ); + glActiveTexture( GL_TEXTURE0 ); + CHECK_GL_ERROR(); + glDeleteTextures(2, mnDepthTextures.data()); + mnDepthTextures = {0u, 0u}; + CHECK_GL_ERROR(); + glDeleteFramebuffers(1, &mnFramebuffer); + mnFramebuffer = 0u; + CHECK_GL_ERROR(); +} + +GLuint HoneycombTransition::makeShader() const +{ + return OpenGLHelper::LoadShaders( "honeycombVertexShader", "honeycombFragmentShader", "honeycombGeometryShader" ); +} + +void HoneycombTransition::prepareTransition( sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, OpenGLContext *pContext ) +{ + CHECK_GL_ERROR(); + PermTextureTransition::prepareTransition( glLeavingSlideTex, glEnteringSlideTex, pContext ); + + CHECK_GL_ERROR(); + maHexagonSizeLocation = glGetUniformLocation(m_nProgramObject, "hexagonSize"); + maSelectedTextureLocation = glGetUniformLocation( m_nProgramObject, "selectedTexture" ); + mnShadowLocation = glGetUniformLocation(m_nProgramObject, "shadow"); + GLint nOrthoProjectionMatrix = glGetUniformLocation(m_nProgramObject, "orthoProjectionMatrix"); + GLint nOrthoViewMatrix = glGetUniformLocation(m_nProgramObject, "orthoViewMatrix"); + GLint location = glGetUniformLocation(m_nProgramObject, "colorShadowTexture"); + glUniform1i(location, 2); + location = glGetUniformLocation(m_nProgramObject, "depthShadowTexture"); + glUniform1i(location, 3); + CHECK_GL_ERROR(); + + // We want to see the entering slide behind the leaving one. + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + CHECK_GL_ERROR(); + + double EyePos(10.0); + double const RealF(1.0); + double const RealN(-1.0); + double const RealL(-4.0); + double RealR(4.0); + double const RealB(-4.0); + double RealT(4.0); + double ClipN(EyePos+5.0*RealN); + double ClipF(EyePos+15.0*RealF); + double ClipL(RealL*8.0); + double ClipR(RealR*8.0); + double ClipB(RealB*8.0); + double ClipT(RealT*8.0); + + glm::mat4 projection = glm::ortho<float>(ClipL, ClipR, ClipB, ClipT, ClipN, ClipF); + //This scaling is to take the plane with BottomLeftCorner(-1,-1,0) and TopRightCorner(1,1,0) and map it to the screen after the perspective division. + glm::vec3 scale(1.0 / (((RealR * 2.0 * ClipN) / (EyePos * (ClipR - ClipL))) - ((ClipR + ClipL) / (ClipR - ClipL))), + 1.0 / (((RealT * 2.0 * ClipN) / (EyePos * (ClipT - ClipB))) - ((ClipT + ClipB) / (ClipT - ClipB))), + 1.0); + projection = glm::scale(projection, scale); + glUniformMatrix4fv(nOrthoProjectionMatrix, 1, false, glm::value_ptr(projection)); + + glm::mat4 view = lookAt(glm::vec3(0, 0, EyePos), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); + glUniformMatrix4fv(nOrthoViewMatrix, 1, false, glm::value_ptr(view)); + + // Generate the framebuffer and textures for the shadows. + glGenTextures(2, mnDepthTextures.data()); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, mnDepthTextures[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2048, 2048, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, mnDepthTextures[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, 2048, 2048, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glActiveTexture(GL_TEXTURE0); + glGenFramebuffers(1, &mnFramebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, mnFramebuffer); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mnDepthTextures[0], 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, mnDepthTextures[1], 0); + + // Always check that our framebuffer is ok + if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + SAL_WARN("slideshow.opengl", "Wrong framebuffer!"); + return; + } + + pContext->restoreDefaultFramebuffer(); +} + +void HoneycombTransition::displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, + double SlideWidthScale, double SlideHeightScale, OpenGLContext *pContext ) +{ + CHECK_GL_ERROR(); + applyOverallOperations(nTime, SlideWidthScale, SlideHeightScale); + glUniform1f(m_nTimeLocation, nTime); + glUniform1f(mnShadowLocation, 1.0); + CHECK_GL_ERROR(); + + const float borderSize = 0.15f; + + std::array<GLint, 4> viewport; + glGetIntegerv(GL_VIEWPORT, viewport.data()); + glViewport(0, 0, 2048, 2048); + glBindFramebuffer(GL_FRAMEBUFFER, mnFramebuffer); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glUniform1f(mnShadowLocation, 1.0); + glUniform1f(maSelectedTextureLocation, 1.0); + glUniform1f(maHexagonSizeLocation, 1.0f - borderSize); + displaySlide(nTime, glLeavingSlideTex, getScene().getLeavingSlide(), SlideWidthScale, SlideHeightScale); + glUniform1f(maHexagonSizeLocation, 1.0f + borderSize); + displaySlide(nTime, glLeavingSlideTex, getScene().getLeavingSlide(), SlideWidthScale, SlideHeightScale); + + // The back (entering) slide needs to be drawn before the front (leaving) one in order for blending to work. + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + pContext->restoreDefaultFramebuffer(); + glUniform1f(mnShadowLocation, 0.0); + glUniform1f(maSelectedTextureLocation, 0.0); + glUniform1f(maHexagonSizeLocation, 1.0f - borderSize); + displaySlide(nTime, glEnteringSlideTex, getScene().getEnteringSlide(), SlideWidthScale, SlideHeightScale); + glUniform1f(maHexagonSizeLocation, 1.0f + borderSize); + displaySlide(nTime, glEnteringSlideTex, getScene().getEnteringSlide(), SlideWidthScale, SlideHeightScale); + glUniform1f(maSelectedTextureLocation, 1.0); + glUniform1f(maHexagonSizeLocation, 1.0f - borderSize); + displaySlide(nTime, glLeavingSlideTex, getScene().getLeavingSlide(), SlideWidthScale, SlideHeightScale); + glUniform1f(maHexagonSizeLocation, 1.0f + borderSize); + displaySlide(nTime, glLeavingSlideTex, getScene().getLeavingSlide(), SlideWidthScale, SlideHeightScale); + CHECK_GL_ERROR(); +} + +std::shared_ptr<OGLTransitionImpl> +makeHoneycombTransition(Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + const TransitionSettings& rSettings) +{ + // The center point should be adjustable by the user, but we have no way to do that in the UI + return std::make_shared<HoneycombTransition>(TransitionScene(std::move(rLeavingSlidePrimitives), std::move(rEnteringSlidePrimitives)), + rSettings); +} + +} + +std::shared_ptr<OGLTransitionImpl> makeHoneycomb() +{ + const int NX = 21; + const int NY = 21; + + TransitionSettings aSettings; + aSettings.mnRequiredGLVersion = 3.2f; + + Primitives_t aSlide; + Primitive aHexagon; + for (int y = 0; y < NY+2; y+=2) + for (int x = 0; x < NX+2; x+=2) + aHexagon.pushTriangle(glm::vec2((y % 4) ? fdiv(x, NX) : fdiv(x + 1, NX), fdiv(y, NY)), glm::vec2(1, 0), glm::vec2(0, 0)); + aSlide.push_back(aHexagon); + + return makeHoneycombTransition(std::vector(aSlide), std::vector(aSlide), aSettings); +} + +std::shared_ptr<OGLTransitionImpl> makeNewsflash() +{ + Primitive Slide; + + Slide.pushTriangle(glm::vec2(0,0),glm::vec2(1,0),glm::vec2(0,1)); + Slide.pushTriangle(glm::vec2(1,0),glm::vec2(0,1),glm::vec2(1,1)); + Slide.Operations.push_back(makeSRotate(glm::vec3(0,0,1),glm::vec3(0,0,0),3000,true,0,0.5)); + Slide.Operations.push_back(makeSScale(glm::vec3(0.01,0.01,0.01),glm::vec3(0,0,0),true,0,0.5)); + Slide.Operations.push_back(makeSTranslate(glm::vec3(-10000, 0, 0),false, 0.5, 2)); + Primitives_t aLeavingSlide; + aLeavingSlide.push_back(Slide); + + Slide.Operations.clear(); + Slide.Operations.push_back(makeSRotate(glm::vec3(0,0,1),glm::vec3(0,0,0),-3000,true,0.5,1)); + Slide.Operations.push_back(makeSTranslate(glm::vec3(-100, 0, 0),false, -1, 1)); + Slide.Operations.push_back(makeSTranslate(glm::vec3(100, 0, 0),false, 0.5, 1)); + Slide.Operations.push_back(makeSScale(glm::vec3(0.01,0.01,0.01),glm::vec3(0,0,0),false,-1,1)); + Slide.Operations.push_back(makeSScale(glm::vec3(100,100,100),glm::vec3(0,0,0),true,0.5,1)); + Primitives_t aEnteringSlide; + aEnteringSlide.push_back(Slide); + + Operations_t aOverallOperations; + aOverallOperations.push_back(makeSRotate(glm::vec3(0,0,1),glm::vec3(0.2,0.2,0),1080,true,0,1)); + + return makeSimpleTransition(std::move(aLeavingSlide), std::move(aEnteringSlide), std::move(aOverallOperations)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/opengl/TransitionImpl.hxx b/slideshow/source/engine/opengl/TransitionImpl.hxx new file mode 100644 index 000000000..9ca35c1e8 --- /dev/null +++ b/slideshow/source/engine/opengl/TransitionImpl.hxx @@ -0,0 +1,393 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef INCLUDED_OGLTRANS_TRANSITIONIMPL_HXX_ +#define INCLUDED_OGLTRANS_TRANSITIONIMPL_HXX_ + +#include <config_lgpl.h> +#include <epoxy/gl.h> +#include <glm/gtc/type_ptr.hpp> + +#include <o3tl/safeint.hxx> +#include <sal/types.h> + +#include <limits> +#include <memory> +#include <vector> + +class Primitive; +class OpenGLContext; +class Operation; +class SceneObject; +class TransitionData; + +struct TransitionSettings +{ + TransitionSettings() : + mbUseMipMapLeaving( true ), + mbUseMipMapEntering( true ), + mnRequiredGLVersion( 3.0f ) + { + } + + /** Whether to use mipmapping for slides textures + */ + bool mbUseMipMapLeaving; + bool mbUseMipMapEntering; + + /** which GL version does the transition require + */ + float mnRequiredGLVersion; +}; + +typedef std::vector<Primitive> Primitives_t; +typedef std::vector<std::shared_ptr<SceneObject> > SceneObjects_t; +typedef std::vector<std::shared_ptr<Operation> > Operations_t; + +class TransitionScene +{ +public: + inline TransitionScene( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + Operations_t&& rOverallOperations = Operations_t(), + SceneObjects_t&& rSceneObjects = SceneObjects_t() + ); + + inline ~TransitionScene(); + + TransitionScene(TransitionScene const& rOther); + TransitionScene& operator=(const TransitionScene& rOther); + + void swap(TransitionScene& rOther); + + const Primitives_t& getLeavingSlide() const + { + return maLeavingSlidePrimitives; + } + + const Primitives_t& getEnteringSlide() const + { + return maEnteringSlidePrimitives; + } + + const Operations_t& getOperations() const + { + return maOverallOperations; + } + + const SceneObjects_t& getSceneObjects() const + { + return maSceneObjects; + } + +private: + /** All the primitives that use the leaving slide texture + */ + Primitives_t maLeavingSlidePrimitives; + + /** All the primitives that use the leaving slide texture + */ + Primitives_t maEnteringSlidePrimitives; + + /** All the operations that should be applied to both leaving and entering slide primitives. These operations will be called in the order they were pushed back in. In OpenGL this effectively uses the operations in the opposite order they were pushed back. + */ + Operations_t maOverallOperations; + + /** All the surrounding scene objects + */ + SceneObjects_t maSceneObjects; +}; + +/** OpenGL 3D Transition class. It implicitly is constructed from XOGLTransition + + It holds Primitives and Operations on those primitives. +*/ +class OGLTransitionImpl +{ +public: + virtual ~OGLTransitionImpl(); + + OGLTransitionImpl(const OGLTransitionImpl&) = delete; + OGLTransitionImpl& operator=(const OGLTransitionImpl&) = delete; + + /** Prepare transition. + */ + bool prepare( sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, OpenGLContext *pContext ); + /** Display a step of the transition. + */ + void display( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, double SlideWidth, double SlideHeight, double DispWidth, double DispHeight, OpenGLContext *pContext ); + /** Clean up after transition. + */ + void finish(); + + TransitionSettings const& getSettings() const + { + return maSettings; + } + +protected: + OGLTransitionImpl(const TransitionScene& rScene, const TransitionSettings& rSettings) + : maScene(rScene) + , maSettings(rSettings) + {} + + TransitionScene const& getScene() const + { + return maScene; + } + + void displaySlide( double nTime, sal_Int32 glSlideTex, const Primitives_t& primitives, double SlideWidthScale, double SlideHeightScale ); + void displayUnbufferedSlide( double nTime, sal_Int32 glSlideTex, const Primitives_t& primitives, double SlideWidthScale, double SlideHeightScale ); + void displayScene( double nTime, double SlideWidth, double SlideHeight, double DispWidth, double DispHeight); + void applyOverallOperations( double nTime, double SlideWidthScale, double SlideHeightScale ); + +private: + /** This function is called in display method to prepare the slides, scene, etc. + * + * Default implementation does nothing. + */ + virtual void prepare( double SlideWidth, double SlideHeight ); + + /** This function is called in display method to prepare the slides, scene, etc. + * + * Default implementation does nothing. + */ + virtual void cleanup(); + + /** This function is called after glx context is ready to let the transition prepare GL related things, like GLSL program. + * + * Default implementation does nothing. + */ + virtual void prepareTransition( sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, OpenGLContext *pContext ); + + /** This function is called when the transition needs to clear after itself, like delete own textures etc. + * + * Default implementation does nothing. + */ + virtual void finishTransition(); + + /** This function is called in display method to display the slides. + * + * Default implementation applies overall operations and then + * displays both slides. + */ + virtual void displaySlides_( double nTime, sal_Int32 glLeavingSlideTex, sal_Int32 glEnteringSlideTex, double SlideWidthScale, double SlideHeightScale, OpenGLContext *pContext ); + + /** This function is called in prepare method to create the GL program. + * + * It is a pure virtual to make sure no class will use a default one. + */ + virtual GLuint makeShader() const = 0; + +private: + const TransitionScene maScene; + const TransitionSettings maSettings; + + /** Calculates the projection and model/view matrices, and upload them. + */ + void uploadModelViewProjectionMatrices(); + + /** Uniform locations for transform matrices + */ + GLint m_nPrimitiveTransformLocation = -1; + GLint m_nSceneTransformLocation = -1; + GLint m_nOperationsTransformLocation = -1; + + /** Per-vertex attribute locations + */ + GLint m_nPositionLocation = -1; + GLint m_nNormalLocation = -1; + GLint m_nTexCoordLocation = -1; + + GLuint m_nVertexArrayObject = 0u; + + std::vector<int> m_nFirstIndices; + +protected: + /** GLSL program object + */ + GLuint m_nProgramObject = 0u; + + /** VBO in which to put primitive data + */ + GLuint m_nVertexBufferObject = 0u; + + /** Location of the "time" uniform + */ + GLint m_nTimeLocation = -1; +}; + + +// "Constructors" of available transitions +std::shared_ptr<OGLTransitionImpl> makeOutsideCubeFaceToLeft(); +std::shared_ptr<OGLTransitionImpl> makeInsideCubeFaceToLeft(); +std::shared_ptr<OGLTransitionImpl> makeNByMTileFlip( sal_uInt16 n, sal_uInt16 m ); +std::shared_ptr<OGLTransitionImpl> makeRevolvingCircles( sal_uInt16 nCircles , sal_uInt16 nPointsOnCircles ); +std::shared_ptr<OGLTransitionImpl> makeHelix( sal_uInt16 nRows ); +std::shared_ptr<OGLTransitionImpl> makeFallLeaving(); +std::shared_ptr<OGLTransitionImpl> makeTurnAround(); +std::shared_ptr<OGLTransitionImpl> makeTurnDown(); +std::shared_ptr<OGLTransitionImpl> makeIris(); +std::shared_ptr<OGLTransitionImpl> makeRochade(); +std::shared_ptr<OGLTransitionImpl> makeVenetianBlinds( bool vertical, int parts ); +std::shared_ptr<OGLTransitionImpl> makeStatic(); +std::shared_ptr<OGLTransitionImpl> makeDissolve(); +std::shared_ptr<OGLTransitionImpl> makeVortex(); +std::shared_ptr<OGLTransitionImpl> makeRipple(); +std::shared_ptr<OGLTransitionImpl> makeGlitter(); +std::shared_ptr<OGLTransitionImpl> makeHoneycomb(); +std::shared_ptr<OGLTransitionImpl> makeNewsflash(); + +/** 2D replacements */ + +std::shared_ptr<OGLTransitionImpl> makeDiamond(); +std::shared_ptr<OGLTransitionImpl> makeFadeSmoothly(); +// fade through black or white +std::shared_ptr<OGLTransitionImpl> makeFadeThroughColor( bool white = false ); + +class SceneObject +{ +public: + SceneObject(); + virtual ~SceneObject(); + SceneObject(const SceneObject&) = delete; + SceneObject& operator=(const SceneObject&) = delete; + + virtual void prepare(GLuint /* program */) {} + virtual void display(GLint sceneTransformLocation, GLint primitiveTransformLocation, double nTime, double SlideWidth, double SlideHeight, double DispWidth, double DispHeight ) const; + virtual void finish() {} + + void pushPrimitive (const Primitive &p); + +protected: + /** All the surrounding scene primitives + */ + Primitives_t maPrimitives; + std::vector<int> maFirstIndices; +}; + +struct Vertex +{ + glm::vec3 position; + glm::vec3 normal; + glm::vec2 texcoord; +}; +static_assert(sizeof(Vertex) == (3 + 3 + 2) * 4, "Vertex struct has wrong size/alignment"); + +/** This class is a list of Triangles that will share Operations, and could possibly share +*/ +class Primitive +{ +public: + Primitive() {} + // making copy constructor explicit makes the class un-suitable for use with stl containers + Primitive(const Primitive& rvalue); + Primitive& operator=(const Primitive& rvalue); + + void swap(Primitive& rOther); + + void applyOperations(glm::mat4& matrix, double nTime, double SlideWidthScale, double SlideHeightScale) const; + void display(GLint primitiveTransformLocation, double nTime, double WidthScale, double HeightScale) const; + void display(GLint primitiveTransformLocation, double nTime, double WidthScale, double HeightScale, int first) const; + + /** PushBack a vertex,normal, and tex coord. Each SlideLocation is where on the slide is mapped to this location ( from (0,0) to (1,1) ). This will make sure the correct aspect ratio is used, and helps to make slides begin and end at the correct position. (0,0) is the top left of the slide, and (1,1) is the bottom right. + + @param SlideLocation0 + Location of first Vertex on slide + + @param SlideLocation1 + Location of second Vertex on slide + + @param SlideLocation2 + Location of third Vertex on slide + + */ + void pushTriangle(const glm::vec2& SlideLocation0,const glm::vec2& SlideLocation1,const glm::vec2& SlideLocation2); + + /** guards against directly changing the vertices + + @return + the list of vertices + */ + const glm::vec3& getVertex(int n) const {return Vertices[n].position;} + + int getVerticesCount() const + { + assert(Vertices.size() < o3tl::make_unsigned(std::numeric_limits<int>::max())); + return int(unsigned(Vertices.size())); + } + + /** accessor for the size of the vertices data + + @return + the size in bytes of the Vertices data + */ + int getVerticesByteSize() const {return Vertices.size() * sizeof(Vertex);} + + /** copies all vertices to the C array passed + + @return + the number of written vertices + */ + int writeVertices(Vertex *location) const { + std::copy(Vertices.begin(), Vertices.end(), location); + return Vertices.size(); + } + + /** list of Operations to be performed on this primitive.These operations will be called in the order they were pushed back in. In OpenGL this effectively uses the operations in the opposite order they were pushed back. + + @return + the list of Operations + + */ + Operations_t Operations; + +private: + /** list of vertices + */ + std::vector<Vertex> Vertices; +}; + +TransitionScene::TransitionScene( + Primitives_t&& rLeavingSlidePrimitives, + Primitives_t&& rEnteringSlidePrimitives, + Operations_t&& rOverallOperations, + SceneObjects_t&& rSceneObjects +) + : maLeavingSlidePrimitives(std::move(rLeavingSlidePrimitives)) + , maEnteringSlidePrimitives(std::move(rEnteringSlidePrimitives)) + , maOverallOperations(std::move(rOverallOperations)) + , maSceneObjects(std::move(rSceneObjects)) +{ +} + +TransitionScene::~TransitionScene() = default; + +#endif // INCLUDED_SLIDESHOW_TRANSITION_HXX_ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/opengl/TransitionerImpl.cxx b/slideshow/source/engine/opengl/TransitionerImpl.cxx new file mode 100644 index 000000000..faef77988 --- /dev/null +++ b/slideshow/source/engine/opengl/TransitionerImpl.cxx @@ -0,0 +1,1346 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <sal/types.h> + +#include <memory> + +#include <com/sun/star/beans/XFastPropertySet.hpp> +#include <com/sun/star/rendering/IntegerBitmapLayout.hpp> +#include <com/sun/star/rendering/ColorComponentTag.hpp> +#include <com/sun/star/rendering/ColorSpaceType.hpp> +#include <com/sun/star/rendering/RenderingIntent.hpp> +#include <com/sun/star/util/Endianness.hpp> +#include <com/sun/star/animations/TransitionType.hpp> +#undef IN +#undef OUT +#include <com/sun/star/animations/TransitionSubType.hpp> +#include <com/sun/star/presentation/XTransitionFactory.hpp> +#include <com/sun/star/presentation/XTransition.hpp> +#include <com/sun/star/presentation/XSlideShowView.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/rendering/XIntegerBitmap.hpp> +#include <com/sun/star/geometry/IntegerSize2D.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> + +#include <canvas/canvastools.hxx> + +#include <tools/diagnose_ex.h> + +#include <vcl/canvastools.hxx> +#include <vcl/opengl/OpenGLContext.hxx> +#include <vcl/opengl/OpenGLHelper.hxx> +#include <vcl/syschild.hxx> +#include <vcl/window.hxx> + +#include "TransitionImpl.hxx" + +#if OSL_DEBUG_LEVEL > 0 +#include <chrono> +#endif + +using namespace ::com::sun::star; +using ::com::sun::star::beans::XFastPropertySet; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; + +namespace +{ + +typedef cppu::WeakComponentImplHelper<presentation::XTransition> OGLTransitionerImplBase; + +#if OSL_DEBUG_LEVEL > 0 +class TimerContext +{ +public: + explicit TimerContext(OUString const& rWhat) + : m_aWhat(rWhat) + , m_StartTime(std::chrono::steady_clock::now()) + { + } + ~TimerContext() + { + auto const aDuration(std::chrono::steady_clock::now() - m_StartTime); + SAL_INFO("slideshow.opengl", m_aWhat << " took: " << std::chrono::duration_cast<std::chrono::microseconds>(aDuration).count()); + } +private: + OUString const m_aWhat; + std::chrono::steady_clock::time_point const m_StartTime; +}; +#endif + +struct OGLFormat +{ + GLint nInternalFormat; + GLenum eFormat; + GLenum eType; +}; + +/* channel ordering: (0:rgba, 1:bgra, 2:argb, 3:abgr) + */ +int calcComponentOrderIndex(const uno::Sequence<sal_Int8>& rTags) +{ + using namespace rendering::ColorComponentTag; + + static const sal_Int8 aOrderTable[] = + { + RGB_RED, RGB_GREEN, RGB_BLUE, ALPHA, + RGB_BLUE, RGB_GREEN, RGB_RED, ALPHA, + ALPHA, RGB_RED, RGB_GREEN, RGB_BLUE, + ALPHA, RGB_BLUE, RGB_GREEN, RGB_RED, + }; + + const sal_Int32 nNumComps(rTags.getLength()); + const sal_Int8* pLine=aOrderTable; + for(int i=0; i<4; ++i) + { + int j=0; + while( j<4 && j<nNumComps && pLine[j] == rTags[j] ) + ++j; + + // all of the line passed, this is a match! + if( j==nNumComps ) + return i; + + pLine+=4; + } + + return -1; +} + +/** This is the Transitioner class for OpenGL 3D transitions in + * slideshow. This class is implicitly + * constructed from XTransitionFactory. +*/ +class OGLTransitionerImpl : private cppu::BaseMutex, public OGLTransitionerImplBase +{ +public: + OGLTransitionerImpl(); + OGLTransitionerImpl(const OGLTransitionerImpl&) = delete; + OGLTransitionerImpl& operator=(const OGLTransitionerImpl&) = delete; + bool setTransition( const std::shared_ptr<OGLTransitionImpl>& pOGLTransition ); + bool initialize( const Reference< presentation::XSlideShowView >& xView, + const Reference< rendering::XBitmap >& xLeavingSlide, + const Reference< rendering::XBitmap >& xEnteringSlide ); + + // XTransition + virtual void SAL_CALL update( double nTime ) override; + virtual void SAL_CALL viewChanged( const Reference< presentation::XSlideShowView >& rView, + const Reference< rendering::XBitmap >& rLeavingBitmap, + const Reference< rendering::XBitmap >& rEnteringBitmap ) override; + +protected: + void disposeTextures(); + + // WeakComponentImplHelperBase + virtual void SAL_CALL disposing() override; + + bool isDisposed() const + { + return (rBHelper.bDisposed || rBHelper.bInDispose); + } + + void createTexture( GLuint* texID, + bool useMipmap, + const uno::Sequence<sal_Int8>& data, + const OGLFormat* pFormat ); + const OGLFormat* chooseFormats(); + +private: + void impl_initializeFlags( bool const bGLXPresent ); + + void impl_dispose(); + + void setSlides( const Reference< rendering::XBitmap >& xLeavingSlide , const uno::Reference< rendering::XBitmap >& xEnteringSlide ); + void impl_prepareSlides(); + + void impl_createTexture( bool useMipmap, const uno::Sequence<sal_Int8>& data, const OGLFormat* pFormat ); + + bool initWindowFromSlideShowView( const uno::Reference< presentation::XSlideShowView >& xView ); + /** After the window has been created, and the slides have been set, we'll initialize the slides with OpenGL. + */ + void GLInitSlides(); + + bool impl_prepareTransition(); + +private: + rtl::Reference<OpenGLContext> mpContext; + + /** OpenGL handle to the leaving slide's texture + */ + GLuint maLeavingSlideGL; + /** OpenGL handle to the entering slide's texture + */ + GLuint maEnteringSlideGL; + + Reference< presentation::XSlideShowView > mxView; + Reference< rendering::XIntegerBitmap > mxLeavingBitmap; + Reference< rendering::XIntegerBitmap > mxEnteringBitmap; + + /** raw bytes of the entering bitmap + */ + uno::Sequence<sal_Int8> maEnteringBytes; + + /** raw bytes of the leaving bitmap + */ + uno::Sequence<sal_Int8> maLeavingBytes; + + bool mbRestoreSync; + + /** the form the raw bytes are in for the bitmaps + */ + rendering::IntegerBitmapLayout maSlideBitmapLayout; + + /** the size of the slides + */ + geometry::IntegerSize2D maSlideSize; + + /** Our Transition to be used. + */ + std::shared_ptr<OGLTransitionImpl> mpTransition; + +public: + /** whether we are running on ATI fglrx with bug related to textures + */ + bool mbBrokenTexturesATI; + + /** GL version + */ + float mnGLVersion; + + /** + Whether the display has GLX extension on X11, always true otherwise (?) + */ + bool mbValidOpenGLContext; + +#if OSL_DEBUG_LEVEL > 0 + std::chrono::steady_clock::time_point m_UpdateStartTime; + std::chrono::steady_clock::time_point m_UpdateEndTime; + std::chrono::steady_clock::time_point m_StartTime; + std::chrono::steady_clock::time_point m_EndTime; + std::chrono::steady_clock::duration m_TotalUpdateDuration; + int mnFrameCount; +#endif +}; + +bool OGLTransitionerImpl::initialize( const Reference< presentation::XSlideShowView >& xView, + const Reference< rendering::XBitmap >& xLeavingSlide, + const Reference< rendering::XBitmap >& xEnteringSlide ) +{ + bool const bValidContext( initWindowFromSlideShowView( xView ) ); + impl_initializeFlags( bValidContext ); + + setSlides( xLeavingSlide, xEnteringSlide ); + + return mbValidOpenGLContext; +} + +void OGLTransitionerImpl::impl_initializeFlags( bool const bValidContext ) +{ + mbValidOpenGLContext = bValidContext; + if ( bValidContext ) { + CHECK_GL_ERROR(); + + mnGLVersion = OpenGLHelper::getGLVersion(); + SAL_INFO("slideshow.opengl", "GL version: " << mnGLVersion << "" ); + +#if defined( UNX ) && !defined( MACOSX ) + const GLubyte* vendor = glGetString( GL_VENDOR ); + /* TODO: check for version once the bug in fglrx driver is fixed */ + mbBrokenTexturesATI = (vendor && strcmp( reinterpret_cast<const char *>(vendor), "ATI Technologies Inc." ) == 0 ); +#endif + + CHECK_GL_ERROR(); + } +} + +bool OGLTransitionerImpl::initWindowFromSlideShowView( const Reference< presentation::XSlideShowView >& xView ) +{ + osl::MutexGuard const guard( m_aMutex ); + + if (isDisposed()) + return false; + + mxView = xView; + if( !mxView.is() ) + return false; + +#if OSL_DEBUG_LEVEL > 0 + TimerContext aTimerContext("initWindowFromSlideShowView"); +#endif + + /// take the XSlideShowView and extract the parent window from it. see viewmediashape.cxx + uno::Reference< rendering::XCanvas > xCanvas(mxView->getCanvas(), uno::UNO_QUERY_THROW); + uno::Sequence< uno::Any > aDeviceParams; + ::canvas::tools::getDeviceInfo( xCanvas, aDeviceParams ); + + OUString aImplName; + aDeviceParams[ 0 ] >>= aImplName; + + sal_Int64 aVal = 0; + aDeviceParams[1] >>= aVal; + + mpContext = OpenGLContext::Create(); + + OutputDevice* pDevice = reinterpret_cast<OutputDevice*>(aVal); + vcl::Window* pWindow = pDevice ? pDevice->GetOwnerWindow() : nullptr; + + if( !mpContext->init( pWindow) ) { + mpContext->requestLegacyContext(); + if( !mpContext->init( pWindow ) ) + return false; + } + SAL_INFO("slideshow.opengl", "created the context"); + + mpContext->makeCurrent(); + CHECK_GL_ERROR(); + + awt::Rectangle aCanvasArea = mxView->getCanvasArea(); + mpContext->setWinPosAndSize(Point(aCanvasArea.X, aCanvasArea.Y), Size(aCanvasArea.Width, aCanvasArea.Height)); + SAL_INFO("slideshow.opengl", "canvas area: " << aCanvasArea.X << "," << aCanvasArea.Y << " - " << aCanvasArea.Width << "x" << aCanvasArea.Height); + + CHECK_GL_ERROR(); + glEnable(GL_CULL_FACE); + CHECK_GL_ERROR(); + glCullFace(GL_BACK); + CHECK_GL_ERROR(); + glClearColor (0, 0, 0, 0); + CHECK_GL_ERROR(); + glClear(GL_COLOR_BUFFER_BIT); + CHECK_GL_ERROR(); + + mpContext->swapBuffers(); + + CHECK_GL_ERROR(); + + return true; +} + +void OGLTransitionerImpl::setSlides( const uno::Reference< rendering::XBitmap >& xLeavingSlide, + const uno::Reference< rendering::XBitmap >& xEnteringSlide ) +{ + osl::MutexGuard const guard( m_aMutex ); + + if (isDisposed()) + return; + + mxLeavingBitmap.set( xLeavingSlide , UNO_QUERY_THROW ); + mxEnteringBitmap.set( xEnteringSlide , UNO_QUERY_THROW ); + + maSlideSize = mxLeavingBitmap->getSize(); + SAL_INFO("slideshow.opengl", "leaving bitmap area: " << maSlideSize.Width << "x" << maSlideSize.Height); + maSlideSize = mxEnteringBitmap->getSize(); + SAL_INFO("slideshow.opengl", "entering bitmap area: " << maSlideSize.Width << "x" << maSlideSize.Height); + + //to avoid annoying flashing under X entering and leaving slides with opengl effects set the leaving + //bitmap as the background pixmap of the opengl child window and the entering bitmap as the background + //pixmap of the non-opengl parent window. If any expose events occur around the start and end of + //the transition then those windows are default filled by X with the desired start/end image so there's + //no visible flash + SystemChildWindow* pChildWindow = mpContext->getChildWindow(); + if (!pChildWindow) + return; + + css::uno::Reference<css::beans::XFastPropertySet> xEnteringFastPropertySet(mxEnteringBitmap, css::uno::UNO_QUERY); + css::uno::Reference<css::beans::XFastPropertySet> xLeavingFastPropertySet(mxLeavingBitmap, css::uno::UNO_QUERY); + css::uno::Sequence<css::uno::Any> aEnteringBitmap; + css::uno::Sequence<css::uno::Any> aLeavingBitmap; + if (xEnteringFastPropertySet && xLeavingFastPropertySet) + { + xEnteringFastPropertySet->getFastPropertyValue(1) >>= aEnteringBitmap; + xLeavingFastPropertySet->getFastPropertyValue(1) >>= aLeavingBitmap; + } + if (aEnteringBitmap.getLength() == 3 && aLeavingBitmap.getLength() == 3) + pChildWindow->SetLeaveEnterBackgrounds(aLeavingBitmap, aEnteringBitmap); +} + + +void OGLTransitionerImpl::impl_prepareSlides() +{ + geometry::IntegerRectangle2D aSlideRect; + aSlideRect.X1 = 0; + aSlideRect.X2 = maSlideSize.Width; + aSlideRect.Y1 = 0; + aSlideRect.Y2 = maSlideSize.Height; + + CHECK_GL_ERROR(); + mpContext->sync(); + CHECK_GL_ERROR(); + + maLeavingBytes = mxLeavingBitmap->getData(maSlideBitmapLayout, aSlideRect); + maEnteringBytes = mxEnteringBitmap->getData(maSlideBitmapLayout, aSlideRect); + + CHECK_GL_ERROR(); + GLInitSlides(); + + SAL_WARN_IF(maSlideBitmapLayout.PlaneStride != 0, "slideshow.opengl","only handle no plane stride now"); + + mpContext->sync(); + + CHECK_GL_ERROR(); + + // synchronized X still gives us much smoother play + // I suspect some issues in above code in slideshow + // synchronize whole transition for now + const GLWindow& rGLWindow(mpContext->getOpenGLWindow()); + mbRestoreSync = rGLWindow.Synchronize(true); +} + +bool OGLTransitionerImpl::impl_prepareTransition() +{ + if( mpTransition && mpTransition->getSettings().mnRequiredGLVersion <= mnGLVersion ) + return mpTransition->prepare( maLeavingSlideGL, maEnteringSlideGL, mpContext.get() ); + return false; +} + +bool OGLTransitionerImpl::setTransition( const std::shared_ptr<OGLTransitionImpl>& pTransition ) +{ + if ( mpTransition ) // already initialized + return true; + + mpTransition = pTransition; + + mpContext->makeCurrent(); + CHECK_GL_ERROR(); + + bool succeeded = impl_prepareTransition(); + if (!succeeded) { + mpTransition = nullptr; + return false; + } + + impl_prepareSlides(); + + // tdf#91456: When the OpenGL context is initialized but nothing has been rendered on it + // it can happen, that an "empty" screen is drawn. Therefore, drawing the content of time 0 + // onto the context + update(0); + + return true; +} + +void OGLTransitionerImpl::createTexture( GLuint* texID, + bool useMipmap, + const uno::Sequence<sal_Int8>& data, + const OGLFormat* pFormat ) +{ + CHECK_GL_ERROR(); + glDeleteTextures( 1, texID ); + glGenTextures( 1, texID ); + glBindTexture( GL_TEXTURE_2D, *texID ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + CHECK_GL_ERROR(); + + impl_createTexture( useMipmap, data, pFormat ); + + SAL_WARN_IF(!glIsTexture(*texID), "slideshow.opengl", "Can't generate Leaving slide textures in OpenGL"); + CHECK_GL_ERROR(); +} + +class OGLColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace > +{ +private: + uno::Sequence< sal_Int8 > maComponentTags; + uno::Sequence< sal_Int32 > maBitCounts; + + virtual sal_Int8 SAL_CALL getType( ) override + { + return rendering::ColorSpaceType::RGB; + } + virtual uno::Sequence< sal_Int8 > SAL_CALL getComponentTags( ) override + { + return maComponentTags; + } + virtual sal_Int8 SAL_CALL getRenderingIntent( ) override + { + return rendering::RenderingIntent::PERCEPTUAL; + } + virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override + { + return uno::Sequence< beans::PropertyValue >(); + } + virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor, + const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override + { + // TODO(P3): if we know anything about target + // colorspace, this can be greatly sped up + uno::Sequence<rendering::ARGBColor> aIntermediate( + convertToARGB(deviceColor)); + return targetColorSpace->convertFromARGB(aIntermediate); + } + virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override + { + const double* pIn( deviceColor.getConstArray() ); + const std::size_t nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::RGBColor > aRes(nLen/4); + rendering::RGBColor* pOut( aRes.getArray() ); + for( std::size_t i=0; i<nLen; i+=4 ) + { + *pOut++ = rendering::RGBColor(pIn[0],pIn[1],pIn[2]); + pIn += 4; + } + return aRes; + } + virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override + { + const double* pIn( deviceColor.getConstArray() ); + const std::size_t nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::ARGBColor > aRes(nLen/4); + rendering::ARGBColor* pOut( aRes.getArray() ); + for( std::size_t i=0; i<nLen; i+=4 ) + { + *pOut++ = rendering::ARGBColor(pIn[3],pIn[0],pIn[1],pIn[2]); + pIn += 4; + } + return aRes; + } + virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override + { + const double* pIn( deviceColor.getConstArray() ); + const std::size_t nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::ARGBColor > aRes(nLen/4); + rendering::ARGBColor* pOut( aRes.getArray() ); + for( std::size_t i=0; i<nLen; i+=4 ) + { + *pOut++ = rendering::ARGBColor(pIn[3],pIn[3]*pIn[0],pIn[3]*pIn[1],pIn[3]*pIn[2]); + pIn += 4; + } + return aRes; + } + virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override + { + const sal_Int32 nLen( rgbColor.getLength() ); + + uno::Sequence< double > aRes(nLen*4); + double* pColors=aRes.getArray(); + for( const rendering::RGBColor& rIn : rgbColor ) + { + *pColors++ = rIn.Red; + *pColors++ = rIn.Green; + *pColors++ = rIn.Blue; + *pColors++ = 1.0; + } + return aRes; + } + virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override + { + const sal_Int32 nLen( rgbColor.getLength() ); + + uno::Sequence< double > aRes(nLen*4); + double* pColors=aRes.getArray(); + for( const rendering::ARGBColor& rIn : rgbColor ) + { + *pColors++ = rIn.Red; + *pColors++ = rIn.Green; + *pColors++ = rIn.Blue; + *pColors++ = rIn.Alpha; + } + return aRes; + } + virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override + { + const sal_Int32 nLen( rgbColor.getLength() ); + + uno::Sequence< double > aRes(nLen*4); + double* pColors=aRes.getArray(); + for( const rendering::ARGBColor& rIn : rgbColor ) + { + *pColors++ = rIn.Red/rIn.Alpha; + *pColors++ = rIn.Green/rIn.Alpha; + *pColors++ = rIn.Blue/rIn.Alpha; + *pColors++ = rIn.Alpha; + } + return aRes; + } + + // XIntegerBitmapColorSpace + virtual sal_Int32 SAL_CALL getBitsPerPixel( ) override + { + return 32; + } + virtual uno::Sequence< sal_Int32 > SAL_CALL getComponentBitCounts( ) override + { + return maBitCounts; + } + virtual sal_Int8 SAL_CALL getEndianness( ) override + { + return util::Endianness::LITTLE; + } + virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< sal_Int8 >& deviceColor, + const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override + { + if( dynamic_cast<OGLColorSpace*>(targetColorSpace.get()) ) + { + const sal_Int32 nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence<double> aRes(nLen); + std::transform(deviceColor.begin(), deviceColor.end(), aRes.getArray(), + vcl::unotools::toDoubleColor); + return aRes; + } + else + { + // TODO(P3): if we know anything about target + // colorspace, this can be greatly sped up + uno::Sequence<rendering::ARGBColor> aIntermediate( + convertIntegerToARGB(deviceColor)); + return targetColorSpace->convertFromARGB(aIntermediate); + } + } + virtual uno::Sequence< sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< sal_Int8 >& deviceColor, + const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override + { + if( dynamic_cast<OGLColorSpace*>(targetColorSpace.get()) ) + { + // it's us, so simply pass-through the data + return deviceColor; + } + else + { + // TODO(P3): if we know anything about target + // colorspace, this can be greatly sped up + uno::Sequence<rendering::ARGBColor> aIntermediate( + convertIntegerToARGB(deviceColor)); + return targetColorSpace->convertIntegerFromARGB(aIntermediate); + } + } + virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< sal_Int8 >& deviceColor ) override + { + const sal_Int8* pIn( deviceColor.getConstArray() ); + const std::size_t nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::RGBColor > aRes(nLen/4); + rendering::RGBColor* pOut( aRes.getArray() ); + for( std::size_t i=0; i<nLen; i+=4 ) + { + *pOut++ = rendering::RGBColor( + vcl::unotools::toDoubleColor(pIn[0]), + vcl::unotools::toDoubleColor(pIn[1]), + vcl::unotools::toDoubleColor(pIn[2])); + pIn += 4; + } + return aRes; + } + + virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< sal_Int8 >& deviceColor ) override + { + const sal_Int8* pIn( deviceColor.getConstArray() ); + const std::size_t nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::ARGBColor > aRes(nLen/4); + rendering::ARGBColor* pOut( aRes.getArray() ); + for( std::size_t i=0; i<nLen; i+=4 ) + { + *pOut++ = rendering::ARGBColor( + vcl::unotools::toDoubleColor(pIn[3]), + vcl::unotools::toDoubleColor(pIn[0]), + vcl::unotools::toDoubleColor(pIn[1]), + vcl::unotools::toDoubleColor(pIn[2])); + pIn += 4; + } + return aRes; + } + + virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< sal_Int8 >& deviceColor ) override + { + const sal_Int8* pIn( deviceColor.getConstArray() ); + const std::size_t nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::ARGBColor > aRes(nLen/4); + rendering::ARGBColor* pOut( aRes.getArray() ); + for( std::size_t i=0; i<nLen; i+=4 ) + { + const sal_Int8 nAlpha( pIn[3] ); + *pOut++ = rendering::ARGBColor( + vcl::unotools::toDoubleColor(nAlpha), + vcl::unotools::toDoubleColor(nAlpha*pIn[0]), + vcl::unotools::toDoubleColor(nAlpha*pIn[1]), + vcl::unotools::toDoubleColor(nAlpha*pIn[2])); + pIn += 4; + } + return aRes; + } + + virtual uno::Sequence< sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override + { + const sal_Int32 nLen( rgbColor.getLength() ); + + uno::Sequence< sal_Int8 > aRes(nLen*4); + sal_Int8* pColors=aRes.getArray(); + for( const rendering::RGBColor& rIn : rgbColor ) + { + *pColors++ = vcl::unotools::toByteColor(rIn.Red); + *pColors++ = vcl::unotools::toByteColor(rIn.Green); + *pColors++ = vcl::unotools::toByteColor(rIn.Blue); + *pColors++ = -1; + } + return aRes; + } + + virtual uno::Sequence< sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override + { + const sal_Int32 nLen( rgbColor.getLength() ); + + uno::Sequence< sal_Int8 > aRes(nLen*4); + sal_Int8* pColors=aRes.getArray(); + for( const rendering::ARGBColor& rIn : rgbColor ) + { + *pColors++ = vcl::unotools::toByteColor(rIn.Red); + *pColors++ = vcl::unotools::toByteColor(rIn.Green); + *pColors++ = vcl::unotools::toByteColor(rIn.Blue); + *pColors++ = vcl::unotools::toByteColor(rIn.Alpha); + } + return aRes; + } + + virtual uno::Sequence< sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override + { + const sal_Int32 nLen( rgbColor.getLength() ); + + uno::Sequence< sal_Int8 > aRes(nLen*4); + sal_Int8* pColors=aRes.getArray(); + for( const rendering::ARGBColor& rIn : rgbColor ) + { + *pColors++ = vcl::unotools::toByteColor(rIn.Red/rIn.Alpha); + *pColors++ = vcl::unotools::toByteColor(rIn.Green/rIn.Alpha); + *pColors++ = vcl::unotools::toByteColor(rIn.Blue/rIn.Alpha); + *pColors++ = vcl::unotools::toByteColor(rIn.Alpha); + } + return aRes; + } + +public: + OGLColorSpace() : + maComponentTags(4), + maBitCounts(4) + { + sal_Int8* pTags = maComponentTags.getArray(); + sal_Int32* pBitCounts = maBitCounts.getArray(); + pTags[0] = rendering::ColorComponentTag::RGB_RED; + pTags[1] = rendering::ColorComponentTag::RGB_GREEN; + pTags[2] = rendering::ColorComponentTag::RGB_BLUE; + pTags[3] = rendering::ColorComponentTag::ALPHA; + + pBitCounts[0] = + pBitCounts[1] = + pBitCounts[2] = + pBitCounts[3] = 8; + } +}; + +struct OGLColorSpaceHolder : public rtl::StaticWithInit<uno::Reference<rendering::XIntegerBitmapColorSpace>, OGLColorSpaceHolder> +{ + uno::Reference<rendering::XIntegerBitmapColorSpace> operator()() + { + return new OGLColorSpace(); + } +}; + +uno::Reference<rendering::XIntegerBitmapColorSpace> const & +getOGLColorSpace() +{ + return OGLColorSpaceHolder::get(); +} + +void buildMipmaps( + GLint internalFormat, GLsizei width, GLsizei height, GLenum format, + GLenum type, const void * data) +{ + if (epoxy_has_gl_extension("GL_ARB_framebuffer_object")) { + glTexImage2D( + GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, + data); + glGenerateMipmap(GL_TEXTURE_2D); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + glTexImage2D( + GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, + data); + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); +} + +void OGLTransitionerImpl::impl_createTexture( + bool useMipmap, + const uno::Sequence<sal_Int8>& data, + const OGLFormat* pFormat ) +{ + if( !pFormat ) + { + CHECK_GL_ERROR(); + // force-convert color to ARGB8888 int color space + uno::Sequence<sal_Int8> tempBytes( + maSlideBitmapLayout.ColorSpace->convertToIntegerColorSpace( + data, + getOGLColorSpace())); + buildMipmaps( GL_RGBA, + maSlideSize.Width, + maSlideSize.Height, + GL_RGBA, + GL_UNSIGNED_BYTE, + &tempBytes[0]); + + if (epoxy_has_gl_extension("GL_EXT_texture_filter_anisotropic")) + { + //anistropic filtering (to make texturing not suck when looking at polygons from oblique angles) + GLfloat largest_supported_anisotropy; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy); + } + } else { + if( mpTransition && !mbBrokenTexturesATI && !useMipmap) { + glTexImage2D( GL_TEXTURE_2D, 0, pFormat->nInternalFormat, maSlideSize.Width, maSlideSize.Height, 0, pFormat->eFormat, pFormat->eType, &data[0] ); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + } else { + buildMipmaps( pFormat->nInternalFormat, maSlideSize.Width, maSlideSize.Height, pFormat->eFormat, pFormat->eType, &data[0] ); + + if (epoxy_has_gl_extension("GL_EXT_texture_filter_anisotropic")) + { + //anistropic filtering (to make texturing not suck when looking at polygons from oblique angles) + GLfloat largest_supported_anisotropy; + glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy ); + } + } + } + CHECK_GL_ERROR(); +} + +const OGLFormat* OGLTransitionerImpl::chooseFormats() +{ + const OGLFormat* pDetectedFormat=nullptr; + uno::Reference<rendering::XIntegerBitmapColorSpace> xIntColorSpace( + maSlideBitmapLayout.ColorSpace); + + if( xIntColorSpace->getType() == rendering::ColorSpaceType::RGB || + xIntColorSpace->getType() == rendering::ColorSpaceType::SRGB ) + { + /* table for canvas->OGL format mapping. outer index is number + of color components (0:3, 1:4), then comes bits per pixel + (0:16, 1:24, 2:32), then channel ordering: (0:rgba, 1:bgra, + 2:argb, 3:abgr) + */ + static const OGLFormat lcl_RGB24[] = + { + // 24 bit RGB + {3, GL_BGR, GL_UNSIGNED_BYTE}, + {3, GL_RGB, GL_UNSIGNED_BYTE}, + {3, GL_BGR, GL_UNSIGNED_BYTE}, + {3, GL_RGB, GL_UNSIGNED_BYTE} + }; + +#if defined(GL_VERSION_1_2) && defined(GLU_VERSION_1_3) + // more format constants available + static const OGLFormat lcl_RGB16[] = + { + // 16 bit RGB + {3, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, + {3, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, + {3, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, + {3, GL_RGB, GL_UNSIGNED_SHORT_5_6_5} + }; + + static const OGLFormat lcl_ARGB16_4[] = + { + // 16 bit ARGB + {4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, + {4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, + {4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4}, + {4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4} + }; + + static const OGLFormat lcl_ARGB16_5[] = + { + // 16 bit ARGB + {4, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + {4, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + {4, GL_BGRA, GL_UNSIGNED_SHORT_5_5_5_1}, + {4, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1} + }; + + static const OGLFormat lcl_ARGB32[] = + { + // 32 bit ARGB + {4, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, + {4, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + {4, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8}, + {4, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8} + }; + + const uno::Sequence<sal_Int8> aComponentTags( + xIntColorSpace->getComponentTags()); + const uno::Sequence<sal_Int32> aComponentBitcounts( + xIntColorSpace->getComponentBitCounts()); + const sal_Int32 nNumComponents( aComponentBitcounts.getLength() ); + const sal_Int32 nBitsPerPixel( xIntColorSpace->getBitsPerPixel() ); + + // supported component ordering? + const int nComponentOrderIndex( + calcComponentOrderIndex(aComponentTags)); + if( nComponentOrderIndex != -1 ) + { + switch( nBitsPerPixel ) + { + case 16: + if( nNumComponents == 3 ) + { + pDetectedFormat = &lcl_RGB16[nComponentOrderIndex]; + } + else if( nNumComponents == 4 ) + { + if( aComponentBitcounts[1] == 4 ) + { + pDetectedFormat = &lcl_ARGB16_4[nComponentOrderIndex]; + } + else if( aComponentBitcounts[1] == 5 ) + { + pDetectedFormat = &lcl_ARGB16_5[nComponentOrderIndex]; + } + } + break; + case 24: + if( nNumComponents == 3 ) + { + pDetectedFormat = &lcl_RGB24[nComponentOrderIndex]; + } + break; + case 32: + if ( nNumComponents == 4 ) + { + pDetectedFormat = &lcl_ARGB32[nComponentOrderIndex]; + } + break; + } + } +#else + const uno::Sequence<sal_Int8> aComponentTags( + xIntColorSpace->getComponentTags()); + const int nComponentOrderIndex(calcComponentOrderIndex(aComponentTags)); + if( aComponentTags.getLength() == 3 && + nComponentOrderIndex != -1 && + xIntColorSpace->getBitsPerPixel() == 24 ) + { + pDetectedFormat = &lcl_RGB24[nComponentOrderIndex]; + } +#endif + } + + return pDetectedFormat; +} + +void OGLTransitionerImpl::GLInitSlides() +{ + osl::MutexGuard const guard( m_aMutex ); + + if (isDisposed() || !mpTransition || mpTransition->getSettings().mnRequiredGLVersion > mnGLVersion) + return; + +#if OSL_DEBUG_LEVEL > 0 + TimerContext aTimerContext("texture creation"); +#endif + + mpContext->makeCurrent(); + + const OGLFormat* pFormat = chooseFormats(); + + CHECK_GL_ERROR(); + createTexture( &maLeavingSlideGL, + mpTransition->getSettings().mbUseMipMapLeaving, + maLeavingBytes, + pFormat ); + + createTexture( &maEnteringSlideGL, + mpTransition->getSettings().mbUseMipMapEntering, + maEnteringBytes, + pFormat ); + + CHECK_GL_ERROR(); + mpContext->sync(); + CHECK_GL_ERROR(); +} + +void SAL_CALL OGLTransitionerImpl::update( double nTime ) +{ +#if OSL_DEBUG_LEVEL > 0 + mnFrameCount ++; + m_UpdateStartTime = std::chrono::steady_clock::now(); + if( mnFrameCount == 1 ) { + m_StartTime = m_UpdateStartTime; + m_TotalUpdateDuration = std::chrono::seconds(0); + } +#endif + osl::MutexGuard const guard( m_aMutex ); + + if (isDisposed() || !mbValidOpenGLContext || !mpTransition || mpTransition->getSettings().mnRequiredGLVersion > mnGLVersion) + return; + + mpContext->makeCurrent(); + CHECK_GL_ERROR(); + + glEnable(GL_DEPTH_TEST); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + CHECK_GL_ERROR(); + + const GLWindow& rGLWindow(mpContext->getOpenGLWindow()); + mpTransition->display(nTime, maLeavingSlideGL, maEnteringSlideGL, + maSlideSize.Width, maSlideSize.Height, + static_cast<double>(rGLWindow.Width), + static_cast<double>(rGLWindow.Height), + mpContext.get()); + + mpContext->swapBuffers(); + + mpContext->show(); + mpContext->sync(); + CHECK_GL_ERROR(); + +#if OSL_DEBUG_LEVEL > 0 + m_UpdateEndTime = std::chrono::steady_clock::now(); + + SAL_INFO("slideshow.opengl", "update time: " << nTime); + SAL_INFO("slideshow.opengl", "update took: " << std::chrono::duration_cast<std::chrono::milliseconds>(m_UpdateEndTime - m_UpdateStartTime).count()); + m_TotalUpdateDuration += m_UpdateEndTime - m_UpdateStartTime; +#endif +} + +void SAL_CALL OGLTransitionerImpl::viewChanged( const Reference< presentation::XSlideShowView >& rView, + const Reference< rendering::XBitmap >& rLeavingBitmap, + const Reference< rendering::XBitmap >& rEnteringBitmap ) +{ + SAL_INFO("slideshow.opengl", "transitioner: view changed"); + + impl_dispose(); + + initWindowFromSlideShowView( rView ); + setSlides( rLeavingBitmap, rEnteringBitmap ); + impl_prepareSlides(); + impl_prepareTransition(); +} + +void OGLTransitionerImpl::disposeTextures() +{ + if (!mbValidOpenGLContext) + return; + + mpContext->makeCurrent(); + CHECK_GL_ERROR(); + + glDeleteTextures(1,&maLeavingSlideGL); + maLeavingSlideGL = 0; + glDeleteTextures(1,&maEnteringSlideGL); + maEnteringSlideGL = 0; + + CHECK_GL_ERROR(); +} + +void OGLTransitionerImpl::impl_dispose() +{ + if (mbValidOpenGLContext) + { + mpContext->makeCurrent(); + CHECK_GL_ERROR(); + } + + if( mpTransition && mpTransition->getSettings().mnRequiredGLVersion <= mnGLVersion ) + mpTransition->finish(); + disposeTextures(); + if( mpContext.is() ) + mpContext->dispose(); + mpContext.clear(); +} + +// we are about to be disposed (someone call dispose() on us) +void OGLTransitionerImpl::disposing() +{ + osl::MutexGuard const guard( m_aMutex ); + +#if OSL_DEBUG_LEVEL > 0 + SAL_INFO("slideshow.opengl", "dispose " << this); + if( mnFrameCount ) { + m_EndTime = std::chrono::steady_clock::now(); + auto const duration = m_EndTime - m_StartTime; + SAL_INFO("slideshow.opengl", + "whole transition (frames: " << mnFrameCount + << ") took: " << std::chrono::duration_cast<std::chrono::microseconds>(duration).count() + << " fps: " + << ((static_cast<double>(mnFrameCount)*1000000000.0)/std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()) + << " time spent in updates: " << std::chrono::duration_cast<std::chrono::microseconds>(m_TotalUpdateDuration).count() + << " percentage of transition time: " + << (100*((static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(m_TotalUpdateDuration).count()))/(static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count())))) + << '%' + ); + } +#endif + + if (mbRestoreSync && mpContext.is()) { + // try to reestablish synchronize state + const char* sal_synchronize = getenv("SAL_SYNCHRONIZE"); + mpContext->getOpenGLWindow().Synchronize(sal_synchronize && *sal_synchronize == '1' ); + } + + impl_dispose(); + + mpTransition.reset(); + + mxLeavingBitmap.clear(); + mxEnteringBitmap.clear(); + mxView.clear(); +} + +OGLTransitionerImpl::OGLTransitionerImpl() + : OGLTransitionerImplBase(m_aMutex) + , mpContext() + , maLeavingSlideGL(0) + , maEnteringSlideGL(0) + , mxView() + , maEnteringBytes() + , maLeavingBytes() + , mbRestoreSync(false) + , maSlideBitmapLayout() + , maSlideSize() + , mbBrokenTexturesATI(false) + , mnGLVersion(0) + , mbValidOpenGLContext(false) +#if OSL_DEBUG_LEVEL > 0 + , mnFrameCount(0) +#endif +{ +} + +typedef cppu::WeakComponentImplHelper<presentation::XTransitionFactory, lang::XServiceInfo> OGLTransitionFactoryImplBase; + +class OGLTransitionFactoryImpl : private cppu::BaseMutex, public OGLTransitionFactoryImplBase +{ +public: + explicit OGLTransitionFactoryImpl() : + OGLTransitionFactoryImplBase(m_aMutex) + {} + + // XServiceInfo + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override + { + return { "com.sun.star.presentation.TransitionFactory" }; + } + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.presentation.OGLTransitionFactory"; + } + virtual sal_Bool SAL_CALL supportsService(const OUString& aServiceName) override + { + return cppu::supportsService(this, aServiceName); + } + + // XTransitionFactory + virtual sal_Bool SAL_CALL hasTransition( sal_Int16 transitionType, sal_Int16 transitionSubType ) override + { + if( !OpenGLHelper::supportsOpenGL()) + return false; + // A set of css::animation::TransitionSubType that don't have any meaning (in the SMIL 2.0 + // standard) for MISCSHAPEWIPE have been chosen to refer to some of these "fancy" optional + // transitions. (The only subtypes of 'miscShapeWipe' defined in the standard are 'heart' + // and 'keyhole'.) The set of subtypes used seems to be a bit random; it starts from the + // beginning of the list (in the order (numeric) in our TransitionSubType set of constants) + // but then jumps a bit randomly. The numeric values as such have no meaning, but still. + + if( transitionType == animations::TransitionType::MISCSHAPEWIPE ) { + switch( transitionSubType ) + { + case animations::TransitionSubType::LEFTTORIGHT: // 1 + case animations::TransitionSubType::TOPTOBOTTOM: // 2 + case animations::TransitionSubType::TOPLEFT: // 3 + case animations::TransitionSubType::TOPRIGHT: // 4 + case animations::TransitionSubType::BOTTOMRIGHT: // 5 + case animations::TransitionSubType::BOTTOMLEFT: // 6 + case animations::TransitionSubType::TOPCENTER: // 7 + case animations::TransitionSubType::RIGHTCENTER: // 8 + case animations::TransitionSubType::BOTTOMCENTER: // 9 + case animations::TransitionSubType::CORNERSIN: // 11 + case animations::TransitionSubType::CORNERSOUT: // 12 + case animations::TransitionSubType::VERTICAL: // 13 + case animations::TransitionSubType::HORIZONTAL: // 14 + case animations::TransitionSubType::DIAMOND: // 26 + case animations::TransitionSubType::CIRCLE: // 27 + case animations::TransitionSubType::HEART: // 31 + case animations::TransitionSubType::FANOUTHORIZONTAL: // 55 + case animations::TransitionSubType::ACROSS: // 108 + return true; + + default: + return false; + } + } else if( transitionType == animations::TransitionType::FADE && transitionSubType == animations::TransitionSubType::CROSSFADE ) { + return true; + } else if( transitionType == animations::TransitionType::FADE && transitionSubType == animations::TransitionSubType::FADEOVERCOLOR ) { + return true; + } else if( transitionType == animations::TransitionType::IRISWIPE && transitionSubType == animations::TransitionSubType::DIAMOND ) { + return true; + } else if( transitionType == animations::TransitionType::ZOOM && transitionSubType == animations::TransitionSubType::ROTATEIN ) { + return true; + } else + return false; + } + + virtual uno::Reference< presentation::XTransition > SAL_CALL createTransition( + sal_Int16 transitionType, + sal_Int16 transitionSubType, + sal_Int32 transitionFadeColor, + const uno::Reference< presentation::XSlideShowView >& view, + const uno::Reference< rendering::XBitmap >& leavingBitmap, + const uno::Reference< rendering::XBitmap >& enteringBitmap ) override + { + if( !hasTransition( transitionType, transitionSubType ) ) + return uno::Reference< presentation::XTransition >(); + + rtl::Reference< OGLTransitionerImpl > xRes( new OGLTransitionerImpl() ); + if ( !xRes->initialize( view, leavingBitmap, enteringBitmap ) ) + return uno::Reference< presentation::XTransition >(); + + std::shared_ptr<OGLTransitionImpl> pTransition; + + if( transitionType == animations::TransitionType::MISCSHAPEWIPE ) { + switch( transitionSubType ) + { + case animations::TransitionSubType::LEFTTORIGHT: + pTransition = makeFallLeaving(); + break; + case animations::TransitionSubType::TOPTOBOTTOM: + pTransition = makeTurnAround(); + break; + case animations::TransitionSubType::TOPLEFT: + pTransition = makeIris(); + break; + case animations::TransitionSubType::TOPRIGHT: + pTransition = makeTurnDown(); + break; + case animations::TransitionSubType::BOTTOMRIGHT: + pTransition = makeRochade(); + break; + case animations::TransitionSubType::BOTTOMLEFT: + pTransition = makeVenetianBlinds( true, 8 ); + break; + case animations::TransitionSubType::TOPCENTER: + pTransition = makeVenetianBlinds( false, 6 ); + break; + case animations::TransitionSubType::RIGHTCENTER: + pTransition = makeStatic(); + break; + case animations::TransitionSubType::BOTTOMCENTER: + pTransition = makeDissolve(); + break; + case animations::TransitionSubType::CORNERSIN: + pTransition = makeInsideCubeFaceToLeft(); + break; + case animations::TransitionSubType::CORNERSOUT: + pTransition = makeOutsideCubeFaceToLeft(); + break; + case animations::TransitionSubType::VERTICAL: + pTransition = makeVortex(); + break; + case animations::TransitionSubType::HORIZONTAL: + pTransition = makeRipple(); + break; + case animations::TransitionSubType::CIRCLE: + pTransition = makeRevolvingCircles(8,128); + break; + case animations::TransitionSubType::FANOUTHORIZONTAL: + pTransition = makeHelix(20); + break; + case animations::TransitionSubType::ACROSS: + pTransition = makeNByMTileFlip(8,6); + break; + case animations::TransitionSubType::DIAMOND: + pTransition = makeGlitter(); + break; + case animations::TransitionSubType::HEART: + pTransition = makeHoneycomb(); + break; + } + } else if( transitionType == animations::TransitionType::FADE && transitionSubType == animations::TransitionSubType::CROSSFADE ) { + pTransition = makeFadeSmoothly(); + } else if( transitionType == animations::TransitionType::FADE && transitionSubType == animations::TransitionSubType::FADEOVERCOLOR ) { + pTransition = makeFadeThroughColor( transitionFadeColor == 0xffffff ); + } else if( transitionType == animations::TransitionType::IRISWIPE && transitionSubType == animations::TransitionSubType::DIAMOND ) { + pTransition = makeDiamond(); + } else if( transitionType == animations::TransitionType::ZOOM && transitionSubType == animations::TransitionSubType::ROTATEIN ) { + pTransition = makeNewsflash(); + } + + if ( !pTransition || !xRes->setTransition(pTransition) ) + return uno::Reference< presentation::XTransition >(); + + return xRes; + } +}; + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +slideshow_OGLTransitionFactoryImpl_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new OGLTransitionFactoryImpl()); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/opengl/ogltrans.component b/slideshow/source/engine/opengl/ogltrans.component new file mode 100644 index 000000000..5ce59a80e --- /dev/null +++ b/slideshow/source/engine/opengl/ogltrans.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.presentation.OGLTransitionFactory" + constructor="slideshow_OGLTransitionFactoryImpl_get_implementation"> + <service name="com.sun.star.presentation.TransitionFactory"/> + </implementation> +</component> |