diff --git a/data/assets/examples/pointclouds/data/interpolation_expand.csv b/data/assets/examples/pointclouds/data/interpolation_expand.csv new file mode 100644 index 0000000000..3bd9aede78 --- /dev/null +++ b/data/assets/examples/pointclouds/data/interpolation_expand.csv @@ -0,0 +1,61 @@ +time,x,y,z,dynamic_value,static_value +0.0,675.0297905065192,1672.6820684730765,-124.14442820502654,1,1 +0.0,9.0852354697237,1080.363474597831,266.4506394528842,3,3 +0.0,783.6498618493837,-332.90868790089644,166.73196289611994,5,5 +0.0,163.88116606175208,-978.1393719572736,1260.016529398092,10,10 +0.0,-934.1529593143864,-590.4853596059422,-622.5048517597389,11,11 +0.0,77.99426967464474,-184.5196836590583,1835.5681393474856,13,13 +0.0,77.99426967464474,-184.5196836590583,1835.5681393474856,14,14 +0.0,-269.98667369858055,-559.2891044933908,-1487.2591171480176,15,15 +0.0,-710.8143185498654,-627.4209210346181,-1504.56518583112,17,17 +0.0,-710.8143185498654,-627.4209210346181,-1504.56518583112,18,18 +1.0,1350.0595810130385,3345.364136946153,-248.28885641005309,2,1 +1.0,18.1704709394474,2160.726949195662,532.9012789057684,6,3 +1.0,1567.2997236987674,-665.8173758017929,333.4639257922399,10,5 +1.0,327.76233212350417,-1956.2787439145472,2520.033058796184,20,10 +1.0,-1868.3059186287728,-1180.9707192118844,-1245.0097035194779,22,11 +1.0,155.98853934928948,-369.0393673181166,3671.136278694971,26,13 +1.0,155.98853934928948,-369.0393673181166,3671.136278694971,28,14 +1.0,-539.9733473971611,-1118.5782089867816,-2974.518234296035,30,15 +1.0,-1421.628637099731,-1254.8418420692362,-3009.13037166224,34,17 +1.0,-1421.628637099731,-1254.8418420692362,-3009.13037166224,36,18 +2.0,2700.119162026077,6690.728273892306,-496.57771282010617,4,1 +2.0,36.3409418788948,4321.453898391324,1065.8025578115369,12,3 +2.0,3134.5994473975347,-1331.6347516035858,666.9278515844798,20,5 +2.0,655.5246642470083,-3912.5574878290945,5040.066117592368,40,10 +2.0,-3736.6118372575456,-2361.941438423769,-2490.0194070389557,44,11 +2.0,311.97707869857896,-738.0787346362332,7342.272557389942,52,13 +2.0,311.97707869857896,-738.0787346362332,7342.272557389942,56,14 +2.0,-1079.9466947943222,-2237.1564179735633,-5949.03646859207,60,15 +2.0,-2843.257274199462,-2509.6836841384725,-6018.26074332448,68,17 +2.0,-2843.257274199462,-2509.6836841384725,-6018.26074332448,72,18 +3.0,5400.238324052154,13381.456547784612,-993.1554256402123,8,1 +3.0,72.6818837577896,8642.907796782649,2131.6051156230737,24,3 +3.0,6269.1988947950695,-2663.2695032071715,1333.8557031689595,40,5 +3.0,1311.0493284940167,-7825.114975658189,10080.132235184736,80,10 +3.0,-7473.223674515091,-4723.882876847538,-4980.038814077911,88,11 +3.0,623.9541573971579,-1476.1574692724664,14684.545114779885,104,13 +3.0,623.9541573971579,-1476.1574692724664,14684.545114779885,112,14 +3.0,-2159.8933895886444,-4474.312835947127,-11898.07293718414,120,15 +3.0,-5686.514548398924,-5019.367368276945,-12036.52148664896,136,17 +3.0,-5686.514548398924,-5019.367368276945,-12036.52148664896,144,18 +4.0,10800.476648104308,26762.913095569224,-1986.3108512804247,16,1 +4.0,145.3637675155792,17285.815593565298,4263.2102312461475,48,3 +4.0,12538.397789590139,-5326.539006414343,2667.711406337919,80,5 +4.0,2622.0986569880333,-15650.229951316378,20160.26447036947,160,10 +4.0,-14946.447349030183,-9447.765753695076,-9960.077628155823,176,11 +4.0,1247.9083147943159,-2952.314938544933,29369.09022955977,208,13 +4.0,1247.9083147943159,-2952.314938544933,29369.09022955977,224,14 +4.0,-4319.786779177289,-8948.625671894253,-23796.14587436828,240,15 +4.0,-11373.029096797847,-10038.73473655389,-24073.04297329792,272,17 +4.0,-11373.029096797847,-10038.73473655389,-24073.04297329792,288,18 +5.0,21600.953296208616,53525.82619113845,-3972.6217025608494,32,1 +5.0,290.7275350311584,34571.631187130595,8526.420462492295,96,3 +5.0,25076.795579180278,-10653.078012828686,5335.422812675838,160,5 +5.0,5244.197313976067,-31300.459902632756,40320.52894073894,320,10 +5.0,-29892.894698060365,-18895.53150739015,-19920.155256311646,352,11 +5.0,2495.8166295886317,-5904.629877089866,58738.18045911954,416,13 +5.0,2495.8166295886317,-5904.629877089866,58738.18045911954,448,14 +5.0,-8639.573558354577,-17897.251343788506,-47592.29174873656,480,15 +5.0,-22746.058193595694,-20077.46947310778,-48146.08594659584,544,17 +5.0,-22746.058193595694,-20077.46947310778,-48146.08594659584,576,18 diff --git a/data/assets/examples/pointclouds/data/interpolation_randomwalk.csv b/data/assets/examples/pointclouds/data/interpolation_randomwalk.csv new file mode 100644 index 0000000000..f8681b6d42 --- /dev/null +++ b/data/assets/examples/pointclouds/data/interpolation_randomwalk.csv @@ -0,0 +1,61 @@ +time,x,y,z +0.0,675.0297905065192,1672.6820684730765,-124.14442820502654 +0.0,9.0852354697237,1080.363474597831,266.4506394528842 +0.0,783.6498618493837,-332.90868790089644,166.73196289611994 +0.0,163.88116606175208,-978.1393719572736,1260.016529398092 +0.0,-934.1529593143864,-590.4853596059422,-622.5048517597389 +0.0,77.99426967464474,-184.5196836590583,1835.5681393474856 +0.0,77.99426967464474,-184.5196836590583,1835.5681393474856 +0.0,-269.98667369858055,-559.2891044933908,-1487.2591171480176 +0.0,-710.8143185498654,-627.4209210346181,-1504.56518583112 +0.0,-710.8143185498654,-627.4209210346181,-1504.56518583112 +1.0,1668.4580965289847,745.846682848152,2807.1531096380813 +1.0,-267.4433668726456,148.90396745731732,-185.1019615201871 +1.0,2079.026938050769,151.01585523117177,301.7883722719676 +1.0,3209.940878877803,-4804.699861272869,-1589.4798430288217 +1.0,-1402.4597087610582,-4040.3210246320077,-1711.2703008101043 +1.0,-390.2796442237164,-1309.0947421410037,2057.413318767218 +1.0,3236.419900689428,-2210.181924327906,-466.4190154971202 +1.0,1264.882784607237,69.2055606971569,-735.8630804566736 +1.0,-1649.7630904197697,-2443.46907207704,-2705.84256566873 +1.0,374.30576862206385,-3452.028323705201,-2087.952685417674 +2.0,465.0448720701909,2222.779842838973,3455.3210484276715 +2.0,3437.11300214523,491.6405298372583,-955.2665223528202 +2.0,2052.032488574901,-80.28070954530929,-1052.0556283399499 +2.0,1094.5190209660022,-5406.907252451447,-366.127265347086 +2.0,242.63011544531992,-6997.3650053668625,350.72874418179754 +2.0,-2831.9669441657607,-2748.783158930421,3919.9735569996146 +2.0,3654.1470906989384,-3131.459466247481,-2144.8540619423975 +2.0,-2654.4574631523137,2183.4500131349887,-1354.287832159103 +2.0,-4306.135188216631,-1756.232492940117,-2043.315702861602 +2.0,768.0282403603109,-6978.108634430669,-136.86243117295544 +3.0,-493.303603620389,2945.5710538558005,3015.977272752648 +3.0,3065.7950488175957,3567.7136627691966,-241.04137932932736 +3.0,-160.63745943715548,-151.93278776521237,1903.7324611430824 +3.0,-1297.8942271953392,-2277.6199408234343,-1402.6677018943808 +3.0,1867.681760233716,-12236.85521354635,-1266.2584616045776 +3.0,-119.48688702411482,-1104.9781501799732,2916.459469830542 +3.0,3510.126847538271,-2957.3653297711385,-314.04982653824914 +3.0,-647.3916673682654,1585.4353122032537,-696.785612839734 +3.0,-3582.8631381213627,-1572.7109398691125,-3102.8361103956795 +3.0,-522.2112688499377,-10953.246463632455,889.6724350537568 +4.0,-299.1485049243082,114.829569754972,-821.5651578454349 +4.0,5003.085029883374,2726.4230172384787,-294.0691302277611 +4.0,-1564.7436471918602,-837.3618208187513,2024.1928810251354 +4.0,-1953.2185203908757,-3882.1744792666723,3523.8165230761915 +4.0,1083.4654539694006,-12559.426636878368,-1650.9803911668228 +4.0,-3046.516783288352,-296.8764365508964,3519.554154497767 +4.0,4102.367401667423,815.0064726499218,-383.47336594873576 +4.0,-125.2811230084867,1934.5909378669317,-3034.141688078798 +4.0,-3572.6362248364408,-1057.610158423584,-817.1904813656383 +4.0,-991.3855356002316,-11102.13829516479,2393.538500427305 +5.0,1282.9153891617857,-2986.4972923772934,-366.64528863717607 +5.0,3184.310120293896,2863.5489668505334,2320.216378337095 +5.0,1240.8449746803383,-2961.969248270961,-1190.7735880973198 +5.0,-4756.920645975437,-2934.989617996309,3893.0842401408 +5.0,2257.179641569941,-14398.275105345974,-1131.2148026699756 +5.0,1334.3944683316054,2802.9923734841823,5083.199898052388 +5.0,2121.294751406046,-751.5001120225525,-2857.3747877048995 +5.0,-1257.8765822140306,1290.4679054555804,-5675.05491424735 +5.0,-3373.3334946611585,569.4242763157556,226.69264986815688 +5.0,-1998.3368438326302,-13563.866928032701,2987.507846893677 diff --git a/data/assets/examples/pointclouds/interpolated_points.asset b/data/assets/examples/pointclouds/interpolated_points.asset new file mode 100644 index 0000000000..ee0110f364 --- /dev/null +++ b/data/assets/examples/pointclouds/interpolated_points.asset @@ -0,0 +1,92 @@ +local Points = { + Identifier = "Example_InterpolatedPoints_ColorMapped", + Renderable = { + Type = "RenderableInterpolatedPoints", + -- The dataset here is just a linearly expanding dataset, where the points move in + -- a straight line + File = asset.resource("data/interpolation_expand.csv"), + -- Specify how many objects the rows in the dataset represent. Here, the dataset is + -- consists of 10 objects with positions at 6 different time steps. This information + -- is required + NumberOfObjects = 10, + -- Both the position and data values will be interpolated, so use a color map + Coloring = { + ColorMapping = { + File = asset.resource("viridis.cmap") + } + }, + -- Reduce the scale of the points a bit compared to default, so we see them more clearly + SizeSettings = { + ScaleExponent = 3.5 + } + }, + GUI = { + Name = "Interpolating Points with Color Map", + Path = "/Example/Interpolated Point Clouds", + Description = [[Example of interpolating points with a color map. The data value + used for the coloring will also be inteprolated, leading to the points changing + color throughout the interpolation.]] + } +} + +local Points_Smoothed = { + Identifier = "Example_InterpolatedPoints_Spline", + Renderable = { + Type = "RenderableInterpolatedPoints", + -- Using a random walk dataset, to get movement in some different directions + File = asset.resource("data/interpolation_randomwalk.csv"), + -- Same number of objects as above - 10 objects with positions at 6 different + -- time steps + NumberOfObjects = 10, + Interpolation = { + -- Smoothen transitions between two different sets of points, by + -- using a spline based interpolation of the points + UseSplineInterpolation = true + }, + -- Just use a fixed coloring here, no color mapping + Coloring = { + FixedColor = { 0.0, 0.5, 0.0 } + }, + -- Reduce the scale of the points a bit compared to default, so we see them more clearly + SizeSettings = { + ScaleExponent = 3.0 + } + }, + GUI = { + Name = "Interpolating Points (Spline)", + Path = "/Example/Interpolated Point Clouds", + Description = [[Example of interpolating points with spline-based interpolation + for the position. This leads to smoother transitions at the nodes of the + interpolation. Try disabling the spline interpolation in the GUI (under + Renderable->Interpolation) to see the difference.]] + } +} + + +asset.onInitialize(function() + openspace.addSceneGraphNode(Points) + openspace.addSceneGraphNode(Points_Smoothed) +end) + +asset.onDeinitialize(function() + openspace.removeSceneGraphNode(Points_Smoothed) + openspace.removeSceneGraphNode(Points) +end) + +asset.export(Points) +asset.export(Points_Smoothed) + + + +asset.meta = { + Name = "Example - Interpolated Point Clouds", + Version = "1.0", + Description = [[Example of point clouds that support interpolation. One uses a linear + motion and color mapping. The other example uses a spline interpolation for the + approximation of the positions, to result in smoother transitions between the sets + of positions. The interpolation can be triggered using the properties under + Renderable->Interpolation in the Scene menu.]], + Author = "OpenSpace Team", + URL = "http://openspaceproject.com", + License = "MIT license" +} diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index 7baa93e400..e3168b3272 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -44,6 +44,7 @@ set(HEADER_FILES rendering/grids/renderablegrid.h rendering/grids/renderableradialgrid.h rendering/grids/renderablesphericalgrid.h + rendering/pointcloud/renderableinterpolatedpoints.h rendering/pointcloud/renderablepointcloud.h rendering/pointcloud/renderablepolygoncloud.h rendering/renderablecartesianaxes.h @@ -105,6 +106,7 @@ set(SOURCE_FILES rendering/grids/renderablegrid.cpp rendering/grids/renderableradialgrid.cpp rendering/grids/renderablesphericalgrid.cpp + rendering/pointcloud/renderableinterpolatedpoints.cpp rendering/pointcloud/renderablepointcloud.cpp rendering/pointcloud/renderablepolygoncloud.cpp rendering/renderablecartesianaxes.cpp @@ -152,9 +154,6 @@ set(SHADER_FILES shaders/arrow_vs.glsl shaders/axes_fs.glsl shaders/axes_vs.glsl - shaders/billboardpoint_fs.glsl - shaders/billboardpoint_gs.glsl - shaders/billboardpoint_vs.glsl shaders/disc_fs.glsl shaders/disc_vs.glsl shaders/grid_vs.glsl @@ -167,6 +166,10 @@ set(SHADER_FILES shaders/model_vs.glsl shaders/plane_fs.glsl shaders/plane_vs.glsl + shaders/pointcloud/billboardpoint_fs.glsl + shaders/pointcloud/billboardpoint_gs.glsl + shaders/pointcloud/billboardpoint_vs.glsl + shaders/pointcloud/billboardpoint_interpolated_vs.glsl shaders/polygon_fs.glsl shaders/polygon_gs.glsl shaders/polygon_vs.glsl diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index 2b46683efb..d2619a9dbe 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -146,6 +147,9 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) { fRenderable->registerClass( "RenderablePlaneTimeVaryingImage" ); + fRenderable->registerClass( + "RenderableInterpolatedPoints" + ); fRenderable->registerClass("RenderablePointCloud"); fRenderable->registerClass("RenderablePolygonCloud"); fRenderable->registerClass("RenderablePrism"); @@ -226,6 +230,7 @@ std::vector BaseModule::documentations() const { RenderableCartesianAxes::Documentation(), RenderableDisc::Documentation(), RenderableGrid::Documentation(), + RenderableInterpolatedPoints::Documentation(), RenderableLabel::Documentation(), RenderableModel::Documentation(), RenderableNodeArrow::Documentation(), diff --git a/modules/base/rendering/pointcloud/renderableinterpolatedpoints.cpp b/modules/base/rendering/pointcloud/renderableinterpolatedpoints.cpp new file mode 100644 index 0000000000..94defce7d0 --- /dev/null +++ b/modules/base/rendering/pointcloud/renderableinterpolatedpoints.cpp @@ -0,0 +1,544 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr std::string_view _loggerCat = "RenderableInterpolatedPoints"; + + constexpr openspace::properties::Property::PropertyInfo InterpolationValueInfo = { + "Value", + "Value", + "The value to use for interpolation. The max value is set from the number of " + "steps in the dataset, so a step of one corresponds to one step in the dataset " + "and values in-between will be determined using interpolation.", + openspace::properties::Property::Visibility::NoviceUser + }; + + constexpr openspace::properties::Property::PropertyInfo StepsInfo = { + "NumberOfSteps", + "Number of Steps", + "The number of steps available in the dataset, including the initial positions.", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo JumpToNextInfo = { + "JumpToNext", + "Jump to Next", + "Immediately set the interpolation value to correspond to the next set of point " + "positions compared to the current.", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo JumpToPrevInfo = { + "JumpToPrevious", + "Jump to Previous", + "Immediately set the interpolation value to correspond to the previous set of " + "point positions compared to the current.", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo InterpolateToNextInfo = { + "InterpolateToNext", + "Interpolate to Next", + "Trigger an interpolation to the next set of point positions. The duration of " + "the interpolation is set based on the Interpolaton Speed property.", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo InterpolateToPrevInfo = { + "InterpolateToPrevious", + "Interpolate to Previous", + "Trigger an interpolation to the previous set of point positions. The duration " + "of the interpolation is set based on the Interpolaton Speed property.", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo InterpolateToEndInfo = { + "InterpolateToEnd", + "Interpolate to End", + "Trigger an interpolation all the way to the final set of positions. The " + "duration of the interpolation is set based on the Interpolaton Speed property.", + openspace::properties::Property::Visibility::NoviceUser + }; + + constexpr openspace::properties::Property::PropertyInfo InterpolateToStartInfo = { + "InterpolateToStart", + "Interpolate to Start", + "Trigger an inverted interpolation to the initial set of positions. The duration " + "of the interpolation is set based on the Interpolaton Speed property.", + openspace::properties::Property::Visibility::NoviceUser + }; + + constexpr openspace::properties::Property::PropertyInfo InterpolationSpeedInfo = { + "Speed", + "Interpolation Speed", + "Affects how long the interpolation takes when triggered using one of the " + "trigger properties. A value of 1 means that a step takes 1 second.", + openspace::properties::Property::Visibility::NoviceUser + }; + + constexpr openspace::properties::Property::PropertyInfo UseSplineInfo = { + "UseSplineInterpolation", + "Use Spline Interpolation", + "If true, the points will be interpolated using a Catmull-Rom spline instead of " + "linearly. This leads to a smoother transition at the breakpoints, i.e. between " + "each step.", + openspace::properties::Property::Visibility::AdvancedUser + }; + + // RenderableInterpolatedPoints is a version of the RenderablePointCloud class, where + // the dataset may contain multiple time steps that can be interpolated between. It + // supports interpolation of both of positions and data values used for color mapping + // or size. + // + // The dataset should be structured in a way so that the first N rows correspond to + // the first set of positions for the objects, the next N rows to the second set of + // positions, and so on. The number of objects in the dataset must be specified in the + // asset. + struct [[codegen::Dictionary(RenderableInterpolatedPoints)]] Parameters { + // The number of objects to read from the dataset. Every N:th datapoint will + // be interpreted as the same point, but at a different step in the interpolation + int numberOfObjects [[codegen::greaterequal(1)]]; + + struct Interpolation { + // [[codegen::verbatim(InterpolationValueInfo.description)]] + std::optional value; + + // [[codegen::verbatim(InterpolationSpeedInfo.description)]] + std::optional speed; + + // [[codegen::verbatim(UseSplineInfo.description)]] + std::optional useSplineInterpolation; + }; + // Initial settings for the interpolation + std::optional interpolation; + }; + +#include "renderableinterpolatedpoints_codegen.cpp" +} // namespace + +namespace openspace { + +documentation::Documentation RenderableInterpolatedPoints::Documentation() { + return codegen::doc( + "base_renderableinterpolatedpoints", + RenderablePointCloud::Documentation() + ); +} + +RenderableInterpolatedPoints::Interpolation::Interpolation() + : properties::PropertyOwner({ "Interpolation", "Interpolation", "" }) + , value(InterpolationValueInfo, 0.f, 0.f, 1.f) + , nSteps(StepsInfo) + , goToNextStep(JumpToNextInfo) + , goToPrevStep(JumpToPrevInfo) + , interpolateToNextStep(InterpolateToNextInfo) + , interpolateToPrevStep(InterpolateToPrevInfo) + , interpolateToEnd(InterpolateToEndInfo) + , interpolateToStart(InterpolateToStartInfo) + , speed(InterpolationSpeedInfo, 1.f, 0.01f, 100.f) + , useSpline(UseSplineInfo, false) +{ + addProperty(value); + + auto triggerInterpolation = [](std::string_view identifier, float v, float d) { + std::string script = fmt::format( + "openspace.setPropertyValueSingle(\"{}\", {}, {})", + identifier, v, d + ); + // No syncing, as this was triggered from a property change (which happened + // based on an already synced script) + global::scriptEngine->queueScript( + script, + scripting::ScriptEngine::ShouldBeSynchronized::No, + scripting::ScriptEngine::ShouldSendToRemote::No + ); + }; + + interpolateToEnd.onChange([triggerInterpolation, this]() { + float remaining = value.maxValue() - value; + float duration = remaining / speed; + triggerInterpolation( + value.fullyQualifiedIdentifier(), + value.maxValue(), + duration + ); + }); + + interpolateToStart.onChange([triggerInterpolation, this]() { + float duration = value / speed; + triggerInterpolation(value.fullyQualifiedIdentifier(), 0.f, duration); + }); + + interpolateToNextStep.onChange([triggerInterpolation, this]() { + float prevValue = glm::floor(value); + float newValue = glm::min(prevValue + 1.f, value.maxValue()); + float duration = 1.f / speed; + triggerInterpolation(value.fullyQualifiedIdentifier(), newValue, duration); + }); + + interpolateToPrevStep.onChange([triggerInterpolation, this]() { + float prevValue = glm::ceil(value); + float newValue = glm::max(prevValue - 1.f, value.minValue()); + float duration = 1.f / speed; + triggerInterpolation(value.fullyQualifiedIdentifier(), newValue, duration); + }); + + addProperty(interpolateToEnd); + addProperty(interpolateToStart); + addProperty(interpolateToNextStep); + addProperty(interpolateToPrevStep); + addProperty(speed); + + goToNextStep.onChange([this]() { + float prevValue = glm::floor(value); + value = glm::min(prevValue + 1.f, value.maxValue()); + }); + + goToPrevStep.onChange([this]() { + float prevValue = glm::ceil(value); + value = glm::max(prevValue - 1.f, value.minValue()); + }); + + addProperty(goToNextStep); + addProperty(goToPrevStep); + + nSteps.setReadOnly(true); + addProperty(nSteps); + + addProperty(useSpline); +} + +RenderableInterpolatedPoints::RenderableInterpolatedPoints( + const ghoul::Dictionary& dictionary) + : RenderablePointCloud(dictionary) +{ + const Parameters p = codegen::bake(dictionary); + + addPropertySubOwner(_interpolation); + + if (p.interpolation.has_value()) { + _interpolation.value = static_cast( + p.interpolation->value.value_or(_interpolation.value) + ); + _interpolation.speed = static_cast( + p.interpolation->speed.value_or(_interpolation.speed) + ); + _interpolation.useSpline = p.interpolation->useSplineInterpolation.value_or( + _interpolation.useSpline + ); + } + + unsigned int nObjects = static_cast(p.numberOfObjects); + + // At this point, the dataset has been loaded and the number of points computed. We + // need to recompute them and compute how many steps the number of points + // corresponded to + + if (_nDataPoints % nObjects != 0) { + LERROR(fmt::format( + "Mismatch between provided number of data entries and the specified number " + "of points. Expected the number of entries in the data file {} to be evenly " + "divisible by the number of points", _dataFile + )); + } + + _interpolation.nSteps = _nDataPoints / nObjects; + _interpolation.value.setMaxValue(static_cast(_interpolation.nSteps - 1)); + + _interpolation.value.onChange([this]() { + bool passedAKnot = + glm::ceil(_interpolation.value) != glm::ceil(_prevInterpolationValue); + + if (passedAKnot) { + _dataIsDirty = true; + } + _prevInterpolationValue = _interpolation.value; + }); + + _interpolation.useSpline.onChange([this]() { + _dataIsDirty = true; + _shouldReinitializeBufferdata = true; + }); + + // This property is mostly for show in the UI, but also used to tell how many points + // should be rendered. So make sure it is updated once we know the number of + // interpolation steps + _nDataPoints = nObjects; +} + +void RenderableInterpolatedPoints::initializeShadersAndGlExtras() { + _program = BaseModule::ProgramObjectManager.request( + "RenderablePointCloud_Interpolated", + []() { + return global::renderEngine->buildRenderProgram( + "RenderablePointCloud_Interpolated", + absPath("${MODULE_BASE}/shaders/pointcloud/billboardpoint_interpolated_vs.glsl"), + absPath("${MODULE_BASE}/shaders/pointcloud/billboardpoint_fs.glsl"), + absPath("${MODULE_BASE}/shaders/pointcloud/billboardpoint_gs.glsl") + ); + } + ); + + initializeBufferData(); +} + +void RenderableInterpolatedPoints::deinitializeShaders() { + BaseModule::ProgramObjectManager.release( + "RenderablePointCloud_Interpolated", + [](ghoul::opengl::ProgramObject* p) { + global::renderEngine->removeRenderProgram(p); + } + ); + _program = nullptr; +} + +void RenderableInterpolatedPoints::bindDataForPointRendering() { + RenderablePointCloud::bindDataForPointRendering(); + + float t0 = computeCurrentLowerValue(); + float t = glm::clamp(_interpolation.value - t0, 0.f, 1.f); + _program->setUniform("interpolationValue", t); + _program->setUniform("useSpline", _interpolation.useSpline); +} + +void RenderableInterpolatedPoints::preUpdate() { + if (_shouldReinitializeBufferdata) { + initializeBufferData(); + _shouldReinitializeBufferdata = false; + } +} + +int RenderableInterpolatedPoints::nAttributesPerPoint() const { + int n = RenderablePointCloud::nAttributesPerPoint(); + // Need twice as much information as the regular points + n *= 2; + if (_interpolation.useSpline) { + // Use two more positions (xyz) + n += 2 * 3; + } + return n; +} + +std::vector RenderableInterpolatedPoints::createDataSlice() { + ZoneScoped; + + if (_dataset.entries.empty()) { + return std::vector(); + } + + std::vector result; + result.reserve(nAttributesPerPoint() * _nDataPoints); + + // Find the information we need for the interpolation and to identify the points, + // and make sure these result in valid indices in all cases + float t0 = computeCurrentLowerValue(); + float t1 = t0 + 1.f; + t1 = glm::clamp(t1, 0.f, _interpolation.value.maxValue()); + unsigned int t0Index = static_cast(t0); + unsigned int t1Index = static_cast(t1); + + // What datavar is in use for the index color + int colorParamIndex = currentColorParameterIndex(); + + // What datavar is in use for the size scaling (if present) + int sizeParamIndex = currentSizeParameterIndex(); + + double maxRadius = 0.0; + + for (unsigned int i = 0; i < _nDataPoints; i++) { + using namespace dataloader; + const Dataset::Entry& e0 = _dataset.entries[t0Index * _nDataPoints + i]; + const Dataset::Entry& e1 = _dataset.entries[t1Index * _nDataPoints + i]; + glm::dvec3 position0 = transformedPosition(e0); + glm::dvec3 position1 = transformedPosition(e1); + + const double r = glm::max(glm::length(position0), glm::length(position1)); + maxRadius = glm::max(maxRadius, r); + + // Positions + for (int j = 0; j < 3; ++j) { + result.push_back(static_cast(position0[j])); + } + + for (int j = 0; j < 3; ++j) { + result.push_back(static_cast(position1[j])); + } + + if (_interpolation.useSpline && _interpolation.nSteps > 1) { + // Compute the extra positions, before and after the other ones + unsigned int beforeIndex = static_cast( + glm::max(t0 - 1.f, 0.f) + ); + unsigned int afterIndex = static_cast( + glm::min(t1 + 1.f, _interpolation.value.maxValue() - 1.f) + ); + + const Dataset::Entry& e00 = _dataset.entries[beforeIndex * _nDataPoints + i]; + const Dataset::Entry& e11 = _dataset.entries[afterIndex * _nDataPoints + i]; + glm::dvec3 positionBefore = transformedPosition(e00); + glm::dvec3 positionAfter = transformedPosition(e11); + + for (int j = 0; j < 3; ++j) { + result.push_back(static_cast(positionBefore[j])); + } + + for (int j = 0; j < 3; ++j) { + result.push_back(static_cast(positionAfter[j])); + } + } + + // Colors + if (_hasColorMapFile) { + result.push_back(e0.data[colorParamIndex]); + result.push_back(e1.data[colorParamIndex]); + } + + // Size data + if (_hasDatavarSize) { + // @TODO: Consider more detailed control over the scaling. Currently the value + // is multiplied with the value as is. Should have similar mapping properties + // as the color mapping + result.push_back(e0.data[sizeParamIndex]); + result.push_back(e1.data[sizeParamIndex]); + } + + // @TODO: Also need to update label positions, if we have created labels from the dataset + // And make sure these are created from only the first set of points.. + } + setBoundingSphere(maxRadius); + return result; +} + +void RenderableInterpolatedPoints::initializeBufferData() { + if (_vao == 0) { + glGenVertexArrays(1, &_vao); + LDEBUG(fmt::format("Generating Vertex Array id '{}'", _vao)); + } + if (_vbo == 0) { + glGenBuffers(1, &_vbo); + LDEBUG(fmt::format("Generating Vertex Buffer Object id '{}'", _vbo)); + } + + const int attibutesPerPoint = nAttributesPerPoint(); + const unsigned int bufferSize = attibutesPerPoint * _nDataPoints * sizeof(float); + + // Allocate the memory for the buffer (we will want to upload the data quite often) + glBindVertexArray(_vao); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferData(GL_ARRAY_BUFFER, bufferSize, nullptr, GL_DYNAMIC_DRAW); + + int attributeOffset = 0; + + auto addFloatAttribute = [&](const std::string& name, GLint nValues) { + GLint attrib = _program->attributeLocation(name); + glEnableVertexAttribArray(attrib); + glVertexAttribPointer( + attrib, + nValues, + GL_FLOAT, + GL_FALSE, + attibutesPerPoint * sizeof(float), + (attributeOffset > 0) ? + reinterpret_cast(attributeOffset * sizeof(float)) : + nullptr + ); + attributeOffset += nValues; + }; + + addFloatAttribute("in_position0", 3); + addFloatAttribute("in_position1", 3); + + if (_interpolation.useSpline) { + addFloatAttribute("in_position_before", 3); + addFloatAttribute("in_position_after", 3); + } + + if (_hasColorMapFile) { + addFloatAttribute("in_colorParameter0", 1); + addFloatAttribute("in_colorParameter1", 1); + } + + if (_hasDatavarSize) { + addFloatAttribute("in_scalingParameter0", 1); + addFloatAttribute("in_scalingParameter1", 1); + } + + glBindVertexArray(0); +} + +void RenderableInterpolatedPoints::updateBufferData() { + if (!_hasDataFile || _dataset.entries.empty()) { + return; + } + + ZoneScopedN("Data dirty"); + TracyGpuZone("Data dirty"); + LDEBUG("Regenerating data"); + + // Regenerate data and update buffer + std::vector slice = createDataSlice(); + + glBindVertexArray(_vao); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, slice.size() * sizeof(float), slice.data()); + + glBindVertexArray(0); + + _dataIsDirty = false; +} + +bool RenderableInterpolatedPoints::isAtKnot() const { + float v = _interpolation.value; + return (v - glm::floor(v)) < std::numeric_limits::epsilon(); +} + +float RenderableInterpolatedPoints::computeCurrentLowerValue() const { + float t0 = glm::floor(_interpolation.value); + + if (isAtKnot()) { + t0 = t0 - 1.f; + } + + const float maxTValue = _interpolation.value.maxValue(); + float maxAllowedT0 = glm::max(maxTValue - 1.f, 0.f); + t0 = glm::clamp(t0, 0.f, maxAllowedT0); + return t0; +} + +} // namespace openspace diff --git a/modules/base/rendering/pointcloud/renderableinterpolatedpoints.h b/modules/base/rendering/pointcloud/renderableinterpolatedpoints.h new file mode 100644 index 0000000000..23d0a479db --- /dev/null +++ b/modules/base/rendering/pointcloud/renderableinterpolatedpoints.h @@ -0,0 +1,100 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_BASE___RENDERABLEINTERPOLATEDPOINTS___H__ +#define __OPENSPACE_MODULE_BASE___RENDERABLEINTERPOLATEDPOINTS___H__ + +#include + +#include +#include +#include +#include + +namespace ghoul::opengl { class Texture; } + +namespace openspace { + +namespace documentation { struct Documentation; } + +/** + * A specialization of the RenderablePointCloud that supports interpolation beetween + * different positions for the points. + */ +class RenderableInterpolatedPoints : public RenderablePointCloud { +public: + explicit RenderableInterpolatedPoints(const ghoul::Dictionary& dictionary); + ~RenderableInterpolatedPoints() override = default; + + static documentation::Documentation Documentation(); + +protected: + void initializeShadersAndGlExtras() override; + void deinitializeShaders() override; + void bindDataForPointRendering() override; + void preUpdate() override; + + int nAttributesPerPoint() const override; + + /** + * Create the data slice to use for rendering the points. Compared to the regular + * point cloud, the data slice for an interpolated set of points will have to be + * recreated when the interpolation value changes, and will only include a subset of + * the points in the entire dataset + * + * \return The dataslice to use for rendering the points + */ + std::vector createDataSlice() override; + + void initializeBufferData(); + void updateBufferData() override; + +private: + bool isAtKnot() const; + float computeCurrentLowerValue() const; + + struct Interpolation : public properties::PropertyOwner { + Interpolation(); + properties::FloatProperty value; + properties::UIntProperty nSteps; + + properties::TriggerProperty goToNextStep; + properties::TriggerProperty goToPrevStep; + properties::TriggerProperty interpolateToNextStep; + properties::TriggerProperty interpolateToPrevStep; + properties::TriggerProperty interpolateToEnd; + properties::TriggerProperty interpolateToStart; + properties::FloatProperty speed; + + properties::BoolProperty useSpline; + }; + Interpolation _interpolation; + + float _prevInterpolationValue = 0.f; + bool _shouldReinitializeBufferdata = false; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___RENDERABLEINTERPOLATEDPOINTS___H__ diff --git a/modules/base/rendering/pointcloud/renderablepointcloud.cpp b/modules/base/rendering/pointcloud/renderablepointcloud.cpp index 6c8e08031f..f7ededdb66 100644 --- a/modules/base/rendering/pointcloud/renderablepointcloud.cpp +++ b/modules/base/rendering/pointcloud/renderablepointcloud.cpp @@ -628,17 +628,7 @@ void RenderablePointCloud::initialize() { void RenderablePointCloud::initializeGL() { ZoneScoped; - _program = BaseModule::ProgramObjectManager.request( - "RenderablePointCloud", - []() { - return global::renderEngine->buildRenderProgram( - "RenderablePointCloud", - absPath("${MODULE_BASE}/shaders/billboardpoint_vs.glsl"), - absPath("${MODULE_BASE}/shaders/billboardpoint_fs.glsl"), - absPath("${MODULE_BASE}/shaders/billboardpoint_gs.glsl") - ); - } - ); + initializeShadersAndGlExtras(); ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); @@ -653,6 +643,27 @@ void RenderablePointCloud::deinitializeGL() { glDeleteVertexArrays(1, &_vao); _vao = 0; + deinitializeShaders(); + + BaseModule::TextureManager.release(_spriteTexture); + _spriteTexture = nullptr; +} + +void RenderablePointCloud::initializeShadersAndGlExtras() { + _program = BaseModule::ProgramObjectManager.request( + "RenderablePointCloud", + []() { + return global::renderEngine->buildRenderProgram( + "RenderablePointCloud", + absPath("${MODULE_BASE}/shaders/pointcloud/billboardpoint_vs.glsl"), + absPath("${MODULE_BASE}/shaders/pointcloud/billboardpoint_fs.glsl"), + absPath("${MODULE_BASE}/shaders/pointcloud/billboardpoint_gs.glsl") + ); + } + ); +} + +void RenderablePointCloud::deinitializeShaders() { BaseModule::ProgramObjectManager.release( "RenderablePointCloud", [](ghoul::opengl::ProgramObject* p) { @@ -660,9 +671,6 @@ void RenderablePointCloud::deinitializeGL() { } ); _program = nullptr; - - BaseModule::TextureManager.release(_spriteTexture); - _spriteTexture = nullptr; } void RenderablePointCloud::bindTextureForRendering() const { @@ -699,51 +707,8 @@ float RenderablePointCloud::computeDistanceFadeValue(const RenderData& data) con return fadeValue * funcValue; } -void RenderablePointCloud::renderBillboards(const RenderData& data, - const glm::dmat4& modelMatrix, - const glm::dvec3& orthoRight, - const glm::dvec3& orthoUp, - float fadeInVariable) -{ - if (!_hasDataFile || _dataset.entries.empty()) { - return; - } - - glEnablei(GL_BLEND, 0); - - if (_useAdditiveBlending) { - glDepthMask(false); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - } - else { - // Normal blending, with transparency - glDepthMask(true); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - _program->activate(); - - _program->setUniform(_uniformCache.cameraPos, data.camera.positionVec3()); - _program->setUniform( - _uniformCache.cameraLookup, - glm::vec3(data.camera.lookUpVectorWorldSpace()) - ); +void RenderablePointCloud::bindDataForPointRendering() { _program->setUniform(_uniformCache.renderOption, _renderOption.value()); - _program->setUniform(_uniformCache.modelMatrix, modelMatrix); - - _program->setUniform( - _uniformCache.cameraViewMatrix, - data.camera.combinedViewMatrix() - ); - - _program->setUniform( - _uniformCache.projectionMatrix, - glm::dmat4(data.camera.projectionMatrix()) - ); - - _program->setUniform(_uniformCache.up, glm::vec3(orthoUp)); - _program->setUniform(_uniformCache.right, glm::vec3(orthoRight)); - _program->setUniform(_uniformCache.fadeInValue, fadeInVariable); _program->setUniform(_uniformCache.opacity, opacity()); _program->setUniform(_uniformCache.scaleExponent, _sizeSettings.scaleExponent); @@ -811,9 +776,58 @@ void RenderablePointCloud::renderBillboards(const RenderData& data, _colorSettings.colorMapping->useBelowRangeColor ); } +} + +void RenderablePointCloud::renderBillboards(const RenderData& data, + const glm::dmat4& modelMatrix, + const glm::dvec3& orthoRight, + const glm::dvec3& orthoUp, + float fadeInVariable) +{ + if (!_hasDataFile || _dataset.entries.empty()) { + return; + } + + glEnablei(GL_BLEND, 0); + + if (_useAdditiveBlending) { + glDepthMask(false); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } + else { + // Normal blending, with transparency + glDepthMask(true); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + _program->activate(); + + _program->setUniform(_uniformCache.cameraPos, data.camera.positionVec3()); + _program->setUniform( + _uniformCache.cameraLookup, + glm::vec3(data.camera.lookUpVectorWorldSpace()) + ); + _program->setUniform(_uniformCache.renderOption, _renderOption.value()); + _program->setUniform(_uniformCache.modelMatrix, modelMatrix); + + _program->setUniform( + _uniformCache.cameraViewMatrix, + data.camera.combinedViewMatrix() + ); + + _program->setUniform( + _uniformCache.projectionMatrix, + glm::dmat4(data.camera.projectionMatrix()) + ); + + _program->setUniform(_uniformCache.up, glm::vec3(orthoUp)); + _program->setUniform(_uniformCache.right, glm::vec3(orthoRight)); + _program->setUniform(_uniformCache.fadeInValue, fadeInVariable); + + bindDataForPointRendering(); glBindVertexArray(_vao); - glDrawArrays(GL_POINTS, 0, static_cast(_dataset.entries.size())); + glDrawArrays(GL_POINTS, 0, static_cast(_nDataPoints)); glBindVertexArray(0); _program->deactivate(); @@ -857,9 +871,13 @@ void RenderablePointCloud::render(const RenderData& data, RendererTasks&) { } } +void RenderablePointCloud::preUpdate() {} + void RenderablePointCloud::update(const UpdateData&) { ZoneScoped; + preUpdate(); + if (_dataIsDirty) { updateBufferData(); } @@ -869,8 +887,16 @@ void RenderablePointCloud::update(const UpdateData&) { } } +glm::dvec3 RenderablePointCloud::transformedPosition( + const dataloader::Dataset::Entry& e) const +{ + const double unitMeter = toMeter(_unit); + glm::dvec4 position = glm::dvec4(glm::dvec3(e.position) * unitMeter, 1.0); + return glm::dvec3(_transformationMatrix * position); +} + int RenderablePointCloud::nAttributesPerPoint() const { - int n = 4; // position + int n = 3; // position n += _hasColorMapFile ? 1 : 0; n += _hasDatavarSize ? 1 : 0; return n; @@ -909,13 +935,13 @@ void RenderablePointCloud::updateBufferData() { glEnableVertexAttribArray(positionAttrib); glVertexAttribPointer( positionAttrib, - 4, + 3, GL_FLOAT, GL_FALSE, attibutesPerPoint * sizeof(float), nullptr ); - attributeOffset += 4; + attributeOffset += 3; if (_hasColorMapFile) { GLint colorParamAttrib = _program->attributeLocation("in_colorParameter"); @@ -1022,24 +1048,20 @@ std::vector RenderablePointCloud::createDataSlice() { int sizeParamIndex = currentSizeParameterIndex(); double maxRadius = 0.0; - double biggestCoord = -1.0; for (const dataloader::Dataset::Entry& e : _dataset.entries) { - const double unitMeter = toMeter(_unit); - glm::dvec4 position = glm::dvec4(glm::dvec3(e.position) * unitMeter, 1.0); - position = _transformationMatrix * position; + glm::dvec3 position = transformedPosition(e); const double r = glm::length(position); maxRadius = std::max(maxRadius, r); // Positions - for (int j = 0; j < 4; ++j) { + for (int j = 0; j < 3; ++j) { result.push_back(static_cast(position[j])); } // Colors if (_hasColorMapFile) { - biggestCoord = std::max(biggestCoord, glm::compMax(position)); result.push_back(e.data[colorParamIndex]); } diff --git a/modules/base/rendering/pointcloud/renderablepointcloud.h b/modules/base/rendering/pointcloud/renderablepointcloud.h index ab51cfe2d9..630678b3b8 100644 --- a/modules/base/rendering/pointcloud/renderablepointcloud.h +++ b/modules/base/rendering/pointcloud/renderablepointcloud.h @@ -74,8 +74,16 @@ public: static documentation::Documentation Documentation(); protected: - int nAttributesPerPoint() const; - void updateBufferData(); + virtual void initializeShadersAndGlExtras(); + virtual void deinitializeShaders(); + virtual void bindDataForPointRendering(); + virtual void preUpdate(); + + glm::dvec3 transformedPosition(const dataloader::Dataset::Entry& e) const; + + virtual int nAttributesPerPoint() const; + + virtual void updateBufferData(); void updateSpriteTexture(); /// Find the index of the currently chosen color parameter in the dataset @@ -83,7 +91,7 @@ protected: /// Find the index of the currently chosen size parameter in the dataset int currentSizeParameterIndex() const; - std::vector createDataSlice(); + virtual std::vector createDataSlice(); virtual void bindTextureForRendering() const; diff --git a/modules/base/shaders/billboardpoint_fs.glsl b/modules/base/shaders/pointcloud/billboardpoint_fs.glsl similarity index 100% rename from modules/base/shaders/billboardpoint_fs.glsl rename to modules/base/shaders/pointcloud/billboardpoint_fs.glsl diff --git a/modules/base/shaders/billboardpoint_gs.glsl b/modules/base/shaders/pointcloud/billboardpoint_gs.glsl similarity index 100% rename from modules/base/shaders/billboardpoint_gs.glsl rename to modules/base/shaders/pointcloud/billboardpoint_gs.glsl diff --git a/modules/base/shaders/pointcloud/billboardpoint_interpolated_vs.glsl b/modules/base/shaders/pointcloud/billboardpoint_interpolated_vs.glsl new file mode 100644 index 0000000000..494b096e2f --- /dev/null +++ b/modules/base/shaders/pointcloud/billboardpoint_interpolated_vs.glsl @@ -0,0 +1,91 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +#include "PowerScaling/powerScaling_vs.hglsl" + +in vec3 in_position0; +in vec3 in_position1; + +// Only used if spline interpolation is desired +in vec3 in_position_before; +in vec3 in_position_after; + +in float in_colorParameter0; +in float in_colorParameter1; +in float in_scalingParameter0; +in float in_scalingParameter1; + +uniform bool useSpline; +uniform float interpolationValue; + +flat out float colorParameter; +flat out float scalingParameter; + +float interpolateDataValue(float v0, float v1, float t) { + const float Epsilon = 1E-7; + const float NaN = log(-1.0); // undefined + // To make sure we render values at knots with neighboring missing values, + // check 0 and 1 expicitly + if (abs(t) < Epsilon) { + return v0; + } + if (abs(1.0 - t) < Epsilon) { + return v1; + } + bool isMissing = isnan(v0) || isnan(v1); + return isMissing ? NaN : mix(v0, v1, t); +} + +vec3 interpolateCatmullRom(float t, vec3 p0, vec3 p1, vec3 p2, vec3 p3) { + float t2 = t * t; + float t3 = t2 * t; + return 0.5 * ( + 2.0 * p1 + + t * (p2 - p0) + + t2 * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) + + t3 * (3.0 * p1 - p0 - 3.0 * p2 + p3) + ); +} + +void main() { + float t = interpolationValue; + + colorParameter = interpolateDataValue(in_colorParameter0, in_colorParameter1, t); + scalingParameter = interpolateDataValue(in_scalingParameter0, in_scalingParameter1, t); + + vec3 position = mix(in_position0, in_position1, t); + if (useSpline) { + position = interpolateCatmullRom( + t, + in_position_before, + in_position0, + in_position1, + in_position_after + ); + } + + gl_Position = vec4(position, 1.0); +} diff --git a/modules/base/shaders/billboardpoint_vs.glsl b/modules/base/shaders/pointcloud/billboardpoint_vs.glsl similarity index 97% rename from modules/base/shaders/billboardpoint_vs.glsl rename to modules/base/shaders/pointcloud/billboardpoint_vs.glsl index 35e8082b41..a92d569161 100644 --- a/modules/base/shaders/billboardpoint_vs.glsl +++ b/modules/base/shaders/pointcloud/billboardpoint_vs.glsl @@ -26,7 +26,7 @@ #include "PowerScaling/powerScaling_vs.hglsl" -in vec4 in_position; +in vec3 in_position; in float in_colorParameter; in float in_scalingParameter; @@ -36,5 +36,5 @@ flat out float scalingParameter; void main() { colorParameter = in_colorParameter; scalingParameter = in_scalingParameter; - gl_Position = in_position; + gl_Position = vec4(in_position, 1.0); }