2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-scene3d/public-api/loader/environment-map-loader.h>
22 #include <dali/integration-api/pixel-data-integ.h>
28 #include <dali-scene3d/internal/common/image-resource-loader.h>
29 #include <dali-scene3d/public-api/loader/ktx-loader.h>
31 #include <dali/integration-api/debug.h>
37 const std::string_view KTX_EXTENSION = ".ktx";
39 static constexpr uint32_t NUMBER_OF_ENVIRONMENT_MAP_TYPE = 5;
40 static constexpr uint32_t NUMBER_OF_CUBE_MAP_TYPE = 4;
43 * @brief cube map face index
44 * Cube map layer order is as fallows:
45 * POSITIVE_X, NEGATIVE_X, POSITIVE_Y, NEGATIVE_Y, POSITIVE_Z, NEGATIVE_Z. @see CubeMapLayer
46 * The indices are for 4 kind of environment cube map. Cross_horizontal, Array_horizontal, Cross_vertical, and Array_vertical.
48 static constexpr uint32_t CUBEMAP_INDEX_X[NUMBER_OF_CUBE_MAP_TYPE][6] = {{2, 0, 1, 1, 1, 3}, {0, 1, 2, 3, 4, 5}, {1, 1, 1, 1, 0, 2}, {0, 0, 0, 0, 0, 0}};
49 static constexpr uint32_t CUBEMAP_INDEX_Y[NUMBER_OF_CUBE_MAP_TYPE][6] = {{1, 1, 0, 2, 1, 1}, {0, 0, 0, 0, 0, 0}, {1, 3, 0, 2, 1, 1}, {0, 1, 2, 3, 4, 5}};
51 static constexpr Vector2 NUMBER_OF_CUBE_FACE[NUMBER_OF_CUBE_MAP_TYPE] =
58 static constexpr float expectedAspectRatios[NUMBER_OF_ENVIRONMENT_MAP_TYPE] =
68 CROSS_HORIZONTAL = 0, // Cross horizontal style cube map
69 ARRAY_HORIZONTAL, // array horizontal style cube map
70 CROSS_VERTICAL, // Cross vertical style cube map
71 ARRAY_VERTICAL, // array vertical style cube map
75 uint8_t* GetCroppedBuffer(uint8_t* sourceBuffer, uint32_t bytesPerPixel, uint32_t width, uint32_t height, uint32_t xOffset, uint32_t yOffset, uint32_t xFaceSize, uint32_t yFaceSize)
77 uint32_t byteSize = bytesPerPixel * xFaceSize * yFaceSize;
78 uint8_t* destBuffer = reinterpret_cast<uint8_t*>(malloc(byteSize + 4u));
80 if(DALI_LIKELY(destBuffer))
82 int32_t srcStride = width * bytesPerPixel;
83 int32_t destStride = xFaceSize * bytesPerPixel;
84 int32_t srcOffset = xOffset * bytesPerPixel + yOffset * srcStride;
85 int32_t destOffset = 0;
86 for(uint16_t row = yOffset; row < yOffset + yFaceSize; ++row)
88 memcpy(destBuffer + destOffset, sourceBuffer + srcOffset, destStride);
89 srcOffset += srcStride;
90 destOffset += destStride;
95 DALI_LOG_ERROR("malloc is failed. request malloc size : %u\n", byteSize + 4u);
101 PixelData GetCubeFace(Dali::PixelData cubePixelData, uint32_t faceIndex, CubeType cubeType, float faceWidth, float faceHeight)
103 PixelData cubeFacePixelData;
106 auto imagePixelFormat = cubePixelData.GetPixelFormat();
107 if(!Dali::Pixel::IsCompressed(imagePixelFormat))
109 uint8_t* imageBuffer = Dali::Integration::GetPixelDataBuffer(cubePixelData).buffer;
110 uint32_t bytesPerPixel = Pixel::GetBytesPerPixel(imagePixelFormat);
111 uint32_t imageWidth = cubePixelData.GetWidth();
112 uint32_t imageHeight = cubePixelData.GetHeight();
114 uint32_t xOffset = CUBEMAP_INDEX_X[cubeType][faceIndex] * static_cast<uint32_t>(faceWidth);
115 uint32_t yOffset = CUBEMAP_INDEX_Y[cubeType][faceIndex] * static_cast<uint32_t>(faceHeight);
117 uint32_t finalFaceWidth = (xOffset + static_cast<uint32_t>(faceWidth) < imageWidth) ? static_cast<uint32_t>(faceWidth) : imageWidth - xOffset;
118 uint32_t finalFaceHeight = (yOffset + static_cast<uint32_t>(faceHeight) < imageHeight) ? static_cast<uint32_t>(faceHeight) : imageHeight - yOffset;
119 uint8_t* tempImageBuffer = GetCroppedBuffer(imageBuffer, bytesPerPixel, imageWidth, imageHeight, xOffset, yOffset, finalFaceWidth, finalFaceHeight);
121 if(DALI_LIKELY(tempImageBuffer))
123 cubeFacePixelData = Dali::Integration::NewPixelDataWithReleaseAfterUpload(tempImageBuffer, finalFaceWidth * finalFaceHeight * bytesPerPixel, finalFaceWidth, finalFaceHeight, 0u, imagePixelFormat, PixelData::FREE);
127 return cubeFacePixelData;
131 * @brief Loads environment map data texture from an image url.
133 * @param[in] environmentMapUrl The environment map file url.
134 * @param[out] environmentMapData The data structure with all pixel data objects.
135 * @return bool True if the loading is succeded.
137 bool LoadEnvironmentMapData(const std::string& environmentMapUrl, Scene3D::Loader::EnvironmentMapData& environmentMapData)
139 // Diffuse Environment Map
140 if(environmentMapUrl.empty())
145 Dali::PixelData pixelData = Dali::Scene3D::Internal::ImageResourceLoader::GetCachedPixelData(environmentMapUrl);
148 CubeType cubeType = NONE;
149 uint32_t imageWidth = pixelData.GetWidth();
150 uint32_t imageHeight = pixelData.GetHeight();
152 * If the environment map type is not EQUIRECTANGULAR,
153 * The type should be defined internally.
154 * DALi checkes aspect ratio of input image and find the closest type.
155 * If the environment map type is CUBEMAP, DALi determines which of the following styles is closest:
156 * cross horizontal, cross vertical, array horizontal, and array vertical.
157 * When the environment map type is AUTO, it finds the closest type, including the Equirectangular type.
159 if(environmentMapData.GetEnvironmentMapType() != Scene3D::EnvironmentMapType::EQUIRECTANGULAR)
161 float aspectRatio = (float)imageWidth / (float)imageHeight;
163 float minDistance = FLT_MAX;
164 uint32_t typeCount = (environmentMapData.GetEnvironmentMapType() == Scene3D::EnvironmentMapType::CUBEMAP) ? NUMBER_OF_CUBE_MAP_TYPE : NUMBER_OF_ENVIRONMENT_MAP_TYPE;
165 for(uint32_t i = 0; i < typeCount; ++i)
167 float distance = fabs(aspectRatio - expectedAspectRatios[i]);
168 if(distance < minDistance)
170 minDistance = distance;
171 cubeType = static_cast<CubeType>(i);
178 float faceWidth = imageWidth, faceHeight = imageHeight;
179 faceWidth /= NUMBER_OF_CUBE_FACE[static_cast<uint32_t>(cubeType)].x;
180 faceHeight /= NUMBER_OF_CUBE_FACE[static_cast<uint32_t>(cubeType)].y;
181 environmentMapData.mPixelData.resize(6);
182 for(uint32_t i = 0; i < 6; ++i)
184 environmentMapData.mPixelData[i].resize(1);
186 for(uint32_t i = 0; i < 6; ++i)
188 environmentMapData.mPixelData[i][0] = GetCubeFace(pixelData, i, cubeType, faceWidth, faceHeight);
190 environmentMapData.SetEnvironmentMapType(Scene3D::EnvironmentMapType::CUBEMAP);
194 environmentMapData.mPixelData.resize(1);
195 environmentMapData.mPixelData[0].resize(1);
196 environmentMapData.mPixelData[0][0] = pixelData;
197 environmentMapData.SetEnvironmentMapType(Scene3D::EnvironmentMapType::EQUIRECTANGULAR);
200 if(!environmentMapData.mPixelData.empty() && !environmentMapData.mPixelData[0].empty() && environmentMapData.mPixelData[0][0])
202 const uint32_t pixelDataWidth = environmentMapData.mPixelData[0][0].GetWidth();
203 const uint32_t pixelDataHeight = environmentMapData.mPixelData[0][0].GetHeight();
205 if(pixelDataWidth > 0u && pixelDataHeight > 0u)
207 uint32_t mipmap = static_cast<uint32_t>(1 + std::floor(std::log2(std::min(pixelDataWidth, pixelDataHeight))));
208 environmentMapData.SetMipmapLevels(mipmap);
221 bool LoadEnvironmentMap(const std::string& environmentMapUrl, EnvironmentMapData& environmentMapData)
223 std::filesystem::path modelPath(environmentMapUrl);
224 std::string extension = modelPath.extension();
225 std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
227 bool successed = (extension == KTX_EXTENSION) ? Dali::Scene3D::Loader::LoadKtxData(environmentMapUrl, environmentMapData) : LoadEnvironmentMapData(environmentMapUrl, environmentMapData);
231 } // namespace Loader
232 } // namespace Scene3D