gltfio: add UbershaderLoader, an implemention of MaterialProvider.

This commit is contained in:
Philip Rideout
2019-03-12 15:29:17 -07:00
parent 3b827da834
commit 0102489748
10 changed files with 367 additions and 117 deletions
+2
View File
@@ -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
+20 -7
View File
@@ -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<uint8_t> 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.
@@ -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. */
+9 -24
View File
@@ -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<UvSet, 8>;
* - 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
+3
View File
@@ -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 },
+76 -18
View File
@@ -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) {
+9 -66
View File
@@ -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);
+97
View File
@@ -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 <gltfio/MaterialProvider.h>
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);
}
+10 -2
View File
@@ -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));
+140
View File
@@ -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 <gltfio/MaterialProvider.h>
#include <filament/MaterialInstance.h>
#include <math/mat4.h>
#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