fix normal morph target

This commit is contained in:
Nick Fisher
2021-11-23 15:51:50 +08:00
parent bb13d82114
commit 7ae6d85878
34 changed files with 1445 additions and 511 deletions

View File

@@ -63,6 +63,9 @@ using namespace gltfio;
using namespace utils;
using namespace std::chrono;
namespace foo {
MaterialProvider* createUbershaderLoader(filament::Engine* engine);
}
namespace filament {
class IndirectLight;
@@ -70,7 +73,12 @@ namespace filament {
}
namespace gltfio {
MaterialProvider* createGPUMorphShaderLoader(const void* data, uint64_t size, Engine* engine);
MaterialProvider* createGPUMorphShaderLoader(
const void* opaqueData,
uint64_t opaqueDataSize,
const void* fadeData,
uint64_t fadeDataSize,
Engine* engine);
void decomposeMatrix(const filament::math::mat4f& mat, filament::math::float3* translation,
filament::math::quatf* rotation, filament::math::float3* scale);
}
@@ -113,12 +121,14 @@ filament::math::mat4f composeMatrix(const filament::math::float3& translation,
FilamentViewer::FilamentViewer(
void* layer,
const char* shaderPath,
const char* opaqueShaderPath,
const char* fadeShaderPath,
LoadResource loadResource,
FreeResource freeResource) : _layer(layer),
_loadResource(loadResource),
_freeResource(freeResource),
materialProviderResources(nullptr, 0) {
opaqueShaderResources(nullptr, 0),
fadeShaderResources(nullptr, 0) {
_engine = Engine::create(Engine::Backend::OPENGL);
_renderer = _engine->createRenderer();
@@ -134,13 +144,19 @@ FilamentViewer::FilamentViewer(
_swapChain = _engine->createSwapChain(_layer);
if(shaderPath) {
materialProviderResources = _loadResource(shaderPath);
_materialProvider = createGPUMorphShaderLoader(materialProviderResources.data, materialProviderResources.size, _engine);
// if(shaderPath) {
opaqueShaderResources = _loadResource(opaqueShaderPath);
fadeShaderResources = _loadResource(fadeShaderPath);
_materialProvider = createGPUMorphShaderLoader(
opaqueShaderResources.data,
opaqueShaderResources.size,
fadeShaderResources.data,
fadeShaderResources.size,
_engine);
// _freeResource((void*)rb.data, rb.size, nullptr); <- TODO this is being freed too early, need to pass to callback?
} else {
_materialProvider = createUbershaderLoader(_engine);
}
// } else {
// _materialProvider = foo::createUbershaderLoader(_engine);
// }
EntityManager& em = EntityManager::get();
_ncm = new NameComponentManager(em);
_assetLoader = AssetLoader::create({_engine, _materialProvider, _ncm, &em});
@@ -191,7 +207,8 @@ void FilamentViewer::loadResources(string relativeResourcePath) {
void FilamentViewer::releaseSourceAssets() {
std::cout << "Releasing source data" << std::endl;
_asset->releaseSourceData();
_freeResource((void*)materialProviderResources.data, materialProviderResources.size, nullptr);
_freeResource((void*)opaqueShaderResources.data, opaqueShaderResources.size, nullptr);
_freeResource((void*)fadeShaderResources.data, fadeShaderResources.size, nullptr);
}
@@ -200,6 +217,44 @@ void FilamentViewer::animateWeights(float* data, int numWeights, int length, flo
morphAnimationBuffer = std::make_unique<MorphAnimationBuffer>(data, numWeights, length / numWeights, 1000 / frameRate );
}
void FilamentViewer::loadGlb(const char* const uri) {
if(_asset) {
_resourceLoader->evictResourceData();
_scene->removeEntities(_asset->getEntities(), _asset->getEntityCount());
_assetLoader->destroyAsset(_asset);
}
_asset = nullptr;
_animator = nullptr;
ResourceBuffer rbuf = _loadResource(uri);
// Parse the glTF file and create Filament entities.
_asset = _assetLoader->createAssetFromJson((uint8_t*)rbuf.data, rbuf.size);
if (!_asset) {
std::cerr << "Unable to parse asset" << std::endl;
exit(1);
}
_resourceLoader->loadResources(_asset);
const Entity* entities = _asset->getEntities();
RenderableManager& rm = _engine->getRenderableManager();
for(int i =0; i< _asset->getEntityCount(); i++) {
Entity e = entities[i];
auto inst = rm.getInstance(e);
rm.setCulling(inst, false);
}
_animator = _asset->getAnimator();
_scene->addEntities(_asset->getEntities(), _asset->getEntityCount());
_freeResource((void*)rbuf.data, rbuf.size, nullptr);
transformToUnitCube();
}
void FilamentViewer::loadGltf(const char* const uri, const char* const relativeResourcePath) {
if(_asset) {
_resourceLoader->evictResourceData();
@@ -280,16 +335,23 @@ void FilamentViewer::playAnimation(int index) {
}
void FilamentViewer::loadSkybox(const char* const skyboxPath, const char* const iblPath) {
std::cout << "Loading skybox from " << skyboxPath << std::endl;
ResourceBuffer skyboxBuffer = _loadResource(skyboxPath);
std::cout << "Loaded skybox resource buffer of size " << skyboxBuffer.size << std::endl;
image::KtxBundle* skyboxBundle =
new image::KtxBundle(static_cast<const uint8_t*>(skyboxBuffer.data),
static_cast<uint32_t>(skyboxBuffer.size));
_skyboxTexture = image::ktx::createTexture(_engine, skyboxBundle, false);
_skyboxTexture = image::ktx::createTexture(_engine, skyboxBundle, true);
_skybox = filament::Skybox::Builder().environment(_skyboxTexture).build(*_engine);
_scene->setSkybox(_skybox);
_freeResource((void*)skyboxBuffer.data, skyboxBuffer.size, nullptr);
std::cout << "Loading IBL from " << iblPath << std::endl;
// Load IBL.
ResourceBuffer iblBuffer = _loadResource(iblPath);

View File

@@ -94,8 +94,9 @@ namespace holovox {
class FilamentViewer {
public:
FilamentViewer(void* layer, const char* shaderPath, LoadResource loadResource, FreeResource freeResource);
FilamentViewer(void* layer, const char* opaqueShaderPath, const char* fadeShaderPath, LoadResource loadResource, FreeResource freeResource);
~FilamentViewer();
void loadGlb(const char* const uri);
void loadGltf(const char* const uri, const char* relativeResourcePath);
void loadSkybox(const char* const skyboxUri, const char* const iblUri);
void updateViewportAndCameraProjection(int height, int width, float scaleFactor);
@@ -127,7 +128,8 @@ namespace holovox {
LoadResource _loadResource;
FreeResource _freeResource;
ResourceBuffer materialProviderResources;
ResourceBuffer opaqueShaderResources;
ResourceBuffer fadeShaderResources;
Scene* _scene;
View* _view;

View File

@@ -35,6 +35,16 @@ using namespace filament;
using namespace filamat;
using namespace filament::math;
using namespace utils;
#include "upcast.h"
#include <backend/Handle.h>
#include <filament/Texture.h>
#include <utils/compiler.h>
namespace gltfio {
static constexpr uint8_t kUnused = 0xff;
@@ -62,6 +72,7 @@ namespace gltfio {
targetMesh = mesh;
for(int i = 0; i < numPrimitives; i++) {
int primitiveIndex = primitiveIndices[i];
//for(int primitiveIndex = 0; primitiveIndex < targetMesh->primitives_count; primitiveIndex++) {
std::cout << "Adding primitive at index " << primitiveIndex << " to morpher " << std::endl;
addPrimitive(mesh, primitiveIndex);
}
@@ -106,7 +117,6 @@ namespace gltfio {
auto textureSize = textureWidth * 3 * sizeof(float) * prim->numTargets;
auto textureBuffer = (float *const) malloc(textureSize);
if(!textureBuffer) {
std::cout << "Error allocating texture buffer" << std::endl;
exit(-1);
@@ -117,7 +127,7 @@ namespace gltfio {
uint32_t offset = 0;
// assume the primitive morph target source buffer is laid out like:
// |target0_v0_pos * 3|target0_v0_norm * 3|target0_v1_pos * 3|target0_v1_norm * 3|...|target1_v0_pos * 3|target1_v0_norm * 3|target1_v1_pos * 3|target1_v1_norm * 3|...
// |target0_v0_pos * 3|target0_v1_pos * 3|...|target0_v0_norm * 3|target0_v1_norm * 3|...|target1_v0_pos * 3|target1_v1_pos * 3|...|target1_v0_norm * 3|target1_v1_norm * 3|...
// where:
// - target0/target1/etc is the first/second/etc morph target
// - v0/v1/etc is the first/second/etc vertex
@@ -126,7 +136,7 @@ namespace gltfio {
if(target.type == cgltf_attribute_type_position
|| (numAttributes > 1 && target.type == cgltf_attribute_type_normal)
) {
float attr = (float)textureBuffer[offset];
memcpy(textureBuffer+offset, target.bufferObject, target.bufferSize);
offset += int(target.bufferSize / sizeof(float));
}
@@ -137,7 +147,7 @@ namespace gltfio {
.height(1)
.depth(prim->numTargets)
.sampler(Texture::Sampler::SAMPLER_2D_ARRAY)
.format(Texture::InternalFormat::RGB32F)
.format(backend::TextureFormat::RGB32F)
.levels(0x01)
.build(engine);
@@ -146,8 +156,8 @@ namespace gltfio {
Texture::PixelBufferDescriptor descriptor(
textureBuffer,
textureSize,
Texture::Format::RGB,
Texture::Type::FLOAT,
backend::PixelDataFormat::RGB,
backend::PixelDataType::FLOAT,
FREE_CALLBACK,
nullptr);
prim->texture->setImage(engine, 0, 0,0, 0, textureWidth, 1, prim->numTargets, std::move(descriptor));
@@ -155,7 +165,8 @@ namespace gltfio {
for(int i = 0; i < mAsset->getMaterialInstanceCount(); i++) {
const char* name = materialInstances[i]->getName();
if(strcmp(name, prim->materialName) == 0) {
std::cout << "Found material instance for primitive under name : " << name << std::endl;
const char* m = materialInstances[i]->getMaterial()->getName();
std::cout << "Found material instance for material " << m << " and primitive under name : " << name << std::endl;
prim->materialInstance = materialInstances[i]; //std::unique_ptr<MaterialInstance>(materialInstances[i]);
break;
}
@@ -165,7 +176,7 @@ namespace gltfio {
exit(-1);
}
float dimensions[] = { (float(prim->numVertices) * float(numAttributes)), float(numAttributes), float(prim->numTargets) };
float dimensions[] = { float(prim->numVertices), float(numAttributes), float(prim->numTargets) };
prim->materialInstance->setParameter("dimensions", dimensions, 3);
// TextureSampler sampler(filament::backend::SamplerMagFilter::NEAREST, filament::TextureSampler::WrapMode::REPEAT);
@@ -214,12 +225,7 @@ namespace gltfio {
if (atype == cgltf_attribute_type_tangent) {
continue;
}
if (
atype == cgltf_attribute_type_position || (numAttributes > 1 && atype == cgltf_attribute_type_normal)
) {
// All position & normal attributes must have the same data type.
if (atype == cgltf_attribute_type_position || numAttributes > 1 && atype == cgltf_attribute_type_normal) {
assert_invariant(
!previous || previous->component_type == accessor->component_type);
assert_invariant(!previous || previous->type == accessor->type);
@@ -234,9 +240,122 @@ namespace gltfio {
animatedPrimitive->targets.push_back({data, size, targetIndex, atype});
}
}
}
}
}
animatablePrimitives.push_back(std::move(animatedPrimitive));
}
}
// assert(
// FTexture::validatePixelFormatAndType(
// backend::TextureFormat::RGB32F,
// backend::PixelDataFormat::RGB,
// backend::PixelDataType::FLOAT));
// namespace filament {
// class FEngine;
// class FStream;
// class FTexture : public Texture {
// public:
// FTexture(FEngine& engine, const Builder& builder);
// // frees driver resources, object becomes invalid
// void terminate(FEngine& engine);
// backend::Handle<backend::HwTexture> getHwHandle() const noexcept { return mHandle; }
// size_t getWidth(size_t level = 0) const noexcept;
// size_t getHeight(size_t level = 0) const noexcept;
// size_t getDepth(size_t level = 0) const noexcept;
// size_t getLevelCount() const noexcept { return mLevelCount; }
// size_t getMaxLevelCount() const noexcept { return FTexture::maxLevelCount(mWidth, mHeight); }
// Sampler getTarget() const noexcept { return mTarget; }
// InternalFormat getFormat() const noexcept { return mFormat; }
// Usage getUsage() const noexcept { return mUsage; }
// void setImage(FEngine& engine, size_t level,
// uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
// PixelBufferDescriptor&& buffer) const;
// void setImage(FEngine& engine, size_t level,
// uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
// uint32_t width, uint32_t height, uint32_t depth,
// PixelBufferDescriptor&& buffer) const;
// void setImage(FEngine& engine, size_t level,
// PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const;
// void generatePrefilterMipmap(FEngine& engine,
// PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,
// PrefilterOptions const* options);
// void setExternalImage(FEngine& engine, void* image) noexcept;
// void setExternalImage(FEngine& engine, void* image, size_t plane) noexcept;
// void setExternalStream(FEngine& engine, FStream* stream) noexcept;
// void generateMipmaps(FEngine& engine) const noexcept;
// void setSampleCount(size_t sampleCount) noexcept { mSampleCount = uint8_t(sampleCount); }
// size_t getSampleCount() const noexcept { return mSampleCount; }
// bool isMultisample() const noexcept { return mSampleCount > 1; }
// bool isCompressed() const noexcept { return backend::isCompressedFormat(mFormat); }
// bool isCubemap() const noexcept { return mTarget == Sampler::SAMPLER_CUBEMAP; }
// FStream const* getStream() const noexcept { return mStream; }
// /*
// * Utilities
// */
// // synchronous call to the backend. returns whether a backend supports a particular format.
// static bool isTextureFormatSupported(FEngine& engine, InternalFormat format) noexcept;
// // synchronous call to the backend. returns whether a backend supports texture swizzling.
// static bool isTextureSwizzleSupported(FEngine& engine) noexcept;
// // storage needed on the CPU side for texture data uploads
// static size_t computeTextureDataSize(Texture::Format format, Texture::Type type,
// size_t stride, size_t height, size_t alignment) noexcept;
// // Size a of a pixel in bytes for the given format
// static size_t getFormatSize(InternalFormat format) noexcept;
// // Returns the with or height for a given mipmap level from the base value.
// static inline size_t valueForLevel(uint8_t level, size_t baseLevelValue) {
// return std::max(size_t(1), baseLevelValue >> level);
// }
// // Returns the max number of levels for a texture of given max dimensions
// static inline uint8_t maxLevelCount(uint32_t maxDimension) noexcept {
// return std::max(1, std::ilogbf(maxDimension) + 1);
// }
// // Returns the max number of levels for a texture of given dimensions
// static inline uint8_t maxLevelCount(uint32_t width, uint32_t height) noexcept {
// return std::max(1, std::ilogbf(std::max(width, height)) + 1);
// }
// static bool validatePixelFormatAndType(backend::TextureFormat internalFormat,
// backend::PixelDataFormat format, backend::PixelDataType type) noexcept;
// private:
// friend class Texture;
// FStream* mStream = nullptr;
// backend::Handle<backend::HwTexture> mHandle;
// uint32_t mWidth = 1;
// uint32_t mHeight = 1;
// uint32_t mDepth = 1;
// InternalFormat mFormat = InternalFormat::RGBA8;
// Sampler mTarget = Sampler::SAMPLER_2D;
// uint8_t mLevelCount = 1;
// uint8_t mSampleCount = 1;
// Usage mUsage = Usage::DEFAULT;
// };
// } // namespace filament

View File

@@ -67,7 +67,7 @@ namespace gltfio {
MaterialInstance* materialInstance;
};
int numAttributes = 1; // just position for now - normals not working with indexing inside shader? byte offset seems not calculated correctly
int numAttributes = 2;
void addPrimitive(cgltf_mesh const *mesh, int primitiveIndex);

View File

@@ -8,6 +8,7 @@
#include <math/mat4.h>
#include <utils/Log.h>
#include <iostream>
#include "gltfio/resources/gltfresources_lite.h"
@@ -22,7 +23,12 @@ namespace {
class GPUMorphShaderLoader : public MaterialProvider {
public:
GPUMorphShaderLoader(const void* mData, uint64_t size, filament::Engine* engine);
GPUMorphShaderLoader(
const void* opaqueMaterial,
uint64_t opaqueMaterialSize,
const void* fadeMaterial,
uint64_t fadeMaterialSize,
filament::Engine* engine);
~GPUMorphShaderLoader() {}
MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap,
@@ -42,8 +48,10 @@ namespace {
return false;
}
}
const void* mData;
const size_t mSize;
const void* opaqueData;
const size_t opaqueDataSize;
const void* fadeData;
const size_t fadeDataSize;
Material* getMaterial(const MaterialKey& config) const;
@@ -59,18 +67,41 @@ namespace {
Engine* mEngine;
};
GPUMorphShaderLoader::GPUMorphShaderLoader(const void* data, uint64_t size, Engine* engine) : mData(data), mSize(size), mEngine(engine) {
unsigned char texels[4] = {};
mDummyTexture = Texture::Builder()
.width(1).height(1)
.format(Texture::InternalFormat::RGBA8)
.build(*mEngine);
Texture::PixelBufferDescriptor pbd(texels, sizeof(texels), Texture::Format::RGBA,
Texture::Type::UBYTE);
mDummyTexture->setImage(*mEngine, 0, std::move(pbd));
#define MATINDEX(shading, alpha, sheen, transmit, volume) (volume ? 11 : (transmit ? 10 : (sheen ? 9 : (int(shading) + 3 * int(alpha)))))
GPUMorphShaderLoader::GPUMorphShaderLoader(const void* opaqueData,
uint64_t opaqueDataSize,
const void* fadeData,
uint64_t fadeDataSize,
Engine* engine) : opaqueData(opaqueData), opaqueDataSize(opaqueDataSize), fadeData(fadeData), fadeDataSize(fadeDataSize), mEngine(engine) {
// unsigned char texels[4] = {};
// mDummyTexture = Texture::Builder()
// .width(1)
// .height(1)
// .format(backend::TextureFormat::RGBA8)
// .sampler(backend::SamplerType::SAMPLER_2D_ARRAY)
// .build(*mEngine);
// Texture::PixelBufferDescriptor pbd(
// texels,
// sizeof(texels),
// backend::PixelDataFormat::RGBA,
// backend::PixelDataType::UBYTE,
// nullptr);
// mDummyTexture->setImage(*mEngine, 0,0,0,0,0,0,0,std::move(pbd));
unsigned char texels[4] = {};
mDummyTexture = Texture::Builder()
.width(1).height(1)
.format(Texture::InternalFormat::RGBA8)
.build(*mEngine);
Texture::PixelBufferDescriptor pbd(texels, sizeof(texels), Texture::Format::RGBA,
Texture::Type::UBYTE);
mDummyTexture->setImage(*mEngine, 0, std::move(pbd));
}
size_t GPUMorphShaderLoader::getMaterialsCount() const noexcept {
return sizeof(mMaterials) / sizeof(mMaterials[0]);
}
@@ -90,13 +121,33 @@ namespace {
Material* GPUMorphShaderLoader::getMaterial(const MaterialKey& config) const {
const ShadingMode shading = config.unlit ? UNLIT :
(config.useSpecularGlossiness ? SPECULAR_GLOSSINESS : LIT);
const int matindex = 0;
const int matindex = MATINDEX(shading, config.alphaMode, config.hasSheen, config.hasTransmission, config.hasVolume);
if (mMaterials[matindex] != nullptr) {
return mMaterials[matindex];
}
switch (matindex) {
case MATINDEX(LIT, AlphaMode::OPAQUE, false, false, false):
{
filamat::Package pkg = filamat::Package(opaqueData, opaqueDataSize);
mMaterials[matindex] = Material::Builder().package(pkg.getData(), pkg.getSize()).build(*mEngine);
}
break;
case MATINDEX(LIT, AlphaMode::BLEND, false, false, false):
{
filamat::Package pkg = filamat::Package(fadeData, fadeDataSize);
mMaterials[matindex] = Material::Builder().package(pkg.getData(), pkg.getSize()).build(*mEngine);
}
break;
default:
{
filamat::Package pkg = filamat::Package(fadeData, fadeDataSize);
mMaterials[matindex] = Material::Builder().package(pkg.getData(), pkg.getSize()).build(*mEngine);
}
break;
}
filamat::Package pkg = filamat::Package(mData, mSize);
return Material::Builder().package(pkg.getData(), pkg.getSize()).build(*mEngine);
return mMaterials[matindex];
}
MaterialInstance* GPUMorphShaderLoader::createMaterialInstance(MaterialKey* config, UvMap* uvmap,
@@ -145,7 +196,9 @@ namespace {
mi->setDoubleSided(config->doubleSided);
mi->setCullingMode(config->doubleSided ? CullingMode::NONE : CullingMode::BACK);
mi->setTransparencyMode(config->doubleSided ?
MaterialInstance::TransparencyMode::TWO_PASSES_TWO_SIDES :
MaterialInstance::TransparencyMode::DEFAULT);
// Initially, assume that the clear coat texture can be honored. This is changed to false when
// running into a sampler count limitation. TODO: check if these constraints can now be relaxed.
bool clearCoatNeedsTexture = true;
@@ -176,7 +229,6 @@ namespace {
getUvIndex(config->sheenRoughnessUV, config->hasSheenRoughnessTexture));
mi->setParameter("sheenColorUvMatrix", identity);
mi->setParameter("sheenRoughnessUvMatrix", identity);
}
if (config->hasVolume) {
clearCoatNeedsTexture = false;
@@ -196,6 +248,8 @@ namespace {
mi->setParameter("normalMap", mDummyTexture, sampler);
mi->setParameter("baseColorMap", mDummyTexture, sampler);
mi->setParameter("metallicRoughnessMap", mDummyTexture, sampler);
// mi->setParameter("roughnessFactor", mDummyTexture, sampler);
mi->setParameter("occlusionMap", mDummyTexture, sampler);
mi->setParameter("emissiveMap", mDummyTexture, sampler);
if (clearCoatNeedsTexture) {
@@ -227,8 +281,18 @@ namespace {
namespace gltfio {
MaterialProvider* createGPUMorphShaderLoader(const void* data, uint64_t size, filament::Engine* engine) {
return new GPUMorphShaderLoader(data, size,engine);
MaterialProvider* createGPUMorphShaderLoader(
const void* opaqueData,
uint64_t opaqueDataSize,
const void* fadeData,
uint64_t fadeDataSize,
filament::Engine* engine) {
return new GPUMorphShaderLoader(
opaqueData,
opaqueDataSize,
fadeData,
fadeDataSize,
engine);
}
} // namespace gltfio

View File

@@ -0,0 +1,308 @@
/*
* 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 <filament/Texture.h>
#include <filament/TextureSampler.h>
#include <math/mat4.h>
#include <utils/Log.h>
#include "gltfio/resources/gltfresources.h"
using namespace filament;
using namespace filament::math;
using namespace gltfio;
using namespace utils;
namespace {
using CullingMode = MaterialInstance::CullingMode;
class UbershaderLoader : public MaterialProvider {
public:
UbershaderLoader(filament::Engine* engine);
~UbershaderLoader() {}
MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap,
const char* label) override;
size_t getMaterialsCount() const noexcept override;
const Material* const* getMaterials() const noexcept override;
void destroyMaterials() override;
bool needsDummyData(VertexAttribute attrib) const noexcept override {
switch (attrib) {
case VertexAttribute::UV0:
case VertexAttribute::UV1:
case VertexAttribute::COLOR:
return true;
default:
return false;
}
}
Material* getMaterial(const MaterialKey& config) const;
enum ShadingMode {
UNLIT = 0,
LIT = 1,
SPECULAR_GLOSSINESS = 2,
};
mutable Material* mMaterials[12] = {};
Texture* mDummyTexture = nullptr;
Engine* mEngine;
};
#if GLTFIO_LITE
#define CREATE_MATERIAL(name) Material::Builder() \
.package(GLTFRESOURCES_LITE_ ## name ## _DATA, GLTFRESOURCES_LITE_ ## name ## _SIZE) \
.build(*mEngine);
#else
#define CREATE_MATERIAL(name) Material::Builder() \
.package(GLTFRESOURCES_ ## name ## _DATA, GLTFRESOURCES_ ## name ## _SIZE) \
.build(*mEngine);
#endif
#define MATINDEX(shading, alpha, sheen, transmit, volume) (volume ? 11 : (transmit ? 10 : (sheen ? 9 : (int(shading) + 3 * int(alpha)))))
UbershaderLoader::UbershaderLoader(Engine* engine) : mEngine(engine) {
unsigned char texels[4] = {};
mDummyTexture = Texture::Builder()
.width(1).height(1)
.format(Texture::InternalFormat::RGBA8)
.build(*mEngine);
Texture::PixelBufferDescriptor pbd(texels, sizeof(texels), Texture::Format::RGBA,
Texture::Type::UBYTE);
mDummyTexture->setImage(*mEngine, 0, std::move(pbd));
}
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;
}
mEngine->destroy(mDummyTexture);
}
Material* UbershaderLoader::getMaterial(const MaterialKey& config) const {
const ShadingMode shading = config.unlit ? UNLIT :
(config.useSpecularGlossiness ? SPECULAR_GLOSSINESS : LIT);
const int matindex = MATINDEX(shading, config.alphaMode, config.hasSheen, config.hasTransmission, config.hasVolume);
if (mMaterials[matindex] != nullptr) {
return mMaterials[matindex];
}
switch (matindex) {
case MATINDEX(LIT, AlphaMode::OPAQUE, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_OPAQUE); break;
case MATINDEX(LIT, AlphaMode::BLEND, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_FADE); break;
case MATINDEX(LIT, AlphaMode::MASK, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_MASKED); break;
case MATINDEX(UNLIT, AlphaMode::OPAQUE, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(UNLIT_OPAQUE); break;
case MATINDEX(UNLIT, AlphaMode::MASK, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(UNLIT_MASKED); break;
case MATINDEX(UNLIT, AlphaMode::BLEND, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(UNLIT_FADE); break;
case MATINDEX(SPECULAR_GLOSSINESS, AlphaMode::OPAQUE, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(SPECULARGLOSSINESS_OPAQUE); break;
case MATINDEX(SPECULAR_GLOSSINESS, AlphaMode::MASK, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(SPECULARGLOSSINESS_MASKED); break;
case MATINDEX(SPECULAR_GLOSSINESS, AlphaMode::BLEND, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(SPECULARGLOSSINESS_FADE); break;
case MATINDEX(0, 0, false, true, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_TRANSMISSION); break;
case MATINDEX(0, 0, true, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_SHEEN); break;
case MATINDEX(0, 0, false, false, true): mMaterials[matindex] = CREATE_MATERIAL(LIT_VOLUME); break;
}
if (mMaterials[matindex] == nullptr) {
slog.w << "Unsupported glTF material configuration; falling back to LIT_OPAQUE." << io::endl;
MaterialKey litOpaque = config;
litOpaque.alphaMode = AlphaMode::OPAQUE;
litOpaque.hasTransmission = false;
litOpaque.hasVolume = false;
litOpaque.hasSheen = false;
litOpaque.useSpecularGlossiness = false;
litOpaque.unlit = false;
return getMaterial(litOpaque);
}
return mMaterials[matindex];
}
MaterialInstance* UbershaderLoader::createMaterialInstance(MaterialKey* config, UvMap* uvmap,
const char* label) {
// Diagnostics are not supported with LOAD_UBERSHADERS, please use GENERATE_SHADERS instead.
if (config->enableDiagnostics) {
return nullptr;
}
if (config->hasVolume && config->hasSheen) {
slog.w << "Volume and sheen are not supported together in ubershader mode,"
" removing sheen (" << label << ")." << io::endl;
config->hasSheen = false;
}
if (config->hasTransmission && config->hasSheen) {
slog.w << "Transmission and sheen are not supported together in ubershader mode,"
" removing sheen (" << label << ")." << io::endl;
config->hasSheen = false;
}
const bool clearCoatConflict = config->hasVolume || config->hasTransmission || config->hasSheen;
// Due to sampler overload, disable transmission if necessary and print a friendly warning.
if (config->hasClearCoat && clearCoatConflict) {
slog.w << "Volume, transmission and sheen are not supported in ubershader mode for clearcoat"
" materials (" << label << ")." << io::endl;
config->hasVolume = false;
config->hasTransmission = false;
config->hasSheen = false;
}
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(label);
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));
mi->setDoubleSided(config->doubleSided);
mi->setCullingMode(config->doubleSided ? CullingMode::NONE : CullingMode::BACK);
mi->setTransparencyMode(config->doubleSided ?
MaterialInstance::TransparencyMode::TWO_PASSES_TWO_SIDES :
MaterialInstance::TransparencyMode::DEFAULT);
#if !GLTFIO_LITE
// Initially, assume that the clear coat texture can be honored. This is changed to false when
// running into a sampler count limitation. TODO: check if these constraints can now be relaxed.
bool clearCoatNeedsTexture = true;
mat3f identity;
mi->setParameter("baseColorUvMatrix", identity);
mi->setParameter("metallicRoughnessUvMatrix", identity);
mi->setParameter("normalUvMatrix", identity);
mi->setParameter("occlusionUvMatrix", identity);
mi->setParameter("emissiveUvMatrix", identity);
if (config->hasClearCoat) {
mi->setParameter("clearCoatIndex",
getUvIndex(config->clearCoatUV, config->hasClearCoatTexture));
mi->setParameter("clearCoatRoughnessIndex",
getUvIndex(config->clearCoatRoughnessUV, config->hasClearCoatRoughnessTexture));
mi->setParameter("clearCoatNormalIndex",
getUvIndex(config->clearCoatNormalUV, config->hasClearCoatNormalTexture));
mi->setParameter("clearCoatUvMatrix", identity);
mi->setParameter("clearCoatRoughnessUvMatrix", identity);
mi->setParameter("clearCoatNormalUvMatrix", identity);
} else {
if (config->hasSheen) {
clearCoatNeedsTexture = false;
mi->setParameter("sheenColorIndex",
getUvIndex(config->sheenColorUV, config->hasSheenColorTexture));
mi->setParameter("sheenRoughnessIndex",
getUvIndex(config->sheenRoughnessUV, config->hasSheenRoughnessTexture));
mi->setParameter("sheenColorUvMatrix", identity);
mi->setParameter("sheenRoughnessUvMatrix", identity);
}
if (config->hasVolume) {
clearCoatNeedsTexture = false;
mi->setParameter("volumeThicknessUvMatrix", identity);
mi->setParameter("volumeThicknessIndex",
getUvIndex(config->transmissionUV, config->hasVolumeThicknessTexture));
}
if (config->hasTransmission) {
clearCoatNeedsTexture = false;
mi->setParameter("transmissionUvMatrix", identity);
mi->setParameter("transmissionIndex",
getUvIndex(config->transmissionUV, config->hasTransmissionTexture));
}
}
#else
// In the GLTFIO_LITE configuration we do not support UV matrices, clear coat, sheen, specular
// glossiness, or transmission. For more details, see `gltflite.mat.in`. To configure a custom
// set of features, create your own MaterialProvider class, perhaps using UbershaderLoader as a
// starting point.
const bool clearCoatNeedsTexture = false;
#endif
TextureSampler sampler;
mi->setParameter("normalMap", mDummyTexture, sampler);
mi->setParameter("baseColorMap", mDummyTexture, sampler);
mi->setParameter("metallicRoughnessMap", mDummyTexture, sampler);
mi->setParameter("occlusionMap", mDummyTexture, sampler);
mi->setParameter("emissiveMap", mDummyTexture, sampler);
if (clearCoatNeedsTexture) {
mi->setParameter("clearCoatMap", mDummyTexture, sampler);
mi->setParameter("clearCoatRoughnessMap", mDummyTexture, sampler);
mi->setParameter("clearCoatNormalMap", mDummyTexture, sampler);
}
if (!config->hasClearCoat) {
if (config->hasTransmission) {
mi->setParameter("transmissionMap", mDummyTexture, sampler);
}
if (config->hasSheen) {
mi->setParameter("sheenColorMap", mDummyTexture, sampler);
mi->setParameter("sheenRoughnessMap", mDummyTexture, sampler);
}
}
if (mi->getMaterial()->hasParameter("ior")) {
mi->setParameter("ior", 1.5f);
}
if (mi->getMaterial()->hasParameter("reflectance")) {
mi->setParameter("reflectance", 0.5f);
}
return mi;
}
} // anonymous namespace
namespace foo {
MaterialProvider* createUbershaderLoader(filament::Engine* engine) {
return new UbershaderLoader(engine);
}
} // namespace gltfio