From 3f0c05ad9298cf0b7bc875c97badbd7bc229aa59 Mon Sep 17 00:00:00 2001 From: Max Van den Eynde Date: Mon, 20 Jul 2026 18:48:07 +0200 Subject: [PATCH 1/2] Added some progress bar for loading models --- atlas/object/model.cpp | 142 ++++++++++++++++++++----------- editor/views/editor/viewport.cpp | 27 +++++- include/atlas/object.h | 11 ++- include/atlas/runtime/context.h | 5 ++ runtime/lib/context.cpp | 14 ++- 5 files changed, 147 insertions(+), 52 deletions(-) diff --git a/atlas/object/model.cpp b/atlas/object/model.cpp index 9971574f..3d82ac5c 100644 --- a/atlas/object/model.cpp +++ b/atlas/object/model.cpp @@ -17,6 +17,7 @@ #include "atlas/window.h" #include "atlas/workspace.h" #include +#include #include #include #include @@ -36,13 +37,28 @@ #include namespace { +class ModelImportProgressHandler : public Assimp::ProgressHandler { + public: + explicit ModelImportProgressHandler( + std::function callback) + : callback(std::move(callback)) {} + + bool Update(float percentage = -1.0f) override { + if (callback) + callback(std::clamp(percentage, 0.0f, 1.0f) * 0.85f, + "Reading and optimizing model"); + return true; + } + + private: + std::function callback; +}; + glm::mat4 assimpToGlmMatrix(const aiMatrix4x4 &matrix) { return glm::transpose(glm::make_mat4(&matrix.a1)); } -float saturate(float value) { - return std::clamp(value, 0.0f, 1.0f); -} +float saturate(float value) { return std::clamp(value, 0.0f, 1.0f); } float luminance(const aiColor3D &color) { return (color.r * 0.2126f) + (color.g * 0.7152f) + (color.b * 0.0722f); @@ -59,20 +75,24 @@ float roughnessFromShininess(float shininess, float strength) { } void importMaterialProperties(aiMaterial *material, CoreObject &object) { - aiColor4D baseColor; - if (material->Get(AI_MATKEY_BASE_COLOR, baseColor) == AI_SUCCESS) { - object.material.albedo = {baseColor.r, baseColor.g, baseColor.b, - baseColor.a}; + aiColor3D diffuseColor; + + if (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor) == AI_SUCCESS) { + object.material.albedo.r = diffuseColor.r; + object.material.albedo.g = diffuseColor.g; + object.material.albedo.b = diffuseColor.b; } else { - aiColor3D diffuseColor; - if (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor) == - AI_SUCCESS) { - object.material.albedo.r = diffuseColor.r; - object.material.albedo.g = diffuseColor.g; - object.material.albedo.b = diffuseColor.b; + aiColor4D baseColor; + + if (material->Get(AI_MATKEY_BASE_COLOR, baseColor) == AI_SUCCESS) { + object.material.albedo = { + baseColor.r, + baseColor.g, + baseColor.b, + baseColor.a, + }; } } - float opacity = 1.0f; if (material->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { object.material.albedo.a = saturate(opacity); @@ -101,8 +121,7 @@ void importMaterialProperties(aiMaterial *material, CoreObject &object) { float shininess = 0.0f; if (material->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { float shininessStrength = 1.0f; - material->Get(AI_MATKEY_SHININESS_STRENGTH, - shininessStrength); + material->Get(AI_MATKEY_SHININESS_STRENGTH, shininessStrength); object.material.roughness = roughnessFromShininess(shininess, shininessStrength); } @@ -137,8 +156,7 @@ void importMaterialProperties(aiMaterial *material, CoreObject &object) { aiColor3D specularColor(0.0f, 0.0f, 0.0f); if (material->Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS) { - object.material.reflectivity = - saturate(luminance(specularColor)); + object.material.reflectivity = saturate(luminance(specularColor)); } } } @@ -146,7 +164,15 @@ void importMaterialProperties(aiMaterial *material, CoreObject &object) { void Model::fromResource(const Resource &resource) { loadModel(resource); } -void Model::loadModel(const Resource &resource) { +void Model::fromResource( + const Resource &resource, + const std::function &progress) { + loadModel(resource, progress); +} + +void Model::loadModel( + const Resource &resource, + const std::function &progress) { Assimp::Importer importer; if (resource.type != ResourceType::Model) { atlas_warning("Resource is not a model: " + resource.name); @@ -155,6 +181,11 @@ void Model::loadModel(const Resource &resource) { atlas_log("Loading model: " + resource.name); + if (progress) { + progress(0.0f, "Opening model"); + importer.SetProgressHandler(new ModelImportProgressHandler(progress)); + } + unsigned int importFlags = aiProcess_Triangulate | aiProcess_CalcTangentSpace | aiProcess_JoinIdenticalVertices | aiProcess_ImproveCacheLocality | @@ -171,11 +202,20 @@ void Model::loadModel(const Resource &resource) { return; } directory = resource.path.parent_path().string(); + importProgress = progress; + importedMeshCount = 0; + totalMeshCount = scene->mNumMeshes; + if (progress) + progress(0.88f, "Loading meshes and materials"); // Texture cache to avoid loading the same texture multiple times std::unordered_map textureCache; processNode(scene->mRootNode, scene, glm::mat4(1.0f), textureCache); + if (progress) + progress(1.0f, "Finishing import"); + importProgress = {}; + atlas_log("Model loaded successfully: " + resource.name + " (" + std::to_string(objects.size()) + " objects, " + std::to_string(scene->mNumMeshes) + " meshes)"); @@ -217,6 +257,13 @@ void Model::processNode( auto obj = std::make_shared( processMesh(mesh, scene, nodeTransform, textureCache)); this->objects.push_back(obj); + importedMeshCount++; + if (importProgress && totalMeshCount > 0) { + const float completed = static_cast(importedMeshCount) / + static_cast(totalMeshCount); + importProgress(0.88f + completed * 0.11f, + "Loading meshes and materials"); + } } for (unsigned int i = 0; i < node->mNumChildren; i++) { @@ -247,10 +294,9 @@ Model::processMesh(aiMesh *mesh, const aiScene *scene, // ---------- Normals ---------- if (mesh->mNormals) { - glm::vec3 normal = normalTransform * - glm::vec3(mesh->mNormals[i].x, - mesh->mNormals[i].y, - mesh->mNormals[i].z); + glm::vec3 normal = normalTransform * glm::vec3(mesh->mNormals[i].x, + mesh->mNormals[i].y, + mesh->mNormals[i].z); if (glm::length(normal) < 1e-6f) { normal = glm::vec3(0.0f, 1.0f, 0.0f); } else { @@ -265,25 +311,24 @@ Model::processMesh(aiMesh *mesh, const aiScene *scene, glm::vec3 normal = vertex.normal.toGlm(); glm::vec3 tangent(1.0f, 0.0f, 0.0f); if (mesh->mTangents) { - tangent = linearTransform * - glm::vec3(mesh->mTangents[i].x, mesh->mTangents[i].y, - mesh->mTangents[i].z); + tangent = linearTransform * glm::vec3(mesh->mTangents[i].x, + mesh->mTangents[i].y, + mesh->mTangents[i].z); } tangent = tangent - normal * glm::dot(normal, tangent); if (glm::length(tangent) < 1e-6f) { - glm::vec3 up = - std::abs(normal.y) < 0.999f ? glm::vec3(0.0f, 1.0f, 0.0f) - : glm::vec3(1.0f, 0.0f, 0.0f); + glm::vec3 up = std::abs(normal.y) < 0.999f + ? glm::vec3(0.0f, 1.0f, 0.0f) + : glm::vec3(1.0f, 0.0f, 0.0f); tangent = glm::cross(up, normal); } tangent = glm::normalize(tangent); glm::vec3 bitangent(0.0f, 0.0f, 1.0f); if (mesh->mBitangents) { - bitangent = linearTransform * - glm::vec3(mesh->mBitangents[i].x, - mesh->mBitangents[i].y, - mesh->mBitangents[i].z); + bitangent = linearTransform * glm::vec3(mesh->mBitangents[i].x, + mesh->mBitangents[i].y, + mesh->mBitangents[i].z); } else { bitangent = glm::cross(normal, tangent); } @@ -349,9 +394,9 @@ Model::processMesh(aiMesh *mesh, const aiScene *scene, loadMaterialTextures(material, std::any(aiTextureType_DIFFUSE), "texture_diffuse", textureCache); if (diffuseMaps.empty()) { - auto baseColorMaps = - loadMaterialTextures(material, std::any(aiTextureType_BASE_COLOR), - "texture_diffuse", textureCache); + auto baseColorMaps = loadMaterialTextures( + material, std::any(aiTextureType_BASE_COLOR), "texture_diffuse", + textureCache); diffuseMaps.insert(diffuseMaps.end(), baseColorMaps.begin(), baseColorMaps.end()); } @@ -381,11 +426,11 @@ Model::processMesh(aiMesh *mesh, const aiScene *scene, heightAsNormalMaps.end()); } if (normalMaps.empty()) { - auto displacementAsNormalMaps = - loadMaterialTextures(material, - std::any(aiTextureType_DISPLACEMENT), - "texture_normal", textureCache); - normalMaps.insert(normalMaps.end(), displacementAsNormalMaps.begin(), + auto displacementAsNormalMaps = loadMaterialTextures( + material, std::any(aiTextureType_DISPLACEMENT), + "texture_normal", textureCache); + normalMaps.insert(normalMaps.end(), + displacementAsNormalMaps.begin(), displacementAsNormalMaps.end()); } textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); @@ -406,19 +451,18 @@ Model::processMesh(aiMesh *mesh, const aiScene *scene, material, std::any(aiTextureType_AMBIENT_OCCLUSION), "texture_ao", textureCache); if (aoMaps.empty()) { - auto lightmapMaps = loadMaterialTextures( - material, std::any(aiTextureType_LIGHTMAP), "texture_ao", - textureCache); + auto lightmapMaps = + loadMaterialTextures(material, std::any(aiTextureType_LIGHTMAP), + "texture_ao", textureCache); aoMaps.insert(aoMaps.end(), lightmapMaps.begin(), lightmapMaps.end()); } textures.insert(textures.end(), aoMaps.begin(), aoMaps.end()); - auto opacityMaps = loadMaterialTextures( - material, std::any(aiTextureType_OPACITY), "texture_opacity", - textureCache); - textures.insert(textures.end(), opacityMaps.begin(), - opacityMaps.end()); + auto opacityMaps = + loadMaterialTextures(material, std::any(aiTextureType_OPACITY), + "texture_opacity", textureCache); + textures.insert(textures.end(), opacityMaps.begin(), opacityMaps.end()); } if (!textures.empty()) { diff --git a/editor/views/editor/viewport.cpp b/editor/views/editor/viewport.cpp index ebb3d7a9..5c5faf4d 100644 --- a/editor/views/editor/viewport.cpp +++ b/editor/views/editor/viewport.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1151,8 +1153,31 @@ bool ViewportPanel::importRuntimeModel(const QString &path) { {"rotation", QJsonArray{0.0, 0.0, 0.0}}, {"scale", QJsonArray{1.0, 1.0, 1.0}}, {"components", QJsonArray{}}}; + QProgressDialog progress(this); + progress.setWindowTitle(tr("Importing Model")); + progress.setLabelText(tr("Preparing %1…").arg(QFileInfo(path).fileName())); + progress.setCancelButton(nullptr); + progress.setRange(0, 100); + progress.setValue(0); + progress.setMinimumDuration(0); + progress.setWindowModality(Qt::WindowModal); + progress.show(); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + + const QString fileName = QFileInfo(path).fileName(); const int id = runtimeContext->pasteObjectDefinition( - QJsonDocument(definition).toJson(QJsonDocument::Compact).toStdString()); + QJsonDocument(definition).toJson(QJsonDocument::Compact).toStdString(), + [&progress, &fileName](float value, const std::string &status) { + const int percentage = + std::clamp(static_cast(std::round(value * 100.0f)), 0, 100); + progress.setValue(percentage); + progress.setLabelText(QString::fromStdString(status) + + QObject::tr("\n%1 — %2%") + .arg(fileName) + .arg(percentage)); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + }); + progress.setValue(100); if (id < 0) return false; refreshSceneSnapshot(); diff --git a/include/atlas/object.h b/include/atlas/object.h index 3b3c628c..74f6acbd 100644 --- a/include/atlas/object.h +++ b/include/atlas/object.h @@ -16,6 +16,7 @@ #include "photon/illuminate.h" #include #include +#include #include #include #include @@ -798,6 +799,9 @@ class Model : public GameObject { * @param resource The resource to load the model from. */ void fromResource(const Resource &resource); + void fromResource( + const Resource &resource, + const std::function &progress); /** * @brief Gets the objects that make up the model. @@ -1043,8 +1047,13 @@ class Model : public GameObject { private: std::vector> objects; std::string directory; + std::function importProgress; + unsigned int importedMeshCount = 0; + unsigned int totalMeshCount = 0; - void loadModel(const Resource &resource); + void loadModel( + const Resource &resource, + const std::function &progress = {}); void processNode(aiNode *node, const aiScene *scene, glm::mat4 parentTransform, std::unordered_map &textureCache); diff --git a/include/atlas/runtime/context.h b/include/atlas/runtime/context.h index 63aff29a..0b1023a9 100644 --- a/include/atlas/runtime/context.h +++ b/include/atlas/runtime/context.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -104,6 +105,7 @@ class Context { json editorEnvironmentData = json::object(); json editorPropertySyncs = json::array(); bool applyingPropertySyncs = false; + std::function modelImportProgress; std::vector> deletedObjectReferences; ProjectConfig config; @@ -149,6 +151,9 @@ class Context { int createObject(const std::string &type, const std::string &name); std::string objectDefinitionJson(int id) const; int pasteObjectDefinition(const std::string &definition); + int pasteObjectDefinition( + const std::string &definition, + const std::function &progress); bool saveCurrentScene(); bool openSceneFile(const std::string &path); std::string currentScenePath() const; diff --git a/runtime/lib/context.cpp b/runtime/lib/context.cpp index d20d5a83..17dc1494 100644 --- a/runtime/lib/context.cpp +++ b/runtime/lib/context.cpp @@ -3847,7 +3847,9 @@ createRenderable(Context &context, const json &objectData, auto object = std::make_shared(); object->fromResource(createRuntimeResource( - baseDir, source, ResourceType::Model, "runtime-model")); + baseDir, source, ResourceType::Model, + "runtime-model"), + context.modelImportProgress); registerGameObject(context, *object, objectData, normalizedType, generatedIndex); @@ -5639,6 +5641,16 @@ int Context::pasteObjectDefinition(const std::string &definition) { } } +int Context::pasteObjectDefinition( + const std::string &definition, + const std::function &progress) { + auto previousProgress = std::move(modelImportProgress); + modelImportProgress = progress; + const int result = pasteObjectDefinition(definition); + modelImportProgress = std::move(previousProgress); + return result; +} + bool Context::saveCurrentScene() { if (currentSceneFile.empty()) { return false; From d5312f5600b5ce0802763cbd7ddfdbce6c12480e Mon Sep 17 00:00:00 2001 From: Max Van den Eynde Date: Mon, 20 Jul 2026 19:11:42 +0200 Subject: [PATCH 2/2] Fixed in the renderer using new PBRPack texture type --- atlas/graphics/deferred.cpp | 2 + atlas/object/model.cpp | 147 +++++++++++++++------ editor/views/editor/materialEditor.cpp | 1 + include/atlas/core/default_shaders.h | 60 ++++++--- include/atlas/runtime/atlasScripts.h | 4 +- include/atlas/texture.h | 3 +- photon/gi.cpp | 24 +++- photon/path_tracing.cpp | 23 +++- runtime/atlas.d.ts | 1 + runtime/lib/context.cpp | 6 + runtime/lib/scripting.cpp | 4 + runtime/scripts/atlas_graphics.js | 1 + shaders/metal/deferred/deferred.frag.metal | 21 ++- shaders/metal/main.frag.metal | 21 ++- shaders/opengl/deferred/deferred.frag | 34 +++-- shaders/opengl/main.frag | 34 +++-- shaders/vulkan/deferred/deferred.frag | 34 +++-- shaders/vulkan/main.frag | 34 +++-- 18 files changed, 344 insertions(+), 110 deletions(-) diff --git a/atlas/graphics/deferred.cpp b/atlas/graphics/deferred.cpp index 747558e7..3aada700 100644 --- a/atlas/graphics/deferred.cpp +++ b/atlas/graphics/deferred.cpp @@ -972,6 +972,8 @@ void Window::deferredRendering( lightPipeline->bindTextureCubemap( "skybox", fallbackSkyboxTexture->textureID, boundTextures); } + lightPipeline->setUniformBool( + "useIBL", scene->skybox != nullptr && scene->skybox->cubemap.id != 0); boundTextures++; lightPipeline->setUniform1f( diff --git a/atlas/object/model.cpp b/atlas/object/model.cpp index 3d82ac5c..6657b7cb 100644 --- a/atlas/object/model.cpp +++ b/atlas/object/model.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "stb/stb_image.h" #include #include @@ -75,22 +76,16 @@ float roughnessFromShininess(float shininess, float strength) { } void importMaterialProperties(aiMaterial *material, CoreObject &object) { - aiColor3D diffuseColor; - - if (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor) == AI_SUCCESS) { - object.material.albedo.r = diffuseColor.r; - object.material.albedo.g = diffuseColor.g; - object.material.albedo.b = diffuseColor.b; + aiColor4D baseColor; + if (material->Get(AI_MATKEY_BASE_COLOR, baseColor) == AI_SUCCESS) { + object.material.albedo = {baseColor.r, baseColor.g, baseColor.b, + baseColor.a}; } else { - aiColor4D baseColor; - - if (material->Get(AI_MATKEY_BASE_COLOR, baseColor) == AI_SUCCESS) { - object.material.albedo = { - baseColor.r, - baseColor.g, - baseColor.b, - baseColor.a, - }; + aiColor3D diffuseColor; + if (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor) == AI_SUCCESS) { + object.material.albedo.r = diffuseColor.r; + object.material.albedo.g = diffuseColor.g; + object.material.albedo.b = diffuseColor.b; } } float opacity = 1.0f; @@ -435,29 +430,38 @@ Model::processMesh(aiMesh *mesh, const aiScene *scene, } textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); - auto metallicMaps = - loadMaterialTextures(material, std::any(aiTextureType_METALNESS), - "texture_metallic", textureCache); - textures.insert(textures.end(), metallicMaps.begin(), - metallicMaps.end()); - - auto roughnessMaps = loadMaterialTextures( - material, std::any(aiTextureType_DIFFUSE_ROUGHNESS), - "texture_roughness", textureCache); - textures.insert(textures.end(), roughnessMaps.begin(), - roughnessMaps.end()); - - auto aoMaps = loadMaterialTextures( - material, std::any(aiTextureType_AMBIENT_OCCLUSION), "texture_ao", - textureCache); - if (aoMaps.empty()) { - auto lightmapMaps = - loadMaterialTextures(material, std::any(aiTextureType_LIGHTMAP), - "texture_ao", textureCache); - aoMaps.insert(aoMaps.end(), lightmapMaps.begin(), - lightmapMaps.end()); + auto pbrPackMaps = loadMaterialTextures( + material, std::any(aiTextureType_GLTF_METALLIC_ROUGHNESS), + "texture_pbr_pack", textureCache); + textures.insert(textures.end(), pbrPackMaps.begin(), pbrPackMaps.end()); + + if (pbrPackMaps.empty()) { + auto metallicMaps = loadMaterialTextures( + material, std::any(aiTextureType_METALNESS), + "texture_metallic", textureCache); + textures.insert(textures.end(), metallicMaps.begin(), + metallicMaps.end()); + + auto roughnessMaps = loadMaterialTextures( + material, std::any(aiTextureType_DIFFUSE_ROUGHNESS), + "texture_roughness", textureCache); + textures.insert(textures.end(), roughnessMaps.begin(), + roughnessMaps.end()); + } + + if (pbrPackMaps.empty()) { + auto aoMaps = loadMaterialTextures( + material, std::any(aiTextureType_AMBIENT_OCCLUSION), + "texture_ao", textureCache); + if (aoMaps.empty()) { + auto lightmapMaps = loadMaterialTextures( + material, std::any(aiTextureType_LIGHTMAP), "texture_ao", + textureCache); + aoMaps.insert(aoMaps.end(), lightmapMaps.begin(), + lightmapMaps.end()); + } + textures.insert(textures.end(), aoMaps.begin(), aoMaps.end()); } - textures.insert(textures.end(), aoMaps.begin(), aoMaps.end()); auto opacityMaps = loadMaterialTextures(material, std::any(aiTextureType_OPACITY), @@ -502,6 +506,12 @@ std::vector Model::loadMaterialTextures( std::string filename = std::string(str.C_Str()); std::string fullPath = directory + "/" + filename; std::string cacheKey = fullPath + "|" + typeName; + if (typeName == "texture_pbr_pack") { + aiString aoPath; + if (material->GetTexture(aiTextureType_AMBIENT_OCCLUSION, 0, + &aoPath) == AI_SUCCESS) + cacheKey += "|" + std::string(aoPath.C_Str()); + } // Check if texture is already cached auto cacheIt = textureCache.find(cacheKey); @@ -534,10 +544,71 @@ std::vector Model::loadMaterialTextures( texType = TextureType::AO; } else if (typeName == "texture_opacity") { texType = TextureType::Opacity; + } else if (typeName == "texture_pbr_pack") { + texType = TextureType::PBRPack; } try { - Texture loadedTexture = Texture::fromResource(resource, texType); + Texture loadedTexture; + if (texType == TextureType::PBRPack) { + int width = 0; + int height = 0; + int channels = 0; + std::unique_ptr data( + stbi_load(fullPath.c_str(), &width, &height, &channels, + STBI_rgb_alpha), + stbi_image_free); + if (data == nullptr) + throw std::runtime_error("Failed to load PBR pack image"); + + const size_t pixelCount = static_cast(width) * height; + for (size_t pixel = 0; pixel < pixelCount; pixel++) + data.get()[pixel * 4] = 255; + + aiString aoPath; + if (material->GetTexture(aiTextureType_AMBIENT_OCCLUSION, 0, + &aoPath) == AI_SUCCESS) { + int aoWidth = 0; + int aoHeight = 0; + int aoChannels = 0; + const std::string fullAoPath = + directory + "/" + std::string(aoPath.C_Str()); + std::unique_ptr + aoData(stbi_load(fullAoPath.c_str(), &aoWidth, &aoHeight, + &aoChannels, STBI_grey), + stbi_image_free); + if (aoData != nullptr && aoWidth > 0 && aoHeight > 0) { + for (int y = 0; y < height; y++) { + const int aoY = y * aoHeight / height; + for (int x = 0; x < width; x++) { + const int aoX = x * aoWidth / width; + data.get()[(static_cast(y) * width + x) * + 4] = + aoData.get()[static_cast(aoY) * + aoWidth + + aoX]; + } + } + } + } + + auto opalTexture = opal::Texture::create( + opal::TextureType::Texture2D, opal::TextureFormat::Rgba8, + width, height, opal::TextureDataFormat::Rgba, data.get(), 1); + opalTexture->setParameters( + opal::TextureWrapMode::Repeat, + opal::TextureWrapMode::Repeat, + opal::TextureFilterMode::Linear, + opal::TextureFilterMode::Linear); + opalTexture->automaticallyGenerateMipmaps(); + loadedTexture = Texture{.resource = resource, + .creationData = {width, height, 4}, + .id = opalTexture->textureID, + .texture = opalTexture, + .type = texType}; + } else { + loadedTexture = Texture::fromResource(resource, texType); + } textureCache[cacheKey] = loadedTexture; textures.push_back(loadedTexture); } catch (const std::exception &ex) { diff --git a/editor/views/editor/materialEditor.cpp b/editor/views/editor/materialEditor.cpp index 0e66cf0c..ca2072db 100644 --- a/editor/views/editor/materialEditor.cpp +++ b/editor/views/editor/materialEditor.cpp @@ -626,6 +626,7 @@ void MaterialEditorPanel::showMaterial() { {"Metallic", "metallicTexture"}, {"Roughness", "roughnessTexture"}, {"Ambient Occlusion", "aoTexture"}, + {"PBR Pack", "pbrPackTexture"}, {"Opacity", "opacityTexture"}, {"Displacement", "displacementTexture"}}; for (const auto &[label, key] : materialSlots) { diff --git a/include/atlas/core/default_shaders.h b/include/atlas/core/default_shaders.h index f5cc83db..f7056622 100644 --- a/include/atlas/core/default_shaders.h +++ b/include/atlas/core/default_shaders.h @@ -1505,25 +1505,40 @@ fragment main0_out main0(main0_in in [[stage_in]], constant UBO& _46 [[buffer(0) normal = fast::normalize(in.Normal); } float3 albedoColor = baseColor.xyz; + int param_pbr_pack = 14; + float4 pbrPackTex = enableTextures(param_pbr_pack, _46, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); + bool hasPbrPack = any(pbrPackTex != float4(-1.0)); float metallicValue = material.metallic; int param_5 = 9; float4 metallicTex = enableTextures(param_5, _46, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(metallicTex != float4(-1.0))) + if (hasPbrPack) + { + metallicValue *= pbrPackTex.z; + } + else if (any(metallicTex != float4(-1.0))) { metallicValue *= metallicTex.x; } - float roughnessValue = material.roughness; + float roughnessValue = mater)", +R"(ial.roughness; int param_6 = 10; float4 roughnessTex = enableTextures(param_6, _46, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(roughnessTex != float4(-1.0))) + if (hasPbrPack) + { + roughnessValue *= pbrPackTex.y; + } + else if (any(roughnessTex != float4(-1.0))) { roughnessValue *= roughnessTex.x; } - float aoValue = material.ao)", -R"(; + float aoValue = material.ao; int param_7 = 11; float4 aoTex = enableTextures(param_7, _46, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(aoTex != float4(-1.0))) + if (hasPbrPack) + { + aoValue *= pbrPackTex.x; + } + else if (any(aoTex != float4(-1.0))) { aoValue *= aoTex.x; } @@ -5837,26 +5852,42 @@ fragment main0_out main0(main0_in in [[stage_in]], constant Uniforms& _163 [[buf discard_fragment(); } } + int param_pbr_pack = 14; + float4 pbrPackTex = enableTextures(param_pbr_pack, _163, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); + bool hasPbrPack = any(pbrPackTex != float4(-1.0)); float metallic = material.metallic; int param_4 = 9; float4 metallicTex = enableTextures(param_4, _163, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(metallicTex != float4(-1.0))) + if (hasPbrPack) + { + metallic *= pbrPackTex.z; + } + else if (any(metallicTex != float4(-1.0))) { metallic *= metallicTex.x; } float roughness = material.roughness; int param_5 = 10; float4 roughnessTex = enableTextures(param_5, _163, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(roughnessTex != float4(-1.0))) + if (hasPbrPack) + { + roughness *= pbrPackTex.y; + } + else if (any(roughnessTex != float4(-1.0))) { roughness *= roughnessTex.x; } float ao = material.ao; int param_6 = 11; float4 aoTex = enableTextures(param_6, _163, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(aoTex != float4(-1.0))) + if (hasPbrPack) { - ao *= aoTex.x; + ao *= pbrPackTex.x; + } + else if (any(aoTex != float4(-1.0))) + { + ao *= a)", +R"(oTex.x; } float3 F0 = float3(0.039999999105930328369140625); F0 = mix(F0, albedo, float3(metallic)); @@ -5872,8 +5903,7 @@ fragment main0_out main0(main0_in in [[stage_in]], constant Uniforms& _163 [[buf { float4 fragPosLightSpace = (_1905.shadowParams[i_1].lightProjection * _1905.shadowParams[i_1].lightView) * float4(in.FragPos, 1.0); ShadowParameters _1934; - )", -R"( _1934.lightView = _1905.shadowParams[i_1].lightView; + _1934.lightView = _1905.shadowParams[i_1].lightView; _1934.lightProjection = _1905.shadowParams[i_1].lightProjection; _1934.bias0 = _1905.shadowParams[i_1].bias0; _1934.textureIndex = _1905.shadowParams[i_1].textureIndex; @@ -6002,7 +6032,8 @@ R"( _1934.lightView = _1905.shadowParams[i_1].lightView; } float facing = _2144; float cosTheta = cos(radians(_2058.areaLights[i_2].angle)); - if ((facing >= cosTheta) && (facing > 0.0)) + if ((facing >= cosTh)", +R"(eta) && (facing > 0.0)) { float range = fast::max(_2058.areaLights[i_2].range, 0.001000000047497451305389404296875); float attenuation = 1.0 / ((1.0 + (dist / range)) + ((dist * dist) / (range * range))); @@ -6010,8 +6041,7 @@ R"( _1934.lightView = _1905.shadowParams[i_1].lightView; float3 radiance = (((float3(_2058.areaLights[i_2].diffuse) * fast::max(_2058.areaLights[i_2].intensity, 0.0)) * attenuation) * facing) * fade; float3 H = fast::normalize(V + L); float3 param_42 = N; - float3 pa)", -R"(ram_43 = H; + float3 param_43 = H; float param_44 = roughness; float NDF = distributionGGX(param_42, param_43, param_44); float3 param_45 = N; diff --git a/include/atlas/runtime/atlasScripts.h b/include/atlas/runtime/atlasScripts.h index 76bb8fce..11304118 100644 --- a/include/atlas/runtime/atlasScripts.h +++ b/include/atlas/runtime/atlasScripts.h @@ -26,8 +26,8 @@ static const char* const ATLAS_AUDIO_PARTS[] = { static const AtlasPackedScriptSource ATLAS_AUDIO = {ATLAS_AUDIO_PARTS, 1}; static const char* const ATLAS_GRAPHICS_PARTS[] = { - "import { Resource, ResourceType } from \"atlas\";\nimport { Color, Position3d, Size2d } from \"atlas/units\";\n\nexport const TextureType = Object.freeze({\n Color: 0,\n Specular: 1,\n Cubemap: 2,\n Depth: 3,\n DepthCube: 4,\n Normal: 5,\n Parallax: 6,\n SSAONoise: 7,\n SSAO: 8,\n Metallic: 9,\n Roughness: 10,\n AO: 11,\n Opacity: 12,\n HDR: 13,\n});\n\nexport const RenderTargetType = Object.freeze({\n Scene: 0,\n Multisampled: 1,\n Shadow: 2,\n CubeShadow: 3,\n GBuffer: 4,\n SSAO: 5,\n SSAOBlur: 6,\n});\n\nexport const RenderPassType = Object.freeze({\n Deferred: 0,\n Forward: 1,\n PathTracing: 2,\n});\n\nexport const Effects = Object.freeze({\n Inversion: { type: \"Inversion\" },\n Grayscale: { type: \"Grayscale\" },\n Sharpen: { type: \"Sharpen\" },\n Blur: { type: \"Blur\", magnitude: 16 },\n EdgeDetection: { type: \"EdgeDetection\" },\n ColorCorrection: {\n type: \"ColorCorrection\",\n exposure: 0,\n contrast: 1,\n saturation: 1,\n gamma: 1,\n temperature: 0,\n tint: 0,\n },\n MotionBlur: { type: \"MotionBlur\", size: 8, separation: 1 },\n ChromaticAberration: {\n type: \"ChromaticAberration\",\n red: 0.01,\n green: 0.006,\n blue: -0.006,\n direction: { x: 0, y: 0 },\n },\n Posterization: { type: \"Posterization\", levels: 5 },\n Pixelation: { type: \"Pixelation\", pixelSize: 8 },\n Dialation: { type: \"Dilation\", size: 8, separation: 1 },\n Dilation: { type: \"Dilation\", size: 8, separation: 1 },\n FilmGrain: { type: \"FilmGrain\", amount: 0.05 },\n});\n\nexport class Texture {\n constructor() {\n this.type = TextureType.Color;\n this.resource = new Resource(ResourceType.File, \"\", \"\");\n this.width = 0;\n this.height = 0;\n this.channels = 0;\n this.id = 0;\n this.borderColor = Color.black();\n }\n\n static fromResource(resource, type = TextureType.Color) {\n return globalThis.__atlasCreateTextureFromResource(resource, type);\n }\n\n static createEmpty(\n width,\n height,\n type = TextureType.Color,\n borderColor = new Color(0, 0, 0, 0),\n ) {\n return globalThis.__atlasCreateEmptyTexture(\n width,\n height,\n type,\n borderColor,\n );\n }\n\n static createColor(color, type = TextureType.Color, width = 1, height = 1) {\n return globalThis.__atlasCreateColorTexture(color, type, width, height);\n }\n\n createCheckerboard(width, height, checkSize, color1, color2) {\n return globalThis.__atlasCreateCheckerboardTexture(\n this,\n width,\n height,\n checkSize,\n color1,\n color2,\n );\n }\n\n createDoubleCheckerboard(\n width,\n height,\n checkSizeBig,\n checkSizeSmall,\n color1,\n color2,\n color3,\n ) {\n return globalThis.__atlasCreateDoubleCheckerboardTexture(\n this,\n width,\n height,\n checkSizeBig,\n checkSizeSmall,\n color1,\n color2,\n color3,\n );\n }\n\n displayToWindow() {\n return globalThis.__atlasDisplayTexture(this);\n }\n}\n\nexport class Cubemap {\n constructor(resources) {\n this.resources = resources;\n this.id = 0;\n return globalThis.__atlasCreateCubemap(resources);\n }\n\n getAverageColor() {\n return globalThis.__atlasGetCubemapAverageColor(this);\n }\n\n static fromResourceGroup(resourceGroup) {\n if (resourceGroup == null) {\n return null;\n }\n return globalThis.__atlasCreateCubemapFromGroup(resourceGroup.resources);\n }\n\n updateWithColors(colors) {\n return globalThis.__atlasUpdateCubemapWithColors(this, colors);\n }\n}\n\nexport class RenderTarget {\n constructor(type = RenderTargetType.Scene, resolution = 1024) {\n this.type = type;\n this.resolution = resolution;\n this.outTextures = [];\n this.depthTexture = null;\n return globalThis.__atlasCreateRenderTarget(type, resolution);\n }\n\n addEffect(effect) {\n return globalThis.__atlasAddRenderTargetEffect(this, effect);\n }\n\n addToPassQueue(type) {\n return globalThis.__atlasAddRenderTargetToPassQueue(this, type);\n }\n\n addToPass(type) {\n return this.addToPassQueue(type);\n }\n\n display() {\n return globalThis.__atlasDisplayRenderTarget(this);\n }\n}\n\nexport class Skybox {\n constructor(cubemap) {\n this.cubemap = cubemap;\n return globalThis.__atlasCreateSkybox(cubemap);\n }\n}\n\nexport class AmbientLight {\n constructor(color = Color.white(), intensity = 0.125) {\n this.color = color;\n this.intensity = intensity;\n }\n}\n\nexport class Light {\n constructor(\n position = Position3d.zero(),\n color = Color.white(),\n distance = 50,\n shineColor = Color.white(),\n intensity = 1,\n ) {\n this.position = position;\n this.color = color;\n this.shineColor = shineColor;\n this.intensity = intensity;\n this.distance = distance;\n return globalThis.__atlasCreatePointLight(this);\n }\n\n setColor(color) {\n this.color = color;\n return globalThis.__atlasUpdatePointLight(this);\n }\n\n createDebugObject() {\n return globalThis.__atlasCreatePointLightDebugObject(this);\n }\n\n castShadows(resolution = 2048) {\n return globalThis.__atlasCastPointLightShadows(this, resolution);\n }\n}\n\nexport class DirectionalLight {\n constructor(\n direction = Position3d.down(),\n color = Color.white(),\n shineColor = Color.white(),\n intensity = 1,\n ) {\n this.direction = direction;\n this.color = color;\n this.shineColor = shineColor;\n this.intensity = intensity;\n return globalThis.__atlasCreateDirectionalLight(this);\n }\n\n setColor(color) {\n this.color = color;\n return globalThis.__atlasUpdateDirectionalLight(this);\n }\n\n castShadows(resolution = 4096) {\n return globalThis.__atlasCastDirectionalLightShadows(\n this,\n resolution,\n );\n }\n}\n\nexport class SpotLight {\n constructor(\n position = Position3d.zero(),\n direction = Position3d.down(),\n color = Color.white(),\n cutOff = 35,\n outerCutOff = 40,\n shineColor = Color.white(),\n intensity = 1,\n range = 50,\n ) {\n this.position = position;\n this.direction = direction;\n this.color = color;\n this.shineColor = shineColor;\n this.range = range;\n this.cutOff = cutOff;\n this.outerCutOff = outerCutOff;\n this.intensity = intensity;\n return globalThis.__atlasCreateSpotLight(this);\n }\n\n setColor(color) {\n this.color = color;\n return globalThis.__atlasUpdateSpotLight(this);\n }\n\n createDebugObject() {\n return globalThis.__atlasCreateSpotLightDebugObject(this);\n }\n\n lookAt(target) {\n return globalThis.__atlasLookAtSpotLight(this, target);\n }\n\n castShadows(resolution = 2048) {\n return globalThis.__atlasCastSpotLightShadows(this, resolution);\n }\n}\n\nexport class AreaLight {\n constructor(\n position = Position3d.zero(),\n right = Position3d.right(),\n up = Position3d.up(),\n size = new Size2d(1, 1),\n color = Color.white(),\n shineColor = Color.white(),\n intensity = 1,\n range = 50,\n angle = 90,\n castsBothSides = false,\n rotation = Position3d.zero(),\n ) {\n this.position = position;\n this.right = right;\n this.up = up;\n this.size = size;\n this.color = color;\n this.shineColor = shineColor;\n this.intensity = intensity;\n this.range = range;\n this.angle = angle;\n this.castsBothSides = castsBothSides;\n this.rotation = rotation;\n return globalThis.__atlasCreateAreaLight(this);\n }\n\n getNormal() {\n return globalThis.__atlasGetAreaLightNormal(this);\n }\n\n", - " setColor(color) {\n this.color = color;\n return globalThis.__atlasUpdateAreaLight(this);\n }\n\n setRotation(rotation) {\n this.rotation = rotation;\n return globalThis.__atlasSetAreaLightRotation(this, rotation);\n }\n\n rotate(delta) {\n return globalThis.__atlasRotateAreaLight(this, delta);\n }\n\n createDebugObject() {\n return globalThis.__atlasCreateAreaLightDebugObject(this);\n }\n\n castShadows(resolution = 2048) {\n return globalThis.__atlasCastAreaLightShadows(this, resolution);\n }\n}\n", + "import { Resource, ResourceType } from \"atlas\";\nimport { Color, Position3d, Size2d } from \"atlas/units\";\n\nexport const TextureType = Object.freeze({\n Color: 0,\n Specular: 1,\n Cubemap: 2,\n Depth: 3,\n DepthCube: 4,\n Normal: 5,\n Parallax: 6,\n SSAONoise: 7,\n SSAO: 8,\n Metallic: 9,\n Roughness: 10,\n AO: 11,\n Opacity: 12,\n HDR: 13,\n PBRPack: 14,\n});\n\nexport const RenderTargetType = Object.freeze({\n Scene: 0,\n Multisampled: 1,\n Shadow: 2,\n CubeShadow: 3,\n GBuffer: 4,\n SSAO: 5,\n SSAOBlur: 6,\n});\n\nexport const RenderPassType = Object.freeze({\n Deferred: 0,\n Forward: 1,\n PathTracing: 2,\n});\n\nexport const Effects = Object.freeze({\n Inversion: { type: \"Inversion\" },\n Grayscale: { type: \"Grayscale\" },\n Sharpen: { type: \"Sharpen\" },\n Blur: { type: \"Blur\", magnitude: 16 },\n EdgeDetection: { type: \"EdgeDetection\" },\n ColorCorrection: {\n type: \"ColorCorrection\",\n exposure: 0,\n contrast: 1,\n saturation: 1,\n gamma: 1,\n temperature: 0,\n tint: 0,\n },\n MotionBlur: { type: \"MotionBlur\", size: 8, separation: 1 },\n ChromaticAberration: {\n type: \"ChromaticAberration\",\n red: 0.01,\n green: 0.006,\n blue: -0.006,\n direction: { x: 0, y: 0 },\n },\n Posterization: { type: \"Posterization\", levels: 5 },\n Pixelation: { type: \"Pixelation\", pixelSize: 8 },\n Dialation: { type: \"Dilation\", size: 8, separation: 1 },\n Dilation: { type: \"Dilation\", size: 8, separation: 1 },\n FilmGrain: { type: \"FilmGrain\", amount: 0.05 },\n});\n\nexport class Texture {\n constructor() {\n this.type = TextureType.Color;\n this.resource = new Resource(ResourceType.File, \"\", \"\");\n this.width = 0;\n this.height = 0;\n this.channels = 0;\n this.id = 0;\n this.borderColor = Color.black();\n }\n\n static fromResource(resource, type = TextureType.Color) {\n return globalThis.__atlasCreateTextureFromResource(resource, type);\n }\n\n static createEmpty(\n width,\n height,\n type = TextureType.Color,\n borderColor = new Color(0, 0, 0, 0),\n ) {\n return globalThis.__atlasCreateEmptyTexture(\n width,\n height,\n type,\n borderColor,\n );\n }\n\n static createColor(color, type = TextureType.Color, width = 1, height = 1) {\n return globalThis.__atlasCreateColorTexture(color, type, width, height);\n }\n\n createCheckerboard(width, height, checkSize, color1, color2) {\n return globalThis.__atlasCreateCheckerboardTexture(\n this,\n width,\n height,\n checkSize,\n color1,\n color2,\n );\n }\n\n createDoubleCheckerboard(\n width,\n height,\n checkSizeBig,\n checkSizeSmall,\n color1,\n color2,\n color3,\n ) {\n return globalThis.__atlasCreateDoubleCheckerboardTexture(\n this,\n width,\n height,\n checkSizeBig,\n checkSizeSmall,\n color1,\n color2,\n color3,\n );\n }\n\n displayToWindow() {\n return globalThis.__atlasDisplayTexture(this);\n }\n}\n\nexport class Cubemap {\n constructor(resources) {\n this.resources = resources;\n this.id = 0;\n return globalThis.__atlasCreateCubemap(resources);\n }\n\n getAverageColor() {\n return globalThis.__atlasGetCubemapAverageColor(this);\n }\n\n static fromResourceGroup(resourceGroup) {\n if (resourceGroup == null) {\n return null;\n }\n return globalThis.__atlasCreateCubemapFromGroup(resourceGroup.resources);\n }\n\n updateWithColors(colors) {\n return globalThis.__atlasUpdateCubemapWithColors(this, colors);\n }\n}\n\nexport class RenderTarget {\n constructor(type = RenderTargetType.Scene, resolution = 1024) {\n this.type = type;\n this.resolution = resolution;\n this.outTextures = [];\n this.depthTexture = null;\n return globalThis.__atlasCreateRenderTarget(type, resolution);\n }\n\n addEffect(effect) {\n return globalThis.__atlasAddRenderTargetEffect(this, effect);\n }\n\n addToPassQueue(type) {\n return globalThis.__atlasAddRenderTargetToPassQueue(this, type);\n }\n\n addToPass(type) {\n return this.addToPassQueue(type);\n }\n\n display() {\n return globalThis.__atlasDisplayRenderTarget(this);\n }\n}\n\nexport class Skybox {\n constructor(cubemap) {\n this.cubemap = cubemap;\n return globalThis.__atlasCreateSkybox(cubemap);\n }\n}\n\nexport class AmbientLight {\n constructor(color = Color.white(), intensity = 0.125) {\n this.color = color;\n this.intensity = intensity;\n }\n}\n\nexport class Light {\n constructor(\n position = Position3d.zero(),\n color = Color.white(),\n distance = 50,\n shineColor = Color.white(),\n intensity = 1,\n ) {\n this.position = position;\n this.color = color;\n this.shineColor = shineColor;\n this.intensity = intensity;\n this.distance = distance;\n return globalThis.__atlasCreatePointLight(this);\n }\n\n setColor(color) {\n this.color = color;\n return globalThis.__atlasUpdatePointLight(this);\n }\n\n createDebugObject() {\n return globalThis.__atlasCreatePointLightDebugObject(this);\n }\n\n castShadows(resolution = 2048) {\n return globalThis.__atlasCastPointLightShadows(this, resolution);\n }\n}\n\nexport class DirectionalLight {\n constructor(\n direction = Position3d.down(),\n color = Color.white(),\n shineColor = Color.white(),\n intensity = 1,\n ) {\n this.direction = direction;\n this.color = color;\n this.shineColor = shineColor;\n this.intensity = intensity;\n return globalThis.__atlasCreateDirectionalLight(this);\n }\n\n setColor(color) {\n this.color = color;\n return globalThis.__atlasUpdateDirectionalLight(this);\n }\n\n castShadows(resolution = 4096) {\n return globalThis.__atlasCastDirectionalLightShadows(\n this,\n resolution,\n );\n }\n}\n\nexport class SpotLight {\n constructor(\n position = Position3d.zero(),\n direction = Position3d.down(),\n color = Color.white(),\n cutOff = 35,\n outerCutOff = 40,\n shineColor = Color.white(),\n intensity = 1,\n range = 50,\n ) {\n this.position = position;\n this.direction = direction;\n this.color = color;\n this.shineColor = shineColor;\n this.range = range;\n this.cutOff = cutOff;\n this.outerCutOff = outerCutOff;\n this.intensity = intensity;\n return globalThis.__atlasCreateSpotLight(this);\n }\n\n setColor(color) {\n this.color = color;\n return globalThis.__atlasUpdateSpotLight(this);\n }\n\n createDebugObject() {\n return globalThis.__atlasCreateSpotLightDebugObject(this);\n }\n\n lookAt(target) {\n return globalThis.__atlasLookAtSpotLight(this, target);\n }\n\n castShadows(resolution = 2048) {\n return globalThis.__atlasCastSpotLightShadows(this, resolution);\n }\n}\n\nexport class AreaLight {\n constructor(\n position = Position3d.zero(),\n right = Position3d.right(),\n up = Position3d.up(),\n size = new Size2d(1, 1),\n color = Color.white(),\n shineColor = Color.white(),\n intensity = 1,\n range = 50,\n angle = 90,\n castsBothSides = false,\n rotation = Position3d.zero(),\n ) {\n this.position = position;\n this.right = right;\n this.up = up;\n this.size = size;\n this.color = color;\n this.shineColor = shineColor;\n this.intensity = intensity;\n this.range = range;\n this.angle = angle;\n this.castsBothSides = castsBothSides;\n this.rotation = rotation;\n return globalThis.__atlasCreateAreaLight(this);\n }\n\n getNormal() {\n return globalThis.__atlasGetAreaLightNorm", + "al(this);\n }\n\n setColor(color) {\n this.color = color;\n return globalThis.__atlasUpdateAreaLight(this);\n }\n\n setRotation(rotation) {\n this.rotation = rotation;\n return globalThis.__atlasSetAreaLightRotation(this, rotation);\n }\n\n rotate(delta) {\n return globalThis.__atlasRotateAreaLight(this, delta);\n }\n\n createDebugObject() {\n return globalThis.__atlasCreateAreaLightDebugObject(this);\n }\n\n castShadows(resolution = 2048) {\n return globalThis.__atlasCastAreaLightShadows(this, resolution);\n }\n}\n", }; static const AtlasPackedScriptSource ATLAS_GRAPHICS = {ATLAS_GRAPHICS_PARTS, 2}; diff --git a/include/atlas/texture.h b/include/atlas/texture.h index 7da12b7f..5034f3c9 100644 --- a/include/atlas/texture.h +++ b/include/atlas/texture.h @@ -128,7 +128,8 @@ enum class TextureType : int { Roughness = 10, AO = 11, Opacity = 12, - HDR = 13 + HDR = 13, + PBRPack = 14 }; /** diff --git a/photon/gi.cpp b/photon/gi.cpp index 19e91cee..3c7d88fd 100644 --- a/photon/gi.cpp +++ b/photon/gi.cpp @@ -359,14 +359,26 @@ void photon::GlobalIllumination::updateProbeLayout() { } } baseMaterial.normalTextureIndex = normalTextureIndex; + const int pbrPackTextureIndex = findTextureSlotForType( + object->textures, TextureType::PBRPack, materialTextures, + textureSlots); baseMaterial.metallicTextureIndex = - findTextureSlotForType(object->textures, TextureType::Metallic, - materialTextures, textureSlots); + pbrPackTextureIndex >= 0 + ? pbrPackTextureIndex + : findTextureSlotForType( + object->textures, TextureType::Metallic, + materialTextures, textureSlots); baseMaterial.roughnessTextureIndex = - findTextureSlotForType(object->textures, TextureType::Roughness, - materialTextures, textureSlots); - baseMaterial.aoTextureIndex = findTextureSlotForType( - object->textures, TextureType::AO, materialTextures, textureSlots); + pbrPackTextureIndex >= 0 + ? pbrPackTextureIndex + : findTextureSlotForType( + object->textures, TextureType::Roughness, + materialTextures, textureSlots); + baseMaterial.aoTextureIndex = + pbrPackTextureIndex >= 0 + ? pbrPackTextureIndex + : findTextureSlotForType(object->textures, TextureType::AO, + materialTextures, textureSlots); bool materialBound = false; int materialID = -1; diff --git a/photon/path_tracing.cpp b/photon/path_tracing.cpp index 3308fd75..193ff8d3 100644 --- a/photon/path_tracing.cpp +++ b/photon/path_tracing.cpp @@ -385,15 +385,26 @@ void photon::PathTracing::buildAccelerationStructure( } } data.normalTextureIndex = normalTextureIndex; + const int pbrPackTextureIndex = findTextureSlotForType( + object->textures, TextureType::PBRPack, materialTextures, + textureSlots); data.metallicTextureIndex = - findTextureSlotForType(object->textures, TextureType::Metallic, - materialTextures, textureSlots); + pbrPackTextureIndex >= 0 + ? pbrPackTextureIndex + : findTextureSlotForType( + object->textures, TextureType::Metallic, + materialTextures, textureSlots); data.roughnessTextureIndex = - findTextureSlotForType(object->textures, TextureType::Roughness, - materialTextures, textureSlots); + pbrPackTextureIndex >= 0 + ? pbrPackTextureIndex + : findTextureSlotForType( + object->textures, TextureType::Roughness, + materialTextures, textureSlots); data.aoTextureIndex = - findTextureSlotForType(object->textures, TextureType::AO, - materialTextures, textureSlots); + pbrPackTextureIndex >= 0 + ? pbrPackTextureIndex + : findTextureSlotForType(object->textures, TextureType::AO, + materialTextures, textureSlots); data.opacityTextureIndex = findTextureSlotForType(object->textures, TextureType::Opacity, materialTextures, textureSlots); diff --git a/runtime/atlas.d.ts b/runtime/atlas.d.ts index ab2b119e..e309cbeb 100644 --- a/runtime/atlas.d.ts +++ b/runtime/atlas.d.ts @@ -609,6 +609,7 @@ declare module "atlas/graphics" { AO, Opacity, HDR, + PBRPack, } export class Texture { diff --git a/runtime/lib/context.cpp b/runtime/lib/context.cpp index 17dc1494..8469b1b9 100644 --- a/runtime/lib/context.cpp +++ b/runtime/lib/context.cpp @@ -636,6 +636,10 @@ TextureType parseTextureTypeString(const std::string &value) { if (token == "ao" || token == "ambientocclusion") { return TextureType::AO; } + if (token == "pbrpack" || token == "orm" || + token == "metallicroughness") { + return TextureType::PBRPack; + } if (token == "opacity" || token == "alpha") { return TextureType::Opacity; } @@ -783,6 +787,8 @@ MaterialDefinition loadMaterialDefinition(const json &value, appendTexture({"roughnessTexture"}, TextureType::Roughness, false); appendTexture({"aoTexture", "ambientOcclusionTexture"}, TextureType::AO, false); + appendTexture({"pbrPackTexture", "ormTexture", "metallicRoughnessTexture"}, + TextureType::PBRPack, false); appendTexture({"opacityTexture", "alphaTexture"}, TextureType::Opacity, false); diff --git a/runtime/lib/scripting.cpp b/runtime/lib/scripting.cpp index 066cf3f9..c5a71f6b 100644 --- a/runtime/lib/scripting.cpp +++ b/runtime/lib/scripting.cpp @@ -318,6 +318,8 @@ TextureType toNativeTextureType(int type) { return TextureType::Opacity; case 13: return TextureType::HDR; + case 14: + return TextureType::PBRPack; case 0: default: return TextureType::Color; @@ -352,6 +354,8 @@ int toScriptTextureType(TextureType type) { return 12; case TextureType::HDR: return 13; + case TextureType::PBRPack: + return 14; case TextureType::Color: default: return 0; diff --git a/runtime/scripts/atlas_graphics.js b/runtime/scripts/atlas_graphics.js index 11ffe3f4..8bac607d 100644 --- a/runtime/scripts/atlas_graphics.js +++ b/runtime/scripts/atlas_graphics.js @@ -16,6 +16,7 @@ export const TextureType = Object.freeze({ AO: 11, Opacity: 12, HDR: 13, + PBRPack: 14, }); export const RenderTargetType = Object.freeze({ diff --git a/shaders/metal/deferred/deferred.frag.metal b/shaders/metal/deferred/deferred.frag.metal index 21284319..c1cd629c 100644 --- a/shaders/metal/deferred/deferred.frag.metal +++ b/shaders/metal/deferred/deferred.frag.metal @@ -342,24 +342,39 @@ fragment main0_out main0(main0_in in [[stage_in]], constant UBO& _46 [[buffer(0) normal = fast::normalize(in.Normal); } float3 albedoColor = baseColor.xyz; + int param_pbr_pack = 14; + float4 pbrPackTex = enableTextures(param_pbr_pack, _46, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); + bool hasPbrPack = any(pbrPackTex != float4(-1.0)); float metallicValue = material.metallic; int param_5 = 9; float4 metallicTex = enableTextures(param_5, _46, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(metallicTex != float4(-1.0))) + if (hasPbrPack) + { + metallicValue *= pbrPackTex.z; + } + else if (any(metallicTex != float4(-1.0))) { metallicValue *= metallicTex.x; } float roughnessValue = material.roughness; int param_6 = 10; float4 roughnessTex = enableTextures(param_6, _46, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(roughnessTex != float4(-1.0))) + if (hasPbrPack) + { + roughnessValue *= pbrPackTex.y; + } + else if (any(roughnessTex != float4(-1.0))) { roughnessValue *= roughnessTex.x; } float aoValue = material.ao; int param_7 = 11; float4 aoTex = enableTextures(param_7, _46, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(aoTex != float4(-1.0))) + if (hasPbrPack) + { + aoValue *= pbrPackTex.x; + } + else if (any(aoTex != float4(-1.0))) { aoValue *= aoTex.x; } diff --git a/shaders/metal/main.frag.metal b/shaders/metal/main.frag.metal index f67d7f7d..229e3702 100644 --- a/shaders/metal/main.frag.metal +++ b/shaders/metal/main.frag.metal @@ -1002,24 +1002,39 @@ fragment main0_out main0(main0_in in [[stage_in]], constant Uniforms& _163 [[buf discard_fragment(); } } + int param_pbr_pack = 14; + float4 pbrPackTex = enableTextures(param_pbr_pack, _163, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); + bool hasPbrPack = any(pbrPackTex != float4(-1.0)); float metallic = material.metallic; int param_4 = 9; float4 metallicTex = enableTextures(param_4, _163, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(metallicTex != float4(-1.0))) + if (hasPbrPack) + { + metallic *= pbrPackTex.z; + } + else if (any(metallicTex != float4(-1.0))) { metallic *= metallicTex.x; } float roughness = material.roughness; int param_5 = 10; float4 roughnessTex = enableTextures(param_5, _163, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(roughnessTex != float4(-1.0))) + if (hasPbrPack) + { + roughness *= pbrPackTex.y; + } + else if (any(roughnessTex != float4(-1.0))) { roughness *= roughnessTex.x; } float ao = material.ao; int param_6 = 11; float4 aoTex = enableTextures(param_6, _163, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - if (any(aoTex != float4(-1.0))) + if (hasPbrPack) + { + ao *= pbrPackTex.x; + } + else if (any(aoTex != float4(-1.0))) { ao *= aoTex.x; } diff --git a/shaders/opengl/deferred/deferred.frag b/shaders/opengl/deferred/deferred.frag index c1292e5d..e90a683c 100644 --- a/shaders/opengl/deferred/deferred.frag +++ b/shaders/opengl/deferred/deferred.frag @@ -18,6 +18,7 @@ const int TEXTURE_METALLIC = 9; const int TEXTURE_ROUGHNESS = 10; const int TEXTURE_AO = 11; const int TEXTURE_OPACITY = 12; +const int TEXTURE_PBR_PACK = 14; struct Material { vec3 albedo; @@ -163,22 +164,37 @@ void main() { vec3 albedo = baseColor.rgb; + vec4 pbrPackTex = enableTextures(TEXTURE_PBR_PACK); + bool hasPbrPack = pbrPackTex != vec4(-1.0); + float metallic = material.metallic; - vec4 metallicTex = enableTextures(TEXTURE_METALLIC); - if (metallicTex != vec4(-1.0)) { - metallic *= metallicTex.r; + if (hasPbrPack) { + metallic *= pbrPackTex.b; + } else { + vec4 metallicTex = enableTextures(TEXTURE_METALLIC); + if (metallicTex != vec4(-1.0)) { + metallic *= metallicTex.r; + } } float roughness = material.roughness; - vec4 roughnessTex = enableTextures(TEXTURE_ROUGHNESS); - if (roughnessTex != vec4(-1.0)) { - roughness *= roughnessTex.r; + if (hasPbrPack) { + roughness *= pbrPackTex.g; + } else { + vec4 roughnessTex = enableTextures(TEXTURE_ROUGHNESS); + if (roughnessTex != vec4(-1.0)) { + roughness *= roughnessTex.r; + } } float ao = material.ao; - vec4 aoTex = enableTextures(TEXTURE_AO); - if (aoTex != vec4(-1.0)) { - ao *= aoTex.r; + if (hasPbrPack) { + ao *= pbrPackTex.r; + } else { + vec4 aoTex = enableTextures(TEXTURE_AO); + if (aoTex != vec4(-1.0)) { + ao *= aoTex.r; + } } metallic = clamp(metallic, 0.0, 1.0); diff --git a/shaders/opengl/main.frag b/shaders/opengl/main.frag index aee88922..01c61739 100644 --- a/shaders/opengl/main.frag +++ b/shaders/opengl/main.frag @@ -18,6 +18,7 @@ const int TEXTURE_ROUGHNESS = 10; const int TEXTURE_AO = 11; const int TEXTURE_OPACITY = 12; const int TEXTURE_HDR_ENVIRONMENT = 13; +const int TEXTURE_PBR_PACK = 14; const float PI = 3.14159265; @@ -692,22 +693,37 @@ void main() { discard; } + vec4 pbrPackTex = enableTextures(TEXTURE_PBR_PACK); + bool hasPbrPack = pbrPackTex != vec4(-1.0); + float metallic = material.metallic; - vec4 metallicTex = enableTextures(TEXTURE_METALLIC); - if (metallicTex != vec4(-1.0)) { - metallic *= metallicTex.r; + if (hasPbrPack) { + metallic *= pbrPackTex.b; + } else { + vec4 metallicTex = enableTextures(TEXTURE_METALLIC); + if (metallicTex != vec4(-1.0)) { + metallic *= metallicTex.r; + } } float roughness = material.roughness; - vec4 roughnessTex = enableTextures(TEXTURE_ROUGHNESS); - if (roughnessTex != vec4(-1.0)) { - roughness *= roughnessTex.r; + if (hasPbrPack) { + roughness *= pbrPackTex.g; + } else { + vec4 roughnessTex = enableTextures(TEXTURE_ROUGHNESS); + if (roughnessTex != vec4(-1.0)) { + roughness *= roughnessTex.r; + } } float ao = material.ao; - vec4 aoTex = enableTextures(TEXTURE_AO); - if (aoTex != vec4(-1.0)) { - ao *= aoTex.r; + if (hasPbrPack) { + ao *= pbrPackTex.r; + } else { + vec4 aoTex = enableTextures(TEXTURE_AO); + if (aoTex != vec4(-1.0)) { + ao *= aoTex.r; + } } vec3 F0 = vec3(0.04); diff --git a/shaders/vulkan/deferred/deferred.frag b/shaders/vulkan/deferred/deferred.frag index caf5084b..0be51cc7 100644 --- a/shaders/vulkan/deferred/deferred.frag +++ b/shaders/vulkan/deferred/deferred.frag @@ -18,6 +18,7 @@ const int TEXTURE_METALLIC = 9; const int TEXTURE_ROUGHNESS = 10; const int TEXTURE_AO = 11; const int TEXTURE_OPACITY = 12; +const int TEXTURE_PBR_PACK = 14; layout(set = 2, binding = 0) uniform sampler2D texture1; layout(set = 2, binding = 1) uniform sampler2D texture2; @@ -175,22 +176,37 @@ void main() { vec3 albedoColor = baseColor.rgb; + vec4 pbrPackTex = enableTextures(TEXTURE_PBR_PACK); + bool hasPbrPack = pbrPackTex != vec4(-1.0); + float metallicValue = material.metallic; - vec4 metallicTex = enableTextures(TEXTURE_METALLIC); - if (metallicTex != vec4(-1.0)) { - metallicValue *= metallicTex.r; + if (hasPbrPack) { + metallicValue *= pbrPackTex.b; + } else { + vec4 metallicTex = enableTextures(TEXTURE_METALLIC); + if (metallicTex != vec4(-1.0)) { + metallicValue *= metallicTex.r; + } } float roughnessValue = material.roughness; - vec4 roughnessTex = enableTextures(TEXTURE_ROUGHNESS); - if (roughnessTex != vec4(-1.0)) { - roughnessValue *= roughnessTex.r; + if (hasPbrPack) { + roughnessValue *= pbrPackTex.g; + } else { + vec4 roughnessTex = enableTextures(TEXTURE_ROUGHNESS); + if (roughnessTex != vec4(-1.0)) { + roughnessValue *= roughnessTex.r; + } } float aoValue = material.ao; - vec4 aoTex = enableTextures(TEXTURE_AO); - if (aoTex != vec4(-1.0)) { - aoValue *= aoTex.r; + if (hasPbrPack) { + aoValue *= pbrPackTex.r; + } else { + vec4 aoTex = enableTextures(TEXTURE_AO); + if (aoTex != vec4(-1.0)) { + aoValue *= aoTex.r; + } } metallicValue = clamp(metallicValue, 0.0, 1.0); diff --git a/shaders/vulkan/main.frag b/shaders/vulkan/main.frag index 70fc642a..1e23e992 100644 --- a/shaders/vulkan/main.frag +++ b/shaders/vulkan/main.frag @@ -19,6 +19,7 @@ const int TEXTURE_ROUGHNESS = 10; const int TEXTURE_AO = 11; const int TEXTURE_OPACITY = 12; const int TEXTURE_HDR_ENVIRONMENT = 13; +const int TEXTURE_PBR_PACK = 14; const float PI = 3.14159265; @@ -722,22 +723,37 @@ void main() { discard; } + vec4 pbrPackTex = enableTextures(TEXTURE_PBR_PACK); + bool hasPbrPack = pbrPackTex != vec4(-1.0); + float metallic = material.metallic; - vec4 metallicTex = enableTextures(TEXTURE_METALLIC); - if (metallicTex != vec4(-1.0)) { - metallic *= metallicTex.r; + if (hasPbrPack) { + metallic *= pbrPackTex.b; + } else { + vec4 metallicTex = enableTextures(TEXTURE_METALLIC); + if (metallicTex != vec4(-1.0)) { + metallic *= metallicTex.r; + } } float roughness = material.roughness; - vec4 roughnessTex = enableTextures(TEXTURE_ROUGHNESS); - if (roughnessTex != vec4(-1.0)) { - roughness *= roughnessTex.r; + if (hasPbrPack) { + roughness *= pbrPackTex.g; + } else { + vec4 roughnessTex = enableTextures(TEXTURE_ROUGHNESS); + if (roughnessTex != vec4(-1.0)) { + roughness *= roughnessTex.r; + } } float ao = material.ao; - vec4 aoTex = enableTextures(TEXTURE_AO); - if (aoTex != vec4(-1.0)) { - ao *= aoTex.r; + if (hasPbrPack) { + ao *= pbrPackTex.r; + } else { + vec4 aoTex = enableTextures(TEXTURE_AO); + if (aoTex != vec4(-1.0)) { + ao *= aoTex.r; + } } vec3 F0 = vec3(0.04);