diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 7169074b32..9ca6805b04 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -143,6 +143,7 @@ endfunction() if (NOT ANDROID) add_assimp_demo(frame_generator) + add_assimp_demo(gltf_viewer) add_assimp_demo(lightbulb) add_assimp_demo(material_sandbox) add_assimp_demo(sample_full_pbr) diff --git a/samples/app/MeshAssimp.cpp b/samples/app/MeshAssimp.cpp index c2ec7725cc..7cbb70c8a7 100644 --- a/samples/app/MeshAssimp.cpp +++ b/samples/app/MeshAssimp.cpp @@ -17,6 +17,7 @@ #include "MeshAssimp.h" #include +#include #include #include @@ -29,12 +30,19 @@ #include #include +#include +#include #include #include #include #include +#include + +#include + + using namespace filament; using namespace filamat; using namespace math; @@ -48,7 +56,107 @@ static constexpr uint8_t DEFAULT_TRANSPARENT_PACKAGE[] = { #include "generated/material/aiDefaultTrans.inc" }; +static constexpr uint8_t GLTF2_PACKAGE[] = { + #include "generated/material/gltf2.inc" +}; + +static constexpr uint8_t GLTF2_DS_PACKAGE[] = { + #include "generated/material/gltf2DoubleSided.inc" +}; + +static constexpr uint8_t GLTF2_TRANS_PACKAGE[] = { + #include "generated/material/gltf2Trans.inc" +}; + +static constexpr uint8_t GLTF2_DS_TRANS_PACKAGE[] = { + #include "generated/material/gltf2DoubleSidedTrans.inc" +}; + +static constexpr uint8_t GLTF2_MASKED_PACKAGE[] = { + #include "generated/material/gltf2Masked.inc" +}; + +static constexpr uint8_t GLTF2_DS_MASKED_PACKAGE[] = { + #include "generated/material/gltf2DoubleSidedMasked.inc" +}; + +static constexpr uint8_t GLTF2_UNLIT_PACKAGE[] = { + #include "generated/material/gltf2Unlit.inc" +}; + +static constexpr uint8_t GLTF2_DS_UNLIT_PACKAGE[] = { + #include "generated/material/gltf2DoubleSidedUnlit.inc" +}; + +Texture* MeshAssimp::createOneByOneTexture(uint32_t pixel) { + uint32_t *textureData = (uint32_t *) malloc(sizeof(uint32_t)); + *textureData = pixel; + + Texture *texturePtr = Texture::Builder() + .width(uint32_t(1)) + .height(uint32_t(1)) + .levels(0xff) + .format(driver::TextureFormat::RGBA8) + .build(mEngine); + + Texture::PixelBufferDescriptor defaultNormalBuffer(textureData, + size_t(1 * 1 * 4), + Texture::Format::RGBA, + Texture::Type::UBYTE, + (driver::BufferDescriptor::Callback) &free); + + texturePtr->setImage(mEngine, 0, std::move(defaultNormalBuffer)); + texturePtr->generateMipmaps(mEngine); + + return texturePtr; +} + MeshAssimp::MeshAssimp(Engine& engine) : mEngine(engine) { + //Initialize some things here + mDefaultMap = createOneByOneTexture(0xffffffff); + mDefaultNormalMap = createOneByOneTexture(0xffff8080); + + mDefaultColorMaterial = Material::Builder() + .package((void*) DEFAULT_MATERIAL_PACKAGE, sizeof(DEFAULT_MATERIAL_PACKAGE)) + .build(mEngine); + + mDefaultColorMaterial->setDefaultParameter("baseColor", RgbType::LINEAR, float3{0.8}); + mDefaultColorMaterial->setDefaultParameter("metallic", 0.0f); + mDefaultColorMaterial->setDefaultParameter("roughness", 0.4f); + mDefaultColorMaterial->setDefaultParameter("reflectance", 0.5f); + + mDefaultTransparentColorMaterial = Material::Builder() + .package((void*) DEFAULT_TRANSPARENT_PACKAGE, sizeof(DEFAULT_TRANSPARENT_PACKAGE)) + .build(mEngine); + + mDefaultTransparentColorMaterial->setDefaultParameter("baseColor", RgbType::LINEAR, float3{0.8}); + mDefaultTransparentColorMaterial->setDefaultParameter("metallic", 0.0f); + mDefaultTransparentColorMaterial->setDefaultParameter("roughness", 0.4f); + + mGltfMaterial = Material::Builder() + .package((void*) GLTF2_PACKAGE, sizeof(GLTF2_PACKAGE)) + .build(mEngine); + mGltfMaterialDS = Material::Builder() + .package((void*) GLTF2_DS_PACKAGE, sizeof(GLTF2_DS_PACKAGE)) + .build(mEngine); + mGltfMaterialTrans = Material::Builder() + .package((void*) GLTF2_TRANS_PACKAGE, sizeof(GLTF2_TRANS_PACKAGE)) + .build(mEngine); + mGltfMaterialDSTrans = Material::Builder() + .package((void*) GLTF2_DS_TRANS_PACKAGE, sizeof(GLTF2_DS_TRANS_PACKAGE)) + .build(mEngine); + mGltfMaterialMasked = Material::Builder() + .package((void*) GLTF2_MASKED_PACKAGE, sizeof(GLTF2_MASKED_PACKAGE)) + .build(mEngine); + mGltfMaterialDSMasked = Material::Builder() + .package((void*) GLTF2_DS_MASKED_PACKAGE, sizeof(GLTF2_DS_MASKED_PACKAGE)) + .build(mEngine); + mGltfMaterialUnlit = Material::Builder() + .package((void*) GLTF2_UNLIT_PACKAGE, sizeof(GLTF2_UNLIT_PACKAGE)) + .build(mEngine); + mGltfMaterialDSUnlit = Material::Builder() + .package((void*) GLTF2_DS_UNLIT_PACKAGE, sizeof(GLTF2_DS_UNLIT_PACKAGE)) + .build(mEngine); } MeshAssimp::~MeshAssimp() { @@ -56,9 +164,25 @@ MeshAssimp::~MeshAssimp() { mEngine.destroy(mIndexBuffer); mEngine.destroy(mDefaultColorMaterial); mEngine.destroy(mDefaultTransparentColorMaterial); + mEngine.destroy(mGltfMaterial); + mEngine.destroy(mGltfMaterialDS); + mEngine.destroy(mGltfMaterialTrans); + mEngine.destroy(mGltfMaterialDSTrans); + mEngine.destroy(mGltfMaterialMasked); + mEngine.destroy(mGltfMaterialDSMasked); + mEngine.destroy(mGltfMaterialUnlit); + mEngine.destroy(mGltfMaterialDSUnlit); + mEngine.destroy(mDefaultNormalMap); + mEngine.destroy(mDefaultMap); + for (Entity renderable : mRenderables) { mEngine.destroy(renderable); } + + for (Texture* texture : mTextures) { + mEngine.destroy(texture); + } + // destroy the Entities itself EntityManager::get().destroy(mRenderables.size(), mRenderables.data()); } @@ -68,13 +192,176 @@ struct State { std::vector state; explicit State(std::vector&& state) : state(state) { } static void free(void* buffer, size_t size, void* user) { - auto* const that = (State*)user; + auto* const that = static_cast*>(user); delete that; } size_t size() const { return state.size() * sizeof(T); } T const * data() const { return state.data(); } }; +//TODO: Remove redundant method from sample_full_pbr +static void loadTexture(Engine *engine, const std::string &filePath, Texture **map, + bool sRGB, bool hasAlpha) { + + if (!filePath.empty()) { + Path path(filePath); + if (path.exists()) { + int w, h, n; + int numChannels = hasAlpha ? 4 : 3; + + driver::TextureFormat inputFormat; + if (sRGB) { + inputFormat = hasAlpha ? driver::TextureFormat::SRGB8_A8 : driver::TextureFormat::SRGB8; + } else { + inputFormat = hasAlpha ? driver::TextureFormat::RGBA8 : driver::TextureFormat::RGB8; + } + + Texture::Format outputFormat = hasAlpha ? Texture::Format::RGBA : Texture::Format::RGB; + + uint8_t *data = stbi_load(path.getAbsolutePath().c_str(), &w, &h, &n, numChannels); + if (data != nullptr) { + *map = Texture::Builder() + .width(uint32_t(w)) + .height(uint32_t(h)) + .levels(0xff) + .format(inputFormat) + .build(*engine); + + Texture::PixelBufferDescriptor buffer(data, + size_t(w * h * numChannels), + outputFormat, + Texture::Type::UBYTE, + (driver::BufferDescriptor::Callback) &stbi_image_free); + + (*map)->setImage(*engine, 0, std::move(buffer)); + (*map)->generateMipmaps(*engine); + } else { + std::cout << "The texture " << path << " could not be loaded" << std::endl; + } + } else { + std::cout << "The texture " << path << " does not exist" << std::endl; + } + } +} + +void loadEmbeddedTexture(Engine *engine, aiTexture *embeddedTexture, Texture **map, + bool sRGB, bool hasAlpha) { + + int w, h, n; + int numChannels = hasAlpha ? 4 : 3; + + driver::TextureFormat inputFormat; + if (sRGB) { + inputFormat = hasAlpha ? driver::TextureFormat::SRGB8_A8 : driver::TextureFormat::SRGB8; + } else { + inputFormat = hasAlpha ? driver::TextureFormat::RGBA8 : driver::TextureFormat::RGB8; + } + + Texture::Format outputFormat = hasAlpha ? Texture::Format::RGBA : Texture::Format::RGB; + + uint8_t *data = stbi_load_from_memory((unsigned char *) embeddedTexture->pcData, + embeddedTexture->mWidth, &w, &h, &n, numChannels); + + *map = Texture::Builder() + .width(uint32_t(w)) + .height(uint32_t(h)) + .levels(0xff) + .format(inputFormat) + .build(*engine); + + Texture::PixelBufferDescriptor defaultBuffer(data, + size_t(w * h * numChannels), + outputFormat, + Texture::Type::UBYTE, + (driver::BufferDescriptor::Callback) &free); + + (*map)->setImage(*engine, 0, std::move(defaultBuffer)); + (*map)->generateMipmaps(*engine); +} + +// Takes a texture filename and returns the index of the embedded texture, +// -1 if the texture is not embedded +int32_t getEmbeddedTextureId(const aiString& path) { + const char *pathStr = path.C_Str(); + if (path.length >= 2 && pathStr[0] == '*') { + for (int i = 1; i < path.length; i++) { + if (!isdigit(pathStr[i])) { + return -1; + } + } + return std::atoi(pathStr + 1); + } + return -1; +} + +TextureSampler::WrapMode aiToFilamentMapMode(aiTextureMapMode mapMode) { + switch(mapMode) { + case aiTextureMapMode_Clamp : + return TextureSampler::WrapMode::CLAMP_TO_EDGE; + case aiTextureMapMode_Mirror : + return TextureSampler::WrapMode::MIRRORED_REPEAT; + default: + return TextureSampler::WrapMode::REPEAT; + } +} + +// TODO: Change this to a member function (requires some alteration of cmakelsts.txt) +void setTextureFromPath(const aiScene *scene, Engine *engine, + std::vector textures, const aiString &texFile, + const std::string &materialName, const std::string &texDir, + aiTextureMapMode *mapMode, const char *parameterName, + std::map &outMaterials) { + + TextureSampler sampler; + if (mapMode) { + sampler = TextureSampler(TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR, + TextureSampler::MagFilter::LINEAR, + aiToFilamentMapMode(mapMode[0]), + aiToFilamentMapMode(mapMode[1]), + aiToFilamentMapMode(mapMode[2])); + } else { + sampler = TextureSampler(TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR, + TextureSampler::MagFilter::LINEAR, TextureSampler::WrapMode::REPEAT); + } + + Texture* textureMap = nullptr; + int32_t embeddedId = getEmbeddedTextureId(texFile); + + //TODO: change this in refactor + bool isSRGB = strcmp(parameterName, "baseColorMap") == 0 || strcmp(parameterName, "emissiveMap") == 0; + bool hasAlpha = strcmp(parameterName, "baseColorMap") == 0; + + if (embeddedId != -1) { + loadEmbeddedTexture(engine, scene->mTextures[embeddedId], &textureMap, isSRGB, hasAlpha); + } else { + loadTexture(engine, texDir + texFile.C_Str(), &textureMap, isSRGB, hasAlpha); + } + + textures.push_back(textureMap); + + if (textureMap != nullptr) { + outMaterials[materialName]->setParameter( + parameterName, textureMap, sampler); + } +} + +template +Box computeTransformedAABB(VECTOR const* vertices, INDEX const* indices, size_t count, + const mat4f& transform) noexcept { + size_t stride = sizeof(VECTOR); + math::float3 bmin(std::numeric_limits::max()); + math::float3 bmax(std::numeric_limits::lowest()); + for (size_t i = 0; i < count; ++i) { + VECTOR const* p = reinterpret_cast( + (char const*) vertices + indices[i] * stride); + const math::float3 v(p->x, p->y, p->z); + float3 tv = (transform * float4(v, 1.0f)).xyz; + bmin = min(bmin, tv); + bmax = max(bmax, tv); + } + return Box().set(bmin, bmax); +} + void MeshAssimp::addFromFile(const Path& path, std::map& materials, bool overrideMaterial) { @@ -91,7 +378,8 @@ void MeshAssimp::addFromFile(const Path& path, // "command buffer" lifetime, we wouldn't need to have to deal with freeing the // std::vectors here. - if (!setFromFile(path, indices, positions, tangents, texCoords, meshes, parents)) { + //TODO: a lot of these method arguments should probably be class or global variables + if (!setFromFile(path, indices, positions, tangents, texCoords, meshes, parents, materials)) { return; } @@ -123,23 +411,6 @@ void MeshAssimp::addFromFile(const Path& path, IndexBuffer::BufferDescriptor(is->data(), is->size(), State::free, is)); } - mDefaultColorMaterial = Material::Builder() - .package((void*) DEFAULT_MATERIAL_PACKAGE, sizeof(DEFAULT_MATERIAL_PACKAGE)) - .build(mEngine); - - mDefaultColorMaterial->setDefaultParameter("baseColor", RgbType::LINEAR, float3{0.8}); - mDefaultColorMaterial->setDefaultParameter("metallic", 0.0f); - mDefaultColorMaterial->setDefaultParameter("roughness", 0.4f); - mDefaultColorMaterial->setDefaultParameter("reflectance", 0.5f); - - mDefaultTransparentColorMaterial = Material::Builder() - .package((void*) DEFAULT_TRANSPARENT_PACKAGE, sizeof(DEFAULT_TRANSPARENT_PACKAGE)) - .build(mEngine); - - mDefaultTransparentColorMaterial->setDefaultParameter("baseColor", RgbType::LINEAR, float3{0.8}); - mDefaultTransparentColorMaterial->setDefaultParameter("metallic", 0.0f); - mDefaultTransparentColorMaterial->setDefaultParameter("roughness", 0.4f); - // always add the DefaultMaterial (with its default parameters), so we don't pick-up // whatever defaults is used in mesh if (materials.find(AI_DEFAULT_MATERIAL_NAME) == materials.end()) { @@ -149,8 +420,11 @@ void MeshAssimp::addFromFile(const Path& path, size_t startIndex = mRenderables.size(); mRenderables.resize(startIndex + meshes.size()); EntityManager::get().create(meshes.size(), mRenderables.data() + startIndex); + EntityManager::get().create(1, &rootEntity); TransformManager& tcm = mEngine.getTransformManager(); + //Add root instance + tcm.create(rootEntity, TransformManager::Instance{}, mat4f()); for (auto& mesh : meshes) { RenderableManager::Builder builder(mesh.parts.size()); @@ -165,6 +439,7 @@ void MeshAssimp::addFromFile(const Path& path, builder.material(partIndex, materials[AI_DEFAULT_MATERIAL_NAME]); } else { auto pos = materials.find(part.material); + if (pos != materials.end()) { builder.material(partIndex, pos->second); } else { @@ -184,7 +459,6 @@ void MeshAssimp::addFromFile(const Path& path, materials[part.material] = colorMaterial; } } - partIndex++; } @@ -195,21 +469,18 @@ void MeshAssimp::addFromFile(const Path& path, } auto pindex = parents[meshIndex]; TransformManager::Instance parent((pindex < 0) ? - TransformManager::Instance{} : tcm.getInstance(mRenderables[pindex])); + tcm.getInstance(rootEntity) : tcm.getInstance(mRenderables[pindex])); tcm.create(entity, parent, mesh.transform); } } using Assimp::Importer; -bool MeshAssimp::setFromFile(const Path& file, - std::vector& outIndices, - std::vector& outPositions, - std::vector& outTangents, - std::vector& outTexCoords, - std::vector& outMeshes, - std::vector& outParents -) { +bool MeshAssimp::setFromFile(const Path& file, std::vector& outIndices, + std::vector& outPositions, std::vector& outTangents, + std::vector& outTexCoords, std::vector& outMeshes, + std::vector& outParents, std::map& outMaterials) { + Importer importer; importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT); @@ -220,6 +491,8 @@ bool MeshAssimp::setFromFile(const Path& file, // normals and tangents aiProcess_GenSmoothNormals | aiProcess_CalcTangentSpace | + // UV Coordinates + aiProcess_GenUVCoords | // topology optimization aiProcess_FindInstances | aiProcess_OptimizeMeshes | @@ -230,6 +503,21 @@ bool MeshAssimp::setFromFile(const Path& file, // we only support triangles aiProcess_Triangulate); + scene = importer.ApplyPostProcessing(aiProcess_CalcTangentSpace); + size_t index = importer.GetImporterIndex(file.getExtension().c_str()); + const aiImporterDesc* importerDesc = importer.GetImporterInfo(index); + bool isGLTF = importerDesc && + (!strncmp("glTF Importer", importerDesc->mName, 13) || + !strncmp("glTF2 Importer", importerDesc->mName, 14)); + + if (!scene) { + std::cout << "No scene" << std::endl; + } + + if (scene && !scene->mRootNode) { + std::cout << "No root node" << std::endl; + } + // we could use those, but we want to keep the graph if any, for testing // aiProcess_OptimizeGraph // aiProcess_PreTransformVertices @@ -238,10 +526,10 @@ bool MeshAssimp::setFromFile(const Path& file, countVertices = [scene, &countVertices] (aiNode const* node, size_t& totalVertexCount, size_t& totalIndexCount) { for (size_t i = 0; i < node->mNumMeshes; i++) { - aiMesh const* mesh = scene->mMeshes[node->mMeshes[i]]; + aiMesh const *mesh = scene->mMeshes[node->mMeshes[i]]; totalVertexCount += mesh->mNumVertices; - const aiFace* faces = mesh->mFaces; + const aiFace *faces = mesh->mFaces; const size_t numFaces = mesh->mNumFaces; totalIndexCount += numFaces * faces[0].mNumIndices; } @@ -253,11 +541,10 @@ bool MeshAssimp::setFromFile(const Path& file, size_t deep = 0; size_t depth = 0; + size_t matCount = 0; const std::function processNode = - [scene, &processNode, &outParents, &deep, &depth, - &outIndices, &outPositions, &outTangents, &outTexCoords, &outMeshes] - (aiNode const* node, int parentIndex) { + [&](aiNode const* node, int parentIndex) { mat4f const& current = transpose(*reinterpret_cast(&node->mTransformation)); @@ -267,9 +554,8 @@ bool MeshAssimp::setFromFile(const Path& file, outMeshes.back().offset = outIndices.size(); outMeshes.back().transform = current; - // Bias and scale factor when storing tangent frames in normalized short4 - const float bias = 1.0f / 32767.0f; - const float factor = (float) (sqrt(1.0 - (double) bias * (double) bias)); + mat4f parentTransform = parentIndex >= 0 ? outMeshes[parentIndex].accTransform : mat4f(); + outMeshes.back().accTransform = parentTransform * current; for (size_t i = 0; i < node->mNumMeshes; i++) { aiMesh const* mesh = scene->mMeshes[node->mMeshes[i]]; @@ -289,9 +575,25 @@ bool MeshAssimp::setFromFile(const Path& file, size_t indicesOffset = outPositions.size(); for (size_t j = 0; j < numVertices; j++) { - quatf q = mat3f::packTangentFrame({tangents[j], bitangents[j], normals[j]}); + float3 normal = normals[j]; + float3 texCoord = texCoords ? texCoords[j] : float3{0.0}; + float3 tangent; + float3 bitangent; + + //If the tangent and bitangent don't exist, make arbitrary ones + // TODO: The glTF specification recommends using the MikkTSpace algorithm + // for computing tangent vectors in the absence of explicit tangents. + if (!tangents) { + bitangent = norm(cross(normal, float3{1.0, 0.0, 0.0})); + tangent = norm(cross(normal, bitangent)); + } else { + tangent = tangents[j]; + bitangent = bitangents[j]; + } + + quatf q = mat3f::packTangentFrame({tangent, bitangent, normal}); outTangents.push_back(packSnorm16(q.xyzw)); - outTexCoords.emplace_back(texCoords[j].xy); + outTexCoords.emplace_back(texCoord.xy); outPositions.emplace_back(positions[j], 1.0_h); } @@ -312,12 +614,26 @@ bool MeshAssimp::setFromFile(const Path& file, aiString name; std::string materialName; + if (material->Get(AI_MATKEY_NAME, name) != AI_SUCCESS) { - materialName = AI_DEFAULT_MATERIAL_NAME; + if (isGLTF) { + while (outMaterials.find("_mat_" + std::to_string(matCount)) + != outMaterials.end()) { + matCount++; + } + materialName = "_mat_" + std::to_string(matCount); + } else { + materialName = AI_DEFAULT_MATERIAL_NAME; + } } else { materialName = name.C_Str(); } + if (isGLTF && outMaterials.find(materialName) == outMaterials.end()) { + std::string dirName = file.getParent(); + processGLTFMaterial(scene, material, materialName, dirName, outMaterials); + } + aiColor3D color; sRGBColor baseColor{1.0f}; if (material->Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) { @@ -371,8 +687,6 @@ bool MeshAssimp::setFromFile(const Path& file, parentIndex = static_cast(outMeshes.size()) - 1; deep++; depth = std::max(deep, depth); - // std::cout << depth << ": num children = " << node->mNumChildren - // << ", parent = " << parentIndex << std::endl; for (size_t i = 0, c = node->mNumChildren; i < c; i++) { processNode(node->mChildren[i], parentIndex); } @@ -397,16 +711,195 @@ bool MeshAssimp::setFromFile(const Path& file, std::cout << "Hierarchy depth = " << depth << std::endl; - // compute the aabb + // compute the aabb and find bounding box of entire model for (auto& mesh : outMeshes) { mesh.aabb = RenderableManager::computeAABB( outPositions.data(), outIndices.data() + mesh.offset, mesh.count); - } + Box transformedAabb = computeTransformedAABB( + outPositions.data(), + outIndices.data() + mesh.offset, + mesh.count, + mesh.accTransform); + + float3 aabbMin = transformedAabb.getMin(); + float3 aabbMax = transformedAabb.getMax(); + + if (!isinf(aabbMin.x) && !isinf(aabbMax.x)) { + if (minBound.x > maxBound.x) { + minBound.x = aabbMin.x; + maxBound.x = aabbMax.x; + } else { + minBound.x = fmin(minBound.x, aabbMin.x); + maxBound.x = fmax(maxBound.x, aabbMax.x); + } + } + + if (!isinf(aabbMin.y) && !isinf(aabbMax.y)) { + if (minBound.y > maxBound.y) { + minBound.y = aabbMin.y; + maxBound.y = aabbMax.y; + } else { + minBound.y = fmin(minBound.y, aabbMin.y); + maxBound.y = fmax(maxBound.y, aabbMax.y); + } + } + + if (!isinf(aabbMin.z) && !isinf(aabbMax.z)) { + if (minBound.z > maxBound.z) { + minBound.z = aabbMin.z; + maxBound.z = aabbMax.z; + } else { + minBound.z = fmin(minBound.z, aabbMin.z); + maxBound.z = fmax(maxBound.z, aabbMax.z); + } + } + } return true; } - return false; } + +void MeshAssimp::processGLTFMaterial(const aiScene* scene, const aiMaterial* material, + const std::string& materialName, const std::string& dirName, + std::map& outMaterials) const { + + aiString baseColorPath; + aiString AOPath; + aiString MRPath; + aiString normalPath; + aiString emissivePath; + aiTextureMapMode mapMode[3]; + + bool materialIsDoubleSided = false; + material->Get("$mat.twosided", 0, 0, materialIsDoubleSided); + + bool materialIsUnlit = false; + material->Get("$mat.gltf.unlit", 0, 0, materialIsUnlit); + + aiString alphaMode; + + if (materialIsUnlit) { + if (materialIsDoubleSided){ + outMaterials[materialName] = mGltfMaterialDSUnlit->createInstance(); + } else { + outMaterials[materialName] = mGltfMaterialUnlit->createInstance(); + } + } else if (materialIsDoubleSided) { + material->Get("$mat.gltf.alphaMode", 0, 0, alphaMode); + + if (strcmp(alphaMode.C_Str(), "BLEND") == 0) { + outMaterials[materialName] = mGltfMaterialDSTrans->createInstance(); + } else if (strcmp(alphaMode.C_Str(), "MASK") == 0) { + outMaterials[materialName] = mGltfMaterialDSMasked->createInstance(); + float maskThreshold = 0.5; + material->Get("$mat.gltf.alphaCutoff", 0, 0, maskThreshold); + outMaterials[materialName]->setParameter( + "maskThreshold", maskThreshold); + } else { + outMaterials[materialName] = mGltfMaterialDS->createInstance(); + } + } else { + material->Get("$mat.gltf.alphaMode", 0, 0, alphaMode); + + if (strcmp(alphaMode.C_Str(), "BLEND") == 0) { + outMaterials[materialName] = mGltfMaterialTrans->createInstance(); + } else if (strcmp(alphaMode.C_Str(), "MASK") == 0) { + outMaterials[materialName] = mGltfMaterialMasked->createInstance(); + float maskThreshold = 0.5; + material->Get("$mat.gltf.alphaCutoff", 0, 0, maskThreshold); + outMaterials[materialName]->setParameter( + "maskThreshold", maskThreshold); + } else { + outMaterials[materialName] = mGltfMaterial->createInstance(); + } + } + + // Load property values for gltf files + aiColor4D baseColorFactor; + sRGBColorA baseColorFactorCast{1.0f}; + aiColor3D emissiveFactor; + sRGBColor emissiveFactorCast{1.0f}; + float metallicFactor = 1.0; + float roughnessFactor = 1.0; + + // TODO: is occlusion strength available on Assimp now? + + // Load texture images for gltf files + TextureSampler sampler( + TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR, + TextureSampler::MagFilter::LINEAR, TextureSampler::WrapMode::REPEAT); + + if (material->GetTexture(aiTextureType_DIFFUSE, 1, &baseColorPath, + nullptr, nullptr, nullptr, nullptr, mapMode) == AI_SUCCESS) { + setTextureFromPath(scene, &mEngine, mTextures, baseColorPath, + materialName, dirName, mapMode, "baseColorMap", outMaterials); + } else { + outMaterials[materialName]->setParameter("baseColorMap", mDefaultMap, + sampler); + } + + if (material->GetTexture(aiTextureType_UNKNOWN, 0, &MRPath, + nullptr, nullptr, nullptr, nullptr, mapMode) == AI_SUCCESS) { + setTextureFromPath(scene, &mEngine, mTextures, MRPath, materialName, + dirName, mapMode, "metallicRoughnessMap", outMaterials); + + } else { + outMaterials[materialName]->setParameter("metallicRoughnessMap", mDefaultMap, sampler); + outMaterials[materialName]->setParameter("metallicFactor", mDefaultMetallic); + outMaterials[materialName]->setParameter("roughnessFactor", mDefaultRoughness); + } + + if (material->GetTexture(aiTextureType_LIGHTMAP, 0, &AOPath, nullptr, + nullptr, nullptr, nullptr, mapMode) == AI_SUCCESS) { + setTextureFromPath(scene, &mEngine, mTextures, AOPath, materialName, + dirName, mapMode, "aoMap", outMaterials); + + } else { + outMaterials[materialName]->setParameter("aoMap", mDefaultMap, sampler); + } + + if (material->GetTexture(aiTextureType_NORMALS, 0, &normalPath, nullptr, + nullptr, nullptr, nullptr, mapMode) == AI_SUCCESS) { + setTextureFromPath(scene, &mEngine, mTextures, normalPath, materialName, + dirName, mapMode, "normalMap", outMaterials); + } else { + outMaterials[materialName]->setParameter("normalMap", mDefaultNormalMap, sampler); + } + + if (material->GetTexture(aiTextureType_EMISSIVE, 0, &emissivePath, nullptr, + nullptr, nullptr, nullptr, mapMode) == AI_SUCCESS) { + setTextureFromPath(scene, &mEngine, mTextures, emissivePath, + materialName, dirName, mapMode, "emissiveMap", outMaterials); + + } else { + outMaterials[materialName]->setParameter("emissiveMap", mDefaultMap, sampler); + outMaterials[materialName]->setParameter("emissiveFactor", mDefaultEmissive); + } + + //If the gltf has texture factors, override the default factor values + if (material->Get("$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0, metallicFactor) + == AI_SUCCESS) { + outMaterials[materialName]->setParameter("metallicFactor", metallicFactor); + } + + if (material->Get("$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0, roughnessFactor) + == AI_SUCCESS) { + outMaterials[materialName]->setParameter("roughnessFactor", roughnessFactor); + } + + if (material->Get(AI_MATKEY_COLOR_EMISSIVE, emissiveFactor) == AI_SUCCESS) { + emissiveFactorCast = *reinterpret_cast(&emissiveFactor); + outMaterials[materialName]->setParameter("emissiveFactor", emissiveFactorCast); + } + + if (material->Get("$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0, baseColorFactor) + == AI_SUCCESS) { + baseColorFactorCast = *reinterpret_cast(&baseColorFactor); + outMaterials[materialName]->setParameter("baseColorFactor", baseColorFactorCast); + } +} + + diff --git a/samples/app/MeshAssimp.h b/samples/app/MeshAssimp.h index 7aeb64a595..483cc1e85c 100644 --- a/samples/app/MeshAssimp.h +++ b/samples/app/MeshAssimp.h @@ -29,6 +29,7 @@ namespace filament { #include #include +#include #include #include #include @@ -39,6 +40,10 @@ namespace filament { #include #include #include +#include +#include +#include +#include class MeshAssimp { public: @@ -57,6 +62,11 @@ public: return mRenderables; } + //For use with normalizing coordinates + math::float3 minBound = math::float3(1.0f); + math::float3 maxBound = math::float3(-1.0f); + utils::Entity rootEntity; + private: struct Part { size_t offset; @@ -75,6 +85,7 @@ private: std::vector parts; filament::Box aabb; mat4f transform; + mat4f accTransform; }; bool setFromFile(const utils::Path& file, @@ -83,8 +94,14 @@ private: std::vector& outTangents, std::vector& outTexCoords, std::vector& outMeshes, - std::vector& outParents); + std::vector& outParents, + std::map& outMaterials); + void processGLTFMaterial(const aiScene* scene, const aiMaterial* material, + const std::string& materialName, const std::string& dirName, + std::map& outMaterials) const; + + filament::Texture* createOneByOneTexture(uint32_t textureData); filament::Engine& mEngine; filament::VertexBuffer* mVertexBuffer = nullptr; filament::IndexBuffer* mIndexBuffer = nullptr; @@ -92,7 +109,23 @@ private: filament::Material* mDefaultColorMaterial = nullptr; filament::Material* mDefaultTransparentColorMaterial = nullptr; + filament::Material* mGltfMaterial = nullptr; // Single sided gltf material + filament::Material* mGltfMaterialDS = nullptr; // Double sided gltf material + filament::Material* mGltfMaterialTrans = nullptr; // Transparent gltf material + filament::Material* mGltfMaterialDSTrans = nullptr; // Double sided Transparent gltf material + filament::Material* mGltfMaterialMasked = nullptr; // Transparent gltf material + filament::Material* mGltfMaterialDSMasked = nullptr; // Double sided Transparent gltf material + filament::Material* mGltfMaterialUnlit = nullptr; + filament::Material* mGltfMaterialDSUnlit = nullptr; + filament::Texture* mDefaultMap = nullptr; + filament::Texture* mDefaultNormalMap = nullptr; + float mDefaultMetallic = 0.0; + float mDefaultRoughness = 0.4; + filament::sRGBColor mDefaultEmissive = filament::sRGBColor({0.0f, 0.0f, 0.0f}); + std::vector mRenderables; + + std::vector mTextures; }; #endif // TNT_FILAMENT_SAMPLE_MESH_ASSIMP_H diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index a09251e580..5fc39037fb 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -139,15 +139,20 @@ static void setup(Engine* engine, View* view, Scene* scene) { for (auto renderable : g_meshSet->getRenderables()) { if (rcm.hasComponent(renderable)) { + auto instance = rcm.getInstance(renderable); + rcm.setCastShadows(instance, true); + rcm.setReceiveShadows(instance, true); scene->addEntity(renderable); } } g_light = EntityManager::get().create(); - LightManager::Builder(LightManager::Type::DIRECTIONAL) - .color(Color::toLinear({0.98f, 0.92f, 0.89f})) + LightManager::Builder(LightManager::Type::SUN) + .color(Color::toLinear(sRGBColor(0.98f, 0.92f, 0.89f))) .intensity(110000) - .direction({0.6, -1, -0.8}) + .direction({ 0.7, -1, -0.8 }) + .sunAngularRadius(1.9f) + .castShadows(true) .build(*engine, g_light); scene->addEntity(g_light); } diff --git a/samples/vk_hellopbr.cpp b/samples/vk_hellopbr.cpp index 66e3f23f75..1fe3c8852f 100644 --- a/samples/vk_hellopbr.cpp +++ b/samples/vk_hellopbr.cpp @@ -32,7 +32,7 @@ using Backend = Engine::Backend; struct App { utils::Entity light; std::map materials; - MeshAssimp* meshes; + MeshAssimp* meshes = nullptr; mat4f transform; }; @@ -65,7 +65,9 @@ int main(int argc, char** argv) { // Enable the metallic surface property to observe reflections. for (auto& pair : app.materials) { - pair.second->setParameter("metallic", 1.0f); + if (pair.second->getMaterial()->hasParameter("metallic")) { + pair.second->setParameter("metallic", 1.0f); + } } // Add light sources into the scene.