Generate gaussian splat chunks during PLY import
This commit is contained in:
@@ -450,22 +450,185 @@ bool IsFinite(const Math::Quaternion& value) {
|
||||
return std::isfinite(value.x) && std::isfinite(value.y) && std::isfinite(value.z) && std::isfinite(value.w);
|
||||
}
|
||||
|
||||
Math::Vector3 MinVector3(const Math::Vector3& lhs, const Math::Vector3& rhs) {
|
||||
return Math::Vector3(
|
||||
std::min(lhs.x, rhs.x),
|
||||
std::min(lhs.y, rhs.y),
|
||||
std::min(lhs.z, rhs.z));
|
||||
}
|
||||
|
||||
Math::Vector3 MaxVector3(const Math::Vector3& lhs, const Math::Vector3& rhs) {
|
||||
return Math::Vector3(
|
||||
std::max(lhs.x, rhs.x),
|
||||
std::max(lhs.y, rhs.y),
|
||||
std::max(lhs.z, rhs.z));
|
||||
}
|
||||
|
||||
Math::Vector4 MinVector4(const Math::Vector4& lhs, const Math::Vector4& rhs) {
|
||||
return Math::Vector4(
|
||||
std::min(lhs.x, rhs.x),
|
||||
std::min(lhs.y, rhs.y),
|
||||
std::min(lhs.z, rhs.z),
|
||||
std::min(lhs.w, rhs.w));
|
||||
}
|
||||
|
||||
Math::Vector4 MaxVector4(const Math::Vector4& lhs, const Math::Vector4& rhs) {
|
||||
return Math::Vector4(
|
||||
std::max(lhs.x, rhs.x),
|
||||
std::max(lhs.y, rhs.y),
|
||||
std::max(lhs.z, rhs.z),
|
||||
std::max(lhs.w, rhs.w));
|
||||
}
|
||||
|
||||
Core::uint16 FloatToHalfBits(float value) {
|
||||
std::uint32_t bits = 0u;
|
||||
std::memcpy(&bits, &value, sizeof(bits));
|
||||
|
||||
const std::uint32_t sign = (bits >> 16u) & 0x8000u;
|
||||
std::uint32_t mantissa = bits & 0x007fffffu;
|
||||
std::int32_t exponent = static_cast<std::int32_t>((bits >> 23u) & 0xffu) - 127 + 15;
|
||||
|
||||
if (exponent <= 0) {
|
||||
if (exponent < -10) {
|
||||
return static_cast<Core::uint16>(sign);
|
||||
}
|
||||
|
||||
mantissa = (mantissa | 0x00800000u) >> static_cast<std::uint32_t>(1 - exponent);
|
||||
if ((mantissa & 0x00001000u) != 0u) {
|
||||
mantissa += 0x00002000u;
|
||||
}
|
||||
|
||||
return static_cast<Core::uint16>(sign | (mantissa >> 13u));
|
||||
}
|
||||
|
||||
if (exponent >= 31) {
|
||||
return static_cast<Core::uint16>(sign | 0x7c00u);
|
||||
}
|
||||
|
||||
if ((mantissa & 0x00001000u) != 0u) {
|
||||
mantissa += 0x00002000u;
|
||||
if ((mantissa & 0x00800000u) != 0u) {
|
||||
mantissa = 0u;
|
||||
++exponent;
|
||||
if (exponent >= 31) {
|
||||
return static_cast<Core::uint16>(sign | 0x7c00u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<Core::uint16>(
|
||||
sign |
|
||||
(static_cast<std::uint32_t>(exponent) << 10u) |
|
||||
(mantissa >> 13u));
|
||||
}
|
||||
|
||||
Core::uint32 PackHalfRange(float minValue, float maxValue) {
|
||||
return static_cast<Core::uint32>(FloatToHalfBits(minValue)) |
|
||||
(static_cast<Core::uint32>(FloatToHalfBits(maxValue)) << 16u);
|
||||
}
|
||||
|
||||
Core::uint32 ComputeChunkCount(Core::uint32 splatCount) {
|
||||
return splatCount == 0u
|
||||
? 0u
|
||||
: ((splatCount + kGaussianSplatChunkSize - 1u) / kGaussianSplatChunkSize);
|
||||
}
|
||||
|
||||
void BuildChunkRecords(Core::uint32 vertexCount,
|
||||
const GaussianSplatPositionRecord* positions,
|
||||
const GaussianSplatOtherRecord* other,
|
||||
const GaussianSplatColorRecord* colors,
|
||||
const GaussianSplatSHRecord* shRecords,
|
||||
GaussianSplatChunkRecord* chunks,
|
||||
Core::uint32 chunkCount) {
|
||||
constexpr Core::uint32 kSHCoefficientsPerChannel =
|
||||
kGaussianSplatSHCoefficientCount / kGaussianSplatSHColorChannelCount;
|
||||
|
||||
for (Core::uint32 chunkIndex = 0u; chunkIndex < chunkCount; ++chunkIndex) {
|
||||
const Core::uint32 startIndex = chunkIndex * kGaussianSplatChunkSize;
|
||||
const Core::uint32 endIndex = std::min(startIndex + kGaussianSplatChunkSize, vertexCount);
|
||||
|
||||
Math::Vector3 minPosition(std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max());
|
||||
Math::Vector3 maxPosition(-std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max());
|
||||
Math::Vector3 minScale(std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max());
|
||||
Math::Vector3 maxScale(-std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max());
|
||||
Math::Vector4 minColor(std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max());
|
||||
Math::Vector4 maxColor(-std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max());
|
||||
Math::Vector3 minSh(std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max());
|
||||
Math::Vector3 maxSh(-std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max());
|
||||
|
||||
for (Core::uint32 vertexIndex = startIndex; vertexIndex < endIndex; ++vertexIndex) {
|
||||
minPosition = MinVector3(minPosition, positions[vertexIndex].position);
|
||||
maxPosition = MaxVector3(maxPosition, positions[vertexIndex].position);
|
||||
minScale = MinVector3(minScale, other[vertexIndex].scale);
|
||||
maxScale = MaxVector3(maxScale, other[vertexIndex].scale);
|
||||
minColor = MinVector4(minColor, colors[vertexIndex].colorOpacity);
|
||||
maxColor = MaxVector4(maxColor, colors[vertexIndex].colorOpacity);
|
||||
|
||||
const GaussianSplatSHRecord& shRecord = shRecords[vertexIndex];
|
||||
for (Core::uint32 coefficientIndex = 0u; coefficientIndex < kSHCoefficientsPerChannel; ++coefficientIndex) {
|
||||
minSh.x = std::min(minSh.x, shRecord.coefficients[coefficientIndex]);
|
||||
maxSh.x = std::max(maxSh.x, shRecord.coefficients[coefficientIndex]);
|
||||
minSh.y = std::min(minSh.y, shRecord.coefficients[coefficientIndex + kSHCoefficientsPerChannel]);
|
||||
maxSh.y = std::max(maxSh.y, shRecord.coefficients[coefficientIndex + kSHCoefficientsPerChannel]);
|
||||
minSh.z = std::min(minSh.z, shRecord.coefficients[coefficientIndex + (kSHCoefficientsPerChannel * 2u)]);
|
||||
maxSh.z = std::max(maxSh.z, shRecord.coefficients[coefficientIndex + (kSHCoefficientsPerChannel * 2u)]);
|
||||
}
|
||||
}
|
||||
|
||||
GaussianSplatChunkRecord chunk = {};
|
||||
chunk.posX = Math::Vector2(minPosition.x, maxPosition.x);
|
||||
chunk.posY = Math::Vector2(minPosition.y, maxPosition.y);
|
||||
chunk.posZ = Math::Vector2(minPosition.z, maxPosition.z);
|
||||
chunk.sclX = PackHalfRange(minScale.x, maxScale.x);
|
||||
chunk.sclY = PackHalfRange(minScale.y, maxScale.y);
|
||||
chunk.sclZ = PackHalfRange(minScale.z, maxScale.z);
|
||||
chunk.colR = PackHalfRange(minColor.x, maxColor.x);
|
||||
chunk.colG = PackHalfRange(minColor.y, maxColor.y);
|
||||
chunk.colB = PackHalfRange(minColor.z, maxColor.z);
|
||||
chunk.colA = PackHalfRange(minColor.w, maxColor.w);
|
||||
chunk.shR = PackHalfRange(minSh.x, maxSh.x);
|
||||
chunk.shG = PackHalfRange(minSh.y, maxSh.y);
|
||||
chunk.shB = PackHalfRange(minSh.z, maxSh.z);
|
||||
chunks[chunkIndex] = chunk;
|
||||
}
|
||||
}
|
||||
|
||||
void BuildSections(Core::uint32 vertexCount,
|
||||
Core::uint32 chunkCount,
|
||||
Containers::Array<GaussianSplatSection>& outSections,
|
||||
size_t& outPayloadSize) {
|
||||
outSections.Clear();
|
||||
outSections.Reserve(4u);
|
||||
outSections.Reserve(chunkCount > 0u ? 5u : 4u);
|
||||
|
||||
size_t payloadOffset = 0u;
|
||||
auto appendSection = [&](GaussianSplatSectionType type,
|
||||
GaussianSplatSectionFormat format,
|
||||
size_t elementStride) {
|
||||
size_t elementStride,
|
||||
Core::uint32 elementCount) {
|
||||
GaussianSplatSection section;
|
||||
section.type = type;
|
||||
section.format = format;
|
||||
section.dataOffset = payloadOffset;
|
||||
section.dataSize = elementStride * static_cast<size_t>(vertexCount);
|
||||
section.elementCount = vertexCount;
|
||||
section.dataSize = elementStride * static_cast<size_t>(elementCount);
|
||||
section.elementCount = elementCount;
|
||||
section.elementStride = static_cast<Core::uint32>(elementStride);
|
||||
outSections.PushBack(section);
|
||||
payloadOffset += section.dataSize;
|
||||
@@ -474,19 +637,30 @@ void BuildSections(Core::uint32 vertexCount,
|
||||
appendSection(
|
||||
GaussianSplatSectionType::Positions,
|
||||
GaussianSplatSectionFormat::VectorFloat32,
|
||||
sizeof(GaussianSplatPositionRecord));
|
||||
sizeof(GaussianSplatPositionRecord),
|
||||
vertexCount);
|
||||
appendSection(
|
||||
GaussianSplatSectionType::Other,
|
||||
GaussianSplatSectionFormat::OtherFloat32,
|
||||
sizeof(GaussianSplatOtherRecord));
|
||||
sizeof(GaussianSplatOtherRecord),
|
||||
vertexCount);
|
||||
appendSection(
|
||||
GaussianSplatSectionType::Color,
|
||||
GaussianSplatSectionFormat::ColorRGBA32F,
|
||||
sizeof(GaussianSplatColorRecord));
|
||||
sizeof(GaussianSplatColorRecord),
|
||||
vertexCount);
|
||||
appendSection(
|
||||
GaussianSplatSectionType::SH,
|
||||
GaussianSplatSectionFormat::SHFloat32,
|
||||
sizeof(GaussianSplatSHRecord));
|
||||
sizeof(GaussianSplatSHRecord),
|
||||
vertexCount);
|
||||
if (chunkCount > 0u) {
|
||||
appendSection(
|
||||
GaussianSplatSectionType::Chunks,
|
||||
GaussianSplatSectionFormat::ChunkFloat32,
|
||||
sizeof(GaussianSplatChunkRecord),
|
||||
chunkCount);
|
||||
}
|
||||
|
||||
outPayloadSize = payloadOffset;
|
||||
}
|
||||
@@ -506,9 +680,10 @@ LoadResult ImportGaussianSplatPlyFile(const Containers::String& path) {
|
||||
return LoadResult(Containers::String("Failed to parse GaussianSplat PLY header: ") + path + " - " + headerError);
|
||||
}
|
||||
|
||||
const Core::uint32 chunkCount = ComputeChunkCount(header.vertexCount);
|
||||
Containers::Array<GaussianSplatSection> sections;
|
||||
size_t payloadSize = 0u;
|
||||
BuildSections(header.vertexCount, sections, payloadSize);
|
||||
BuildSections(header.vertexCount, chunkCount, sections, payloadSize);
|
||||
|
||||
Containers::Array<Core::uint8> payload;
|
||||
payload.Resize(payloadSize);
|
||||
@@ -521,6 +696,10 @@ LoadResult ImportGaussianSplatPlyFile(const Containers::String& path) {
|
||||
payload.Data() + static_cast<size_t>(sections[2].dataOffset));
|
||||
auto* shRecords = reinterpret_cast<GaussianSplatSHRecord*>(
|
||||
payload.Data() + static_cast<size_t>(sections[3].dataOffset));
|
||||
auto* chunks = chunkCount > 0u
|
||||
? reinterpret_cast<GaussianSplatChunkRecord*>(
|
||||
payload.Data() + static_cast<size_t>(sections[4].dataOffset))
|
||||
: nullptr;
|
||||
|
||||
Math::Bounds bounds;
|
||||
bool hasBounds = false;
|
||||
@@ -600,17 +779,21 @@ LoadResult ImportGaussianSplatPlyFile(const Containers::String& path) {
|
||||
}
|
||||
}
|
||||
|
||||
if (chunkCount > 0u && chunks != nullptr) {
|
||||
BuildChunkRecords(header.vertexCount, positions, other, colors, shRecords, chunks, chunkCount);
|
||||
}
|
||||
|
||||
GaussianSplatMetadata metadata;
|
||||
metadata.contentVersion = 1u;
|
||||
metadata.splatCount = header.vertexCount;
|
||||
metadata.chunkCount = 0u;
|
||||
metadata.chunkCount = chunkCount;
|
||||
metadata.cameraCount = 0u;
|
||||
metadata.bounds = bounds;
|
||||
metadata.positionFormat = GaussianSplatSectionFormat::VectorFloat32;
|
||||
metadata.otherFormat = GaussianSplatSectionFormat::OtherFloat32;
|
||||
metadata.colorFormat = GaussianSplatSectionFormat::ColorRGBA32F;
|
||||
metadata.shFormat = GaussianSplatSectionFormat::SHFloat32;
|
||||
metadata.chunkFormat = GaussianSplatSectionFormat::Unknown;
|
||||
metadata.chunkFormat = chunkCount > 0u ? GaussianSplatSectionFormat::ChunkFloat32 : GaussianSplatSectionFormat::Unknown;
|
||||
metadata.cameraFormat = GaussianSplatSectionFormat::Unknown;
|
||||
|
||||
auto* gaussianSplat = new GaussianSplat();
|
||||
|
||||
Reference in New Issue
Block a user