|
1 | 1 | #include "BatchTableToGltfStructuralMetadata.h" |
2 | 2 |
|
3 | 3 | #include "BatchTableHierarchyPropertyValues.h" |
| 4 | +#include "MetadataProperty.h" |
4 | 5 |
|
| 6 | +#include <Cesium3DTilesContent/GltfConverterUtility.h> |
| 7 | +#include <CesiumGltf/Accessor.h> |
5 | 8 | #include <CesiumGltf/Buffer.h> |
6 | 9 | #include <CesiumGltf/BufferView.h> |
7 | 10 | #include <CesiumGltf/Class.h> |
8 | 11 | #include <CesiumGltf/ClassProperty.h> |
| 12 | +#include <CesiumGltf/ExtensionExtInstanceFeatures.h> |
9 | 13 | #include <CesiumGltf/ExtensionExtMeshFeatures.h> |
| 14 | +#include <CesiumGltf/ExtensionExtMeshGpuInstancing.h> |
10 | 15 | #include <CesiumGltf/ExtensionKhrDracoMeshCompression.h> |
11 | 16 | #include <CesiumGltf/ExtensionModelExtStructuralMetadata.h> |
12 | 17 | #include <CesiumGltf/FeatureId.h> |
13 | 18 | #include <CesiumGltf/Mesh.h> |
14 | 19 | #include <CesiumGltf/MeshPrimitive.h> |
15 | 20 | #include <CesiumGltf/Model.h> |
| 21 | +#include <CesiumGltf/Node.h> |
16 | 22 | #include <CesiumGltf/PropertyTable.h> |
17 | 23 | #include <CesiumGltf/PropertyTableProperty.h> |
18 | 24 | #include <CesiumGltf/PropertyType.h> |
|
29 | 35 | #include <rapidjson/stringbuffer.h> |
30 | 36 | #include <rapidjson/writer.h> |
31 | 37 |
|
| 38 | +#include <algorithm> |
32 | 39 | #include <cstddef> |
33 | 40 | #include <cstdint> |
34 | 41 | #include <cstring> |
@@ -2008,4 +2015,215 @@ ErrorList BatchTableToGltfStructuralMetadata::convertFromPnts( |
2008 | 2015 |
|
2009 | 2016 | return result; |
2010 | 2017 | } |
| 2018 | + |
| 2019 | +namespace { |
| 2020 | + |
| 2021 | +// Does something like this already exist? |
| 2022 | + |
| 2023 | +template <typename T> int32_t componentTypeFromCpp(); |
| 2024 | + |
| 2025 | +template <> int32_t componentTypeFromCpp<uint8_t>() { |
| 2026 | + return Accessor::ComponentType::UNSIGNED_BYTE; |
| 2027 | +} |
| 2028 | + |
| 2029 | +template <> int32_t componentTypeFromCpp<uint16_t>() { |
| 2030 | + return Accessor::ComponentType::UNSIGNED_SHORT; |
| 2031 | +} |
| 2032 | + |
| 2033 | +template <> int32_t componentTypeFromCpp<uint32_t>() { |
| 2034 | + return Accessor::ComponentType::UNSIGNED_INT; |
| 2035 | +} |
| 2036 | + |
| 2037 | +// encapsulation of the binary batch id data in an I3dm |
| 2038 | +struct BatchIdSemantic { |
| 2039 | + std::variant< |
| 2040 | + std::span<const uint8_t>, |
| 2041 | + std::span<const uint16_t>, |
| 2042 | + std::span<const uint32_t>> |
| 2043 | + batchSpan; |
| 2044 | + const std::byte* rawData; |
| 2045 | + uint32_t numElements; |
| 2046 | + uint32_t byteSize; |
| 2047 | + |
| 2048 | + template <typename UintType> |
| 2049 | + static std::span<const UintType> |
| 2050 | + makeSpan(const std::byte* byteData, uint32_t offset, uint32_t numElements) { |
| 2051 | + return std::span<const UintType>( |
| 2052 | + reinterpret_cast<const UintType*>(byteData + offset), |
| 2053 | + numElements); |
| 2054 | + } |
| 2055 | + |
| 2056 | + BatchIdSemantic( |
| 2057 | + const rapidjson::Document& featureTableJson, |
| 2058 | + uint32_t numInstances, |
| 2059 | + const std::span<const std::byte>& featureTableJsonData) |
| 2060 | + : rawData(nullptr), numElements(0), byteSize(0) { |
| 2061 | + const auto batchIdIt = featureTableJson.FindMember("BATCH_ID"); |
| 2062 | + if (batchIdIt == featureTableJson.MemberEnd() || |
| 2063 | + !batchIdIt->value.IsObject()) { |
| 2064 | + return; |
| 2065 | + } |
| 2066 | + const auto byteOffsetIt = batchIdIt->value.FindMember("byteOffset"); |
| 2067 | + if (byteOffsetIt == batchIdIt->value.MemberEnd() || |
| 2068 | + !byteOffsetIt->value.IsUint()) { |
| 2069 | + // Warning |
| 2070 | + } |
| 2071 | + uint32_t byteOffset = byteOffsetIt->value.GetUint(); |
| 2072 | + const auto componentTypeIt = batchIdIt->value.FindMember("componentType"); |
| 2073 | + if (componentTypeIt != featureTableJson.MemberEnd() && |
| 2074 | + componentTypeIt->value.IsString()) { |
| 2075 | + const std::string& componentTypeString = |
| 2076 | + componentTypeIt->value.GetString(); |
| 2077 | + if (MetadataProperty::stringToMetadataComponentType.find( |
| 2078 | + componentTypeString) == |
| 2079 | + MetadataProperty::stringToMetadataComponentType.end()) { |
| 2080 | + // Warning |
| 2081 | + } |
| 2082 | + MetadataProperty::ComponentType componentType = |
| 2083 | + MetadataProperty::stringToMetadataComponentType.at( |
| 2084 | + componentTypeString); |
| 2085 | + rawData = featureTableJsonData.data(); |
| 2086 | + if (componentType == MetadataProperty::ComponentType::UNSIGNED_BYTE) { |
| 2087 | + batchSpan = makeSpan<uint8_t>(rawData, byteOffset, numInstances); |
| 2088 | + numElements = numInstances; |
| 2089 | + byteSize = numElements * sizeof(uint8_t); |
| 2090 | + } else if ( |
| 2091 | + componentType == MetadataProperty::ComponentType::UNSIGNED_SHORT) { |
| 2092 | + batchSpan = makeSpan<uint8_t>(rawData, byteOffset, numInstances); |
| 2093 | + numElements = numInstances; |
| 2094 | + byteSize = numElements * sizeof(uint16_t); |
| 2095 | + } else if ( |
| 2096 | + componentType == MetadataProperty::ComponentType::UNSIGNED_INT) { |
| 2097 | + batchSpan = makeSpan<uint32_t>(rawData, byteOffset, numInstances); |
| 2098 | + numElements = numInstances; |
| 2099 | + byteSize = numElements * sizeof(uint32_t); |
| 2100 | + } |
| 2101 | + } |
| 2102 | + } |
| 2103 | + |
| 2104 | + size_t idSize() const { |
| 2105 | + return std::visit( |
| 2106 | + [](auto&& batchIds) { return sizeof(batchIds[0]); }, |
| 2107 | + batchSpan); |
| 2108 | + } |
| 2109 | + |
| 2110 | + uint32_t maxBatchId() const { |
| 2111 | + return std::visit( |
| 2112 | + [](auto&& batchIds) { |
| 2113 | + auto itr = std::max_element(batchIds.begin(), batchIds.end()); |
| 2114 | + return static_cast<uint32_t>(*itr); |
| 2115 | + }, |
| 2116 | + batchSpan); |
| 2117 | + } |
| 2118 | + |
| 2119 | + int32_t componentType() const { |
| 2120 | + return std::visit( |
| 2121 | + [](auto&& batchIds) { |
| 2122 | + using span_type = std::remove_reference_t<decltype(batchIds)>; |
| 2123 | + return componentTypeFromCpp<typename span_type::value_type>(); |
| 2124 | + }, |
| 2125 | + batchSpan); |
| 2126 | + } |
| 2127 | +}; |
| 2128 | + |
| 2129 | +// returns an accessor ID for the added feature IDs |
| 2130 | +int32_t |
| 2131 | +addFeatureIdsToGltf(CesiumGltf::Model& gltf, const BatchIdSemantic& batchIds) { |
| 2132 | + int32_t featuresBufferId = GltfConverterUtility::createBufferInGltf(gltf); |
| 2133 | + auto& featuresBuffer = gltf.buffers[static_cast<uint32_t>(featuresBufferId)]; |
| 2134 | + featuresBuffer.cesium.data.resize(batchIds.byteSize); |
| 2135 | + std::memcpy( |
| 2136 | + &featuresBuffer.cesium.data[0], |
| 2137 | + batchIds.rawData, |
| 2138 | + batchIds.byteSize); |
| 2139 | + int32_t featuresBufferViewId = GltfConverterUtility::createBufferViewInGltf( |
| 2140 | + gltf, |
| 2141 | + featuresBufferId, |
| 2142 | + 0, |
| 2143 | + static_cast<int64_t>(batchIds.idSize())); |
| 2144 | + gltf.bufferViews[static_cast<uint32_t>(featuresBufferViewId)].byteLength = |
| 2145 | + batchIds.byteSize; |
| 2146 | + |
| 2147 | + int32_t accessorId = GltfConverterUtility::createAccessorInGltf( |
| 2148 | + gltf, |
| 2149 | + featuresBufferViewId, |
| 2150 | + batchIds.componentType(), |
| 2151 | + batchIds.numElements, |
| 2152 | + Accessor::Type::SCALAR); |
| 2153 | + return accessorId; |
| 2154 | +} |
| 2155 | + |
| 2156 | +} // namespace |
| 2157 | + |
| 2158 | +ErrorList BatchTableToGltfStructuralMetadata::convertFromI3dm( |
| 2159 | + const rapidjson::Document& featureTableJson, |
| 2160 | + const rapidjson::Document& batchTableJson, |
| 2161 | + const std::span<const std::byte>& featureTableJsonData, |
| 2162 | + const std::span<const std::byte>& batchTableBinaryData, |
| 2163 | + CesiumGltf::Model& gltf) { |
| 2164 | + // Check to make sure a char of rapidjson is 1 byte |
| 2165 | + static_assert( |
| 2166 | + sizeof(rapidjson::Value::Ch) == 1, |
| 2167 | + "RapidJson::Value::Ch is not 1 byte"); |
| 2168 | + |
| 2169 | + ErrorList result; |
| 2170 | + |
| 2171 | + // Parse the batch table and convert it to the EXT_structural_metadata |
| 2172 | + // extension. |
| 2173 | + |
| 2174 | + // Batch table length is either the max batch ID + 1 or, if there are no batch |
| 2175 | + // IDs, the number of instances. |
| 2176 | + int64_t featureCount = 0; |
| 2177 | + std::optional<BatchIdSemantic> optBatchIds; |
| 2178 | + const auto batchIdIt = featureTableJson.FindMember("BATCH_ID"); |
| 2179 | + std::optional<uint32_t> optInstancesLength = |
| 2180 | + GltfConverterUtility::getValue<uint32_t>( |
| 2181 | + featureTableJson, |
| 2182 | + "INSTANCES_LENGTH"); |
| 2183 | + if (!optInstancesLength) { |
| 2184 | + result.emplaceError("Required INSTANCES_LENGTH semantic is missing"); |
| 2185 | + return result; |
| 2186 | + } |
| 2187 | + if (batchIdIt == featureTableJson.MemberEnd()) { |
| 2188 | + featureCount = *optInstancesLength; |
| 2189 | + } else { |
| 2190 | + optBatchIds = BatchIdSemantic( |
| 2191 | + featureTableJson, |
| 2192 | + *optInstancesLength, |
| 2193 | + featureTableJsonData); |
| 2194 | + uint32_t maxBatchId = optBatchIds->maxBatchId(); |
| 2195 | + featureCount = maxBatchId + 1; |
| 2196 | + } |
| 2197 | + |
| 2198 | + convertBatchTableToGltfStructuralMetadataExtension( |
| 2199 | + batchTableJson, |
| 2200 | + batchTableBinaryData, |
| 2201 | + gltf, |
| 2202 | + featureCount, |
| 2203 | + result); |
| 2204 | + |
| 2205 | + int32_t featureIdAccessor = -1; |
| 2206 | + if (optBatchIds.has_value()) { |
| 2207 | + featureIdAccessor = addFeatureIdsToGltf(gltf, *optBatchIds); |
| 2208 | + } |
| 2209 | + |
| 2210 | + // Create an EXT_instance_features extension for node that has an |
| 2211 | + // EXT_mesh_gpu_instancing extension |
| 2212 | + for (Node& node : gltf.nodes) { |
| 2213 | + if (auto* pGpuInstancing = |
| 2214 | + node.getExtension<ExtensionExtMeshGpuInstancing>()) { |
| 2215 | + auto& instanceFeatureExt = |
| 2216 | + node.addExtension<ExtensionExtInstanceFeatures>(); |
| 2217 | + gltf.addExtensionUsed(ExtensionExtInstanceFeatures::ExtensionName); |
| 2218 | + instanceFeatureExt.featureIds.resize(1); |
| 2219 | + instanceFeatureExt.featureIds[0].featureCount = featureCount; |
| 2220 | + instanceFeatureExt.featureIds[0].propertyTable = 0; |
| 2221 | + if (featureIdAccessor >= 0) { |
| 2222 | + pGpuInstancing->attributes["_FEATURE_ID_0"] = featureIdAccessor; |
| 2223 | + instanceFeatureExt.featureIds[0].attribute = 0; |
| 2224 | + } |
| 2225 | + } |
| 2226 | + } |
| 2227 | + return result; |
| 2228 | +} |
2011 | 2229 | } // namespace Cesium3DTilesContent |
0 commit comments