From 0c33e2a2b693601200b802a4b09535a9738498cd Mon Sep 17 00:00:00 2001 From: Kalle Bladin Date: Mon, 18 Apr 2016 19:52:01 -0400 Subject: [PATCH] Calculating uv coordinates to be used for sampling from tiled textures. --- .../globebrowsing/rendering/patchrenderer.cpp | 28 ++-- .../globebrowsing/rendering/patchrenderer.h | 2 +- .../rendering/texturetileset.cpp | 62 +++++++- .../globebrowsing/rendering/texturetileset.h | 4 +- modules/globebrowsing/shaders/simple_fs.glsl | 4 +- tests/main.cpp | 5 +- tests/test_texturetileset.inl | 148 ++++++++++++++++++ 7 files changed, 230 insertions(+), 23 deletions(-) create mode 100644 tests/test_texturetileset.inl diff --git a/modules/globebrowsing/rendering/patchrenderer.cpp b/modules/globebrowsing/rendering/patchrenderer.cpp index 035c3ce994..e9cf77710d 100644 --- a/modules/globebrowsing/rendering/patchrenderer.cpp +++ b/modules/globebrowsing/rendering/patchrenderer.cpp @@ -57,6 +57,7 @@ namespace openspace { ////////////////////////////////////////////////////////////////////////////////////// PatchRenderer::PatchRenderer(shared_ptr geometry) : _geometry(geometry) + , _tileSet(LatLon(M_PI, M_PI * 2), LatLon(M_PI / 2, - M_PI), 0) { } @@ -104,16 +105,18 @@ namespace openspace { * viewTransform * modelTransform; // Get the textures that should be used for rendering - glm::ivec3 tileIndex = tileSet.getTileIndex(patch); - LatLonPatch tilePatch = tileSet.getTilePositionAndScale(tileIndex); - std::shared_ptr tile00 = tileSet.getTile(tileIndex); + glm::ivec3 tileIndex = _tileSet.getTileIndex(patch); + LatLonPatch tilePatch = _tileSet.getTilePositionAndScale(tileIndex); + std::shared_ptr tile00 = _tileSet.getTile(tileIndex); + glm::mat3 uvTransform = _tileSet.getUvTransformationPatchToTile(patch, tileIndex); // Bind and use the texture ghoul::opengl::TextureUnit texUnit; texUnit.activate(); tile00->bind(); _programObject->setUniform("textureSampler", texUnit); - + _programObject->setUniform("uvTransformPatchToTile", uvTransform); + LatLon swCorner = patch.southWestCorner(); _programObject->setUniform("modelViewProjectionTransform", modelViewProjectionTransform); _programObject->setUniform("minLatLon", vec2(swCorner.toLonLatVec2())); @@ -171,9 +174,10 @@ namespace openspace { ivec2 intSnapCoord = ivec2( patch.center().lat / (M_PI * 2) * segmentsPerPatch * patchesToCoverGlobe.y, patch.center().lon / (M_PI) * segmentsPerPatch * patchesToCoverGlobe.x); - LatLon swCorner = LatLon( - stepSize.lat * intSnapCoord.x - halfSize.lat, - stepSize.lon * intSnapCoord.y - halfSize.lon); + LatLon newPatchCenter = LatLon( + stepSize.lat * intSnapCoord.x, + stepSize.lon * intSnapCoord.y); + LatLonPatch newPatch(newPatchCenter, patch.halfSize()); ivec2 contraction = ivec2(intSnapCoord.y % 2, intSnapCoord.x % 2); @@ -183,18 +187,20 @@ namespace openspace { // Get the textures that should be used for rendering - glm::ivec3 tileIndex = tileSet.getTileIndex(patch); - LatLonPatch tilePatch = tileSet.getTilePositionAndScale(tileIndex); - std::shared_ptr tile00 = tileSet.getTile(tileIndex); + glm::ivec3 tileIndex = _tileSet.getTileIndex(patch); + LatLonPatch tilePatch = _tileSet.getTilePositionAndScale(tileIndex); + std::shared_ptr tile00 = _tileSet.getTile(tileIndex); + glm::mat3 uvTransform = _tileSet.getUvTransformationPatchToTile(newPatch, tileIndex); // Bind and use the texture ghoul::opengl::TextureUnit texUnit; texUnit.activate(); tile00->bind(); _programObject->setUniform("textureSampler", texUnit); + _programObject->setUniform("uvTransformPatchToTile", mat3(uvTransform)); _programObject->setUniform("modelViewProjectionTransform", data.camera.projectionMatrix() * viewTransform * modelTransform); - _programObject->setUniform("minLatLon", vec2(swCorner.toLonLatVec2())); + _programObject->setUniform("minLatLon", vec2(newPatch.southWestCorner().toLonLatVec2())); _programObject->setUniform("lonLatScalingFactor", 2.0f * vec2(halfSize.toLonLatVec2())); _programObject->setUniform("globeRadius", float(radius)); _programObject->setUniform("contraction", contraction); diff --git a/modules/globebrowsing/rendering/patchrenderer.h b/modules/globebrowsing/rendering/patchrenderer.h index 3e48f925b3..cd1ed588ae 100644 --- a/modules/globebrowsing/rendering/patchrenderer.h +++ b/modules/globebrowsing/rendering/patchrenderer.h @@ -67,7 +67,7 @@ namespace openspace { unique_ptr _programObject; shared_ptr _geometry; - TextureTileSet tileSet; + TextureTileSet _tileSet; }; diff --git a/modules/globebrowsing/rendering/texturetileset.cpp b/modules/globebrowsing/rendering/texturetileset.cpp index 83e6dce477..912ca10fd2 100644 --- a/modules/globebrowsing/rendering/texturetileset.cpp +++ b/modules/globebrowsing/rendering/texturetileset.cpp @@ -36,8 +36,13 @@ namespace { } namespace openspace { - TextureTileSet::TextureTileSet() - { + TextureTileSet::TextureTileSet(LatLon sizeLevel0, LatLon offsetLevel0, int depth) + : _sizeLevel0(sizeLevel0) + , _offsetLevel0(offsetLevel0) + , _depth(depth) + { + + // Set e texture to test _testTexture = std::move(ghoul::io::TextureReader::ref().loadTexture(absPath("textures/earth_bluemarble.jpg"))); if (_testTexture) { LDEBUG("Loaded texture from '" << "textures/earth_bluemarble.jpg" << "'"); @@ -48,6 +53,16 @@ namespace openspace { //_testTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); _testTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); } + /* + int dataSize = _testTexture->width() * _testTexture->height() * _testTexture->bytesPerPixel(); + GLubyte* data = new GLubyte[dataSize]; + for (size_t i = 0; i < dataSize; i++) + { + data[i] = unsigned char(i / float(dataSize) * 255); + } + _testTexture->setPixelData(data); + _testTexture->uploadTexture(); + */ } @@ -58,10 +73,16 @@ namespace openspace { glm::ivec3 TextureTileSet::getTileIndex(LatLonPatch patch) { int level = log2(static_cast(glm::max( - _sizeLevel0.lat / patch.halfSize().lat * 2, - _sizeLevel0.lon / patch.halfSize().lon * 2))); - Vec2 TileSize = _sizeLevel0.toLonLatVec2() / pow(2, level); - glm::ivec2 tileIndex = -(patch.northWestCorner().toLonLatVec2() + _offsetLevel0.toLonLatVec2()) / TileSize; + _sizeLevel0.lat / (patch.halfSize().lat * 2), + _sizeLevel0.lon / (patch.halfSize().lon * 2)))); + level = glm::min(level, _depth); + Vec2 tileSize = _sizeLevel0.toLonLatVec2() / pow(2, level); + Vec2 nw = patch.northWestCorner().toLonLatVec2(); + Vec2 offset = _offsetLevel0.toLonLatVec2(); + glm::ivec2 tileIndex = (nw - offset) / tileSize; + + // Flip y since indices increase from top to bottom + tileIndex.y *= -1; return glm::ivec3(tileIndex, level); } @@ -86,8 +107,35 @@ namespace openspace { _offsetLevel0.lon + tileIndex.x * tileSize.lon); return LatLonPatch( - LatLon(northWest.lat + tileSize.lat / 2, northWest.lon + tileSize.lon / 2), + LatLon(northWest.lat - tileSize.lat / 2, northWest.lon + tileSize.lon / 2), LatLon(tileSize.lat / 2, tileSize.lon / 2)); } + glm::mat3 TextureTileSet::getUvTransformationPatchToTile( + LatLonPatch patch, + glm::ivec3 tileIndex) + { + LatLonPatch tile = getTilePositionAndScale(tileIndex); + Vec2 posDiff = + patch.southWestCorner().toLonLatVec2() - + tile.southWestCorner().toLonLatVec2(); + + glm::mat3 invTileScale = glm::mat3( + {1 / (tile.halfSize().lon * 2), 0, 0, + 0, 1 / (tile.halfSize().lat * 2), 0, + 0, 0, 1}); + + glm::mat3 globalTranslation = glm::mat3( + { 1, 0, 0, + 0, 1, 0, + posDiff.x, posDiff.y, 1 }); + + glm::mat3 patchScale = glm::mat3( + { (patch.halfSize().lon * 2), 0, 0, + 0, (patch.halfSize().lat * 2), 0, + 0, 0, 1 }); + + return invTileScale * globalTranslation * patchScale; + } + } // namespace openspace diff --git a/modules/globebrowsing/rendering/texturetileset.h b/modules/globebrowsing/rendering/texturetileset.h index 4774ae09de..4605ae2645 100644 --- a/modules/globebrowsing/rendering/texturetileset.h +++ b/modules/globebrowsing/rendering/texturetileset.h @@ -38,7 +38,7 @@ namespace openspace { class TextureTileSet { public: - TextureTileSet(); + TextureTileSet(LatLon sizeLevel0, LatLon offsetLevel0, int depth); ~TextureTileSet(); /// Returns the index of the tile at an appropriate level. @@ -49,9 +49,11 @@ namespace openspace { std::shared_ptr getTile(LatLonPatch patch); std::shared_ptr getTile(glm::ivec3 tileIndex); LatLonPatch getTilePositionAndScale(glm::ivec3 tileIndex); + glm::mat3 getUvTransformationPatchToTile(LatLonPatch patch, glm::ivec3 tileIndex); private: LatLon _sizeLevel0; LatLon _offsetLevel0; + int _depth; std::shared_ptr _testTexture; }; diff --git a/modules/globebrowsing/shaders/simple_fs.glsl b/modules/globebrowsing/shaders/simple_fs.glsl index 503510e77d..ea3fc883f3 100644 --- a/modules/globebrowsing/shaders/simple_fs.glsl +++ b/modules/globebrowsing/shaders/simple_fs.glsl @@ -36,6 +36,7 @@ uniform sampler2D texture1; uniform sampler2D nightTex; uniform sampler2D textureSampler; +uniform mat3 uvTransformPatchToTile; in vec4 vs_position; in vec2 vs_uv; @@ -47,7 +48,8 @@ in vec2 vs_uv; Fragment getFragment() { Fragment frag; - frag.color = texture2D(textureSampler, vs_uv);// vec4(fract(vs_uv * 1), 0.4,1); + frag.color = texture2D(textureSampler, vec2(uvTransformPatchToTile * vec3(vs_uv.s, vs_uv.t, 1))); + frag.color = frag.color * 1.0 + vec4(fract(vs_uv * 1), 0.4,1) * 0.2; frag.depth = pscDepth(vs_position); return frag; diff --git a/tests/main.cpp b/tests/main.cpp index 3a2725d288..f0b0579030 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -33,10 +33,11 @@ //#include //#include //#include -#include +//#include //#include //#include -#include +//#include +#include #include #include diff --git a/tests/test_texturetileset.inl b/tests/test_texturetileset.inl new file mode 100644 index 0000000000..71d4807ebe --- /dev/null +++ b/tests/test_texturetileset.inl @@ -0,0 +1,148 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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 "gtest/gtest.h" + +#include + +#define _USE_MATH_DEFINES +#include +#include + +class TextureTileSetTest : public testing::Test {}; + +using namespace openspace; + +TEST_F(TextureTileSetTest, getTileIndexLevel) { + + // Create a tile set with maximum depth 0 + TextureTileSet tileSet(LatLon(M_PI, M_PI * 2), LatLon(M_PI / 2, - M_PI), 0); + + LatLonPatch patch(LatLon(0, 0), LatLon(M_PI / 16, M_PI / 8)); + glm::ivec3 tileIndex0 = tileSet.getTileIndex(patch); + + // Maximum level is 0 + ASSERT_EQ(tileIndex0.z, 0); + + + // Create a tile set with maximum depth 10 + TextureTileSet tileSetDepth10(LatLon(M_PI, M_PI * 2), LatLon(M_PI / 2, - M_PI), 10); + + // A big tile that covers the whole latlon space + LatLonPatch patchBig(LatLon(0, 0), LatLon(M_PI / 2, M_PI)); + tileIndex0 = tileSetDepth10.getTileIndex(patchBig); + + // Should return 0 since the tile covers the whole latlon space + ASSERT_EQ(tileIndex0.z, 0); + + + // An edge case tile that covers a fourth of the latlon space + LatLonPatch patchEdgeCase(LatLon(0, 0), LatLon(M_PI / 4, M_PI / 2)); + glm::ivec3 tileIndex1 = tileSetDepth10.getTileIndex(patchEdgeCase); + + // Now it can go up a level + ASSERT_EQ(tileIndex1.z, 1); + + + // Bigger than the edge case + LatLonPatch patchEdgeCaseBigger(LatLon(0, 0), LatLon(M_PI / 4 + 0.001, M_PI / 2 + 0.001)); + tileIndex0 = tileSetDepth10.getTileIndex(patchEdgeCaseBigger); + + // Should return 0 again + ASSERT_EQ(tileIndex0.z, 0); +} + +TEST_F(TextureTileSetTest, getTileIndexXY) { + + // Create a tile set with maximum depth 0 + TextureTileSet tileSet(LatLon(M_PI, M_PI * 2), LatLon(M_PI / 2, - M_PI), 0); + + LatLonPatch patch(LatLon(0, 0), LatLon(M_PI / 16, M_PI / 8)); + glm::ivec3 tileIndex0 = tileSet.getTileIndex(patch); + + // Maximum level is 0 so the x y indices should also be 0 + ASSERT_EQ(tileIndex0.x, 0); + ASSERT_EQ(tileIndex0.y, 0); + + + // Create a tile set with maximum depth 10 + TextureTileSet tileSetDepth10(LatLon(M_PI, M_PI * 2), LatLon(M_PI / 2, - M_PI), 10); + + // A big tile that covers the whole latlon space + LatLonPatch patchBig(LatLon(0, 0), LatLon(M_PI / 2, M_PI)); + tileIndex0 = tileSetDepth10.getTileIndex(patchBig); + + // Should return 0 in x and y since the tile covers the whole latlon space + ASSERT_EQ(tileIndex0.x, 0); + ASSERT_EQ(tileIndex0.y, 0); + + + // A tile that covers a fourth of the latlon space + LatLonPatch patchEdgeCase(LatLon(0, 0), LatLon(M_PI / 4, M_PI / 2)); + glm::ivec3 tileIndex1 = tileSetDepth10.getTileIndex(patchEdgeCase); + + // Now it can go up a level (1) + // Since the position is 0, 0 it has 0, 0, in x, y index + ASSERT_EQ(tileIndex1.x, 0); + ASSERT_EQ(tileIndex1.y, 0); + + + // A smaller edge case tile + LatLonPatch patchEdgeCase2(LatLon(0, 0), LatLon(M_PI / 8, M_PI / 4)); + glm::ivec3 tileIndex11 = tileSetDepth10.getTileIndex(patchEdgeCase2); + + // Now it can go up two levels (2) + // Since the position is 0, 0 it now has 1, 1, in x, y index + // (north west corner is in that tile) + ASSERT_EQ(tileIndex11.x, 1); + ASSERT_EQ(tileIndex11.y, 1); +} + + +TEST_F(TextureTileSetTest, getUvTransformationPatchToTile) { + // Create a tile set with maximum depth 0 + TextureTileSet tileSet(LatLon(M_PI, M_PI * 2), LatLon(M_PI / 2, -M_PI), 0); + + // Create a patch that covers the whole latlon space + LatLonPatch patch(LatLon(0, 0), LatLon(M_PI / 2, M_PI)); + + // Should be a 1:1 mapping + glm::mat3 patchToTileTransform = + tileSet.getUvTransformationPatchToTile(patch, glm::ivec3(0, 0, 0)); + + ASSERT_EQ(patchToTileTransform, glm::mat3(1)); + + // Create a smaller patch in the upper west side + patch = LatLonPatch(LatLon(M_PI / 4, - M_PI / 2), LatLon(M_PI / 4, M_PI / 2)); + patchToTileTransform = + tileSet.getUvTransformationPatchToTile(patch, glm::ivec3(0, 0, 0)); + + glm::vec2 uvPatchSpace = glm::vec2(0, 0); + glm::vec2 uvTileSpace = glm::vec2(patchToTileTransform * glm::vec3(uvPatchSpace, 1)); + + int hej = 0; + + + +}