diff --git a/data/assets/examples/pointclouds/data/dummydata.csv b/data/assets/examples/pointclouds/data/dummydata.csv new file mode 100644 index 0000000000..e8b698833f --- /dev/null +++ b/data/assets/examples/pointclouds/data/dummydata.csv @@ -0,0 +1,101 @@ +x,y,z,a,b,normaldist_withMissing,number_withNan +13428000,26239000,45870000,-3.226548224,33.95773276,-0.357778948,29 +14727000,45282000,10832000,45.05941924,-106.0395917,,29 +24999000,28370000,19911000,-70.58906931,154.1851656,-0.167961782,Nan +26539000,36165000,39582000,-13.3663358,71.79484733,0.113536778,9 +49056000,24775000,14626000,21.42870979,-115.6088304,0.125551095,37 +43965000,21472000,18760000,65.07055022,-56.36880466,0.172981386,18 +15661000,28429000,16965000,76.15826514,125.3163407,,40 +25046000,36006000,49453000,-22.31710915,137.4486786,0.447921314,28 +13797000,34811000,18825000,-79.40300933,74.05580595,-0.02379786,12 +13879000,14824000,41472000,-30.93548431,-8.755047834,-0.041777813,50 +11481000,20480000,25925000,75.51244012,178.1377926,-0.044396255,0 +45032000,16606000,23537000,37.38766828,175.5064508,-0.449674816,13 +14395000,34940000,21968000,-31.6863061,-116.6587323,-0.09741961,30 +15945000,21478000,29536000,-53.01226701,59.18196347,0.304142338,30 +13458000,19017000,18542000,8.72692265,80.65872957,,45 +45302000,15732000,34369000,-85.57061714,124.6890481,-0.364449145,20 +15559000,36269000,18160000,-4.975784243,-156.8789425,-0.012893853,22 +16552000,14570000,39763000,-39.2579671,75.22960824,-0.281792015,Nan +11353000,13410000,38311000,8.610608538,-36.43103437,-0.196021206,9 +12383000,49302000,33539000,69.26506588,-109.0830926,-0.147483079,34 +44302000,21387000,23434000,16.14574541,78.81171702,0.115624185,5 +43107000,14206000,24728000,37.57233254,142.6103028,,Nan +49320000,43878000,24240000,-39.10527498,149.2751575,-0.086246397,29 +37519000,40514000,16409000,-24.14434437,157.2871976,-0.071946303,12 +37202000,40122000,42551000,-79.88824934,-124.089513,0.375414362,31 +12783000,11757000,25348000,-11.83866388,118.2423568,-0.243289139,Nan +39621000,30560000,15929000,75.8365484,-95.18288548,-0.399996182,37 +10937000,15183000,21413000,-32.40573,-157.8337361,-0.202310776,39 +39411000,12532000,44006000,34.67409469,-83.29512212,,Nan +47905000,25552000,28902000,36.19437814,52.25734185,,32 +28426000,45268000,27886000,-70.843469,-63.7321218,0.112287307,45 +15026000,45897000,23911000,-89.01161062,10.16376248,-0.00400866,Nan +24625000,14844000,12837000,88.91663258,169.8003753,-0.151728888,5 +46304000,21144000,24421000,-41.58069299,59.63971512,-0.371030712,48 +29558000,10724000,27312000,9.663492403,-71.18121738,0.344402457,Nan +49636000,27202000,24626000,59.46440387,-92.91431055,0.26256468,31 +38603000,23794000,41040000,70.45128929,-67.84861911,-0.184920666,48 +37336000,21742000,40982000,-15.69131418,134.6754731,0.078786176,15 +35128000,38725000,25244000,2.279345664,34.91612867,0.091098807,44 +30419000,33591000,39512000,-56.16030331,133.9105282,-0.590693487,8 +18665000,11189000,45438000,-45.68302316,-85.35524485,0.046936859,26 +15442000,36392000,16074000,81.4389588,-56.70945587,0.038804669,5 +43378000,27900000,38748000,-74.35249265,-133.1884413,0.429053963,17 +44424000,19982000,13528000,-81.19709235,49.49364675,0.115926289,47 +42733000,15383000,28933000,75.17020088,-11.11330632,0.127627239,32 +40144000,46822000,29588000,-81.25577073,-135.8002245,0.346061193,33 +16927000,38232000,43931000,-80.97022269,157.7565432,-0.096039391,39 +38910000,36030000,38003000,-37.79005665,-1.5474944,0.107402594,11 +48340000,38510000,39290000,21.94846638,-112.6129615,0.230803493,3 +17258000,45665000,27152000,80.73726495,-31.33714033,-0.079079307,10 +48363000,39701000,11905000,11.83766221,-69.99777106,-0.002514692,3 +27660000,29370000,26029000,8.179457229,147.7719143,-0.006520509,31 +47106000,30527000,17038000,73.00898959,9.988869939,0.797492967,5 +10471000,37521000,35398000,57.46145065,-29.5045034,-0.261313809,39 +27774000,44082000,40036000,78.47974779,-4.998179635,0.437739286,24 +46887000,20413000,36649000,51.11098102,-136.3276432,0.237434245,41 +10093000,46706000,19187000,-58.41750426,-157.9933447,0.275586609,45 +49093000,36514000,38499000,-55.41355694,150.177605,0.576467955,21 +48945000,49826000,23225000,-63.91174083,-177.7191007,0.475443991,Nan +26852000,28926000,32243000,-77.854481,116.9006581,,1 +34219000,34787000,30801000,-62.32990484,23.24232366,0.089523916,40 +31075000,30369000,36898000,-77.78314619,38.69526825,-0.046423443,7 +28135000,21412000,25552000,-7.411565773,-88.65122734,0.258690016,Nan +13347000,15980000,43094000,74.71054756,-37.32614369,,7 +26928000,31159000,16653000,-32.74503199,146.7695347,0.576290488,34 +12364000,14374000,43646000,28.70641859,-92.35199713,-0.059531367,16 +38991000,49388000,48534000,56.70658222,8.014973542,-0.173400177,30 +17180000,14935000,11680000,-45.39957586,102.5726701,0.16285705,1 +27666000,29337000,44778000,89.4063742,-79.68068618,0.044140443,41 +13660000,11108000,37986000,57.03349279,7.98684928,0.066242706,24 +36910000,10252000,41657000,-9.906961203,21.79114496,-0.378241692,6 +21785000,20095000,14903000,77.49612237,-84.68789002,-0.071118836,16 +33363000,42124000,39814000,-64.47776113,-30.79591997,0.371115832,35 +42758000,48892000,44762000,42.39782697,-32.55868099,-0.146011285,1 +16590000,22098000,28515000,-36.90992927,-55.27481385,-0.203981239,27 +26448000,45475000,33361000,64.22197115,146.6687002,-0.026210913,1 +41182000,23927000,25075000,-81.55929754,137.9213773,0.042229126,10 +49801000,15636000,39883000,-89.22857667,159.9681504,0.035529624,15 +32725000,13037000,49874000,-55.17873101,61.01413612,0.519438664,43 +31130000,16471000,41939000,-45.36963748,-156.5100924,0.245045969,22 +19599000,37119000,26468000,-26.58523044,174.7731562,-0.210527713,22 +30024000,23097000,14711000,44.31464407,150.7894846,-0.041347399,45 +39567000,48586000,49391000,1.721781555,156.1687027,,31 +46249000,28249000,32393000,-13.3221674,-176.0644697,-0.108879159,30 +12180000,20488000,46288000,36.02858732,30.43526779,-0.099205446,47 +29659000,40098000,45152000,72.15373455,62.01511311,,46 +41026000,19309000,15845000,-38.63636224,145.9569745,-0.083266866,32 +47146000,39852000,26666000,35.2426196,-33.97130609,-0.650089141,7 +39958000,46945000,11789000,-68.31344333,172.4154216,0.001713968,37 +25921000,13147000,22257000,-15.89505915,-150.39693,0.165178387,37 +28892000,28329000,49094000,-58.72206735,-157.3776844,-0.228068143,48 +29563000,48312000,45703000,86.78718085,179.1386066,-0.146072441,49 +46127000,46582000,13462000,39.98033674,-164.152123,-0.081830298,17 +23268000,24051000,48652000,-43.61767649,83.51428751,-0.106824408,45 +22163000,26901000,28702000,51.04347167,169.7632117,0.052782471,31 +46968000,17027000,25787000,-73.8664022,2.915300454,-0.028069047,22 +24148000,44640000,15422000,8.379412685,101.9542853,-0.049339904,14 +11566000,26486000,11735000,-36.78748293,-129.8960512,,Nan +11063000,38883000,16772000,-65.43894139,105.6607872,,7 +43081000,16718000,45813000,-2.464830259,87.07999887,-0.24057898,40 diff --git a/data/assets/examples/pointclouds/data/dummydata.label b/data/assets/examples/pointclouds/data/dummydata.label new file mode 100644 index 0000000000..fb38a1f3e0 --- /dev/null +++ b/data/assets/examples/pointclouds/data/dummydata.label @@ -0,0 +1,100 @@ +13428000 26239000 45870000 id P0 text Point 0 +14727000 45282000 10832000 id P1 text Point 1 +24999000 28370000 19911000 id P2 text Point 2 +26539000 36165000 39582000 id P3 text Point 3 +49056000 24775000 14626000 id P4 text Point 4 +43965000 21472000 18760000 id P5 text Point 5 +15661000 28429000 16965000 id P6 text Point 6 +25046000 36006000 49453000 id P7 text Point 7 +13797000 34811000 18825000 id P8 text Point 8 +13879000 14824000 41472000 id P9 text Point 9 +11481000 20480000 25925000 id P10 text Point 10 +45032000 16606000 23537000 id P11 text Point 11 +14395000 34940000 21968000 id P12 text Point 12 +15945000 21478000 29536000 id P13 text Point 13 +13458000 19017000 18542000 id P14 text Point 14 +45302000 15732000 34369000 id P15 text Point 15 +15559000 36269000 18160000 id P16 text Point 16 +16552000 14570000 39763000 id P17 text Point 17 +11353000 13410000 38311000 id P18 text Point 18 +12383000 49302000 33539000 id P19 text Point 19 +44302000 21387000 23434000 id P20 text Point 20 +43107000 14206000 24728000 id P21 text Point 21 +49320000 43878000 24240000 id P22 text Point 22 +37519000 40514000 16409000 id P23 text Point 23 +37202000 40122000 42551000 id P24 text Point 24 +12783000 11757000 25348000 id P25 text Point 25 +39621000 30560000 15929000 id P26 text Point 26 +10937000 15183000 21413000 id P27 text Point 27 +39411000 12532000 44006000 id P28 text Point 28 +47905000 25552000 28902000 id P29 text Point 29 +28426000 45268000 27886000 id P30 text Point 30 +15026000 45897000 23911000 id P31 text Point 31 +24625000 14844000 12837000 id P32 text Point 32 +46304000 21144000 24421000 id P33 text Point 33 +29558000 10724000 27312000 id P34 text Point 34 +49636000 27202000 24626000 id P35 text Point 35 +38603000 23794000 41040000 id P36 text Point 36 +37336000 21742000 40982000 id P37 text Point 37 +35128000 38725000 25244000 id P38 text Point 38 +30419000 33591000 39512000 id P39 text Point 39 +18665000 11189000 45438000 id P40 text Point 40 +15442000 36392000 16074000 id P41 text Point 41 +43378000 27900000 38748000 id P42 text Point 42 +44424000 19982000 13528000 id P43 text Point 43 +42733000 15383000 28933000 id P44 text Point 44 +40144000 46822000 29588000 id P45 text Point 45 +16927000 38232000 43931000 id P46 text Point 46 +38910000 36030000 38003000 id P47 text Point 47 +48340000 38510000 39290000 id P48 text Point 48 +17258000 45665000 27152000 id P49 text Point 49 +48363000 39701000 11905000 id P50 text Point 50 +27660000 29370000 26029000 id P51 text Point 51 +47106000 30527000 17038000 id P52 text Point 52 +10471000 37521000 35398000 id P53 text Point 53 +27774000 44082000 40036000 id P54 text Point 54 +46887000 20413000 36649000 id P55 text Point 55 +10093000 46706000 19187000 id P56 text Point 56 +49093000 36514000 38499000 id P57 text Point 57 +48945000 49826000 23225000 id P58 text Point 58 +26852000 28926000 32243000 id P59 text Point 59 +34219000 34787000 30801000 id P60 text Point 60 +31075000 30369000 36898000 id P61 text Point 61 +28135000 21412000 25552000 id P62 text Point 62 +13347000 15980000 43094000 id P63 text Point 63 +26928000 31159000 16653000 id P64 text Point 64 +12364000 14374000 43646000 id P65 text Point 65 +38991000 49388000 48534000 id P66 text Point 66 +17180000 14935000 11680000 id P67 text Point 67 +27666000 29337000 44778000 id P68 text Point 68 +13660000 11108000 37986000 id P69 text Point 69 +36910000 10252000 41657000 id P70 text Point 70 +21785000 20095000 14903000 id P71 text Point 71 +33363000 42124000 39814000 id P72 text Point 72 +42758000 48892000 44762000 id P73 text Point 73 +16590000 22098000 28515000 id P74 text Point 74 +26448000 45475000 33361000 id P75 text Point 75 +41182000 23927000 25075000 id P76 text Point 76 +49801000 15636000 39883000 id P77 text Point 77 +32725000 13037000 49874000 id P78 text Point 78 +31130000 16471000 41939000 id P79 text Point 79 +19599000 37119000 26468000 id P80 text Point 80 +30024000 23097000 14711000 id P81 text Point 81 +39567000 48586000 49391000 id P82 text Point 82 +46249000 28249000 32393000 id P83 text Point 83 +12180000 20488000 46288000 id P84 text Point 84 +29659000 40098000 45152000 id P85 text Point 85 +41026000 19309000 15845000 id P86 text Point 86 +47146000 39852000 26666000 id P87 text Point 87 +39958000 46945000 11789000 id P88 text Point 88 +25921000 13147000 22257000 id P89 text Point 89 +28892000 28329000 49094000 id P90 text Point 90 +29563000 48312000 45703000 id P91 text Point 91 +46127000 46582000 13462000 id P92 text Point 92 +23268000 24051000 48652000 id P93 text Point 93 +22163000 26901000 28702000 id P94 text Point 94 +46968000 17027000 25787000 id P95 text Point 95 +24148000 44640000 15422000 id P96 text Point 96 +11566000 26486000 11735000 id P97 text Point 97 +11063000 38883000 16772000 id P98 text Point 98 +43081000 16718000 45813000 id P99 text Point 99 diff --git a/data/assets/examples/pointclouds/points.asset b/data/assets/examples/pointclouds/points.asset new file mode 100644 index 0000000000..e298045d79 --- /dev/null +++ b/data/assets/examples/pointclouds/points.asset @@ -0,0 +1,237 @@ +local earthAsset = asset.require("scene/solarsystem/planets/earth/earth") + + + +-- Color mapped points close to Earth, with some different settings for color and +-- sizing, and a StaticRotation that makes them not overlap + +-- Point cloud with fixed color and default size (fixed) +local FixedColor_FixedSize = { + Identifier = "ExamplePoints_FixedSize", + Parent = earthAsset.Earth.Identifier, + Renderable = { + Type = "RenderablePointCloud", + File = asset.resource("data/dummydata.csv"), + Coloring = { + FixedColor = { 0.0, 0.5, 0.0 } + } + }, + GUI = { + Name = "Fixed Color / Fixed Size", + Path = "/Example/Point Clouds", + Description = "Point cloud with a fixed color and fixed sizing" + } +} + +-- Point cloud with fixed color and size scaling that is limited by a certain size, +-- in pixels +local FixedColor_MaxPixelSize = { + Identifier = "ExamplePoints_MaxPixelSize", + Parent = earthAsset.Earth.Identifier, + Transform = { + Rotation = { + Type = "StaticRotation", + Rotation = { 0, 0, 0.5 * math.pi } + } + }, + Renderable = { + Type = "RenderablePointCloud", + File = asset.resource("data/dummydata.csv"), + Coloring = { + FixedColor = { 0.0, 1.0, 1.0 } + }, + SizeSettings = { + MaxPixelSize = 4.7, + EnablePixelSizeControl = true + } + }, + GUI = { + Name = "Fixed Color / Max Pixel Size", + Path = "/Example/Point Clouds", + Description = "Point cloud with a fixed color and sizing with a given max pixel size" + } +} + +-- Point cloud with color mapping and fixed point sizing +local ColorMapped_FixedSize = { + Identifier = "ExamplePoints_ColorMapped", + Parent = earthAsset.Earth.Identifier, + Transform = { + Rotation = { + Type = "StaticRotation", + Rotation = { 0, 0, -0.5 * math.pi } + } + }, + Renderable = { + Type = "RenderablePointCloud", + File = asset.resource("data/dummydata.csv"), + Coloring = { + ColorMapping = { + File = asset.resource("viridis.cmap") + } + }, + SizeSettings = { + -- Here we set the exponent for the scale explicitly, to a value that + -- gives the points a suitable size based on their world-space coordinates + ScaleExponent = 6.5 + } + }, + GUI = { + Name = "Color Mapped", + Path = "/Example/Point Clouds", + Description = "Color mapped point cloud with fixed sizing" + } +} + +-- Point cloud with fixed color and size scaling from a selected data column +local FixedColor_ScaleBasedOnData = { + Identifier = "ExamplePoints_ScaleFromData", + Parent = earthAsset.Earth.Identifier, + Transform = { + Rotation = { + Type = "StaticRotation", + Rotation = { 0, 0.5 * math.pi, 0 } + } + }, + Renderable = { + Type = "RenderablePointCloud", + File = asset.resource("data/dummydata.csv"), + Coloring = { + FixedColor = { 0.5, 0.5, 0.0 } + }, + SizeSettings = { + -- The options for the columns that the points can be scaled by. The first + -- alternative is chosen per default + SizeMapping = { "number_withNan", "a" }, + -- Use a slightly smaller scale than above for the base size of the points + -- (will decide the size of the smallest point). That way, the points don't + -- become too big when scaled by the data parameter + ScaleExponent = 5 + } + }, + GUI = { + Name = "Fixed Color / Size From Data", + Path = "/Example/Point Clouds", + Description = [[Point cloud with a fixed color and sizing that can be set based on a + column in the dataset]] + } +} + +-- Point cloud with textures. Textured points can also be color mapped, but here it +-- is disabled per default +local Textured = { + Identifier = "ExamplePoints_Textured", + Parent = earthAsset.Earth.Identifier, + Transform = { + Rotation = { + Type = "StaticRotation", + Rotation = { 0, math.pi, 0 } + } + }, + Renderable = { + Type = "RenderablePointCloud", + File = asset.resource("data/dummydata.csv"), + -- The path to the texture file. Here we use openspace.absPath so that we can use + -- the ${DATA} token to get the path to a texture in the "OpenSpace/data" folder, + -- but for a file at a relative location it would also work to use asset.resource, + -- like for the data file above + Texture = openspace.absPath("${DATA}/test3.jpg"), + -- Disable additive blending, so that points will be rendered with their actual color + -- and overlapping points will be sorted by depth. This works best when the points + -- have an opacity of 1 + UseAdditiveBlending = false, + Coloring = { + ColorMapping = { + -- Disable color map per default. When enabled, the texture color will be + -- multiplied with the color from the color map + Enabled = false, + File = asset.resource("viridis.cmap") + } + } + }, + GUI = { + Name = "Textured", + Path = "/Example/Point Clouds", + Description = "Point cloud with a texture per point" + } +} + +-- Point cloud with more advanced color mapping and fixed point sizing. +-- Here we have predefined a couple of parameters to use for the color mapping. +-- Also, missing/NaN values are mapped to a specific color. +-- Finally, no additive blending is used for the color, meaning that the color of +-- overlapping points will not be mixed/added +local ColorMappedAdvanced_NoBlend = { + Identifier = "ExamplePoints_ColorMappedNoBlend", + Parent = earthAsset.Earth.Identifier, + Transform = { + Rotation = { + Type = "StaticRotation", + Rotation = { 0, 0, math.pi } + } + }, + Renderable = { + Type = "RenderablePointCloud", + File = asset.resource("data/dummydata.csv"), + UseAdditiveBlending = false, -- Disable additive blending + Coloring = { + ColorMapping = { + File = asset.resource("viridis.cmap"), + ParameterOptions = { + { Key = "number_withNan" }, -- no range => compute min and max + { Key = "normaldist_withMissing", Range = { -0.5, 0.5 } } + }, + ShowMissingData = true, + NoDataColor = { 0.5, 0.5, 0.5, 1.0 } + } + }, + SizeSettings = { + ScaleExponent = 6.5 + } + }, + GUI = { + Name = "Color Mapped (Advanced) - No blending", + Path = "/Example/Point Clouds", + Description = [[Color mapped point cloud without additive blending, with missing + values shown in grey, and a limited choice of parameter options to use for + the color mapping.]] + } +} + + +asset.onInitialize(function() + openspace.addSceneGraphNode(FixedColor_FixedSize) + openspace.addSceneGraphNode(FixedColor_MaxPixelSize) + openspace.addSceneGraphNode(FixedColor_ScaleBasedOnData) + openspace.addSceneGraphNode(Textured) + openspace.addSceneGraphNode(ColorMapped_FixedSize) + openspace.addSceneGraphNode(ColorMappedAdvanced_NoBlend) +end) + +asset.onDeinitialize(function() + openspace.removeSceneGraphNode(ColorMappedAdvanced_NoBlend) + openspace.removeSceneGraphNode(ColorMapped_FixedSize) + openspace.removeSceneGraphNode(Textured) + openspace.removeSceneGraphNode(FixedColor_ScaleBasedOnData) + openspace.removeSceneGraphNode(FixedColor_MaxPixelSize) + openspace.removeSceneGraphNode(FixedColor_FixedSize) +end) + +asset.export(FixedColor_FixedSize) +asset.export(FixedColor_MaxPixelSize) +asset.export(FixedColor_ScaleBasedOnData) +asset.export(Textured) +asset.export(ColorMapped_FixedSize) +asset.export(ColorMappedAdvanced_NoBlend) + + + +asset.meta = { + Name = "Example - Point Clouds", + Version = "1.0", + Description = [[Examples of point clouds with a few different settings for sizing and + coloring]], + Author = "OpenSpace Team", + URL = "http://openspaceproject.com", + License = "MIT license" +} diff --git a/data/assets/examples/pointclouds/points_colormappingsettings.asset b/data/assets/examples/pointclouds/points_colormappingsettings.asset new file mode 100644 index 0000000000..83ca91f125 --- /dev/null +++ b/data/assets/examples/pointclouds/points_colormappingsettings.asset @@ -0,0 +1,78 @@ +local earthAsset = asset.require("scene/solarsystem/planets/earth/earth") + + + +local Example = { + Identifier = "ExamplePoints_ColorMapping", + Parent = earthAsset.Earth.Identifier, + Renderable = { + Type = "RenderablePointCloud", + File = asset.resource("data/dummydata.csv"), + Coloring = { + ColorMapping = { + File = asset.resource("viridis.cmap"), + -- Set the default choice of parameter and value range explicitly. Values + -- outside this range will be given special colors + Parameter = "normaldist_withMissing", + ValueRange = { -0.25, 0.25 }, + -- Show missing data values, so we can show these in a specific color + ShowMissingData = true, + -- Color for missing data points + NoDataColor = { 1.0, 0.0, 0.0, 1.0 }, + -- Color for point with values above the given range. If not set, or if + -- UseAboveRangeColor is false, the color will be set to the last value + -- in the color map (as per default) + AboveRangeColor = { 0.0, 1.0, 0.0, 1.0 }, + -- Color for point with values below the given range. If not set, or if + -- UseAboveRangeColor is false, the color will be set to the first value + -- in the color map (as per default) + BelowRangeColor = { 0.0, 0.0, 1.0, 1.0 }, + + -- Some other parameters that can be set are the following (these are all the + -- default values): + + -- If true, completely hide all values outside the range + HideValuesOutsideRange = false, + -- Toggle whether the above range color should be used + UseAboveRangeColor = true, + -- Toggle whether the below range color should be used + UseBelowRangeColor = true + } + }, + SizeSettings = { + -- Reduce the size of the point a little bit so that they don't overlap so much + ScaleFactor = 0.5 + } + }, + GUI = { + Name = "Advanced Color Mapping Settings", + Path = "/Example/Point Clouds", + Description = [[Example of a point cloud where the range is set explicitly and + specific colors are used for values outside the range, as well as for missing + data values.]] + } +} + + +asset.onInitialize(function() + openspace.addSceneGraphNode(Example) +end) + +asset.onDeinitialize(function() + openspace.removeSceneGraphNode(Example) +end) + +asset.export(Example) + + + +asset.meta = { + Name = "Example - Advanced Color Mapping Settings", + Version = "1.0", + Description = [[Example of a point cloud where the range is set explicitly and + specific colors are used for values outside the range, as well as for missing + data values]], + Author = "OpenSpace Team", + URL = "http://openspaceproject.com", + License = "MIT license" +} diff --git a/data/assets/examples/pointclouds/points_datamapping.asset b/data/assets/examples/pointclouds/points_datamapping.asset new file mode 100644 index 0000000000..791e945257 --- /dev/null +++ b/data/assets/examples/pointclouds/points_datamapping.asset @@ -0,0 +1,68 @@ +local earthAsset = asset.require("scene/solarsystem/planets/earth/earth") + + + +local Example = { + Identifier = "ExamplePoints_DataMapping", + Parent = earthAsset.Earth.Identifier, + Renderable = { + Type = "RenderablePointCloud", + File = asset.resource("data/dummydata.csv"), + DataMapping = { + -- Using the DataMapping, we can specify the X, Y and Z values of the point + -- positions to be set by any value in the dataset, without changing the dataset + -- used for the rendering + X = "a", + Y = "b", + Z = "a", + -- It is also possible to specify a numeric value that corresponds to missing + -- values in the dataset. These will be interpreted as NaN values + MissingDataValue = 29, + -- And some column that we do not want to include in the loading. Here we can for + -- example skip the regular position columns + ExcludeColumns = { "x", "y", "z" } + }, + -- Interpret values as Parsec rather than meter. The values in the a and b columns + -- are much smaller than the x, y and z + Unit = "pc", + -- To show the values corresponding to missing values, use a color map and show + -- missing data values in a specific color + Coloring = { + ColorMapping = { + File = asset.resource("viridis.cmap"), + ShowMissingData = true, + NoDataColor = { 1.0, 0.0, 0.0, 1.0 } + } + }, + }, + GUI = { + Name = "Data Mapping", + Path = "/Example/Point Clouds", + Description = [[Example of a point cloud where the X, Y and Z position are mapped to + other columns in the dataset.]] + } +} + + +asset.onInitialize(function() + openspace.addSceneGraphNode(Example) +end) + +asset.onDeinitialize(function() + openspace.removeSceneGraphNode(Example) +end) + +asset.export(Example) + + + +asset.meta = { + Name = "Example - Point Cloud with Custom Data Mapping", + Version = "1.0", + Description = [[Example of a point cloud where the X, Y and Z position are mapped to + other columns in the dataset. The data mapping also includes some settings for + missing data values and columns to exclude when loading the dataset]], + Author = "OpenSpace Team", + URL = "http://openspaceproject.com", + License = "MIT license" +} diff --git a/data/assets/examples/pointclouds/points_fading.asset b/data/assets/examples/pointclouds/points_fading.asset new file mode 100644 index 0000000000..2b9b5ea604 --- /dev/null +++ b/data/assets/examples/pointclouds/points_fading.asset @@ -0,0 +1,88 @@ +local earthAsset = asset.require("scene/solarsystem/planets/earth/earth") + + + +local EarthRadius = 6371000 + +local Example = { + Identifier = "ExamplePoints_Fading", + Parent = earthAsset.Earth.Identifier, + Renderable = { + Type = "RenderablePointCloud", + File = asset.resource("data/dummydata.csv"), + Coloring = { + FixedColor = { 0.0, 0.3, 1.0 } + }, + Fading = { + -- Control at what distance the points fade in. The points will be invisible + -- when the camera is closer than the first value, and fully visible when the + -- camera is further away then the last value. In-between they will linearly + -- fade in or out + FadeInDistances = { 15.0 * EarthRadius, 35.0 * EarthRadius } + } + }, + GUI = { + Name = "PointCloud - Fading", + Path = "/Example/Point Clouds", + Description = [[Example of a point cloud with distance-based fading (the points + are visible when the camera reaches a certain distance away from the origin)]] + } +} + +local Example_Invert = { + Identifier = "ExamplePoints_FadingInverted", + Parent = earthAsset.Earth.Identifier, + -- Rotate so that the points don't overlap with the previous ones + Transform = { + Rotation = { + Type = "StaticRotation", + Rotation = { 0, math.pi, 0 } + } + }, + Renderable = { + Type = "RenderablePointCloud", + File = asset.resource("data/dummydata.csv"), + Coloring = { + FixedColor = { 1.0, 0.3, 0.0 } + }, + Fading = { + -- Use the same fade distances, but invert the fading so that the points are + -- visible when the camera is closer to the origin that the first value, and + -- invisible when further away than the last value + FadeInDistances = { 15.0 * EarthRadius, 35.0 * EarthRadius }, + Invert = true + } + }, + GUI = { + Name = "PointCloud - Fading (Inverted)", + Path = "/Example/Point Clouds", + Description = [[Example of a point cloud with inverted distance-based fading + (the points are visible when the camera is close to the origin, and invisible + when further away)]] + } +} + + +asset.onInitialize(function() + openspace.addSceneGraphNode(Example) + openspace.addSceneGraphNode(Example_Invert) +end) + +asset.onDeinitialize(function() + openspace.removeSceneGraphNode(Example_Invert) + openspace.removeSceneGraphNode(Example) +end) + +asset.export(Example) +asset.export(Example_Invert) + + + +asset.meta = { + Name = "Example - Point Cloud with Fading", + Version = "1.0", + Description = [[Example of a point cloud with distance-based fading]], + Author = "OpenSpace Team", + URL = "http://openspaceproject.com", + License = "MIT license" +} diff --git a/data/assets/examples/pointclouds/points_units_labels.asset b/data/assets/examples/pointclouds/points_units_labels.asset new file mode 100644 index 0000000000..a1d02c3247 --- /dev/null +++ b/data/assets/examples/pointclouds/points_units_labels.asset @@ -0,0 +1,55 @@ +local earthAsset = asset.require("scene/solarsystem/planets/earth/earth") + + + +local Example = { + Identifier = "ExamplePoints_UnitsAndLabels", + Parent = earthAsset.Earth.Identifier, + Renderable = { + Type = "RenderablePointCloud", + File = asset.resource("data/dummydata.csv"), + Coloring = { + FixedColor = { 0.0, 0.3, 1.0 } + }, + -- Add a unit to interpret the points to be in kilometers rather than meters + Unit = "Km", + -- Also load a label file with the same position information as the CSV file. + -- The unit should be the same as the renderable + Labels = { + Enabled = true, + File = asset.resource("data/dummydata.label"), + Size = 7.5, + Unit = "Km" + } + -- @TODO The labels are not correctly oriented towards the camera! + }, + GUI = { + Name = "Units & Labels", + Path = "/Example/Point Clouds", + Description = [[Example of a point cloud where a spcific unit is used when + interpreting the position values, and text labels are placed at the points]] + } +} + + +asset.onInitialize(function() + openspace.addSceneGraphNode(Example) +end) + +asset.onDeinitialize(function() + openspace.removeSceneGraphNode(Example) +end) + +asset.export(Example) + + + +asset.meta = { + Name = "Example - Point Cloud with Unit and Labels", + Version = "1.0", + Description = [[Example of a point cloud where a spcific unit is used when + interpreting the position values, and text labels are placed at the points]], + Author = "OpenSpace Team", + URL = "http://openspaceproject.com", + License = "MIT license" +} diff --git a/data/assets/examples/pointclouds/polygon_cloud.asset b/data/assets/examples/pointclouds/polygon_cloud.asset new file mode 100644 index 0000000000..6b2ba10677 --- /dev/null +++ b/data/assets/examples/pointclouds/polygon_cloud.asset @@ -0,0 +1,45 @@ +local Example = { + Identifier = "ExamplePoints_Polygon", + Renderable = { + Type = "RenderablePolygonCloud", + File = asset.resource("data/dummydata.csv"), + Coloring = { + FixedColor = { 0.8, 0.0, 0.0 } + }, + -- Specify the number of sides for the polygon. 3 = triangle, 4 = square, + -- 5 = pentagon, 6 = hexagon, and so on + PolygonSides = 6, + -- Scale up the dataset, so that it is interpreted in Kilometers instead of meters, + -- for increased visiblity + Unit = "Km" + }, + GUI = { + Name = "Polygon Cloud", + Path = "/Example/Point Clouds", + Description = [[Example of a polygon cloud, which is a point cloud where a uniform + polygon is used for the shape of the points]] + } +} + + +asset.onInitialize(function() + openspace.addSceneGraphNode(Example) +end) + +asset.onDeinitialize(function() + openspace.removeSceneGraphNode(Example) +end) + +asset.export(Example) + + + +asset.meta = { + Name = "Example - Polygon Cloud", + Version = "1.0", + Description = [[Example of a polygon cloud, which is a point cloud where a uniform + polygon is used for the shape of the points]], + Author = "OpenSpace Team", + URL = "http://openspaceproject.com", + License = "MIT license" +} diff --git a/data/assets/examples/pointclouds/viridis.cmap b/data/assets/examples/pointclouds/viridis.cmap new file mode 100644 index 0000000000..2c00fba35d --- /dev/null +++ b/data/assets/examples/pointclouds/viridis.cmap @@ -0,0 +1,258 @@ +# Viridis Color map +256 +0.267004 0.004874 0.329415 1.000000 +0.268510 0.009605 0.335427 1.000000 +0.269944 0.014625 0.341379 1.000000 +0.271305 0.019942 0.347269 1.000000 +0.272594 0.025563 0.353093 1.000000 +0.273809 0.031497 0.358853 1.000000 +0.274952 0.037752 0.364543 1.000000 +0.276022 0.044167 0.370164 1.000000 +0.277018 0.050344 0.375715 1.000000 +0.277941 0.056324 0.381191 1.000000 +0.278791 0.062145 0.386592 1.000000 +0.279566 0.067836 0.391917 1.000000 +0.280267 0.073417 0.397163 1.000000 +0.280894 0.078907 0.402329 1.000000 +0.281446 0.084320 0.407414 1.000000 +0.281924 0.089666 0.412415 1.000000 +0.282327 0.094955 0.417331 1.000000 +0.282656 0.100196 0.422160 1.000000 +0.282910 0.105393 0.426902 1.000000 +0.283091 0.110553 0.431554 1.000000 +0.283197 0.115680 0.436115 1.000000 +0.283229 0.120777 0.440584 1.000000 +0.283187 0.125848 0.444960 1.000000 +0.283072 0.130895 0.449241 1.000000 +0.282884 0.135920 0.453427 1.000000 +0.282623 0.140926 0.457517 1.000000 +0.282290 0.145912 0.461510 1.000000 +0.281887 0.150881 0.465405 1.000000 +0.281412 0.155834 0.469201 1.000000 +0.280868 0.160771 0.472899 1.000000 +0.280255 0.165693 0.476498 1.000000 +0.279574 0.170599 0.479997 1.000000 +0.278826 0.175490 0.483397 1.000000 +0.278012 0.180367 0.486697 1.000000 +0.277134 0.185228 0.489898 1.000000 +0.276194 0.190074 0.493001 1.000000 +0.275191 0.194905 0.496005 1.000000 +0.274128 0.199721 0.498911 1.000000 +0.273006 0.204520 0.501721 1.000000 +0.271828 0.209303 0.504434 1.000000 +0.270595 0.214069 0.507052 1.000000 +0.269308 0.218818 0.509577 1.000000 +0.267968 0.223549 0.512008 1.000000 +0.266580 0.228262 0.514349 1.000000 +0.265145 0.232956 0.516599 1.000000 +0.263663 0.237631 0.518762 1.000000 +0.262138 0.242286 0.520837 1.000000 +0.260571 0.246922 0.522828 1.000000 +0.258965 0.251537 0.524736 1.000000 +0.257322 0.256130 0.526563 1.000000 +0.255645 0.260703 0.528312 1.000000 +0.253935 0.265254 0.529983 1.000000 +0.252194 0.269783 0.531579 1.000000 +0.250425 0.274290 0.533103 1.000000 +0.248629 0.278775 0.534556 1.000000 +0.246811 0.283237 0.535941 1.000000 +0.244972 0.287675 0.537260 1.000000 +0.243113 0.292092 0.538516 1.000000 +0.241237 0.296485 0.539709 1.000000 +0.239346 0.300855 0.540844 1.000000 +0.237441 0.305202 0.541921 1.000000 +0.235526 0.309527 0.542944 1.000000 +0.233603 0.313828 0.543914 1.000000 +0.231674 0.318106 0.544834 1.000000 +0.229739 0.322361 0.545706 1.000000 +0.227802 0.326594 0.546532 1.000000 +0.225863 0.330805 0.547314 1.000000 +0.223925 0.334994 0.548053 1.000000 +0.221989 0.339161 0.548752 1.000000 +0.220057 0.343307 0.549413 1.000000 +0.218130 0.347432 0.550038 1.000000 +0.216210 0.351535 0.550627 1.000000 +0.214298 0.355619 0.551184 1.000000 +0.212395 0.359683 0.551710 1.000000 +0.210503 0.363727 0.552206 1.000000 +0.208623 0.367752 0.552675 1.000000 +0.206756 0.371758 0.553117 1.000000 +0.204903 0.375746 0.553533 1.000000 +0.203063 0.379716 0.553925 1.000000 +0.201239 0.383670 0.554294 1.000000 +0.199430 0.387607 0.554642 1.000000 +0.197636 0.391528 0.554969 1.000000 +0.195860 0.395433 0.555276 1.000000 +0.194100 0.399323 0.555565 1.000000 +0.192357 0.403199 0.555836 1.000000 +0.190631 0.407061 0.556089 1.000000 +0.188923 0.410910 0.556326 1.000000 +0.187231 0.414746 0.556547 1.000000 +0.185556 0.418570 0.556753 1.000000 +0.183898 0.422383 0.556944 1.000000 +0.182256 0.426184 0.557120 1.000000 +0.180629 0.429975 0.557282 1.000000 +0.179019 0.433756 0.557430 1.000000 +0.177423 0.437527 0.557565 1.000000 +0.175841 0.441290 0.557685 1.000000 +0.174274 0.445044 0.557792 1.000000 +0.172719 0.448791 0.557885 1.000000 +0.171176 0.452530 0.557965 1.000000 +0.169646 0.456262 0.558030 1.000000 +0.168126 0.459988 0.558082 1.000000 +0.166617 0.463708 0.558119 1.000000 +0.165117 0.467423 0.558141 1.000000 +0.163625 0.471133 0.558148 1.000000 +0.162142 0.474838 0.558140 1.000000 +0.160665 0.478540 0.558115 1.000000 +0.159194 0.482237 0.558073 1.000000 +0.157729 0.485932 0.558013 1.000000 +0.156270 0.489624 0.557936 1.000000 +0.154815 0.493313 0.557840 1.000000 +0.153364 0.497000 0.557724 1.000000 +0.151918 0.500685 0.557587 1.000000 +0.150476 0.504369 0.557430 1.000000 +0.149039 0.508051 0.557250 1.000000 +0.147607 0.511733 0.557049 1.000000 +0.146180 0.515413 0.556823 1.000000 +0.144759 0.519093 0.556572 1.000000 +0.143343 0.522773 0.556295 1.000000 +0.141935 0.526453 0.555991 1.000000 +0.140536 0.530132 0.555659 1.000000 +0.139147 0.533812 0.555298 1.000000 +0.137770 0.537492 0.554906 1.000000 +0.136408 0.541173 0.554483 1.000000 +0.135066 0.544853 0.554029 1.000000 +0.133743 0.548535 0.553541 1.000000 +0.132444 0.552216 0.553018 1.000000 +0.131172 0.555899 0.552459 1.000000 +0.129933 0.559582 0.551864 1.000000 +0.128729 0.563265 0.551229 1.000000 +0.127568 0.566949 0.550556 1.000000 +0.126453 0.570633 0.549841 1.000000 +0.125394 0.574318 0.549086 1.000000 +0.124395 0.578002 0.548287 1.000000 +0.123463 0.581687 0.547445 1.000000 +0.122606 0.585371 0.546557 1.000000 +0.121831 0.589055 0.545623 1.000000 +0.121148 0.592739 0.544641 1.000000 +0.120565 0.596422 0.543611 1.000000 +0.120092 0.600104 0.542530 1.000000 +0.119738 0.603785 0.541400 1.000000 +0.119512 0.607464 0.540218 1.000000 +0.119423 0.611141 0.538982 1.000000 +0.119483 0.614817 0.537692 1.000000 +0.119699 0.618490 0.536347 1.000000 +0.120081 0.622161 0.534946 1.000000 +0.120638 0.625828 0.533488 1.000000 +0.121380 0.629492 0.531973 1.000000 +0.122312 0.633153 0.530398 1.000000 +0.123444 0.636809 0.528763 1.000000 +0.124780 0.640461 0.527068 1.000000 +0.126326 0.644107 0.525311 1.000000 +0.128087 0.647749 0.523491 1.000000 +0.130067 0.651384 0.521608 1.000000 +0.132268 0.655014 0.519661 1.000000 +0.134692 0.658636 0.517649 1.000000 +0.137339 0.662252 0.515571 1.000000 +0.140210 0.665859 0.513427 1.000000 +0.143303 0.669459 0.511215 1.000000 +0.146616 0.673050 0.508936 1.000000 +0.150148 0.676631 0.506589 1.000000 +0.153894 0.680203 0.504172 1.000000 +0.157851 0.683765 0.501686 1.000000 +0.162016 0.687316 0.499129 1.000000 +0.166383 0.690856 0.496502 1.000000 +0.170948 0.694384 0.493803 1.000000 +0.175707 0.697900 0.491033 1.000000 +0.180653 0.701402 0.488189 1.000000 +0.185783 0.704891 0.485273 1.000000 +0.191090 0.708366 0.482284 1.000000 +0.196571 0.711827 0.479221 1.000000 +0.202219 0.715272 0.476084 1.000000 +0.208030 0.718701 0.472873 1.000000 +0.214000 0.722114 0.469588 1.000000 +0.220124 0.725509 0.466226 1.000000 +0.226397 0.728888 0.462789 1.000000 +0.232815 0.732247 0.459277 1.000000 +0.239374 0.735588 0.455688 1.000000 +0.246070 0.738910 0.452024 1.000000 +0.252899 0.742211 0.448284 1.000000 +0.259857 0.745492 0.444467 1.000000 +0.266941 0.748751 0.440573 1.000000 +0.274149 0.751988 0.436601 1.000000 +0.281477 0.755203 0.432552 1.000000 +0.288921 0.758394 0.428426 1.000000 +0.296479 0.761561 0.424223 1.000000 +0.304148 0.764704 0.419943 1.000000 +0.311925 0.767822 0.415586 1.000000 +0.319809 0.770914 0.411152 1.000000 +0.327796 0.773980 0.406640 1.000000 +0.335885 0.777018 0.402049 1.000000 +0.344074 0.780029 0.397381 1.000000 +0.352360 0.783011 0.392636 1.000000 +0.360741 0.785964 0.387814 1.000000 +0.369214 0.788888 0.382914 1.000000 +0.377779 0.791781 0.377939 1.000000 +0.386433 0.794644 0.372886 1.000000 +0.395174 0.797475 0.367757 1.000000 +0.404001 0.800275 0.362552 1.000000 +0.412913 0.803041 0.357269 1.000000 +0.421908 0.805774 0.351910 1.000000 +0.430983 0.808473 0.346476 1.000000 +0.440137 0.811138 0.340967 1.000000 +0.449368 0.813768 0.335384 1.000000 +0.458674 0.816363 0.329727 1.000000 +0.468053 0.818921 0.323998 1.000000 +0.477504 0.821444 0.318195 1.000000 +0.487026 0.823929 0.312321 1.000000 +0.496615 0.826376 0.306377 1.000000 +0.506271 0.828786 0.300362 1.000000 +0.515992 0.831158 0.294279 1.000000 +0.525776 0.833491 0.288127 1.000000 +0.535621 0.835785 0.281908 1.000000 +0.545524 0.838039 0.275626 1.000000 +0.555484 0.840254 0.269281 1.000000 +0.565498 0.842430 0.262877 1.000000 +0.575563 0.844566 0.256415 1.000000 +0.585678 0.846661 0.249897 1.000000 +0.595839 0.848717 0.243329 1.000000 +0.606045 0.850733 0.236712 1.000000 +0.616293 0.852709 0.230052 1.000000 +0.626579 0.854645 0.223353 1.000000 +0.636902 0.856542 0.216620 1.000000 +0.647257 0.858400 0.209861 1.000000 +0.657642 0.860219 0.203082 1.000000 +0.668054 0.861999 0.196293 1.000000 +0.678489 0.863742 0.189503 1.000000 +0.688944 0.865448 0.182725 1.000000 +0.699415 0.867117 0.175971 1.000000 +0.709898 0.868751 0.169257 1.000000 +0.720391 0.870350 0.162603 1.000000 +0.730889 0.871916 0.156029 1.000000 +0.741388 0.873449 0.149561 1.000000 +0.751884 0.874951 0.143228 1.000000 +0.762373 0.876424 0.137064 1.000000 +0.772852 0.877868 0.131109 1.000000 +0.783315 0.879285 0.125405 1.000000 +0.793760 0.880678 0.120005 1.000000 +0.804182 0.882046 0.114965 1.000000 +0.814576 0.883393 0.110347 1.000000 +0.824940 0.884720 0.106217 1.000000 +0.835270 0.886029 0.102646 1.000000 +0.845561 0.887322 0.099702 1.000000 +0.855810 0.888601 0.097452 1.000000 +0.866013 0.889868 0.095953 1.000000 +0.876168 0.891125 0.095250 1.000000 +0.886271 0.892374 0.095374 1.000000 +0.896320 0.893616 0.096335 1.000000 +0.906311 0.894855 0.098125 1.000000 +0.916242 0.896091 0.100717 1.000000 +0.926106 0.897330 0.104071 1.000000 +0.935904 0.898570 0.108131 1.000000 +0.945636 0.899815 0.112838 1.000000 +0.955300 0.901065 0.118128 1.000000 +0.964894 0.902323 0.123941 1.000000 +0.974417 0.903590 0.130215 1.000000 +0.983868 0.904867 0.136897 1.000000 +0.993248 0.906157 0.143936 1.000000 diff --git a/data/assets/scene/digitaluniverse/2dF.asset b/data/assets/scene/digitaluniverse/2dF.asset index fca4597e97..a86bea1544 100644 --- a/data/assets/scene/digitaluniverse/2dF.asset +++ b/data/assets/scene/digitaluniverse/2dF.asset @@ -9,26 +9,33 @@ local speck = asset.resource({ Name = "2dF Speck Files", Type = "HttpSynchronization", Identifier = "digitaluniverse_2dF_speck", - Version = 2 + Version = 3 }) local Object = { Identifier = "2dF", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, - Color = { 1.0, 1.0, 1.0 }, Opacity = 1.0, File = speck .. "2dF.speck", Unit = "Mpc", Texture = textures .. "point3A.png", - ColorMap = speck .. "2dF.cmap", - ColorOption = { "redshift", "proximity" }, - ColorRange = { { 0.0, 0.075 }, { 1.0, 25.0 } }, - ScaleFactor = 520.0, - BillboardMinMaxSize = { 0.0, 4.7 }, - EnablePixelSizeControl = true + Coloring = { + ColorMapping = { + File = speck .. "2dF.cmap", + ParameterOptions = { + { Key = "proximity", Range = { 1.0, 25.0 } }, + { Key = "redshift", Range = { 0.0, 0.075 } } + } + } + }, + SizeSettings = { + ScaleExponent = 22.6, + MaxPixelSize = 4.7, + EnablePixelSizeControl = true + } }, GUI = { Name = "2dF Galaxies", @@ -64,7 +71,7 @@ asset.export(Object) asset.meta = { Name = "2dF Galaxies", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for the The Two-degree Field (2dF) Survey", Author = "Brian Abbott (AMNH), Eric Gawiser (Rutgers U)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/2mass.asset b/data/assets/scene/digitaluniverse/2mass.asset index da92e173fc..8ed791380b 100644 --- a/data/assets/scene/digitaluniverse/2mass.asset +++ b/data/assets/scene/digitaluniverse/2mass.asset @@ -9,28 +9,34 @@ local speck = asset.resource({ Name = "2MASS Speck Files", Type = "HttpSynchronization", Identifier = "digitaluniverse_2mass_speck", - Version = 1 + Version = 2 }) local Object = { Identifier = "2MASS", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, - Color = { 1.0, 0.4, 0.2 }, Opacity = 1.0, File = speck .. "2MASS.speck", Unit = "Mpc", Texture = textures .. "point3A.png", - ColorMap = speck .. "lss.cmap", - ColorOption = { "redshift", "prox5Mpc" }, - ColorRange = { { 0.0, 0.075 }, { 1.0, 50.0 } }, - CorrectionSizeEndDistance = 20.6, - CorrectionSizeFactor = 15.0, - ScaleFactor = 510.78, - BillboardMinMaxSize = { 0.0, 11.15 }, - EnablePixelSizeControl = true + Coloring = { + FixedColor = { 1.0, 0.4, 0.2 }, + ColorMapping = { + File = speck .. "lss.cmap", + ParameterOptions = { + { Key = "prox5Mpc", Range = { 1.0, 50.0 } }, + { Key = "redshift", Range = { 0.0, 0.075 } } + } + } + }, + SizeSettings = { + ScaleExponent = 22.5, + MaxPixelSize = 11.15, + EnablePixelSizeControl = true + } }, GUI = { Name = "2MASS Galaxies", @@ -60,7 +66,7 @@ asset.export(Object) asset.meta = { Name = "2MASS Galaxies", - Version = "1.1", + Version = "2.0", Description = "Digital Universe asset for the Two Micron All-Sky Survey (2MASS) survey", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/6dF.asset b/data/assets/scene/digitaluniverse/6dF.asset index 34bdd92b3b..780aefc678 100644 --- a/data/assets/scene/digitaluniverse/6dF.asset +++ b/data/assets/scene/digitaluniverse/6dF.asset @@ -9,26 +9,34 @@ local speck = asset.resource({ Name = "6dF Speck Files", Type = "HttpSynchronization", Identifier = "digitaluniverse_6dF_speck", - Version = 2 + Version = 3 }) local Object = { Identifier = "6dF", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, - Color = { 1.0, 1.0, 0.0 }, Opacity = 1.0, File = speck .. "6dF.speck", Unit = "Mpc", Texture = textures .. "point3A.png", - ColorMap = speck .. "6dF.cmap", - ColorOption = { "redshift", "proximity" }, - ColorRange = { { 0.0, 0.075 }, { 1.0, 10.0 } }, - ScaleFactor = 534.0, - BillboardMinMaxSize = { 0.0, 9.0 }, - EnablePixelSizeControl = true + Coloring = { + FixedColor = { 1.0, 1.0, 0.0 }, + ColorMapping = { + File = speck .. "6dF.cmap", + ParameterOptions = { + { Key = "proximity", Range = { 1.0, 10.0 } }, + { Key = "redshift", Range = { 0.0, 0.075 } } + } + } + }, + SizeSettings = { + ScaleExponent = 23.2, + MaxPixelSize = 9.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "6dF Galaxies", @@ -62,7 +70,7 @@ asset.export(Object) asset.meta = { Name = "6dF Galaxies", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for The Six-degree Field (6dF) Galaxy Survey", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/abell.asset b/data/assets/scene/digitaluniverse/abell.asset index b7c5de498d..2e3f6f0d02 100644 --- a/data/assets/scene/digitaluniverse/abell.asset +++ b/data/assets/scene/digitaluniverse/abell.asset @@ -9,7 +9,7 @@ local speck = asset.resource({ Name = "Abell Speck Files", Type = "HttpSynchronization", Identifier = "digitaluniverse_abell_speck", - Version = 2 + Version = 3 }) @@ -23,7 +23,7 @@ local TransformMatrix = { local Object = { Identifier = "Abell", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { File = speck .. "abell.label", @@ -34,16 +34,20 @@ local Object = { Unit = "Mpc", TransformationMatrix = TransformMatrix }, - Color = { 1.0, 0.4, 0.2 }, Opacity = 1.0, - --ColorMap = speck .. "abell.cmap", + Coloring = { + FixedColor = { 1.0, 0.4, 0.2 }, + --ColorMap = speck .. "abell.cmap", -- TODO: Decide whether to add + }, File = speck .. "abell.speck", Texture = textures .. "point3A.png", Unit = "Mpc", TransformationMatrix = TransformMatrix, - ScaleFactor = 520.0, - BillboardMinMaxSize = { 0.0, 7.0 }, - EnablePixelSizeControl = true + SizeSettings = { + ScaleExponent = 22.6, + MaxPixelSize = 7.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "Abell Galaxy Clusters", @@ -78,7 +82,7 @@ asset.export(Object) asset.meta = { Name = "Abell Galaxy Clusters", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for The Abell catalog", Author = "Stuart Levy (NCSA/UIUC), Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/alternatestarlabels.asset b/data/assets/scene/digitaluniverse/alternatestarlabels.asset index 070e50eb7d..6ae6004962 100644 --- a/data/assets/scene/digitaluniverse/alternatestarlabels.asset +++ b/data/assets/scene/digitaluniverse/alternatestarlabels.asset @@ -9,7 +9,7 @@ local speck = asset.resource({ local Object = { Identifier = "StarLabelsAlternate", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { Enabled = true, @@ -19,7 +19,6 @@ local Object = { MinMaxSize = { 6, 20 }, Unit = "pc" }, - Color = { 1.0, 1.0, 1.0 }, Opacity = 0.65, Unit = "pc" }, @@ -51,7 +50,7 @@ asset.export(Object) asset.meta = { Name = "Stars Labels - Alternate", - Version = "1.1", + Version = "2.0", Description = "Digital Universe asset for alternate start labels", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/clusters.asset b/data/assets/scene/digitaluniverse/clusters.asset index f266d6afb9..96ff69dad6 100644 --- a/data/assets/scene/digitaluniverse/clusters.asset +++ b/data/assets/scene/digitaluniverse/clusters.asset @@ -16,7 +16,7 @@ local TransformMatrix = { local Object = { Identifier = "GalaxyClusterLabels", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { Enabled = true, @@ -27,7 +27,6 @@ local Object = { Unit = "Mpc", TransformationMatrix = TransformMatrix }, - Color = { 1.0, 1.0, 1.0 }, Opacity = 0.65, Unit = "Mpc", TransformationMatrix = TransformMatrix @@ -58,7 +57,7 @@ asset.export(Object) asset.meta = { Name = "Galaxy Clusters Labels", - Version = "1.1", + Version = "2.0", Description = "Digital Universe asset for Galaxy Clusters", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/deepsky.asset b/data/assets/scene/digitaluniverse/deepsky.asset index 371fd66ea1..6be73ad6e4 100644 --- a/data/assets/scene/digitaluniverse/deepsky.asset +++ b/data/assets/scene/digitaluniverse/deepsky.asset @@ -16,7 +16,7 @@ local speck = asset.resource({ local DeepSkyObjects = { Identifier = "DeepSkyObjects", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { File = speck .. "dso.label", @@ -25,22 +25,19 @@ local DeepSkyObjects = { MinMaxSize = { 16, 20 }, Unit = "pc" }, - Color = { 1.0, 1.0, 0.0 }, Opacity = 0.99, - ScaleFactor = 500.0, + Coloring = { + FixedColor = { 1.0, 1.0, 0.0 } + }, File = speck .. "dso.speck", Texture = textures .. "point3.png", - --ColorMap = speck .. "tully.cmap", - --ColorMap = speck .. "lss.cmap", - --ColorOption = { "proximity" }, - --ColorOption = { "prox5Mpc" }, - --ColorRange = { { 1.0, 30.0 } }, Unit = "pc", --FadeInDistances = { 0.05, 1.0 }, -- Fade in value in the same unit as "Unit" - BillboardMinMaxSize = { 0.0, 8.22 }, -- in pixels - --CorrectionSizeEndDistance = 22.0, - --CorrectionSizeFactor = 10.45 - EnablePixelSizeControl = true + SizeSettings = { + ScaleExponent = 21.7, + MaxPixelSize = 8.22, + EnablePixelSizeControl = true + } }, Transform = { Rotation = { @@ -112,7 +109,7 @@ asset.export(DeepSkyObjectsImages) asset.meta = { Name = "Deep Sky Objects Images", - Version = "1.1", + Version = "2.0", Description = "Digital Universe asset for Deep Sky Objects and their Images", Author = "Nate Greenstein, Matt Everhart, Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/dwarfs.asset b/data/assets/scene/digitaluniverse/dwarfs.asset index fe879a8ec6..312112931d 100644 --- a/data/assets/scene/digitaluniverse/dwarfs.asset +++ b/data/assets/scene/digitaluniverse/dwarfs.asset @@ -9,14 +9,14 @@ local speck = asset.resource({ Name = "Brown Dwarf Speck Files", Type = "HttpSynchronization", Identifier = "digitaluniverse_dwarfs_speck", - Version = 2 + Version = 3 }) local Object = { Identifier = "Dwarfs", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { File = speck .. "dwarfs.label", @@ -25,19 +25,24 @@ local Object = { MinMaxSize = { 10, 20 }, Unit = "pc" }, - Color = { 0.4, 0.0, 0.1 }, Opacity = 1.0, File = speck .. "dwarfs.speck", Texture = textures .. "point3.png", Unit = "pc", - ColorMap = speck .. "dwarfs.cmap", - ColorOption = { "typeindex" }, - --ColorRange = { { 1.0, 4.0} }, - ScaleFactor = 372.1, - --CorrectionSizeEndDistance = 16.1, - --CorrectionSizeFactor = 7.75, - BillboardMinMaxSize = { 0.0, 20.0 }, - EnablePixelSizeControl = true + Coloring = { + FixedColor = { 0.4, 0.0, 0.1 }, + ColorMapping = { + File = speck .. "dwarfs.cmap", + ParameterOptions = { + { Key = "typeindex", Range = { 1.0, 4.0 } } + } + } + }, + SizeSettings = { + ScaleExponent = 16.2, + MaxPixelSize = 20.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "Brown Dwarfs", @@ -77,7 +82,7 @@ asset.export(Object) asset.meta = { Name = "Brown Dwarfs", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for Brown Dwarfs", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/exoplanets.asset b/data/assets/scene/digitaluniverse/exoplanets.asset index c20351cb17..f7abff68f8 100644 --- a/data/assets/scene/digitaluniverse/exoplanets.asset +++ b/data/assets/scene/digitaluniverse/exoplanets.asset @@ -16,7 +16,7 @@ local speck = asset.resource({ local Object = { Identifier = "Exoplanets", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { File = speck .. "expl.label", @@ -25,16 +25,15 @@ local Object = { MinMaxSize = { 10, 100 }, Unit = "pc" }, - Color = { 1.0, 1.0, 1.0 }, Opacity = 1.0, Texture = textures .. "target-blue.png", File = speck .. "expl.speck", Unit = "pc", - ScaleFactor = 388.67923, - CorrectionSizeEndDistance = 15.23, - CorrectionSizeFactor = 13.3, - BillboardMinMaxSize = { 0.0, 75.0 }, - EnablePixelSizeControl = true + SizeSettings = { + ScaleExponent = 16.9, + MaxPixelSize = 75.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "Exoplanets", @@ -64,7 +63,7 @@ asset.export(Object) asset.meta = { Name = "Exoplanets", - Version = "1.1", + Version = "2.0", Description = "Digital Universe asset for Exoplanets", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/exoplanets_candidates.asset b/data/assets/scene/digitaluniverse/exoplanets_candidates.asset index 5fa0436b7e..d33e413484 100644 --- a/data/assets/scene/digitaluniverse/exoplanets_candidates.asset +++ b/data/assets/scene/digitaluniverse/exoplanets_candidates.asset @@ -16,18 +16,20 @@ local speck = asset.resource({ local Object = { Identifier = "PlanetaryCandidates", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, - Color = { 1.0, 1.0, 0.0 }, Opacity = 0.99, - ScaleFactor = 410.0, File = speck .. "exoplanet_candidates.speck", Unit = "pc", Texture = textures .. "halo.png", - CorrectionSizeEndDistance = 15.86, - CorrectionSizeFactor = 8.59, - BillboardMinMaxSize = { 0.0, 30.0 }, - EnablePixelSizeControl = true + Coloring = { + FixedColor = { 1.0, 1.0, 0.0 } + }, + SizeSettings = { + ScaleExponent = 17.8, + MaxPixelSize = 30.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "Planetary Candidates", @@ -60,7 +62,7 @@ asset.export(Object) asset.meta = { Name = "Planetary Candidates", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for Planetary Candidates", Author = "Brian Abbott, Emily Rice, and Jason No (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/globularclusters.asset b/data/assets/scene/digitaluniverse/globularclusters.asset index 4e7095f8e4..ec6232b7af 100644 --- a/data/assets/scene/digitaluniverse/globularclusters.asset +++ b/data/assets/scene/digitaluniverse/globularclusters.asset @@ -16,7 +16,7 @@ local speck = asset.resource({ local Object = { Identifier = "GlobularClusters", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePolygonCloud", Enabled = false, Labels = { File = speck .. "gc.label", @@ -25,15 +25,18 @@ local Object = { MinMaxSize = { 4, 20 }, Unit = "pc" }, - Color = { 0.8, 0.8, 0.0 }, + Coloring = { + FixedColor = { 0.8, 0.8, 0.0 } + }, Opacity = 0.4, File = speck .. "gc.speck", - Texture = textures .. "point4.png", PolygonSides = 5, Unit = "pc", - ScaleFactor = 431.0, - BillboardMinMaxSize = { 0.0, 500.0 }, - EnablePixelSizeControl = true + SizeSettings = { + ScaleExponent = 18.7, + MaxPixelSize = 500.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "Globular Clusters", @@ -69,7 +72,7 @@ asset.export(Object) asset.meta = { Name = "Globular Clusters", - Version = "2.1", + Version = "3.0", Description = [[Census: 157 globular clusters. DU Version 2.6. Globular star clusters are gravitationally bound groups of 100,000 to 1 million stars. They are compact, spherical "balls" of stars with very high stellar densities in their centers (stars diff --git a/data/assets/scene/digitaluniverse/grids.asset b/data/assets/scene/digitaluniverse/grids.asset index 65989fab4d..413694058f 100644 --- a/data/assets/scene/digitaluniverse/grids.asset +++ b/data/assets/scene/digitaluniverse/grids.asset @@ -126,7 +126,7 @@ local EclipticSphereLabels = { Identifier = "EclipticSphereLabels", Parent = transforms.SolarSystemBarycenter.Name, Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { Enabled = true, @@ -137,7 +137,6 @@ local EclipticSphereLabels = { Unit = "pc", TransformationMatrix = EclipticTransformationMatrix }, - Color = { 1.0, 1.0, 1.0 }, Opacity = 0.65, Unit = "pc", TransformationMatrix = EclipticTransformationMatrix @@ -178,7 +177,7 @@ local EquatorialSphereLabels = { Identifier = "EquatorialSphereLabels", Parent = transforms.SolarSystemBarycenter.Name, Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { Enabled = true, @@ -189,7 +188,6 @@ local EquatorialSphereLabels = { Unit = "pc", TransformationMatrix = EquatorialTransformationMatrix }, - Color = { 1.0, 1.0, 1.0 }, Opacity = 0.65, Unit = "pc", TransformationMatrix = EquatorialTransformationMatrix @@ -226,7 +224,7 @@ local GalacticSphereLabels = { Identifier = "GalacticSphereLabels", Parent = transforms.SolarSystemBarycenter.Name, Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { Enabled = true, @@ -236,7 +234,6 @@ local GalacticSphereLabels = { MinMaxSize = { 1, 100 }, Unit = "pc" }, - Color = { 1.0, 1.0, 1.0 }, Opacity = 0.65, Unit = "pc" }, @@ -699,7 +696,7 @@ asset.export(Plane20Gly) asset.meta = { Name = "Grids", - Version = "2.1", + Version = "3.0", Description = [[Various grids for showing size reference. Included: 10,000 light year grid, 10 light year grid, 20 billion light year grid, 10 million light year grid, 100 light year grid, 100 million light year grid, Ecliptic Coordinate Sphere diff --git a/data/assets/scene/digitaluniverse/groups.asset b/data/assets/scene/digitaluniverse/groups.asset index 03cb117c68..a93d3608f4 100644 --- a/data/assets/scene/digitaluniverse/groups.asset +++ b/data/assets/scene/digitaluniverse/groups.asset @@ -16,7 +16,7 @@ local TransformMatrix = { local Object = { Identifier = "NearbyGalaxyGroups", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { Enabled = true, @@ -27,9 +27,7 @@ local Object = { Unit = "Mpc", TransformationMatrix = TransformMatrix }, - Color = { 1.0, 1.0, 1.0 }, Opacity = 0.65, - --ScaleFactor = 10.0, Unit = "Mpc", TransformationMatrix = TransformMatrix }, @@ -58,7 +56,7 @@ asset.export(Object) asset.meta = { Name = "Galaxy Group Labels", - Version = "1.1", + Version = "2.0", Author = "Brian Abbott (AMNH)", Description = "Digital Universe asset for Galaxy Groups", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/h2regions.asset b/data/assets/scene/digitaluniverse/h2regions.asset index c5fb3baced..3868e22b08 100644 --- a/data/assets/scene/digitaluniverse/h2regions.asset +++ b/data/assets/scene/digitaluniverse/h2regions.asset @@ -16,7 +16,7 @@ local speck = asset.resource({ local Object = { Identifier = "HIIRegions", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePolygonCloud", Enabled = false, Labels = { File = speck .. "h2.label", @@ -25,15 +25,18 @@ local Object = { MinMaxSize = { 4, 20 }, Unit = "pc" }, - Color = { 0.0, 0.5, 1.0 }, + Coloring = { + FixedColor = { 0.0, 0.5, 1.0 } + }, Opacity = 0.70, File = speck .. "h2.speck", - Texture = textures .. "point4.png", PolygonSides = 6, Unit = "pc", - ScaleFactor = 420.0, - BillboardMinMaxSize = { 0.0, 300.0 }, - EnablePixelSizeControl = false + SizeSettings = { + ScaleExponent = 18.24, + MaxPixelSize = 300.0, + EnablePixelSizeControl = false + } }, GUI = { Name = "HII Regions", @@ -67,7 +70,7 @@ asset.export(Object) asset.meta = { Name = "HII Regions", - Version = "1.1", + Version = "2.0", Description = "Digital Universe asset for HII Regions", Author = "Carter Emmart, Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/hdf.asset b/data/assets/scene/digitaluniverse/hdf.asset index f34094b0c2..e44aeec0c0 100644 --- a/data/assets/scene/digitaluniverse/hdf.asset +++ b/data/assets/scene/digitaluniverse/hdf.asset @@ -23,19 +23,26 @@ local ColorMap = asset.resource({ local Object = { Identifier = "HubbleDeepField", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, - Color = { 1.0, 1.0, 1.0 }, Opacity = 1.0, File = HUDFSpeck .. "hudf.speck", Texture = circle .. "circle.png", - ColorMap = ColorMap .. "hudf.cmap", - ColorOption = { "redshift", "proximity" }, - ColorRange = { { 0.0, 0.075 }, { 1.0, 25.0 } }, + Coloring = { + ColorMapping = { + File = ColorMap .. "hudf.cmap", + ParameterOptions = { + { Key = "proximity", Range = { 1.0, 25.0 } }, + { Key = "redshift", Range = { 0.0, 0.075 } } + } + } + }, Unit = "Mpc", - ScaleFactor = 505.0, - BillboardMaxSize = 4.7, - EnablePixelSizeControl = true + SizeSettings = { + ScaleExponent = 21.9, + MaxPixelSize = 4.7, + EnablePixelSizeControl = true + } }, GUI = { Name = "Hubble Deep Field", @@ -62,7 +69,7 @@ asset.export(Object) asset.meta = { Name = "Hubble Ultra Deep Field", - Version = "1.2", + Version = "2.0", Description = "Hubble Ultra Deep Field galaxy survey", Author = "Frank Summers (STScI), Brian Abbott (AMNH)", URL = "http://www.haydenplanetarium.org/universe", diff --git a/data/assets/scene/digitaluniverse/localdwarfs.asset b/data/assets/scene/digitaluniverse/localdwarfs.asset index 7426659150..635b6e0edc 100644 --- a/data/assets/scene/digitaluniverse/localdwarfs.asset +++ b/data/assets/scene/digitaluniverse/localdwarfs.asset @@ -16,7 +16,7 @@ local speck = asset.resource({ local Object = { Identifier = "LocalDwarfGalaxies", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePolygonCloud", Enabled = false, Labels = { File = speck .. "localgroup.label", @@ -25,17 +25,29 @@ local Object = { MinMaxSize = { 7, 20 }, Unit = "Mpc" }, - Color = { 0.5, 1.0, 0.2 }, - ColorMap = speck .. "localgroup.cmap", - ColorOption = { "association" }, + Coloring = { + FixedColor = { 0.0, 1.0, 0.0 }, + -- @TODO: This one wasn't actually properly used before the point cloud update. + -- All points were mapped to green. Decide if we want it around. + -- @TODO: Also, the cmap is currently not applied correctly, due to speck file + -- not being read properly (it inlcudes more information than just datavar-s). + -- This should be adressed. (2023-12-13, emmbr) + ColorMapping = { + File = speck .. "localgroup.cmap", + ParameterOptions = { + { Key = "association" } + } + } + }, Opacity = 0.3, File = speck .. "localgroup.speck", - Texture = textures .. "point4.png", PolygonSides = 12, Unit = "Mpc", - ScaleFactor = 465.0, - BillboardMinMaxSize = { 0.0, 20.0 }, - EnablePixelSizeControl = true + SizeSettings = { + ScaleExponent = 20.2, + MaxPixelSize = 20.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "Local Group", @@ -69,7 +81,7 @@ asset.export(Object) asset.meta = { Name = "Local Group", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for the Local Goup", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/milkyway_label.asset b/data/assets/scene/digitaluniverse/milkyway_label.asset index c2098b14ff..19088fdd44 100644 --- a/data/assets/scene/digitaluniverse/milkyway_label.asset +++ b/data/assets/scene/digitaluniverse/milkyway_label.asset @@ -16,7 +16,7 @@ local TransformMatrix = { local HomeLabel = { Identifier = "HomeLabel", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { Enabled = true, @@ -26,15 +26,7 @@ local HomeLabel = { MinMaxSize = { 16, 20 }, Unit = "Mpc", TransformationMatrix = TransformMatrix - }, - Color = { 1.0, 0.4, 0.2 }, - Opacity = 0.99, - ScaleFactor = 500.0, - Unit = "Mpc", - TransformationMatrix = TransformMatrix, - FadeInDistances = { 0.05, 1.0 }, - BillboardMinMaxSize = { 0.0, 8.22 }, - EnablePixelSizeControl = true + } }, GUI = { Name = "Home Label", @@ -58,7 +50,7 @@ asset.export(HomeLabel) asset.meta = { Name = "Home Label", - Version = "1.1", + Version = "2.0", Description = "Label for the Milky Way titled 'Home', sided for the galactic level", Author = "OpenSpace Team", URL = "http://openspaceproject.com", diff --git a/data/assets/scene/digitaluniverse/obassociations.asset b/data/assets/scene/digitaluniverse/obassociations.asset index 73644a8c82..c1793d6717 100644 --- a/data/assets/scene/digitaluniverse/obassociations.asset +++ b/data/assets/scene/digitaluniverse/obassociations.asset @@ -9,14 +9,14 @@ local speck = asset.resource({ Name = "OB Associations Speck Files", Type = "HttpSynchronization", Identifier = "digitaluniverse_obassociations_speck", - Version = 3 + Version = 4 }) local Object = { Identifier = "OBAssociations", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePolygonCloud", Enabled = false, Labels = { File = speck .. "ob.label", @@ -25,19 +25,25 @@ local Object = { MinMaxSize = { 4, 25 }, Unit = "pc" }, - Color = { 1.0, 1.0, 1.0 }, - ColorMap = speck .. "ob.cmap", - ColorOption = { "arm" }, - SizeOption = { "diameter" }, - ExactColorMap = true, + Coloring = { + ColorMapping = { + File = speck .. "ob.cmap", + ParameterOptions = { + { Key = "arm" } + } + } + }, Opacity = 0.7, File = speck .. "ob.speck", Unit = "pc", Texture = textures .. "point4.png", PolygonSides = 7, - ScaleFactor = 390.0, - BillboardMinMaxSize = { 0.0, 450.0 }, - EnablePixelSizeControl = true + SizeSettings = { + SizeMapping = { "diameter" }, + ScaleExponent = 16.9, + MaxPixelSize = 450.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "OB Associations", @@ -71,7 +77,7 @@ asset.export(Object) asset.meta = { Name = "OB Associations", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for OB Associations", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/openclusters.asset b/data/assets/scene/digitaluniverse/openclusters.asset index e16a67dcbf..dd35569693 100644 --- a/data/assets/scene/digitaluniverse/openclusters.asset +++ b/data/assets/scene/digitaluniverse/openclusters.asset @@ -16,7 +16,7 @@ local speck = asset.resource({ local Object = { Identifier = "OpenStarClusters", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePolygonCloud", Enabled = false, Labels = { File = speck .. "oc.label", @@ -25,15 +25,18 @@ local Object = { MinMaxSize = { 4, 30 }, Unit = "pc" }, - Color = { 0.1, 0.8, 0.4 }, + Coloring = { + FixedColor = { 0.1, 0.8, 0.4 } + }, Opacity = 0.5, File = speck .. "oc.speck", Unit = "pc", - Texture = textures .. "point4.png", PolygonSides = 12, - ScaleFactor = 405.75, - BillboardMinMaxSize = { 0.0, 604.0 }, - EnablePixelSizeControl = true + SizeSettings = { + ScaleExponent = 17.6, + MaxPixelSize = 604.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "Open Star Clusters", @@ -68,7 +71,7 @@ asset.export(Object) asset.meta = { Name = "Open Star Clusters", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for Open Star Clusters", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/planetarynebulae.asset b/data/assets/scene/digitaluniverse/planetarynebulae.asset index 48837eac3d..303243769a 100644 --- a/data/assets/scene/digitaluniverse/planetarynebulae.asset +++ b/data/assets/scene/digitaluniverse/planetarynebulae.asset @@ -16,7 +16,7 @@ local speck = asset.resource({ local Object = { Identifier = "PlanetaryNebulae", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePolygonCloud", Enabled = false, Labels = { File = speck .. "pn.label", @@ -25,15 +25,18 @@ local Object = { MinMaxSize = { 4, 25 }, Unit = "pc" }, - Color = { 0.4, 0.4, 0.9 }, + Coloring = { + FixedColor = { 0.4, 0.4, 0.9 } + }, Opacity = 0.65, File = speck .. "pn.speck", - Texture = textures .. "point4.png", PolygonSides = 3, Unit = "pc", - ScaleFactor = 425.0, - BillboardMinMaxSize = { 0.0, 500.0 }, - EnablePixelSizeControl = true + SizeSettings = { + ScaleExponent = 18.46, + MaxPixelSize = 500.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "Planetary Nebulae", @@ -64,7 +67,7 @@ asset.export(Object) asset.meta = { Name = "Planetary Nebulae", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for Planetary Nebulae", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/pulsars.asset b/data/assets/scene/digitaluniverse/pulsars.asset index 8118e80fe8..f18b4e3b5a 100644 --- a/data/assets/scene/digitaluniverse/pulsars.asset +++ b/data/assets/scene/digitaluniverse/pulsars.asset @@ -16,7 +16,7 @@ local speck = asset.resource({ local Object = { Identifier = "Pulsars", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePolygonCloud", Enabled = false, Labels = { File = speck .. "pulsar.label", @@ -25,15 +25,18 @@ local Object = { MinMaxSize = { 4, 20 }, Unit = "pc" }, - Color = { 0.7, 0.0, 0.0 }, + Coloring = { + FixedColor = { 0.7, 0.0, 0.0 } + }, Opacity = 1.0, File = speck .. "pulsar.speck", - Texture = textures .. "point4.png", PolygonSides = 4, Unit = "pc", - ScaleFactor = 424.0, - BillboardMinMaxSize = { 0.0, 500.0 }, - EnablePixelSizeControl = false + SizeSettings = { + ScaleExponent = 18.4, + MaxPixelSize = 500.0, + EnablePixelSizeControl = false + } }, GUI = { Name = "Pulsars", @@ -68,7 +71,7 @@ asset.export(Object) asset.meta = { Name = "Pulsars", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for Pulsars", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/quasars.asset b/data/assets/scene/digitaluniverse/quasars.asset index 783b1b9b1b..92c57b19e0 100644 --- a/data/assets/scene/digitaluniverse/quasars.asset +++ b/data/assets/scene/digitaluniverse/quasars.asset @@ -23,21 +23,32 @@ local colormaps = asset.resource({ local Object = { Identifier = "Quasars", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = true, - Color = { 1.0, 0.4, 0.2 }, Opacity = 0.95, File = speck .. "quasars.speck", Texture = textures .. "point3A.png", Unit = "Mpc", - ScaleFactor = 540.9, - FadeInDistances = { 1000.0, 10000.0 }, -- Fade in value in the same unit as "Unit" - BillboardMinMaxSize = { 0.0, 11.1 }, - EnablePixelSizeControl = true, - ColorMap = colormaps .. "viridis.cmap", - ColorOption = { "redshift", "Tlookback", "distMpc" }, - ColorRange = { { 0.102, 7.085 }, { 1.4, 13.0 }, { 440.5, 8852.099609 } }, - UseColorMap = false + Fading = { + FadeInDistances = { 1000.0, 10000.0 } -- Fade in value in the same unit as "Unit" + }, + Coloring = { + FixedColor = { 1.0, 0.4, 0.2 }, + ColorMapping = { + Enabled = false, + File = colormaps .. "viridis.cmap", + ParameterOptions = { + { Key = "distMpc", Range = { 440.5, 8852.099609 } }, + { Key = "redshift", Range = { 0.102, 7.085 } }, + { Key = "Tlookback", Range = { 1.4, 13.0 } } + } + } + }, + SizeSettings = { + ScaleExponent = 23.5, + MaxPixelSize = 11.1, + EnablePixelSizeControl = true + } }, GUI = { Name = "Quasars", @@ -68,7 +79,7 @@ asset.export(Object) asset.meta = { Name = "Quasars", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for Quasars", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/sdss.asset b/data/assets/scene/digitaluniverse/sdss.asset index b1159ddf92..0ed6ee3d8c 100644 --- a/data/assets/scene/digitaluniverse/sdss.asset +++ b/data/assets/scene/digitaluniverse/sdss.asset @@ -9,31 +9,37 @@ local speck = asset.resource({ Name = "Sloan Digital Sky Survey Speck Files", Type = "HttpSynchronization", Identifier = "digitaluniverse_sloandss_speck", - Version = 2 + Version = 3 }) local Object = { Identifier = "SloanDigitalSkySurvey", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = true, - Color = { 0.8, 0.8, 1.0 }, Opacity = 0.8, - ScaleFactor = 520.0, File = speck .. "SDSSgals.speck", - ColorMap = speck .. "SDSSgals.cmap", - ColorOption = { "redshift", "proximity" }, - ColorRange = { { 0.0, 0.075 }, { 1.0, 50.0 } }, + Coloring = { + FixedColor = { 0.8, 0.8, 1.0 }, + ColorMapping = { + File = speck .. "SDSSgals.cmap", + ParameterOptions = { + { Key = "proximity", Range = { 1.0, 50.0 } }, + { Key = "redshift", Range = { 0.0, 0.075 } } + } + } + }, Texture = textures .. "point3A.png", Unit = "Mpc", - FadeInDistances = { 220.0, 650.0 }, -- Fade in value in the same unit as "Unit" - BillboardMinMaxSize = { 0.0, 5.5 }, - CorrectionSizeEndDistance = 20.65, - CorrectionSizeFactor = 10.41, - TextSize = 14.8, - TextMinMaxSize = { 10, 50 }, - EnablePixelSizeControl = true + Fading = { + FadeInDistances = { 220.0, 650.0 } -- Fade in value in the same unit as "Unit" + }, + SizeSettings = { + ScaleExponent = 22.6, + MaxPixelSize = 5.5, + EnablePixelSizeControl = true + } }, GUI = { Name = "Sloan Digital Sky Survey", @@ -71,7 +77,7 @@ asset.export(Object) asset.meta = { Name = "Sloan Digital Sky Survey", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for The Sloan Digital Sky Survey (SDSS)", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/starlabels.asset b/data/assets/scene/digitaluniverse/starlabels.asset index bd33b78b85..1babd0160f 100644 --- a/data/assets/scene/digitaluniverse/starlabels.asset +++ b/data/assets/scene/digitaluniverse/starlabels.asset @@ -9,7 +9,7 @@ local speck = asset.resource({ local Object = { Identifier = "StarsLabels", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { Enabled = true, @@ -19,7 +19,6 @@ local Object = { MinMaxSize = { 6, 50 }, Unit = "pc" }, - Color = { 1.0, 1.0, 1.0 }, Opacity = 0.65, Unit = "pc" }, @@ -45,7 +44,7 @@ asset.export(Object) asset.meta = { Name = "Star Labels", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for labels of the stars", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/superclusters.asset b/data/assets/scene/digitaluniverse/superclusters.asset index 4959dc3c47..19d8a55c41 100644 --- a/data/assets/scene/digitaluniverse/superclusters.asset +++ b/data/assets/scene/digitaluniverse/superclusters.asset @@ -16,7 +16,7 @@ local speck = asset.resource({ local Object = { Identifier = "GalaxySuperclusters", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { Enabled = true, @@ -27,14 +27,15 @@ local Object = { Unit = "Mpc" }, DrawElements = false, - Color = { 1.0, 1.0, 1.0 }, Opacity = 0.65, File = speck .. "superclust.speck", Texture = textures .. "point3A.png", Unit = "Mpc", - ScaleFactor = 531.0, - -- BillboardMinMaxSize = { 0.0, 7.2 }, - EnablePixelSizeControl = true + SizeSettings = { + ScaleExponent = 23.1, + MaxPixelSize = 7.2, + EnablePixelSizeControl = true + } }, GUI = { Name = "Galaxy Superclusters", @@ -63,7 +64,7 @@ asset.export(Object) asset.meta = { Name = "Galaxy Superclusters", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for Galaxy Superclusters", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/supernovaremnants.asset b/data/assets/scene/digitaluniverse/supernovaremnants.asset index 5332888150..388118796a 100644 --- a/data/assets/scene/digitaluniverse/supernovaremnants.asset +++ b/data/assets/scene/digitaluniverse/supernovaremnants.asset @@ -16,7 +16,7 @@ local speck = asset.resource({ local Object = { Identifier = "SupernovaRemnants", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePolygonCloud", Enabled = false, Labels = { File = speck .. "snr.label", @@ -25,17 +25,18 @@ local Object = { MinMaxSize = { 4, 100 }, Unit = "pc" }, - Color = { 1.0, 0.5, 0.0 }, + Coloring = { + FixedColor = { 1.0, 0.5, 0.0 } + }, Opacity = 0.32, File = speck .. "snr.speck", - Texture = textures .. "point4.png", PolygonSides = 7, Unit = "pc", - ScaleFactor = 424.0, - --CorrectionSizeEndDistance = 17.5, - --CorrectionSizeFactor = 13.96, - BillboardMinMaxSize = { 0.0, 500.0 }, - EnablePixelSizeControl = true + SizeSettings = { + ScaleExponent = 18.4, + MaxPixelSize = 500.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "Supernova Remnants", @@ -62,7 +63,7 @@ asset.export(Object) asset.meta = { Name = "Supernova Remnants", - Version = "2.1", + Version = "3.0", Description = "Digital Universe asset for Supernova Remnants", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/tully.asset b/data/assets/scene/digitaluniverse/tully.asset index b4a20b1e4b..f3a329348a 100644 --- a/data/assets/scene/digitaluniverse/tully.asset +++ b/data/assets/scene/digitaluniverse/tully.asset @@ -9,7 +9,7 @@ local speck = asset.resource({ Name = "Tully Speck Files", Type = "HttpSynchronization", Identifier = "digitaluniverse_tully_speck", - Version = 2 + Version = 3 }) @@ -23,7 +23,7 @@ local TransformMatrix = { local TullyGalaxies = { Identifier = "TullyGalaxies", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = true, Labels = { File = speck .. "tully.label", @@ -33,23 +33,28 @@ local TullyGalaxies = { Unit = "Mpc", TransformationMatrix = TransformMatrix }, - Color = { 1.0, 0.4, 0.2 }, Opacity = 0.99, - ScaleFactor = 504.0, File = speck .. "tully.speck", Texture = textures .. "point3A.png", - --ColorMap = speck .. "tully.cmap", - ColorMap = speck .. "lss.cmap", - --ColorOption = { "proximity" }, - ColorOption = { "prox5Mpc" }, - ColorRange = { { 1.0, 30.0 } }, + Coloring = { + FixedColor = { 1.0, 0.4, 0.2 }, + ColorMapping = { + File = speck .. "lss.cmap", + ParameterOptions = { + { Key = "prox5Mpc", Range = { 1.0, 30.0 } } + } + } + }, Unit = "Mpc", TransformationMatrix = TransformMatrix, - FadeInDistances = { 0.001, 1.0 }, -- Fade in value in the same unit as "Unit" - BillboardMinMaxSize = { 0.0, 7.0 }, -- in pixels - --CorrectionSizeEndDistance = 22.0, - --CorrectionSizeFactor = 10.45 - EnablePixelSizeControl = true + Fading = { + FadeInDistances = { 0.001, 1.0 } -- Fade in value in the same unit as "Unit" + }, + SizeSettings = { + ScaleExponent = 21.9, + MaxPixelSize = 7.0, + EnablePixelSizeControl = true + } }, GUI = { Name = "Tully Galaxies", @@ -125,7 +130,7 @@ asset.export(TullyGalaxiesImages) asset.meta = { Name = "Tully Galaxies", - Version = "3.1", + Version = "4.0", Description = [[Digital Universe asset for Tully Galaxies, including point cloud and images]], Author = "Stuart Levy (NCSA/UIUC), Brian Abbott (AMNH)", diff --git a/data/assets/scene/digitaluniverse/voids.asset b/data/assets/scene/digitaluniverse/voids.asset index 6f38436ee0..40db6e91b0 100644 --- a/data/assets/scene/digitaluniverse/voids.asset +++ b/data/assets/scene/digitaluniverse/voids.asset @@ -9,7 +9,7 @@ local speck = asset.resource({ local Object = { Identifier = "Voids", Renderable = { - Type = "RenderableBillboardsCloud", + Type = "RenderablePointCloud", Enabled = false, Labels = { Enabled = true, @@ -20,7 +20,6 @@ local Object = { Unit = "Mpc" }, DrawElements = false, - Color = { 1.0, 1.0, 1.0 }, Opacity = 0.65, Unit = "Mpc" }, @@ -55,7 +54,7 @@ asset.export(Object) asset.meta = { Name = "Voids", - Version = "2.1", + Version = "3.0", Author = "Brian Abbott (AMNH)", Description = "Digital Universe asset for Cosmic voids", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/modules/digitaluniverse/shaders/points_sprite_fs.glsl b/include/openspace/data/csvloader.h similarity index 83% rename from modules/digitaluniverse/shaders/points_sprite_fs.glsl rename to include/openspace/data/csvloader.h index fa56e68087..dcc4163415 100644 --- a/modules/digitaluniverse/shaders/points_sprite_fs.glsl +++ b/include/openspace/data/csvloader.h @@ -22,26 +22,18 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include "fragment.glsl" +#ifndef __OPENSPACE_CORE___CSVLOADER___H__ +#define __OPENSPACE_CORE___CSVLOADER___H__ -in float vs_screenSpaceDepth; +#include +#include +#include -uniform vec3 color; -uniform float alphaValue; -uniform sampler2D spriteTexture; +namespace openspace::dataloader::csv { +Dataset loadCsvFile(std::filesystem::path path, + std::optional specs = std::nullopt); -Fragment getFragment() { - Fragment frag; +} // namespace openspace::dataloader - if (alphaValue == 0.0) { - discard; - } - - frag.color = texture(spriteTexture, gl_PointCoord) * vec4(color, alphaValue); - //frag.depth = gs_screenSpaceDepth; - frag.depth = vs_screenSpaceDepth; - frag.blend = BLEND_MODE_ADDITIVE; - - return frag; -} +#endif // __OPENSPACE_CORE___CSVLOADER___H__ diff --git a/modules/space/speckloader.h b/include/openspace/data/dataloader.h similarity index 73% rename from modules/space/speckloader.h rename to include/openspace/data/dataloader.h index 74d4150841..8e0e5920ca 100644 --- a/modules/space/speckloader.h +++ b/include/openspace/data/dataloader.h @@ -22,20 +22,28 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __OPENSPACE_MODULE_SPACE___SPECKLOADER___H__ -#define __OPENSPACE_MODULE_SPACE___SPECKLOADER___H__ +#ifndef __OPENSPACE_CORE___DATALOADER___H__ +#define __OPENSPACE_CORE___DATALOADER___H__ +#include #include #include +#include #include #include #include #include -namespace openspace::speck { - -BooleanType(SkipAllZeroLines); +namespace openspace::dataloader { +/** + * A dataset representing objects with positions and various other data columns. + * Based on the SPECK format originally used for the digital universe datasets. + * Mostly used for point-data. + * + * The read data files may also have associated texture values to be used for the + * points. + */ struct Dataset { struct Variable { int index = -1; @@ -59,8 +67,14 @@ struct Dataset { }; std::vector entries; + /// This variable can be used to get an understanding of the world scale size of + /// the dataset + float maxPositionComponent = 0.f; + int index(std::string_view variableName) const; bool normalizeVariable(std::string_view variableName); + glm::vec2 findValueRange(int variableIndex) const; + glm::vec2 findValueRange(std::string_view variableName) const; }; struct Labelset { @@ -76,49 +90,49 @@ struct Labelset { }; struct ColorMap { + std::optional belowRangeColor; + std::optional aboveRangeColor; + std::optional nanColor; std::vector entries; }; namespace data { Dataset loadFile(std::filesystem::path path, - SkipAllZeroLines skipAllZeroLines = SkipAllZeroLines::Yes); + std::optional specs = std::nullopt); std::optional loadCachedFile(std::filesystem::path path); void saveCachedFile(const Dataset& dataset, std::filesystem::path path); - Dataset loadFileWithCache(std::filesystem::path speckPath, - SkipAllZeroLines skipAllZeroLines = SkipAllZeroLines::Yes); + Dataset loadFileWithCache(std::filesystem::path path, + std::optional specs = std::nullopt); } // namespace data namespace label { Labelset loadFile(std::filesystem::path path, - SkipAllZeroLines skipAllZeroLines = SkipAllZeroLines::Yes); + std::optional specs = std::nullopt); std::optional loadCachedFile(std::filesystem::path path); void saveCachedFile(const Labelset& labelset, std::filesystem::path path); - Labelset loadFileWithCache(std::filesystem::path speckPath, - SkipAllZeroLines skipAllZeroLines = SkipAllZeroLines::Yes); + Labelset loadFileWithCache(std::filesystem::path path); } // namespace label namespace color { ColorMap loadFile(std::filesystem::path path, - SkipAllZeroLines skipAllZeroLines = SkipAllZeroLines::Yes); + std::optional specs = std::nullopt); std::optional loadCachedFile(std::filesystem::path path); void saveCachedFile(const ColorMap& colorMap, std::filesystem::path path); - ColorMap loadFileWithCache(std::filesystem::path path, - SkipAllZeroLines skipAllZeroLines = SkipAllZeroLines::Yes); + ColorMap loadFileWithCache(std::filesystem::path path); } // namespace color +} // namespace openspace::dataloader -} // namespace openspace::speck - -#endif // __OPENSPACE_MODULE_SPACE___SPECKLOADER___H__ +#endif // __OPENSPACE_CORE___DATALOADER___H__ diff --git a/include/openspace/data/datamapping.h b/include/openspace/data/datamapping.h new file mode 100644 index 0000000000..bec5890a18 --- /dev/null +++ b/include/openspace/data/datamapping.h @@ -0,0 +1,74 @@ +/***************************************************************************************** + * * + * 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_CORE___DATAMAPPING___H__ +#define __OPENSPACE_CORE___DATAMAPPING___H__ + +#include +#include +#include + +namespace openspace::documentation { struct Documentation; } +namespace ghoul { class Dictionary; } + +namespace openspace::dataloader { + +struct DataMapping { + static DataMapping createFromDictionary(const ghoul::Dictionary& dictionary); + static documentation::Documentation Documentation(); + + bool hasExcludeColumns() const; + bool isExcludeColumn(std::string_view column) const; + + std::optional xColumnName; + std::optional yColumnName; + std::optional zColumnName; + + std::optional missingDataValue; + + bool isCaseSensitive = false; + + std::vector excludeColumns; + + // OBS! When new parameters are added they should be included in the generateHash + // function +}; + +/** + * Generate a string based on the data mapping, that can be used to uniquely + * identify the dataset. + */ +std::string generateHashString(const DataMapping& dm); + +bool isPositionColumn(const std::string& c, const std::optional& mapping); + +bool isColumnX(const std::string& c, const std::optional& mapping); + +bool isColumnY(const std::string& c, const std::optional& mapping); + +bool isColumnZ(const std::string& c, const std::optional& mapping); + +} // namespace openspace::dataloader + +#endif // __OPENSPACE_CORE___DATAMAPPING___H__ diff --git a/modules/digitaluniverse/shaders/points_vs.glsl b/include/openspace/data/speckloader.h similarity index 77% rename from modules/digitaluniverse/shaders/points_vs.glsl rename to include/openspace/data/speckloader.h index d27c7c35a9..0ff86a8ba7 100644 --- a/modules/digitaluniverse/shaders/points_vs.glsl +++ b/include/openspace/data/speckloader.h @@ -22,29 +22,22 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#version __CONTEXT__ +#ifndef __OPENSPACE_CORE___SPECKLOADER___H__ +#define __OPENSPACE_CORE___SPECKLOADER___H__ -#include "PowerScaling/powerScaling_vs.hglsl" +#include +#include +#include -in dvec4 in_position; -in dvec4 in_colormap; +namespace openspace::dataloader::speck { -out float vs_screenSpaceDepth; -out float vs_scaleFactor; -out vec4 colorMap; +Dataset loadSpeckFile(std::filesystem::path path, + std::optional specs = std::nullopt); -uniform dmat4 modelViewProjectionTransform; -uniform float scaleFactor; +Labelset loadLabelFile(std::filesystem::path path); +ColorMap loadCmapFile(std::filesystem::path path); -void main() { - vec4 positionClipSpace = vec4(modelViewProjectionTransform * in_position); - vec4 positionScreenSpace = vec4(z_normalization(positionClipSpace)); +} // namespace openspace::dataloader::speck - vs_screenSpaceDepth = positionScreenSpace.w; - vs_scaleFactor = scaleFactor; - colorMap = vec4(in_colormap); - - gl_PointSize = scaleFactor; - gl_Position = positionScreenSpace; -} +#endif // __OPENSPACE_CORE___SPECKLOADER___H__ diff --git a/include/openspace/rendering/colormappingcomponent.h b/include/openspace/rendering/colormappingcomponent.h new file mode 100644 index 0000000000..60b9fb7868 --- /dev/null +++ b/include/openspace/rendering/colormappingcomponent.h @@ -0,0 +1,117 @@ +/***************************************************************************************** + * * + * 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___COLORMAPCOMPONENT___H__ +#define __OPENSPACE_MODULE_BASE___COLORMAPCOMPONENT___H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openspace { + +namespace documentation { struct Documentation; } + +/** + * This is a component that can be used to consistently hold parameters and properties + * for controlling color mapping in different types of renderables. This includes things + * like the color map file itself (converted to a texture), colors to use for missing + * values and the available data columns and value ranges. + * + * @TODO Also provide a small shader snippet that can be included in fragment shaders + * that use this color mapping. As well as a set of uniforms? Now every + * renderable needs to handle this separately. (emmbr, 2023-10-13) + */ +class ColorMappingComponent : public properties::PropertyOwner { +public: + ColorMappingComponent(); + explicit ColorMappingComponent(const ghoul::Dictionary& dictionary); + ~ColorMappingComponent() override = default; + + ghoul::opengl::Texture* texture() const; + + /** + * Initialize the color map information (ranges, etc.) based on the input dataset. + * + * \param dataset The *loaded* input dataset + */ + void initialize(const dataloader::Dataset& dataset); + + /** + * Initialize a 1D texture based on the entries in the color map file. + */ + void initializeTexture(); + + static documentation::Documentation Documentation(); + + glm::vec4 colorFromColorMap(float value) const; + + properties::BoolProperty enabled; + properties::OptionProperty dataColumn; + properties::StringProperty colorMapFile; + properties::Vec2Property valueRange; + properties::TriggerProperty setRangeFromData; + + properties::BoolProperty hideOutsideRange; + properties::BoolProperty useNanColor; + properties::Vec4Property nanColor; + + properties::BoolProperty useAboveRangeColor; + properties::Vec4Property aboveRangeColor; + + properties::BoolProperty useBelowRangeColor; + properties::Vec4Property belowRangeColor; + +private: + /** + * Fill parameter options list and range data based on the dataset and provided + * information. + */ + void initializeParameterData(const dataloader::Dataset& dataset); + + // One item per color parameter option + std::vector _colorRangeData; + + std::unique_ptr _texture; + + dataloader::ColorMap _colorMap; + + std::optional _providedParameter; + std::optional _providedRange; + + bool _hasNanColorInAsset = false; + bool _hasBelowRangeColorInAsset = false; + bool _hasAboveRangeColorInAsset = false; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___COLORMAPCOMPONENT___H__ diff --git a/modules/space/labelscomponent.h b/include/openspace/rendering/labelscomponent.h similarity index 91% rename from modules/space/labelscomponent.h rename to include/openspace/rendering/labelscomponent.h index 9d8759fa7f..c00fbfc605 100644 --- a/modules/space/labelscomponent.h +++ b/include/openspace/rendering/labelscomponent.h @@ -22,13 +22,13 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __OPENSPACE_MODULE_SPACE___LABELSCOMPONENT___H__ -#define __OPENSPACE_MODULE_SPACE___LABELSCOMPONENT___H__ +#ifndef __OPENSPACE_CORE___LABELSCOMPONENT___H__ +#define __OPENSPACE_CORE___LABELSCOMPONENT___H__ #include #include -#include +#include #include #include #include @@ -49,8 +49,8 @@ public: explicit LabelsComponent(const ghoul::Dictionary& dictionary); ~LabelsComponent() override = default; - speck::Labelset& labelSet(); - const speck::Labelset& labelSet() const; + dataloader::Labelset& labelSet(); + const dataloader::Labelset& labelSet() const; void initialize(); @@ -68,7 +68,9 @@ public: private: std::filesystem::path _labelFile; DistanceUnit _unit = DistanceUnit::Parsec; - speck::Labelset _labelset; + dataloader::Labelset _labelset; + + bool _useCache = true; std::shared_ptr _font = nullptr; @@ -85,4 +87,4 @@ private: } // namespace openspace -#endif // __OPENSPACE_MODULE_SPACE___LABELSCOMPONENT___H__ +#endif // __OPENSPACE_CORE___LABELSCOMPONENT___H__ diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index 6559ad54b3..7baa93e400 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -44,6 +44,8 @@ set(HEADER_FILES rendering/grids/renderablegrid.h rendering/grids/renderableradialgrid.h rendering/grids/renderablesphericalgrid.h + rendering/pointcloud/renderablepointcloud.h + rendering/pointcloud/renderablepolygoncloud.h rendering/renderablecartesianaxes.h rendering/renderabledisc.h rendering/renderablelabel.h @@ -103,6 +105,8 @@ set(SOURCE_FILES rendering/grids/renderablegrid.cpp rendering/grids/renderableradialgrid.cpp rendering/grids/renderablesphericalgrid.cpp + rendering/pointcloud/renderablepointcloud.cpp + rendering/pointcloud/renderablepolygoncloud.cpp rendering/renderablecartesianaxes.cpp rendering/renderabledisc.cpp rendering/renderablelabel.cpp @@ -148,6 +152,9 @@ 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 @@ -160,6 +167,9 @@ set(SHADER_FILES shaders/model_vs.glsl shaders/plane_fs.glsl shaders/plane_vs.glsl + shaders/polygon_fs.glsl + shaders/polygon_gs.glsl + shaders/polygon_vs.glsl shaders/prism_fs.glsl shaders/prism_vs.glsl shaders/renderabletrail_fs.glsl diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index 9939cea058..2b46683efb 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include #include @@ -144,6 +146,8 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) { fRenderable->registerClass( "RenderablePlaneTimeVaryingImage" ); + fRenderable->registerClass("RenderablePointCloud"); + fRenderable->registerClass("RenderablePolygonCloud"); fRenderable->registerClass("RenderablePrism"); fRenderable->registerClass( "RenderableTimeVaryingSphere" @@ -230,6 +234,8 @@ std::vector BaseModule::documentations() const { RenderablePlaneImageLocal::Documentation(), RenderablePlaneImageOnline::Documentation(), RenderablePlaneTimeVaryingImage::Documentation(), + RenderablePointCloud::Documentation(), + RenderablePolygonCloud::Documentation(), RenderablePrism::Documentation(), RenderableRadialGrid::Documentation(), RenderableSphere::Documentation(), diff --git a/modules/base/rendering/grids/renderableboxgrid.cpp b/modules/base/rendering/grids/renderableboxgrid.cpp index 7af3822024..c3712e9eec 100644 --- a/modules/base/rendering/grids/renderableboxgrid.cpp +++ b/modules/base/rendering/grids/renderableboxgrid.cpp @@ -76,7 +76,7 @@ namespace { // [[codegen::verbatim(LabelsInfo.description)]] std::optional labels - [[codegen::reference("space_labelscomponent")]]; + [[codegen::reference("labelscomponent")]]; }; #include "renderableboxgrid_codegen.cpp" } // namespace diff --git a/modules/base/rendering/grids/renderableboxgrid.h b/modules/base/rendering/grids/renderableboxgrid.h index e4a24c7df0..025ec3b36c 100644 --- a/modules/base/rendering/grids/renderableboxgrid.h +++ b/modules/base/rendering/grids/renderableboxgrid.h @@ -27,9 +27,9 @@ #include -#include #include #include +#include #include namespace ghoul::opengl { diff --git a/modules/base/rendering/grids/renderablegrid.cpp b/modules/base/rendering/grids/renderablegrid.cpp index ceddd1bd0f..c75afc7cdc 100644 --- a/modules/base/rendering/grids/renderablegrid.cpp +++ b/modules/base/rendering/grids/renderablegrid.cpp @@ -122,7 +122,7 @@ namespace { // [[codegen::verbatim(LabelsInfo.description)]] std::optional labels - [[codegen::reference("space_labelscomponent")]]; + [[codegen::reference("labelscomponent")]]; }; #include "renderablegrid_codegen.cpp" } // namespace diff --git a/modules/base/rendering/grids/renderablegrid.h b/modules/base/rendering/grids/renderablegrid.h index f9ddc2a1d9..14385f0847 100644 --- a/modules/base/rendering/grids/renderablegrid.h +++ b/modules/base/rendering/grids/renderablegrid.h @@ -27,12 +27,12 @@ #include -#include #include #include #include #include #include +#include #include namespace ghoul::opengl { class ProgramObject; } diff --git a/modules/base/rendering/grids/renderableradialgrid.cpp b/modules/base/rendering/grids/renderableradialgrid.cpp index 4f528fd15b..5c231a3dca 100644 --- a/modules/base/rendering/grids/renderableradialgrid.cpp +++ b/modules/base/rendering/grids/renderableradialgrid.cpp @@ -104,7 +104,7 @@ namespace { // [[codegen::verbatim(LabelsInfo.description)]] std::optional labels - [[codegen::reference("space_labelscomponent")]]; + [[codegen::reference("labelscomponent")]]; }; #include "renderableradialgrid_codegen.cpp" } // namespace diff --git a/modules/base/rendering/grids/renderableradialgrid.h b/modules/base/rendering/grids/renderableradialgrid.h index 88689832fd..cf55c07ab0 100644 --- a/modules/base/rendering/grids/renderableradialgrid.h +++ b/modules/base/rendering/grids/renderableradialgrid.h @@ -27,13 +27,13 @@ #include -#include #include #include #include #include #include #include +#include #include namespace ghoul::opengl { class ProgramObject; } diff --git a/modules/base/rendering/grids/renderablesphericalgrid.cpp b/modules/base/rendering/grids/renderablesphericalgrid.cpp index b196d5d460..a0f2a1fe61 100644 --- a/modules/base/rendering/grids/renderablesphericalgrid.cpp +++ b/modules/base/rendering/grids/renderablesphericalgrid.cpp @@ -79,7 +79,7 @@ namespace { // [[codegen::verbatim(LabelsInfo.description)]] std::optional labels - [[codegen::reference("space_labelscomponent")]]; + [[codegen::reference("labelscomponent")]]; }; #include "renderablesphericalgrid_codegen.cpp" } // namespace diff --git a/modules/base/rendering/grids/renderablesphericalgrid.h b/modules/base/rendering/grids/renderablesphericalgrid.h index 992dd7090f..5f6ac23b63 100644 --- a/modules/base/rendering/grids/renderablesphericalgrid.h +++ b/modules/base/rendering/grids/renderablesphericalgrid.h @@ -27,10 +27,10 @@ #include -#include #include #include #include +#include #include namespace ghoul::opengl { class ProgramObject; } diff --git a/modules/base/rendering/pointcloud/renderablepointcloud.cpp b/modules/base/rendering/pointcloud/renderablepointcloud.cpp new file mode 100644 index 0000000000..f43acf279a --- /dev/null +++ b/modules/base/rendering/pointcloud/renderablepointcloud.cpp @@ -0,0 +1,1042 @@ +/***************************************************************************************** + * * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr std::string_view _loggerCat = "RenderablePointCloud"; + + constexpr std::array UniformNames = { + "cameraViewProjectionMatrix", "modelMatrix", "cameraPosition", "cameraLookUp", + "renderOption", "maxBillboardSize", "color", "opacity", "scaleExponent", + "scaleFactor", "up", "right", "fadeInValue", "screenSize", "hasSpriteTexture", + "spriteTexture", "useColorMap", "colorMapTexture", "cmapRangeMin", "cmapRangeMax", + "nanColor", "useNanColor", "hideOutsideRange", "enablePixelSizeControl", + "aboveRangeColor", "useAboveRangeColor", "belowRangeColor", "useBelowRangeColor", + "hasDvarScaling" + }; + + enum RenderOption { + ViewDirection = 0, + PositionNormal + }; + + constexpr openspace::properties::Property::PropertyInfo SpriteTextureInfo = { + "Texture", + "Point Sprite Texture", + "The path to the texture that should be used as the point sprite", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo UseSpriteTextureInfo = { + "UseTexture", + "Use Texture", + "If true, use the provided sprite texture to render the point. If false, draw " + "the points using the default point shape", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo PointColorInfo = { + "FixedColor", + "Fixed Color", + "This value is used to define the color of the points when no color map is" + "used", + openspace::properties::Property::Visibility::NoviceUser + }; + + constexpr openspace::properties::Property::PropertyInfo DrawElementsInfo = { + "DrawElements", + "Draw Elements", + "Enables/Disables the drawing of the points", + // @VISIBILITY(1.25) + openspace::properties::Property::Visibility::NoviceUser + }; + + static const openspace::properties::PropertyOwner::PropertyOwnerInfo LabelsInfo = { + "Labels", + "Labels", + "The labels for the points" + }; + + constexpr openspace::properties::Property::PropertyInfo RenderOptionInfo = { + "RenderOption", + "Render Option", + "Option wether the point billboards should face the camera or not. Used for " + "non-linear display environments such as fisheye.", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo FadeInDistancesInfo = { + "FadeInDistances", + "Fade-In Start and End Distances", + "These values determine the initial and final distances from the origin of " + "the dataset at which the points will start and end fading-in. The distances " + "are specified in the same unit as the points, that is, the one provodied as the " + "Unit, or meters. With normal fading the points are fully visible once the " + "camera is outside this range and fully invisible when inside the range. With " + "inverted fading the situation is the opposite: the points are visible inside " + "hen closer than the min value of the range and invisible when further away", + // @VISIBILITY(3.25) + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo EnableDistanceFadeInfo = { + "Enabled", + "Enable Distance-based Fading", + "Enables/disables the Fade-in effect based on camera distance. Automatically set " + "to true if FadeInDistances are specified in the asset", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo InvertFadeInfo = { + "Invert", + "Invert", + "This property can be used the invert the fading so that the points are " + "invisible when the camera is further away than the max fade distance " + "and fully visible when it is closer than the min distance", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo UseAdditiveBlendingInfo = { + "UseAdditiveBlending", + "Use Additive Blending", + "If true (default), the color of points rendered on top of each other is " + "blended additively, resulting in a brighter color where points overlap. " + "If false, no such blending will take place and the color of the point " + "will not be modified by blending. Note that this may lead to weird behaviors " + "when the points are rendered with transparency.", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo NumShownDataPointsInfo = { + "NumberOfDataPoints", + "Number of Shown Data Points", + "This read only property includes information about how many points are being " + "rendered.", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo ScaleExponentInfo = { + "ScaleExponent", + "Scale Exponent", + "This value is used as in exponential scaling to set the absolute size of the " + "point. In general, the larger distance the dataset covers, the larger this " + "value should be. If not included, it is computed based on the maximum " + "positional component of the data points. This is useful for showing the " + "dataset at all, but you will likely want to change it to something that looks " + "good", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo ScaleFactorInfo = { + "ScaleFactor", + "Scale Factor", + "This value is used as a multiplicative factor to adjust the size of the points, " + "after the exponential scaling and any pixel-size control effects. Simply just " + "increases or decreases the visual size of the points", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo PixelSizeControlInfo = { + "EnablePixelSizeControl", + "Enable Pixel Size Control", + "If true, the Max Size in Pixels property will be used as an upper limit for the " + "size of the point. Reduces the size of the points when approaching them, so that " + "they stick to a maximum screen space size. Currently, the scaling is computed " + "based on rectangular displays and might look weird in other projections", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo MaxPixelSizeInfo = { + "MaxPixelSize", + "Max Size in Pixels", + "The maximum size (in pixels) for the billboard representing the point.", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo SizeMappingEnabledInfo = { + "Enabled", + "Size Mapping Enabled", + "If this value is set to 'true' and at least one column was loaded as an option " + "for size mapping, the chosen data column will be used to scale the size of the " + "points. The first option in the list is selected per default.", + openspace::properties::Property::Visibility::NoviceUser + }; + + constexpr openspace::properties::Property::PropertyInfo SizeMappingOptionInfo = { + "Parameter", + "Parameter Option", + "This value determines which parameter is used for scaling of the point. The " + "parameter value will be used as a miltiplicative factor to scale the size of " + "the points. Not that they may however still be scaled by pixel size adjustment " + "effects.", + openspace::properties::Property::Visibility::AdvancedUser + }; + + // A RenderablePointCloud can be used to render point-based datasets in 3D space, + // optionally including color mapping, a sprite texture and labels. There are several + // properties that affect the visuals of the points, such as settings for scaling, + // fading, sprite texture, color mapping and whether the colors of overlapping points + // should be blended additively or not. + // + // The point size depends on a few different things: + // + // - At the core, scaling is done based on an exponential value, the 'ScaleExponent'. + // A relatively small change to this value will lead to a large change in size. + // When no exponent is set, one will be created based on the coordinates in the + // dataset. The points will be visible, but may be appeared as too large or small. + // One option is to not specify the exponent when loading the dataset for the the, + // first time, to make sure the points are visual, and then adapt the value + // interactively when OpenSpace is running until you find a value that you find + // suitable. + // + // - There is also an option to limit the size of the points based on a given pixel + // size. For now, this only works for flat projection displays. + // + // - To easily change the visual size of the points, the multiplicative 'ScaleFactor' + // may be used. A value of 2 makes the points twice as large, visually, compared + // to 1. + // + // See example files in data/assets/examples/pointcloud for some concrete examples of + // point clouds with different settings. + struct [[codegen::Dictionary(RenderablePointCloud)]] Parameters { + // The path to the data file that contains information about the point to be + // rendered. Can be either a CSV or SPECK file + std::optional file; + + // If true (default), the loaded dataset will be cached so that it can be loaded + // faster at a later time. This does however mean that any updates to the values + // in the dataset will not lead to changes in the rendering without first removing + // the cached file. Set it to false to disable caching. This can be useful for + // example when working on importing a new dataset + std::optional useCaching; + + // A dictionary specifying details on how to load the dataset. Updating the data + // mapping will lead to a new cached version of the dataset + std::optional dataMapping + [[codegen::reference("dataloader_datamapping")]]; + + // [[codegen::verbatim(SpriteTextureInfo.description)]] + std::optional texture; + + // [[codegen::verbatim(UseSpriteTextureInfo.description)]] + std::optional useTexture; + + // [[codegen::verbatim(DrawElementsInfo.description)]] + std::optional drawElements; + + enum class [[codegen::map(RenderOption)]] RenderOption { + ViewDirection [[codegen::key("Camera View Direction")]], + PositionNormal [[codegen::key("Camera Position Normal")]] + }; + // [[codegen::verbatim(RenderOptionInfo.description)]] + std::optional renderOption; + + // [[codegen::verbatim(UseAdditiveBlendingInfo.description)]] + std::optional useAdditiveBlending; + + enum class [[codegen::map(openspace::DistanceUnit)]] Unit { + Meter [[codegen::key("m")]], + Kilometer [[codegen::key("Km")]], + Parsec [[codegen::key("pc")]], + Kiloparsec [[codegen::key("Kpc")]], + Megaparsec [[codegen::key("Mpc")]], + Gigaparsec [[codegen::key("Gpc")]], + Gigalightyear [[codegen::key("Gly")]] + }; + // The unit used for all distances. Should match the unit of any + // distances/positions in the data files + std::optional unit; + + // [[codegen::verbatim(LabelsInfo.description)]] + std::optional labels + [[codegen::reference("labelscomponent")]]; + + struct SizeSettings { + // A list specifying all parameters that may be used for size mapping, i.e. + // scaling the points based on the provided data columns + std::optional> sizeMapping; + + // [[codegen::verbatim(ScaleExponentInfo.description)]] + std::optional scaleExponent; + + // [[codegen::verbatim(ScaleFactorInfo.description)]] + std::optional scaleFactor; + + // [[codegen::verbatim(PixelSizeControlInfo.description)]] + std::optional enablePixelSizeControl; + + // [[codegen::verbatim(MaxPixelSizeInfo.description)]] + std::optional maxPixelSize; + }; + // Settings related to the scale of the points, whether they should limit to + // a certain pixel size, etc. + std::optional sizeSettings; + + struct ColorSettings { + // [[codegen::verbatim(PointColorInfo.description)]] + std::optional fixedColor [[codegen::color()]]; + + // Settings related to the choice of color map, parameters, etc. + std::optional colorMapping + [[codegen::reference("colormappingcomponent")]]; + }; + // Settings related to the coloring of the points, such as a fixed color, + // color map, etc. + std::optional coloring; + + struct Fading { + // [[codegen::verbatim(EnableDistanceFadeInfo.description)]] + std::optional enabled; + + // [[codegen::verbatim(FadeInDistancesInfo.description)]] + std::optional fadeInDistances; + + // [[codegen::verbatim(InvertFadeInfo.description)]] + std::optional invert; + }; + // Settings related to fading based on camera distance. Can be used to either + // fade away or fade in the points when reaching a certain distance from the + // origin of the dataset + std::optional fading; + + // Transformation matrix to be applied to each object + std::optional transformationMatrix; + }; + +#include "renderablepointcloud_codegen.cpp" +} // namespace + +namespace openspace { + +documentation::Documentation RenderablePointCloud::Documentation() { + return codegen::doc("base_renderablepointcloud"); +} + +RenderablePointCloud::SizeSettings::SizeSettings(const ghoul::Dictionary& dictionary) + : properties::PropertyOwner({ "Sizing", "Sizing", ""}) + , scaleExponent(ScaleExponentInfo, 1.f, 0.f, 25.f) + , scaleFactor(ScaleFactorInfo, 1.f, 0.f, 50.f) + , pixelSizeControl(PixelSizeControlInfo, false) + , maxPixelSize(MaxPixelSizeInfo, 400.f, 0.f, 1000.f) +{ + const Parameters p = codegen::bake(dictionary); + + if (p.sizeSettings.has_value()) { + const Parameters::SizeSettings settings = *p.sizeSettings; + + scaleFactor = settings.scaleFactor.value_or(scaleFactor); + scaleExponent = settings.scaleExponent.value_or(scaleExponent); + pixelSizeControl = settings.enablePixelSizeControl.value_or(pixelSizeControl); + maxPixelSize = settings.maxPixelSize.value_or(maxPixelSize); + + if (settings.sizeMapping.has_value()) { + std::vector opts = *settings.sizeMapping; + for (size_t i = 0; i < opts.size(); ++i) { + // Note that options are added in order + sizeMapping.parameterOption.addOption(static_cast(i), opts[i]); + } + sizeMapping.enabled = true; + + addPropertySubOwner(sizeMapping); + } + } + + addProperty(scaleFactor); + addProperty(scaleExponent); + addProperty(pixelSizeControl); + addProperty(maxPixelSize); +} + +RenderablePointCloud::SizeSettings::SizeMapping::SizeMapping() + : properties::PropertyOwner({ "SizeMapping", "Size Mapping", "" }) + , enabled(SizeMappingEnabledInfo, false) + , parameterOption( + SizeMappingOptionInfo, + properties::OptionProperty::DisplayType::Dropdown + ) +{ + addProperty(enabled); + addProperty(parameterOption); +} + +RenderablePointCloud::ColorSettings::ColorSettings(const ghoul::Dictionary& dictionary) + : properties::PropertyOwner({ "Coloring", "Coloring", "" }) + , pointColor(PointColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f)) +{ + const Parameters p = codegen::bake(dictionary); + + if (p.coloring.has_value()) { + const Parameters::ColorSettings settings = *p.coloring; + pointColor = settings.fixedColor.value_or(pointColor); + + if (settings.colorMapping.has_value()) { + colorMapping = std::make_unique( + *settings.colorMapping + ); + addPropertySubOwner(colorMapping.get()); + } + } + pointColor.setViewOption(properties::Property::ViewOptions::Color); + addProperty(pointColor); +} + +RenderablePointCloud::Fading::Fading(const ghoul::Dictionary& dictionary) + : properties::PropertyOwner({ "Fading", "Fading", "" }) + , enabled(EnableDistanceFadeInfo, false) + , fadeInDistances( + FadeInDistancesInfo, + glm::vec2(0.f), + glm::vec2(0.f), + glm::vec2(100.f) + ) + , invert(InvertFadeInfo, false) +{ + const Parameters p = codegen::bake(dictionary); + + if (p.fading.has_value()) { + const Parameters::Fading f = *p.fading; + + if (f.fadeInDistances.has_value()) { + fadeInDistances = *f.fadeInDistances; + // Set the allowed max value based of that which was entered. Just to give + // useful values for the slider + fadeInDistances.setMaxValue(10.f * glm::vec2(fadeInDistances.value().y)); + } + + enabled = f.enabled.value_or(f.fadeInDistances.has_value()); + invert = f.invert.value_or(invert); + } + + addProperty(enabled); + fadeInDistances.setViewOption(properties::Property::ViewOptions::MinMaxRange); + addProperty(fadeInDistances); + addProperty(invert); +} + +RenderablePointCloud::RenderablePointCloud(const ghoul::Dictionary& dictionary) + : Renderable(dictionary) + , _spriteTexturePath(SpriteTextureInfo) + , _useSpriteTexture(UseSpriteTextureInfo, true) + , _drawElements(DrawElementsInfo, true) + , _useAdditiveBlending(UseAdditiveBlendingInfo, true) + , _renderOption(RenderOptionInfo, properties::OptionProperty::DisplayType::Dropdown) + , _nDataPoints(NumShownDataPointsInfo, 0) + , _fading(dictionary) + , _colorSettings(dictionary) + , _sizeSettings(dictionary) +{ + const Parameters p = codegen::bake(dictionary); + + addProperty(Fadeable::_opacity); + + if (p.file.has_value()) { + _hasDataFile = true; + _dataFile = absPath(*p.file).string(); + } + + if (p.dataMapping.has_value()) { + _dataMapping = dataloader::DataMapping::createFromDictionary(*p.dataMapping); + } + + _drawElements = p.drawElements.value_or(_drawElements); + addProperty(_drawElements); + + _renderOption.addOption(RenderOption::ViewDirection, "Camera View Direction"); + _renderOption.addOption(RenderOption::PositionNormal, "Camera Position Normal"); + + if (p.renderOption.has_value()) { + _renderOption = codegen::map(*p.renderOption); + } + else { + _renderOption = RenderOption::ViewDirection; + } + addProperty(_renderOption); + + _useAdditiveBlending = p.useAdditiveBlending.value_or(_useAdditiveBlending); + addProperty(_useAdditiveBlending); + + if (p.unit.has_value()) { + _unit = codegen::map(*p.unit); + } + else { + _unit = DistanceUnit::Meter; + } + + _spriteTexturePath.onChange([this]() { _spriteTextureIsDirty = true; }); + addProperty(_spriteTexturePath); + + _useSpriteTexture = p.useTexture.value_or(_useSpriteTexture); + addProperty(_useSpriteTexture); + + if (p.texture.has_value()) { + _spriteTexturePath = absPath(*p.texture).string(); + } + + _hasSpriteTexture = p.texture.has_value(); + + if (p.labels.has_value()) { + _labels = std::make_unique(*p.labels); + _hasLabels = true; + addPropertySubOwner(_labels.get()); + // Fading of the labels should also depend on the fading of the renderable + _labels->setParentFadeable(this); + } + + _transformationMatrix = p.transformationMatrix.value_or(_transformationMatrix); + + if (p.sizeSettings.has_value() && p.sizeSettings->sizeMapping.has_value()) { + _sizeSettings.sizeMapping.parameterOption.onChange( + [this]() { _dataIsDirty = true; } + ); + _hasDatavarSize = true; + } + + addPropertySubOwner(_sizeSettings); + addPropertySubOwner(_colorSettings); + + if (p.fading.has_value()) { + addPropertySubOwner(_fading); + } + + if (p.coloring.has_value() && (*p.coloring).colorMapping.has_value()) { + _hasColorMapFile = true; + + _colorSettings.colorMapping->dataColumn.onChange( + [this]() { _dataIsDirty = true; } + ); + + _colorSettings.colorMapping->setRangeFromData.onChange([this]() { + int parameterIndex = currentColorParameterIndex(); + _colorSettings.colorMapping->valueRange = _dataset.findValueRange( + parameterIndex + ); + }); + } + + if (_hasDataFile) { + bool useCaching = p.useCaching.value_or(true); + if (useCaching) { + _dataset = dataloader::data::loadFileWithCache(_dataFile, _dataMapping); + } + else { + _dataset = dataloader::data::loadFile(_dataFile, _dataMapping); + } + _nDataPoints = static_cast(_dataset.entries.size()); + } + + // If no scale exponent was specified, compute one that will at least show the points + // based on the scale of the positions in the dataset + if (!p.sizeSettings.has_value() || !p.sizeSettings->scaleExponent.has_value()) { + double dist = _dataset.maxPositionComponent * toMeter(_unit); + float exponent = static_cast(std::log10(dist)); + // Reduce the actually used exponent a little bit, as just using the logarithm + // as is leads to very large points + _sizeSettings.scaleExponent = 0.9f * exponent; + } + + _nDataPoints.setReadOnly(true); + addProperty(_nDataPoints); +} + +bool RenderablePointCloud::isReady() const { + bool isReady = _program && !_dataset.entries.empty(); + + // If we have labels, they also need to be loaded + if (_hasLabels) { + isReady = isReady && _labels->isReady(); + } + return isReady; +} + +void RenderablePointCloud::initialize() { + ZoneScoped; + + if (_hasDataFile && _hasColorMapFile) { + _colorSettings.colorMapping->initialize(_dataset); + } + + if (_hasLabels) { + _labels->initialize(); + _labels->loadLabels(); + } +} + +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") + ); + } + ); + + ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); + + if (_hasColorMapFile) { + _colorSettings.colorMapping->initializeTexture(); + } +} + +void RenderablePointCloud::deinitializeGL() { + glDeleteBuffers(1, &_vbo); + _vbo = 0; + glDeleteVertexArrays(1, &_vao); + _vao = 0; + + BaseModule::ProgramObjectManager.release( + "RenderablePointCloud", + [](ghoul::opengl::ProgramObject* p) { + global::renderEngine->removeRenderProgram(p); + } + ); + _program = nullptr; + + BaseModule::TextureManager.release(_spriteTexture); + _spriteTexture = nullptr; +} + +void RenderablePointCloud::bindTextureForRendering() const { + if (_spriteTexture) { + _spriteTexture->bind(); + } +} + +float RenderablePointCloud::computeDistanceFadeValue(const RenderData& data) const { + if (!_fading.enabled) { + return 1.f; + } + + float fadeValue = 1.f; + glm::dmat4 invModelMatrix = glm::inverse(calcModelTransform(data)); + + glm::dvec3 cameraPosModelSpace = glm::dvec3( + invModelMatrix * glm::dvec4(data.camera.positionVec3(), 1.0) + ); + + float distCamera = static_cast( + glm::length(cameraPosModelSpace) / toMeter(_unit) + ); + + const glm::vec2 fadeRange = _fading.fadeInDistances; + const float fadeRangeWidth = (fadeRange.y - fadeRange.x); + float funcValue = (distCamera - fadeRange.x) / fadeRangeWidth; + funcValue = glm::clamp(funcValue, 0.f, 1.f); + + if (_fading.invert) { + funcValue = 1.f - funcValue; + } + + return fadeValue * funcValue; +} + +void RenderablePointCloud::renderBillboards(const RenderData& data, + const glm::dmat4& modelMatrix, + const glm::dvec3& orthoRight, + const glm::dvec3& orthoUp, + float fadeInVariable) +{ + 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( + "screenSize", + glm::vec2(global::renderEngine->renderingResolution()) + ); + + _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.cameraViewProjectionMatrix, + glm::dmat4(data.camera.projectionMatrix()) * data.camera.combinedViewMatrix() + ); + + _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); + _program->setUniform(_uniformCache.scaleFactor, _sizeSettings.scaleFactor); + _program->setUniform(_uniformCache.enablePixelSizeControl, _sizeSettings.pixelSizeControl); + _program->setUniform(_uniformCache.maxBillboardSize, _sizeSettings.maxPixelSize); + _program->setUniform(_uniformCache.hasDvarScaling, _sizeSettings.sizeMapping.enabled); + + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + _program->setUniform(_uniformCache.screenSize, glm::vec2(viewport[2], viewport[3])); + + bool useTexture = _hasSpriteTexture && _useSpriteTexture; + _program->setUniform(_uniformCache.hasSpriteTexture, useTexture); + + ghoul::opengl::TextureUnit spriteTextureUnit; + _program->setUniform(_uniformCache.spriteTexture, spriteTextureUnit); + if (useTexture) { + spriteTextureUnit.activate(); + bindTextureForRendering(); + } + + _program->setUniform(_uniformCache.color, _colorSettings.pointColor); + + bool useColorMap = _hasColorMapFile && _colorSettings.colorMapping->enabled && + _colorSettings.colorMapping->texture(); + + _program->setUniform(_uniformCache.useColormap, useColorMap); + + ghoul::opengl::TextureUnit colorMapTextureUnit; + _program->setUniform(_uniformCache.colorMapTexture, colorMapTextureUnit); + + if (useColorMap) { + colorMapTextureUnit.activate(); + _colorSettings.colorMapping->texture()->bind(); + + const glm::vec2 range = _colorSettings.colorMapping->valueRange; + _program->setUniform(_uniformCache.cmapRangeMin, range.x); + _program->setUniform(_uniformCache.cmapRangeMax, range.y); + _program->setUniform( + _uniformCache.hideOutsideRange, + _colorSettings.colorMapping->hideOutsideRange + ); + + _program->setUniform( + _uniformCache.nanColor, + _colorSettings.colorMapping->nanColor + ); + _program->setUniform( + _uniformCache.useNanColor, + _colorSettings.colorMapping->useNanColor + ); + + _program->setUniform( + _uniformCache.aboveRangeColor, + _colorSettings.colorMapping->aboveRangeColor + ); + _program->setUniform( + _uniformCache.useAboveRangeColor, + _colorSettings.colorMapping->useAboveRangeColor + ); + + _program->setUniform( + _uniformCache.belowRangeColor, + _colorSettings.colorMapping->belowRangeColor + ); + _program->setUniform( + _uniformCache.useBelowRangeColor, + _colorSettings.colorMapping->useBelowRangeColor + ); + } + + glBindVertexArray(_vao); + glDrawArrays(GL_POINTS, 0, static_cast(_dataset.entries.size())); + glBindVertexArray(0); + _program->deactivate(); + + global::renderEngine->openglStateCache().resetBlendState(); + global::renderEngine->openglStateCache().resetDepthState(); +} + +void RenderablePointCloud::render(const RenderData& data, RendererTasks&) { + float fadeInVar = computeDistanceFadeValue(data); + + if (fadeInVar < 0.01f) { + return; + } + + glm::dmat4 modelMatrix = calcModelTransform(data); + glm::dmat4 modelViewProjectionMatrix = + calcModelViewProjectionTransform(data, modelMatrix); + + glm::dvec3 cameraViewDirectionWorld = -data.camera.viewDirectionWorldSpace(); + glm::dvec3 cameraUpDirectionWorld = data.camera.lookUpVectorWorldSpace(); + glm::dvec3 orthoRight = glm::normalize( + glm::cross(cameraUpDirectionWorld, cameraViewDirectionWorld) + ); + if (orthoRight == glm::dvec3(0.0)) { + glm::dvec3 otherVector = glm::vec3( + cameraUpDirectionWorld.y, + cameraUpDirectionWorld.x, + cameraUpDirectionWorld.z + ); + orthoRight = glm::normalize(glm::cross(otherVector, cameraViewDirectionWorld)); + } + glm::dvec3 orthoUp = glm::normalize(glm::cross(cameraViewDirectionWorld, orthoRight)); + + if (_hasDataFile && _drawElements) { + renderBillboards(data, modelMatrix, orthoRight, orthoUp, fadeInVar); + } + + if (_hasLabels) { + _labels->render(data, modelViewProjectionMatrix, orthoRight, orthoUp, fadeInVar); + } +} + +void RenderablePointCloud::update(const UpdateData&) { + ZoneScoped; + + if (_dataIsDirty) { + updateBufferData(); + } + + if (_spriteTextureIsDirty) { + updateSpriteTexture(); + } +} + +int RenderablePointCloud::nAttributesPerPoint() const { + int n = 4; // position + n += _hasColorMapFile ? 1 : 0; + n += _hasDatavarSize ? 1 : 0; + return n; +} + +void RenderablePointCloud::updateBufferData() { + if (!_hasDataFile) { + return; + } + + ZoneScopedN("Data dirty"); + TracyGpuZone("Data dirty"); + LDEBUG("Regenerating data"); + + std::vector slice = createDataSlice(); + + int size = static_cast(slice.size()); + + 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)); + } + + glBindVertexArray(_vao); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferData(GL_ARRAY_BUFFER, size * sizeof(float), slice.data(), GL_STATIC_DRAW); + + const int attibutesPerPoint = nAttributesPerPoint(); + int attributeOffset = 0; + + GLint positionAttrib = _program->attributeLocation("in_position"); + glEnableVertexAttribArray(positionAttrib); + glVertexAttribPointer( + positionAttrib, + 4, + GL_FLOAT, + GL_FALSE, + attibutesPerPoint * sizeof(float), + nullptr + ); + attributeOffset += 4; + + if (_hasColorMapFile) { + GLint colorParamAttrib = _program->attributeLocation("in_colorParameter"); + glEnableVertexAttribArray(colorParamAttrib); + glVertexAttribPointer( + colorParamAttrib, + 1, + GL_FLOAT, + GL_FALSE, + attibutesPerPoint * sizeof(float), + reinterpret_cast(attributeOffset * sizeof(float)) + ); + attributeOffset += 1; + } + + if (_hasDatavarSize) { + GLint scalingAttrib = _program->attributeLocation("in_scalingParameter"); + glEnableVertexAttribArray(scalingAttrib); + glVertexAttribPointer( + scalingAttrib, + 1, + GL_FLOAT, + GL_FALSE, + attibutesPerPoint * sizeof(float), + reinterpret_cast(attributeOffset * sizeof(float)) + ); + attributeOffset += 1; + } + + glBindVertexArray(0); + + _dataIsDirty = false; +} + +void RenderablePointCloud::updateSpriteTexture() { + bool shouldUpdate = _hasSpriteTexture && _spriteTextureIsDirty && + !_spriteTexturePath.value().empty(); + + if (!shouldUpdate) { + return; + } + + ZoneScopedN("Sprite texture"); + TracyGpuZone("Sprite texture"); + + ghoul::opengl::Texture* texture = _spriteTexture; + + unsigned int hash = ghoul::hashCRC32File(_spriteTexturePath); + + _spriteTexture = BaseModule::TextureManager.request( + std::to_string(hash), + [path = _spriteTexturePath]() -> std::unique_ptr { + std::filesystem::path p = absPath(path); + LINFO(fmt::format("Loaded texture from {}", p)); + std::unique_ptr t = + ghoul::io::TextureReader::ref().loadTexture(p.string(), 2); + t->uploadTexture(); + t->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + t->purgeFromRAM(); + return t; + } + ); + + BaseModule::TextureManager.release(texture); + _spriteTextureIsDirty = false; +} + +int RenderablePointCloud::currentColorParameterIndex() const { + const properties::OptionProperty& property = + _colorSettings.colorMapping->dataColumn; + + if (!_hasColorMapFile || property.options().empty()) { + return 0; + } + + return _dataset.index(property.option().description); +} + +int RenderablePointCloud::currentSizeParameterIndex() const { + const properties::OptionProperty& property = + _sizeSettings.sizeMapping.parameterOption; + + if (!_hasDatavarSize || property.options().empty()) { + return 0; + } + + return _dataset.index(property.option().description); +} + +std::vector RenderablePointCloud::createDataSlice() { + ZoneScoped; + + if (_dataset.entries.empty()) { + return std::vector(); + } + + std::vector result; + result.reserve(nAttributesPerPoint() * _dataset.entries.size()); + + // 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; + 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; + + const double r = glm::length(position); + maxRadius = std::max(maxRadius, r); + + // Positions + for (int j = 0; j < 4; ++j) { + result.push_back(static_cast(position[j])); + } + + // Colors + if (_hasColorMapFile) { + biggestCoord = std::max(biggestCoord, glm::compMax(position)); + result.push_back(e.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(e.data[sizeParamIndex]); + } + } + setBoundingSphere(maxRadius); + return result; +} + +} // namespace openspace diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.h b/modules/base/rendering/pointcloud/renderablepointcloud.h similarity index 58% rename from modules/digitaluniverse/rendering/renderablebillboardscloud.h rename to modules/base/rendering/pointcloud/renderablepointcloud.h index 93701b6aea..0cfcbed715 100644 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.h +++ b/modules/base/rendering/pointcloud/renderablepointcloud.h @@ -22,27 +22,27 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLEBILLBOARDSCLOUD___H__ -#define __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLEBILLBOARDSCLOUD___H__ +#ifndef __OPENSPACE_MODULE_BASE___RENDERABLEPOINTCLOUD___H__ +#define __OPENSPACE_MODULE_BASE___RENDERABLEPOINTCLOUD___H__ #include -#include #include #include #include #include #include +#include #include #include #include +#include +#include #include #include #include #include -#include -namespace ghoul::filesystem { class File; } namespace ghoul::opengl { class ProgramObject; class Texture; @@ -52,10 +52,15 @@ namespace openspace { namespace documentation { struct Documentation; } -class RenderableBillboardsCloud : public Renderable { +/** + * This class describes a point cloud renderable that can be used to draw billboraded + * points based on a data file with 3D positions. Alternatively the points can also + * be colored and sized based on a separate column in the data file. + */ +class RenderablePointCloud : public Renderable { public: - explicit RenderableBillboardsCloud(const ghoul::Dictionary& dictionary); - ~RenderableBillboardsCloud() override = default; + explicit RenderablePointCloud(const ghoul::Dictionary& dictionary); + ~RenderablePointCloud() override = default; void initialize() override; void initializeGL() override; @@ -68,88 +73,104 @@ public: static documentation::Documentation Documentation(); -private: +protected: + int nAttributesPerPoint() const; + void updateBufferData(); + void updateSpriteTexture(); + + /// Find the index of the currently chosen color parameter in the dataset + int currentColorParameterIndex() const; + /// Find the index of the currently chosen size parameter in the dataset + int currentSizeParameterIndex() const; + std::vector createDataSlice(); - void createPolygonTexture(); - void renderToTexture(GLuint textureToRenderTo, GLuint textureWidth, - GLuint textureHeight); - void loadPolygonGeometryForRendering(); - void renderPolygonGeometry(GLuint vao); + + virtual void bindTextureForRendering() const; + + float computeDistanceFadeValue(const RenderData& data) const; + void renderBillboards(const RenderData& data, const glm::dmat4& modelMatrix, const glm::dvec3& orthoRight, const glm::dvec3& orthoUp, float fadeInVariable); - bool _hasSpeckFile = false; bool _dataIsDirty = true; - bool _hasSpriteTexture = false; bool _spriteTextureIsDirty = true; + + bool _hasSpriteTexture = false; + bool _hasDataFile = false; bool _hasColorMapFile = false; - bool _isColorMapExact = false; bool _hasDatavarSize = false; - bool _hasPolygon = false; bool _hasLabels = false; - int _polygonSides = 0; + struct SizeSettings : properties::PropertyOwner { + explicit SizeSettings(const ghoul::Dictionary& dictionary); - GLuint _pTexture = 0; + struct SizeMapping : properties::PropertyOwner { + SizeMapping(); + properties::BoolProperty enabled; + properties::OptionProperty parameterOption; + }; + SizeMapping sizeMapping; - properties::FloatProperty _scaleFactor; - properties::BoolProperty _useColorMap; - properties::Vec3Property _pointColor; + properties::FloatProperty scaleExponent; + properties::FloatProperty scaleFactor; + + properties::BoolProperty pixelSizeControl; + properties::FloatProperty maxPixelSize; + }; + SizeSettings _sizeSettings; + + struct ColorSettings : properties::PropertyOwner { + explicit ColorSettings(const ghoul::Dictionary& dictionary); + properties::Vec3Property pointColor; + std::unique_ptr colorMapping; + }; + ColorSettings _colorSettings; + + struct Fading : properties::PropertyOwner { + explicit Fading(const ghoul::Dictionary& dictionary); + properties::Vec2Property fadeInDistances; + properties::BoolProperty enabled; + properties::BoolProperty invert; + }; + Fading _fading; + + properties::BoolProperty _useSpriteTexture; properties::StringProperty _spriteTexturePath; + + properties::BoolProperty _useAdditiveBlending; + properties::BoolProperty _drawElements; - properties::BoolProperty _pixelSizeControl; - properties::OptionProperty _colorOption; - properties::Vec2Property _optionColorRangeData; - properties::OptionProperty _datavarSizeOption; - properties::Vec2Property _fadeInDistances; - properties::BoolProperty _disableFadeInDistance; - properties::Vec2Property _billboardMinMaxSize; - properties::FloatProperty _correctionSizeEndDistance; - properties::FloatProperty _correctionSizeFactor; - properties::BoolProperty _useLinearFiltering; - properties::TriggerProperty _setRangeFromData; properties::OptionProperty _renderOption; - ghoul::opengl::Texture* _polygonTexture = nullptr; + properties::UIntProperty _nDataPoints; + ghoul::opengl::Texture* _spriteTexture = nullptr; ghoul::opengl::ProgramObject* _program = nullptr; - ghoul::opengl::ProgramObject* _renderToPolygonProgram = nullptr; UniformCache( cameraViewProjectionMatrix, modelMatrix, cameraPos, cameraLookup, renderOption, - minBillboardSize, maxBillboardSize, correctionSizeEndDistance, - correctionSizeFactor, color, alphaValue, scaleFactor, up, right, fadeInValue, - screenSize, spriteTexture, hasColormap, useColormap, enabledRectSizeControl, - hasDvarScaling + maxBillboardSize, color, opacity, scaleExponent, scaleFactor, up, right, + fadeInValue, screenSize, hasSpriteTexture, spriteTexture, useColormap, + colorMapTexture, cmapRangeMin, cmapRangeMax, nanColor, useNanColor, + hideOutsideRange, enablePixelSizeControl, aboveRangeColor, useAboveRangeColor, + belowRangeColor, useBelowRangeColor, hasDvarScaling ) _uniformCache; - std::string _speckFile; - std::string _colorMapFile; - std::string _colorOptionString; - std::string _datavarSizeOptionString; + std::string _dataFile; DistanceUnit _unit = DistanceUnit::Parsec; - speck::Dataset _dataset; - speck::ColorMap _colorMap; + dataloader::Dataset _dataset; + dataloader::DataMapping _dataMapping; - // Everything related to the labels is handled by LabelsComponent std::unique_ptr _labels; - std::vector _colorRangeData; - std::unordered_map _optionConversionMap; - std::unordered_map _optionConversionSizeMap; - glm::dmat4 _transformationMatrix = glm::dmat4(1.0); GLuint _vao = 0; GLuint _vbo = 0; - - // For polygons - GLuint _polygonVao = 0; - GLuint _polygonVbo = 0; }; } // namespace openspace -#endif // __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLEBILLBOARDSCLOUD___H__ +#endif // __OPENSPACE_MODULE_BASE___RENDERABLEPOINTCLOUD___H__ diff --git a/modules/base/rendering/pointcloud/renderablepolygoncloud.cpp b/modules/base/rendering/pointcloud/renderablepolygoncloud.cpp new file mode 100644 index 0000000000..672586f435 --- /dev/null +++ b/modules/base/rendering/pointcloud/renderablepolygoncloud.cpp @@ -0,0 +1,193 @@ +/***************************************************************************************** + * * + * 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 + +namespace { + constexpr std::string_view _loggerCat = "RenderablePolygonCloud"; + + // A RenderablePolygonCloud is a RenderablePointCloud where the shape of the points + // is a uniform polygon with a given number of sides instead of a texture. For + // instance, PolygonSides = 5 results in the points being rendered as pentagons. + // Note that while this renderable inherits the texture property from + // RenderablePointCloud, any added texture value will be ignored in favor of the + // polygon shape. + // + // See documentation of RenderablePointCloud for details on the other parts of the + // point cloud rendering. + struct [[codegen::Dictionary(RenderablePolygonCloud)]] Parameters { + // The number of sides for the polygon used to represent each point. Default is + // 3, i.e. to use triangles + std::optional polygonSides [[codegen::greaterequal(3)]]; + }; + +#include "renderablepolygoncloud_codegen.cpp" +} // namespace + +namespace openspace { + +documentation::Documentation RenderablePolygonCloud::Documentation() { + return codegen::doc( + "base_renderablepolygoncloud", + RenderablePointCloud::Documentation() + ); +} + +RenderablePolygonCloud::RenderablePolygonCloud(const ghoul::Dictionary& dictionary) + : RenderablePointCloud(dictionary) +{ + const Parameters p = codegen::bake(dictionary); + + _nPolygonSides = p.polygonSides.value_or(_nPolygonSides); + + // The texture to use for the rendering will be generated in initializeGl. Make sure + // we use it in the rnedering + _hasSpriteTexture = true; +} + +void RenderablePolygonCloud::initializeGL() { + ZoneScoped; + + RenderablePointCloud::initializeGL(); + createPolygonTexture(); +} + +void RenderablePolygonCloud::deinitializeGL() { + glDeleteBuffers(1, &_polygonVbo); + _polygonVbo = 0; + glDeleteVertexArrays(1, &_polygonVao); + _polygonVao = 0; + + glDeleteTextures(1, &_pTexture); + + RenderablePointCloud::deinitializeGL(); +} + +void RenderablePolygonCloud::bindTextureForRendering() const { + glBindTexture(GL_TEXTURE_2D, _pTexture); +} + +void RenderablePolygonCloud::createPolygonTexture() { + ZoneScoped; + + LDEBUG("Creating Polygon Texture"); + constexpr gl::GLsizei TexSize = 512; + + glGenTextures(1, &_pTexture); + glBindTexture(GL_TEXTURE_2D, _pTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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); + // Stopped using a buffer object for GL_PIXEL_UNPACK_BUFFER + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TexSize, TexSize, 0, GL_RGBA, GL_BYTE, nullptr); + + renderToTexture(_pTexture, TexSize, TexSize); +} + +void RenderablePolygonCloud::renderToTexture(GLuint textureToRenderTo, + GLuint textureWidth, GLuint textureHeight) +{ + LDEBUG("Rendering to Texture"); + + // Saves initial Application's OpenGL State + GLint defaultFBO; + GLint viewport[4]; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); + glGetIntegerv(GL_VIEWPORT, viewport); + + GLuint textureFBO; + glGenFramebuffers(1, &textureFBO); + glBindFramebuffer(GL_FRAMEBUFFER, textureFBO); + GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 }; + glDrawBuffers(1, drawBuffers); + + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureToRenderTo, 0); + + glViewport(viewport[0], viewport[1], textureWidth, textureHeight); + + if (_polygonVao == 0) { + glGenVertexArrays(1, &_polygonVao); + } + if (_polygonVbo == 0) { + glGenBuffers(1, &_polygonVbo); + } + + glBindVertexArray(_polygonVao); + glBindBuffer(GL_ARRAY_BUFFER, _polygonVbo); + + constexpr std::array VertexData = { + // x y z w + 0.f, 0.f, 0.f, 1.f, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData), VertexData.data(), GL_STATIC_DRAW); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr); + glEnableVertexAttribArray(0); + glBindVertexArray(0); + + renderPolygonGeometry(_polygonVao); + + // Restores Applications' OpenGL State + glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + glDeleteBuffers(1, &_polygonVbo); + glDeleteVertexArrays(1, &_polygonVao); + glDeleteFramebuffers(1, &textureFBO); +} + +void RenderablePolygonCloud::renderPolygonGeometry(GLuint vao) { + std::unique_ptr program = + ghoul::opengl::ProgramObject::Build( + "RenderablePointCloud_Polygon", + absPath("${MODULE_BASE}/shaders/polygon_vs.glsl"), + absPath("${MODULE_BASE}/shaders/polygon_fs.glsl"), + absPath("${MODULE_BASE}/shaders/polygon_gs.glsl") + ); + + program->activate(); + constexpr glm::vec4 Black = glm::vec4(0.f, 0.f, 0.f, 0.f); + glClearBufferfv(GL_COLOR, 0, glm::value_ptr(Black)); + + program->setUniform("sides", _nPolygonSides); + program->setUniform("polygonColor", _colorSettings.pointColor); + + glBindVertexArray(vao); + glDrawArrays(GL_POINTS, 0, 1); + glBindVertexArray(0); + + program->deactivate(); +} + +} // namespace openspace diff --git a/modules/base/rendering/pointcloud/renderablepolygoncloud.h b/modules/base/rendering/pointcloud/renderablepolygoncloud.h new file mode 100644 index 0000000000..f873636779 --- /dev/null +++ b/modules/base/rendering/pointcloud/renderablepolygoncloud.h @@ -0,0 +1,70 @@ +/***************************************************************************************** + * * + * 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___RENDERABLEPOLYGONCLOUD___H__ +#define __OPENSPACE_MODULE_BASE___RENDERABLEPOLYGONCLOUD___H__ + +#include + +#include + +namespace ghoul::opengl { class Texture; } + +namespace openspace { + +namespace documentation { struct Documentation; } + +/** + * A billboarded point cloud, but with dynamically created uniform polygon shapes instead + * of a custom texture. Overwrites the sprite set in parent class, RenderablePointCloud + */ +class RenderablePolygonCloud : public RenderablePointCloud { +public: + explicit RenderablePolygonCloud(const ghoul::Dictionary& dictionary); + ~RenderablePolygonCloud() override = default; + + void initializeGL() override; + void deinitializeGL() override; + + static documentation::Documentation Documentation(); + +private: + void createPolygonTexture(); + void renderToTexture(GLuint textureToRenderTo, GLuint textureWidth, + GLuint textureHeight); + void renderPolygonGeometry(GLuint vao); + + void bindTextureForRendering() const override; + + int _nPolygonSides = 3; + + GLuint _pTexture = 0; + + GLuint _polygonVao = 0; + GLuint _polygonVbo = 0; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___RENDERABLEPOLYGONCLOUD___H__ diff --git a/modules/digitaluniverse/shaders/billboard_fs.glsl b/modules/base/shaders/billboardpoint_fs.glsl similarity index 64% rename from modules/digitaluniverse/shaders/billboard_fs.glsl rename to modules/base/shaders/billboardpoint_fs.glsl index f6ce480f6a..b0dfaaeb6b 100644 --- a/modules/digitaluniverse/shaders/billboard_fs.glsl +++ b/modules/base/shaders/billboardpoint_fs.glsl @@ -24,45 +24,86 @@ #include "fragment.glsl" -flat in vec4 gs_colorMap; +flat in float gs_colorParameter; flat in float vs_screenSpaceDepth; in vec2 texCoord; -in float ta; -uniform float alphaValue; +uniform float opacity; uniform vec3 color; + +uniform vec4 nanColor = vec4(0.5); +uniform bool useNanColor = true; + +uniform vec4 aboveRangeColor; +uniform bool useAboveRangeColor; + +uniform vec4 belowRangeColor; +uniform bool useBelowRangeColor; + +uniform bool hasSpriteTexture; uniform sampler2D spriteTexture; -uniform bool hasColorMap; + uniform bool useColorMap; +uniform sampler1D colorMapTexture; +uniform float cmapRangeMin; +uniform float cmapRangeMax; +uniform bool hideOutsideRange; + uniform float fadeInValue; +vec4 sampleColorMap(float dataValue) { + if (useNanColor && isnan(dataValue)) { + return nanColor; + } + + bool isOutside = dataValue < cmapRangeMin || dataValue > cmapRangeMax; + if (isnan(dataValue) || (hideOutsideRange && isOutside)) { + discard; + } + + if (useBelowRangeColor && dataValue < cmapRangeMin) { + return belowRangeColor; + } + + if (useAboveRangeColor && dataValue > cmapRangeMax) { + return aboveRangeColor; + } + + float t = (dataValue - cmapRangeMin) / (cmapRangeMax - cmapRangeMin); + t = clamp(t, 0.0, 1.0); + return texture(colorMapTexture, t); +} Fragment getFragment() { - if (gs_colorMap.a == 0.0 || ta == 0.0 || fadeInValue == 0.0 || alphaValue == 0.0) { + if (fadeInValue == 0.0 || opacity == 0.0) { discard; } - vec4 textureColor = texture(spriteTexture, texCoord); - if (textureColor.a == 0.0) { - discard; + if (!hasSpriteTexture) { + // Moving the origin to the center + vec2 st = (texCoord - vec2(0.5)) * 2.0; + if (length(st) > 1.0) { + discard; + } } - vec4 fullColor = textureColor; + vec4 fullColor = vec4(1.0); + if (hasSpriteTexture) { + fullColor = texture(spriteTexture, texCoord); + } - if (hasColorMap && useColorMap) { - fullColor *= gs_colorMap; + if (useColorMap) { + fullColor *= sampleColorMap(gs_colorParameter); } else { fullColor.rgb *= color; } - float textureOpacity = dot(fullColor.rgb, vec3(1.0)); - if (textureOpacity == 0.0) { + fullColor.a *= opacity * fadeInValue; + if (fullColor.a < 0.01) { discard; } - fullColor.a *= alphaValue * fadeInValue * ta; - Fragment frag; frag.color = fullColor; frag.depth = vs_screenSpaceDepth; diff --git a/modules/digitaluniverse/shaders/billboard_gs.glsl b/modules/base/shaders/billboardpoint_gs.glsl similarity index 73% rename from modules/digitaluniverse/shaders/billboard_gs.glsl rename to modules/base/shaders/billboardpoint_gs.glsl index 0e5cfc658d..19db521a59 100644 --- a/modules/digitaluniverse/shaders/billboard_gs.glsl +++ b/modules/base/shaders/billboardpoint_gs.glsl @@ -27,21 +27,21 @@ #include "PowerScaling/powerScalingMath.hglsl" layout(points) in; -flat in vec4 colorMap[]; -flat in float dvarScaling[]; +flat in float colorParameter[]; +flat in float scalingParameter[]; layout(triangle_strip, max_vertices = 4) out; -flat out vec4 gs_colorMap; +flat out float gs_colorParameter; out vec2 texCoord; flat out float vs_screenSpaceDepth; -out float ta; // General settings +uniform float scaleExponent; uniform float scaleFactor; uniform int renderOption; uniform dmat4 cameraViewProjectionMatrix; uniform dmat4 modelMatrix; -uniform bool enabledRectSizeControl; +uniform bool enablePixelSizeControl; uniform bool hasDvarScaling; // RenderOption: CameraViewDirection @@ -51,15 +51,10 @@ uniform vec3 right; // RenderOption: CameraPositionNormal uniform dvec3 cameraPosition; uniform vec3 cameraLookUp; -uniform float correctionSizeFactor; -uniform float correctionSizeEndDistance; // Pixel size control: true uniform vec2 screenSize; uniform float maxBillboardSize; -uniform float minBillboardSize; - -const double PARSEC = 0.308567756e17LF; const vec2 corners[4] = vec2[4]( vec2(0.0, 0.0), @@ -71,30 +66,15 @@ const vec2 corners[4] = vec2[4]( const int RenderOptionCameraViewDirection = 0; const int RenderOptionCameraPositionNormal = 1; - void main() { - ta = 1.0; vec4 pos = gl_in[0].gl_Position; - gs_colorMap = colorMap[0]; + gs_colorParameter = colorParameter[0]; - double unit = PARSEC; + dvec4 dpos = modelMatrix * dvec4(dvec3(pos.xyz), 1.0); - // Must be the same as the enum in RenderableBillboardsCloud.h - if (pos.w == 1.0) { unit = 1E3; } - else if (pos.w == 2.0) { unit = PARSEC; } - else if (pos.w == 3.0) { unit = 1E3 * PARSEC; } - else if (pos.w == 4.0) { unit = 1E6 * PARSEC; } - else if (pos.w == 5.0) { unit = 1E9 * PARSEC; } - else if (pos.w == 6.0) { - // Conversion factor from Parsecs to GigalightYears - unit = 306391534.73091 * PARSEC; - } - - dvec4 dpos = modelMatrix * dvec4(dvec3(pos.xyz) * unit, 1.0); - - float scaleMultiply = exp(scaleFactor * 0.10); + float scaleMultiply = pow(10.0, scaleExponent); if (hasDvarScaling) { - scaleMultiply *= dvarScaling[0]; + scaleMultiply *= scalingParameter[0]; } vec3 scaledRight = vec3(0.0); @@ -109,18 +89,13 @@ void main() { vec3 newRight = normalize(cross(cameraLookUp, normal)); vec3 newUp = cross(normal, newRight); - if (!enabledRectSizeControl) { - double distCamera = length(cameraPosition - dpos.xyz); - float expVar = float(-distCamera) / pow(10.0, correctionSizeEndDistance); - float factorVar = pow(10.0, correctionSizeFactor); - scaleMultiply *= 1.0 / (1.0 + factorVar * exp(expVar)); - } - scaledRight = scaleMultiply * newRight * 0.5; scaledUp = scaleMultiply * newUp * 0.5; } - if (enabledRectSizeControl) { + // @TODO: Come up with some better solution for this scaling, that + // also work with non planar projections and multiple viewport resolutions. + if (enablePixelSizeControl) { vec4 initialPosition = z_normalization(vec4(cameraViewProjectionMatrix * dvec4(dpos.xyz - dvec3(scaledRight - scaledUp), dpos.w))); @@ -137,30 +112,21 @@ void main() { // width and height vec2 sizes = abs(halfViewSize * (topRight - bottomLeft)); - if (enabledRectSizeControl && (length(sizes) > maxBillboardSize)) { + if (length(sizes) > maxBillboardSize) { float correctionScale = maxBillboardSize / length(sizes); - scaledRight *= correctionScale; scaledUp *= correctionScale; } - else { - // linear alpha decay - if (sizes.x < 2.0 * minBillboardSize) { - float maxVar = 2.0 * minBillboardSize; - float minVar = minBillboardSize; - float var = sizes.y + sizes.x; - ta = (var - minVar) / (maxVar - minVar); - if (ta == 0.0) { - return; - } - } - } + + // TODO: add checks for wether the generated plane covers too many or too few pixels } // Saving one matrix multiplication: vec4 dposClip = vec4(cameraViewProjectionMatrix * dpos); - vec4 scaledRightClip = vec4(cameraViewProjectionMatrix * dvec4(scaledRight, 0.0)); - vec4 scaledUpClip = vec4(cameraViewProjectionMatrix * dvec4(scaledUp, 0.0)); + vec4 scaledRightClip = scaleFactor * + vec4(cameraViewProjectionMatrix * dvec4(scaledRight, 0.0)); + vec4 scaledUpClip = scaleFactor * + vec4(cameraViewProjectionMatrix * dvec4(scaledUp, 0.0)); vec4 initialPosition = z_normalization(dposClip - scaledRightClip - scaledUpClip); vs_screenSpaceDepth = initialPosition.w; diff --git a/modules/digitaluniverse/shaders/billboard_vs.glsl b/modules/base/shaders/billboardpoint_vs.glsl similarity index 91% rename from modules/digitaluniverse/shaders/billboard_vs.glsl rename to modules/base/shaders/billboardpoint_vs.glsl index b1f0510a4a..35e8082b41 100644 --- a/modules/digitaluniverse/shaders/billboard_vs.glsl +++ b/modules/base/shaders/billboardpoint_vs.glsl @@ -27,15 +27,14 @@ #include "PowerScaling/powerScaling_vs.hglsl" in vec4 in_position; -in vec4 in_colormap; -in float in_dvarScaling; - -flat out vec4 colorMap; -flat out float dvarScaling; +in float in_colorParameter; +in float in_scalingParameter; +flat out float colorParameter; +flat out float scalingParameter; void main() { - colorMap = in_colormap; - dvarScaling = in_dvarScaling; + colorParameter = in_colorParameter; + scalingParameter = in_scalingParameter; gl_Position = in_position; } diff --git a/modules/digitaluniverse/shaders/billboardpolygon_fs.glsl b/modules/base/shaders/polygon_fs.glsl similarity index 100% rename from modules/digitaluniverse/shaders/billboardpolygon_fs.glsl rename to modules/base/shaders/polygon_fs.glsl diff --git a/modules/digitaluniverse/shaders/billboardpolygon_gs.glsl b/modules/base/shaders/polygon_gs.glsl similarity index 100% rename from modules/digitaluniverse/shaders/billboardpolygon_gs.glsl rename to modules/base/shaders/polygon_gs.glsl diff --git a/modules/digitaluniverse/shaders/billboardpolygon_vs.glsl b/modules/base/shaders/polygon_vs.glsl similarity index 100% rename from modules/digitaluniverse/shaders/billboardpolygon_vs.glsl rename to modules/base/shaders/polygon_vs.glsl diff --git a/modules/digitaluniverse/CMakeLists.txt b/modules/digitaluniverse/CMakeLists.txt index 252b33d460..df3b0b2eeb 100644 --- a/modules/digitaluniverse/CMakeLists.txt +++ b/modules/digitaluniverse/CMakeLists.txt @@ -25,34 +25,22 @@ include(${PROJECT_SOURCE_DIR}/support/cmake/module_definition.cmake) set(HEADER_FILES - rendering/renderablepoints.h rendering/renderabledumeshes.h - rendering/renderablebillboardscloud.h rendering/renderableplanescloud.h ) source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES - rendering/renderablepoints.cpp rendering/renderabledumeshes.cpp - rendering/renderablebillboardscloud.cpp rendering/renderableplanescloud.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) set(SHADER_FILES - shaders/billboard_fs.glsl - shaders/billboard_gs.glsl - shaders/billboard_vs.glsl - shaders/billboardpolygon_fs.glsl - shaders/billboardpolygon_gs.glsl - shaders/billboardpolygon_vs.glsl shaders/dumesh_vs.glsl shaders/dumesh_fs.glsl shaders/plane_vs.glsl shaders/plane_fs.glsl - shaders/points_sprite_fs.glsl - shaders/points_vs.glsl ) source_group("Shader Files" FILES ${SHADER_FILES}) diff --git a/modules/digitaluniverse/digitaluniversemodule.cpp b/modules/digitaluniverse/digitaluniversemodule.cpp index b77708080b..92cb71ac1e 100644 --- a/modules/digitaluniverse/digitaluniversemodule.cpp +++ b/modules/digitaluniverse/digitaluniversemodule.cpp @@ -24,10 +24,8 @@ #include -#include #include #include -#include #include #include #include @@ -48,8 +46,6 @@ void DigitalUniverseModule::internalInitialize(const ghoul::Dictionary&) { FactoryManager::ref().factory(); ghoul_assert(fRenderable, "Renderable factory was not created"); - fRenderable->registerClass("RenderablePoints"); - fRenderable->registerClass("RenderableBillboardsCloud"); fRenderable->registerClass("RenderablePlanesCloud"); fRenderable->registerClass("RenderableDUMeshes"); } @@ -61,8 +57,6 @@ void DigitalUniverseModule::internalDeinitializeGL() { std::vector DigitalUniverseModule::documentations() const { return { - RenderablePoints::Documentation(), - RenderableBillboardsCloud::Documentation(), RenderablePlanesCloud::Documentation(), RenderableDUMeshes::Documentation() }; diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp deleted file mode 100644 index 3f04668c98..0000000000 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp +++ /dev/null @@ -1,1142 +0,0 @@ -/***************************************************************************************** - * * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - constexpr std::string_view _loggerCat = "RenderableBillboardsCloud"; - - constexpr std::array UniformNames = { - "cameraViewProjectionMatrix", "modelMatrix", "cameraPosition", "cameraLookUp", - "renderOption", "minBillboardSize", "maxBillboardSize", - "correctionSizeEndDistance", "correctionSizeFactor", "color", "alphaValue", - "scaleFactor", "up", "right", "fadeInValue", "screenSize", "spriteTexture", - "hasColorMap", "useColorMap", "enabledRectSizeControl", "hasDvarScaling" - }; - - enum RenderOption { - ViewDirection = 0, - PositionNormal - }; - - constexpr openspace::properties::Property::PropertyInfo SpriteTextureInfo = { - "Texture", - "Point Sprite Texture", - "The path to the texture that should be used as the point sprite", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo ScaleFactorInfo = { - "ScaleFactor", - "Scale Factor", - "This value is used as a multiplicative factor that is applied to the apparent " - "size of each point", - // @VISIBILITY(2.5) - openspace::properties::Property::Visibility::User - }; - - constexpr openspace::properties::Property::PropertyInfo UseColorMapInfo = { - "UseColorMap", - "Use Color Map", - "If this value is set to 'true', the provided color map is used (if one was " - "provided in the configuration). If no color map was provided, changing this " - "setting does not do anything", - // @VISIBILITY(2.75) - openspace::properties::Property::Visibility::User - }; - - constexpr openspace::properties::Property::PropertyInfo ColorInfo = { - "Color", - "Color", - "This value is used to define the color of the astronomical object", - // @VISIBILITY(1.5) - openspace::properties::Property::Visibility::NoviceUser - }; - - constexpr openspace::properties::Property::PropertyInfo ColorMapInfo = { - "ColorMap", - "Color Map File", - "The path to the color map file of the astronomical object", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo DrawElementsInfo = { - "DrawElements", - "Draw Elements", - "Enables/Disables the drawing of the astronomical objects", - // @VISIBILITY(1.25) - openspace::properties::Property::Visibility::NoviceUser - }; - - static const openspace::properties::PropertyOwner::PropertyOwnerInfo LabelsInfo = { - "Labels", - "Labels", - "The labels for the astronomical objects" - }; - - constexpr openspace::properties::Property::PropertyInfo ColorOptionInfo = { - "ColorOption", - "Color Option", - "This value determines which paramenter is used for default color of the " - "astronomical objects", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo OptionColorRangeInfo = { - "OptionColorRange", - "Option Color Range", - "This value changes the range of values to be mapped with the current color map", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo SizeOptionInfo = { - "SizeOption", - "Size Option Variable", - "This value determines which paramenter (datavar) is used for scaling of the " - "astronomical objects", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo RenderOptionInfo = { - "RenderOption", - "Render Option", - "Option wether the billboards should face the camera or not. Used for non-linear " - "display environments such as fisheye.", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo FadeInDistancesInfo = { - "FadeInDistances", - "Fade-In Start and End Distances", - "These values determine the initial and final distances from the center of " - "our galaxy from which the astronomical object will start and end " - "fading-in", - // @VISIBILITY(3.25) - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo DisableFadeInInfo = { - "DisableFadeIn", - "Disable Fade-in Effect", - "Enables/Disables the Fade-in effect", - openspace::properties::Property::Visibility::User - }; - - constexpr openspace::properties::Property::PropertyInfo PixelSizeControlInfo = { - "EnablePixelSizeControl", - "Enable Pixel Size Control", - "Enable pixel size control for rectangular projections. If set to true, the " - "billboard size is restricted by the min/max size in pixels property", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo BillboardMinMaxSizeInfo = { - "BillboardMinMaxSize", - "Billboard Min/Max Size in Pixels", - "The minimum and maximum size (in pixels) for the billboard representing the " - "astronomical object", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo - CorrectionSizeEndDistanceInfo = - { - "CorrectionSizeEndDistance", - "Distance in 10^X meters where correction size stops acting", - "Distance in 10^X meters where correction size stops acting", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo CorrectionSizeFactorInfo = { - "CorrectionSizeFactor", - "Control variable for distance size", - "", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo UseLinearFiltering = { - "UseLinearFiltering", - "Use Linear Filtering", - "Determines whether the provided color map should be sampled nearest neighbor " - "(=off) or linearly (=on)", - // @VISIBILITY(3.25) - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo SetRangeFromData = { - "SetRangeFromData", - "Set Data Range from Data", - "Set the data range based on the available data", - openspace::properties::Property::Visibility::AdvancedUser - }; - - struct [[codegen::Dictionary(RenderableBillboardsCloud)]] Parameters { - // The path to the SPECK file that contains information about the astronomical - // object being rendered - std::optional file; - - // [[codegen::verbatim(ColorInfo.description)]] - glm::vec3 color [[codegen::color()]]; - - // [[codegen::verbatim(SpriteTextureInfo.description)]] - std::optional texture; - - // [[codegen::verbatim(DrawElementsInfo.description)]] - std::optional drawElements; - - enum class [[codegen::map(RenderOption)]] RenderOption { - ViewDirection [[codegen::key("Camera View Direction")]], - PositionNormal [[codegen::key("Camera Position Normal")]] - }; - // [[codegen::verbatim(RenderOptionInfo.description)]] - std::optional renderOption; - - enum class [[codegen::map(openspace::DistanceUnit)]] Unit { - Meter [[codegen::key("m")]], - Kilometer [[codegen::key("Km")]], - Parsec [[codegen::key("pc")]], - Kiloparsec [[codegen::key("Kpc")]], - Megaparsec [[codegen::key("Mpc")]], - Gigaparsec [[codegen::key("Gpc")]], - Gigalightyear [[codegen::key("Gly")]] - }; - // The unit used for all distances. Must match the unit of any - // distances/positions in the data files - std::optional unit; - - // [[codegen::verbatim(ScaleFactorInfo.description)]] - std::optional scaleFactor; - - // [[codegen::verbatim(UseColorMapInfo.description)]] - std::optional useColorMap; - - // [[codegen::verbatim(ColorMapInfo.description)]] - std::optional colorMap; - - // Set a 1 to 1 relationship between the color index variable and the colormap - // entrered value - std::optional exactColorMap; - - // The number of sides for the polygon used to represent the astronomical object - std::optional polygonSides; - - // [[codegen::verbatim(LabelsInfo.description)]] - std::optional labels - [[codegen::reference("space_labelscomponent")]]; - - // [[codegen::verbatim(ColorOptionInfo.description)]] - std::optional> colorOption; - - // [[codegen::verbatim(SizeOptionInfo.description)]] - std::optional> sizeOption; - - // This value determines the colormap ranges for the color parameters of the - // astronomical objects - std::optional> colorRange; - - // Transformation matrix to be applied to each astronomical object - std::optional transformationMatrix; - - // [[codegen::verbatim(FadeInDistancesInfo.description)]] - std::optional fadeInDistances; - - // [[codegen::verbatim(DisableFadeInInfo.description)]] - std::optional disableFadeIn; - - // [[codegen::verbatim(BillboardMinMaxSizeInfo.description)]] - std::optional billboardMinMaxSize; - - // [[codegen::verbatim(CorrectionSizeEndDistanceInfo.description)]] - std::optional correctionSizeEndDistance; - - // [[codegen::verbatim(CorrectionSizeFactorInfo.description)]] - std::optional correctionSizeFactor; - - // [[codegen::verbatim(PixelSizeControlInfo.description)]] - std::optional enablePixelSizeControl; - - // [[codegen::verbatim(UseLinearFiltering.description)]] - std::optional useLinearFiltering; - }; -#include "renderablebillboardscloud_codegen.cpp" -} // namespace - -namespace openspace { - -documentation::Documentation RenderableBillboardsCloud::Documentation() { - return codegen::doc("digitaluniverse_RenderableBillboardsCloud"); -} - -RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& dictionary) - : Renderable(dictionary) - , _scaleFactor(ScaleFactorInfo, 1.f, 0.f, 600.f) - , _useColorMap(UseColorMapInfo, true) - , _pointColor(ColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f)) - , _spriteTexturePath(SpriteTextureInfo) - , _drawElements(DrawElementsInfo, true) - , _pixelSizeControl(PixelSizeControlInfo, false) - , _colorOption(ColorOptionInfo, properties::OptionProperty::DisplayType::Dropdown) - , _optionColorRangeData(OptionColorRangeInfo, glm::vec2(0.f)) - , _datavarSizeOption( - SizeOptionInfo, - properties::OptionProperty::DisplayType::Dropdown - ) - , _fadeInDistances( - FadeInDistancesInfo, - glm::vec2(0.f), - glm::vec2(0.f), - glm::vec2(100.f) - ) - , _disableFadeInDistance(DisableFadeInInfo, true) - , _billboardMinMaxSize( - BillboardMinMaxSizeInfo, - glm::vec2(0.f, 400.f), - glm::vec2(0.f), - glm::vec2(1000.f) - ) - , _correctionSizeEndDistance(CorrectionSizeEndDistanceInfo, 17.f, 12.f, 25.f) - , _correctionSizeFactor(CorrectionSizeFactorInfo, 8.f, 0.f, 20.f) - , _useLinearFiltering(UseLinearFiltering, false) - , _setRangeFromData(SetRangeFromData) - , _renderOption(RenderOptionInfo, properties::OptionProperty::DisplayType::Dropdown) -{ - const Parameters p = codegen::bake(dictionary); - - if (p.file.has_value()) { - _speckFile = absPath(*p.file).string(); - } - _hasSpeckFile = p.file.has_value(); - - _useColorMap = p.useColorMap.value_or(_useColorMap); - - _drawElements = p.drawElements.value_or(_drawElements); - _drawElements.onChange([this]() { _hasSpeckFile = !_hasSpeckFile; }); - addProperty(_drawElements); - - _renderOption.addOption(RenderOption::ViewDirection, "Camera View Direction"); - _renderOption.addOption(RenderOption::PositionNormal, "Camera Position Normal"); - - if (p.renderOption.has_value()) { - _renderOption = codegen::map(*p.renderOption); - } - else { - _renderOption = RenderOption::ViewDirection; - } - addProperty(_renderOption); - - if (p.unit.has_value()) { - _unit = codegen::map(*p.unit); - } - else { - _unit = DistanceUnit::Meter; - } - - if (p.texture.has_value()) { - _spriteTexturePath = absPath(*p.texture).string(); - _spriteTexturePath.onChange([this]() { _spriteTextureIsDirty = true; }); - - // @TODO (abock, 2021-01-31) I don't know why we only add this property if the - // texture is given, but I think it's a bug - // @TODO (emmbr, 2021-05-24) This goes for several properties in this renderable - addProperty(_spriteTexturePath); - } - _hasSpriteTexture = p.texture.has_value(); - - if (p.colorMap.has_value()) { - _colorMapFile = absPath(*p.colorMap).string(); - _hasColorMapFile = true; - - if (p.colorOption.has_value()) { - std::vector opts = *p.colorOption; - for (size_t i = 0; i < opts.size(); ++i) { - _colorOption.addOption(static_cast(i), opts[i]); - _optionConversionMap.insert({ static_cast(i), opts[i] }); - _colorOptionString = opts[i]; - } - } - _colorOption.onChange([this]() { - _dataIsDirty = true; - const glm::vec2 colorRange = _colorRangeData[_colorOption.value()]; - _optionColorRangeData = colorRange; - _colorOptionString = _optionConversionMap[_colorOption.value()]; - }); - addProperty(_colorOption); - - _colorRangeData = p.colorRange.value_or(_colorRangeData); - if (!_colorRangeData.empty()) { - _optionColorRangeData = _colorRangeData[_colorRangeData.size() - 1]; - } - _optionColorRangeData.onChange([this]() { - const glm::vec2 colorRange = _optionColorRangeData; - _colorRangeData[_colorOption.value()] = colorRange; - _dataIsDirty = true; - }); - addProperty(_optionColorRangeData); - - _isColorMapExact = p.exactColorMap.value_or(_isColorMapExact); - } - - _pointColor = p.color; - _pointColor.setViewOption(properties::Property::ViewOptions::Color); - addProperty(_pointColor); - - addProperty(Fadeable::_opacity); - - _scaleFactor = p.scaleFactor.value_or(_scaleFactor); - addProperty(_scaleFactor); - - if (p.sizeOption.has_value()) { - std::vector opts = *p.sizeOption; - for (size_t i = 0; i < opts.size(); ++i) { - _datavarSizeOption.addOption(static_cast(i), opts[i]); - _optionConversionSizeMap.insert({ static_cast(i), opts[i] }); - _datavarSizeOptionString = opts[i]; - } - - _datavarSizeOption.onChange([this]() { - _dataIsDirty = true; - _datavarSizeOptionString = _optionConversionSizeMap[_datavarSizeOption]; - }); - addProperty(_datavarSizeOption); - - _hasDatavarSize = true; - } - - _polygonSides = p.polygonSides.value_or(_polygonSides); - _hasPolygon = p.polygonSides.has_value(); - - if (p.labels.has_value()) { - _labels = std::make_unique(*p.labels); - _hasLabels = true; - addPropertySubOwner(_labels.get()); - // Fading of the labels should also depend on the fading of the renderable - _labels->setParentFadeable(this); - } - - _transformationMatrix = p.transformationMatrix.value_or(_transformationMatrix); - - if (p.fadeInDistances.has_value()) { - _fadeInDistances = *p.fadeInDistances; - _fadeInDistances.setViewOption(properties::Property::ViewOptions::MinMaxRange); - addProperty(_fadeInDistances); - - _disableFadeInDistance = false; - addProperty(_disableFadeInDistance); - } - - _pixelSizeControl = p.enablePixelSizeControl.value_or(_pixelSizeControl); - addProperty(_pixelSizeControl); - - _billboardMinMaxSize = p.billboardMinMaxSize.value_or(_billboardMinMaxSize); - _billboardMinMaxSize.setViewOption(properties::Property::ViewOptions::MinMaxRange); - addProperty(_billboardMinMaxSize); - - _correctionSizeEndDistance = - p.correctionSizeEndDistance.value_or(_correctionSizeEndDistance); - addProperty(_correctionSizeEndDistance); - - _correctionSizeFactor = p.correctionSizeFactor.value_or(_correctionSizeFactor); - if (p.correctionSizeFactor.has_value()) { - addProperty(_correctionSizeFactor); - } - - _setRangeFromData.onChange([this]() { - const int colorMapInUse = - _hasColorMapFile ? _dataset.index(_colorOptionString) : 0; - - float minValue = std::numeric_limits::max(); - float maxValue = -std::numeric_limits::max(); - for (const speck::Dataset::Entry& e : _dataset.entries) { - float color = e.data[colorMapInUse]; - minValue = std::min(minValue, color); - maxValue = std::max(maxValue, color); - } - - _optionColorRangeData = glm::vec2(minValue, maxValue); - }); - addProperty(_setRangeFromData); - - _useColorMap.onChange([this]() { _dataIsDirty = true; }); - addProperty(_useColorMap); - - _useLinearFiltering = p.useLinearFiltering.value_or(_useLinearFiltering); - _useLinearFiltering.onChange([this]() { _dataIsDirty = true; }); - addProperty(_useLinearFiltering); -} - -bool RenderableBillboardsCloud::isReady() const { - bool isReady = _program && !_dataset.entries.empty(); - - // If we have labels, they also need to be loaded - if (_hasLabels) { - isReady = isReady || _labels->isReady(); - } - return isReady; -} - -void RenderableBillboardsCloud::initialize() { - ZoneScoped; - - if (_hasSpeckFile) { - _dataset = speck::data::loadFileWithCache(_speckFile); - } - - if (_hasColorMapFile) { - _colorMap = speck::color::loadFileWithCache(_colorMapFile); - } - - if (_hasLabels) { - _labels->initialize(); - _labels->loadLabels(); - } - - if (!_colorOptionString.empty() && (_colorRangeData.size() > 1)) { - // Following DU behavior here. The last colormap variable - // entry is the one selected by default. - _colorOption.setValue(static_cast(_colorRangeData.size() - 1)); - } - - setRenderBin(Renderable::RenderBin::PreDeferredTransparent); -} - -void RenderableBillboardsCloud::initializeGL() { - ZoneScoped; - - _program = DigitalUniverseModule::ProgramObjectManager.request( - "RenderableBillboardsCloud", - []() { - return global::renderEngine->buildRenderProgram( - "RenderableBillboardsCloud", - absPath("${MODULE_DIGITALUNIVERSE}/shaders/billboard_vs.glsl"), - absPath("${MODULE_DIGITALUNIVERSE}/shaders/billboard_fs.glsl"), - absPath("${MODULE_DIGITALUNIVERSE}/shaders/billboard_gs.glsl") - ); - } - ); - - _renderToPolygonProgram = DigitalUniverseModule::ProgramObjectManager.request( - "RenderableBillboardsCloud_Polygon", - []() { - return ghoul::opengl::ProgramObject::Build( - "RenderableBillboardsCloud_Polygon", - absPath("${MODULE_DIGITALUNIVERSE}/shaders/billboardpolygon_vs.glsl"), - absPath("${MODULE_DIGITALUNIVERSE}/shaders/billboardpolygon_fs.glsl"), - absPath("${MODULE_DIGITALUNIVERSE}/shaders/billboardpolygon_gs.glsl") - ); - } - ); - - ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); - - if (_hasPolygon) { - createPolygonTexture(); - } -} - -void RenderableBillboardsCloud::deinitializeGL() { - glDeleteBuffers(1, &_vbo); - _vbo = 0; - glDeleteVertexArrays(1, &_vao); - _vao = 0; - - DigitalUniverseModule::ProgramObjectManager.release( - "RenderableBillboardsCloud", - [](ghoul::opengl::ProgramObject* p) { - global::renderEngine->removeRenderProgram(p); - } - ); - _program = nullptr; - - DigitalUniverseModule::ProgramObjectManager.release( - "RenderableBillboardsCloud_Polygon" - ); - _renderToPolygonProgram = nullptr; - - DigitalUniverseModule::TextureManager.release(_spriteTexture); - _spriteTexture = nullptr; - - if (_hasPolygon) { - _polygonTexture = nullptr; - glDeleteTextures(1, &_pTexture); - } -} - -void RenderableBillboardsCloud::renderBillboards(const RenderData& data, - const glm::dmat4& modelMatrix, - const glm::dvec3& orthoRight, - const glm::dvec3& orthoUp, - float fadeInVariable) -{ - glDepthMask(false); - - glEnablei(GL_BLEND, 0); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - - _program->activate(); - - _program->setUniform( - "screenSize", - glm::vec2(global::renderEngine->renderingResolution()) - ); - - _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.cameraViewProjectionMatrix, - glm::dmat4(data.camera.projectionMatrix()) * data.camera.combinedViewMatrix() - ); - - const float minBillboardSize = _billboardMinMaxSize.value().x; // in pixels - const float maxBillboardSize = _billboardMinMaxSize.value().y; // in pixels - _program->setUniform(_uniformCache.minBillboardSize, minBillboardSize); - _program->setUniform(_uniformCache.maxBillboardSize, maxBillboardSize); - _program->setUniform(_uniformCache.color, _pointColor); - _program->setUniform(_uniformCache.alphaValue, opacity()); - _program->setUniform(_uniformCache.scaleFactor, _scaleFactor); - _program->setUniform(_uniformCache.up, glm::vec3(orthoUp)); - _program->setUniform(_uniformCache.right, glm::vec3(orthoRight)); - _program->setUniform(_uniformCache.fadeInValue, fadeInVariable); - - _program->setUniform( - _uniformCache.correctionSizeEndDistance, - _correctionSizeEndDistance - ); - _program->setUniform(_uniformCache.correctionSizeFactor, _correctionSizeFactor); - - _program->setUniform(_uniformCache.enabledRectSizeControl, _pixelSizeControl); - - _program->setUniform(_uniformCache.hasDvarScaling, _hasDatavarSize); - - GLint viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - _program->setUniform(_uniformCache.screenSize, glm::vec2(viewport[2], viewport[3])); - - ghoul::opengl::TextureUnit textureUnit; - textureUnit.activate(); - if (_hasPolygon) { - glBindTexture(GL_TEXTURE_2D, _pTexture); - } - else if (_spriteTexture) { - _spriteTexture->bind(); - } - _program->setUniform(_uniformCache.spriteTexture, textureUnit); - _program->setUniform(_uniformCache.hasColormap, _hasColorMapFile); - _program->setUniform(_uniformCache.useColormap, _useColorMap); - - glBindVertexArray(_vao); - glDrawArrays(GL_POINTS, 0, static_cast(_dataset.entries.size())); - glBindVertexArray(0); - _program->deactivate(); - - global::renderEngine->openglStateCache().resetBlendState(); - global::renderEngine->openglStateCache().resetDepthState(); -} - -void RenderableBillboardsCloud::render(const RenderData& data, RendererTasks&) { - float fadeInVar = 1.f; - if (!_disableFadeInDistance) { - float distCamera = static_cast(glm::length(data.camera.positionVec3())); - const glm::vec2 fadeRange = _fadeInDistances; - const float a = static_cast( - 1.f / ((fadeRange.y - fadeRange.x) * toMeter(_unit)) - ); - const float b = -(fadeRange.x / (fadeRange.y - fadeRange.x)); - const float funcValue = a * distCamera + b; - fadeInVar *= funcValue > 1.f ? 1.f : funcValue; - - if (funcValue < 0.01f) { - return; - } - } - - glm::dmat4 modelMatrix = calcModelTransform(data); - glm::dmat4 modelViewProjectionMatrix = - calcModelViewProjectionTransform(data, modelMatrix); - - glm::dvec3 cameraViewDirectionWorld = -data.camera.viewDirectionWorldSpace(); - glm::dvec3 cameraUpDirectionWorld = data.camera.lookUpVectorWorldSpace(); - glm::dvec3 orthoRight = glm::normalize( - glm::cross(cameraUpDirectionWorld, cameraViewDirectionWorld) - ); - if (orthoRight == glm::dvec3(0.0)) { - glm::dvec3 otherVector = glm::vec3( - cameraUpDirectionWorld.y, - cameraUpDirectionWorld.x, - cameraUpDirectionWorld.z - ); - orthoRight = glm::normalize(glm::cross(otherVector, cameraViewDirectionWorld)); - } - glm::dvec3 orthoUp = glm::normalize(glm::cross(cameraViewDirectionWorld, orthoRight)); - - if (_hasSpeckFile && _drawElements) { - renderBillboards(data, modelMatrix, orthoRight, orthoUp, fadeInVar); - } - - if (_hasLabels) { - _labels->render(data, modelViewProjectionMatrix, orthoRight, orthoUp, fadeInVar); - } -} - -void RenderableBillboardsCloud::update(const UpdateData&) { - ZoneScoped; - - if (_dataIsDirty && _hasSpeckFile) { - ZoneScopedN("Data dirty"); - TracyGpuZone("Data dirty"); - LDEBUG("Regenerating data"); - - std::vector slice = createDataSlice(); - - int size = static_cast(slice.size()); - - 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)); - } - - glBindVertexArray(_vao); - glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferData(GL_ARRAY_BUFFER, size * sizeof(float), slice.data(), GL_STATIC_DRAW); - GLint positionAttrib = _program->attributeLocation("in_position"); - - if (_hasColorMapFile && _hasDatavarSize) { - glEnableVertexAttribArray(positionAttrib); - glVertexAttribPointer( - positionAttrib, - 4, - GL_FLOAT, - GL_FALSE, - 9 * sizeof(float), - nullptr - ); - - GLint colorMapAttrib = _program->attributeLocation("in_colormap"); - glEnableVertexAttribArray(colorMapAttrib); - glVertexAttribPointer( - colorMapAttrib, - 4, - GL_FLOAT, - GL_FALSE, - 9 * sizeof(float), - reinterpret_cast(4 * sizeof(float)) - ); - - GLint dvarScalingAttrib = _program->attributeLocation("in_dvarScaling"); - glEnableVertexAttribArray(dvarScalingAttrib); - glVertexAttribPointer( - dvarScalingAttrib, - 1, - GL_FLOAT, - GL_FALSE, - 9 * sizeof(float), - reinterpret_cast(8 * sizeof(float)) - ); - } - else if (_hasColorMapFile) { - glEnableVertexAttribArray(positionAttrib); - glVertexAttribPointer( - positionAttrib, - 4, - GL_FLOAT, - GL_FALSE, - 8 * sizeof(float), - nullptr - ); - - GLint colorMapAttrib = _program->attributeLocation("in_colormap"); - glEnableVertexAttribArray(colorMapAttrib); - glVertexAttribPointer( - colorMapAttrib, - 4, - GL_FLOAT, - GL_FALSE, - 8 * sizeof(float), - reinterpret_cast(4 * sizeof(float)) - ); - } - else if (_hasDatavarSize) { - glEnableVertexAttribArray(positionAttrib); - glVertexAttribPointer( - positionAttrib, - 4, - GL_FLOAT, - GL_FALSE, - 8 * sizeof(float), - nullptr - ); - - GLint dvarScalingAttrib = _program->attributeLocation("in_dvarScaling"); - glEnableVertexAttribArray(dvarScalingAttrib); - glVertexAttribPointer( - dvarScalingAttrib, - 1, - GL_FLOAT, - GL_FALSE, - 5 * sizeof(float), - reinterpret_cast(4 * sizeof(float)) - ); - } - else { - glEnableVertexAttribArray(positionAttrib); - glVertexAttribPointer( - positionAttrib, - 4, - GL_FLOAT, - GL_FALSE, - 0, - nullptr - ); - } - - glBindVertexArray(0); - - _dataIsDirty = false; - } - - if (_hasSpriteTexture && _spriteTextureIsDirty && !_spriteTexturePath.value().empty()) - { - ZoneScopedN("Sprite texture"); - TracyGpuZone("Sprite texture"); - - ghoul::opengl::Texture* texture = _spriteTexture; - - unsigned int hash = ghoul::hashCRC32File(_spriteTexturePath); - - _spriteTexture = DigitalUniverseModule::TextureManager.request( - std::to_string(hash), - [path = _spriteTexturePath]() -> std::unique_ptr { - std::filesystem::path p = absPath(path); - LINFO(fmt::format("Loaded texture from {}", p)); - std::unique_ptr t = - ghoul::io::TextureReader::ref().loadTexture(p.string(), 2); - t->uploadTexture(); - t->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - t->purgeFromRAM(); - return t; - } - ); - - DigitalUniverseModule::TextureManager.release(texture); - _spriteTextureIsDirty = false; - } -} - -std::vector RenderableBillboardsCloud::createDataSlice() { - ZoneScoped; - - if (_dataset.entries.empty()) { - return std::vector(); - } - - std::vector result; - if (_hasColorMapFile) { - result.reserve(8 * _dataset.entries.size()); - } - else { - result.reserve(4 * _dataset.entries.size()); - } - - // what datavar in use for the index color - int colorMapInUse = - _hasColorMapFile ? _dataset.index(_colorOptionString) : 0; - - // what datavar in use for the size scaling (if present) - int sizeScalingInUse = - _hasDatavarSize ? _dataset.index(_datavarSizeOptionString) : -1; - - float minColorIdx = std::numeric_limits::max(); - float maxColorIdx = -std::numeric_limits::max(); - for (const speck::Dataset::Entry& e : _dataset.entries) { - if (e.data.size() > 0) { - float color = e.data[colorMapInUse]; - minColorIdx = std::min(color, minColorIdx); - maxColorIdx = std::max(color, maxColorIdx); - } else { - minColorIdx = 0; - maxColorIdx = 0; - } - } - - double maxRadius = 0.0; - - float biggestCoord = -1.f; - for (const speck::Dataset::Entry& e : _dataset.entries) { - glm::vec3 transformedPos = glm::vec3(_transformationMatrix * glm::vec4( - e.position, 1.0 - )); - - float unitValue = 0.f; - // (abock, 2022-01-02) This is vestigial from a previous rewrite. I just want to - // make it work for now and we can rewrite it properly later - switch (_unit) { - case DistanceUnit::Meter: - unitValue = 0.f; - break; - case DistanceUnit::Kilometer: - unitValue = 1.f; - break; - case DistanceUnit::Parsec: - unitValue = 2; - break; - case DistanceUnit::Kiloparsec: - unitValue = 3; - break; - case DistanceUnit::Megaparsec: - unitValue = 4; - break; - case DistanceUnit::Gigaparsec: - unitValue = 5; - break; - case DistanceUnit::Gigalightyear: - unitValue = 6; - break; - default: - throw ghoul::MissingCaseException(); - } - - glm::vec4 position(transformedPos, unitValue); - - const double unitMeter = toMeter(_unit); - glm::dvec3 p = glm::dvec3(position) * unitMeter; - const double r = glm::length(p); - maxRadius = std::max(maxRadius, r); - - if (_hasColorMapFile && _useColorMap && !_colorMap.entries.empty()) { - for (int j = 0; j < 4; ++j) { - result.push_back(position[j]); - } - biggestCoord = std::max(biggestCoord, glm::compMax(position)); - // Note: if exact colormap option is not selected, the first color and the - // last color in the colormap file are the outliers colors. - float variableColor = e.data[colorMapInUse]; - - float cmax, cmin; - if (_colorRangeData.empty()) { - cmax = maxColorIdx; // Max value of datavar used for the index color - cmin = minColorIdx; // Min value of datavar used for the index color - } - else { - glm::vec2 currentColorRange = _colorRangeData[_colorOption.value()]; - cmax = currentColorRange.y; - cmin = currentColorRange.x; - } - - if (_isColorMapExact) { - int colorIndex = static_cast(variableColor + cmin); - for (int j = 0; j < 4; ++j) { - result.push_back(_colorMap.entries[colorIndex][j]); - } - } - else { - if (_useLinearFiltering) { - float valueT = (variableColor - cmin) / (cmax - cmin); // in [0, 1) - valueT = std::clamp(valueT, 0.f, 1.f); - - const float idx = valueT * (_colorMap.entries.size() - 1); - const int floorIdx = static_cast(std::floor(idx)); - const int ceilIdx = static_cast(std::ceil(idx)); - - const glm::vec4 floorColor = _colorMap.entries[floorIdx]; - const glm::vec4 ceilColor = _colorMap.entries[ceilIdx]; - - if (floorColor != ceilColor) { - const glm::vec4 c = floorColor + idx * (ceilColor - floorColor); - result.push_back(c.r); - result.push_back(c.g); - result.push_back(c.b); - result.push_back(c.a); - } - else { - result.push_back(floorColor.r); - result.push_back(floorColor.g); - result.push_back(floorColor.b); - result.push_back(floorColor.a); - } - } - else { - float ncmap = static_cast(_colorMap.entries.size()); - float normalization = ((cmax != cmin) && (ncmap > 2.f)) ? - (ncmap - 2.f) / (cmax - cmin) : 0; - int colorIndex = static_cast( - (variableColor - cmin) * normalization + 1.f - ); - colorIndex = colorIndex < 0 ? 0 : colorIndex; - colorIndex = colorIndex >= ncmap ? - static_cast(ncmap - 1.f) : colorIndex; - - for (int j = 0; j < 4; ++j) { - result.push_back(_colorMap.entries[colorIndex][j]); - } - } - } - - if (_hasDatavarSize) { - result.push_back(e.data[sizeScalingInUse]); - } - } - else if (_hasDatavarSize) { - result.push_back(e.data[sizeScalingInUse]); - for (int j = 0; j < 4; ++j) { - result.push_back(position[j]); - } - } - else { - for (int j = 0; j < 4; ++j) { - result.push_back(position[j]); - } - } - } - setBoundingSphere(maxRadius); - _fadeInDistances.setMaxValue(glm::vec2(10.f * biggestCoord)); - return result; -} - -void RenderableBillboardsCloud::createPolygonTexture() { - ZoneScoped; - - LDEBUG("Creating Polygon Texture"); - - glGenTextures(1, &_pTexture); - glBindTexture(GL_TEXTURE_2D, _pTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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); - // Stopped using a buffer object for GL_PIXEL_UNPACK_BUFFER - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_BYTE, nullptr); - - renderToTexture(_pTexture, 256, 256); -} - -void RenderableBillboardsCloud::renderToTexture(GLuint textureToRenderTo, - GLuint textureWidth, GLuint textureHeight) -{ - LDEBUG("Rendering to Texture"); - - // Saves initial Application's OpenGL State - GLint defaultFBO; - GLint viewport[4]; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); - glGetIntegerv(GL_VIEWPORT, viewport); - - GLuint textureFBO; - glGenFramebuffers(1, &textureFBO); - glBindFramebuffer(GL_FRAMEBUFFER, textureFBO); - GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 }; - glDrawBuffers(1, drawBuffers); - - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureToRenderTo, 0); - - glViewport(viewport[0], viewport[1], textureWidth, textureHeight); - - loadPolygonGeometryForRendering(); - renderPolygonGeometry(_polygonVao); - - // Restores Applications' OpenGL State - glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); - glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); - - glDeleteBuffers(1, &_polygonVbo); - glDeleteVertexArrays(1, &_polygonVao); - glDeleteFramebuffers(1, &textureFBO); -} - -void RenderableBillboardsCloud::loadPolygonGeometryForRendering() { - glGenVertexArrays(1, &_polygonVao); - glGenBuffers(1, &_polygonVbo); - glBindVertexArray(_polygonVao); - glBindBuffer(GL_ARRAY_BUFFER, _polygonVbo); - - constexpr std::array VertexData = { - // x y z w - 0.f, 0.f, 0.f, 1.f, - }; - - glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData), VertexData.data(), GL_STATIC_DRAW); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr); - glEnableVertexAttribArray(0); - glBindVertexArray(0); -} - -void RenderableBillboardsCloud::renderPolygonGeometry(GLuint vao) { - std::unique_ptr program = - ghoul::opengl::ProgramObject::Build( - "RenderableBillboardsCloud_Polygon", - absPath("${MODULE_DIGITALUNIVERSE}/shaders/billboardpolygon_vs.glsl"), - absPath("${MODULE_DIGITALUNIVERSE}/shaders/billboardpolygon_fs.glsl"), - absPath("${MODULE_DIGITALUNIVERSE}/shaders/billboardpolygon_gs.glsl") - ); - - program->activate(); - constexpr glm::vec4 Black = glm::vec4(0.f, 0.f, 0.f, 0.f); - glClearBufferfv(GL_COLOR, 0, glm::value_ptr(Black)); - - program->setUniform("sides", _polygonSides); - program->setUniform("polygonColor", _pointColor); - - glBindVertexArray(vao); - glDrawArrays(GL_POINTS, 0, 1); - glBindVertexArray(0); - - program->deactivate(); -} - -} // namespace openspace diff --git a/modules/digitaluniverse/rendering/renderabledumeshes.cpp b/modules/digitaluniverse/rendering/renderabledumeshes.cpp index 96b4f4fd39..02baefd477 100644 --- a/modules/digitaluniverse/rendering/renderabledumeshes.cpp +++ b/modules/digitaluniverse/rendering/renderabledumeshes.cpp @@ -385,7 +385,7 @@ void RenderableDUMeshes::renderLabels(const RenderData& data, glm::vec4 textColor = glm::vec4(glm::vec3(_textColor), _textOpacity); - for (const speck::Labelset::Entry& e : _labelset.entries) { + for (const dataloader::Labelset::Entry& e : _labelset.entries) { glm::vec3 scaledPos(e.position); scaledPos *= scale; ghoul::fontrendering::FontRenderer::defaultProjectionRenderer().render( @@ -457,7 +457,7 @@ bool RenderableDUMeshes::loadData() { std::string labelFile = _labelFile; if (!labelFile.empty()) { - _labelset = speck::label::loadFileWithCache(_labelFile); + _labelset = dataloader::label::loadFileWithCache(_labelFile); } return success; diff --git a/modules/digitaluniverse/rendering/renderabledumeshes.h b/modules/digitaluniverse/rendering/renderabledumeshes.h index 472a413bd4..c4312fa18a 100644 --- a/modules/digitaluniverse/rendering/renderabledumeshes.h +++ b/modules/digitaluniverse/rendering/renderabledumeshes.h @@ -27,7 +27,7 @@ #include -#include +#include #include #include #include @@ -130,7 +130,7 @@ private: DistanceUnit _unit = DistanceUnit::Parsec; std::vector _fullData; - speck::Labelset _labelset; + dataloader::Labelset _labelset; std::unordered_map _meshColorMap; std::unordered_map _renderingMeshesMap; diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.cpp b/modules/digitaluniverse/rendering/renderableplanescloud.cpp index fe16144353..ce8ce811d3 100644 --- a/modules/digitaluniverse/rendering/renderableplanescloud.cpp +++ b/modules/digitaluniverse/rendering/renderableplanescloud.cpp @@ -156,7 +156,7 @@ namespace { // [[codegen::verbatim(LabelsInfo.description)]] std::optional labels - [[codegen::reference("space_labelscomponent")]]; + [[codegen::reference("labelscomponent")]]; // [[codegen::verbatim(TransformationMatrixInfo.description)]] std::optional transformationMatrix; @@ -322,7 +322,7 @@ void RenderablePlanesCloud::initialize() { ZoneScoped; if (_hasSpeckFile && std::filesystem::is_regular_file(_speckFile)) { - _dataset = speck::data::loadFileWithCache(_speckFile); + _dataset = dataloader::data::loadFileWithCache(_speckFile); if (_dataset.entries.empty()) { throw ghoul::RuntimeError("Error loading data"); } @@ -488,7 +488,7 @@ void RenderablePlanesCloud::update(const UpdateData&) { } void RenderablePlanesCloud::loadTextures() { - for (const speck::Dataset::Texture& tex : _dataset.textures) { + for (const dataloader::Dataset::Texture& tex : _dataset.textures) { std::filesystem::path fullPath = absPath(_texturesPath.string() + '/' + tex.file); std::filesystem::path pngPath = fullPath; pngPath.replace_extension(".png"); @@ -535,7 +535,7 @@ void RenderablePlanesCloud::createPlanes() { LDEBUG("Creating planes..."); float maxSize = 0.f; double maxRadius = 0.0; - for (const speck::Dataset::Entry& e : _dataset.entries) { + for (const dataloader::Dataset::Entry& e : _dataset.entries) { const glm::vec4 transformedPos = glm::vec4( _transformationMatrix * glm::dvec4(e.position, 1.0) ); diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.h b/modules/digitaluniverse/rendering/renderableplanescloud.h index 8c8773e70d..29edae010b 100644 --- a/modules/digitaluniverse/rendering/renderableplanescloud.h +++ b/modules/digitaluniverse/rendering/renderableplanescloud.h @@ -27,13 +27,14 @@ #include -#include +#include #include #include #include #include #include #include +#include #include #include #include @@ -111,7 +112,7 @@ private: DistanceUnit _unit = DistanceUnit::Parsec; - speck::Dataset _dataset; + dataloader::Dataset _dataset; // Everything related to the labels is handled by LabelsComponent std::unique_ptr _labels; diff --git a/modules/digitaluniverse/rendering/renderablepoints.cpp b/modules/digitaluniverse/rendering/renderablepoints.cpp deleted file mode 100644 index c104723ffb..0000000000 --- a/modules/digitaluniverse/rendering/renderablepoints.cpp +++ /dev/null @@ -1,437 +0,0 @@ -/***************************************************************************************** - * * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - constexpr std::string_view _loggerCat = "RenderablePoints"; - - constexpr std::array UniformNames = { - "modelViewProjectionTransform", "color", "sides", "alphaValue", "scaleFactor", - "spriteTexture", "hasColorMap" - }; - - constexpr openspace::properties::Property::PropertyInfo SpriteTextureInfo = { - "Texture", - "Point Sprite Texture", - "The path to the texture that should be used as the point sprite", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo ScaleFactorInfo = { - "ScaleFactor", - "Scale Factor", - "This value is used as a multiplicative factor that is applied to the apparent " - "size of each point", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo ColorInfo = { - "Color", - "Color", - "This value is used to define the color of the astronomical object", - openspace::properties::Property::Visibility::NoviceUser - }; - - constexpr openspace::properties::Property::PropertyInfo ColorMapInfo = { - "ColorMap", - "Color Map File", - "The path to the color map file of the astronomical object", - openspace::properties::Property::Visibility::AdvancedUser - }; - - struct [[codegen::Dictionary(RenderablePoints)]] Parameters { - // The path to the SPECK file that contains information about the astronomical - // object being rendered - std::string file; - - // Astronomical Object Color (r,g,b) - glm::vec3 color [[codegen::color()]]; - - enum class [[codegen::map(openspace::DistanceUnit)]] Unit { - Meter [[codegen::key("m")]], - Kilometer [[codegen::key("Km")]], - Parsec [[codegen::key("pc")]], - Kiloparsec [[codegen::key("Kpc")]], - Megaparsec [[codegen::key("Mpc")]], - Gigaparsec [[codegen::key("Gpc")]], - Gigalightyear [[codegen::key("Gly")]] - }; - std::optional unit; - - // [[codegen::verbatim(SpriteTextureInfo.description)]] - std::optional texture; - - // [[codegen::verbatim(ScaleFactorInfo.description)]] - std::optional scaleFactor; - - // [[codegen::verbatim(ColorMapInfo.description)]] - std::optional colorMap; - }; -#include "renderablepoints_codegen.cpp" -} // namespace - -namespace openspace { - -documentation::Documentation RenderablePoints::Documentation() { - return codegen::doc("digitaluniverse_renderablepoints"); -} - -RenderablePoints::RenderablePoints(const ghoul::Dictionary& dictionary) - : Renderable(dictionary) - , _scaleFactor(ScaleFactorInfo, 1.f, 0.f, 64.f) - , _pointColor( - ColorInfo, - glm::vec3(1.f, 0.4f, 0.2f), - glm::vec3(0.f, 0.f, 0.f), - glm::vec3(1.f, 1.f, 1.f) - ) - , _spriteTexturePath(SpriteTextureInfo) -{ - const Parameters p = codegen::bake(dictionary); - - addProperty(Fadeable::_opacity); - - _speckFile = absPath(p.file); - - if (p.unit.has_value()) { - _unit = codegen::map(*p.unit); - } - else { - _unit = DistanceUnit::Meter; - } - - _pointColor = p.color; - _pointColor.setViewOption(properties::Property::ViewOptions::Color); - addProperty(_pointColor); - - if (p.texture.has_value()) { - _spriteTexturePath = absPath(*p.texture).string(); - _spriteTextureFile = std::make_unique( - _spriteTexturePath.value() - ); - - _spriteTexturePath.onChange([this]() { _spriteTextureIsDirty = true; }); - _spriteTextureFile->setCallback([this]() { _spriteTextureIsDirty = true; }); - addProperty(_spriteTexturePath); - - _hasSpriteTexture = true; - } - - if (p.colorMap.has_value()) { - _colorMapFile = absPath(*p.colorMap); - _hasColorMapFile = true; - } - - _scaleFactor = p.scaleFactor.value_or(_scaleFactor); - addProperty(_scaleFactor); -} - -bool RenderablePoints::isReady() const { - return _program && (!_dataset.entries.empty()); -} - -void RenderablePoints::initialize() { - ZoneScoped; - - _dataset = speck::data::loadFileWithCache(_speckFile); - - if (_hasColorMapFile) { - readColorMapFile(); - } -} - -void RenderablePoints::initializeGL() { - ZoneScoped; - - if (_hasSpriteTexture) { - _program = DigitalUniverseModule::ProgramObjectManager.request( - "RenderablePoints Sprite", - []() -> std::unique_ptr { - return global::renderEngine->buildRenderProgram( - "RenderablePoints Sprite", - absPath("${MODULE_DIGITALUNIVERSE}/shaders/points_vs.glsl"), - absPath("${MODULE_DIGITALUNIVERSE}/shaders/points_sprite_fs.glsl") - ); - } - ); - } - else { - _program = DigitalUniverseModule::ProgramObjectManager.request( - "RenderablePoints", - []() -> std::unique_ptr { - return global::renderEngine->buildRenderProgram( - "RenderablePoints", - absPath("${MODULE_DIGITALUNIVERSE}/shaders/points_vs.glsl"), - absPath("${MODULE_DIGITALUNIVERSE}/shaders/points_sprite_fs.glsl") - ); - } - ); - } - ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); -} - -void RenderablePoints::deinitializeGL() { - glDeleteBuffers(1, &_vbo); - _vbo = 0; - glDeleteVertexArrays(1, &_vao); - _vao = 0; - - DigitalUniverseModule::ProgramObjectManager.release( - _program->name(), - [](ghoul::opengl::ProgramObject* p) { - global::renderEngine->removeRenderProgram(p); - } - ); - - if (_hasSpriteTexture) { - _spriteTexture = nullptr; - } -} - -void RenderablePoints::render(const RenderData& data, RendererTasks&) { - glDepthMask(false); - _program->activate(); - - _program->setUniform( - _uniformCache.modelViewProjectionTransform, - calcModelViewProjectionTransform(data, glm::dmat4(1.0)) - ); - - _program->setUniform(_uniformCache.color, _pointColor); - _program->setUniform(_uniformCache.sides, 4); - _program->setUniform(_uniformCache.alphaValue, opacity()); - _program->setUniform(_uniformCache.scaleFactor, _scaleFactor); - - if (_hasSpriteTexture) { - ghoul::opengl::TextureUnit spriteTextureUnit; - spriteTextureUnit.activate(); - _spriteTexture->bind(); - _program->setUniform(_uniformCache.spriteTexture, spriteTextureUnit); - } - - _program->setUniform(_uniformCache.hasColorMap, _hasColorMapFile); - - glEnable(GL_PROGRAM_POINT_SIZE); - glBindVertexArray(_vao); - glDrawArrays(GL_POINTS, 0, static_cast(_dataset.entries.size())); - - glDisable(GL_PROGRAM_POINT_SIZE); - glBindVertexArray(0); - _program->deactivate(); - - glDepthMask(true); -} - -void RenderablePoints::update(const UpdateData&) { - if (_dataIsDirty) { - LDEBUG("Regenerating data"); - - std::vector slice = createDataSlice(); - - if (_vao == 0) { - glGenVertexArrays(1, &_vao); - } - if (_vbo == 0) { - glGenBuffers(1, &_vbo); - } - - glBindVertexArray(_vao); - glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferData( - GL_ARRAY_BUFFER, - slice.size() * sizeof(double), - slice.data(), - GL_STATIC_DRAW - ); - GLint positionAttrib = _program->attributeLocation("in_position"); - - if (_hasColorMapFile) { - glEnableVertexAttribArray(positionAttrib); - glVertexAttribLPointer( - positionAttrib, 4, GL_DOUBLE, sizeof(double) * 8, nullptr - ); - - GLint colorMapAttrib = _program->attributeLocation("in_colormap"); - glEnableVertexAttribArray(colorMapAttrib); - glVertexAttribLPointer( - colorMapAttrib, - 4, - GL_DOUBLE, - 8 * sizeof(double), - reinterpret_cast(4 * sizeof(double)) - ); - } - else { - glEnableVertexAttribArray(positionAttrib); - glVertexAttribLPointer(positionAttrib, 4, GL_DOUBLE, 0, nullptr); - } - - glBindVertexArray(0); - - _dataIsDirty = false; - } - - if (_hasSpriteTexture && _spriteTextureIsDirty) { - LDEBUG("Reloading Sprite Texture"); - _spriteTexture = nullptr; - if (!_spriteTexturePath.value().empty()) { - _spriteTexture = ghoul::io::TextureReader::ref().loadTexture( - absPath(_spriteTexturePath).string(), - 2 - ); - if (_spriteTexture) { - LDEBUG( - fmt::format("Loaded texture from {}", absPath(_spriteTexturePath)) - ); - _spriteTexture->uploadTexture(); - } - _spriteTexture->setFilter( - ghoul::opengl::Texture::FilterMode::AnisotropicMipMap - ); - - _spriteTextureFile = std::make_unique( - _spriteTexturePath.value() - ); - _spriteTextureFile->setCallback([this]() { _spriteTextureIsDirty = true; }); - } - _spriteTextureIsDirty = false; - } -} - -void RenderablePoints::readColorMapFile() { - std::ifstream file(_colorMapFile); - if (!file.good()) { - throw ghoul::RuntimeError(fmt::format( - "Failed to open Color Map file {}", _colorMapFile - )); - } - - std::size_t numberOfColors = 0; - - // The beginning of the speck file has a header that either contains comments - // (signaled by a preceding '#') or information about the structure of the file - // (signaled by the keywords 'datavar', 'texturevar', and 'texture') - std::string line; - while (true) { - // std::streampos position = file.tellg(); - std::getline(file, line); - - if (line.empty() || line[0] == '#') { - continue; - } - - // Initial number of colors - std::locale loc; - if (std::isdigit(line[0], loc)) { - std::string::size_type sz; - numberOfColors = std::stoi(line, &sz); - break; - } - else if (file.eof()) { - throw ghoul::RuntimeError(fmt::format( - "Failed to load colors from Color Map file {}", _colorMapFile - )); - } - } - - for (size_t i = 0; i < numberOfColors; ++i) { - std::getline(file, line); - std::stringstream str(line); - - glm::vec4 color; - str >> color.r >> color.g >> color.b >> color.a; - - _colorMapData.push_back(color); - } -} - -std::vector RenderablePoints::createDataSlice() { - std::vector slice; - if (_hasColorMapFile) { - slice.reserve(8 * _dataset.entries.size()); - } - else { - slice.reserve(4 * _dataset.entries.size()); - } - - double maxRadius = 0.0; - - int colorIndex = 0; - for (const speck::Dataset::Entry& e : _dataset.entries) { - glm::dvec3 p = e.position; - double scale = toMeter(_unit); - p *= scale; - - const double r = glm::length(p); - maxRadius = std::max(maxRadius, r); - - glm::dvec4 position(p, 1.0); - - if (_hasColorMapFile) { - for (int j = 0; j < 4; ++j) { - slice.push_back(position[j]); - } - for (int j = 0; j < 4; ++j) { - slice.push_back(_colorMapData[colorIndex][j]); - } - } - else { - for (int j = 0; j < 4; ++j) { - slice.push_back(position[j]); - } - } - - colorIndex = (colorIndex == static_cast(_colorMapData.size() - 1)) ? - 0 : - colorIndex + 1; - } - setBoundingSphere(maxRadius); - - return slice; -} - -} // namespace openspace diff --git a/modules/digitaluniverse/rendering/renderablepoints.h b/modules/digitaluniverse/rendering/renderablepoints.h deleted file mode 100644 index 1fba9ebbff..0000000000 --- a/modules/digitaluniverse/rendering/renderablepoints.h +++ /dev/null @@ -1,106 +0,0 @@ -/***************************************************************************************** - * * - * 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_DIGITALUNIVERSE___RENDERABLEPOINTS___H__ -#define __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLEPOINTS___H__ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ghoul::filesystem { class File; } - -namespace ghoul::opengl { - class ProgramObject; - class Texture; -} // namespace ghoul::opengl - -namespace openspace { - -namespace documentation { struct Documentation; } - -class RenderablePoints : public Renderable { -public: - explicit RenderablePoints(const ghoul::Dictionary& dictionary); - ~RenderablePoints() override = default; - - void initialize() override; - void initializeGL() override; - void deinitializeGL() override; - - bool isReady() const override; - - void render(const RenderData& data, RendererTasks& rendererTask) override; - void update(const UpdateData& data) override; - - static documentation::Documentation Documentation(); - -private: - std::vector createDataSlice(); - - void readColorMapFile(); - - bool _dataIsDirty = true; - bool _hasSpriteTexture = false; - bool _spriteTextureIsDirty = true; - bool _hasColorMapFile = false; - - properties::FloatProperty _scaleFactor; - properties::Vec3Property _pointColor; - properties::StringProperty _spriteTexturePath; - - std::unique_ptr _spriteTexture; - std::unique_ptr _spriteTextureFile; - ghoul::opengl::ProgramObject* _program = nullptr; - UniformCache( - modelViewProjectionTransform, color, sides, alphaValue, scaleFactor, - spriteTexture, hasColorMap - ) _uniformCache; - - std::filesystem::path _speckFile; - std::filesystem::path _colorMapFile; - - DistanceUnit _unit = DistanceUnit::Parsec; - - speck::Dataset _dataset; - std::vector _colorMapData; - - //int _nValuesPerAstronomicalObject = 0; - - GLuint _vao = 0; - GLuint _vbo = 0; -}; - -} // namespace openspace - -#endif // __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLEPOINTS___H__ diff --git a/modules/space/CMakeLists.txt b/modules/space/CMakeLists.txt index d4b308bfeb..f191570586 100644 --- a/modules/space/CMakeLists.txt +++ b/modules/space/CMakeLists.txt @@ -27,8 +27,6 @@ include(${PROJECT_SOURCE_DIR}/support/cmake/module_definition.cmake) set(HEADER_FILES horizonsfile.h kepler.h - labelscomponent.h - speckloader.h rendering/renderableconstellationsbase.h rendering/renderableconstellationbounds.h rendering/renderableconstellationlines.h @@ -51,8 +49,6 @@ set(SOURCE_FILES horizonsfile.cpp kepler.cpp spacemodule_lua.inl - labelscomponent.cpp - speckloader.cpp rendering/renderableconstellationsbase.cpp rendering/renderableconstellationbounds.cpp rendering/renderableconstellationlines.cpp diff --git a/modules/space/rendering/renderableconstellationlines.cpp b/modules/space/rendering/renderableconstellationlines.cpp index de1cc76e19..8bd0f81827 100644 --- a/modules/space/rendering/renderableconstellationlines.cpp +++ b/modules/space/rendering/renderableconstellationlines.cpp @@ -144,7 +144,7 @@ void RenderableConstellationLines::selectionPropertyHasChanged() { } if (_hasLabels) { - for (speck::Labelset::Entry& e : _labels->labelSet().entries) { + for (dataloader::Labelset::Entry& e : _labels->labelSet().entries) { e.isEnabled = true; } } @@ -156,7 +156,7 @@ void RenderableConstellationLines::selectionPropertyHasChanged() { pair.second.isEnabled = isSelected; if (_hasLabels) { - for (speck::Labelset::Entry& e : _labels->labelSet().entries) { + for (dataloader::Labelset::Entry& e : _labels->labelSet().entries) { if (constellationFullName(e.identifier) == pair.second.name) { e.isEnabled = isSelected; break; diff --git a/modules/space/rendering/renderableconstellationsbase.cpp b/modules/space/rendering/renderableconstellationsbase.cpp index fb75c8fb5a..ace7b0c53d 100644 --- a/modules/space/rendering/renderableconstellationsbase.cpp +++ b/modules/space/rendering/renderableconstellationsbase.cpp @@ -82,7 +82,7 @@ namespace { // [[codegen::verbatim(LabelsInfo.description)]] std::optional labels - [[codegen::reference("space_labelscomponent")]]; + [[codegen::reference("labelscomponent")]]; }; #include "renderableconstellationsbase_codegen.cpp" } // namespace @@ -195,7 +195,7 @@ void RenderableConstellationsBase::initialize() { _labels->initialize(); _labels->loadLabels(); - for (speck::Labelset::Entry& entry : _labels->labelSet().entries) { + for (dataloader::Labelset::Entry& entry : _labels->labelSet().entries) { if (!entry.identifier.empty()) { std::string fullName = constellationFullName(entry.identifier); if (!fullName.empty()) { diff --git a/modules/space/rendering/renderableconstellationsbase.h b/modules/space/rendering/renderableconstellationsbase.h index 7c86c12e4b..288d4cb78f 100644 --- a/modules/space/rendering/renderableconstellationsbase.h +++ b/modules/space/rendering/renderableconstellationsbase.h @@ -27,11 +27,11 @@ #include -#include #include #include #include #include +#include #include #include #include diff --git a/modules/space/rendering/renderablestars.cpp b/modules/space/rendering/renderablestars.cpp index 8207385ad8..e696d14e5b 100644 --- a/modules/space/rendering/renderablestars.cpp +++ b/modules/space/rendering/renderablestars.cpp @@ -1355,14 +1355,14 @@ void RenderableStars::loadData() { return; } - _dataset = speck::data::loadFileWithCache(file); + _dataset = dataloader::data::loadFileWithCache(file); if (_dataset.entries.empty()) { return; } std::vector variableNames; variableNames.reserve(_dataset.variables.size()); - for (const speck::Dataset::Variable& v : _dataset.variables) { + for (const dataloader::Dataset::Variable& v : _dataset.variables) { variableNames.push_back(v.name); } _otherDataOption.addOptions(variableNames); @@ -1399,7 +1399,7 @@ std::vector RenderableStars::createDataSlice(ColorOption option) { std::vector result; // 7 for the default Color option of 3 positions + bv + lum + abs + app magnitude result.reserve(_dataset.entries.size() * 7); - for (const speck::Dataset::Entry& e : _dataset.entries) { + for (const dataloader::Dataset::Entry& e : _dataset.entries) { glm::dvec3 position = glm::dvec3(e.position) * distanceconstants::Parsec; maxRadius = std::max(maxRadius, glm::length(position)); diff --git a/modules/space/rendering/renderablestars.h b/modules/space/rendering/renderablestars.h index eb7af56799..85ea0c25b4 100644 --- a/modules/space/rendering/renderablestars.h +++ b/modules/space/rendering/renderablestars.h @@ -27,7 +27,7 @@ #include -#include +#include #include #include #include @@ -143,7 +143,7 @@ private: bool _dataIsDirty = true; bool _otherDataColorMapIsDirty = true; - speck::Dataset _dataset; + dataloader::Dataset _dataset; std::string _queuedOtherData; diff --git a/modules/space/spacemodule.cpp b/modules/space/spacemodule.cpp index f5f8d792cf..67c29cf461 100644 --- a/modules/space/spacemodule.cpp +++ b/modules/space/spacemodule.cpp @@ -24,7 +24,6 @@ #include -#include #include #include #include @@ -133,7 +132,6 @@ std::vector SpaceModule::documentations() const { RenderableTravelSpeed::Documentation(), SpiceRotation::Documentation(), SpiceTranslation::Documentation(), - LabelsComponent::Documentation(), GPTranslation::Documentation() }; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a147c084dc..1d3e45b073 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,6 +27,10 @@ include(${PROJECT_SOURCE_DIR}/support/cmake/set_openspace_compile_settings.cmake set(OPENSPACE_SOURCE openspace.cpp camera/camera.cpp + data/csvloader.cpp + data/dataloader.cpp + data/datamapping.cpp + data/speckloader.cpp documentation/core_registration.cpp documentation/documentation.cpp documentation/documentationengine.cpp @@ -120,6 +124,7 @@ set(OPENSPACE_SOURCE properties/vector/vec3property.cpp properties/vector/vec4property.cpp query/query.cpp + rendering/colormappingcomponent.cpp rendering/dashboard.cpp rendering/dashboard_lua.inl rendering/dashboarditem.cpp @@ -128,6 +133,7 @@ set(OPENSPACE_SOURCE rendering/deferredcastermanager.cpp rendering/fadeable.cpp rendering/helper.cpp + rendering/labelscomponent.cpp rendering/loadingscreen.cpp rendering/luaconsole.cpp rendering/raycastermanager.cpp @@ -209,6 +215,10 @@ set(OPENSPACE_HEADER ${PROJECT_SOURCE_DIR}/include/openspace/json.h ${PROJECT_SOURCE_DIR}/include/openspace/camera/camera.h ${PROJECT_SOURCE_DIR}/include/openspace/camera/camerapose.h + ${PROJECT_SOURCE_DIR}/include/openspace/data/csvloader.h + ${PROJECT_SOURCE_DIR}/include/openspace/data/dataloader.h + ${PROJECT_SOURCE_DIR}/include/openspace/data/datamapping.h + ${PROJECT_SOURCE_DIR}/include/openspace/data/speckloader.h ${PROJECT_SOURCE_DIR}/include/openspace/documentation/core_registration.h ${PROJECT_SOURCE_DIR}/include/openspace/documentation/documentation.h ${PROJECT_SOURCE_DIR}/include/openspace/documentation/documentationengine.h @@ -308,6 +318,7 @@ set(OPENSPACE_HEADER ${PROJECT_SOURCE_DIR}/include/openspace/properties/vector/vec3property.h ${PROJECT_SOURCE_DIR}/include/openspace/properties/vector/vec4property.h ${PROJECT_SOURCE_DIR}/include/openspace/query/query.h + ${PROJECT_SOURCE_DIR}/include/openspace/rendering/colormappingcomponent.h ${PROJECT_SOURCE_DIR}/include/openspace/rendering/dashboard.h ${PROJECT_SOURCE_DIR}/include/openspace/rendering/dashboarditem.h ${PROJECT_SOURCE_DIR}/include/openspace/rendering/dashboardtextitem.h @@ -319,6 +330,7 @@ set(OPENSPACE_HEADER ${PROJECT_SOURCE_DIR}/include/openspace/rendering/loadingscreen.h ${PROJECT_SOURCE_DIR}/include/openspace/rendering/luaconsole.h ${PROJECT_SOURCE_DIR}/include/openspace/rendering/helper.h + ${PROJECT_SOURCE_DIR}/include/openspace/rendering/labelscomponent.h ${PROJECT_SOURCE_DIR}/include/openspace/rendering/raycasterlistener.h ${PROJECT_SOURCE_DIR}/include/openspace/rendering/raycastermanager.h ${PROJECT_SOURCE_DIR}/include/openspace/rendering/renderable.h diff --git a/src/data/csvloader.cpp b/src/data/csvloader.cpp new file mode 100644 index 0000000000..7da87bf571 --- /dev/null +++ b/src/data/csvloader.cpp @@ -0,0 +1,194 @@ +/***************************************************************************************** + * * + * 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 +#include +#include +#include + +namespace { + constexpr std::string_view _loggerCat = "DataLoader: CSV"; +} // namespace + +namespace openspace::dataloader::csv { + +Dataset loadCsvFile(std::filesystem::path filePath, std::optional specs) { + ghoul_assert(std::filesystem::exists(filePath), "File must exist"); + + auto readFloatData = [](const std::string& str) -> float { + float result; +#ifdef WIN32 + auto [p, ec] = std::from_chars(str.data(), str.data() + str.size(), result); + if (ec == std::errc() && std::isfinite(result)) { + return result; + } + return std::numeric_limits::quiet_NaN(); +#else + // clang is missing float support for std::from_chars + try { + result = std::stof(str.c_str(), nullptr); + if (std::isfinite(result)) { + return result; + } + } + catch (std::invalid_argument const& e) {} + return NAN; +#endif + }; + + LDEBUG("Parsing CSV file"); + + std::vector> rows = ghoul::loadCSVFile( + filePath.string(), + true + ); + + if (rows.size() < 2) { + LWARNING(fmt::format( + "Error loading data file {}. No data items read", filePath + )); + return Dataset(); + } + + Dataset res; + res.entries.reserve(rows.size() - 1); + + // First row is the column names + const std::vector& columns = rows.front(); + + int xColumn = -1; + int yColumn = -1; + int zColumn = -1; + + int nDataColumns = 0; + bool hasExcludeColumns = specs.has_value() && (*specs).hasExcludeColumns(); + std::vector skipColumns; + if (hasExcludeColumns) { + skipColumns.reserve((*specs).excludeColumns.size()); + } + + for (size_t i = 0; i < columns.size(); ++i) { + const std::string& col = columns[i]; + + if (isPositionColumn(col, specs)) { + if (isColumnX(col, specs)) { + xColumn = static_cast(i); + } + if (isColumnY(col, specs)) { + yColumn = static_cast(i); + } + if (isColumnZ(col, specs)) { + zColumn = static_cast(i); + } + } + else if (hasExcludeColumns && (*specs).isExcludeColumn(col)) { + skipColumns.push_back(i); + continue; + } + else { + res.variables.push_back({ + .index = nDataColumns, + .name = col + }); + nDataColumns++; + } + } + + if (xColumn < 0 || yColumn < 0 || zColumn < 0) { + // One or more position columns weren't read + LERROR(fmt::format( + "Error loading data file {}. Missing X, Y or Z position column", filePath + )); + } + + LINFO(fmt::format( + "Loading {} rows with {} columns", rows.size(), columns.size() + )); + ProgressBar progress(rows.size()); + + // Skip first row (column names) + for (size_t rowIdx = 1; rowIdx < rows.size(); ++rowIdx) { + const std::vector& row = rows[rowIdx]; + + Dataset::Entry entry; + entry.data.reserve(nDataColumns); + + for (size_t i = 0; i < row.size(); ++i) { + // Check if column should be exluded. Note that list of indices is sorted + // so we can do a binary search + if (hasExcludeColumns && + std::binary_search(skipColumns.begin(), skipColumns.end(), i)) + { + continue; + } + + const std::string& strValue = row[i]; + + // For now, all values are converted to float + float value = readFloatData(strValue); + + if (i == xColumn) { + entry.position.x = value; + } + else if (i == yColumn) { + entry.position.y = value; + } + else if (i == zColumn) { + entry.position.z = value; + } + else { + entry.data.push_back(value); + } + + // @TODO: comment mapping + } + + glm::vec3 positive = glm::abs(entry.position); + float max = glm::compMax(positive); + if (max > res.maxPositionComponent) { + res.maxPositionComponent = max; + } + + res.entries.push_back(entry); + + progress.print(rowIdx + 1); + } + + return res; +} + +} // namespace openspace::dataloader::csv diff --git a/src/data/dataloader.cpp b/src/data/dataloader.cpp new file mode 100644 index 0000000000..e2af74e0b0 --- /dev/null +++ b/src/data/dataloader.cpp @@ -0,0 +1,687 @@ +/***************************************************************************************** + * * + * 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 +#include +#include + +namespace { + constexpr int8_t DataCacheFileVersion = 11; + constexpr int8_t LabelCacheFileVersion = 11; + constexpr int8_t ColorCacheFileVersion = 11; + + constexpr std::string_view DefaultXColumn = "x"; + constexpr std::string_view DefaultYColumn = "y"; + constexpr std::string_view DefaultZColumn = "z"; + + template + void checkSize(U value, std::string_view message) { + if (value > std::numeric_limits::max()) { + throw ghoul::RuntimeError(fmt::format("Error saving file: {}", message)); + } + } + + template + using LoadCacheFunc = std::function(std::filesystem::path)>; + + template + using SaveCacheFunc = std::function; + + template + using LoadDataFunc = std::function specs + )>; + + template + T internalLoadFileWithCache(std::filesystem::path filePath, + std::optional specs, + LoadDataFunc loadFunction, + LoadCacheFunc loadCacheFunction, + SaveCacheFunc saveCacheFunction) + { + static_assert( + std::is_same_v || + std::is_same_v || + std::is_same_v + ); + + std::string info; + if (specs.has_value()) { + info = openspace::dataloader::generateHashString(*specs); + } + std::filesystem::path cached = FileSys.cacheManager()->cachedFilename(filePath, info); + + if (std::filesystem::exists(cached)) { + LINFOC( + "DataLoader", + fmt::format("Cached file {} used for file {}", cached, filePath) + ); + + std::optional dataset = loadCacheFunction(cached); + if (dataset.has_value()) { + // We could load the cache file and we are now done with this + return *dataset; + } + else { + FileSys.cacheManager()->removeCacheFile(cached); + } + } + LINFOC("DataLoader", fmt::format("Loading file {}", filePath)); + T dataset = loadFunction(filePath, specs); + + if (!dataset.entries.empty()) { + LINFOC("DataLoader", "Saving cache"); + saveCacheFunction(dataset, cached); + } + + return dataset; + } +} // namespace + +namespace openspace::dataloader { + +namespace data { + +Dataset loadFile(std::filesystem::path path, std::optional specs) { + ghoul_assert(std::filesystem::exists(path), "File must exist"); + + std::ifstream file(path); + if (!file.good()) { + throw ghoul::RuntimeError(fmt::format("Failed to open data file {}", path)); + } + + std::string extension = ghoul::toLowerCase(path.extension().string()); + + Dataset res; + if (extension == ".csv") { + res = csv::loadCsvFile(path, specs); + } + else if (extension == ".speck") { + res = speck::loadSpeckFile(path, specs); + } + else { + LERRORC("DataLoader", fmt::format( + "Could not read data file {}. File format {} is not supported", + path, path.extension() + )); + } + + return res; +} + +std::optional loadCachedFile(std::filesystem::path path) { + std::ifstream file(path, std::ios::binary); + if (!file.good()) { + return std::nullopt; + } + + Dataset result; + + int8_t fileVersion; + file.read(reinterpret_cast(&fileVersion), sizeof(int8_t)); + if (fileVersion != DataCacheFileVersion) { + // Incompatible version and we won't be able to read the file + return std::nullopt; + } + + // + // Read variables + uint16_t nVariables; + file.read(reinterpret_cast(&nVariables), sizeof(uint16_t)); + result.variables.resize(nVariables); + for (int i = 0; i < nVariables; i += 1) { + Dataset::Variable var; + + int16_t idx; + file.read(reinterpret_cast(&idx), sizeof(int16_t)); + var.index = idx; + + uint16_t len; + file.read(reinterpret_cast(&len), sizeof(uint16_t)); + var.name.resize(len); + file.read(var.name.data(), len); + + result.variables[i] = std::move(var); + } + + // + // Read textures + uint16_t nTextures; + file.read(reinterpret_cast(&nTextures), sizeof(uint16_t)); + result.textures.resize(nTextures); + for (int i = 0; i < nTextures; i += 1) { + Dataset::Texture tex; + + int16_t idx; + file.read(reinterpret_cast(&idx), sizeof(int16_t)); + tex.index = idx; + + uint16_t len; + file.read(reinterpret_cast(&len), sizeof(uint16_t)); + tex.file.resize(len); + file.read(tex.file.data(), len); + + result.textures[i] = std::move(tex); + } + + // + // Read indices + int16_t texDataIdx; + file.read(reinterpret_cast(&texDataIdx), sizeof(int16_t)); + result.textureDataIndex = texDataIdx; + + int16_t oriDataIdx; + file.read(reinterpret_cast(&oriDataIdx), sizeof(int16_t)); + result.orientationDataIndex = oriDataIdx; + + // + // Read entries + uint64_t nEntries; + file.read(reinterpret_cast(&nEntries), sizeof(uint64_t)); + result.entries.reserve(nEntries); + for (uint64_t i = 0; i < nEntries; i += 1) { + Dataset::Entry e; + file.read(reinterpret_cast(&e.position.x), sizeof(float)); + file.read(reinterpret_cast(&e.position.y), sizeof(float)); + file.read(reinterpret_cast(&e.position.z), sizeof(float)); + + uint16_t nValues; + file.read(reinterpret_cast(&nValues), sizeof(uint16_t)); + e.data.resize(nValues); + file.read(reinterpret_cast(e.data.data()), nValues * sizeof(float)); + + uint16_t len; + file.read(reinterpret_cast(&len), sizeof(uint16_t)); + if (len > 0) { + std::string comment; + comment.resize(len); + file.read(comment.data(), len); + e.comment = std::move(comment); + } + + result.entries.push_back(std::move(e)); + } + + // + // Read max data point variable + float max; + file.read(reinterpret_cast(&max), sizeof(float)); + result.maxPositionComponent = max; + + return result; +} + +void saveCachedFile(const Dataset& dataset, std::filesystem::path path) { + std::ofstream file(path, std::ofstream::binary); + + file.write(reinterpret_cast(&DataCacheFileVersion), sizeof(int8_t)); + + // + // Store variables + checkSize(dataset.variables.size(), "Too many variables"); + uint16_t nVariables = static_cast(dataset.variables.size()); + file.write(reinterpret_cast(&nVariables), sizeof(uint16_t)); + for (const Dataset::Variable& var : dataset.variables) { + checkSize(var.index, "Variable index too large"); + int16_t idx = static_cast(var.index); + file.write(reinterpret_cast(&idx), sizeof(int16_t)); + + checkSize(var.name.size(), "Variable name too long"); + uint16_t len = static_cast(var.name.size()); + file.write(reinterpret_cast(&len), sizeof(uint16_t)); + file.write(var.name.data(), len); + } + + // + // Store textures + checkSize(dataset.textures.size(), "Too many textures"); + uint16_t nTextures = static_cast(dataset.textures.size()); + file.write(reinterpret_cast(&nTextures), sizeof(uint16_t)); + for (const Dataset::Texture& tex : dataset.textures) { + checkSize(tex.index, "Texture index too large"); + int16_t idx = static_cast(tex.index); + file.write(reinterpret_cast(&idx), sizeof(int16_t)); + + + checkSize(tex.file.size(), "Texture file too long"); + uint16_t len = static_cast(tex.file.size()); + file.write(reinterpret_cast(&len), sizeof(uint16_t)); + file.write(tex.file.data(), len); + } + + // + // Store indices + checkSize(dataset.textureDataIndex, "Texture index too large"); + int16_t texIdx = static_cast(dataset.textureDataIndex); + file.write(reinterpret_cast(&texIdx), sizeof(int16_t)); + + checkSize(dataset.orientationDataIndex, "Orientation index too large"); + int16_t orientationIdx = static_cast(dataset.orientationDataIndex); + file.write(reinterpret_cast(&orientationIdx), sizeof(int16_t)); + + // + // Store entries + checkSize(dataset.entries.size(), "Too many entries"); + uint64_t nEntries = static_cast(dataset.entries.size()); + file.write(reinterpret_cast(&nEntries), sizeof(uint64_t)); + for (const Dataset::Entry& e : dataset.entries) { + file.write(reinterpret_cast(&e.position.x), sizeof(float)); + file.write(reinterpret_cast(&e.position.y), sizeof(float)); + file.write(reinterpret_cast(&e.position.z), sizeof(float)); + + checkSize(e.data.size(), "Too many data variables"); + uint16_t nValues = static_cast(e.data.size()); + file.write(reinterpret_cast(&nValues), sizeof(uint16_t)); + file.write( + reinterpret_cast(e.data.data()), + e.data.size() * sizeof(float) + ); + + if (e.comment.has_value()) { + checkSize(e.comment->size(), "Comment too long"); + } + uint16_t commentLen = e.comment.has_value() ? + static_cast(e.comment->size()) : + 0; + file.write(reinterpret_cast(&commentLen), sizeof(uint16_t)); + if (e.comment.has_value()) { + file.write(e.comment->data(), e.comment->size()); + } + } + + // + // Store max data point variable + file.write(reinterpret_cast(&dataset.maxPositionComponent), sizeof(float)); +} + +Dataset loadFileWithCache(std::filesystem::path filePath, std::optional specs) +{ + return internalLoadFileWithCache( + filePath, + specs, + &loadFile, + &loadCachedFile, + &saveCachedFile + ); +} + +} // namespace data + +namespace label { + +Labelset loadFile(std::filesystem::path path, std::optional) { + ghoul_assert(std::filesystem::exists(path), "File must exist"); + + std::ifstream file(path); + if (!file.good()) { + throw ghoul::RuntimeError(fmt::format("Failed to open dataset file {}", path)); + } + + std::string extension = ghoul::toLowerCase(path.extension().string()); + + Labelset res; + if (extension == ".label") { + res = speck::loadLabelFile(path); + } + else { + LERRORC("DataLoader", fmt::format( + "Could not read label data file {}. File format {} is not supported", + path, path.extension() + )); + } + + return res; +} + +std::optional loadCachedFile(std::filesystem::path path) { + std::ifstream file(path, std::ios::binary); + if (!file.good()) { + return std::nullopt; + } + + int8_t fileVersion; + file.read(reinterpret_cast(&fileVersion), sizeof(int8_t)); + if (fileVersion != LabelCacheFileVersion) { + // Incompatible version and we won't be able to read the file + return std::nullopt; + } + + Labelset result; + + int16_t textColorIdx; + file.read(reinterpret_cast(&textColorIdx), sizeof(int16_t)); + result.textColorIndex = textColorIdx; + + uint32_t nEntries; + file.read(reinterpret_cast(&nEntries), sizeof(uint32_t)); + result.entries.reserve(nEntries); + for (unsigned int i = 0; i < nEntries; i += 1) { + Labelset::Entry e; + file.read(reinterpret_cast(&e.position.x), sizeof(float)); + file.read(reinterpret_cast(&e.position.y), sizeof(float)); + file.read(reinterpret_cast(&e.position.z), sizeof(float)); + + // Identifier + uint8_t idLen; + file.read(reinterpret_cast(&idLen), sizeof(uint8_t)); + e.identifier.resize(idLen); + file.read(e.identifier.data(), idLen); + + // Text + uint16_t len; + file.read(reinterpret_cast(&len), sizeof(uint16_t)); + e.text.resize(len); + file.read(e.text.data(), len); + + result.entries.push_back(e); + } + + return result; +} + +void saveCachedFile(const Labelset& labelset, std::filesystem::path path) { + std::ofstream file(path, std::ofstream::binary); + + file.write(reinterpret_cast(&LabelCacheFileVersion), sizeof(int8_t)); + + // + // Storage text color + checkSize(labelset.textColorIndex, "Too high text color"); + int16_t textColorIdx = static_cast(labelset.textColorIndex); + file.write(reinterpret_cast(&textColorIdx), sizeof(int16_t)); + + // + // Storage text lines + checkSize(labelset.entries.size(), "Too many entries"); + uint32_t nEntries = static_cast(labelset.entries.size()); + file.write(reinterpret_cast(&nEntries), sizeof(uint32_t)); + for (const Labelset::Entry& e : labelset.entries) { + file.write(reinterpret_cast(&e.position.x), sizeof(float)); + file.write(reinterpret_cast(&e.position.y), sizeof(float)); + file.write(reinterpret_cast(&e.position.z), sizeof(float)); + + // Identifier + checkSize(e.identifier.size(), "Identifier too long"); + uint8_t idLen = static_cast(e.identifier.size()); + file.write(reinterpret_cast(&idLen), sizeof(uint8_t)); + file.write(e.identifier.data(), idLen); + + // Text + checkSize(e.text.size(), "Text too long"); + uint16_t len = static_cast(e.text.size()); + file.write(reinterpret_cast(&len), sizeof(uint16_t)); + file.write(e.text.data(), len); + } +} + +Labelset loadFileWithCache(std::filesystem::path filePath) { + return internalLoadFileWithCache( + filePath, + std::nullopt, + &loadFile, + &loadCachedFile, + &saveCachedFile + ); +} + +} // namespace label + +namespace color { + +ColorMap loadFile(std::filesystem::path path, std::optional) { + ghoul_assert(std::filesystem::exists(path), "File must exist"); + + std::ifstream file(path); + if (!file.good()) { + throw ghoul::RuntimeError(fmt::format("Failed to open colormap file {}", path)); + } + + std::string extension = ghoul::toLowerCase(path.extension().string()); + + ColorMap res; + if (extension == ".cmap") { + res = speck::loadCmapFile(path); + } + else { + LERRORC("DataLoader", fmt::format( + "Could not read color map file {}. File format {} is not supported", + path, path.extension() + )); + } + + return res; +} + +std::optional loadCachedFile(std::filesystem::path path) { + std::ifstream file(path, std::ios::binary); + if (!file.good()) { + return std::nullopt; + } + + int8_t fileVersion; + file.read(reinterpret_cast(&fileVersion), sizeof(int8_t)); + if (fileVersion != ColorCacheFileVersion) { + // Incompatible version and we won't be able to read the file + return std::nullopt; + } + + ColorMap result; + + uint32_t nColors; + file.read(reinterpret_cast(&nColors), sizeof(uint32_t)); + result.entries.reserve(nColors); + for (unsigned int i = 0; i < nColors; i += 1) { + glm::vec4 color; + file.read(reinterpret_cast(&color.x), sizeof(float)); + file.read(reinterpret_cast(&color.y), sizeof(float)); + file.read(reinterpret_cast(&color.z), sizeof(float)); + file.read(reinterpret_cast(&color.w), sizeof(float)); + result.entries.push_back(color); + } + + glm::vec4 color; + + bool hasBelowColor = false; + file.read(reinterpret_cast(&hasBelowColor), sizeof(bool)); + file.read(reinterpret_cast(&color.x), sizeof(float)); + file.read(reinterpret_cast(&color.y), sizeof(float)); + file.read(reinterpret_cast(&color.z), sizeof(float)); + file.read(reinterpret_cast(&color.w), sizeof(float)); + + if (hasBelowColor) { + result.belowRangeColor = color; + } + + bool hasAboveColor = false; + file.read(reinterpret_cast(&hasAboveColor), sizeof(bool)); + file.read(reinterpret_cast(&color.x), sizeof(float)); + file.read(reinterpret_cast(&color.y), sizeof(float)); + file.read(reinterpret_cast(&color.z), sizeof(float)); + file.read(reinterpret_cast(&color.w), sizeof(float)); + + if (hasAboveColor) { + result.aboveRangeColor = color; + } + + bool hasNanColor = false; + file.read(reinterpret_cast(&hasNanColor), sizeof(bool)); + file.read(reinterpret_cast(&color.x), sizeof(float)); + file.read(reinterpret_cast(&color.y), sizeof(float)); + file.read(reinterpret_cast(&color.z), sizeof(float)); + file.read(reinterpret_cast(&color.w), sizeof(float)); + + if (hasNanColor) { + result.nanColor = color; + } + + return result; +} + +void saveCachedFile(const ColorMap& colorMap, std::filesystem::path path) { + std::ofstream file(path, std::ofstream::binary); + + file.write(reinterpret_cast(&ColorCacheFileVersion), sizeof(int8_t)); + + uint32_t nColors = static_cast(colorMap.entries.size()); + file.write(reinterpret_cast(&nColors), sizeof(uint32_t)); + for (const glm::vec4& color : colorMap.entries) { + file.write(reinterpret_cast(&color.x), sizeof(float)); + file.write(reinterpret_cast(&color.y), sizeof(float)); + file.write(reinterpret_cast(&color.z), sizeof(float)); + file.write(reinterpret_cast(&color.w), sizeof(float)); + } + + bool hasBelowColor = colorMap.belowRangeColor.has_value(); + const glm::vec4 belowColor = colorMap.belowRangeColor.value_or(glm::vec4(0.f)); + file.write(reinterpret_cast(&hasBelowColor), sizeof(bool)); + file.write(reinterpret_cast(&belowColor.x), sizeof(float)); + file.write(reinterpret_cast(&belowColor.y), sizeof(float)); + file.write(reinterpret_cast(&belowColor.z), sizeof(float)); + file.write(reinterpret_cast(&belowColor.w), sizeof(float)); + + bool hasAboveColor = colorMap.aboveRangeColor.has_value(); + const glm::vec4 aboveColor = colorMap.aboveRangeColor.value_or(glm::vec4(0.f)); + file.write(reinterpret_cast(&hasAboveColor), sizeof(bool)); + file.write(reinterpret_cast(&aboveColor.x), sizeof(float)); + file.write(reinterpret_cast(&aboveColor.y), sizeof(float)); + file.write(reinterpret_cast(&aboveColor.z), sizeof(float)); + file.write(reinterpret_cast(&aboveColor.w), sizeof(float)); + + bool hasNanColor = colorMap.nanColor.has_value(); + const glm::vec4 nanColor = colorMap.nanColor.value_or(glm::vec4(0.f)); + file.write(reinterpret_cast(&hasNanColor), sizeof(bool)); + file.write(reinterpret_cast(&nanColor.x), sizeof(float)); + file.write(reinterpret_cast(&nanColor.y), sizeof(float)); + file.write(reinterpret_cast(&nanColor.z), sizeof(float)); + file.write(reinterpret_cast(&nanColor.w), sizeof(float)); +} + +ColorMap loadFileWithCache(std::filesystem::path path) +{ + return internalLoadFileWithCache( + path, + std::nullopt, + &loadFile, + &loadCachedFile, + &saveCachedFile + ); +} + +} // namespace color + +int Dataset::index(std::string_view variableName) const { + for (const Dataset::Variable& v : variables) { + if (v.name == variableName) { + return v.index; + } + } + return -1; +} + +bool Dataset::normalizeVariable(std::string_view variableName) { + int idx = index(variableName); + + if (idx == -1) { + // We didn't find the variable that was specified + return false; + } + + float minValue = std::numeric_limits::max(); + float maxValue = -std::numeric_limits::max(); + for (Dataset::Entry& e : entries) { + float value = e.data[idx]; + if (std::isnan(value)) { + continue; + } + minValue = std::min(minValue, value); + maxValue = std::max(maxValue, value); + } + + for (Dataset::Entry& e : entries) { + float value = e.data[idx]; + if (std::isnan(value)) { + continue; + } + e.data[idx] = (value - minValue) / (maxValue - minValue); + } + + return true; +} + +glm::vec2 Dataset::findValueRange(int variableIndex) const { + if (entries.empty()) { + // Can't find range if there are no entries + return glm::vec2(0.f); + } + + if (variableIndex >= entries[0].data.size()) { + // The index is not a valid variable index + return glm::vec2(0.f); + } + + float minValue = std::numeric_limits::max(); + float maxValue = -std::numeric_limits::max(); + for (const Dataset::Entry& e : entries) { + if (!e.data.empty()) { + float value = e.data[variableIndex]; + if (std::isnan(value)) { + continue; + } + minValue = std::min(value, minValue); + maxValue = std::max(value, maxValue); + } + else { + minValue = 0.f; + maxValue = 0.f; + } + } + + return glm::vec2(minValue, maxValue); +} + +glm::vec2 Dataset::findValueRange(std::string_view variableName) const { + int idx = index(variableName); + + if (idx == -1) { + // We didn't find the variable that was specified + return glm::vec2(0.f); + } + + return findValueRange(idx); +} + +} // namespace openspace::dataloader diff --git a/src/data/datamapping.cpp b/src/data/datamapping.cpp new file mode 100644 index 0000000000..eba72d6178 --- /dev/null +++ b/src/data/datamapping.cpp @@ -0,0 +1,174 @@ +/***************************************************************************************** + * * + * 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 + +namespace { + constexpr std::string_view DefaultX = "x"; + constexpr std::string_view DefaultY = "y"; + constexpr std::string_view DefaultZ = "z"; + + enum class PositionColumn { + X, Y, Z + }; + + bool checkColumnInternal(PositionColumn columnCase, const std::string& c, + const std::optional& mapping, + const std::string_view defaultValue) + { + std::string testColumn = c; + std::string column = std::string(defaultValue); + if (mapping.has_value()) { + switch (columnCase) { + case PositionColumn::X: + column = (*mapping).xColumnName.value_or(column); + break; + case PositionColumn::Y: + column = (*mapping).yColumnName.value_or(column); + break; + case PositionColumn::Z: + column = (*mapping).zColumnName.value_or(column); + break; + } + } + + // Per default, allow both lower case and upper case versions of column names + if (!mapping.has_value() || !(*mapping).isCaseSensitive) { + column = ghoul::toLowerCase(column); + testColumn = ghoul::toLowerCase(testColumn); + } + + return testColumn == column; + } + + // This is a data mapping structure that can be used when creating point cloud + // datasets, e.g. from a CSV or Speck file. + // + // It allows specifying things like column names, whether the reading of those + // column names should be case sensitive, data value that represents missing + // values in the dataset, and more. See details for each field / class member. + // + // Note that things related to reading the point position will not be handled for + // SPECK files, as for those we always expect the first three values per row to + // specify the XYZ position + struct [[codegen::Dictionary(DataMapping)]] Parameters { + // Specifies the column name for the x coordinate + std::optional x; + + // Specifies the column name for the y coordinate + std::optional y; + + // Specifies the column name for the z coordinate + std::optional z; + + // Specifies whether to do case sensitive checks when reading column names. + // Default is not to, so that 'X' and 'x' are both valid column names for the + // x position column, for example + std::optional caseSensitive; + + // Specifies a value that, when read from the file, should be interpreted as 'no + // value', i.e. a missing data value. Note that the same value is used across all + // data columns + std::optional missingDataValue; + + // A list of column names, of columns that will not be loaded into the dataset. + // Note that not all data formats support this. E.g. SPECK files do not + std::optional> excludeColumns; + }; +#include "datamapping_codegen.cpp" +} + +namespace openspace::dataloader { + +documentation::Documentation DataMapping::Documentation() { + return codegen::doc("dataloader_datamapping"); +} + +DataMapping DataMapping::createFromDictionary(const ghoul::Dictionary& dictionary) { + const Parameters p = codegen::bake(dictionary); + + DataMapping result; + + result.xColumnName = p.x; + result.yColumnName = p.y; + result.zColumnName = p.z; + + result.missingDataValue = p.missingDataValue; + + result.isCaseSensitive = p.caseSensitive.value_or(result.isCaseSensitive); + result.excludeColumns = p.excludeColumns.value_or(result.excludeColumns); + + return result; +} + +bool DataMapping::hasExcludeColumns() const { + return !excludeColumns.empty(); +} + +bool DataMapping::isExcludeColumn(std::string_view column) const { + auto found = std::find(excludeColumns.begin(), excludeColumns.end(), column); + return (found != excludeColumns.end()); +} + +std::string generateHashString(const DataMapping& dm) { + std::string a; + for (std::string_view c : dm.excludeColumns) { + a += c; + } + unsigned int excludeColumnsHash = ghoul::hashCRC32(a); + + return fmt::format( + "DM|x{}|y{}|z{}|m{}|{}|{}", + dm.xColumnName.value_or(""), + dm.yColumnName.value_or(""), + dm.zColumnName.value_or(""), + dm.missingDataValue.has_value() ? ghoul::to_string(*dm.missingDataValue) : "", + dm.isCaseSensitive ? "1" : "0", + excludeColumnsHash + ); +} + +bool isPositionColumn(const std::string& c, const std::optional& mapping) { + return isColumnX(c, mapping) || isColumnY(c, mapping) || isColumnZ(c, mapping); +} + +bool isColumnX(const std::string& c, const std::optional& mapping) { + return checkColumnInternal(PositionColumn::X, c, mapping, DefaultX); +} + +bool isColumnY(const std::string& c, const std::optional& mapping) { + return checkColumnInternal(PositionColumn::Y, c, mapping, DefaultY); +} + +bool isColumnZ(const std::string& c, const std::optional& mapping) { + return checkColumnInternal(PositionColumn::Z, c, mapping, DefaultZ); +} + +} // namespace openspace::dataloader diff --git a/modules/space/speckloader.cpp b/src/data/speckloader.cpp similarity index 51% rename from modules/space/speckloader.cpp rename to src/data/speckloader.cpp index 72e07d19d4..ee26953f5e 100644 --- a/modules/space/speckloader.cpp +++ b/src/data/speckloader.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include @@ -30,20 +30,15 @@ #include #include #include +#include #include #include #include #include namespace { - constexpr int8_t DataCacheFileVersion = 10; - constexpr int8_t LabelCacheFileVersion = 11; - constexpr int8_t ColorCacheFileVersion = 10; - bool startsWith(std::string lhs, std::string_view rhs) noexcept { - for (size_t i = 0; i < lhs.size(); i++) { - lhs[i] = static_cast(tolower(lhs[i])); - } + lhs = ghoul::toLowerCase(lhs); return (rhs.size() <= lhs.size()) && (lhs.substr(0, rhs.size()) == rhs); } @@ -70,71 +65,11 @@ namespace { } } - template - void checkSize(U value, std::string_view message) { - if (value > std::numeric_limits::max()) { - throw ghoul::RuntimeError(fmt::format("Error saving file: {}", message)); - } - } - - template - using LoadCacheFunc = std::function(std::filesystem::path)>; - - template - using SaveCacheFunc = std::function; - - template - using LoadSpeckFunc = std::function; - - - - template - T internalLoadFileWithCache(std::filesystem::path speckPath, - openspace::speck::SkipAllZeroLines skipAllZeroLines, - LoadSpeckFunc loadSpeckFunction, - LoadCacheFunc loadCacheFunction, - SaveCacheFunc saveCacheFunction) - { - static_assert( - std::is_same_v || - std::is_same_v || - std::is_same_v - ); - - std::filesystem::path cached = FileSys.cacheManager()->cachedFilename(speckPath); - - if (std::filesystem::exists(cached)) { - LINFOC( - "SpeckLoader", - fmt::format("Cached file {} used for file {}", cached, speckPath) - ); - - std::optional dataset = loadCacheFunction(cached); - if (dataset.has_value()) { - // We could load the cache file and we are now done with this - return *dataset; - } - else { - FileSys.cacheManager()->removeCacheFile(cached); - } - } - LINFOC("SpeckLoader", fmt::format("Loading file {}", speckPath)); - T dataset = loadSpeckFunction(speckPath, skipAllZeroLines); - - if (!dataset.entries.empty()) { - LINFOC("SpeckLoader", "Saving cache"); - saveCacheFunction(dataset, cached); - } - return dataset; - } } // namespace -namespace openspace::speck { +namespace openspace::dataloader::speck { -namespace data { - -Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) { +Dataset loadSpeckFile(std::filesystem::path path, std::optional specs) { ghoul_assert(std::filesystem::exists(path), "File must exist"); std::ifstream file(path); @@ -329,11 +264,19 @@ Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) bool allZero = true; + // For SPECK we know that the first 3 values are the position, so no need to + // check agains data mapping std::stringstream str(line); Dataset::Entry entry; str >> entry.position.x >> entry.position.y >> entry.position.z; allZero &= (entry.position == glm::vec3(0.0)); + glm::vec3 positive = glm::abs(entry.position); + float max = glm::compMax(positive); + if (max > res.maxPositionComponent) { + res.maxPositionComponent = max; + } + if (!str.good()) { // Need to subtract one of the line number here as we increase the current // line count in the beginning of the while loop we are currently in @@ -357,6 +300,15 @@ Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) valueStream.str(value); valueStream >> entry.data[i]; + // Check if value corresponds to a missing value + if (specs.has_value() && specs->missingDataValue.has_value()) { + float missingDataValue = specs->missingDataValue.value(); + float diff = std::abs(entry.data[i] - missingDataValue); + if (diff < std::numeric_limits::epsilon()) { + entry.data[i] = std::numeric_limits::quiet_NaN(); + } + } + allZero &= (entry.data[i] == 0.0); if (valueStream.fail()) { // Need to subtract one of the line number here as we increase the @@ -371,7 +323,7 @@ Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) } } - if (skipAllZeroLines && allZero) { + if (allZero) { continue; } @@ -402,203 +354,12 @@ Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) return res; } -std::optional loadCachedFile(std::filesystem::path path) { - std::ifstream file(path, std::ios::binary); - if (!file.good()) { - return std::nullopt; - } - - Dataset result; - - int8_t fileVersion; - file.read(reinterpret_cast(&fileVersion), sizeof(int8_t)); - if (fileVersion != DataCacheFileVersion) { - // Incompatible version and we won't be able to read the file - return std::nullopt; - } - - // - // Read variables - uint16_t nVariables; - file.read(reinterpret_cast(&nVariables), sizeof(uint16_t)); - result.variables.resize(nVariables); - for (int i = 0; i < nVariables; i += 1) { - Dataset::Variable var; - - int16_t idx; - file.read(reinterpret_cast(&idx), sizeof(int16_t)); - var.index = idx; - - uint16_t len; - file.read(reinterpret_cast(&len), sizeof(uint16_t)); - var.name.resize(len); - file.read(var.name.data(), len); - - result.variables[i] = std::move(var); - } - - // - // Read textures - uint16_t nTextures; - file.read(reinterpret_cast(&nTextures), sizeof(uint16_t)); - result.textures.resize(nTextures); - for (int i = 0; i < nTextures; i += 1) { - Dataset::Texture tex; - - int16_t idx; - file.read(reinterpret_cast(&idx), sizeof(int16_t)); - tex.index = idx; - - uint16_t len; - file.read(reinterpret_cast(&len), sizeof(uint16_t)); - tex.file.resize(len); - file.read(tex.file.data(), len); - - result.textures[i] = std::move(tex); - } - - // - // Read indices - int16_t texDataIdx; - file.read(reinterpret_cast(&texDataIdx), sizeof(int16_t)); - result.textureDataIndex = texDataIdx; - - int16_t oriDataIdx; - file.read(reinterpret_cast(&oriDataIdx), sizeof(int16_t)); - result.orientationDataIndex = oriDataIdx; - - // - // Read entries - uint64_t nEntries; - file.read(reinterpret_cast(&nEntries), sizeof(uint64_t)); - result.entries.reserve(nEntries); - for (uint64_t i = 0; i < nEntries; i += 1) { - Dataset::Entry e; - file.read(reinterpret_cast(&e.position.x), sizeof(float)); - file.read(reinterpret_cast(&e.position.y), sizeof(float)); - file.read(reinterpret_cast(&e.position.z), sizeof(float)); - - uint16_t nValues; - file.read(reinterpret_cast(&nValues), sizeof(uint16_t)); - e.data.resize(nValues); - file.read(reinterpret_cast(e.data.data()), nValues * sizeof(float)); - - uint16_t len; - file.read(reinterpret_cast(&len), sizeof(uint16_t)); - if (len > 0) { - std::string comment; - comment.resize(len); - file.read(comment.data(), len); - e.comment = std::move(comment); - } - - result.entries.push_back(std::move(e)); - } - - return result; -} - -void saveCachedFile(const Dataset& dataset, std::filesystem::path path) { - std::ofstream file(path, std::ofstream::binary); - - file.write(reinterpret_cast(&DataCacheFileVersion), sizeof(int8_t)); - - // - // Store variables - checkSize(dataset.variables.size(), "Too many variables"); - uint16_t nVariables = static_cast(dataset.variables.size()); - file.write(reinterpret_cast(&nVariables), sizeof(uint16_t)); - for (const Dataset::Variable& var : dataset.variables) { - checkSize(var.index, "Variable index too large"); - int16_t idx = static_cast(var.index); - file.write(reinterpret_cast(&idx), sizeof(int16_t)); - - checkSize(var.name.size(), "Variable name too long"); - uint16_t len = static_cast(var.name.size()); - file.write(reinterpret_cast(&len), sizeof(uint16_t)); - file.write(var.name.data(), len); - } - - // - // Store textures - checkSize(dataset.textures.size(), "Too many textures"); - uint16_t nTextures = static_cast(dataset.textures.size()); - file.write(reinterpret_cast(&nTextures), sizeof(uint16_t)); - for (const Dataset::Texture& tex : dataset.textures) { - checkSize(tex.index, "Texture index too large"); - int16_t idx = static_cast(tex.index); - file.write(reinterpret_cast(&idx), sizeof(int16_t)); - - - checkSize(tex.file.size(), "Texture file too long"); - uint16_t len = static_cast(tex.file.size()); - file.write(reinterpret_cast(&len), sizeof(uint16_t)); - file.write(tex.file.data(), len); - } - - // - // Store indices - checkSize(dataset.textureDataIndex, "Texture index too large"); - int16_t texIdx = static_cast(dataset.textureDataIndex); - file.write(reinterpret_cast(&texIdx), sizeof(int16_t)); - - checkSize(dataset.orientationDataIndex, "Orientation index too large"); - int16_t orientationIdx = static_cast(dataset.orientationDataIndex); - file.write(reinterpret_cast(&orientationIdx), sizeof(int16_t)); - - // - // Store entries - checkSize(dataset.entries.size(), "Too many entries"); - uint64_t nEntries = static_cast(dataset.entries.size()); - file.write(reinterpret_cast(&nEntries), sizeof(uint64_t)); - for (const Dataset::Entry& e : dataset.entries) { - file.write(reinterpret_cast(&e.position.x), sizeof(float)); - file.write(reinterpret_cast(&e.position.y), sizeof(float)); - file.write(reinterpret_cast(&e.position.z), sizeof(float)); - - checkSize(e.data.size(), "Too many data variables"); - uint16_t nValues = static_cast(e.data.size()); - file.write(reinterpret_cast(&nValues), sizeof(uint16_t)); - file.write( - reinterpret_cast(e.data.data()), - e.data.size() * sizeof(float) - ); - - if (e.comment.has_value()) { - checkSize(e.comment->size(), "Comment too long"); - } - uint16_t commentLen = e.comment.has_value() ? - static_cast(e.comment->size()) : - 0; - file.write(reinterpret_cast(&commentLen), sizeof(uint16_t)); - if (e.comment.has_value()) { - file.write(e.comment->data(), e.comment->size()); - } - } -} - -Dataset loadFileWithCache(std::filesystem::path speckPath, - SkipAllZeroLines skipAllZeroLines) -{ - return internalLoadFileWithCache( - speckPath, - skipAllZeroLines, - &loadFile, - &loadCachedFile, - &saveCachedFile - ); -} - -} // namespace data - -namespace label { - -Labelset loadFile(std::filesystem::path path, SkipAllZeroLines) { +Labelset loadLabelFile(std::filesystem::path path) { ghoul_assert(std::filesystem::exists(path), "File must exist"); std::ifstream file(path); if (!file.good()) { - throw ghoul::RuntimeError(fmt::format("Failed to open speck file {}", path)); + throw ghoul::RuntimeError(fmt::format("Failed to open dataset file {}", path)); } Labelset res; @@ -637,7 +398,6 @@ Labelset loadFile(std::filesystem::path path, SkipAllZeroLines) { )); } - std::stringstream str(line); std::string dummy; str >> dummy >> res.textColorIndex; @@ -728,117 +488,17 @@ Labelset loadFile(std::filesystem::path path, SkipAllZeroLines) { return res; } -std::optional loadCachedFile(std::filesystem::path path) { - std::ifstream file(path, std::ios::binary); - if (!file.good()) { - return std::nullopt; - } - - int8_t fileVersion; - file.read(reinterpret_cast(&fileVersion), sizeof(int8_t)); - if (fileVersion != LabelCacheFileVersion) { - // Incompatible version and we won't be able to read the file - return std::nullopt; - } - - Labelset result; - - int16_t textColorIdx; - file.read(reinterpret_cast(&textColorIdx), sizeof(int16_t)); - result.textColorIndex = textColorIdx; - - uint32_t nEntries; - file.read(reinterpret_cast(&nEntries), sizeof(uint32_t)); - result.entries.reserve(nEntries); - for (unsigned int i = 0; i < nEntries; i += 1) { - Labelset::Entry e; - file.read(reinterpret_cast(&e.position.x), sizeof(float)); - file.read(reinterpret_cast(&e.position.y), sizeof(float)); - file.read(reinterpret_cast(&e.position.z), sizeof(float)); - - // Identifier - uint8_t idLen; - file.read(reinterpret_cast(&idLen), sizeof(uint8_t)); - e.identifier.resize(idLen); - file.read(e.identifier.data(), idLen); - - // Text - uint16_t len; - file.read(reinterpret_cast(&len), sizeof(uint16_t)); - e.text.resize(len); - file.read(e.text.data(), len); - - result.entries.push_back(e); - } - - return result; -} - -void saveCachedFile(const Labelset& labelset, std::filesystem::path path) { - std::ofstream file(path, std::ofstream::binary); - - file.write(reinterpret_cast(&LabelCacheFileVersion), sizeof(int8_t)); - - // - // Storage text color - checkSize(labelset.textColorIndex, "Too high text color"); - int16_t textColorIdx = static_cast(labelset.textColorIndex); - file.write(reinterpret_cast(&textColorIdx), sizeof(int16_t)); - - // - // Storage text lines - checkSize(labelset.entries.size(), "Too many entries"); - uint32_t nEntries = static_cast(labelset.entries.size()); - file.write(reinterpret_cast(&nEntries), sizeof(uint32_t)); - for (const Labelset::Entry& e : labelset.entries) { - file.write(reinterpret_cast(&e.position.x), sizeof(float)); - file.write(reinterpret_cast(&e.position.y), sizeof(float)); - file.write(reinterpret_cast(&e.position.z), sizeof(float)); - - // Identifier - checkSize(e.identifier.size(), "Identifier too long"); - uint8_t idLen = static_cast(e.identifier.size()); - file.write(reinterpret_cast(&idLen), sizeof(uint8_t)); - file.write(e.identifier.data(), idLen); - - // Text - checkSize(e.text.size(), "Text too long"); - uint16_t len = static_cast(e.text.size()); - file.write(reinterpret_cast(&len), sizeof(uint16_t)); - file.write(e.text.data(), len); - } -} - -Labelset loadFileWithCache(std::filesystem::path speckPath, - SkipAllZeroLines skipAllZeroLines) -{ - return internalLoadFileWithCache( - speckPath, - skipAllZeroLines, - &loadFile, - &loadCachedFile, - &saveCachedFile - ); -} - -} // namespace label - -namespace color { - -ColorMap loadFile(std::filesystem::path path, SkipAllZeroLines) { +ColorMap loadCmapFile(std::filesystem::path path) { ghoul_assert(std::filesystem::exists(path), "File must exist"); std::ifstream file(path); if (!file.good()) { - throw ghoul::RuntimeError(fmt::format("Failed to open speck file {}", path)); + throw ghoul::RuntimeError(fmt::format("Failed to open color map file {}", path)); } ColorMap res; int nColorLines = -1; - // The beginning of the speck file has a header that either contains comments - // (signaled by a preceding '#') or information about the structure of the file - // (signaled by the keywords 'datavar', 'texturevar', and 'texture') std::string line; while (std::getline(file, line)) { // Ignore empty line or commented-out lines @@ -854,11 +514,11 @@ ColorMap loadFile(std::filesystem::path path, SkipAllZeroLines) { strip(line); - std::stringstream str(line); if (nColorLines == -1) { // This is the first time we get this far, it will have to be the first number // meaning that it is the number of color values + std::stringstream str(line); str >> nColorLines; res.entries.reserve(nColorLines); } @@ -867,114 +527,43 @@ ColorMap loadFile(std::filesystem::path path, SkipAllZeroLines) { // reading the individual value lines glm::vec4 color; - str >> color.x >> color.y >> color.z >> color.w; - res.entries.push_back(std::move(color)); + std::string dummy; + // Note that startwith converts the input string to all lowercase + if (startsWith(line, "belowrange")) { + std::stringstream str(line); + str >> dummy >> color.x >> color.y >> color.z >> color.w; + res.belowRangeColor = color; + } + else if (startsWith(line, "aboverange")) { + std::stringstream str(line); + str >> dummy >> color.x >> color.y >> color.z >> color.w; + res.aboveRangeColor = color; + } + else if (startsWith(line, "nan")) { + std::stringstream str(line); + str >> dummy >> color.x >> color.y >> color.z >> color.w; + res.nanColor = color; + } + else { + // TODO: Catch when this is not a color! + std::stringstream str(line); + str >> color.x >> color.y >> color.z >> color.w; + res.entries.push_back(std::move(color)); + } } } + res.entries.shrink_to_fit(); + if (nColorLines != static_cast(res.entries.size())) { LWARNINGC("SpeckLoader", fmt::format( - "While loading color map, the expected number of color values '{}' was " + "While loading color map '{}', the expected number of color values '{}' was " "different from the actual number of color values '{}'", - nColorLines, res.entries.size() + path, nColorLines, res.entries.size() )); } return res; } -std::optional loadCachedFile(std::filesystem::path path) { - std::ifstream file(path, std::ios::binary); - if (!file.good()) { - return std::nullopt; - } - - int8_t fileVersion; - file.read(reinterpret_cast(&fileVersion), sizeof(int8_t)); - if (fileVersion != ColorCacheFileVersion) { - // Incompatible version and we won't be able to read the file - return std::nullopt; - } - - ColorMap result; - - uint32_t nColors; - file.read(reinterpret_cast(&nColors), sizeof(uint32_t)); - result.entries.reserve(nColors); - for (unsigned int i = 0; i < nColors; i += 1) { - glm::vec4 color; - file.read(reinterpret_cast(&color.x), sizeof(float)); - file.read(reinterpret_cast(&color.y), sizeof(float)); - file.read(reinterpret_cast(&color.z), sizeof(float)); - file.read(reinterpret_cast(&color.w), sizeof(float)); - result.entries.push_back(color); - } - return result; -} - -void saveCachedFile(const ColorMap& colorMap, std::filesystem::path path) { - std::ofstream file(path, std::ofstream::binary); - - file.write(reinterpret_cast(&ColorCacheFileVersion), sizeof(int8_t)); - - uint32_t nColors = static_cast(colorMap.entries.size()); - file.write(reinterpret_cast(&nColors), sizeof(uint32_t)); - for (const glm::vec4& color : colorMap.entries) { - file.write(reinterpret_cast(&color.x), sizeof(float)); - file.write(reinterpret_cast(&color.y), sizeof(float)); - file.write(reinterpret_cast(&color.z), sizeof(float)); - file.write(reinterpret_cast(&color.w), sizeof(float)); - } -} - -ColorMap loadFileWithCache(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) -{ - return internalLoadFileWithCache( - path, - skipAllZeroLines, - &loadFile, - &loadCachedFile, - &saveCachedFile - ); -} - -} // namespace color - -int Dataset::index(std::string_view variableName) const { - for (const Dataset::Variable& v : variables) { - if (v.name == variableName) { - return v.index; - } - } - return -1; -} - -bool Dataset::normalizeVariable(std::string_view variableName) { - std::optional idx; - for (const Dataset::Variable& var : variables) { - if (var.name == variableName) { - idx = var.index; - break; - } - } - - if (!idx.has_value()) { - // We didn't find the variable that was specified - return false; - } - - float minValue = std::numeric_limits::max(); - float maxValue = -std::numeric_limits::max(); - for (Dataset::Entry& e : entries) { - minValue = std::min(minValue, e.data[*idx]); - maxValue = std::max(maxValue, e.data[*idx]); - } - - for (Dataset::Entry& e : entries) { - e.data[*idx] = (e.data[*idx] - minValue) / (maxValue - minValue); - } - - return true; -} - -} // namespace openspace::speck +} // namespace openspace::dataloader::speck diff --git a/src/documentation/core_registration.cpp b/src/documentation/core_registration.cpp index 5e8b02345c..bdec3fa0a9 100644 --- a/src/documentation/core_registration.cpp +++ b/src/documentation/core_registration.cpp @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -38,7 +39,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -62,6 +65,8 @@ namespace openspace { void registerCoreClasses(documentation::DocumentationEngine& engine) { engine.addDocumentation(LogFactoryDocumentation()); + engine.addDocumentation(ColorMappingComponent::Documentation()); + engine.addDocumentation(LabelsComponent::Documentation()); engine.addDocumentation(LightSource::Documentation()); engine.addDocumentation(Mission::Documentation()); engine.addDocumentation(Renderable::Documentation()); @@ -73,6 +78,8 @@ void registerCoreClasses(documentation::DocumentationEngine& engine) { engine.addDocumentation(Translation::Documentation()); engine.addDocumentation(TimeFrame::Documentation()); + engine.addDocumentation(dataloader::DataMapping::Documentation()); + engine.addDocumentation(interaction::NavigationState::Documentation()); engine.addDocumentation(interaction::Path::Documentation()); } diff --git a/src/rendering/colormappingcomponent.cpp b/src/rendering/colormappingcomponent.cpp new file mode 100644 index 0000000000..f20038b392 --- /dev/null +++ b/src/rendering/colormappingcomponent.cpp @@ -0,0 +1,470 @@ +/***************************************************************************************** + * * + * 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 + +namespace { + constexpr std::string_view _loggerCat = "ColorMapping"; + + constexpr openspace::properties::Property::PropertyInfo EnabledInfo = { + "Enabled", + "Color Map Enabled", + "If this value is set to 'true', the provided color map is used (if one was " + "provided in the configuration). If no color map was provided, changing this " + "setting does not do anything", + openspace::properties::Property::Visibility::NoviceUser + }; + + constexpr openspace::properties::Property::PropertyInfo FileInfo = { + "File", + "Color Map File", + "The path to the color map file to use for coloring the points", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo ParameterInfo = { + "Parameter", + "Parameter", + "This value determines which paramenter is used for coloring the points based " + "on the color map. The property is set based on predefined options specified in " + "the asset file. When changing the parameter, the value range to used for the" + "mapping will also be changed. Per default, it is set to the last parameter in " + "the list of options", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo RangeInfo = { + "ValueRange", + "Value Range", + "This value changes the range of values to be mapped with the current color map", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo SetRangeFromDataInfo = { + "SetRangeFromData", + "Set Data Range from Data", + "Set the data range for the color mapping based on the available data for the " + "curently selected data column", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo HideOutsideInfo = { + "HideValuesOutsideRange", + "Hide Values Outside Range", + "If true, points with values outside the provided range for the coloring will be " + "hidden, i.e. not rendered at all", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo UseNoDataColorInfo = { + "ShowMissingData", + "Show Missing Data", + "If true, use a separate color (see NoDataColor) for items with values " + "corresponding to missing data values", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo NoDataColorInfo = { + "NoDataColor", + "No Data Color", + "The color to use for items with values corresponding to missing data values, " + "if enabled. This color can also be read from the color map, but setting this " + "value overrides any value in the color map. If a color value for the below " + "range values is provided, the ShowMissingData property will automatically be " + "set to true", + openspace::properties::Property::Visibility::User + }; + + constexpr openspace::properties::Property::PropertyInfo UseAboveRangeColorInfo = { + "UseAboveRangeColor", + "Use Above Range Color", + "If true, use a separate color (see AboveRangeColor) for items with values " + "larger than the one in the provided data range. Otherwise, the values will " + "be clamped to use the color at the upper limit of the color map. If a color is " + "provided in the color map, this value will automatically be set to true", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo AboveRangeColorInfo = { + "AboveRangeColor", + "Above Range Color", + "The color to use for items with values larger than the one in the provided " + "data range, if enabled. This color can also be read from the color map, but " + "setting this value overrides any value in the color map. If a color value for " + "the above range values is provided, the UseAboveRangeColor property will " + "automatically be set to true", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo UseBelowRangeColorInfo = { + "UseBelowRangeColor", + "Use Below Range Color", + "If true, use a separate color (see BelowRangeColor) for items with values " + "smaller than the one in the provided data range. Otherwise, the values will " + "be clamped to use the color at the lower limit of the color map. If a color is " + "provided in the color map, this value will automatically be set to true", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo BelowRangeColorInfo = { + "BelowRangeColor", + "Below Range Color", + "The color to use for items with values smaller than the one in the provided " + "data range, if enabled. This color can also be read from the color map, but " + "setting this value overrides any value in the color map. If a color value for " + "the below range values is provided, the UseBelowRangeColor property will " + "automatically be set to true", + openspace::properties::Property::Visibility::AdvancedUser + }; + + struct [[codegen::Dictionary(ColorMappingComponent)]] Parameters { + // [[codegen::verbatim(EnabledInfo.description)]] + std::optional enabled; + + // [[codegen::verbatim(FileInfo.description)]] + std::optional file; + + struct ColorMapParameter { + // The key for the data variable to use for color + std::string key; + + // An optional value range to use for coloring when this option is selected. + // If not included, the range will be set from the min and max value in the + // dataset + std::optional range; + }; + // A list of options for color parameters to use for color mapping, that will + // appear as options in the drop-down menu in the user interface. Per default, + // the first option in the list is selected. Each option is a table in the form + // { Key = \"theKey\", Range = {min, max} }, where the value range is optional. + // If added, this range will automatically be set when the option is selected + std::optional> parameterOptions; + + // [[codegen::verbatim(ParameterInfo.description)]] + std::optional parameter; + + // [[codegen::verbatim(RangeInfo.description)]] + std::optional valueRange; + + // [[codegen::verbatim(HideOutsideInfo.description)]] + std::optional hideValuesOutsideRange; + + // [[codegen::verbatim(UseNoDataColorInfo.description)]] + std::optional showMissingData; + + // [[codegen::verbatim(NoDataColorInfo.description)]] + std::optional noDataColor [[codegen::color()]]; + + // [[codegen::verbatim(UseAboveRangeColorInfo.description)]] + std::optional useAboveRangeColor; + + // [[codegen::verbatim(AboveRangeColorInfo.description)]] + std::optional aboveRangeColor [[codegen::color()]]; + + // [[codegen::verbatim(UseBelowRangeColorInfo.description)]] + std::optional useBelowRangeColor; + + // [[codegen::verbatim(BelowRangeColorInfo.description)]] + std::optional belowRangeColor [[codegen::color()]]; + }; +#include "colormappingcomponent_codegen.cpp" +} // namespace + +namespace openspace { + +documentation::Documentation ColorMappingComponent::Documentation() { + return codegen::doc("colormappingcomponent"); +} + +ColorMappingComponent::ColorMappingComponent() + : properties::PropertyOwner({ "ColorMapping", "Color Mapping", "" }) + , enabled(EnabledInfo, true) + , dataColumn(ParameterInfo, properties::OptionProperty::DisplayType::Dropdown) + , colorMapFile(FileInfo) + , valueRange(RangeInfo, glm::vec2(0.f)) + , setRangeFromData(SetRangeFromDataInfo) + , hideOutsideRange(HideOutsideInfo, false) + , useNanColor(UseNoDataColorInfo, false) + , nanColor( + NoDataColorInfo, + glm::vec4(0.5f, 0.5f, 0.5f, 1.f), + glm::vec4(0.f), + glm::vec4(1.f) + ) + , useAboveRangeColor(UseAboveRangeColorInfo, false) + , aboveRangeColor(AboveRangeColorInfo, glm::vec4(1.f), glm::vec4(0.f), glm::vec4(1.f)) + , useBelowRangeColor(UseBelowRangeColorInfo, false) + , belowRangeColor(BelowRangeColorInfo, glm::vec4(1.f), glm::vec4(0.f), glm::vec4(1.f)) +{ + addProperty(enabled); + addProperty(dataColumn); + + addProperty(valueRange); + addProperty(setRangeFromData); + + colorMapFile.setReadOnly(true); // Currently this can't be changed + addProperty(colorMapFile); + + addProperty(hideOutsideRange); + addProperty(useNanColor); + nanColor.setViewOption(properties::Property::ViewOptions::Color); + addProperty(nanColor); + + addProperty(useAboveRangeColor); + aboveRangeColor.setViewOption(properties::Property::ViewOptions::Color); + addProperty(aboveRangeColor); + + addProperty(useBelowRangeColor); + belowRangeColor.setViewOption(properties::Property::ViewOptions::Color); + addProperty(belowRangeColor); +} + +ColorMappingComponent::ColorMappingComponent(const ghoul::Dictionary& dictionary) + : ColorMappingComponent() +{ + const Parameters p = codegen::bake(dictionary); + + enabled = p.enabled.value_or(enabled); + + if (p.parameterOptions.has_value()) { + std::vector opts = *p.parameterOptions; + + _colorRangeData.reserve(opts.size()); + for (size_t i = 0; i < opts.size(); ++i) { + dataColumn.addOption(static_cast(i), opts[i].key); + // Add the provided range or an empty range. We will fill it later on, + // when the dataset is loaded, if it is empty + _colorRangeData.push_back(opts[i].range.value_or(glm::vec2(0.f))); + } + } + + dataColumn.onChange([this]() { + valueRange = _colorRangeData[dataColumn.value()]; + }); + + _providedParameter = p.parameter; + _providedRange = p.valueRange; + + hideOutsideRange = p.hideValuesOutsideRange.value_or(hideOutsideRange); + + useNanColor = p.showMissingData.value_or(useNanColor); + if (p.noDataColor.has_value()) { + useNanColor = p.showMissingData.value_or(true); + nanColor = *p.noDataColor; + _hasNanColorInAsset = true; + } + + useAboveRangeColor = p.useAboveRangeColor.value_or(useAboveRangeColor); + if (p.aboveRangeColor.has_value()) { + useAboveRangeColor = p.useAboveRangeColor.value_or(true); + aboveRangeColor = *p.aboveRangeColor; + _hasAboveRangeColorInAsset = true; + } + + useBelowRangeColor = p.useBelowRangeColor.value_or(useBelowRangeColor); + if (p.belowRangeColor.has_value()) { + useBelowRangeColor = p.useBelowRangeColor.value_or(true); + belowRangeColor = *p.belowRangeColor; + _hasBelowRangeColorInAsset = true; + } + + if (p.file.has_value()) { + colorMapFile = absPath(*p.file).string(); + } +} + +ghoul::opengl::Texture* ColorMappingComponent::texture() const { + return _texture.get(); +} + +void ColorMappingComponent::initialize(const dataloader::Dataset& dataset) { + _colorMap = dataloader::color::loadFileWithCache(colorMapFile.value()); + + initializeParameterData(dataset); + + if (_colorMap.nanColor.has_value() && !_hasNanColorInAsset) { + nanColor = *_colorMap.nanColor; + useNanColor = true; // @ TODO: Avoid overriding value set in asset? (also for useBelow and useAbove) + } + + if (_colorMap.belowRangeColor.has_value() && !_hasBelowRangeColorInAsset) { + belowRangeColor = *_colorMap.belowRangeColor; + useBelowRangeColor = true; + } + + if (_colorMap.aboveRangeColor.has_value() && !_hasAboveRangeColorInAsset) { + aboveRangeColor = *_colorMap.aboveRangeColor; + useAboveRangeColor = true; + } +} + +void ColorMappingComponent::initializeTexture() { + if (_colorMap.entries.empty()) { + return; + } + + unsigned int width = static_cast(_colorMap.entries.size()); + unsigned int height = 1; + unsigned int size = width * height; + std::vector img; + img.reserve(size * 4); + + for (const glm::vec4& c : _colorMap.entries) { + img.push_back(static_cast(255 * c.r)); + img.push_back(static_cast(255 * c.g)); + img.push_back(static_cast(255 * c.b)); + img.push_back(static_cast(255 * c.a)); + } + + _texture = std::make_unique( + glm::uvec3(width, height, 1), + GL_TEXTURE_1D, + ghoul::opengl::Texture::Format::RGBA + + ); + + // TODO: update this for linear mapping? + _texture->setFilter(ghoul::opengl::Texture::FilterMode::Nearest); + _texture->setWrapping(ghoul::opengl::Texture::WrappingMode::ClampToEdge); + _texture->setPixelData( + reinterpret_cast(img.data()), + ghoul::opengl::Texture::TakeOwnership::No + ); + + _texture->uploadTexture(); +} + +glm::vec4 ColorMappingComponent::colorFromColorMap(float valueToColorFrom) const { + glm::vec2 currentColorRange = valueRange; + float cmax = currentColorRange.y; + float cmin = currentColorRange.x; + + float nColors = static_cast(_colorMap.entries.size()); + + bool isOutsideMin = valueToColorFrom < cmin; + bool isOutsideMax = valueToColorFrom > cmax; + + if (hideOutsideRange && (isOutsideMin || isOutsideMax)) { + return glm::vec4(0.f); + } + + if (useNanColor && std::isnan(valueToColorFrom)) { + return nanColor; + } + + // Find color value using Nearest neighbor (same as texture) + float normalization = (cmax != cmin) ? (nColors) / (cmax - cmin) : 0; + int colorIndex = static_cast((valueToColorFrom - cmin) * normalization); + + // Clamp color index to valid range + colorIndex = std::max(colorIndex, 0); + colorIndex = std::min(colorIndex, static_cast(nColors) - 1); + + return _colorMap.entries[colorIndex]; +} + +void ColorMappingComponent::initializeParameterData(const dataloader::Dataset& dataset) { + // Initialize empty ranges based on values in the dataset + for (const properties::OptionProperty::Option& option : dataColumn.options()) { + int optionIndex = option.value; + int colorParameterIndex = dataset.index(option.description); + + glm::vec2& range = _colorRangeData[optionIndex]; + if (glm::length(range) < glm::epsilon()) { + range = dataset.findValueRange(colorParameterIndex); + } + } + + // Index to keep track of the potentially provided default option for the parameter + int indexOfProvidedOption = -1; + + // If no options were added, add each dataset parameter and its range as options + if (dataColumn.options().empty() && !dataset.entries.empty()) { + int i = 0; + _colorRangeData.reserve(dataset.variables.size()); + for (const dataloader::Dataset::Variable& v : dataset.variables) { + dataColumn.addOption(i, v.name); + _colorRangeData.push_back(dataset.findValueRange(v.index)); + + // While iterating over options, try to find the one provided, if any + if (_providedParameter.has_value() && *_providedParameter == v.name) { + indexOfProvidedOption = i; + } + + i++; + } + } + else { + // Otherwise, check if the selected columns exist + for (size_t i = 0; i < dataColumn.options().size(); ++i) { + std::string o = dataColumn.options()[i].description; + + bool found = false; + for (const dataloader::Dataset::Variable& v : dataset.variables) { + if (v.name == o) { + found = true; + break; + } + } + + if (!found) { + LWARNING(fmt::format( + "Dataset does not contain specified parameter '{}'", o + )); + } + // While iterating over options, try to find the one provided, if any + else if (_providedParameter.has_value() && *_providedParameter == o) { + indexOfProvidedOption = i; + } + } + } + + if (_providedParameter.has_value()) { + if (indexOfProvidedOption == -1) { + LERROR(fmt::format( + "Error when reading Parameter. Could not find provided parameter '{}' in " + "list of parameter options", *_providedParameter + )); + } + else { + dataColumn = indexOfProvidedOption; + } + } + + // Set the value range to correspond to the selected data column, or the one set by + // the user + if (_providedRange.has_value()) { + valueRange = *_providedRange; + } + else { + valueRange = _colorRangeData[dataColumn.value()]; + } +} + +} // namespace openspace diff --git a/modules/space/labelscomponent.cpp b/src/rendering/labelscomponent.cpp similarity index 92% rename from modules/space/labelscomponent.cpp rename to src/rendering/labelscomponent.cpp index 3beb097f64..983d5c8285 100644 --- a/modules/space/labelscomponent.cpp +++ b/src/rendering/labelscomponent.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include @@ -107,6 +107,12 @@ namespace { // [[codegen::verbatim(FileInfo.description)]] std::filesystem::path file; + // If true (default), the loaded labels file will be cached so that it can be + // loaded faster at a later time. Note that this also means that changes in the + // file will not be registered until the cached file is deleted. Set to false + // to disable chaching and always do a fresh load of the label file + std::optional useCaching; + // The opacity of the labels std::optional opacity [[codegen::inrange(0.0, 1.0)]]; @@ -145,7 +151,7 @@ namespace { namespace openspace { documentation::Documentation LabelsComponent::Documentation() { - return codegen::doc("space_labelscomponent"); + return codegen::doc("labelscomponent"); } LabelsComponent::LabelsComponent(const ghoul::Dictionary& dictionary) @@ -165,6 +171,7 @@ LabelsComponent::LabelsComponent(const ghoul::Dictionary& dictionary) const Parameters p = codegen::bake(dictionary); _labelFile = absPath(p.file); + _useCache = p.useCaching.value_or(true); if (p.unit.has_value()) { _unit = codegen::map(*p.unit); @@ -216,11 +223,11 @@ LabelsComponent::LabelsComponent(const ghoul::Dictionary& dictionary) _transformationMatrix = p.transformationMatrix.value_or(_transformationMatrix); } -speck::Labelset& LabelsComponent::labelSet() { +dataloader::Labelset& LabelsComponent::labelSet() { return _labelset; } -const speck::Labelset& LabelsComponent::labelSet() const { +const dataloader::Labelset& LabelsComponent::labelSet() const { return _labelset; } @@ -235,7 +242,12 @@ void LabelsComponent::initialize() { void LabelsComponent::loadLabels() { LINFO(fmt::format("Loading label file {}", _labelFile)); - _labelset = speck::label::loadFileWithCache(_labelFile); + if (_useCache) { + _labelset = dataloader::label::loadFileWithCache(_labelFile); + } + else { + _labelset = dataloader::label::loadFile(_labelFile); + } } bool LabelsComponent::isReady() const { @@ -273,7 +285,7 @@ void LabelsComponent::render(const RenderData& data, glm::vec4 textColor = glm::vec4(glm::vec3(_color), opacity() * fadeInVariable); - for (const speck::Labelset::Entry& e : _labelset.entries) { + for (const dataloader::Labelset::Entry& e : _labelset.entries) { if (!e.isEnabled) { continue; }