diff --git a/libs/gltfio/CMakeLists.txt b/libs/gltfio/CMakeLists.txt index a7b0e56629..509e29149d 100644 --- a/libs/gltfio/CMakeLists.txt +++ b/libs/gltfio/CMakeLists.txt @@ -22,7 +22,9 @@ set(SRCS src/FilamentAsset.cpp src/GltfEnums.h src/MaterialGenerator.cpp + src/MaterialProvider.cpp src/ResourceLoader.cpp + src/UbershaderLoader.cpp src/Wireframe.cpp src/Wireframe.h src/math.h diff --git a/libs/gltfio/include/gltfio/AssetLoader.h b/libs/gltfio/include/gltfio/AssetLoader.h index 5e4ecd1c39..8d07bdf449 100644 --- a/libs/gltfio/include/gltfio/AssetLoader.h +++ b/libs/gltfio/include/gltfio/AssetLoader.h @@ -26,6 +26,21 @@ namespace gltfio { +/** + * AssetLoader can be configured to generate materials using filamat, or to use a small set of + * prebuilt ubershader materials. + */ +enum MaterialSource { + GENERATE_SHADERS, + LOAD_UBERSHADERS, +}; + +struct AssetConfiguration { + class filament::Engine* engine; + utils::NameComponentManager* names = nullptr; + MaterialSource materials = GENERATE_SHADERS; +}; + /** * AssetLoader consumes a blob of glTF 2.0 content (either JSON or GLB) and produces an "asset", * which is a bundle of Filament entities, material instances, textures, vertex buffers, and index @@ -37,15 +52,11 @@ namespace gltfio { * * AssetLoader also owns a cache of Material objects that may be re-used across multiple loads. * - * TODO: currently this uses filamat to generate materials on the fly, but we may wish to allow - * clients to load a small set of precompiled ubershader materials to avoid the footprint of the - * filamat library on resource-constrained platforms. - * * Example usage: * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * auto engine = Engine::create(); - * auto loader = AssetLoader::create(engine); + * auto loader = AssetLoader::create({engine}); * * // Parse the glTF content and create Filament entities. * std::vector content(...); @@ -84,12 +95,14 @@ class AssetLoader { public: /** - * Creates an asset loader for the given engine. + * Creates an asset loader for the given configuration, which specifies the Filament engine. * * The engine is held weakly, used only for the creation and destruction of Filament objects. * The optional name component manager can be used to assign names to renderables. + * The material source specifies whether to use filamat to generate materials on the fly, or to + * load a small set of precompiled ubershader materials. */ - static AssetLoader* create(filament::Engine* engine, utils::NameComponentManager* = nullptr); + static AssetLoader* create(const AssetConfiguration& config); /** * Frees the loader. diff --git a/libs/gltfio/include/gltfio/FilamentAsset.h b/libs/gltfio/include/gltfio/FilamentAsset.h index a3751d1098..3ed541bee4 100644 --- a/libs/gltfio/include/gltfio/FilamentAsset.h +++ b/libs/gltfio/include/gltfio/FilamentAsset.h @@ -135,6 +135,7 @@ struct BufferBinding { bool convertBytesToShorts; // the resource loader must convert the buffer from u8 to u16 bool generateTrivialIndices; // the resource loader must generate indices like: 0, 1, 2, ... + bool generateDummyData; // the resource loader should generate a sequence of 1.0 values }; /** Describes a binding from a Texture to a MaterialInstance. */ diff --git a/libs/gltfio/include/gltfio/MaterialProvider.h b/libs/gltfio/include/gltfio/MaterialProvider.h index 73b86d4efc..aac8b54984 100644 --- a/libs/gltfio/include/gltfio/MaterialProvider.h +++ b/libs/gltfio/include/gltfio/MaterialProvider.h @@ -56,24 +56,7 @@ struct alignas(4) MaterialKey { static_assert(sizeof(MaterialKey) == 12, "MaterialKey has unexpected padding."); -inline bool operator==(const MaterialKey& k1, const MaterialKey& k2) { - return - (k1.doubleSided == k2.doubleSided) && - (k1.unlit == k2.unlit) && - (k1.hasVertexColors == k2.hasVertexColors) && - (k1.hasBaseColorTexture == k2.hasBaseColorTexture) && - (k1.hasMetallicRoughnessTexture == k2.hasMetallicRoughnessTexture) && - (k1.hasNormalTexture == k2.hasNormalTexture) && - (k1.hasOcclusionTexture == k2.hasOcclusionTexture) && - (k1.hasEmissiveTexture == k2.hasEmissiveTexture) && - (k1.alphaMode == k2.alphaMode) && - (k1.baseColorUV == k2.baseColorUV) && - (k1.metallicRoughnessUV == k2.metallicRoughnessUV) && - (k1.emissiveUV == k2.emissiveUV) && - (k1.aoUV == k2.aoUV) && - (k1.normalUV == k2.normalUV) && - (k1.alphaMaskThreshold == k2.alphaMaskThreshold); -} +bool operator==(const MaterialKey& k1, const MaterialKey& k2); // Define a mapping from a uv set index in the source asset to one of Filament's uv sets. enum UvSet : uint8_t { UNUSED, UV0, UV1 }; @@ -85,17 +68,13 @@ using UvMap = std::array; * - The "MaterialGenerator" implementation generates materials at run time (which can be slow) * and requires the filamat library, but produces streamlined shaders. * - * - The "Ubermaterial" implementation uses a small number of pre-built materials with complex + * - The "UbershaderLoader" implementation uses a small number of pre-built materials with complex * fragment shaders, but does not require any run time work or usage of filamat. */ class MaterialProvider { public: static MaterialProvider* createMaterialGenerator(filament::Engine* engine); - - static MaterialProvider* createUbermaterial(filament::Engine* engine) { - /* TODO: ubermaterial not yet implemented */ - return nullptr; - } + static MaterialProvider* createUbershaderLoader(filament::Engine* engine); virtual ~MaterialProvider() {} @@ -111,6 +90,12 @@ public: virtual void destroyMaterials() = 0; }; +namespace details { + void constrainMaterial(MaterialKey* key, UvMap* uvmap); + void processShaderString(std::string* shader, const UvMap& uvmap, + const MaterialKey& config); +} + } // namespace gltfio #endif // GLTFIO_MATERIALPROVIDER_H diff --git a/libs/gltfio/materials/ubershader.mat.in b/libs/gltfio/materials/ubershader.mat.in index 0b245a25c3..c099086289 100644 --- a/libs/gltfio/materials/ubershader.mat.in +++ b/libs/gltfio/materials/ubershader.mat.in @@ -3,6 +3,9 @@ material { requires : [ uv0, uv1, color ], shadingModel : ${SHADINGMODEL}, blending : ${BLENDING}, + depthWrite : true, + doubleSided : true, + flipUV : false, parameters : [ // Base Color { type : int, name : baseColorIndex }, diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index 0fe6a80ec4..ef1f114c5d 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -98,14 +98,23 @@ static uint32_t computeBindingOffset(const cgltf_accessor* accessor) { return uint32_t(accessor->offset + accessor->buffer_view->offset); }; +static MaterialProvider* createMaterialProvider(const AssetConfiguration& config) { + if (config.materials == GENERATE_SHADERS) { + return MaterialProvider::createMaterialGenerator(config.engine); + } else { + return MaterialProvider::createUbershaderLoader(config.engine); + } +} + struct FAssetLoader : public AssetLoader { - FAssetLoader(Engine* engine, NameComponentManager* names) : + FAssetLoader(const AssetConfiguration& config) : mEntityManager(EntityManager::get()), - mRenderableManager(engine->getRenderableManager()), - mNameManager(names), - mTransformManager(engine->getTransformManager()), - mMaterials(MaterialProvider::createMaterialGenerator(engine)), - mEngine(engine) {} + mRenderableManager(config.engine->getRenderableManager()), + mNameManager(config.names), + mTransformManager(config.engine->getTransformManager()), + mMaterials(createMaterialProvider(config)), + mEngine(config.engine), + mMaterialSource(config.materials) {} FFilamentAsset* createAssetFromJson(const uint8_t* bytes, uint32_t nbytes); FilamentAsset* createAssetFromBinary(const uint8_t* bytes, uint32_t nbytes); @@ -147,6 +156,7 @@ struct FAssetLoader : public AssetLoader { TransformManager& mTransformManager; MaterialProvider* mMaterials; Engine* mEngine; + MaterialSource mMaterialSource; // The loader owns a few transient mappings used only for the current asset being loaded. FFilamentAsset* mResult; @@ -406,14 +416,12 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out } mResult->mIndexBuffers.push_back(indices); - // We do not necessarily upload all glTF attribute buffers to the GPU. For example, we do not - // upload tangent vectors in their source format or more than two UV sets. However the buffer - // count that gets passed to the Builder should be equal to the glTF attribute count because we - // do not remap the slots. VertexBuffer::Builder vbb; - vbb.bufferCount(inPrim->attributes_count); - for (int slot = 0; slot < inPrim->attributes_count; slot++) { + int slot = 0; + bool hasUv0 = false, hasUv1 = false, hasVertexColor = false; + uint32_t vertexCount = 0; + for (; slot < inPrim->attributes_count; slot++) { const cgltf_attribute& inputAttribute = inPrim->attributes[slot]; const cgltf_accessor* inputAccessor = inputAttribute.data; @@ -430,6 +438,10 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out continue; } + if (inputAttribute.type == cgltf_attribute_type_color) { + hasVertexColor = true; + } + // Translate the cgltf attribute enum into a Filament enum and ignore all uv sets // that do not have entries in the mapping table. VertexAttribute semantic; @@ -442,9 +454,11 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out switch (uvset) { case UV0: semantic = VertexAttribute::UV0; + hasUv0 = true; break; case UV1: semantic = VertexAttribute::UV1; + hasUv1 = true; break; case UNUSED: // It is perfectly acceptable to drop unused texture coordinate sets. In fact @@ -453,8 +467,7 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out } } - // This will needlessly set the same vertex count multiple times, which should be fine. - vbb.vertexCount(inputAccessor->count); + vertexCount = inputAccessor->count; // The positions accessor is required to have min/max properties, use them to expand // the bounding box for this primitive. @@ -486,6 +499,34 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out } } + vbb.vertexCount(vertexCount); + + // If an ubershader is used, then we provide a single dummy buffer for all unfulfilled vertex + // requirements. The color data should be a sequence of normalized UBYTE4, so dummy UVs are + // USHORT2 to make the sizes match. + int dummySlot = -1; + if (mMaterialSource == LOAD_UBERSHADERS) { + bool needsDummyData = false; + if (!hasUv0) { + needsDummyData = true; + vbb.attribute(VertexAttribute::UV0, slot, VertexBuffer::AttributeType::USHORT2); + } + if (!hasUv1) { + needsDummyData = true; + vbb.attribute(VertexAttribute::UV1, slot, VertexBuffer::AttributeType::USHORT2); + } + if (!hasVertexColor) { + needsDummyData = true; + vbb.attribute(VertexAttribute::COLOR, slot, VertexBuffer::AttributeType::UBYTE4); + vbb.normalized(VertexAttribute::COLOR); + } + if (needsDummyData) { + dummySlot = slot++; + } + } + + vbb.bufferCount(slot); + VertexBuffer* vertices = mResult->mPrimMap[inPrim] = vbb.build(*mEngine); mResult->mVertexBuffers.push_back(vertices); @@ -501,7 +542,7 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out uvmap[inputAttribute.index] == UNUSED) { continue; } - mResult->mBufferBindings.emplace_back(BufferBinding { + mResult->mBufferBindings.push_back({ .uri = bv->buffer->uri, .totalSize = uint32_t(bv->buffer->size), .bufferIndex = uint8_t(slot), @@ -511,7 +552,24 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out .vertexBuffer = vertices, .indexBuffer = nullptr, .convertBytesToShorts = false, - .generateTrivialIndices = false + .generateTrivialIndices = false, + .generateDummyData = false + }); + } + + if (dummySlot > 0) { + mResult->mBufferBindings.push_back({ + .uri = "", + .totalSize = uint32_t(sizeof(ubyte4) * vertexCount), + .bufferIndex = uint8_t(dummySlot), + .offset = 0, + .size = uint32_t(sizeof(ubyte4) * vertexCount), + .data = nullptr, + .vertexBuffer = vertices, + .indexBuffer = nullptr, + .convertBytesToShorts = false, + .generateTrivialIndices = false, + .generateDummyData = true }); } @@ -711,8 +769,8 @@ bool FAssetLoader::primitiveHasVertexColor(const cgltf_primitive* inPrim) const return false; } -AssetLoader* AssetLoader::create(Engine* engine, NameComponentManager* names) { - return new FAssetLoader(engine, names); +AssetLoader* AssetLoader::create(const AssetConfiguration& config) { + return new FAssetLoader(config); } void AssetLoader::destroy(AssetLoader** loader) { diff --git a/libs/gltfio/src/MaterialGenerator.cpp b/libs/gltfio/src/MaterialGenerator.cpp index 87d04e3b62..c111763f91 100644 --- a/libs/gltfio/src/MaterialGenerator.cpp +++ b/libs/gltfio/src/MaterialGenerator.cpp @@ -74,11 +74,11 @@ void MaterialGenerator::destroyMaterials() { mCache.clear(); } -static std::string shaderFromKey(const MaterialKey& config, const UvMap& uvmap) { +static std::string shaderFromKey(const MaterialKey& config) { std::string shader = "void material(inout MaterialInputs material) {\n"; if (config.hasNormalTexture && !config.unlit) { - shader += "float2 normalUV = getUV${normal}();\n"; + shader += "float2 normalUV = ${normal};\n"; if (config.hasTextureTransforms) { shader += "normalUV = (vec3(normalUV, 1.0) * materialParams.normalUvMatrix).xy;\n"; } @@ -95,7 +95,7 @@ static std::string shaderFromKey(const MaterialKey& config, const UvMap& uvmap) )SHADER"; if (config.hasBaseColorTexture) { - shader += "float2 baseColorUV = getUV${color}();\n"; + shader += "float2 baseColorUV = ${color};\n"; if (config.hasTextureTransforms) { shader += "baseColorUV = (vec3(baseColorUV, 1.0) * " "materialParams.baseColorUvMatrix).xy;\n"; @@ -122,7 +122,7 @@ static std::string shaderFromKey(const MaterialKey& config, const UvMap& uvmap) material.emissive.rgb = materialParams.emissiveFactor.rgb; )SHADER"; if (config.hasMetallicRoughnessTexture) { - shader += "float2 metallicRoughnessUV = getUV${metallic}();\n"; + shader += "float2 metallicRoughnessUV = ${metallic};\n"; if (config.hasTextureTransforms) { shader += "metallicRoughnessUV = (vec3(metallicRoughnessUV, 1.0) * " "materialParams.metallicRoughnessUvMatrix).xy;\n"; @@ -134,7 +134,7 @@ static std::string shaderFromKey(const MaterialKey& config, const UvMap& uvmap) )SHADER"; } if (config.hasOcclusionTexture) { - shader += "float2 aoUV = getUV${ao}();\n"; + shader += "float2 aoUV = ${ao};\n"; if (config.hasTextureTransforms) { shader += "aoUV = (vec3(aoUV, 1.0) * materialParams.occlusionUvMatrix).xy;\n"; } @@ -144,7 +144,7 @@ static std::string shaderFromKey(const MaterialKey& config, const UvMap& uvmap) )SHADER"; } if (config.hasEmissiveTexture) { - shader += "float2 emissiveUV = getUV${emissive}();\n"; + shader += "float2 emissiveUV = ${emissive};\n"; if (config.hasTextureTransforms) { shader += "emissiveUV = (vec3(emissiveUV, 1.0) * " "materialParams.emissiveUvMatrix).xy;\n"; @@ -157,71 +157,14 @@ static std::string shaderFromKey(const MaterialKey& config, const UvMap& uvmap) } shader += "}\n"; - - auto replaceAll = [&shader](const std::string& from, const std::string& to) { - size_t pos = shader.find(from); - for (; pos != std::string::npos; pos = shader.find(from, pos)) { - shader.replace(pos, from.length(), to); - } - }; - - const auto normalUV = std::to_string(uvmap[config.normalUV] - 1); - const auto baseColorUV = std::to_string(uvmap[config.baseColorUV] - 1); - const auto metallicRoughnessUV = std::to_string(uvmap[config.metallicRoughnessUV] - 1); - const auto emissiveUV = std::to_string(uvmap[config.emissiveUV] - 1); - const auto aoUV = std::to_string(uvmap[config.aoUV] - 1); - - replaceAll("${normal}", normalUV); - replaceAll("${color}", baseColorUV); - replaceAll("${metallic}", metallicRoughnessUV); - replaceAll("${ao}", aoUV); - replaceAll("${emissive}", emissiveUV); - return shader; } -// Filament supports up to 2 UV sets. glTF has arbitrary texcoord set indices, but it allows -// implementations to support only 2 simultaneous sets. Here we build a mapping table with 1-based -// indices where 0 means unused. Note that the order in which we drop textures can affect the look -// of certain assets. This "order of degradation" is stipulated by the glTF 2.0 specification. -static void constrainMaterial(MaterialKey* key, UvMap* uvmap) { - const int MAX_INDEX = 2; - UvMap retval {}; - int index = 1; - if (key->hasBaseColorTexture) { - retval[key->baseColorUV] = (UvSet) index++; - } - if (key->hasMetallicRoughnessTexture && retval[key->metallicRoughnessUV] == UNUSED) { - retval[key->metallicRoughnessUV] = (UvSet) index++; - } - if (key->hasNormalTexture && retval[key->normalUV] == UNUSED) { - if (index > MAX_INDEX) { - key->hasNormalTexture = false; - } else { - retval[key->normalUV] = (UvSet) index++; - } - } - if (key->hasOcclusionTexture && retval[key->aoUV] == UNUSED) { - if (index > MAX_INDEX) { - key->hasOcclusionTexture = false; - } else { - retval[key->aoUV] = (UvSet) index++; - } - } - if (key->hasEmissiveTexture && retval[key->emissiveUV] == UNUSED) { - if (index > MAX_INDEX) { - key->hasEmissiveTexture = false; - } else { - retval[key->emissiveUV] = (UvSet) index++; - } - } - *uvmap = retval; -} - static Material* createMaterial(Engine* engine, const MaterialKey& config, const UvMap& uvmap, const char* name) { using CullingMode = MaterialBuilder::CullingMode; - std::string shader = shaderFromKey(config, uvmap); + std::string shader = shaderFromKey(config); + gltfio::details::processShaderString(&shader, uvmap, config); MaterialBuilder builder = MaterialBuilder() .name(name) .flipUV(false) @@ -312,7 +255,7 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const MaterialInstance* MaterialGenerator::createMaterialInstance(MaterialKey* config, UvMap* uvmap, const char* label) { - constrainMaterial(config, uvmap); + gltfio::details::constrainMaterial(config, uvmap); auto iter = mCache.find(*config); if (iter == mCache.end()) { Material* mat = createMaterial(mEngine, *config, *uvmap, label); diff --git a/libs/gltfio/src/MaterialProvider.cpp b/libs/gltfio/src/MaterialProvider.cpp new file mode 100644 index 0000000000..5332eaed34 --- /dev/null +++ b/libs/gltfio/src/MaterialProvider.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2019 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. + */ + +#include + +using namespace gltfio; + +bool gltfio::operator==(const MaterialKey& k1, const MaterialKey& k2) { + return + (k1.doubleSided == k2.doubleSided) && + (k1.unlit == k2.unlit) && + (k1.hasVertexColors == k2.hasVertexColors) && + (k1.hasBaseColorTexture == k2.hasBaseColorTexture) && + (k1.hasMetallicRoughnessTexture == k2.hasMetallicRoughnessTexture) && + (k1.hasNormalTexture == k2.hasNormalTexture) && + (k1.hasOcclusionTexture == k2.hasOcclusionTexture) && + (k1.hasEmissiveTexture == k2.hasEmissiveTexture) && + (k1.alphaMode == k2.alphaMode) && + (k1.baseColorUV == k2.baseColorUV) && + (k1.metallicRoughnessUV == k2.metallicRoughnessUV) && + (k1.emissiveUV == k2.emissiveUV) && + (k1.aoUV == k2.aoUV) && + (k1.normalUV == k2.normalUV) && + (k1.alphaMaskThreshold == k2.alphaMaskThreshold); +} + +// Filament supports up to 2 UV sets. glTF has arbitrary texcoord set indices, but it allows +// implementations to support only 2 simultaneous sets. Here we build a mapping table with 1-based +// indices where 0 means unused. Note that the order in which we drop textures can affect the look +// of certain assets. This "order of degradation" is stipulated by the glTF 2.0 specification. +void details::constrainMaterial(MaterialKey* key, UvMap* uvmap) { + const int MAX_INDEX = 2; + UvMap retval {}; + int index = 1; + if (key->hasBaseColorTexture) { + retval[key->baseColorUV] = (UvSet) index++; + } + if (key->hasMetallicRoughnessTexture && retval[key->metallicRoughnessUV] == UNUSED) { + retval[key->metallicRoughnessUV] = (UvSet) index++; + } + if (key->hasNormalTexture && retval[key->normalUV] == UNUSED) { + if (index > MAX_INDEX) { + key->hasNormalTexture = false; + } else { + retval[key->normalUV] = (UvSet) index++; + } + } + if (key->hasOcclusionTexture && retval[key->aoUV] == UNUSED) { + if (index > MAX_INDEX) { + key->hasOcclusionTexture = false; + } else { + retval[key->aoUV] = (UvSet) index++; + } + } + if (key->hasEmissiveTexture && retval[key->emissiveUV] == UNUSED) { + if (index > MAX_INDEX) { + key->hasEmissiveTexture = false; + } else { + retval[key->emissiveUV] = (UvSet) index++; + } + } + *uvmap = retval; +} + +void details::processShaderString(std::string* shader, const UvMap& uvmap, + const MaterialKey& config) { + auto replaceAll = [shader](const std::string& from, const std::string& to) { + size_t pos = shader->find(from); + for (; pos != std::string::npos; pos = shader->find(from, pos)) { + shader->replace(pos, from.length(), to); + } + }; + static const std::string uvstrings[] = { "vec2(0)", "getUV0()", "getUV1()" }; + const auto& normalUV = uvstrings[uvmap[config.normalUV]]; + const auto& baseColorUV = uvstrings[uvmap[config.baseColorUV]]; + const auto& metallicRoughnessUV = uvstrings[uvmap[config.metallicRoughnessUV]]; + const auto& emissiveUV = uvstrings[uvmap[config.emissiveUV]]; + const auto& aoUV = uvstrings[uvmap[config.aoUV]]; + replaceAll("${normal}", normalUV); + replaceAll("${color}", baseColorUV); + replaceAll("${metallic}", metallicRoughnessUV); + replaceAll("${ao}", aoUV); + replaceAll("${emissive}", emissiveUV); +} diff --git a/libs/gltfio/src/ResourceLoader.cpp b/libs/gltfio/src/ResourceLoader.cpp index 7cecaf0eba..5371f76bef 100644 --- a/libs/gltfio/src/ResourceLoader.cpp +++ b/libs/gltfio/src/ResourceLoader.cpp @@ -149,11 +149,17 @@ bool ResourceLoader::loadResources(FilamentAsset* asset) { const BufferBinding* bindings = asset->getBufferBindings(); for (size_t i = 0, n = asset->getBufferBindingCount(); i < n; ++i) { auto bb = bindings[i]; - const uint8_t* data8 = bb.offset + (const uint8_t*) *bb.data; - if (bb.vertexBuffer) { + if (bb.vertexBuffer && !bb.generateDummyData) { + const uint8_t* data8 = bb.offset + (const uint8_t*) *bb.data; mPool->addPendingUpload(); VertexBuffer::BufferDescriptor bd(data8, bb.size, AssetPool::onLoadedResource, mPool); bb.vertexBuffer->setBufferAt(*mConfig.engine, bb.bufferIndex, std::move(bd)); + } else if (bb.vertexBuffer) { + uint32_t* dummyData = (uint32_t*) malloc(bb.size); + memset(dummyData, 0xff, bb.size); + auto callback = (VertexBuffer::BufferDescriptor::Callback) free; + VertexBuffer::BufferDescriptor bd(dummyData, bb.size, callback); + bb.vertexBuffer->setBufferAt(*mConfig.engine, bb.bufferIndex, std::move(bd)); } else if (bb.generateTrivialIndices) { uint32_t* data32 = (uint32_t*) malloc(bb.size); generateTrivialIndices(data32, bb.size / sizeof(uint32_t)); @@ -161,6 +167,7 @@ bool ResourceLoader::loadResources(FilamentAsset* asset) { IndexBuffer::BufferDescriptor bd(data32, bb.size, callback); bb.indexBuffer->setBuffer(*mConfig.engine, std::move(bd)); } else if (bb.convertBytesToShorts) { + const uint8_t* data8 = bb.offset + (const uint8_t*) *bb.data; size_t size16 = bb.size * 2; uint16_t* data16 = (uint16_t*) malloc(size16); convertBytesToShorts(data16, data8, bb.size); @@ -168,6 +175,7 @@ bool ResourceLoader::loadResources(FilamentAsset* asset) { IndexBuffer::BufferDescriptor bd(data16, size16, callback); bb.indexBuffer->setBuffer(*mConfig.engine, std::move(bd)); } else if (bb.indexBuffer) { + const uint8_t* data8 = bb.offset + (const uint8_t*) *bb.data; mPool->addPendingUpload(); IndexBuffer::BufferDescriptor bd(data8, bb.size, AssetPool::onLoadedResource, mPool); bb.indexBuffer->setBuffer(*mConfig.engine, std::move(bd)); diff --git a/libs/gltfio/src/UbershaderLoader.cpp b/libs/gltfio/src/UbershaderLoader.cpp new file mode 100644 index 0000000000..0eec9895a5 --- /dev/null +++ b/libs/gltfio/src/UbershaderLoader.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2019 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. + */ + +#include + +#include + +#include + +#include "gltfresources.h" + +using namespace filament; +using namespace filament::math; +using namespace gltfio; +using namespace utils; + +namespace { + +class UbershaderLoader : public MaterialProvider { +public: + UbershaderLoader(filament::Engine* engine); + ~UbershaderLoader() {} + + filament::MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap, + const char* label) override; + + size_t getMaterialsCount() const noexcept override; + const filament::Material* const* getMaterials() const noexcept override; + void destroyMaterials() override; + + Material* getMaterial(const MaterialKey& config) const; + + Material* mMaterials[6]; + Material* mLitOpaqueMaterial; + Material* mLitMaskedMaterial; + Material* mLitTransparentMaterial; + Material* mUnlitOpaqueMaterial; + Material* mUnlitMaskedMaterial; + Material* mUnlitTransparentMaterial; + + filament::Engine* mEngine; +}; + +UbershaderLoader::UbershaderLoader(Engine* engine) : mEngine(engine) { + int i = 0; + mMaterials[i++] = mLitOpaqueMaterial = Material::Builder() + .package(GLTFRESOURCES_LIT_OPAQUE_DATA, GLTFRESOURCES_LIT_OPAQUE_SIZE).build(*engine); + mMaterials[i++] = mLitMaskedMaterial = Material::Builder() + .package(GLTFRESOURCES_LIT_MASKED_DATA, GLTFRESOURCES_LIT_MASKED_SIZE).build(*engine); + mMaterials[i++] = mLitTransparentMaterial = Material::Builder() + .package(GLTFRESOURCES_LIT_TRANSPARENT_DATA, GLTFRESOURCES_LIT_TRANSPARENT_SIZE) + .build(*engine); + mMaterials[i++] = mUnlitOpaqueMaterial = Material::Builder() + .package(GLTFRESOURCES_UNLIT_OPAQUE_DATA, GLTFRESOURCES_UNLIT_OPAQUE_SIZE).build(*engine); + mMaterials[i++] = mUnlitMaskedMaterial = Material::Builder() + .package(GLTFRESOURCES_UNLIT_MASKED_DATA, GLTFRESOURCES_UNLIT_MASKED_SIZE).build(*engine); + mMaterials[i++] = mUnlitTransparentMaterial = Material::Builder() + .package(GLTFRESOURCES_UNLIT_TRANSPARENT_DATA, GLTFRESOURCES_UNLIT_TRANSPARENT_SIZE) + .build(*engine); +} + +size_t UbershaderLoader::getMaterialsCount() const noexcept { + return sizeof(mMaterials) / sizeof(mMaterials[0]); +} + +const Material* const* UbershaderLoader::getMaterials() const noexcept { + return &mMaterials[0]; +} + +void UbershaderLoader::destroyMaterials() { + for (auto& material : mMaterials) { + mEngine->destroy(material); + material = nullptr; + } +} + +Material* UbershaderLoader::getMaterial(const MaterialKey& config) const { + switch(config.alphaMode) { + case AlphaMode::OPAQUE: + return config.unlit ? mUnlitOpaqueMaterial : mLitOpaqueMaterial; + case AlphaMode::MASK: + return config.unlit ? mUnlitMaskedMaterial : mLitMaskedMaterial; + case AlphaMode::BLEND: + return config.unlit ? mUnlitTransparentMaterial : mLitTransparentMaterial; + } +} + +MaterialInstance* UbershaderLoader::createMaterialInstance(MaterialKey* config, UvMap* uvmap, + const char* label) { + gltfio::details::constrainMaterial(config, uvmap); + auto getUvIndex = [uvmap](uint8_t srcIndex, bool hasTexture) -> int { + return hasTexture ? int(uvmap->at(srcIndex)) - 1 : -1; + }; + Material* material = getMaterial(*config); + MaterialInstance* mi = material->createInstance(); + mi->setParameter("baseColorIndex", + getUvIndex(config->baseColorUV, config->hasBaseColorTexture)); + mi->setParameter("normalIndex", getUvIndex(config->normalUV, config->hasNormalTexture)); + mi->setParameter("metallicRoughnessIndex", + getUvIndex(config->metallicRoughnessUV, config->hasMetallicRoughnessTexture)); + mi->setParameter("aoIndex", getUvIndex(config->aoUV, config->hasOcclusionTexture)); + mi->setParameter("emissiveIndex", getUvIndex(config->emissiveUV, config->hasEmissiveTexture)); + + if (config->alphaMode == AlphaMode::MASK) { + mi->setParameter("maskThreshold", config->alphaMaskThreshold); + } + + // TODO: honor config->doubleSided + + mat3f identity; + mi->setParameter("baseColorUvMatrix", identity); + mi->setParameter("metallicRoughnessUvMatrix", identity); + mi->setParameter("normalUvMatrix", identity); + mi->setParameter("occlusionUvMatrix", identity); + mi->setParameter("emissiveUvMatrix", identity); + return mi; +} + +} // anonymous namespace + +namespace gltfio { + +MaterialProvider* MaterialProvider::createUbershaderLoader(filament::Engine* engine) { + return new UbershaderLoader(engine); +} + +} // namespace gltfio