From 5572097fb3334aea832b1da8c665c49d051f88ed Mon Sep 17 00:00:00 2001
From: Benjamin Doherty
Date: Mon, 2 Oct 2023 16:55:52 -0700
Subject: [PATCH 01/63] Fix build failures due to filamat lite removal
---
build.sh | 4 ++--
build/common/test_list.txt | 1 -
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/build.sh b/build.sh
index daaa419f7e..1dce7d2931 100755
--- a/build.sh
+++ b/build.sh
@@ -495,7 +495,7 @@ function build_android {
if [[ "${INSTALL_COMMAND}" ]]; then
echo "Installing out/filamat-android-debug.aar..."
- cp filamat-android/build/outputs/aar/filamat-android-full-debug.aar ../out/filamat-android-debug.aar
+ cp filamat-android/build/outputs/aar/filamat-android-debug.aar ../out/filamat-android-debug.aar
echo "Installing out/filament-android-debug.aar..."
cp filament-android/build/outputs/aar/filament-android-debug.aar ../out/
@@ -544,7 +544,7 @@ function build_android {
if [[ "${INSTALL_COMMAND}" ]]; then
echo "Installing out/filamat-android-release.aar..."
- cp filamat-android/build/outputs/aar/filamat-android-full-release.aar ../out/filamat-android-release.aar
+ cp filamat-android/build/outputs/aar/filamat-android-release.aar ../out/filamat-android-release.aar
echo "Installing out/filament-android-release.aar..."
cp filament-android/build/outputs/aar/filament-android-release.aar ../out/
diff --git a/build/common/test_list.txt b/build/common/test_list.txt
index 39a71c5f4f..3121fccfd0 100644
--- a/build/common/test_list.txt
+++ b/build/common/test_list.txt
@@ -5,7 +5,6 @@ libs/math/test_math
libs/image/test_image compare libs/image/tests/reference/
libs/utils/test_utils
libs/filamat/test_filamat
-libs/filamat/test_filamat_lite
tools/matc/test_matc
tools/cmgen/test_cmgen compare
tools/glslminifier/test_glslminifier
From 2cf86454bb3b4494a653fa1afc865df45742ff15 Mon Sep 17 00:00:00 2001
From: Powei Feng
Date: Tue, 3 Oct 2023 10:49:45 -0700
Subject: [PATCH 02/63] vk: refactor VulkanProgram (#7221)
---
filament/backend/src/vulkan/VulkanBlitter.cpp | 9 +-
filament/backend/src/vulkan/VulkanDriver.cpp | 109 +++++++--------
filament/backend/src/vulkan/VulkanHandles.cpp | 130 ++++++++++--------
filament/backend/src/vulkan/VulkanHandles.h | 58 +++++++-
.../src/vulkan/VulkanPipelineCache.cpp | 17 ++-
.../backend/src/vulkan/VulkanPipelineCache.h | 5 +-
6 files changed, 199 insertions(+), 129 deletions(-)
diff --git a/filament/backend/src/vulkan/VulkanBlitter.cpp b/filament/backend/src/vulkan/VulkanBlitter.cpp
index 9f20b11ac8..bfda73fa32 100644
--- a/filament/backend/src/vulkan/VulkanBlitter.cpp
+++ b/filament/backend/src/vulkan/VulkanBlitter.cpp
@@ -237,11 +237,12 @@ void VulkanBlitter::lazyInit() noexcept {
VkShaderModule vertexShader = decode(VKSHADERS_BLITDEPTHVS_DATA, VKSHADERS_BLITDEPTHVS_SIZE);
VkShaderModule fragmentShader = decode(VKSHADERS_BLITDEPTHFS_DATA, VKSHADERS_BLITDEPTHFS_SIZE);
- mDepthResolveProgram = new VulkanProgram(mDevice, vertexShader, fragmentShader);
// Allocate one anonymous sampler at slot 0.
- mDepthResolveProgram->samplerGroupInfo[0].samplers.reserve(1);
- mDepthResolveProgram->samplerGroupInfo[0].samplers.resize(1);
+ VulkanProgram::CustomSamplerInfoList samplers = {
+ {0, 0, ShaderStageFlags::FRAGMENT},
+ };
+ mDepthResolveProgram = new VulkanProgram(mDevice, vertexShader, fragmentShader, samplers);
#if FVK_ENABLED(FVK_DEBUG_BLITTER)
utils::slog.d << "Created Shader Module for VulkanBlitter "
@@ -359,7 +360,7 @@ void VulkanBlitter::blitSlowDepth(VkFilter filter, const VkExtent2D srcExtent, V
// DRAW THE TRIANGLE
// -----------------
- mPipelineCache.bindProgram(*mDepthResolveProgram);
+ mPipelineCache.bindProgram(mDepthResolveProgram);
mPipelineCache.bindPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
auto vkraster = mPipelineCache.getCurrentRasterState();
diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp
index 217aab5690..b8381330e4 100644
--- a/filament/backend/src/vulkan/VulkanDriver.cpp
+++ b/filament/backend/src/vulkan/VulkanDriver.cpp
@@ -1548,7 +1548,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r
VkDeviceSize const* offsets = prim.vertexBuffer->getOffsets();
// Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls.
- mPipelineCache.bindProgram(*program);
+ mPipelineCache.bindProgram(program);
mPipelineCache.bindRasterState(mPipelineCache.getCurrentRasterState());
mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount);
@@ -1560,68 +1560,69 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r
VkDescriptorImageInfo samplerInfo[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {};
VulkanTexture* samplerTextures[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {nullptr};
- VulkanPipelineCache::UsageFlags usage;
+ auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex();
+ VulkanPipelineCache::UsageFlags usage = program->getUsage();
UTILS_NOUNROLL
- for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::SAMPLER_BINDING_COUNT; samplerGroupIdx++) {
- const auto& samplerGroup = program->samplerGroupInfo[samplerGroupIdx];
- const auto& samplers = samplerGroup.samplers;
- if (samplers.empty()) {
+ for (uint8_t binding = 0; binding < VulkanPipelineCache::SAMPLER_BINDING_COUNT; binding++) {
+ uint16_t const indexPair = bindingToSamplerIndex[binding];
+
+ if (indexPair == 0xffff) {
+ usage = VulkanPipelineCache::disableUsageFlags(binding, usage);
continue;
}
- VulkanSamplerGroup* vksb = mSamplerBindings[samplerGroupIdx];
+
+ uint16_t const samplerGroupInd = (indexPair >> 8) & 0xff;
+ uint16_t const samplerInd = (indexPair & 0xff);
+
+ VulkanSamplerGroup* vksb = mSamplerBindings[samplerGroupInd];
if (!vksb) {
+ usage = VulkanPipelineCache::disableUsageFlags(binding, usage);
continue;
}
- SamplerGroup* sb = vksb->sb.get();
- assert_invariant(sb->getSize() == samplers.size());
- size_t samplerIdx = 0;
- for (auto& sampler : samplers) {
- const SamplerDescriptor* boundSampler = sb->data() + samplerIdx;
- samplerIdx++;
+ SamplerDescriptor const* boundSampler = ((SamplerDescriptor*) vksb->sb->data()) + samplerInd;
- if (UTILS_LIKELY(boundSampler->t)) {
- VulkanTexture* texture = mResourceAllocator.handle_cast(boundSampler->t);
- VkImageViewType const expectedType = texture->getViewType();
-
- // TODO: can this uninitialized check be checked in a higher layer?
- // This fallback path is very flaky because the dummy texture might not have
- // matching characteristics. (e.g. if the missing texture is a 3D texture)
- if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) {
-#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
- utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'";
- utils::slog.w << " in material '" << program->name.c_str() << "'";
- utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl;
-#endif
- texture = mEmptyTexture.get();
- }
-
- const SamplerParams& samplerParams = boundSampler->s;
- VkSampler vksampler = mSamplerCache.getSampler(samplerParams);
-
- usage = VulkanPipelineCache::getUsageFlags(sampler.binding, samplerGroup.stageFlags, usage);
-
- VkImageView imageView = VK_NULL_HANDLE;
- VkImageSubresourceRange const range = texture->getPrimaryViewRange();
- if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT)
- && expectedType == VK_IMAGE_VIEW_TYPE_2D) {
- // If the sampler is part of a mipmapped depth texture, where one of the level
- // *can* be an attachment, then the sampler for this texture has the same view
- // properties as a view for an attachment. Therefore, we can use
- // getAttachmentView to get a corresponding VkImageView.
- imageView = texture->getAttachmentView(range);
- } else {
- imageView = texture->getViewForType(range, expectedType);
- }
-
- samplerInfo[sampler.binding] = {
- .sampler = vksampler,
- .imageView = imageView,
- .imageLayout = ImgUtil::getVkLayout(texture->getPrimaryImageLayout())
- };
- samplerTextures[sampler.binding] = texture;
- }
+ if (UTILS_UNLIKELY(!boundSampler->t)) {
+ usage = VulkanPipelineCache::disableUsageFlags(binding, usage);
+ continue;
}
+
+ VulkanTexture* texture = mResourceAllocator.handle_cast(boundSampler->t);
+ VkImageViewType const expectedType = texture->getViewType();
+
+ // TODO: can this uninitialized check be checked in a higher layer?
+ // This fallback path is very flaky because the dummy texture might not have
+ // matching characteristics. (e.g. if the missing texture is a 3D texture)
+ if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) {
+#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
+ utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'";
+ utils::slog.w << " in material '" << program->name.c_str() << "'";
+ utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl;
+#endif
+ texture = mEmptyTexture.get();
+ }
+
+ SamplerParams const& samplerParams = boundSampler->s;
+ VkSampler const vksampler = mSamplerCache.getSampler(samplerParams);
+ VkImageView imageView = VK_NULL_HANDLE;
+ VkImageSubresourceRange const range = texture->getPrimaryViewRange();
+ if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
+ expectedType == VK_IMAGE_VIEW_TYPE_2D) {
+ // If the sampler is part of a mipmapped depth texture, where one of the level *can* be
+ // an attachment, then the sampler for this texture has the same view properties as a
+ // view for an attachment. Therefore, we can use getAttachmentView to get a
+ // corresponding VkImageView.
+ imageView = texture->getAttachmentView(range);
+ } else {
+ imageView = texture->getViewForType(range, expectedType);
+ }
+
+ samplerInfo[binding] = {
+ .sampler = vksampler,
+ .imageView = imageView,
+ .imageLayout = ImgUtil::getVkLayout(texture->getPrimaryImageLayout())
+ };
+ samplerTextures[binding] = texture;
}
mPipelineCache.bindSamplers(samplerInfo, samplerTextures, usage);
diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp
index 963ca70f0f..ac6b4ccea1 100644
--- a/filament/backend/src/vulkan/VulkanHandles.cpp
+++ b/filament/backend/src/vulkan/VulkanHandles.cpp
@@ -49,88 +49,100 @@ static void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeig
VulkanProgram::VulkanProgram(VkDevice device, const Program& builder) noexcept
: HwProgram(builder.getName()),
VulkanResource(VulkanResourceType::PROGRAM),
+ mInfo(new PipelineInfo(builder.getSpecializationConstants().size())),
mDevice(device) {
- auto const& blobs = builder.getShadersSource();
- VkShaderModule* modules[2] = {&bundle.vertex, &bundle.fragment};
- // TODO: handle compute shaders.
- for (size_t i = 0; i < 2; i++) {
+ auto& blobs = builder.getShadersSource();
+ auto& modules = mInfo->shaders;
+ for (size_t i = 0; i < MAX_SHADER_MODULES; i++) {
const auto& blob = blobs[i];
- VkShaderModule* module = modules[i];
- VkShaderModuleCreateInfo moduleInfo = {};
- moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
- moduleInfo.codeSize = blob.size();
- moduleInfo.pCode = (uint32_t*) blob.data();
- VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, module);
+ uint32_t* data = (uint32_t*)blob.data();
+ VkShaderModule& module = modules[i];
+ VkShaderModuleCreateInfo moduleInfo = {
+ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
+ .codeSize = blob.size(),
+ .pCode = data,
+ };
+ VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
}
+ // Note that bools are 4-bytes in Vulkan
+ // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBool32.html
+ constexpr uint32_t const CONSTANT_SIZE = 4;
+
// populate the specialization constants requirements right now
auto const& specializationConstants = builder.getSpecializationConstants();
- if (!specializationConstants.empty()) {
- // Allocate a single heap block to store all the specialization constants structures
- // our supported types are int32, float and bool, so we use 4 bytes per data. bool will
- // just use the first byte.
- char* pStorage = (char*)malloc(
- sizeof(VkSpecializationInfo) +
- specializationConstants.size() * sizeof(VkSpecializationMapEntry) +
- specializationConstants.size() * 4);
-
- VkSpecializationInfo* const pInfo = (VkSpecializationInfo*)pStorage;
- VkSpecializationMapEntry* const pEntries =
- (VkSpecializationMapEntry*)(pStorage + sizeof(VkSpecializationInfo));
- void* pData = pStorage + sizeof(VkSpecializationInfo) +
- specializationConstants.size() * sizeof(VkSpecializationMapEntry);
-
- *pInfo = {
- .mapEntryCount = specializationConstants.size(),
- .pMapEntries = pEntries,
- .dataSize = specializationConstants.size() * 4,
- .pData = pData,
+ uint32_t const specConstCount = static_cast(specializationConstants.size());
+ char* specData = mInfo->specConstData.get();
+ if (specConstCount > 0) {
+ mInfo->specializationInfo = {
+ .mapEntryCount = specConstCount,
+ .pMapEntries = mInfo->specConsts.data(),
+ .dataSize = specConstCount * CONSTANT_SIZE,
+ .pData = specData,
};
+ }
+ for (uint32_t i = 0; i < specConstCount; ++i) {
+ uint32_t const offset = i * CONSTANT_SIZE;
+ mInfo->specConsts[i] = {
+ .constantID = specializationConstants[i].id,
+ .offset = offset,
+ .size = CONSTANT_SIZE,
+ };
+ using SpecConstant = Program::SpecializationConstant::Type;
+ char const* addr = (char*)specData + offset;
+ SpecConstant const& arg = specializationConstants[i].value;
+ if (std::holds_alternative(arg)) {
+ *((VkBool32*)addr) = std::get(arg) ? VK_TRUE : VK_FALSE;
+ } else if (std::holds_alternative(arg)) {
+ *((float*)addr) = std::get(arg);
+ } else {
+ *((int32_t*)addr) = std::get(arg);
+ }
+ }
- for (size_t i = 0; i < specializationConstants.size(); i++) {
- uint32_t const offset = uint32_t(i) * 4;
- pEntries[i] = {
- .constantID = specializationConstants[i].id,
- .offset = offset,
- // Note that bools are 4-bytes in Vulkan
- // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBool32.html
- .size = 4,
- };
-
- using SpecConstant = Program::SpecializationConstant::Type;
- char const* addr = (char*)pData + offset;
- SpecConstant const& arg = specializationConstants[i].value;
- if (std::holds_alternative(arg)) {
- *((VkBool32*)addr) = std::get(arg) ? VK_TRUE : VK_FALSE;
- } else if (std::holds_alternative(arg)) {
- *((float*)addr) = std::get(arg);
- } else {
- *((int32_t*)addr) = std::get(arg);
- }
+ auto& groupInfo = builder.getSamplerGroupInfo();
+ auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
+ auto& usage = mInfo->usage;
+ for (uint8_t groupInd = 0; groupInd < Program::SAMPLER_BINDING_COUNT; groupInd++) {
+ auto const& group = groupInfo[groupInd];
+ auto const& samplers = group.samplers;
+ for (size_t i = 0; i < samplers.size(); ++i) {
+ uint32_t const binding = samplers[i].binding;
+ bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i);
+ usage = VulkanPipelineCache::getUsageFlags(binding, group.stageFlags, usage);
}
- bundle.specializationInfos = pInfo;
}
- // Make a copy of the binding map
- samplerGroupInfo = builder.getSamplerGroupInfo();
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex
<< ", " << bundle.fragment << ")" << utils::io::endl;
#endif
}
-VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs) noexcept
+VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs,
+ CustomSamplerInfoList const& samplerInfo) noexcept
: VulkanResource(VulkanResourceType::PROGRAM),
+ mInfo(new PipelineInfo(0)),
mDevice(device) {
- bundle.vertex = vs;
- bundle.fragment = fs;
+ mInfo->shaders[0] = vs;
+ mInfo->shaders[1] = fs;
+ auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
+ auto& usage = mInfo->usage;
+ bindingToSamplerIndex.resize(samplerInfo.size());
+ for (uint16_t binding = 0; binding < samplerInfo.size(); ++binding) {
+ auto const& sampler = samplerInfo[binding];
+ bindingToSamplerIndex[binding]
+ = (sampler.groupIndex << 8) | (0xff & sampler.samplerIndex);
+ usage = VulkanPipelineCache::getUsageFlags(binding, sampler.flags, usage);
+ }
}
VulkanProgram::~VulkanProgram() {
- vkDestroyShaderModule(mDevice, bundle.vertex, VKALLOC);
- vkDestroyShaderModule(mDevice, bundle.fragment, VKALLOC);
- free(bundle.specializationInfos);
+ for (auto shader: mInfo->shaders) {
+ vkDestroyShaderModule(mDevice, shader, VKALLOC);
+ }
+ delete mInfo;
}
// Creates a special "default" render target (i.e. associated with the swap chain)
diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h
index 223a13dbd6..f60d73ca55 100644
--- a/filament/backend/src/vulkan/VulkanHandles.h
+++ b/filament/backend/src/vulkan/VulkanHandles.h
@@ -37,14 +37,64 @@ namespace filament::backend {
class VulkanTimestamps;
struct VulkanProgram : public HwProgram, VulkanResource {
+
VulkanProgram(VkDevice device, const Program& builder) noexcept;
- VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs) noexcept;
+
+ struct CustomSamplerInfo {
+ uint8_t groupIndex;
+ uint8_t samplerIndex;
+ ShaderStageFlags flags;
+ };
+ using CustomSamplerInfoList = utils::FixedCapacityVector;
+
+ // We allow custom descriptor of the samplers within shaders. This is needed if we want to use
+ // a program that exists only in the backend - for example, for shader-based bliting.
+ VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs,
+ CustomSamplerInfoList const& samplerInfo) noexcept;
~VulkanProgram();
- VulkanPipelineCache::ProgramBundle bundle;
- Program::SamplerGroupInfo samplerGroupInfo;
+
+ inline VkShaderModule getVertexShader() const {
+ return mInfo->shaders[0];
+ }
+
+ inline VkShaderModule getFragmentShader() const { return mInfo->shaders[1]; }
+
+ inline VulkanPipelineCache::UsageFlags getUsage() const { return mInfo->usage; }
+
+ inline utils::FixedCapacityVector const& getBindingToSamplerIndex() const {
+ return mInfo->bindingToSamplerIndex;
+ }
+
+ inline VkSpecializationInfo const& getSpecConstInfo() const {
+ return mInfo->specializationInfo;
+ }
private:
- VkDevice mDevice;
+ // TODO: handle compute shaders.
+ // The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
+ static constexpr uint8_t MAX_SHADER_MODULES = 2;
+
+ struct PipelineInfo {
+ PipelineInfo(size_t specConstsCount) :
+ bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff),
+ specConsts(specConstsCount, VkSpecializationMapEntry{}),
+ specConstData(new char[specConstsCount * 4])
+ {}
+
+ // This bitset maps to each of the sampler in the sampler groups associated with this
+ // program, and whether each sampler is used in which shader (i.e. vert, frag, compute).
+ VulkanPipelineCache::UsageFlags usage;
+
+ // We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit.
+ utils::FixedCapacityVector bindingToSamplerIndex;
+ VkShaderModule shaders[MAX_SHADER_MODULES] = {VK_NULL_HANDLE};
+ VkSpecializationInfo specializationInfo = {};
+ utils::FixedCapacityVector specConsts;
+ std::unique_ptr specConstData;
+ };
+
+ PipelineInfo* mInfo;
+ VkDevice mDevice = VK_NULL_HANDLE;
};
// The render target bundles together a set of attachments, each of which can have one of the
diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp
index fb0ef037c4..000f8c755c 100644
--- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp
+++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp
@@ -65,6 +65,13 @@ VulkanPipelineCache::getUsageFlags(uint16_t binding, ShaderStageFlags flags, Usa
return src;
}
+VulkanPipelineCache::UsageFlags VulkanPipelineCache::disableUsageFlags(uint16_t binding,
+ UsageFlags src) {
+ src.unset(binding);
+ src.unset(MAX_SAMPLER_COUNT + binding);
+ return src;
+}
+
VulkanPipelineCache::VulkanPipelineCache(VulkanResourceAllocator* allocator)
: mCurrentRasterState(createDefaultRasterState()),
mResourceAllocator(allocator),
@@ -558,12 +565,10 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP
return &mPipelineLayouts.emplace(mPipelineRequirements.layout, cacheEntry).first.value();
}
-void VulkanPipelineCache::bindProgram(const VulkanProgram& program) noexcept {
- const VkShaderModule shaders[2] = { program.bundle.vertex, program.bundle.fragment };
- for (uint32_t ssi = 0; ssi < SHADER_MODULE_COUNT; ssi++) {
- mPipelineRequirements.shaders[ssi] = shaders[ssi];
- }
- mSpecializationRequirements = program.bundle.specializationInfos;
+void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
+ mPipelineRequirements.shaders[0] = program->getVertexShader();
+ mPipelineRequirements.shaders[1] = program->getFragmentShader();
+ mSpecializationRequirements = &program->getSpecConstInfo();
}
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.h b/filament/backend/src/vulkan/VulkanPipelineCache.h
index b7248420f8..0e092e9555 100644
--- a/filament/backend/src/vulkan/VulkanPipelineCache.h
+++ b/filament/backend/src/vulkan/VulkanPipelineCache.h
@@ -91,6 +91,7 @@ public:
using UsageFlags = utils::bitset128;
static UsageFlags getUsageFlags(uint16_t binding, ShaderStageFlags stages, UsageFlags src = {});
+ static UsageFlags disableUsageFlags(uint16_t binding, UsageFlags src);
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wpadded"
@@ -150,7 +151,7 @@ public:
void bindScissor(VkCommandBuffer cmdbuffer, VkRect2D scissor) noexcept;
// Each of the following methods are fast and do not make Vulkan calls.
- void bindProgram(const VulkanProgram& program) noexcept;
+ void bindProgram(VulkanProgram* program) noexcept;
void bindRasterState(const RasterState& rasterState) noexcept;
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
@@ -415,7 +416,7 @@ private:
RasterState mCurrentRasterState;
PipelineKey mPipelineRequirements = {};
DescriptorKey mDescriptorRequirements = {};
- VkSpecializationInfo* mSpecializationRequirements = {};
+ VkSpecializationInfo const* mSpecializationRequirements = nullptr;
// Current bindings for the pipeline and descriptor sets.
PipelineKey mBoundPipeline = {};
From b4d6f975c1a6ff22b7e3cdb0bf39438fd7789b5a Mon Sep 17 00:00:00 2001
From: Powei Feng
Date: Tue, 3 Oct 2023 12:39:50 -0700
Subject: [PATCH 03/63] Fix build failures due to filamat lite removal #2
(#7224)
---
build.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.sh b/build.sh
index 1dce7d2931..f64dcd7bf2 100755
--- a/build.sh
+++ b/build.sh
@@ -501,7 +501,7 @@ function build_android {
cp filament-android/build/outputs/aar/filament-android-debug.aar ../out/
echo "Installing out/gltfio-android-debug.aar..."
- cp gltfio-android/build/outputs/aar/gltfio-android-full-debug.aar ../out/gltfio-android-debug.aar
+ cp gltfio-android/build/outputs/aar/gltfio-android-debug.aar ../out/gltfio-android-debug.aar
echo "Installing out/filament-utils-android-debug.aar..."
cp filament-utils-android/build/outputs/aar/filament-utils-android-debug.aar ../out/filament-utils-android-debug.aar
From df935b75e5bdc9ba8de82ee8268d0a72592155d9 Mon Sep 17 00:00:00 2001
From: Powei Feng
Date: Tue, 3 Oct 2023 15:22:46 -0700
Subject: [PATCH 04/63] engine: add job system thread count configuration
(#7223)
- Plumb Engine::Config in Java
- Add Engine::Builder for Java
- Add jobSystemThreadCount to Engine::Config
BUG=303129581
---
NEW_RELEASE_NOTES.md | 3 +
.../filament-android/src/main/cpp/Engine.cpp | 57 ++++-
.../com/google/android/filament/Engine.java | 214 ++++++++++++++++--
filament/include/filament/Engine.h | 13 ++
filament/src/details/Engine.cpp | 8 +-
filament/src/details/Engine.h | 2 +-
6 files changed, 271 insertions(+), 26 deletions(-)
diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md
index 4a1a9c7fa7..4a42fb94ef 100644
--- a/NEW_RELEASE_NOTES.md
+++ b/NEW_RELEASE_NOTES.md
@@ -7,3 +7,6 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
+
+engine: Added parameter for configuring JobSystem thread count
+engine: In Java, decpreate Engine.create() and recommend Engine.Builder instead
\ No newline at end of file
diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp
index e530e7bb55..e5bb4e95fa 100644
--- a/android/filament-android/src/main/cpp/Engine.cpp
+++ b/android/filament-android/src/main/cpp/Engine.cpp
@@ -25,15 +25,8 @@
using namespace filament;
using namespace utils;
-extern "C" JNIEXPORT jlong JNICALL
-Java_com_google_android_filament_Engine_nCreateEngine(JNIEnv*, jclass, jlong backend,
- jlong sharedContext) {
- return (jlong) Engine::create((Engine::Backend) backend, nullptr, (void*) sharedContext);
-}
-
extern "C" JNIEXPORT void JNICALL
-Java_com_google_android_filament_Engine_nDestroyEngine(JNIEnv*, jclass,
- jlong nativeEngine) {
+Java_com_google_android_filament_Engine_nDestroyEngine(JNIEnv*, jclass, jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
Engine::destroy(&engine);
}
@@ -454,4 +447,50 @@ Java_com_google_android_filament_Engine_nGetActiveFeatureLevel(JNIEnv *, jclass,
jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (jint)engine->getActiveFeatureLevel();
-}
\ No newline at end of file
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_Engine_nCreateBuilder(JNIEnv*,
+ jclass) {
+ Engine::Builder* builder = new Engine::Builder{};
+ return (jlong) builder;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nDestroyBuilder(JNIEnv*,
+ jclass, jlong nativeBuilder) {
+ Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
+ delete builder;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderBackend(
+ JNIEnv*, jclass, jlong nativeBuilder, jlong backend) {
+ Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
+ builder->backend((Engine::Backend) backend);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderConfig(JNIEnv*,
+ jclass, jlong nativeBuilder, jlong commandBufferSizeMB, jlong perRenderPassArenaSizeMB,
+ jlong driverHandleArenaSizeMB, jlong minCommandBufferSizeMB, jlong perFrameCommandsSizeMB,
+ jlong jobSystemThreadCount) {
+ Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
+ Engine::Config config = {
+ .commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
+ .perRenderPassArenaSizeMB = (uint32_t) perRenderPassArenaSizeMB,
+ .driverHandleArenaSizeMB = (uint32_t) driverHandleArenaSizeMB,
+ .minCommandBufferSizeMB = (uint32_t) minCommandBufferSizeMB,
+ .perFrameCommandsSizeMB = (uint32_t) perFrameCommandsSizeMB,
+ .jobSystemThreadCount = (uint32_t) jobSystemThreadCount,
+ };
+ builder->config(&config);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderSharedContext(
+ JNIEnv*, jclass, jlong nativeBuilder, jlong sharedContext) {
+ Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
+ builder->sharedContext((void*) sharedContext);
+}
+
+extern "C" JNIEXPORT jlong JNICALL
+Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder) {
+ Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
+ return (jlong) builder->build();
+}
diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java
index e6bc4d9243..291200adca 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java
@@ -154,6 +154,184 @@ public class Engine {
FEATURE_LEVEL_2
};
+ /**
+ * Constructs Engine objects using a builder pattern.
+ */
+ public static class Builder {
+ @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
+ private final BuilderFinalizer mFinalizer;
+ private final long mNativeBuilder;
+
+ public Builder() {
+ mNativeBuilder = nCreateBuilder();
+ mFinalizer = new BuilderFinalizer(mNativeBuilder);
+ }
+
+ /**
+ * Sets the {@link Backend} for the Engine.
+ *
+ * @param backend Driver backend to use
+ * @return A reference to this Builder for chaining calls.
+ */
+ Builder backend(Backend backend) {
+ nSetBuilderBackend(mNativeBuilder, backend.ordinal());
+ return this;
+ }
+
+ /**
+ * Sets a sharedContext for the Engine.
+ *
+ * @param sharedContext A platform-dependant OpenGL context used as a shared context
+ * when creating filament's internal context. On Android this parameter
+ * must be an instance of {@link android.opengl.EGLContext}.
+ * @return A reference to this Builder for chaining calls.
+ */
+ Builder sharedContext(Object sharedContext) {
+ if (Platform.get().validateSharedContext(sharedContext)) {
+ nSetBuilderSharedContext(mNativeBuilder,
+ Platform.get().getSharedContextNativeHandle(sharedContext));
+ return this;
+ }
+ throw new IllegalArgumentException("Invalid shared context " + sharedContext);
+ }
+
+ /**
+ * Configure the Engine with custom parameters.
+ *
+ * @param config A {@link Config} object
+ * @return A reference to this Builder for chaining calls.
+ */
+ Builder config(Config config) {
+ nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB,
+ config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB,
+ config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB,
+ config.jobSystemThreadCount);
+ return this;
+ }
+
+ /**
+ * Creates an instance of Engine
+ *
+ * @return A newly created Engine, or null if the GPU driver couldn't
+ * be initialized, for instance if it doesn't support the right version of OpenGL or
+ * OpenGL ES.
+ *
+ * @exception IllegalStateException can be thrown if there isn't enough memory to
+ * allocate the command buffer.
+ */
+ Engine build() {
+ long nativeEngine = nBuilderBuild(mNativeBuilder);
+ if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine");
+ return new Engine(nativeEngine);
+ }
+
+ private static class BuilderFinalizer {
+ private final long mNativeObject;
+
+ BuilderFinalizer(long nativeObject) {
+ mNativeObject = nativeObject;
+ }
+
+ @Override
+ public void finalize() {
+ try {
+ super.finalize();
+ } catch (Throwable t) { // Ignore
+ } finally {
+ nDestroyBuilder(mNativeObject);
+ }
+ }
+ }
+ }
+
+ /**
+ * Parameters for customizing the initialization of {@link Engine}.
+ */
+ public static class Config {
+
+ // #defines in Engine.h
+ private static final long FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB = 3;
+ private static final long FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB = 2;
+ private static final long FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB = 1;
+ private static final long FILAMENT_COMMAND_BUFFER_SIZE_IN_MB =
+ FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB * 3;
+
+ /**
+ * Size in MiB of the low-level command buffer arena.
+ *
+ * Each new command buffer is allocated from here. If this buffer is too small the program
+ * might terminate or rendering errors might occur.
+ *
+ * This is typically set to minCommandBufferSizeMB * 3, so that up to 3 frames can be
+ * batched-up at once.
+ *
+ * This value affects the application's memory usage.
+ */
+ public long commandBufferSizeMB = FILAMENT_COMMAND_BUFFER_SIZE_IN_MB;
+
+ /**
+ * Size in MiB of the per-frame data arena.
+ *
+ * This is the main arena used for allocations when preparing a frame.
+ * e.g.: Froxel data and high-level commands are allocated from this arena.
+ *
+ * If this size is too small, the program will abort on debug builds and have undefined
+ * behavior otherwise.
+ *
+ * This value affects the application's memory usage.
+ */
+ public long perRenderPassArenaSizeMB = FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB;
+
+ /**
+ * Size in MiB of the backend's handle arena.
+ *
+ * Backends will fallback to slower heap-based allocations when running out of space and
+ * log this condition.
+ *
+ * If 0, then the default value for the given platform is used
+ *
+ * This value affects the application's memory usage.
+ */
+ public long driverHandleArenaSizeMB = 0;
+
+ /**
+ * Minimum size in MiB of a low-level command buffer.
+ *
+ * This is how much space is guaranteed to be available for low-level commands when a new
+ * buffer is allocated. If this is too small, the engine might have to stall to wait for
+ * more space to become available, this situation is logged.
+ *
+ * This value does not affect the application's memory usage.
+ */
+ public long minCommandBufferSizeMB = FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB;
+
+ /**
+ * Size in MiB of the per-frame high level command buffer.
+ *
+ * This buffer is related to the number of draw calls achievable within a frame, if it is
+ * too small, the program will abort on debug builds and have undefined behavior otherwise.
+ *
+ * It is allocated from the 'per-render-pass arena' above. Make sure that at least 1 MiB is
+ * left in the per-render-pass arena when deciding the size of this buffer.
+ *
+ * This value does not affect the application's memory usage.
+ */
+ public long perFrameCommandsSizeMB = FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB;
+
+ /**
+ * Number of threads to use in Engine's JobSystem.
+ *
+ * Engine uses a utils::JobSystem to carry out paralleization of Engine workloads. This
+ * value sets the number of threads allocated for JobSystem. Configuring this value can be
+ * helpful in CPU-constrained environments where too many threads can cause contention of
+ * CPU and reduce performance.
+ *
+ * The default value is 0, which implies that the Engine will use a heuristic to determine
+ * the number of threads to use.
+ */
+ public long jobSystemThreadCount = 0;
+ }
+
private Engine(long nativeEngine) {
mNativeObject = nativeEngine;
mTransformManager = new TransformManager(nGetTransformManager(nativeEngine));
@@ -174,12 +352,12 @@ public class Engine {
* @exception IllegalStateException can be thrown if there isn't enough memory to
* allocate the command buffer.
*
+ * @deprecated use {@link Builder}
*/
+ @Deprecated
@NonNull
public static Engine create() {
- long nativeEngine = nCreateEngine(0, 0);
- if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine");
- return new Engine(nativeEngine);
+ return new Builder().build();
}
/**
@@ -196,12 +374,14 @@ public class Engine {
* @exception IllegalStateException can be thrown if there isn't enough memory to
* allocate the command buffer.
*
+ * @deprecated use {@link Builder}
*/
+ @Deprecated
@NonNull
public static Engine create(@NonNull Backend backend) {
- long nativeEngine = nCreateEngine(backend.ordinal(), 0);
- if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine");
- return new Engine(nativeEngine);
+ return new Builder()
+ .backend(backend)
+ .build();
}
/**
@@ -220,16 +400,14 @@ public class Engine {
* @exception IllegalStateException can be thrown if there isn't enough memory to
* allocate the command buffer.
*
+ * @deprecated use {@link Builder}
*/
+ @Deprecated
@NonNull
public static Engine create(@NonNull Object sharedContext) {
- if (Platform.get().validateSharedContext(sharedContext)) {
- long nativeEngine = nCreateEngine(0,
- Platform.get().getSharedContextNativeHandle(sharedContext));
- if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine");
- return new Engine(nativeEngine);
- }
- throw new IllegalArgumentException("Invalid shared context " + sharedContext);
+ return new Builder()
+ .sharedContext(sharedContext)
+ .build();
}
/**
@@ -914,7 +1092,6 @@ public class Engine {
}
}
- private static native long nCreateEngine(long backend, long sharedContext);
private static native void nDestroyEngine(long nativeEngine);
private static native long nGetBackend(long nativeEngine);
private static native long nCreateSwapChain(long nativeEngine, Object nativeWindow, long flags);
@@ -971,4 +1148,13 @@ public class Engine {
private static native int nGetSupportedFeatureLevel(long nativeEngine);
private static native int nSetActiveFeatureLevel(long nativeEngine, int ordinal);
private static native int nGetActiveFeatureLevel(long nativeEngine);
+
+ private static native long nCreateBuilder();
+ private static native void nDestroyBuilder(long nativeBuilder);
+ private static native void nSetBuilderBackend(long nativeBuilder, long backend);
+ private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB,
+ long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB,
+ long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount);
+ private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
+ private static native long nBuilderBuild(long nativeBuilder);
}
diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h
index f4173f2614..a7b4e3c048 100644
--- a/filament/include/filament/Engine.h
+++ b/filament/include/filament/Engine.h
@@ -267,6 +267,19 @@ public:
* This value does not affect the application's memory usage.
*/
uint32_t perFrameCommandsSizeMB = FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB;
+
+ /**
+ * Number of threads to use in Engine's JobSystem.
+ *
+ * Engine uses a utils::JobSystem to carry out paralleization of Engine workloads. This
+ * value sets the number of threads allocated for JobSystem. Configuring this value can be
+ * helpful in CPU-constrained environments where too many threads can cause contention of
+ * CPU and reduce performance.
+ *
+ * The default value is 0, which implies that the Engine will use a heuristic to determine
+ * the number of threads to use.
+ */
+ uint32_t jobSystemThreadCount = 0;
};
diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp
index 3e50fc7135..2b33a564ec 100644
--- a/filament/src/details/Engine.cpp
+++ b/filament/src/details/Engine.cpp
@@ -200,7 +200,7 @@ FEngine::FEngine(Engine::Builder const& builder) :
"FEngine::mPerRenderPassAllocator",
builder->mConfig.perRenderPassArenaSizeMB * MiB),
mHeapAllocator("FEngine::mHeapAllocator", AreaPolicy::NullArea{}),
- mJobSystem(getJobSystemThreadPoolSize()),
+ mJobSystem(getJobSystemThreadPoolSize(builder->mConfig)),
mEngineEpoch(std::chrono::steady_clock::now()),
mDriverBarrier(1),
mMainThreadId(ThreadUtils::getThreadId()),
@@ -214,7 +214,11 @@ FEngine::FEngine(Engine::Builder const& builder) :
<< "(threading is " << (UTILS_HAS_THREADING ? "enabled)" : "disabled)") << io::endl;
}
-uint32_t FEngine::getJobSystemThreadPoolSize() noexcept {
+uint32_t FEngine::getJobSystemThreadPoolSize(Engine::Config const& config) noexcept {
+ if (config.jobSystemThreadCount > 0) {
+ return config.jobSystemThreadCount;
+ }
+
// 1 thread for the user, 1 thread for the backend
int threadCount = (int)std::thread::hardware_concurrency() - 2;
// make sure we have at least 1 thread though
diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h
index c690619378..bca0346001 100644
--- a/filament/src/details/Engine.h
+++ b/filament/src/details/Engine.h
@@ -508,7 +508,7 @@ private:
HeapAllocatorArena mHeapAllocator;
utils::JobSystem mJobSystem;
- static uint32_t getJobSystemThreadPoolSize() noexcept;
+ static uint32_t getJobSystemThreadPoolSize(Engine::Config const& config) noexcept;
std::default_random_engine mRandomEngine;
From 5ab526cbb0a229583c534e0b85c25bddbcbf246d Mon Sep 17 00:00:00 2001
From: Powei Feng
Date: Tue, 3 Oct 2023 15:23:03 -0700
Subject: [PATCH 05/63] Fix build failures due to filamat lite removal try 3
(#7226)
---
build.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.sh b/build.sh
index f64dcd7bf2..7c9bba84a2 100755
--- a/build.sh
+++ b/build.sh
@@ -550,7 +550,7 @@ function build_android {
cp filament-android/build/outputs/aar/filament-android-release.aar ../out/
echo "Installing out/gltfio-android-release.aar..."
- cp gltfio-android/build/outputs/aar/gltfio-android-full-release.aar ../out/gltfio-android-release.aar
+ cp gltfio-android/build/outputs/aar/gltfio-android-release.aar ../out/gltfio-android-release.aar
echo "Installing out/filament-utils-android-release.aar..."
cp filament-utils-android/build/outputs/aar/filament-utils-android-release.aar ../out/filament-utils-android-release.aar
From 07975868fec54cbc7adeb9538a00e7cfbc9ee685 Mon Sep 17 00:00:00 2001
From: Mathias Agopian
Date: Tue, 3 Oct 2023 13:56:57 -0700
Subject: [PATCH 06/63] update cgltf to latest v1.13
FIXES=[239321615]
---
third_party/cgltf/cgltf.h | 235 +++++++---------------
third_party/cgltf/cgltf_write.h | 121 ++---------
third_party/cgltf/test/CMakeLists.txt | 13 --
third_party/cgltf/test/test_all.py | 1 -
third_party/cgltf/test/test_write_glb.cpp | 58 ------
third_party/cgltf/tnt/README | 8 +-
6 files changed, 100 insertions(+), 336 deletions(-)
delete mode 100644 third_party/cgltf/test/test_write_glb.cpp
diff --git a/third_party/cgltf/cgltf.h b/third_party/cgltf/cgltf.h
index a534cefb4f..95b56ceda5 100644
--- a/third_party/cgltf/cgltf.h
+++ b/third_party/cgltf/cgltf.h
@@ -80,9 +80,13 @@
* `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t
* and only works with single-component data types.
*
- * `cgltf_copy_extras_json` allows users to retrieve the "extras" data that can be attached to many
- * glTF objects (which can be arbitrary JSON data). This is a legacy function, consider using
- * cgltf_extras::data directly instead. You can parse this data using your own JSON parser
+ * `cgltf_result cgltf_copy_extras_json(const cgltf_data*, const cgltf_extras*,
+ * char* dest, cgltf_size* dest_size)` allows users to retrieve the "extras" data that
+ * can be attached to many glTF objects (which can be arbitrary JSON data). The
+ * `cgltf_extras` struct stores the offsets of the start and end of the extras JSON data
+ * as it appears in the complete glTF JSON data. This function copies the extras data
+ * into the provided buffer. If `dest` is NULL, the length of the data is written into
+ * `dest_size`. You can then parse this data using your own JSON parser
* or, if you've included the cgltf implementation using the integrated JSMN JSON parser.
*/
#ifndef CGLTF_H_INCLUDED__
@@ -252,10 +256,8 @@ typedef enum cgltf_data_free_method {
} cgltf_data_free_method;
typedef struct cgltf_extras {
- cgltf_size start_offset; /* this field is deprecated and will be removed in the future; use data instead */
- cgltf_size end_offset; /* this field is deprecated and will be removed in the future; use data instead */
-
- char* data;
+ cgltf_size start_offset;
+ cgltf_size end_offset;
} cgltf_extras;
typedef struct cgltf_extension {
@@ -430,6 +432,8 @@ typedef struct cgltf_pbr_metallic_roughness
cgltf_float base_color_factor[4];
cgltf_float metallic_factor;
cgltf_float roughness_factor;
+
+ cgltf_extras extras;
} cgltf_pbr_metallic_roughness;
typedef struct cgltf_pbr_specular_glossiness
@@ -837,7 +841,6 @@ cgltf_size cgltf_num_components(cgltf_type type);
cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count);
-/* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */
cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size);
#ifdef __cplusplus
@@ -921,15 +924,12 @@ static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t
*/
-#ifndef CGLTF_CONSTS
static const cgltf_size GlbHeaderSize = 12;
static const cgltf_size GlbChunkHeaderSize = 8;
static const uint32_t GlbVersion = 2;
static const uint32_t GlbMagic = 0x46546C67;
static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
static const uint32_t GlbMagicBinChunk = 0x004E4942;
-#define CGLTF_CONSTS
-#endif
#ifndef CGLTF_MALLOC
#define CGLTF_MALLOC(size) malloc(size)
@@ -1745,12 +1745,7 @@ cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras*
return cgltf_result_success;
}
-static void cgltf_free_extras(cgltf_data* data, cgltf_extras* extras)
-{
- data->memory.free_func(data->memory.user_data, extras->data);
-}
-
-static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count)
+void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count)
{
for (cgltf_size i = 0; i < extensions_count; ++i)
{
@@ -1760,12 +1755,6 @@ static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions,
data->memory.free_func(data->memory.user_data, extensions);
}
-static void cgltf_free_texture_view(cgltf_data* data, cgltf_texture_view* view)
-{
- cgltf_free_extensions(data, view->extensions, view->extensions_count);
- cgltf_free_extras(data, &view->extras);
-}
-
void cgltf_free(cgltf_data* data)
{
if (!data)
@@ -1781,7 +1770,6 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->asset.min_version);
cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count);
- cgltf_free_extras(data, &data->asset.extras);
for (cgltf_size i = 0; i < data->accessors_count; ++i)
{
@@ -1792,12 +1780,8 @@ void cgltf_free(cgltf_data* data)
cgltf_free_extensions(data, data->accessors[i].sparse.extensions, data->accessors[i].sparse.extensions_count);
cgltf_free_extensions(data, data->accessors[i].sparse.indices_extensions, data->accessors[i].sparse.indices_extensions_count);
cgltf_free_extensions(data, data->accessors[i].sparse.values_extensions, data->accessors[i].sparse.values_extensions_count);
- cgltf_free_extras(data, &data->accessors[i].sparse.extras);
- cgltf_free_extras(data, &data->accessors[i].sparse.indices_extras);
- cgltf_free_extras(data, &data->accessors[i].sparse.values_extras);
}
cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count);
- cgltf_free_extras(data, &data->accessors[i].extras);
}
data->memory.free_func(data->memory.user_data, data->accessors);
@@ -1807,7 +1791,6 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->buffer_views[i].data);
cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count);
- cgltf_free_extras(data, &data->buffer_views[i].extras);
}
data->memory.free_func(data->memory.user_data, data->buffer_views);
@@ -1827,8 +1810,8 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->buffers[i].uri);
cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count);
- cgltf_free_extras(data, &data->buffers[i].extras);
}
+
data->memory.free_func(data->memory.user_data, data->buffers);
for (cgltf_size i = 0; i < data->meshes_count; ++i)
@@ -1866,15 +1849,9 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes);
}
- for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k)
- {
- cgltf_free_extras(data, &data->meshes[i].primitives[j].mappings[k].extras);
- }
-
data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].mappings);
cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count);
- cgltf_free_extras(data, &data->meshes[i].primitives[j].extras);
}
data->memory.free_func(data->memory.user_data, data->meshes[i].primitives);
@@ -1886,7 +1863,6 @@ void cgltf_free(cgltf_data* data)
}
cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count);
- cgltf_free_extras(data, &data->meshes[i].extras);
data->memory.free_func(data->memory.user_data, data->meshes[i].target_names);
}
@@ -1899,50 +1875,49 @@ void cgltf_free(cgltf_data* data)
if(data->materials[i].has_pbr_metallic_roughness)
{
- cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.metallic_roughness_texture);
- cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.base_color_texture);
+ cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions_count);
+ cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions_count);
}
if(data->materials[i].has_pbr_specular_glossiness)
{
- cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.diffuse_texture);
- cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.specular_glossiness_texture);
+ cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions_count);
+ cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions_count);
}
if(data->materials[i].has_clearcoat)
{
- cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_texture);
- cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_roughness_texture);
- cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_normal_texture);
+ cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_texture.extensions, data->materials[i].clearcoat.clearcoat_texture.extensions_count);
+ cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions_count);
+ cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_normal_texture.extensions, data->materials[i].clearcoat.clearcoat_normal_texture.extensions_count);
}
if(data->materials[i].has_specular)
{
- cgltf_free_texture_view(data, &data->materials[i].specular.specular_texture);
- cgltf_free_texture_view(data, &data->materials[i].specular.specular_color_texture);
+ cgltf_free_extensions(data, data->materials[i].specular.specular_texture.extensions, data->materials[i].specular.specular_texture.extensions_count);
+ cgltf_free_extensions(data, data->materials[i].specular.specular_color_texture.extensions, data->materials[i].specular.specular_color_texture.extensions_count);
}
if(data->materials[i].has_transmission)
{
- cgltf_free_texture_view(data, &data->materials[i].transmission.transmission_texture);
+ cgltf_free_extensions(data, data->materials[i].transmission.transmission_texture.extensions, data->materials[i].transmission.transmission_texture.extensions_count);
}
if (data->materials[i].has_volume)
{
- cgltf_free_texture_view(data, &data->materials[i].volume.thickness_texture);
+ cgltf_free_extensions(data, data->materials[i].volume.thickness_texture.extensions, data->materials[i].volume.thickness_texture.extensions_count);
}
if(data->materials[i].has_sheen)
{
- cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_color_texture);
- cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_roughness_texture);
+ cgltf_free_extensions(data, data->materials[i].sheen.sheen_color_texture.extensions, data->materials[i].sheen.sheen_color_texture.extensions_count);
+ cgltf_free_extensions(data, data->materials[i].sheen.sheen_roughness_texture.extensions, data->materials[i].sheen.sheen_roughness_texture.extensions_count);
}
if(data->materials[i].has_iridescence)
{
- cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_texture);
- cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_thickness_texture);
+ cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_texture.extensions, data->materials[i].iridescence.iridescence_texture.extensions_count);
+ cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_thickness_texture.extensions, data->materials[i].iridescence.iridescence_thickness_texture.extensions_count);
}
- cgltf_free_texture_view(data, &data->materials[i].normal_texture);
- cgltf_free_texture_view(data, &data->materials[i].occlusion_texture);
- cgltf_free_texture_view(data, &data->materials[i].emissive_texture);
+ cgltf_free_extensions(data, data->materials[i].normal_texture.extensions, data->materials[i].normal_texture.extensions_count);
+ cgltf_free_extensions(data, data->materials[i].occlusion_texture.extensions, data->materials[i].occlusion_texture.extensions_count);
+ cgltf_free_extensions(data, data->materials[i].emissive_texture.extensions, data->materials[i].emissive_texture.extensions_count);
cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count);
- cgltf_free_extras(data, &data->materials[i].extras);
}
data->memory.free_func(data->memory.user_data, data->materials);
@@ -1954,7 +1929,6 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->images[i].mime_type);
cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count);
- cgltf_free_extras(data, &data->images[i].extras);
}
data->memory.free_func(data->memory.user_data, data->images);
@@ -1962,9 +1936,7 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size i = 0; i < data->textures_count; ++i)
{
data->memory.free_func(data->memory.user_data, data->textures[i].name);
-
cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count);
- cgltf_free_extras(data, &data->textures[i].extras);
}
data->memory.free_func(data->memory.user_data, data->textures);
@@ -1972,9 +1944,7 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size i = 0; i < data->samplers_count; ++i)
{
data->memory.free_func(data->memory.user_data, data->samplers[i].name);
-
cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count);
- cgltf_free_extras(data, &data->samplers[i].extras);
}
data->memory.free_func(data->memory.user_data, data->samplers);
@@ -1985,7 +1955,6 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->skins[i].joints);
cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count);
- cgltf_free_extras(data, &data->skins[i].extras);
}
data->memory.free_func(data->memory.user_data, data->skins);
@@ -1993,18 +1962,7 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size i = 0; i < data->cameras_count; ++i)
{
data->memory.free_func(data->memory.user_data, data->cameras[i].name);
-
- if (data->cameras[i].type == cgltf_camera_type_perspective)
- {
- cgltf_free_extras(data, &data->cameras[i].data.perspective.extras);
- }
- else if (data->cameras[i].type == cgltf_camera_type_orthographic)
- {
- cgltf_free_extras(data, &data->cameras[i].data.orthographic.extras);
- }
-
cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count);
- cgltf_free_extras(data, &data->cameras[i].extras);
}
data->memory.free_func(data->memory.user_data, data->cameras);
@@ -2012,8 +1970,6 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size i = 0; i < data->lights_count; ++i)
{
data->memory.free_func(data->memory.user_data, data->lights[i].name);
-
- cgltf_free_extras(data, &data->lights[i].extras);
}
data->memory.free_func(data->memory.user_data, data->lights);
@@ -2023,19 +1979,7 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->nodes[i].name);
data->memory.free_func(data->memory.user_data, data->nodes[i].children);
data->memory.free_func(data->memory.user_data, data->nodes[i].weights);
-
- if (data->nodes[i].has_mesh_gpu_instancing)
- {
- for (cgltf_size j = 0; j < data->nodes[i].mesh_gpu_instancing.attributes_count; ++j)
- {
- data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes[j].name);
- }
-
- data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes);
- }
-
cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count);
- cgltf_free_extras(data, &data->nodes[i].extras);
}
data->memory.free_func(data->memory.user_data, data->nodes);
@@ -2046,7 +1990,6 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->scenes[i].nodes);
cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count);
- cgltf_free_extras(data, &data->scenes[i].extras);
}
data->memory.free_func(data->memory.user_data, data->scenes);
@@ -2057,19 +2000,16 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j)
{
cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count);
- cgltf_free_extras(data, &data->animations[i].samplers[j].extras);
}
data->memory.free_func(data->memory.user_data, data->animations[i].samplers);
for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j)
{
cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count);
- cgltf_free_extras(data, &data->animations[i].channels[j].extras);
}
data->memory.free_func(data->memory.user_data, data->animations[i].channels);
cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count);
- cgltf_free_extras(data, &data->animations[i].extras);
}
data->memory.free_func(data->memory.user_data, data->animations);
@@ -2077,14 +2017,11 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size i = 0; i < data->variants_count; ++i)
{
data->memory.free_func(data->memory.user_data, data->variants[i].name);
-
- cgltf_free_extras(data, &data->variants[i].extras);
}
data->memory.free_func(data->memory.user_data, data->variants);
cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count);
- cgltf_free_extras(data, &data->extras);
for (cgltf_size i = 0; i < data->extensions_used_count; ++i)
{
@@ -2746,27 +2683,11 @@ static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t con
return i;
}
-static int cgltf_parse_json_extras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras)
+static int cgltf_parse_json_extras(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras)
{
- if (out_extras->data)
- {
- return CGLTF_ERROR_JSON;
- }
-
- /* fill deprecated fields for now, this will be removed in the future */
+ (void)json_chunk;
out_extras->start_offset = tokens[i].start;
out_extras->end_offset = tokens[i].end;
-
- size_t start = tokens[i].start;
- size_t size = tokens[i].end - start;
- out_extras->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1);
- if (!out_extras->data)
- {
- return CGLTF_ERROR_NOMEM;
- }
- strncpy(out_extras->data, (const char*)json_chunk + start, size);
- out_extras->data[size] = '\0';
-
i = cgltf_skip_json(tokens, i);
return i;
}
@@ -2921,7 +2842,7 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto
int material = -1;
int variants_tok = -1;
- int extras_tok = -1;
+ cgltf_extras extras = {0, 0};
for (int k = 0; k < obj_size; ++k)
{
@@ -2942,8 +2863,7 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- extras_tok = i + 1;
- i = cgltf_skip_json(tokens, extras_tok);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &extras);
}
else
{
@@ -2971,13 +2891,7 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto
out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material);
out_mappings[*offset].variant = variant;
-
- if (extras_tok >= 0)
- {
- int e = cgltf_parse_json_extras(options, tokens, extras_tok, json_chunk, &out_mappings[*offset].extras);
- if (e < 0)
- return e;
- }
+ out_mappings[*offset].extras = extras;
(*offset)++;
}
@@ -3092,7 +3006,7 @@ static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* t
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_prim->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_prim->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -3339,7 +3253,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->indices_extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->indices_extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -3382,7 +3296,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->values_extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->values_extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -3401,7 +3315,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -3524,7 +3438,7 @@ static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* to
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_accessor->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_accessor->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -3630,7 +3544,7 @@ static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture_view->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture_view->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -3726,6 +3640,10 @@ static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmnt
i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
&out_pbr->metallic_roughness_texture);
}
+ else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+ {
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_pbr->extras);
+ }
else
{
i = cgltf_skip_json(tokens, i+1);
@@ -4158,7 +4076,7 @@ static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* token
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_image->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_image->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -4227,7 +4145,7 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -4276,7 +4194,7 @@ static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tok
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -4439,7 +4357,7 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_material->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_material->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -4792,7 +4710,7 @@ static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const*
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer_view->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer_view->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -4895,7 +4813,7 @@ static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* toke
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -4979,7 +4897,7 @@ static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_skin->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_skin->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -5033,6 +4951,19 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
{
i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name);
}
+ else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0)
+ {
+ ++i;
+ if (cgltf_json_strcmp(tokens + i, json_chunk, "perspective") == 0)
+ {
+ out_camera->type = cgltf_camera_type_perspective;
+ }
+ else if (cgltf_json_strcmp(tokens + i, json_chunk, "orthographic") == 0)
+ {
+ out_camera->type = cgltf_camera_type_orthographic;
+ }
+ ++i;
+ }
else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0)
{
++i;
@@ -5042,11 +4973,6 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
int data_size = tokens[i].size;
++i;
- if (out_camera->type != cgltf_camera_type_invalid)
- {
- return CGLTF_ERROR_JSON;
- }
-
out_camera->type = cgltf_camera_type_perspective;
for (int k = 0; k < data_size; ++k)
@@ -5081,7 +5007,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.perspective.extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.perspective.extras);
}
else
{
@@ -5103,11 +5029,6 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
int data_size = tokens[i].size;
++i;
- if (out_camera->type != cgltf_camera_type_invalid)
- {
- return CGLTF_ERROR_JSON;
- }
-
out_camera->type = cgltf_camera_type_orthographic;
for (int k = 0; k < data_size; ++k)
@@ -5140,7 +5061,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras);
}
else
{
@@ -5155,7 +5076,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -5288,7 +5209,7 @@ static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* token
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_light->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_light->extras);
}
else
{
@@ -5414,7 +5335,7 @@ static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_node->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_node->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -5552,7 +5473,7 @@ static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* token
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_scene->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_scene->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -5634,7 +5555,7 @@ static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -5714,7 +5635,7 @@ static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_channel->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_channel->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -5796,7 +5717,7 @@ static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* t
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_animation->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_animation->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -5852,7 +5773,7 @@ static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tok
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_variant->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_variant->extras);
}
else
{
@@ -5916,7 +5837,7 @@ static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* token
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_asset->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_asset->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@@ -6072,7 +5993,7 @@ static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens
}
else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0)
{
- i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_data->extras);
+ i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_data->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
diff --git a/third_party/cgltf/cgltf_write.h b/third_party/cgltf/cgltf_write.h
index 033b0d1cca..5c4edaf72d 100644
--- a/third_party/cgltf/cgltf_write.h
+++ b/third_party/cgltf/cgltf_write.h
@@ -15,17 +15,19 @@
*
* Reference:
* `cgltf_result cgltf_write_file(const cgltf_options* options, const char*
- * path, const cgltf_data* data)` writes a glTF data to the given file path.
- * If `options->type` is `cgltf_file_type_glb`, both JSON content and binary
- * buffer of the given glTF data will be written in a GLB format.
- * Otherwise, only the JSON part will be written.
- * External buffers and images are not written out. `data` is not deallocated.
+ * path, const cgltf_data* data)` writes JSON to the given file path. Buffer
+ * files and external images are not written out. `data` is not deallocated.
*
* `cgltf_size cgltf_write(const cgltf_options* options, char* buffer,
* cgltf_size size, const cgltf_data* data)` writes JSON into the given memory
* buffer. Returns the number of bytes written to `buffer`, including a null
* terminator. If buffer is null, returns the number of bytes that would have
* been written. `data` is not deallocated.
+ *
+ * To write custom JSON into the `extras` field, aggregate all the custom JSON
+ * into a single buffer, then set `file_data` to this buffer. By supplying
+ * start_offset and end_offset values for various objects, you can select a
+ * range of characters within the aggregated buffer.
*/
#ifndef CGLTF_WRITE_H_INCLUDED__
#define CGLTF_WRITE_H_INCLUDED__
@@ -145,17 +147,6 @@ typedef struct {
context->needs_comma = 1; }
#define CGLTF_WRITE_TEXTURE_INFO(label, info) if (info.texture) { \
- cgltf_write_line(context, "\"" label "\": {"); \
- CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
- cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
- if (info.has_transform) { \
- context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
- cgltf_write_texture_transform(context, &info.transform); \
- } \
- cgltf_write_extras(context, &info.extras); \
- cgltf_write_line(context, "}"); }
-
-#define CGLTF_WRITE_NORMAL_TEXTURE_INFO(label, info) if (info.texture) { \
cgltf_write_line(context, "\"" label "\": {"); \
CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
@@ -167,28 +158,6 @@ typedef struct {
cgltf_write_extras(context, &info.extras); \
cgltf_write_line(context, "}"); }
-#define CGLTF_WRITE_OCCLUSION_TEXTURE_INFO(label, info) if (info.texture) { \
- cgltf_write_line(context, "\"" label "\": {"); \
- CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
- cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
- cgltf_write_floatprop(context, "strength", info.scale, 1.0f); \
- if (info.has_transform) { \
- context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
- cgltf_write_texture_transform(context, &info.transform); \
- } \
- cgltf_write_extras(context, &info.extras); \
- cgltf_write_line(context, "}"); }
-
-#ifndef CGLTF_CONSTS
-static const cgltf_size GlbHeaderSize = 12;
-static const cgltf_size GlbChunkHeaderSize = 8;
-static const uint32_t GlbVersion = 2;
-static const uint32_t GlbMagic = 0x46546C67;
-static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
-static const uint32_t GlbMagicBinChunk = 0x004E4942;
-#define CGLTF_CONSTS
-#endif
-
static void cgltf_write_indent(cgltf_write_context* context)
{
if (context->needs_comma)
@@ -239,24 +208,15 @@ static void cgltf_write_strprop(cgltf_write_context* context, const char* label,
static void cgltf_write_extras(cgltf_write_context* context, const cgltf_extras* extras)
{
- if (extras->data)
+ cgltf_size length = extras->end_offset - extras->start_offset;
+ if (length > 0 && context->data->file_data)
{
+ char* json_string = ((char*) context->data->file_data) + extras->start_offset;
cgltf_write_indent(context);
- CGLTF_SPRINTF("\"extras\": %s", extras->data);
+ CGLTF_SPRINTF("%s", "\"extras\": ");
+ CGLTF_SNPRINTF(length, "%.*s", (int)(extras->end_offset - extras->start_offset), json_string);
context->needs_comma = 1;
}
- else
- {
- cgltf_size length = extras->end_offset - extras->start_offset;
- if (length > 0 && context->data->json)
- {
- char* json_string = ((char*) context->data->json) + extras->start_offset;
- cgltf_write_indent(context);
- CGLTF_SPRINTF("%s", "\"extras\": ");
- CGLTF_SNPRINTF(length, "%.*s", (int)(extras->end_offset - extras->start_offset), json_string);
- context->needs_comma = 1;
- }
- }
}
static void cgltf_write_stritem(cgltf_write_context* context, const char* item)
@@ -653,6 +613,7 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
{
cgltf_write_floatarrayprop(context, "baseColorFactor", params->base_color_factor, 4);
}
+ cgltf_write_extras(context, ¶ms->extras);
cgltf_write_line(context, "}");
}
@@ -665,7 +626,7 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
cgltf_write_line(context, "\"KHR_materials_clearcoat\": {");
CGLTF_WRITE_TEXTURE_INFO("clearcoatTexture", params->clearcoat_texture);
CGLTF_WRITE_TEXTURE_INFO("clearcoatRoughnessTexture", params->clearcoat_roughness_texture);
- CGLTF_WRITE_NORMAL_TEXTURE_INFO("clearcoatNormalTexture", params->clearcoat_normal_texture);
+ CGLTF_WRITE_TEXTURE_INFO("clearcoatNormalTexture", params->clearcoat_normal_texture);
cgltf_write_floatprop(context, "clearcoatFactor", params->clearcoat_factor, 0.0f);
cgltf_write_floatprop(context, "clearcoatRoughnessFactor", params->clearcoat_roughness_factor, 0.0f);
cgltf_write_line(context, "}");
@@ -770,8 +731,8 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
cgltf_write_line(context, "}");
}
- CGLTF_WRITE_NORMAL_TEXTURE_INFO("normalTexture", material->normal_texture);
- CGLTF_WRITE_OCCLUSION_TEXTURE_INFO("occlusionTexture", material->occlusion_texture);
+ CGLTF_WRITE_TEXTURE_INFO("normalTexture", material->normal_texture);
+ CGLTF_WRITE_TEXTURE_INFO("occlusionTexture", material->occlusion_texture);
CGLTF_WRITE_TEXTURE_INFO("emissiveTexture", material->emissive_texture);
if (cgltf_check_floatarray(material->emissive_factor, 3, 0.0f))
{
@@ -1136,47 +1097,6 @@ static void cgltf_write_variant(cgltf_write_context* context, const cgltf_materi
cgltf_write_line(context, "}");
}
-static void cgltf_write_glb(FILE* file, const void* json_buf, const cgltf_size json_size, const void* bin_buf, const cgltf_size bin_size)
-{
- char header[GlbHeaderSize];
- char chunk_header[GlbChunkHeaderSize];
- char json_pad[3] = { 0x20, 0x20, 0x20 };
- char bin_pad[3] = { 0, 0, 0 };
-
- cgltf_size json_padsize = (json_size % 4 != 0) ? 4 - json_size % 4 : 0;
- cgltf_size bin_padsize = (bin_size % 4 != 0) ? 4 - bin_size % 4 : 0;
- cgltf_size total_size = GlbHeaderSize + GlbChunkHeaderSize + json_size + json_padsize;
- if (bin_buf != NULL && bin_size > 0) {
- total_size += GlbChunkHeaderSize + bin_size + bin_padsize;
- }
-
- // Write a GLB header
- memcpy(header, &GlbMagic, 4);
- memcpy(header + 4, &GlbVersion, 4);
- memcpy(header + 8, &total_size, 4);
- fwrite(header, 1, GlbHeaderSize, file);
-
- // Write a JSON chunk (header & data)
- uint32_t json_chunk_size = (uint32_t)(json_size + json_padsize);
- memcpy(chunk_header, &json_chunk_size, 4);
- memcpy(chunk_header + 4, &GlbMagicJsonChunk, 4);
- fwrite(chunk_header, 1, GlbChunkHeaderSize, file);
-
- fwrite(json_buf, 1, json_size, file);
- fwrite(json_pad, 1, json_padsize, file);
-
- if (bin_buf != NULL && bin_size > 0) {
- // Write a binary chunk (header & data)
- uint32_t bin_chunk_size = (uint32_t)(bin_size + bin_padsize);
- memcpy(chunk_header, &bin_chunk_size, 4);
- memcpy(chunk_header + 4, &GlbMagicBinChunk, 4);
- fwrite(chunk_header, 1, GlbChunkHeaderSize, file);
-
- fwrite(bin_buf, 1, bin_size, file);
- fwrite(bin_pad, 1, bin_padsize, file);
- }
-}
-
cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data)
{
cgltf_size expected = cgltf_write(options, NULL, 0, data);
@@ -1185,18 +1105,13 @@ cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, co
if (expected != actual) {
fprintf(stderr, "Error: expected %zu bytes but wrote %zu bytes.\n", expected, actual);
}
- FILE* file = fopen(path, "wb");
+ FILE* file = fopen(path, "wt");
if (!file)
{
return cgltf_result_file_not_found;
}
// Note that cgltf_write() includes a null terminator, which we omit from the file content.
- if (options->type == cgltf_file_type_glb) {
- cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size);
- } else {
- // Write a plain JSON file.
- fwrite(buffer, actual - 1, 1, file);
- }
+ fwrite(buffer, actual - 1, 1, file);
fclose(file);
free(buffer);
return cgltf_result_success;
diff --git a/third_party/cgltf/test/CMakeLists.txt b/third_party/cgltf/test/CMakeLists.txt
index d62d01d6cc..aa31730524 100644
--- a/third_party/cgltf/test/CMakeLists.txt
+++ b/third_party/cgltf/test/CMakeLists.txt
@@ -41,19 +41,6 @@ else()
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
-set( EXE_NAME test_write_glb )
-add_executable( ${EXE_NAME} test_write_glb.cpp )
-set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
-if(MSVC)
- target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
- add_definitions( -D_CRT_SECURE_NO_WARNINGS)
-else()
- target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
- target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
- target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
-endif()
-install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
-
set( EXE_NAME test_math )
add_executable( ${EXE_NAME} test_math.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
diff --git a/third_party/cgltf/test/test_all.py b/third_party/cgltf/test/test_all.py
index ac0882f5d8..ec9b9f0164 100755
--- a/third_party/cgltf/test/test_all.py
+++ b/third_party/cgltf/test/test_all.py
@@ -59,7 +59,6 @@ if __name__ == "__main__":
collect_files("glTF-Sample-Models/2.0/", ".glb", "test_conversion")
collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_conversion")
collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_write")
- collect_files("glTF-Sample-Models/2.0/", ".glb", "test_write_glb")
result = os.system(get_executable_path("test_math"))
if result != 0:
diff --git a/third_party/cgltf/test/test_write_glb.cpp b/third_party/cgltf/test/test_write_glb.cpp
deleted file mode 100644
index c3c5b4430e..0000000000
--- a/third_party/cgltf/test/test_write_glb.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#define CGLTF_IMPLEMENTATION
-#define CGLTF_WRITE_IMPLEMENTATION
-#include "../cgltf_write.h"
-
-#include
-#include
-#include
-#include
-#include
-
-int main(int argc, char** argv)
-{
- if (argc < 2)
- {
- printf("err\n");
- return -1;
- }
-
- cgltf_options options = {};
- cgltf_data* data0 = NULL;
- cgltf_result result = cgltf_parse_file(&options, argv[1], &data0);
-
- // Silently skip over files that are unreadable since this is a writing test.
- if (result != cgltf_result_success)
- {
- return cgltf_result_success;
- }
-
- options.type = cgltf_file_type_glb; // Write back in a GLB format
- result = cgltf_write_file(&options, "out.glb", data0);
- if (result != cgltf_result_success)
- {
- return result;
- }
-
- cgltf_data* data1 = NULL;
- result = cgltf_parse_file(&options, "out.glb", &data1);
- if (result != cgltf_result_success)
- {
- return result;
- }
-
- if (data0->meshes_count != data1->meshes_count) {
- return -1;
- }
-
- // Compare binary buffers
- if (data0->bin_size != data1->bin_size) {
- return -1;
- }
- if (memcmp(data0->bin, data1->bin, data0->bin_size) != 0) {
- return -1;
- }
-
- cgltf_free(data1);
- cgltf_free(data0);
- return cgltf_result_success;
-}
diff --git a/third_party/cgltf/tnt/README b/third_party/cgltf/tnt/README
index 1e992ce14c..fbfc97e770 100644
--- a/third_party/cgltf/tnt/README
+++ b/third_party/cgltf/tnt/README
@@ -1,10 +1,10 @@
This folder was last updated as follows:
- export sha=280ab89
+ export tag=v1.13
cd third_party
- curl -L -O https://github.com/jkuhlmann/cgltf/archive/${sha}.zip
- unzip ${sha}.zip
+ curl -L -O https://github.com/jkuhlmann/cgltf/archive/refs/tags/${tag}.zip
+ unzip ${tag}.zip
mv cgltf-* cgltf_new
rsync -r cgltf_new/ cgltf/ --delete --exclude tnt
- rm -rf ${sha}.zip cgltf_new
+ rm -rf ${tag}.zip cgltf_new
git add cgltf ; git status
From 2cd492ed38c140494e0695dc598a3e5b3e7567c5 Mon Sep 17 00:00:00 2001
From: Powei Feng
Date: Wed, 4 Oct 2023 10:25:15 -0700
Subject: [PATCH 07/63] Fix build failures due to filamat lite removal try 4
(#7228)
---
.github/workflows/android-continuous.yml | 4 ----
.github/workflows/release.yml | 1 -
2 files changed, 5 deletions(-)
diff --git a/.github/workflows/android-continuous.yml b/.github/workflows/android-continuous.yml
index 326ceb6546..e9aebe8e76 100644
--- a/.github/workflows/android-continuous.yml
+++ b/.github/workflows/android-continuous.yml
@@ -29,10 +29,6 @@ jobs:
with:
name: filamat-android-full
path: out/filamat-android-release.aar
- - uses: actions/upload-artifact@v1.0.0
- with:
- name: filamat-android-lite
- path: out/filamat-android-lite-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: gltfio-android-release
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6f2422b1b6..fa12f3d322 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -124,7 +124,6 @@ jobs:
cd ../..
mv out/filament-android-release.aar out/filament-${TAG}-android.aar
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
- mv out/filamat-android-lite-release.aar out/filamat-${TAG}-lite-android.aar
mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar
mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar
- name: Sign sample-gltf-viewer
From 5190b03f891b8a4be1e10e1774e95fa199b36195 Mon Sep 17 00:00:00 2001
From: Mathias Agopian
Date: Wed, 4 Oct 2023 22:10:49 -0700
Subject: [PATCH 08/63] the visibility type can be 8 bits instead of 16.
It was changed to 16 a while back to handle more shadows, but since
then we changed the culling algorithm and 8 bits is enough again.
---
filament/src/Culler.h | 4 ++--
filament/src/ShadowMap.h | 13 +++++++------
filament/src/details/View.h | 2 --
3 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/filament/src/Culler.h b/filament/src/Culler.h
index ad5fd4f3c8..6f3a0bf9f9 100644
--- a/filament/src/Culler.h
+++ b/filament/src/Culler.h
@@ -37,12 +37,12 @@ namespace filament {
class Culler {
public:
// Culler can only process buffers with a size multiple of MODULO
- static constexpr size_t MODULO = 4u;
+ static constexpr size_t MODULO = 8u;
static inline size_t round(size_t count) noexcept {
return (count + (MODULO - 1)) & ~(MODULO - 1);
}
- using result_type = uint16_t;
+ using result_type = uint8_t;
/*
* returns whether each AABB in an array intersects with the frustum
diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h
index af78e271a4..3849710c3b 100644
--- a/filament/src/ShadowMap.h
+++ b/filament/src/ShadowMap.h
@@ -39,12 +39,12 @@ class RenderPass;
// The value of the 'VISIBLE_MASK' after culling. Each bit represents visibility in a frustum
// (either camera or light).
//
-// 1
-// bits 5 ... 7 6 5 4 3 2 1 0
-// +------------------------------------------------------+
-// VISIBLE_RENDERABLE X
-// VISIBLE_DIR_SHADOW_RENDERABLE X
-// VISIBLE_DYN_SHADOW_RENDERABLE X
+//
+// bits 7 6 5 4 3 2 1 0
+// +---------------------------------------------+
+// VISIBLE_RENDERABLE X
+// VISIBLE_DIR_SHADOW_RENDERABLE X
+// VISIBLE_DYN_SHADOW_RENDERABLE X
// A "shadow renderable" is a renderable rendered to the shadow map during a shadow pass:
// PCF shadows: only shadow casters
@@ -54,6 +54,7 @@ static constexpr size_t VISIBLE_RENDERABLE_BIT = 0u;
static constexpr size_t VISIBLE_DIR_SHADOW_RENDERABLE_BIT = 1u;
static constexpr size_t VISIBLE_DYN_SHADOW_RENDERABLE_BIT = 2u;
+static constexpr Culler::result_type VISIBLE_RENDERABLE = 1u << VISIBLE_RENDERABLE_BIT;
static constexpr Culler::result_type VISIBLE_DIR_SHADOW_RENDERABLE = 1u << VISIBLE_DIR_SHADOW_RENDERABLE_BIT;
static constexpr Culler::result_type VISIBLE_DYN_SHADOW_RENDERABLE = 1u << VISIBLE_DYN_SHADOW_RENDERABLE_BIT;
diff --git a/filament/src/details/View.h b/filament/src/details/View.h
index f452ce22f1..50a6c6bb52 100644
--- a/filament/src/details/View.h
+++ b/filament/src/details/View.h
@@ -73,8 +73,6 @@ class FMaterialInstance;
class FRenderer;
class FScene;
-static constexpr Culler::result_type VISIBLE_RENDERABLE = 1u << VISIBLE_RENDERABLE_BIT;
-
// ------------------------------------------------------------------------------------------------
class FView : public View {
From f1f7aeb14fea40899b0c8efd5e7bf57dd7b7c911 Mon Sep 17 00:00:00 2001
From: Jacob Su
Date: Thu, 5 Oct 2023 00:33:27 +0800
Subject: [PATCH 09/63] fix java Engine.Builder method access modifiers.
---
.../java/com/google/android/filament/Engine.java | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java
index 291200adca..3423e87d05 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java
@@ -173,7 +173,7 @@ public class Engine {
* @param backend Driver backend to use
* @return A reference to this Builder for chaining calls.
*/
- Builder backend(Backend backend) {
+ public Builder backend(Backend backend) {
nSetBuilderBackend(mNativeBuilder, backend.ordinal());
return this;
}
@@ -186,7 +186,7 @@ public class Engine {
* must be an instance of {@link android.opengl.EGLContext}.
* @return A reference to this Builder for chaining calls.
*/
- Builder sharedContext(Object sharedContext) {
+ public Builder sharedContext(Object sharedContext) {
if (Platform.get().validateSharedContext(sharedContext)) {
nSetBuilderSharedContext(mNativeBuilder,
Platform.get().getSharedContextNativeHandle(sharedContext));
@@ -201,7 +201,7 @@ public class Engine {
* @param config A {@link Config} object
* @return A reference to this Builder for chaining calls.
*/
- Builder config(Config config) {
+ public Builder config(Config config) {
nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB,
config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB,
config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB,
@@ -219,7 +219,7 @@ public class Engine {
* @exception IllegalStateException can be thrown if there isn't enough memory to
* allocate the command buffer.
*/
- Engine build() {
+ public Engine build() {
long nativeEngine = nBuilderBuild(mNativeBuilder);
if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine");
return new Engine(nativeEngine);
@@ -352,9 +352,7 @@ public class Engine {
* @exception IllegalStateException can be thrown if there isn't enough memory to
* allocate the command buffer.
*
- * @deprecated use {@link Builder}
*/
- @Deprecated
@NonNull
public static Engine create() {
return new Builder().build();
@@ -374,9 +372,7 @@ public class Engine {
* @exception IllegalStateException can be thrown if there isn't enough memory to
* allocate the command buffer.
*
- * @deprecated use {@link Builder}
*/
- @Deprecated
@NonNull
public static Engine create(@NonNull Backend backend) {
return new Builder()
@@ -400,9 +396,7 @@ public class Engine {
* @exception IllegalStateException can be thrown if there isn't enough memory to
* allocate the command buffer.
*
- * @deprecated use {@link Builder}
*/
- @Deprecated
@NonNull
public static Engine create(@NonNull Object sharedContext) {
return new Builder()
From 6590f62052149b36cba15be2df22d339bf254e19 Mon Sep 17 00:00:00 2001
From: Mathias Agopian
Date: Thu, 5 Oct 2023 10:38:18 -0700
Subject: [PATCH 10/63] handle more generic projections for shadowFar
When we update the Far plane in the projection matrix, we assumed the
shape of the matrix. This fell appart when the projection matrix was
(for instance) a blend between an ortho and perspective projection.
We now do this more generally, that is, with less assumptions on the
projection matrix shape.
---
filament/src/ShadowMapManager.cpp | 64 ++++++++++++++++++++++++++-----
1 file changed, 54 insertions(+), 10 deletions(-)
diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp
index 34c47519e6..30880c96a9 100644
--- a/filament/src/ShadowMapManager.cpp
+++ b/filament/src/ShadowMapManager.cpp
@@ -446,21 +446,65 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight);
// Adjust the camera's projection for the light's shadowFar
-
cameraInfo.zf = params.options.shadowFar > 0.0f ? params.options.shadowFar : cameraInfo.zf;
if (UTILS_UNLIKELY(params.options.shadowFar > 0.0f)) {
cameraInfo.zf = params.options.shadowFar;
float const n = cameraInfo.zn;
float const f = cameraInfo.zf;
- if (std::abs(cameraInfo.cullingProjection[2].w) > std::numeric_limits::epsilon()) {
- // perspective projection
- cameraInfo.cullingProjection[2].z = (f + n) / (n - f);
- cameraInfo.cullingProjection[3].z = (2 * f * n) / (n - f);
- } else {
- // orthographic projection
- cameraInfo.cullingProjection[2].z = 2.0f / (n - f);
- cameraInfo.cullingProjection[3].z = (f + n) / (n - f);
- }
+
+ /*
+ * Updating a projection matrix near and far planes:
+ *
+ * We assume that the near and far plane equations are of the form:
+ * N = { 0, 0, 1, n }
+ * F = { 0, 0, -1, -f }
+ *
+ * We also assume that the lower-left 2x2 of the projection is all 0:
+ * P = A 0 C 0
+ * 0 B D 0
+ * 0 0 E F
+ * 0 0 G H
+ *
+ * It result that we need to calculate E and F while leaving all other parameter unchanged.
+ *
+ * We know that:
+ * with N, F the near/far normalized plane equation parameters
+ * sn, sf arbitrary scale factors (they don't affect the planes)
+ * m is the transpose of projection (see Frustum.cpp)
+ *
+ * sn * N == -m[3] - m[2]
+ * sf * F == -m[3] + m[2]
+ *
+ * sn * N + sf * F == -2 * m[3]
+ * sn * N - sf * F == -2 * m[2]
+ *
+ * sn * N.z + sf * F.z == -2 * m[3].z
+ * sn * N.w + sf * F.w == -2 * m[3].w
+ * sn * N.z - sf * F.z == -2 * m[2].z
+ * sn * N.w - sf * F.w == -2 * m[2].w
+ *
+ * sn * N.z + sf * F.z == -2 * p[2].w
+ * sn * N.w + sf * F.w == -2 * p[3].w
+ * sn * N.z - sf * F.z == -2 * p[2].z
+ * sn * N.w - sf * F.w == -2 * p[3].z
+ *
+ * We now need to solve for { p[2].z, p[3].z, sn, sf } :
+ *
+ * sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z)
+ * sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z)
+ * p[2].z == (sf * F.z - sn * N.z) / 2
+ * p[3].z == (sf * F.w - sn * N.w) / 2
+ */
+ auto& p = cameraInfo.cullingProjection;
+ float4 const N = { 0, 0, 1, n }; // near plane equation
+ float4 const F = { 0, 0, -1, -f }; // far plane equation
+ // near plane equation scale factor
+ float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z);
+ // far plane equation scale factor
+ float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z);
+ // New values for the projection
+ p[2].z = (sf * F.z - sn * N.z) * 0.5f;
+ p[3].z = (sf * F.w - sn * N.w) * 0.5f;
}
const ShadowMap::ShadowMapInfo shadowMapInfo{
From 78d433cafad46dfa2b27a86408dab397b652bd93 Mon Sep 17 00:00:00 2001
From: Powei Feng
Date: Thu, 5 Oct 2023 22:43:39 -0700
Subject: [PATCH 11/63] Update NEW_RELEASE_NOTES.md
---
NEW_RELEASE_NOTES.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md
index 4a42fb94ef..5b74000c76 100644
--- a/NEW_RELEASE_NOTES.md
+++ b/NEW_RELEASE_NOTES.md
@@ -8,5 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
-engine: Added parameter for configuring JobSystem thread count
-engine: In Java, decpreate Engine.create() and recommend Engine.Builder instead
\ No newline at end of file
+- engine: Added parameter for configuring JobSystem thread count
+- engine: In Java, introduce Engine.Builder
From 6279613b79bcf003edd9781f1a53f833b0ce147d Mon Sep 17 00:00:00 2001
From: Ben Doherty
Date: Mon, 9 Oct 2023 14:15:07 -0700
Subject: [PATCH 12/63] Metal: support float16 operations in --optimize-size
mode (#7249)
---
libs/filamat/src/GLSLPostProcessor.cpp | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp
index 73605eb258..6d92153d5f 100644
--- a/libs/filamat/src/GLSLPostProcessor.cpp
+++ b/libs/filamat/src/GLSLPostProcessor.cpp
@@ -587,13 +587,14 @@ std::shared_ptr GLSLPostProcessor::createOptimizer(
registerSizePasses(*optimizer, config);
} else if (optimization == MaterialBuilder::Optimization::PERFORMANCE) {
registerPerformancePasses(*optimizer, config);
- // Metal doesn't support relaxed precision, but does have support for float16 math operations.
- if (config.targetApi == MaterialBuilder::TargetApi::METAL) {
- optimizer->RegisterPass(CreateConvertRelaxedToHalfPass());
- optimizer->RegisterPass(CreateSimplificationPass());
- optimizer->RegisterPass(CreateRedundancyEliminationPass());
- optimizer->RegisterPass(CreateAggressiveDCEPass());
- }
+ }
+
+ // Metal doesn't support relaxed precision, but does have support for float16 math operations.
+ if (config.targetApi == MaterialBuilder::TargetApi::METAL) {
+ optimizer->RegisterPass(CreateConvertRelaxedToHalfPass());
+ optimizer->RegisterPass(CreateSimplificationPass());
+ optimizer->RegisterPass(CreateRedundancyEliminationPass());
+ optimizer->RegisterPass(CreateAggressiveDCEPass());
}
return optimizer;
From e78a06797ccd01149d0d561085e7c52a31e6f81a Mon Sep 17 00:00:00 2001
From: Powei Feng
Date: Tue, 10 Oct 2023 13:06:24 -0700
Subject: [PATCH 13/63] doc: pin viewer lit to a specific version (#7253)
- Pin lit to version 2.8.0 (to fix a breakage caused by new
release).
- Update viewer filament version to latest
- Use symbolic link instead of having two copies of the same
file. (Could we consider removing `filament-viewer.js` in
`web/filament-js/` ?)
- Update `web/filament-js/README.md`
---
docs/viewer/filament-viewer.js | 456 +----------------------------
docs/viewer/index.html | 2 +-
web/filament-js/README.md | 4 +-
web/filament-js/filament-viewer.js | 2 +-
4 files changed, 4 insertions(+), 460 deletions(-)
mode change 100644 => 120000 docs/viewer/filament-viewer.js
diff --git a/docs/viewer/filament-viewer.js b/docs/viewer/filament-viewer.js
deleted file mode 100644
index 01439952f4..0000000000
--- a/docs/viewer/filament-viewer.js
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
-* Copyright (C) 2021 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-// If you are bundling this with rollup, webpack, or esbuild, the following URL should be trimmed.
-import { LitElement, html, css } from "https://unpkg.com/lit?module";
-
-// This little utility checks if the Filament module is ready for action.
-// If so, it immediately calls the given function. If not, it asks the Filament
-// loader to call it as soon as the module becomes ready.
-class FilamentTasks {
- add(callback) {
- if (Filament.isReady) {
- callback();
- } else {
- Filament.init([], callback);
- }
- }
-}
-
-// FilamentViewer is a fairly limited web component for showing glTF models with Filament.
-//
-// To embed a 3D viewer in your web page, simply add something like the following to your HTML,
-// similar to an
element.
-//
-//
-//
-// In addition to the src URL, attributes can be used to set up an optional IBL and skybox.
-// The documentation for these attributes is in the FilamentViewer constructor.
-//
-// MISSING FEATURES
-// ----------------
-// None of the following features are implemented. They would be easy to add.
-// - Replace gltumble and Trackball with camutils Manipulator (i.e. enable scroll-to-zoom)
-// - Write a documentation page (might be neat if the doc page has instances of the actual viewer)
-// - Fix the import at the top of the file to support webpack / rollup / esbuild
-// - Expose more animation properties (e.g. enable / disable, selected index)
-// - Expose camera properties, glTF camera selection, and clear color
-// - Expose more IBL properties
-// - Expose directional light properties
-// - Optional turntable animation
-//
-class FilamentViewer extends LitElement {
- constructor() {
- super();
-
- // LitElement properties:
- this.src = null; // Path to glTF file.
- this.alt = null; // Alternate canvas content.
- this.ibl = null; // Path to image based light ktx.
- this.sky = null; // Path to skybox ktx.
- this.enableDrop = null; // Enables drag and drop.
- this.intensity = 30000; // Intensity of the image based light.
- this.materialVariant = 0; // Index of material variant.
-
- // Private properties:
- this.filamentTasks = new FilamentTasks();
- this.canvasId = "filament-viewer-canvas";
- this.overlayId = "filament-viewer-overlay";
- this.srcBlob = null;
- }
-
- static get properties() {
- return {
- src: { type: String },
- alt: { type: String },
- ibl: { type: String },
- sky: { type: String },
- enableDrop: { type: Boolean },
- intensity: { type: Number },
- materialVariant: { type: Number },
- }
- }
-
- firstUpdated() {
- // At this point in the lit-element lifecycle, the "render" has taken place, which simply
- // means the canvas element now exists. However the Filament wasm module may or may not be
- // fully loaded, which is why we use the task manager.
- const canvas = this.shadowRoot.getElementById(this.canvasId);
- if (canvas.parentNode.host.parentElement.tagName === "FILAMENT-VIEWER") {
- console.error("Do not nest FilamentViewer, this is unsupported.");
- console.error("Try placing each viewer in a wrapper element.");
- return;
- }
- this.filamentTasks.add(this._startFilament.bind(this));
-
- const overlay = this.shadowRoot.getElementById(this.overlayId);
- ["dragenter", "dragover", "dragleave", "drop"].forEach(eventName => {
- overlay.addEventListener(eventName, e => { e.preventDefault(); e.stopPropagation() }, false)
- });
- if (this.enableDrop) {
- overlay.addEventListener("drop", this._dropHandler.bind(this), false);
- }
- }
-
- updated(props) {
- if (props.has("src")) this.filamentTasks.add(this._loadAsset.bind(this));
- if (props.has("ibl")) this.filamentTasks.add(this._loadIbl.bind(this));
- if (props.has("sky")) this.filamentTasks.add(this._loadSky.bind(this));
- if (props.has("enableDrop")) this._updateOverlay();
- if (props.has("intensity") && this.indirectLight) {
- this.indirectLight.setIntensity(this.intensity);
- }
- if (props.has("materialVariant") && this.asset) this._applyMaterialVariant();
- }
-
- static get styles() {
- return css`
- :host {
- display: inline-block;
- width: 300px;
- height: 300px;
- border: solid 1px black;
- position: relative;
- }
- canvas {
- background: #D9D9D9; /* This is consistent with the default clear color. */
- }
- canvas, .overlay {
- width: 100%;
- height: 100%;
- position: absolute;
- }
- .overlay {
- text-align: center;
- padding: 10px;
- }`;
- }
-
- render() {
- return html`
-
-
- `;
- }
-
- _dropHandler(dragEvent) {
- if (!dragEvent.dataTransfer) return;
- this.srcBlob = null;
- this.srcBlobResources = {};
- for (const file of dragEvent.dataTransfer.files) {
- if (file.name.endsWith(".glb") || file.name.endsWith(".gltf")) {
- this.srcBlob = file;
- } else {
- this.srcBlobResources[file.name] = file;
- }
- }
- if (this.srcBlob) {
- this._loadAsset();
- } else {
- console.error("Please include a glTF file.");
- }
- };
-
- _updateOverlay() {
- const overlay = this.shadowRoot.getElementById(this.overlayId);
- if (!this.enableDrop || this.asset) {
- overlay.innerHTML = "";
- } else {
- overlay.innerHTML = "Drop glb or file set here.";
- }
- }
-
- _startFilament() {
- const LightType = Filament.LightManager$Type;
-
- const canvas = this.shadowRoot.getElementById(this.canvasId);
- const overlay = this.shadowRoot.getElementById(this.overlayId);
-
- this.engine = Filament.Engine.create(canvas);
- this.scene = this.engine.createScene();
- this.sunlight = Filament.EntityManager.get().create();
- this.scene.addEntity(this.sunlight);
- this.loader = this.engine.createAssetLoader();
- this.cameraEntity = Filament.EntityManager.get().create();
- this.camera = this.engine.createCamera(this.cameraEntity);
- this.swapChain = this.engine.createSwapChain();
- this.renderer = this.engine.createRenderer();
- this.view = this.engine.createView();
- this.view.setVignetteOptions({ midPoint: 0.7, enabled: true });
- this.view.setCamera(this.camera);
- this.view.setScene(this.scene);
-
- // If gltumble has been loaded, use it.
- if (window.Trackball) {
- this.trackball = new Trackball(overlay, { startSpin: 0.0 });
- }
-
- // This color is consistent with the default CSS background color.
- this.renderer.setClearOptions({ clearColor: [0.8, 0.8, 0.8, 1.0], clear: true });
-
- Filament.LightManager.Builder(LightType.SUN)
- .direction([0, -.7, -.7])
- .sunAngularRadius(1.9)
- .castShadows(true)
- .sunHaloSize(10.0)
- .sunHaloFalloff(80.0)
- .build(this.engine, this.sunlight);
-
- // TODO: if ResizeObserver is not supported, then we should call _onResized and
- // pass (canvas.clientWidth * window.devicePixelRatio) for the dimensions.
- var ro = new ResizeObserver(entries => {
- const width = entries[0].devicePixelContentBoxSize[0].inlineSize;
- const height = entries[0].devicePixelContentBoxSize[0].blockSize;
- this._onResized(width, height);
- });
- ro.observe(canvas);
-
- window.requestAnimationFrame(this._renderFrame.bind(this));
- }
-
- _onResized(width, height) {
- const Fov = Filament.Camera$Fov;
- const canvas = this.shadowRoot.getElementById(this.canvasId);
- canvas.width = width;
- canvas.height = height;
- this.view.setViewport([0, 0, width, height]);
- const y = -0.125, eye = [0, y, 1.5], center = [0, y, 0], up = [0, 1, 0];
- this.camera.lookAt(eye, center, up);
- const aspect = width / height;
- const fov = aspect < 1 ? Fov.HORIZONTAL : Fov.VERTICAL;
- this.camera.setProjectionFov(25, aspect, 1.0, 10.0, fov);
- }
-
- _loadIbl() {
- if (!this.ibl) {
- return;
- }
- if (this.indirectLight) {
- console.info("FilamentViewer does not allow the IBL to be changed.");
- return;
- }
- fetch(this.ibl).then(response => {
- return response.arrayBuffer();
- }).then(arrayBuffer => {
- const ktxData = new Uint8Array(arrayBuffer);
- this.indirectLight = this.engine.createIblFromKtx1(ktxData);
- this.indirectLight.setIntensity(this.intensity);
- this.scene.setIndirectLight(this.indirectLight);
- });
- }
-
- _loadSky() {
- if (!this.sky) {
- return;
- }
- if (this.skybox) {
- console.info("FilamentViewer does not allow the skybox to be changed.");
- return;
- }
- fetch(this.sky).then(response => {
- return response.arrayBuffer();
- }).then(arrayBuffer => {
- const ktxData = new Uint8Array(arrayBuffer);
- this.skybox = this.engine.createSkyFromKtx1(ktxData);
- this.scene.setSkybox(this.skybox);
- });
- }
-
- _loadAsset() {
- const zoffset = 4;
-
- if (this.asset) {
- this.scene.removeEntities(this.asset.getEntities());
- this.animator = null;
- this.asset = null;
- }
-
- // If we have neither a URL nor a dropped file, leave early.
- if (!this.src && !this.srcBlob) {
- this._updateOverlay();
- return;
- }
-
- // Dropping a glb file is simple because there are no external resources.
- if (this.srcBlob && this.srcBlob.name.endsWith(".glb")) {
- this.srcBlob.arrayBuffer().then(buffer => {
- this.asset = this.loader.createAssetFromBinary(new Uint8Array(buffer));
- const aabb = this.asset.getBoundingBox();
- this.assetRoot = this.asset.getRoot();
- this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset);
- this.asset.loadResources();
- this.animator = this.asset.getAnimator();
- this.animationStartTime = Date.now();
- this._updateOverlay();
- });
- return;
- }
-
- // Dropping a fileset requires pushing each resource to ResourceLoader.
- if (this.srcBlob && this.srcBlob.name.endsWith(".gltf")) {
-
- const config = {
- normalizeSkinningWeights: true,
- recomputeBoundingBoxes: false,
- ignoreBindTransform: false,
- asyncInterval: 30
- };
-
- const doneAddingResources = (resourceLoader, stbProvider, ktx2Provider) => {
- this.srcBlobResources = {};
- resourceLoader.asyncBeginLoad(this.asset);
- const timer = setInterval(() => {
- resourceLoader.asyncUpdateLoad();
- const progress = resourceLoader.asyncGetLoadProgress();
- if (progress >= 1) {
- clearInterval(timer);
- resourceLoader.delete();
- stbProvider.delete();
- ktx2Provider.delete();
- this.animator = this.asset.getAnimator();
- this.animationStartTime = Date.now();
- }
- }, config.asyncInterval);
- };
-
- this.srcBlob.arrayBuffer().then(buffer => {
- this.asset = this.loader.createAssetFromJson(new Uint8Array(buffer));
- const aabb = this.asset.getBoundingBox();
- this.assetRoot = this.asset.getRoot();
- this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset);
-
- const resourceLoader = new Filament.gltfio$ResourceLoader(this.engine,
- config.normalizeSkinningWeights,
- config.recomputeBoundingBoxes,
- config.ignoreBindTransform);
-
- const stbProvider = new Filament.gltfio$StbProvider(this.engine);
- const ktx2Provider = new Filament.gltfio$Ktx2Provider(this.engine);
-
- resourceLoader.addStbProvider("image/jpeg", stbProvider);
- resourceLoader.addStbProvider("image/png", stbProvider);
- resourceLoader.addKtx2Provider("image/ktx2", ktx2Provider);
-
- let remaining = Object.keys(this.srcBlobResources).length;
- for (const name in this.srcBlobResources) {
- this.srcBlobResources[name].arrayBuffer().then(buffer => {
- const desc = getBufferDescriptor(new Uint8Array(buffer));
- resourceLoader.addResourceData(name, getBufferDescriptor(desc));
- if (--remaining === 0) {
- doneAddingResources(resourceLoader, stbProvider, ktx2Provider);
- }
- });
- }
-
- this._updateOverlay();
- });
-
- return;
- }
-
- // If we reach this point, we are loading from a src URL rather than drag-and-drop.
-
- fetch(this.src).then(response => {
- return response.arrayBuffer();
- }).then(arrayBuffer => {
- const modelData = new Uint8Array(arrayBuffer);
- if (this.src.endsWith(".glb")) {
- this.asset = this.loader.createAssetFromBinary(modelData);
- } else {
- this.asset = this.loader.createAssetFromJson(modelData);
- }
-
- const aabb = this.asset.getBoundingBox();
- this.assetRoot = this.asset.getRoot();
- this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset);
-
- const basePath = '' + new URL(this.src, document.location);
-
- this.asset.loadResources(() => {
- this.animator = this.asset.getAnimator();
- this.animationStartTime = Date.now();
- this._applyMaterialVariant();
- }, null, basePath);
-
- this._updateOverlay();
- });
- }
-
- _updateAsset() {
- // Invoke the first glTF animation if it exists.
- if (this.animator) {
- if (this.animator.getAnimationCount() > 0) {
- const ms = Date.now() - this.animationStartTime;
- this.animator.applyAnimation(0, ms / 1000);
- }
- this.animator.updateBoneMatrices();
- }
-
- // Apply the root transform of the model.
- const tcm = this.engine.getTransformManager();
- const inst = tcm.getInstance(this.assetRoot);
- let rootTransform = this.unitCubeTransform;
- if (this.trackball) {
- rootTransform = Filament.multiplyMatrices(rootTransform, this.trackball.getMatrix());
- }
- tcm.setTransform(inst, rootTransform);
- inst.delete();
-
- // Add renderable entities to the scene as they become ready.
- while (true) {
- const entity = this.asset.popRenderable();
- if (entity.getId() == 0) {
- entity.delete();
- break;
- }
- this.scene.addEntity(entity);
- entity.delete();
- }
- }
-
- _renderFrame() {
- // Apply transforms and add entities to the scene.
- if (this.asset) {
- this._updateAsset();
- }
-
- // Render the view and update the Filament engine.
- if (this.renderer.beginFrame(this.swapChain)) {
- this.renderer.renderView(this.view);
- this.renderer.endFrame();
- }
- this.engine.execute();
-
- window.requestAnimationFrame(this._renderFrame.bind(this));
- }
-
- _applyMaterialVariant() {
- if (!this.hasAttribute("materialVariant")) {
- return;
- }
- const names = this.asset.getMaterialVariantNames();
- const index = this.materialVariant;
- if (index < 0 || index >= names.length) {
- console.error(`Material variant ${index} does not exist in this asset.`);
- return;
- }
- console.info(this.src, `Applying material variant: ${names[index]}`);
- this.asset.applyMaterialVariant(index);
- }
-}
-
-customElements.define("filament-viewer", FilamentViewer);
diff --git a/docs/viewer/filament-viewer.js b/docs/viewer/filament-viewer.js
new file mode 120000
index 0000000000..bf4479c2be
--- /dev/null
+++ b/docs/viewer/filament-viewer.js
@@ -0,0 +1 @@
+../../web/filament-js/filament-viewer.js
\ No newline at end of file
diff --git a/docs/viewer/index.html b/docs/viewer/index.html
index 8d1ca5a760..2bbc6b187d 100644
--- a/docs/viewer/index.html
+++ b/docs/viewer/index.html
@@ -43,7 +43,7 @@ filament-viewer::part(canvas) {
-
+
+
+
+