diff --git a/atlas/application/window.cpp b/atlas/application/window.cpp index a094d0e1..57cd7b5a 100644 --- a/atlas/application/window.cpp +++ b/atlas/application/window.cpp @@ -1703,6 +1703,10 @@ bool Window::stepFrame() { model->material.useNormalMap; meshObject->material.normalMapStrength = model->material.normalMapStrength; + meshObject->material.textureScale = + model->material.textureScale; + meshObject->material.textureOffset = + model->material.textureOffset; meshObject->useDeferredRendering = model->useDeferredRendering; if (meshObject->canUseDeferredRendering()) { diff --git a/atlas/graphics/deferred.cpp b/atlas/graphics/deferred.cpp index 8fd8c89c..747558e7 100644 --- a/atlas/graphics/deferred.cpp +++ b/atlas/graphics/deferred.cpp @@ -358,6 +358,10 @@ void Window::deferredRendering( model->material.useNormalMap; meshRenderable->material.normalMapStrength = model->material.normalMapStrength; + meshRenderable->material.textureScale = + model->material.textureScale; + meshRenderable->material.textureOffset = + model->material.textureOffset; meshRenderable->useDeferredRendering = model->useDeferredRendering; if (!meshRenderable->canUseDeferredRendering()) { diff --git a/atlas/object/core_object.cpp b/atlas/object/core_object.cpp index a6039973..0dea4be8 100644 --- a/atlas/object/core_object.cpp +++ b/atlas/object/core_object.cpp @@ -620,6 +620,10 @@ void CoreObject::render(float dt, material.normalMapStrength); this->pipeline->setUniform1i("useNormalMap", material.useNormalMap ? 1 : 0); + this->pipeline->setUniform2f("textureScale", material.textureScale[0], + material.textureScale[1]); + this->pipeline->setUniform2f("textureOffset", material.textureOffset[0], + material.textureOffset[1]); if (Window::mainWindow != nullptr && Window::mainWindow->getCamera() != nullptr) { this->pipeline->setUniform3f( diff --git a/editor/views/editor/materialEditor.cpp b/editor/views/editor/materialEditor.cpp index 4bae91be..0e66cf0c 100644 --- a/editor/views/editor/materialEditor.cpp +++ b/editor/views/editor/materialEditor.cpp @@ -46,12 +46,11 @@ QColor jsonColor(const QJsonValue &value, const QColor &fallback) { if (array.size() < 3) { return fallback; } - return QColor::fromRgbF(std::clamp(array.at(0).toDouble(), 0.0, 1.0), - std::clamp(array.at(1).toDouble(), 0.0, 1.0), - std::clamp(array.at(2).toDouble(), 0.0, 1.0), - array.size() > 3 - ? std::clamp(array.at(3).toDouble(), 0.0, 1.0) - : 1.0); + return QColor::fromRgbF( + std::clamp(array.at(0).toDouble(), 0.0, 1.0), + std::clamp(array.at(1).toDouble(), 0.0, 1.0), + std::clamp(array.at(2).toDouble(), 0.0, 1.0), + array.size() > 3 ? std::clamp(array.at(3).toDouble(), 0.0, 1.0) : 1.0); } QJsonArray colorJson(const QColor &color) { @@ -61,9 +60,9 @@ QJsonArray colorJson(const QColor &color) { void displayColor(QPushButton *button, const QColor &color) { button->setProperty("materialColor", color); button->setObjectName("materialColorButton"); - button->setText(color.name(color.alpha() < 255 ? QColor::HexArgb - : QColor::HexRgb) - .toUpper()); + button->setText( + color.name(color.alpha() < 255 ? QColor::HexArgb : QColor::HexRgb) + .toUpper()); button->setIcon(styling::colorSwatch(color, QSize(18, 18))); button->setIconSize(QSize(18, 18)); } @@ -85,8 +84,7 @@ QString texturePath(const QJsonValue &value) { } if (value.isObject()) { const QJsonObject object = value.toObject(); - return object.value("path").toString( - object.value("source").toString()); + return object.value("path").toString(object.value("source").toString()); } return {}; } @@ -104,14 +102,19 @@ QImage loadTextureImage(const QString &baseDir, const QJsonValue &value) { return path.isEmpty() ? QImage() : QImage(path); } +double arrayValue(const QJsonValue &value, int index, double fallback) { + const QJsonArray array = value.toArray(); + return array.size() > index ? array.at(index).toDouble(fallback) : fallback; +} + double channelAt(const QImage &image, double u, double v) { if (image.isNull()) { return 1.0; } - const int x = std::clamp(static_cast(u * image.width()), 0, - image.width() - 1); - const int y = std::clamp(static_cast(v * image.height()), 0, - image.height() - 1); + const int x = + std::clamp(static_cast(u * image.width()), 0, image.width() - 1); + const int y = + std::clamp(static_cast(v * image.height()), 0, image.height() - 1); return QColor::fromRgba(image.pixel(x, y)).lightnessF(); } @@ -120,13 +123,13 @@ QColor imageAt(const QImage &image, double u, double v, if (image.isNull()) { return fallback; } - const int x = std::clamp(static_cast(u * image.width()), 0, - image.width() - 1); - const int y = std::clamp(static_cast(v * image.height()), 0, - image.height() - 1); + const int x = + std::clamp(static_cast(u * image.width()), 0, image.width() - 1); + const int y = + std::clamp(static_cast(v * image.height()), 0, image.height() - 1); return QColor::fromRgba(image.pixel(x, y)); } -} +} // namespace class MaterialPreviewWidget : public QWidget { public: @@ -149,6 +152,12 @@ class MaterialPreviewWidget : public QWidget { roughnessImage = loadTextureImage(baseDir, material.value("roughnessTexture")); aoImage = loadTextureImage(baseDir, material.value("aoTexture")); + displacementImage = + loadTextureImage(baseDir, material.value("displacementTexture")); + textureScaleU = arrayValue(material.value("textureScale"), 0, 1.0); + textureScaleV = arrayValue(material.value("textureScale"), 1, 1.0); + textureOffsetU = arrayValue(material.value("textureOffset"), 0, 0.0); + textureOffsetV = arrayValue(material.value("textureOffset"), 1, 0.0); update(); } @@ -176,17 +185,16 @@ class MaterialPreviewWidget : public QWidget { std::clamp(material.value("roughness").toDouble(0.5), 0.02, 1.0); const double ao = std::clamp(material.value("ao").toDouble(1.0), 0.0, 1.0); - const double reflectivity = std::clamp( - material.value("reflectivity").toDouble(0.5), 0.0, 1.0); - const double emissionStrength = std::max( - 0.0, material.value("emissiveIntensity").toDouble(0.0)); - const double transmission = std::clamp( - material.value("transmittance").toDouble(0.0), 0.0, 1.0); + const double reflectivity = + std::clamp(material.value("reflectivity").toDouble(0.5), 0.0, 1.0); + const double emissionStrength = + std::max(0.0, material.value("emissiveIntensity").toDouble(0.0)); + const double transmission = + std::clamp(material.value("transmittance").toDouble(0.0), 0.0, 1.0); const double normalStrength = std::clamp( material.value("normalMapStrength").toDouble(1.0), 0.0, 4.0); - const bool useNormal = - material.value("useNormalMap").toBool(true) && - !normalImage.isNull(); + const bool useNormal = material.value("useNormalMap").toBool(true) && + !normalImage.isNull(); const double cx = widthPixels * 0.5; const double cy = heightPixels * 0.5; const double radius = std::min(widthPixels, heightPixels) * 0.39; @@ -211,12 +219,14 @@ class MaterialPreviewWidget : public QWidget { double nx = px; double ny = py; double nz = std::sqrt(std::max(0.0, 1.0 - rr)); - double u = std::atan2(nx, nz) / - (2.0 * std::numbers::pi_v) + - 0.5; - double v = 0.5 - - std::asin(std::clamp(ny, -1.0, 1.0)) / - std::numbers::pi_v; + double u = + std::atan2(nx, nz) / (2.0 * std::numbers::pi_v)+0.5; + double v = 0.5 - std::asin(std::clamp(ny, -1.0, 1.0)) / + std::numbers::pi_v; + u = u * textureScaleU + textureOffsetU; + v = v * textureScaleV + textureOffsetV; + u -= std::floor(u); + v -= std::floor(v); if (useNormal) { const QColor sampled = imageAt(normalImage, u, v, QColor(128, 128, 255)); @@ -224,7 +234,8 @@ class MaterialPreviewWidget : public QWidget { const double ty = sampled.greenF() * 2.0 - 1.0; nx += tx * normalStrength * 0.28; ny += ty * normalStrength * 0.28; - const double length = std::sqrt(nx * nx + ny * ny + nz * nz); + const double length = + std::sqrt(nx * nx + ny * ny + nz * nz); nx /= length; ny /= length; nz /= length; @@ -238,37 +249,36 @@ class MaterialPreviewWidget : public QWidget { roughness * channelAt(roughnessImage, u, v), 0.02, 1.0); const double localAo = std::clamp(ao * channelAt(aoImage, u, v), 0.0, 1.0); - const double diffuse = std::max(0.0, nx * lx + ny * ly + nz * lz); + const double diffuse = + std::max(0.0, nx * lx + ny * ly + nz * lz); const double hx = lx; const double hy = ly; const double hz = lz + 1.0; const double hlen = std::sqrt(hx * hx + hy * hy + hz * hz); - const double ndh = std::max( - 0.0, (nx * hx + ny * hy + nz * hz) / hlen); - const double exponent = 4.0 + - (1.0 - localRoughness) * - (1.0 - localRoughness) * 252.0; - const double specular = - std::pow(ndh, exponent) * - (0.12 + reflectivity * 0.88) * - (0.35 + localMetallic * 0.65); + const double ndh = + std::max(0.0, (nx * hx + ny * hy + nz * hz) / hlen); + const double exponent = 4.0 + (1.0 - localRoughness) * + (1.0 - localRoughness) * + 252.0; + const double specular = std::pow(ndh, exponent) * + (0.12 + reflectivity * 0.88) * + (0.35 + localMetallic * 0.65); const double fresnel = std::pow(1.0 - std::clamp(nz, 0.0, 1.0), 5.0); - const double light = localAo * 0.17 + - diffuse * (0.83 - localMetallic * 0.38); + const double light = + localAo * 0.17 + diffuse * (0.83 - localMetallic * 0.38); const double edgeTransmission = transmission * (0.2 + fresnel * 0.55); const double rx = 2.0 * nx * nz; const double ry = 2.0 * ny * nz; const QColor reflected = environmentAt(rx, ry); - const double reflectionWeight = std::clamp( - reflectivity * (0.12 + localMetallic * 0.88) * - (1.0 - localRoughness * 0.72) + - fresnel * 0.24, - 0.0, 0.92); - auto output = [&](double base, double texture, - double emitted, double environment, - double behind) { + const double reflectionWeight = + std::clamp(reflectivity * (0.12 + localMetallic * 0.88) * + (1.0 - localRoughness * 0.72) + + fresnel * 0.24, + 0.0, 0.92); + auto output = [&](double base, double texture, double emitted, + double environment, double behind) { double surface = base * texture * light + specular + fresnel * reflectivity * 0.18; surface = surface * (1.0 - reflectionWeight) + @@ -279,21 +289,18 @@ class MaterialPreviewWidget : public QWidget { 0.0, 1.0); }; line[x] = qRgba( - static_cast(output(albedo.redF(), - sampledAlbedo.redF(), + static_cast(output(albedo.redF(), sampledAlbedo.redF(), emission.redF(), reflected.redF(), background.redF()) * 255.0), - static_cast(output(albedo.greenF(), - sampledAlbedo.greenF(), - emission.greenF(), - reflected.greenF(), - background.greenF()) * - 255.0), + static_cast( + output(albedo.greenF(), sampledAlbedo.greenF(), + emission.greenF(), reflected.greenF(), + background.greenF()) * + 255.0), static_cast(output(albedo.blueF(), sampledAlbedo.blueF(), - emission.blueF(), - reflected.blueF(), + emission.blueF(), reflected.blueF(), background.blueF()) * 255.0), 255); @@ -341,6 +348,11 @@ class MaterialPreviewWidget : public QWidget { QImage metallicImage; QImage roughnessImage; QImage aoImage; + QImage displacementImage; + double textureScaleU = 1.0; + double textureScaleV = 1.0; + double textureOffsetU = 0.0; + double textureOffsetV = 0.0; int environmentMode = 0; }; @@ -362,12 +374,10 @@ MaterialEditorPanel::MaterialEditorPanel(ViewportPanel *viewport, statusLabel->setObjectName("materialEditorStatus"); auto *saveButton = new QPushButton("Save", header); saveButton->setObjectName("materialSaveButton"); - saveButton->setIcon( - styling::icon(styling::Icon::FloppyDisk, "#A1957D")); + saveButton->setIcon(styling::icon(styling::Icon::FloppyDisk, "#A1957D")); auto *assignButton = new QPushButton("Assign to Selected", header); assignButton->setObjectName("materialAssignButton"); - assignButton->setIcon( - styling::icon(styling::Icon::Assign, "#9E897D")); + assignButton->setIcon(styling::icon(styling::Icon::Assign, "#9E897D")); headerLayout->addWidget(titleLabel, 1); headerLayout->addWidget(statusLabel); headerLayout->addWidget(assignButton); @@ -425,6 +435,10 @@ MaterialEditorPanel::normalizedMaterial(const QJsonObject &source) const { result.insert("normalMapStrength", 1.0); if (!result.value("useNormalMap").isBool()) result.insert("useNormalMap", true); + if (!result.value("textureScale").isArray()) + result.insert("textureScale", QJsonArray{1.0, 1.0}); + if (!result.value("textureOffset").isArray()) + result.insert("textureOffset", QJsonArray{0.0, 0.0}); if (!result.value("transmittance").isDouble()) result.insert("transmittance", 0.0); if (!result.value("ior").isDouble()) @@ -443,7 +457,8 @@ void MaterialEditorPanel::openMaterial(const QString &path) { return; } QJsonParseError error; - const QJsonDocument document = QJsonDocument::fromJson(file.readAll(), &error); + const QJsonDocument document = + QJsonDocument::fromJson(file.readAll(), &error); if (error.error != QJsonParseError::NoError || !document.isObject()) { QMessageBox::warning(this, "Material Editor", "The material file is not valid JSON."); @@ -566,8 +581,7 @@ void MaterialEditorPanel::showMaterial() { auto *volumeForm = new QFormLayout(volume); transmittanceField = scalarField(0.0, 1.0, 0.01, volume); iorField = scalarField(1.0, 3.0, 0.01, volume); - transmittanceField->setValue( - material.value("transmittance").toDouble()); + transmittanceField->setValue(material.value("transmittance").toDouble()); iorField->setValue(material.value("ior").toDouble()); volumeForm->addRow("Weight", transmittanceField); volumeForm->addRow("IOR", iorField); @@ -584,12 +598,36 @@ void MaterialEditorPanel::showMaterial() { normalForm->addRow("Strength", normalStrengthField); propertiesLayout->addWidget(normal); + auto *tiling = new QGroupBox("Texture Mapping", properties); + auto *tilingForm = new QFormLayout(tiling); + textureScaleUField = scalarField(-100.0, 100.0, 0.1, tiling); + textureScaleVField = scalarField(-100.0, 100.0, 0.1, tiling); + textureOffsetUField = scalarField(-100.0, 100.0, 0.05, tiling); + textureOffsetVField = scalarField(-100.0, 100.0, 0.05, tiling); + textureScaleUField->setValue( + arrayValue(material.value("textureScale"), 0, 1.0)); + textureScaleVField->setValue( + arrayValue(material.value("textureScale"), 1, 1.0)); + textureOffsetUField->setValue( + arrayValue(material.value("textureOffset"), 0, 0.0)); + textureOffsetVField->setValue( + arrayValue(material.value("textureOffset"), 1, 0.0)); + tilingForm->addRow("Tiling U", textureScaleUField); + tilingForm->addRow("Tiling V", textureScaleVField); + tilingForm->addRow("Offset U", textureOffsetUField); + tilingForm->addRow("Offset V", textureOffsetVField); + propertiesLayout->addWidget(tiling); + auto *textures = new QGroupBox("Texture Slots", properties); auto *textureLayout = new QVBoxLayout(textures); const QList> materialSlots{ - {"Base Color", "albedoTexture"}, {"Normal", "normalTexture"}, - {"Metallic", "metallicTexture"}, {"Roughness", "roughnessTexture"}, - {"Ambient Occlusion", "aoTexture"}, {"Opacity", "opacityTexture"}}; + {"Base Color", "albedoTexture"}, + {"Normal", "normalTexture"}, + {"Metallic", "metallicTexture"}, + {"Roughness", "roughnessTexture"}, + {"Ambient Occlusion", "aoTexture"}, + {"Opacity", "opacityTexture"}, + {"Displacement", "displacementTexture"}}; for (const auto &[label, key] : materialSlots) { auto *row = new QWidget(textures); row->setObjectName("materialTextureSlot"); @@ -605,8 +643,7 @@ void MaterialEditorPanel::showMaterial() { field->setPlaceholderText("No image"); auto *choose = new QToolButton(row); choose->setText("Choose…"); - choose->setIcon( - styling::icon(styling::Icon::FolderOpen, "#7E929C")); + choose->setIcon(styling::icon(styling::Icon::FolderOpen, "#7E929C")); choose->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); auto *clear = new QToolButton(row); clear->setIcon(styling::icon(styling::Icon::Close, "#A17F7F")); @@ -639,11 +676,18 @@ void MaterialEditorPanel::showMaterial() { [this] { setColor("albedo", albedoButton); }); connect(emissiveButton, &QPushButton::clicked, this, [this] { setColor("emissiveColor", emissiveButton); }); - const QList scalars{ - metallicField, roughnessField, aoField, - reflectivityField, emissiveIntensityField, - normalStrengthField, transmittanceField, - iorField}; + const QList scalars{metallicField, + roughnessField, + aoField, + reflectivityField, + emissiveIntensityField, + normalStrengthField, + textureScaleUField, + textureScaleVField, + textureOffsetUField, + textureOffsetVField, + transmittanceField, + iorField}; for (QDoubleSpinBox *field : scalars) { connect(field, &QDoubleSpinBox::valueChanged, this, [this](double) { materialChanged(); }); @@ -720,6 +764,12 @@ void MaterialEditorPanel::materialChanged() { material.insert("emissiveIntensity", emissiveIntensityField->value()); material.insert("normalMapStrength", normalStrengthField->value()); material.insert("useNormalMap", normalMapField->isChecked()); + material.insert("textureScale", + QJsonArray{textureScaleUField->value(), + textureScaleVField->value()}); + material.insert("textureOffset", + QJsonArray{textureOffsetUField->value(), + textureOffsetVField->value()}); material.insert("transmittance", transmittanceField->value()); material.insert("ior", iorField->value()); if (material == previous) diff --git a/include/atlas/core/default_shaders.h b/include/atlas/core/default_shaders.h index db84f3d8..f5cc83db 100644 --- a/include/atlas/core/default_shaders.h +++ b/include/atlas/core/default_shaders.h @@ -1176,6 +1176,8 @@ struct UBO float3 cameraPosition; float normalMapStrength; uint useNormalMap; + float2 textureScale; + float2 textureOffset; }; struct MaterialPush @@ -1303,7 +1305,7 @@ float2 parallaxMapping(thread const float2& texCoords, thread const float3& view } int param = textureIndex; float2 param_1 = currentTexCoords; - float currentDepthMapValue = sampleTextureAt(param, param_1, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; + float currentDepthMapValue = 1.0 - sampleTextureAt(param, param_1, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; int maxIterations = int(numLayers) + 1; int iteration = 0; while (currentLayerDepth < currentDepthMapValue && iteration < maxIterations) @@ -1311,7 +1313,7 @@ float2 parallaxMapping(thread const float2& texCoords, thread const float3& view currentTexCoords -= deltaTexCoords; int param_2 = textureIndex; float2 param_3 = currentTexCoords; - currentDepthMapValue = sampleTextureAt(param_2, param_3, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; + currentDepthMapValue = 1.0 - sampleTextureAt(param_2, param_3, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; currentLayerDepth += layerDepth; iteration++; } @@ -1319,9 +1321,9 @@ float2 parallaxMapping(thread const float2& texCoords, thread const float3& view float afterDepth = currentDepthMapValue - currentLayerDepth; int param_4 = textureIndex; float2 param_5 = prevTexCoords; - float beforeDepth = sampleTextureAt(param_4, param_5, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x - (currentLayerDepth - layerDepth); - float denom = fast::max(afterDepth - beforeDepth, 9.9999997473787516355514526367188e-05); - float weight = fast::clamp(afterDepth / denom, 0.0, 1.0); + float beforeDepth = 1.0 - sampleTextureAt(param_4, param_5, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x - (currentLayerDepth - layerDepth); + float denom = afterDepth - beforeDepth; + float weight = abs(denom) > 9.9999997473787516355514526367188e-05 ? fast::clamp(afterDepth / denom, 0.0, 1.0) : 0.0; currentTexCoords = (prevTexCoords * weight) + (currentTexCoords * (1.0 - weight)); return currentTexCoords; } @@ -1349,9 +1351,9 @@ float4 enableTextures(thread const int& type, constant UBO& _46, texture2d 1.0; - bool _483; - if (!_476) - { - _483 = texCoord.y > 1.0; - } - else - { - _483 = _476; - } - bool _490; - if (!_483) - { - _490 = texCoord.x < 0.0; - } - else - { - _490 = _483; - } - bool _497; - if (!_490) - { - _497 = texCoord.y < 0.0; - } - else - { - _497 = _490; - } - if (_497) - { - discard_fragment(); - } } int param_2 = 0; float4 sampledColor = enableTextures(param_2, _46, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); @@ -1543,14 +1513,14 @@ fragment main0_out main0(main0_in in [[stage_in]], constant UBO& _46 [[buffer(0) metallicValue *= metallicTex.x; } float roughnessValue = material.roughness; - int param_6 = )", -R"(10; + 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))) { roughnessValue *= roughnessTex.x; } - float aoValue = material.ao; + float aoValue = material.ao)", +R"(; 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))) @@ -4966,6 +4936,8 @@ struct Uniforms float3 cameraPosition; float normalMapStrength; uint useNormalMap; + float2 textureScale; + float2 textureOffset; }; struct Environment @@ -5101,8 +5073,8 @@ struct AmbientLight float3 _pad0; }; -constant spvUnsafeArray _1657 = spvUnsafeArray({ float3(0.5381000041961669921875, 0.18559999763965606689453125, -0.4318999946117401123046875), float3(0.13789999485015869140625, 0.248600006103515625, 0.4429999887943267822265625), float3(0.3370999991893768310546875, 0.567900002002716064453125, -0.0057000000961124897003173828125), float3(-0.699899971485137939453125, -0.0450999997556209564208984375, -0.0019000000320374965667724609375), float3(0.068899996578693389892578125, -0.159799993038177490234375, -0.854700028896331787109375), float3(0.056000001728534698486328125, 0.0068999999202787876129150390625, -0.184300005435943603515625), float3(-0.014600000344216823577880859375, 0.14020000398159027099609375, 0.076200000941753387451171875), float3(0.00999999977648258209228515625, -0.19239999353885650634765625, -0.03440000116825103759765625), float3(-0.35769999027252197265625, -0.53009998798370361328125, -0.4357999861240386962890625), float3(-0.3169000148773193359375, 0.10629999637603759765625, 0.015799999237060546875), float3(0.010300000198185443878173828125, -0.5868999958038330078125, 0.0046000001020729541778564453125), float3(-0.08969999849796295166015625, -0.4939999878406524658203125, 0.328700006008148193359375), float3(0.7118999958038330078125, -0.015399999916553497314453125, -0.091799996793270111083984375), float3(-0.053300000727176666259765625, 0.0595999993383884429931640625, -0.541100025177001953125), float3(0.03519999980926513671875, -0.063100002706050872802734375, 0.546000003814697265625), float3(-0.4776000082492828369140625, 0.2847000062465667724609375, -0.0271000005304813385009765625), float3(-0.11200000345706939697265625, 0.1234000027179718017578125, -0.744599997997283935546875), float3(-0.212999999523162841796875, -0.07819999754428863525390625, -0.13789999485015869140625), float3(0.2944000065326690673828125, -0.3111999928951263427734375, -0.2644999921321868896484375), float3(-0.4564000070095062255859375, 0.4174999892711639404296875, -0.184300005435943603515625), float3(0.1234000027179718017578125, -0.567799985408782958984375, 0)", -R"(.788999974727630615234375), float3(-0.6789000034332275390625, 0.23450000584125518798828125, -0.4566999971866607666015625), float3(0.34560000896453857421875, -0.788999974727630615234375, 0.1234000027179718017578125), float3(-0.23450000584125518798828125, 0.567799985408782958984375, -0.6789000034332275390625), float3(0.788999974727630615234375, 0.1234000027179718017578125, 0.567799985408782958984375), float3(-0.567799985408782958984375, -0.6789000034332275390625, 0.23450000584125518798828125), float3(0.4566999971866607666015625, 0.788999974727630615234375, -0.23450000584125518798828125), float3(-0.788999974727630615234375, 0.34560000896453857421875, -0.567799985408782958984375), float3(0.6789000034332275390625, -0.23450000584125518798828125, 0.788999974727630615234375), float3(-0.1234000027179718017578125, 0.6789000034332275390625, -0.4566999971866607666015625), float3(0.23450000584125518798828125, -0.567799985408782958984375, 0.6789000034332275390625), float3(-0.34560000896453857421875, 0.788999974727630615234375, -0.1234000027179718017578125), float3(0.567799985408782958984375, 0.23450000584125518798828125, -0.788999974727630615234375), float3(-0.6789000034332275390625, -0.567799985408782958984375, 0.34560000896453857421875), float3(0.788999974727630615234375, -0.34560000896453857421875, 0.4566999971866607666015625), float3(-0.23450000584125518798828125, 0.1234000027179718017578125, -0.6789000034332275390625), float3(0.4566999971866607666015625, 0.788999974727630615234375, -0.567799985408782958984375), float3(-0.567799985408782958984375, 0.23450000584125518798828125, 0.6789000034332275390625), float3(0.34560000896453857421875, -0.788999974727630615234375, -0.1234000027179718017578125), float3(-0.788999974727630615234375, 0.567799985408782958984375, -0.23450000584125518798828125), float3(0.6789000034332275390625, -0.1234000027179718017578125, 0.34560000896453857421875), float3(-0.4566999971866607666015625, 0.788999974727630615234375, 0.23450000584125518798828125), float3(0.567799985408782958984375, -0.6789000034332275390625, 0.788999974727630615234375), float3(-0.34560000896453857421875, 0.567799985408782958984375, -0.6789000034332275390625), float3(0.23450000584125518798828125, -0.788999974727630615234375, 0.567799985408782958984375), float3(-0.6789000034332275390625, 0.23450000584125518798828125, -0.1234000027179718017578125), float3(0.788999974727630615234375, -0.34560000896453857421875, -0.567799985408782958984375), float3(-0.567799985408782958984375, 0.6789000034332275390625, 0.23450000584125518798828125), float3(0.4566999971866607666015625, -0.788999974727630615234375, 0.34560000896453857421875), float3(-0.23450000584125518798828125, 0.1234000027179718017578125, -0.788999974727630615234375), float3(0.34560000896453857421875, -0.567799985408782958984375, 0.6789000034332275390625), float3(-0.788999974727630615234375, 0.4566999971866607666015625, -0.34560000896453857421875), float3(0.6789000034332275390625, -0.1234000027179718017578125, -0.567799985408782958984375), float3(-0.4566999971866607666015625, 0.23450000584125518798828125, 0.788999974727630615234375) }); +constant spvUnsafeArray _1657 = spvUnsafeArray({ float3(0.5381000041961669921875, 0.18559999763965606689453125, -0.4318999946117401123046875), float3(0.13789999485015869140625, 0.248600006103515625, 0.4429999887943267822265625), float3(0.3370999991893768310546875, 0.567900002002716064453125, -0.0057000000961124897003173828125), float3(-0.699899971485137939453125, -0.0450999997556209564208984375, -0.0019000000320374965667724609375), float3(0.068899996578693389892578125, -0.159799993038177490234375, -0.854700028896331787109375), float3(0.056000001728534698486328125, 0.0068999999202787876129150390625, -0.184300005435943603515625), float3(-0.014600000344216823577880859375, 0.14020000398159027099609375, 0.076200000941753387451171875), float3(0.00999999977648258209228515625, -0.19239999353885650634765625, -0.03440000116825103759765625), float3(-0.35769999027252197265625, -0.53009998798370361328125, -0.4357999861240386962890625), float3(-0.3169000148773193359375, 0.10629999637603759765625, 0.015799999237060546875), float3(0.010300000198185443878173828125, -0.5868999958038330078125, 0.0046000001020729541778564453125), float3(-0.08969999849796295166015625, -0.4939999878406524658203125, 0.328700006008148193359375), float3(0.7118999958038330078125, -0.015399999916553497314453125, -0.091799996793270111083984375), float3(-0.053300000727176666259765625, 0.0595999993383884429931640625, -0.541100025177001953125), float3(0.03519999980926513671875, -0.063100002706050872802734375, 0.546000003814697265625), float3(-0.4776000082492828369140625, 0.2847000062465667724609375, -0.0271000005304813385009765625), float3(-0.11200000345706939697265625, 0.1234000027179718017578125, -0.744599997997283935546875), float3(-0.212999999523162841796875, -0.07819999754428863525390625, -0.13789999485015869140625), float3(0.2944000065326690673828125, -0.3111999928951263427734375, -0.2644999921321868896484375), float3(-0.4564000070095062255859375, 0.4174999892711639404296875, -0.184300005435943603515625), float3(0.123400)", +R"(0027179718017578125, -0.567799985408782958984375, 0.788999974727630615234375), float3(-0.6789000034332275390625, 0.23450000584125518798828125, -0.4566999971866607666015625), float3(0.34560000896453857421875, -0.788999974727630615234375, 0.1234000027179718017578125), float3(-0.23450000584125518798828125, 0.567799985408782958984375, -0.6789000034332275390625), float3(0.788999974727630615234375, 0.1234000027179718017578125, 0.567799985408782958984375), float3(-0.567799985408782958984375, -0.6789000034332275390625, 0.23450000584125518798828125), float3(0.4566999971866607666015625, 0.788999974727630615234375, -0.23450000584125518798828125), float3(-0.788999974727630615234375, 0.34560000896453857421875, -0.567799985408782958984375), float3(0.6789000034332275390625, -0.23450000584125518798828125, 0.788999974727630615234375), float3(-0.1234000027179718017578125, 0.6789000034332275390625, -0.4566999971866607666015625), float3(0.23450000584125518798828125, -0.567799985408782958984375, 0.6789000034332275390625), float3(-0.34560000896453857421875, 0.788999974727630615234375, -0.1234000027179718017578125), float3(0.567799985408782958984375, 0.23450000584125518798828125, -0.788999974727630615234375), float3(-0.6789000034332275390625, -0.567799985408782958984375, 0.34560000896453857421875), float3(0.788999974727630615234375, -0.34560000896453857421875, 0.4566999971866607666015625), float3(-0.23450000584125518798828125, 0.1234000027179718017578125, -0.6789000034332275390625), float3(0.4566999971866607666015625, 0.788999974727630615234375, -0.567799985408782958984375), float3(-0.567799985408782958984375, 0.23450000584125518798828125, 0.6789000034332275390625), float3(0.34560000896453857421875, -0.788999974727630615234375, -0.1234000027179718017578125), float3(-0.788999974727630615234375, 0.567799985408782958984375, -0.23450000584125518798828125), float3(0.6789000034332275390625, -0.1234000027179718017578125, 0.34560000896453857421875), float3(-0.4566999971866607666015625, 0.788999974727630615234375, 0.23450000584125518798828125), float3(0.567799985408782958984375, -0.6789000034332275390625, 0.788999974727630615234375), float3(-0.34560000896453857421875, 0.567799985408782958984375, -0.6789000034332275390625), float3(0.23450000584125518798828125, -0.788999974727630615234375, 0.567799985408782958984375), float3(-0.6789000034332275390625, 0.23450000584125518798828125, -0.1234000027179718017578125), float3(0.788999974727630615234375, -0.34560000896453857421875, -0.567799985408782958984375), float3(-0.567799985408782958984375, 0.6789000034332275390625, 0.23450000584125518798828125), float3(0.4566999971866607666015625, -0.788999974727630615234375, 0.34560000896453857421875), float3(-0.23450000584125518798828125, 0.1234000027179718017578125, -0.788999974727630615234375), float3(0.34560000896453857421875, -0.567799985408782958984375, 0.6789000034332275390625), float3(-0.788999974727630615234375, 0.4566999971866607666015625, -0.34560000896453857421875), float3(0.6789000034332275390625, -0.1234000027179718017578125, -0.567799985408782958984375), float3(-0.4566999971866607666015625, 0.23450000584125518798828125, 0.788999974727630615234375) }); struct main0_out { @@ -5218,24 +5190,24 @@ float2 parallaxMapping(thread const float2& texCoords, thread const float3& view } int param = textureIndex; float2 param_1 = currentTexCoords; - float currentDepthMapValue = sampleTextureAt(param, param_1, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; + float currentDepthMapValue = 1.0 - sampleTextureAt(param, param_1, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; while (currentLayerDepth < currentDepthMapValue) { - currentTexCoords = fast::clamp(currentTexCoords - deltaTexCoords, flo)", -R"(at2(0.0), float2(1.0)); + currentTexCo)", +R"(ords -= deltaTexCoords; int param_2 = textureIndex; float2 param_3 = currentTexCoords; - currentDepthMapValue = sampleTextureAt(param_2, param_3, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; + currentDepthMapValue = 1.0 - sampleTextureAt(param_2, param_3, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; currentLayerDepth += layerDepth; } float2 prevTexCoords = currentTexCoords + deltaTexCoords; float afterDepth = currentDepthMapValue - currentLayerDepth; int param_4 = textureIndex; float2 param_5 = prevTexCoords; - float beforeDepth = sampleTextureAt(param_4, param_5, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x - (currentLayerDepth - layerDepth); + float beforeDepth = 1.0 - sampleTextureAt(param_4, param_5, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x - (currentLayerDepth - layerDepth); float weight = afterDepth / (afterDepth - beforeDepth); currentTexCoords = (prevTexCoords * weight) + (currentTexCoords * (1.0 - weight)); - return fast::clamp(currentTexCoords, float2(0.0), float2(1.0)); + return currentTexCoords; } static inline __attribute__((always_inline)) @@ -5402,8 +5374,8 @@ float2 getTextureDimensions(thread const int& textureIndex, texture2d tex } static inline __attribute__((always_inline)) -float calculateShadow(thread const ShadowParameters& shadowParam,)", -R"( thread const float4& fragPosLightSpace, constant Uniforms& _163, texture2d texture1, sampler texture1Smplr, texture2d texture2, sampler texture2Smplr, texture2d texture3, sampler texture3Smplr, texture2d texture4, sampler texture4Smplr, texture2d texture5, sampler texture5Smplr, texture2d texture6, sampler texture6Smplr, texture2d texture7, sampler texture7Smplr, texture2d texture8, sampler texture8Smplr, texture2d texture9, sampler texture9Smplr, texture2d texture10, sampler texture10Smplr, device DirectionalLightsUBO& _1083, thread float3& Normal, thread float3& FragPos) +float calculateShadow(thread const ShadowParameters& shadowParam, thread const float4& fragP)", +R"(osLightSpace, constant Uniforms& _163, texture2d texture1, sampler texture1Smplr, texture2d texture2, sampler texture2Smplr, texture2d texture3, sampler texture3Smplr, texture2d texture4, sampler texture4Smplr, texture2d texture5, sampler texture5Smplr, texture2d texture6, sampler texture6Smplr, texture2d texture7, sampler texture7Smplr, texture2d texture8, sampler texture8Smplr, texture2d texture9, sampler texture9Smplr, texture2d texture10, sampler texture10Smplr, device DirectionalLightsUBO& _1083, thread float3& Normal, thread float3& FragPos) { float3 projCoords = fragPosLightSpace.xyz / float3(fragPosLightSpace.w); projCoords = (projCoords * 0.5) + float3(0.5); @@ -5612,8 +5584,8 @@ float3 calculatePBR(thread const float3& N, thread const float3& V, thread const float denominator = ((4.0 * fast::max(dot(N, V), 0.0)) * fast::max(dot(N, L), 0.0)) + 9.9999997473787516355514526367188e-05; float3 specular = numerator / float3(denominator); float NdotL = fast::max(dot(N, L), 0.0); - flo)", -R"(at3 Lo = ((((kD * albedo) / float3(3.1415927410125732421875)) + specular) * radiance) * NdotL; + float3 Lo = ((((kD * albedo) /)", +R"( float3(3.1415927410125732421875)) + specular) * radiance) * NdotL; return Lo; } @@ -5746,10 +5718,10 @@ float3 sampleHDRTexture(thread const int& textureIndex, thread const float3& dir } static inline __attribute__((always_inline)) -float3 sampleEnvironmentRadiance(thread const float3& direction, constant Uniforms& _163, texture2d texture1, sampler texture1Smplr, texture2d texture2, sampler texture2Smplr, texture2d texture3, sampler texture3Smplr, texture2d texture4, sampler texture4Smplr, texture2d texture5, sampler texture5Smplr, texture2d texture6, sampler texture6Smplr, texture2d texture7, sampler texture7Smplr, texture2d texture8, sampler texture8Smplr, texture2d texture9, sampler texture9Smplr, texture2d texture10, sampler texture10S)", -R"(mplr) +float3 sampleEnvironmentRadiance(thread const float3& direction, constant Uniforms& _163, texture2d texture1, sampler texture1Smplr, texture2d texture2, sampler texture2Smplr, texture2d texture3, sampler texture3Smplr, texture2d texture4, sampler texture4Smplr, texture2d texture5, sampler texture5Smplr, texture2d texture6, sampler texture6Smplr, texture2d texture7, sampler texture7Smplr, texture2d texture8, sampler texture8Smplr, texture2d texture9, sampler texture9Smplr, texture2d texture10, sampler texture10Smplr) { - float3 envColor = float3(0.0); + float3 envColor)", +R"( = float3(0.0); int count = 0; for (int i = 0; i < _163.textureCount; i++) { @@ -5788,7 +5760,7 @@ fragment main0_out main0(main0_in in [[stage_in]], constant Uniforms& _163 [[buf TBN[0] = in.TBN_0; TBN[1] = in.TBN_1; TBN[2] = in.TBN_2; - float2 texCoord = in.TexCoord; + float2 texCoord = in.TexCoord * _163.textureScale + _163.textureOffset; bool hasParallaxMap = false; for (int i = 0; i < _163.textureCount; i++) { @@ -5804,38 +5776,6 @@ fragment main0_out main0(main0_in in [[stage_in]], constant Uniforms& _163 [[buf float2 param = texCoord; float3 param_1 = tangentViewDir; texCoord = parallaxMapping(param, param_1, _163, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - bool _1744 = texCoord.x > 1.0; - bool _1751; - if (!_1744) - { - _1751 = texCoord.y > 1.0; - } - else - { - _1751 = _1744; - } - bool _1758; - if (!_1751) - { - _1758 = texCoord.x < 0.0; - } - else - { - _1758 = _1751; - } - bool _1765; - if (!_1758) - { - _1765 = texCoord.y < 0.0; - } - else - { - _1765 = _1758; - } - if (_1765) - { - discard_fragment(); - } } int param_2 = 5; float4 normTexture = enableTextures(param_2, _163, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); @@ -5918,8 +5858,7 @@ fragment main0_out main0(main0_in in [[stage_in]], constant Uniforms& _163 [[buf { ao *= aoTex.x; } - float3 F0 = float3(0.0399999)", -R"(99105930328369140625); + float3 F0 = float3(0.039999999105930328369140625); F0 = mix(F0, albedo, float3(metallic)); float directionalShadow = 0.0; float spotShadow = 0.0; @@ -5933,7 +5872,8 @@ R"(99105930328369140625); { float4 fragPosLightSpace = (_1905.shadowParams[i_1].lightProjection * _1905.shadowParams[i_1].lightView) * float4(in.FragPos, 1.0); ShadowParameters _1934; - _1934.lightView = _1905.shadowParams[i_1].lightView; + )", +R"( _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; @@ -6064,14 +6004,14 @@ R"(99105930328369140625); float cosTheta = cos(radians(_2058.areaLights[i_2].angle)); if ((facing >= cosTheta) && (facing > 0.0)) { - )", -R"( float range = fast::max(_2058.areaLights[i_2].range, 0.001000000047497451305389404296875); + float range = fast::max(_2058.areaLights[i_2].range, 0.001000000047497451305389404296875); float attenuation = 1.0 / ((1.0 + (dist / range)) + ((dist * dist) / (range * range))); float fade = 1.0 - smoothstep(range * 0.89999997615814208984375, range, dist); 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 param_43 = H; + float3 pa)", +R"(ram_43 = H; float param_44 = roughness; float NDF = distributionGGX(param_42, param_43, param_44); float3 param_45 = N; diff --git a/include/atlas/object.h b/include/atlas/object.h index 91993465..3b3c628c 100644 --- a/include/atlas/object.h +++ b/include/atlas/object.h @@ -84,6 +84,8 @@ struct Material { * @brief Whether normal map sampling is enabled for this material. */ bool useNormalMap = true; + TextureCoordinate textureScale = {1.0f, 1.0f}; + TextureCoordinate textureOffset = {0.0f, 0.0f}; /** * @brief Fraction of transmitted light for translucent materials. */ @@ -883,6 +885,8 @@ class Model : public GameObject { } obj->material.useNormalMap = material.useNormalMap; obj->material.normalMapStrength = material.normalMapStrength; + obj->material.textureScale = material.textureScale; + obj->material.textureOffset = material.textureOffset; obj->useDeferredRendering = useDeferredRendering; obj->render(dt, commandBuffer, updatePipeline); } @@ -903,6 +907,8 @@ class Model : public GameObject { } obj->material.useNormalMap = material.useNormalMap; obj->material.normalMapStrength = material.normalMapStrength; + obj->material.textureScale = material.textureScale; + obj->material.textureOffset = material.textureOffset; obj->useDeferredRendering = useDeferredRendering; obj->update(window); } @@ -926,6 +932,8 @@ class Model : public GameObject { } obj->material.useNormalMap = material.useNormalMap; obj->material.normalMapStrength = material.normalMapStrength; + obj->material.textureScale = material.textureScale; + obj->material.textureOffset = material.textureOffset; obj->useDeferredRendering = useDeferredRendering; obj->initialize(); } diff --git a/include/editor/views/materialEditor.h b/include/editor/views/materialEditor.h index c35192c6..ef65e73f 100644 --- a/include/editor/views/materialEditor.h +++ b/include/editor/views/materialEditor.h @@ -61,6 +61,10 @@ class MaterialEditorPanel : public QWidget { QDoubleSpinBox *reflectivityField = nullptr; QDoubleSpinBox *emissiveIntensityField = nullptr; QDoubleSpinBox *normalStrengthField = nullptr; + QDoubleSpinBox *textureScaleUField = nullptr; + QDoubleSpinBox *textureScaleVField = nullptr; + QDoubleSpinBox *textureOffsetUField = nullptr; + QDoubleSpinBox *textureOffsetVField = nullptr; QDoubleSpinBox *transmittanceField = nullptr; QDoubleSpinBox *iorField = nullptr; QCheckBox *normalMapField = nullptr; diff --git a/runtime/lib/context.cpp b/runtime/lib/context.cpp index c2bce8a0..d20d5a83 100644 --- a/runtime/lib/context.cpp +++ b/runtime/lib/context.cpp @@ -623,6 +623,10 @@ TextureType parseTextureTypeString(const std::string &value) { if (token == "normal" || token == "normalmap") { return TextureType::Normal; } + if (token == "parallax" || token == "displacement" || + token == "height") { + return TextureType::Parallax; + } if (token == "metallic" || token == "metalness") { return TextureType::Metallic; } @@ -739,6 +743,19 @@ MaterialDefinition loadMaterialDefinition(const json &value, loaded.material.normalMapStrength); tryReadBoolAny(materialData, {"useNormalMap"}, loaded.material.useNormalMap); + if (const json *scale = findField(materialData, {"textureScale", "uvScale"}); + scale != nullptr && scale->is_array() && scale->size() >= 2) { + loaded.material.textureScale = { + static_cast((*scale)[0].get()), + static_cast((*scale)[1].get())}; + } + if (const json *offset = + findField(materialData, {"textureOffset", "uvOffset"}); + offset != nullptr && offset->is_array() && offset->size() >= 2) { + loaded.material.textureOffset = { + static_cast((*offset)[0].get()), + static_cast((*offset)[1].get())}; + } tryReadFloatAny(materialData, {"transmittance"}, loaded.material.transmittance); tryReadFloatAny(materialData, {"ior"}, loaded.material.ior); @@ -758,6 +775,9 @@ MaterialDefinition loadMaterialDefinition(const json &value, appendTexture({"specularTexture", "specularMap"}, TextureType::Specular, false); appendTexture({"normalTexture", "normalMap"}, TextureType::Normal, false); + appendTexture({"displacementTexture", "displacementMap", "heightTexture", + "heightMap"}, + TextureType::Parallax, false); appendTexture({"metallicTexture", "metalnessTexture"}, TextureType::Metallic, false); appendTexture({"roughnessTexture"}, TextureType::Roughness, false); diff --git a/shaders/metal/deferred/deferred.frag.metal b/shaders/metal/deferred/deferred.frag.metal index 9c9ebf03..21284319 100644 --- a/shaders/metal/deferred/deferred.frag.metal +++ b/shaders/metal/deferred/deferred.frag.metal @@ -14,6 +14,8 @@ struct UBO float3 cameraPosition; float normalMapStrength; uint useNormalMap; + float2 textureScale; + float2 textureOffset; }; struct MaterialPush @@ -141,7 +143,7 @@ float2 parallaxMapping(thread const float2& texCoords, thread const float3& view } int param = textureIndex; float2 param_1 = currentTexCoords; - float currentDepthMapValue = sampleTextureAt(param, param_1, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; + float currentDepthMapValue = 1.0 - sampleTextureAt(param, param_1, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; int maxIterations = int(numLayers) + 1; int iteration = 0; while (currentLayerDepth < currentDepthMapValue && iteration < maxIterations) @@ -149,7 +151,7 @@ float2 parallaxMapping(thread const float2& texCoords, thread const float3& view currentTexCoords -= deltaTexCoords; int param_2 = textureIndex; float2 param_3 = currentTexCoords; - currentDepthMapValue = sampleTextureAt(param_2, param_3, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; + currentDepthMapValue = 1.0 - sampleTextureAt(param_2, param_3, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; currentLayerDepth += layerDepth; iteration++; } @@ -157,9 +159,9 @@ float2 parallaxMapping(thread const float2& texCoords, thread const float3& view float afterDepth = currentDepthMapValue - currentLayerDepth; int param_4 = textureIndex; float2 param_5 = prevTexCoords; - float beforeDepth = sampleTextureAt(param_4, param_5, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x - (currentLayerDepth - layerDepth); - float denom = fast::max(afterDepth - beforeDepth, 9.9999997473787516355514526367188e-05); - float weight = fast::clamp(afterDepth / denom, 0.0, 1.0); + float beforeDepth = 1.0 - sampleTextureAt(param_4, param_5, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x - (currentLayerDepth - layerDepth); + float denom = afterDepth - beforeDepth; + float weight = abs(denom) > 9.9999997473787516355514526367188e-05 ? fast::clamp(afterDepth / denom, 0.0, 1.0) : 0.0; currentTexCoords = (prevTexCoords * weight) + (currentTexCoords * (1.0 - weight)); return currentTexCoords; } @@ -261,7 +263,7 @@ fragment main0_out main0(main0_in in [[stage_in]], constant UBO& _46 [[buffer(0) TBN[0] = in.TBN_0; TBN[1] = in.TBN_1; TBN[2] = in.TBN_2; - float2 texCoord = in.TexCoord; + float2 texCoord = in.TexCoord * _46.textureScale + _46.textureOffset; bool hasParallaxMap = false; for (int i = 0; i < _46.textureCount; i++) { @@ -277,38 +279,6 @@ fragment main0_out main0(main0_in in [[stage_in]], constant UBO& _46 [[buffer(0) float2 param = texCoord; float3 param_1 = tangentViewDir; texCoord = parallaxMapping(param, param_1, _46, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - bool _476 = texCoord.x > 1.0; - bool _483; - if (!_476) - { - _483 = texCoord.y > 1.0; - } - else - { - _483 = _476; - } - bool _490; - if (!_483) - { - _490 = texCoord.x < 0.0; - } - else - { - _490 = _483; - } - bool _497; - if (!_490) - { - _497 = texCoord.y < 0.0; - } - else - { - _497 = _490; - } - if (_497) - { - discard_fragment(); - } } int param_2 = 0; float4 sampledColor = enableTextures(param_2, _46, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); diff --git a/shaders/metal/main.frag.metal b/shaders/metal/main.frag.metal index a92893c5..f67d7f7d 100644 --- a/shaders/metal/main.frag.metal +++ b/shaders/metal/main.frag.metal @@ -106,6 +106,8 @@ struct Uniforms float3 cameraPosition; float normalMapStrength; uint useNormalMap; + float2 textureScale; + float2 textureOffset; }; struct Environment @@ -357,23 +359,23 @@ float2 parallaxMapping(thread const float2& texCoords, thread const float3& view } int param = textureIndex; float2 param_1 = currentTexCoords; - float currentDepthMapValue = sampleTextureAt(param, param_1, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; + float currentDepthMapValue = 1.0 - sampleTextureAt(param, param_1, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; while (currentLayerDepth < currentDepthMapValue) { - currentTexCoords = fast::clamp(currentTexCoords - deltaTexCoords, float2(0.0), float2(1.0)); + currentTexCoords -= deltaTexCoords; int param_2 = textureIndex; float2 param_3 = currentTexCoords; - currentDepthMapValue = sampleTextureAt(param_2, param_3, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; + currentDepthMapValue = 1.0 - sampleTextureAt(param_2, param_3, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x; currentLayerDepth += layerDepth; } float2 prevTexCoords = currentTexCoords + deltaTexCoords; float afterDepth = currentDepthMapValue - currentLayerDepth; int param_4 = textureIndex; float2 param_5 = prevTexCoords; - float beforeDepth = sampleTextureAt(param_4, param_5, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x - (currentLayerDepth - layerDepth); + float beforeDepth = 1.0 - sampleTextureAt(param_4, param_5, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr).x - (currentLayerDepth - layerDepth); float weight = afterDepth / (afterDepth - beforeDepth); currentTexCoords = (prevTexCoords * weight) + (currentTexCoords * (1.0 - weight)); - return fast::clamp(currentTexCoords, float2(0.0), float2(1.0)); + return currentTexCoords; } static inline __attribute__((always_inline)) @@ -923,7 +925,7 @@ fragment main0_out main0(main0_in in [[stage_in]], constant Uniforms& _163 [[buf TBN[0] = in.TBN_0; TBN[1] = in.TBN_1; TBN[2] = in.TBN_2; - float2 texCoord = in.TexCoord; + float2 texCoord = in.TexCoord * _163.textureScale + _163.textureOffset; bool hasParallaxMap = false; for (int i = 0; i < _163.textureCount; i++) { @@ -939,38 +941,6 @@ fragment main0_out main0(main0_in in [[stage_in]], constant Uniforms& _163 [[buf float2 param = texCoord; float3 param_1 = tangentViewDir; texCoord = parallaxMapping(param, param_1, _163, texture1, texture1Smplr, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); - bool _1744 = texCoord.x > 1.0; - bool _1751; - if (!_1744) - { - _1751 = texCoord.y > 1.0; - } - else - { - _1751 = _1744; - } - bool _1758; - if (!_1751) - { - _1758 = texCoord.x < 0.0; - } - else - { - _1758 = _1751; - } - bool _1765; - if (!_1758) - { - _1765 = texCoord.y < 0.0; - } - else - { - _1765 = _1758; - } - if (_1765) - { - discard_fragment(); - } } int param_2 = 5; float4 normTexture = enableTextures(param_2, _163, texture1, texture1Smplr, texCoord, texture2, texture2Smplr, texture3, texture3Smplr, texture4, texture4Smplr, texture5, texture5Smplr, texture6, texture6Smplr, texture7, texture7Smplr, texture8, texture8Smplr, texture9, texture9Smplr, texture10, texture10Smplr); diff --git a/shaders/opengl/deferred/deferred.frag b/shaders/opengl/deferred/deferred.frag index 7fe89420..c1292e5d 100644 --- a/shaders/opengl/deferred/deferred.frag +++ b/shaders/opengl/deferred/deferred.frag @@ -42,6 +42,8 @@ uniform int textureTypes[16]; uniform int textureCount; uniform Material material; uniform vec3 cameraPosition; +uniform vec2 textureScale; +uniform vec2 textureOffset; uniform bool useTexture; uniform bool useColor; @@ -106,33 +108,32 @@ vec2 parallaxMapping(vec2 texCoords, vec3 viewDir) { } if (textureIndex == -1) return currentTexCoords; - float currentDepthMapValue = sampleTextureAt(textureIndex, currentTexCoords).r; + float currentDepthMapValue = 1.0 - sampleTextureAt(textureIndex, currentTexCoords).r; while (currentLayerDepth < currentDepthMapValue) { currentTexCoords -= deltaTexCoords; - currentDepthMapValue = sampleTextureAt(textureIndex, currentTexCoords).r; + currentDepthMapValue = 1.0 - sampleTextureAt(textureIndex, currentTexCoords).r; currentLayerDepth += layerDepth; } vec2 prevTexCoords = currentTexCoords + deltaTexCoords; float afterDepth = currentDepthMapValue - currentLayerDepth; - float beforeDepth = sampleTextureAt(textureIndex, prevTexCoords).r - (currentLayerDepth - layerDepth); - float denom = max(afterDepth - beforeDepth, 1e-4); - float weight = clamp(afterDepth / denom, 0.0, 1.0); + float beforeDepth = 1.0 - sampleTextureAt(textureIndex, prevTexCoords).r - (currentLayerDepth - layerDepth); + float denom = afterDepth - beforeDepth; + float weight = abs(denom) > 1e-4 + ? clamp(afterDepth / denom, 0.0, 1.0) + : 0.0; currentTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); return currentTexCoords; } void main() { - texCoord = TexCoord; + texCoord = TexCoord * textureScale + textureOffset; vec3 tangentViewDir = normalize(transpose(TBN) * (cameraPosition - FragPos)); texCoord = parallaxMapping(texCoord, tangentViewDir); - if (texCoord.x > 1.0 || texCoord.y > 1.0 || texCoord.x < 0.0 || texCoord.y < 0.0) - discard; - vec4 sampledColor = enableTextures(TEXTURE_COLOR); bool hasColorTexture = sampledColor != vec4(-1.0); @@ -146,7 +147,7 @@ void main() { if (opacityTex.r < 0.1) { discard; } - } else if (material.albedo.a < 0.999 && baseColor.a < 0.1) { + } else if (baseColor.a < 0.1) { discard; } diff --git a/shaders/opengl/lighting/blinn_phong.frag b/shaders/opengl/lighting/blinn_phong.frag index dbc3623a..1f1cbdbb 100644 --- a/shaders/opengl/lighting/blinn_phong.frag +++ b/shaders/opengl/lighting/blinn_phong.frag @@ -105,6 +105,8 @@ uniform ShadowParameters shadowParams[10]; uniform int shadowParamCount; uniform vec3 cameraPosition; +uniform vec2 textureScale; +uniform vec2 textureOffset; uniform bool useTexture; uniform bool useColor; @@ -220,17 +222,17 @@ vec2 parallaxMapping(vec2 texCoords, vec3 viewDir) { } } if (textureIndex == -1) return texCoords; - float currentDepthMapValue = sampleTextureAt(textureIndex, currentTexCoords).r; + float currentDepthMapValue = 1.0 - sampleTextureAt(textureIndex, currentTexCoords).r; while (currentLayerDepth < currentDepthMapValue) { currentTexCoords -= deltaTexCoords; - currentDepthMapValue = sampleTextureAt(textureIndex, currentTexCoords).r; + currentDepthMapValue = 1.0 - sampleTextureAt(textureIndex, currentTexCoords).r; currentLayerDepth += layerDepth; } vec2 prevTexCoords = currentTexCoords + deltaTexCoords; float afterDepth = currentDepthMapValue - currentLayerDepth; - float beforeDepth = sampleTextureAt(textureIndex, prevTexCoords).r - (currentLayerDepth - layerDepth); + float beforeDepth = 1.0 - sampleTextureAt(textureIndex, prevTexCoords).r - (currentLayerDepth - layerDepth); float weight = afterDepth / (afterDepth - beforeDepth); currentTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); @@ -500,13 +502,11 @@ float calculateAllPointShadows(vec3 fragPos) { // ----- Main ----- void main() { - texCoord = TexCoord; + texCoord = TexCoord * textureScale + textureOffset; vec4 baseColor; vec3 tangentViewDir = normalize((TBN * cameraPosition) - (TBN * FragPos)); texCoord = parallaxMapping(texCoord, tangentViewDir); - if (texCoord.x > 1.0 || texCoord.y > 1.0 || texCoord.x < 0.0 || texCoord.y < 0.0) - discard; if (useTexture && !useColor) baseColor = enableTextures(TEXTURE_COLOR); diff --git a/shaders/opengl/main.frag b/shaders/opengl/main.frag index 53452ddf..aee88922 100644 --- a/shaders/opengl/main.frag +++ b/shaders/opengl/main.frag @@ -138,6 +138,8 @@ uniform ShadowParameters shadowParams[10]; uniform int shadowParamCount; uniform vec3 cameraPosition; +uniform vec2 textureScale; +uniform vec2 textureOffset; uniform bool useTexture; uniform bool useColor; @@ -285,21 +287,21 @@ vec2 parallaxMapping(vec2 texCoords, vec3 viewDir) { } } if (textureIndex == -1) return texCoords; - float currentDepthMapValue = sampleTextureAt(textureIndex, currentTexCoords).r; + float currentDepthMapValue = 1.0 - sampleTextureAt(textureIndex, currentTexCoords).r; while (currentLayerDepth < currentDepthMapValue) { - currentTexCoords = clamp(currentTexCoords - deltaTexCoords, vec2(0.0), vec2(1.0)); - currentDepthMapValue = sampleTextureAt(textureIndex, currentTexCoords).r; + currentTexCoords -= deltaTexCoords; + currentDepthMapValue = 1.0 - sampleTextureAt(textureIndex, currentTexCoords).r; currentLayerDepth += layerDepth; } vec2 prevTexCoords = currentTexCoords + deltaTexCoords; float afterDepth = currentDepthMapValue - currentLayerDepth; - float beforeDepth = sampleTextureAt(textureIndex, prevTexCoords).r - (currentLayerDepth - layerDepth); + float beforeDepth = 1.0 - sampleTextureAt(textureIndex, prevTexCoords).r - (currentLayerDepth - layerDepth); float weight = afterDepth / (afterDepth - beforeDepth); currentTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); - return clamp(currentTexCoords, vec2(0.0), vec2(1.0)); + return currentTexCoords; } vec3 reinhardToneMapping(vec3 hdrColor) { @@ -648,7 +650,7 @@ float calculateAllPointShadows(vec3 fragPos) { // ----- Main ----- void main() { - texCoord = TexCoord; + texCoord = TexCoord * textureScale + textureOffset; bool hasParallaxMap = false; for (int i = 0; i < textureCount; i++) { @@ -661,8 +663,6 @@ void main() { if (hasParallaxMap) { vec3 tangentViewDir = normalize(transpose(TBN) * (cameraPosition - FragPos)); texCoord = parallaxMapping(texCoord, tangentViewDir); - if (texCoord.x > 1.0 || texCoord.y > 1.0 || texCoord.x < 0.0 || texCoord.y < 0.0) - discard; } vec4 normTexture = enableTextures(TEXTURE_NORMAL); @@ -688,7 +688,7 @@ void main() { if (opacityTex.r < 0.1) { discard; } - } else if (material.albedo.a < 0.999 && albedoTex != vec4(-1.0) && albedoTex.a < 0.1) { + } else if (albedoTex != vec4(-1.0) && albedoTex.a < 0.1) { discard; } diff --git a/shaders/vulkan/deferred/deferred.frag b/shaders/vulkan/deferred/deferred.frag index 84480d30..caf5084b 100644 --- a/shaders/vulkan/deferred/deferred.frag +++ b/shaders/vulkan/deferred/deferred.frag @@ -36,6 +36,8 @@ layout(set = 1, binding = 0) uniform UBO { bool useTexture; bool useColor; vec3 cameraPosition; + vec2 textureScale; + vec2 textureOffset; }; layout(push_constant) uniform MaterialPush { @@ -107,26 +109,28 @@ vec2 parallaxMapping(vec2 texCoords, vec3 viewDir) { } if (textureIndex == -1) return currentTexCoords; - float currentDepthMapValue = sampleTextureAt(textureIndex, currentTexCoords).r; + float currentDepthMapValue = 1.0 - sampleTextureAt(textureIndex, currentTexCoords).r; while (currentLayerDepth < currentDepthMapValue) { currentTexCoords -= deltaTexCoords; - currentDepthMapValue = sampleTextureAt(textureIndex, currentTexCoords).r; + currentDepthMapValue = 1.0 - sampleTextureAt(textureIndex, currentTexCoords).r; currentLayerDepth += layerDepth; } vec2 prevTexCoords = currentTexCoords + deltaTexCoords; float afterDepth = currentDepthMapValue - currentLayerDepth; - float beforeDepth = sampleTextureAt(textureIndex, prevTexCoords).r - (currentLayerDepth - layerDepth); - float denom = max(afterDepth - beforeDepth, 1e-4); - float weight = clamp(afterDepth / denom, 0.0, 1.0); + float beforeDepth = 1.0 - sampleTextureAt(textureIndex, prevTexCoords).r - (currentLayerDepth - layerDepth); + float denom = afterDepth - beforeDepth; + float weight = abs(denom) > 1e-4 + ? clamp(afterDepth / denom, 0.0, 1.0) + : 0.0; currentTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); return currentTexCoords; } void main() { - texCoord = TexCoord; + texCoord = TexCoord * textureScale + textureOffset; // Only apply parallax mapping if a parallax texture exists bool hasParallaxMap = false; @@ -140,8 +144,6 @@ void main() { if (hasParallaxMap) { vec3 tangentViewDir = normalize(transpose(TBN) * (cameraPosition - FragPos)); texCoord = parallaxMapping(texCoord, tangentViewDir); - if (texCoord.x > 1.0 || texCoord.y > 1.0 || texCoord.x < 0.0 || texCoord.y < 0.0) - discard; } vec4 sampledColor = enableTextures(TEXTURE_COLOR); @@ -157,7 +159,7 @@ void main() { if (opacityTex.r < 0.1) { discard; } - } else if (material.albedo.a < 0.999 && baseColor.a < 0.1) { + } else if (baseColor.a < 0.1) { discard; } diff --git a/shaders/vulkan/main.frag b/shaders/vulkan/main.frag index d768b750..70fc642a 100644 --- a/shaders/vulkan/main.frag +++ b/shaders/vulkan/main.frag @@ -117,6 +117,8 @@ layout(set = 1, binding = 0) uniform Uniforms { int textureTypes[16]; int textureCount; vec3 cameraPosition; + vec2 textureScale; + vec2 textureOffset; }; layout(push_constant) uniform PushConstants { @@ -310,21 +312,21 @@ vec2 parallaxMapping(vec2 texCoords, vec3 viewDir) { } } if (textureIndex == -1) return texCoords; - float currentDepthMapValue = sampleTextureAt(textureIndex, currentTexCoords).r; + float currentDepthMapValue = 1.0 - sampleTextureAt(textureIndex, currentTexCoords).r; while (currentLayerDepth < currentDepthMapValue) { - currentTexCoords = clamp(currentTexCoords - deltaTexCoords, vec2(0.0), vec2(1.0)); - currentDepthMapValue = sampleTextureAt(textureIndex, currentTexCoords).r; + currentTexCoords -= deltaTexCoords; + currentDepthMapValue = 1.0 - sampleTextureAt(textureIndex, currentTexCoords).r; currentLayerDepth += layerDepth; } vec2 prevTexCoords = currentTexCoords + deltaTexCoords; float afterDepth = currentDepthMapValue - currentLayerDepth; - float beforeDepth = sampleTextureAt(textureIndex, prevTexCoords).r - (currentLayerDepth - layerDepth); + float beforeDepth = 1.0 - sampleTextureAt(textureIndex, prevTexCoords).r - (currentLayerDepth - layerDepth); float weight = afterDepth / (afterDepth - beforeDepth); currentTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); - return clamp(currentTexCoords, vec2(0.0), vec2(1.0)); + return currentTexCoords; } vec3 reinhardToneMapping(vec3 hdrColor) { @@ -678,7 +680,7 @@ float calculateAllPointShadows(vec3 fragPos) { // ----- Main ----- void main() { - texCoord = TexCoord; + texCoord = TexCoord * textureScale + textureOffset; bool hasParallaxMap = false; for (int i = 0; i < textureCount; i++) { @@ -691,8 +693,6 @@ void main() { if (hasParallaxMap) { vec3 tangentViewDir = normalize(transpose(TBN) * (cameraPosition - FragPos)); texCoord = parallaxMapping(texCoord, tangentViewDir); - if (texCoord.x > 1.0 || texCoord.y > 1.0 || texCoord.x < 0.0 || texCoord.y < 0.0) - discard; } vec4 normTexture = enableTextures(TEXTURE_NORMAL); @@ -718,7 +718,7 @@ void main() { if (opacityTex.r < 0.1) { discard; } - } else if (material.albedo.a < 0.999 && albedoTex != vec4(-1.0) && albedoTex.a < 0.1) { + } else if (albedoTex != vec4(-1.0) && albedoTex.a < 0.1) { discard; } diff --git a/tests/textures/assets/cobblestone.zip b/tests/textures/assets/cobblestone.zip new file mode 100644 index 00000000..12ee6de4 Binary files /dev/null and b/tests/textures/assets/cobblestone.zip differ diff --git a/tests/textures/assets/textures/cobblestone_floor_09_ao_1k.jpg b/tests/textures/assets/textures/cobblestone_floor_09_ao_1k.jpg new file mode 100644 index 00000000..48c8866f Binary files /dev/null and b/tests/textures/assets/textures/cobblestone_floor_09_ao_1k.jpg differ diff --git a/tests/textures/assets/textures/cobblestone_floor_09_arm_1k.jpg b/tests/textures/assets/textures/cobblestone_floor_09_arm_1k.jpg new file mode 100644 index 00000000..b3ce8f1c Binary files /dev/null and b/tests/textures/assets/textures/cobblestone_floor_09_arm_1k.jpg differ diff --git a/tests/textures/assets/textures/cobblestone_floor_09_diff_1k.jpg b/tests/textures/assets/textures/cobblestone_floor_09_diff_1k.jpg new file mode 100644 index 00000000..fa43c3b2 Binary files /dev/null and b/tests/textures/assets/textures/cobblestone_floor_09_diff_1k.jpg differ diff --git a/tests/textures/assets/textures/cobblestone_floor_09_disp_1k.png b/tests/textures/assets/textures/cobblestone_floor_09_disp_1k.png new file mode 100644 index 00000000..fd70f6d7 Binary files /dev/null and b/tests/textures/assets/textures/cobblestone_floor_09_disp_1k.png differ diff --git a/tests/textures/assets/textures/cobblestone_floor_09_nor_gl_1k.jpg b/tests/textures/assets/textures/cobblestone_floor_09_nor_gl_1k.jpg new file mode 100644 index 00000000..919bf33a Binary files /dev/null and b/tests/textures/assets/textures/cobblestone_floor_09_nor_gl_1k.jpg differ diff --git a/tests/textures/assets/textures/cobblestone_floor_09_rough_1k.jpg b/tests/textures/assets/textures/cobblestone_floor_09_rough_1k.jpg new file mode 100644 index 00000000..db895fc0 Binary files /dev/null and b/tests/textures/assets/textures/cobblestone_floor_09_rough_1k.jpg differ diff --git a/tests/textures/lib/atlas.d.ts b/tests/textures/lib/atlas.d.ts new file mode 100644 index 00000000..ab2b119e --- /dev/null +++ b/tests/textures/lib/atlas.d.ts @@ -0,0 +1,2535 @@ +// +// atlas.d.ts +// As part of the Atlas project +// Created by Max Van den Eynde in 2026 +// -------------------------------------------------- +// Description: Declarations for the Atlas Library for scripting +// Copyright (c) 2026 Max Van den Eynde +// + +declare module "atlas/log" { + export const Debug: { + print(message: string): void; + warning(message: string): void; + error(message: string): void; + }; +} + +declare module "atlas" { + import { + Position3d, + Color, + Position2d, + Size2d, + Quaternion, + Size3d, + Point3d, + Normal3d, + Rotation3d, + Scale3d, + } from "atlas/units"; + import { + Skybox, + Light, + SpotLight, + DirectionalLight, + AreaLight, + RenderTarget, + Texture, + Cubemap, + } from "atlas/graphics"; + import { + AxisTrigger, + Trigger, + Key, + MouseButton, + InputAction, + AxisPacket, + } from "atlas/input"; + import { QueryResult } from "bezel"; + import { AudioEngine } from "finewave"; + import { Atmosphere } from "hydra"; + + export type Fog = { + color: Color; + intensity: number; + }; + + export type VolumetricLighting = { + enabled: boolean; + density: number; + weight: number; + decay: number; + exposure: number; + }; + + export type LightBloomConfiguration = { + radius: number; + maxSamples: number; + }; + + export type RimLightingConfiguration = { + color: Color; + intensity: number; + }; + + export type Environment = { + fog: Fog; + volumetricLighting: VolumetricLighting; + lightBloom: LightBloomConfiguration; + rimLighting: RimLightingConfiguration; + lookupTexture: Texture; + }; + + export class Scene { + name: string; + + setAmbientIntensity(intensity: number): void; + setAutomaticAmbient(enabled: boolean): void; + + setSkybox(skybox: Skybox): void; + useAtmosphereSkybox(enabled: boolean): void; + + setEnvironment(environment: Environment): void; + + setAmbientColor(color: Color): void; + setAmbientIntensity(intensity: number): void; + addDirectionalLight(light: DirectionalLight): void; + addLight(light: Light): void; + addSpotLight(light: SpotLight): void; + addAreaLight(light: AreaLight): void; + + getCamera(): Camera; + getWindow(): Window; + + atmosphere: Atmosphere; + } + + export abstract class Component { + parentId: number; + + abstract init(): void; + abstract update(deltaTime: number): void; + beforePhysics(): void; + atAttach(): void; + + onCollisionEnter(other: GameObject): void; + onCollisionStay(other: GameObject): void; + onCollisionExit(other: GameObject): void; + onSignalReceive(signal: string, sender: GameObject): void; + onSignalEnd(signal: string, sender: GameObject): void; + onQueryReceive(query: QueryResult, sender: GameObject): void; + + getParent(): GameObject; + getParent( + type: new (...args: any[]) => T, + ): T | null; + getObject(identifier: number | string): CoreObject; + getScene(): Scene; + getWindow(): Window; + } + + export class Material { + constructor(); + + albedo: Color; + metallic: number; + roughness: number; + ao: number; + reflectivity: number; + emissiveColor: Color; + emissiveIntensity: number; + normalMapStrength: number; + useNormalMap: boolean; + transmittance: number; + ior: number; + } + + export class CoreVertex { + constructor( + position?: Position3d, + color?: Color, + textureCoord?: Position2d, + normal?: Normal3d, + tangent?: Normal3d, + bitangent?: Normal3d, + ); + + position: Position3d; + color: Color; + textureCoord: Position2d; + normal: Normal3d; + tangent: Normal3d; + bitangent: Normal3d; + } + + export class Instance { + position: Position3d; + rotation: Rotation3d; + scale: Scale3d; + + move(position: Position3d): void; + setPosition(position: Position3d): void; + setRotation(rotation: Rotation3d): void; + rotate(rotation: Rotation3d): void; + setScale(scale: Scale3d): void; + scaleBy(scale: Scale3d): void; + + equals(other: Instance): boolean; + } + + export abstract class GameObject { + id: number; + components: Component[]; + position: Position3d; + rotation: Rotation3d; + scale: Scale3d; + name: string; + + constructor(); + + attachTexture(texture: Texture): void; + setPosition(position: Position3d): void; + move(position: Position3d): void; + lookAt(target: Position3d, up?: Normal3d): void; + setRotation(rotation: Rotation3d): void; + rotate(rotation: Rotation3d): void; + setScale(scale: Scale3d): void; + scaleBy(scale: Scale3d): void; + show(): void; + hide(): void; + + as(type: new (...args: any[]) => T): T | null; + + addComponent(component: T): void; + } + + export abstract class UIObject extends GameObject { + getSize(): Size2d; + getScreenPosition(): Position2d; + abstract setScreenPosition(position: Position2d): void; + } + + export class CoreObject extends GameObject { + vertices: CoreVertex[]; + indices: number[]; + textures: Texture[]; + material: Material; + instances: Instance[]; + position: Position3d; + rotation: Rotation3d; + scale: Scale3d; + castsShadows: boolean; + name: string; + + constructor(); + + makeEmissive(color: Color, intensity: number): void; + attachVertices(vertices: CoreVertex[]): void; + attachIndices(indices: number[]): void; + attachTexture(texture: Texture): void; + + setPosition(position: Position3d): void; + move(position: Position3d): void; + setRotation(rotation: Rotation3d): void; + setRotationQuaternion(rotation: Quaternion): void; + rotate(rotation: Rotation3d): void; + lookAt(target: Position3d, up?: Normal3d): void; + setScale(scale: Scale3d): void; + scaleBy(scale: Scale3d): void; + + clone(): CoreObject; + + show(): void; + hide(): void; + + addComponent(component: T): void; + + enableDeferredRendering(): void; + disableDeferredRendering(): void; + + createInstance(): Instance; + + getComponent( + type: new (...args: any[]) => T, + ): T | null; + + static box(size: Size3d): CoreObject; + static plane(size: Size2d): CoreObject; + static pyramid(size: Size3d): CoreObject; + static sphere( + radius: number, + sectorCount: number, + stackCount: number, + ): CoreObject; + } + + export class Model extends GameObject { + static fromResource(path: string): Model; + + getObjects(): CoreObject[]; + + override move(position: Position3d): void; + override setPosition(position: Position3d): void; + override setRotation(rotation: Rotation3d): void; + override lookAt(target: Position3d, up?: Normal3d): void; + override rotate(rotation: Rotation3d): void; + override setScale(scale: Scale3d): void; + override scaleBy(scale: Scale3d): void; + + override show(): void; + override hide(): void; + override attachTexture(texture: Texture): void; + } + + export enum ResourceType { + File, + Texture, + SpecularMap, + Audio, + Font, + Model, + } + + export class Resource { + type: ResourceType; + path: string; + name: string; + + constructor(type: ResourceType, path: string, name: string); + + static fromAssetPath( + path: string, + type: ResourceType, + name?: string, + ): Resource; + + static fromName(name: string, type: ResourceType): Resource | null; + } + + export class ResourceGroup { + resources: Resource[]; + name: string; + + constructor(resources: Resource[], name: string); + + addResource(resource: Resource): void; + getResourceByName(name: string): Resource | null; + } + + export class Camera { + position: Position3d; + target: Point3d; + fov: number; + nearClip: number; + farClip: number; + orthographicSize: number; + movementSpeed: number; + mouseSensitivity: number; + controllerLookSensitivity: number; + lookSmoothness: number; + useOrthographic: boolean; + focusDepth: number; + focusRange: number; + + constructor(); + + move(offset: Position3d): void; + setPosition(position: Position3d): void; + setPositionKeepingOrientation(position: Position3d): void; + lookAt(target: Point3d, up?: Normal3d): void; + moveTo(target: Point3d, speed: number): void; + getDirection(): Normal3d; + } + + export type ViewInformation = { + position: Position3d; + target: Point3d; + time: number; + deltaTime: number; + }; + + export type WindowConfiguration = { + title: string; + width: number; + height: number; + renderScale: number; + mouseCaptured: boolean; + posX: number; + posY: number; + multisampling: boolean; + editorControls: boolean; + decorations: boolean; + resizable: boolean; + transparent: boolean; + alwaysOnTop: boolean; + opacity: number; + aspectRatioX: number; + aspectRatioY: number; + ssaoScale: number; + }; + + export type VideoMode = { + width: number; + height: number; + refreshRate: number; + }; + + export class Monitor { + monitorId: number; + primary: boolean; + + queryVideoModes(): VideoMode[]; + getCurrentVideoMode(): VideoMode; + getPhysicalSize(): Size2d; + getPosition(): Position2d; + getContentScale(): number; + getName(): string; + } + + export enum ControllerAxis { + LeftStick, + LeftStickX, + LeftStickY, + RightStick, + RightStickX, + RightStickY, + Trigger, + TriggerLeft, + TriggerRight, + } + + export enum ControllerButton { + A = 0, + B, + X, + Y, + LeftBumper, + RightBumper, + Back, + Start, + Guide, + LeftThumb, + RightThumb, + DPadUp, + DPadRight, + DPadDown, + DPadLeft, + ButtonCount, + } + + export enum NintendoControllerButton { + B = 0, + A, + Y, + X, + L, + R, + ZL, + ZR, + Minus, + Plus, + LeftStick, + RightStick, + DPadUp, + DPadRight, + DPadDown, + DPadLeft, + ButtonCount, + } + + export enum SonyControllerButton { + Cross = 0, + Circle, + Square, + Triangle, + L1, + R1, + L2, + R2, + Share, + Options, + LeftStick, + RightStick, + DPadUp, + DPadRight, + DPadDown, + DPadLeft, + ButtonCount, + } + + export const CONTROLLER_UNDEFINED = -2; + + export class Gamepad { + controllerId: number; + name: string; + connected: boolean; + + getAxisTrigger(axis: ControllerAxis): AxisTrigger; + static getGlobalAxisTrigger(axis: ControllerAxis): AxisTrigger; + getButtonTrigger(button: ControllerButton): Trigger; + static getGlobalButtonTrigger(button: ControllerButton): Trigger; + + runble(strength: number, duration: number): void; + } + + export type Controller = Gamepad; + + export class Joystick { + joystickId: number; + name: string; + connected: boolean; + + getSingleAxisTrigger(axisIndex: number): AxisTrigger; + getDualAxisTrigger(axisIndexX: number, axisIndexY: number): AxisTrigger; + getButtonTrigger(buttonIndex: number): Trigger; + + getAxisCount(): number; + getButtonCount(): number; + } + + export type ControllerID = { + id: number; + name: string; + isJoystick: boolean; + }; + + export class Window { + title: string; + width: number; + height: number; + currentFrame: number; + + audioEngine: AudioEngine; + + setClearColor(color: Color): void; + close(): void; + setFullscreen(enabled: boolean): void; + setFullscreen(monitor: Monitor): void; + setWindowed(config: WindowConfiguration): void; + + enumerateMonitors(): Monitor[]; + getControllers(): ControllerID[]; + getController(id: ControllerID): Controller | null; + getJoystick(id: ControllerID): Joystick | null; + + instantiate(object: GameObject): void; + destroy(object: GameObject): void; + + addUIObject(object: UIObject): void; + setCamera(camera: Camera): void; + setScene(scene: Scene): void; + getTime(): number; + isKeyActive(key: Key): boolean; + + isMouseButtonActive(button: MouseButton): boolean; + isMouseButtonPressed(button: MouseButton): boolean; + getTextInput(): string; + startTextInput(): void; + stopTextInput(): void; + isTextInputActive(): boolean; + + isControllerButtonPressed( + controllerID: number, + buttonIndex: number, + ): boolean; + getControllerAxisValue(controllerID: number, axisIndex: number): number; + getControllerAxisPairValue( + controllerID: number, + axisIndexX: number, + axisIndexY: number, + ): Position2d; + + releaseMouse(): void; + captureMouse(): void; + getCursorPosition(): Position2d; + + main: Window; + + getCurrentScene(): Scene; + getCamera(): Camera; + addRenderTarget(): RenderTarget; + getSize(): Size2d; + activateDebug(): void; + desactivateDebug(): void; + + getDeltaTime(): number; + getFramesPerSecond(): number; + gravity: number; + + useAtlasTracer(enabled: boolean): void; + setLogOutput( + showLogs: boolean, + showWarnings: boolean, + showErrors: boolean, + ): void; + + usesDeferred: boolean; + + getRenderScale(): number; + useMetalUpscaling(ratio: number): void; + isMetalUpscalingEnabled(): boolean; + getMetalUpscalingRatio(): number; + + getSSAORenderScale(): number; + + addInputAction(action: InputAction): void; + resetInputActions(): void; + getInputAction(name: string): InputAction | null; + + isActionTriggered(name: string): boolean; + isActionCurrentlyActive(name: string): boolean; + getActionAxisValue(name: string): AxisPacket; + } +} + +declare module "atlas/graphics" { + import { Resource, ResourceGroup } from "atlas"; + import { + Color, + Magnitude2d, + Position3d, + Magnitude3d, + Rotation3d, + Size2d, + } from "atlas/units"; + + export enum TextureType { + Color, + Specular, + Cubemap, + Depth, + DepthCube, + Normal, + Parallax, + SSAONoise, + SSAO, + Metallic, + Roughness, + AO, + Opacity, + HDR, + } + + export class Texture { + type: TextureType; + resource: Resource; + width: number; + height: number; + channels: number; + id: number; + borderColor: Color; + + static fromResource( + resource: Resource | string, + type: TextureType, + ): Texture; + + static createEmpty( + width: number, + height: number, + type: TextureType, + borderColor?: Color, + ): Texture; + + static createColor( + color: Color, + type: TextureType, + width: number, + height: number, + ): Texture; + + createCheckerboard( + width: number, + height: number, + checkSize: number, + color1: Color, + color2: Color, + ): void; + + createDoubleCheckerboard( + width: number, + height: number, + checkSizeBig: number, + checkSizeSmall: number, + color1: Color, + color2: Color, + color3: Color, + ): void; + + displayToWindow(): void; + } + + export class Cubemap { + resources: Resource[]; + id: number; + + constructor(resources: Resource[]); + + getAverageColor(): Color; + static fromResourceGroup(resourceGroup: ResourceGroup): Cubemap | null; + updateWithColors(colors: Color[]): void; + } + + export enum RenderTargetType { + Scene, + Multisampled, + Shadow, + CubeShadow, + GBuffer, + SSAO, + SSAOBlur, + } + + export enum RenderPassType { + Deferred, + Forward, + PathTracing, + } + + export const Effects: { + Inversion: { type: "Inversion" }; + Grayscale: { type: "Grayscale" }; + Sharpen: { type: "Sharpen" }; + Blur: { type: "Blur"; magnitude: number }; + EdgeDetection: { type: "EdgeDetection" }; + ColorCorrection: { + type: "ColorCorrection"; + exposure: number; + contrast: number; + saturation: number; + gamma: number; + temperature: number; + tint: number; + }; + MotionBlur: { type: "MotionBlur"; size: number; separation: number }; + ChromaticAberration: { + type: "ChromaticAberration"; + red: number; + green: number; + blue: number; + direction: Magnitude2d; + }; + Posterization: { type: "Posterization"; levels: number }; + Pixelation: { type: "Pixelation"; pixelSize: number }; + Dialation: { type: "Dilation"; size: number; separation: number }; + Dilation: { type: "Dilation"; size: number; separation: number }; + FilmGrain: { type: "FilmGrain"; amount: number }; + }; + + export type Effect = + | keyof typeof Effects + | (typeof Effects)[keyof typeof Effects]; + + export class RenderTarget { + type: RenderTargetType; + resolution: number; + outTextures: Texture[]; + depthTexture: Texture | null; + + constructor(type: RenderTargetType, resolution: number); + + addEffect(effect: Effect): void; + + addToPassQueue(type: RenderPassType): void; + display(): void; + } + + export class Skybox { + cubemap: Cubemap; + + constructor(cubemap: Cubemap); + } + + export class AmbientLight { + color: Color; + intensity: number; + + constructor(color?: Color, intensity?: number); + } + + export class Light { + position: Position3d; + color: Color; + shineColor: Color; + intensity: number; + distance: number; + + constructor( + position?: Position3d, + color?: Color, + distance?: number, + shineColor?: Color, + intensity?: number, + ); + + setColor(color: Color): void; + // Also calls addDebugObject(Window&); + createDebugObject(): void; + castShadows(resolution: number): void; + } + + export class DirectionalLight { + direction: Magnitude3d; + color: Color; + shineColor: Color; + intensity: number; + + constructor( + direction?: Magnitude3d, + color?: Color, + shineColor?: Color, + intensity?: number, + ); + + setColor(color: Color): void; + castShadows(resolution: number): void; + } + + export class SpotLight { + position: Position3d; + direction: Magnitude3d; + color: Color; + shineColor: Color; + range: number; + cutOff: number; + outerCutOff: number; + intensity: number; + + constructor( + position?: Position3d, + direction?: Magnitude3d, + color?: Color, + cutOff?: number, + outerCutOff?: number, + shineColor?: Color, + intensity?: number, + range?: number, + ); + + setColor(color: Color): void; + // Also calls addDebugObject(Window&); + createDebugObject(): void; + lookAt(target: Position3d): void; + castShadows(resolution: number): void; + } + + export class AreaLight { + position: Position3d; + right: Magnitude3d; + up: Magnitude3d; + size: Size2d; + color: Color; + shineColor: Color; + intensity: number; + range: number; + angle: number; + castsBothSides: boolean; + rotation: Rotation3d; + + constructor( + position?: Position3d, + right?: Magnitude3d, + up?: Magnitude3d, + size?: Size2d, + color?: Color, + shineColor?: Color, + intensity?: number, + range?: number, + angle?: number, + castsBothSides?: boolean, + rotation?: Rotation3d, + ); + + getNormal(): Magnitude3d; + setColor(color: Color): void; + setRotation(rotation: Rotation3d): void; + rotate(delta: Rotation3d): void; + // Also calls addDebugObject(Window&); + createDebugObject(): void; + castShadows(resolution: number): void; + } +} + +declare module "atlas/units" { + export class Position3d { + x: number; + y: number; + z: number; + + constructor(x: number, y: number, z: number); + + static zero(): Position3d; + static down(): Position3d; + static up(): Position3d; + static forward(): Position3d; + static back(): Position3d; + static right(): Position3d; + static left(): Position3d; + static invalid(): Position3d; + + add(other: Position3d | number): Position3d; + subtract(other: Position3d | number): Position3d; + multiply(other: Position3d | number): Position3d; + divide(other: Position3d | number): Position3d; + is(other: Position3d): boolean; + + normalized(): Position3d; + toString(): string; + } + + export type Scale3d = Position3d; + export type Size3d = Position3d; + export type Point3d = Position3d; + export type Normal3d = Position3d; + export type Magnitude3d = Position3d; + export type Impulse3d = Position3d; + export type Force3d = Position3d; + export type Vector3d = Position3d; + export type Velocity3d = Position3d; + export type Rotation3d = Position3d; + + export class BoundingBox { + min: Position3d; + max: Position3d; + + constructor(min: Position3d, max: Position3d); + + toString(): string; + contains(point: Position3d): boolean; + intersects(other: BoundingBox): boolean; + } + + export class Quaternion { + x: number; + y: number; + z: number; + w: number; + + constructor(x: number, y: number, z: number, w: number); + constructor(rotation: Rotation3d); + + toEuler(): Rotation3d; + static fromEuler(rotation: Rotation3d): Quaternion; + } + + export class Color { + r: number; + g: number; + b: number; + a: number; + + constructor(r: number, g: number, b: number, a?: number); + + add(other: Color | number): Color; + subtract(other: Color | number): Color; + multiply(other: Color | number): Color; + divide(other: Color | number): Color; + is(other: Color): boolean; + + static white(): Color; + static black(): Color; + static red(): Color; + static green(): Color; + static blue(): Color; + static transparent(): Color; + static yellow(): Color; + static cyan(): Color; + static magenta(): Color; + static gray(): Color; + static orange(): Color; + static purple(): Color; + static brown(): Color; + static pink(): Color; + static lime(): Color; + static navy(): Color; + static teal(): Color; + static olive(): Color; + static maroon(): Color; + + static fromHex(hex: string): Color; + static mix(color1: Color, color2: Color, t: number): Color; + } + + export enum Direction3d { + Up, + Down, + Left, + Right, + Forward, + Backward, + } + + export class Position2d { + x: number; + y: number; + + constructor(x: number, y: number); + + static zero(): Position2d; + static up(): Position2d; + static down(): Position2d; + static left(): Position2d; + static right(): Position2d; + static invalid(): Position2d; + + add(other: Position2d | number): Position2d; + subtract(other: Position2d | number): Position2d; + multiply(other: Position2d | number): Position2d; + divide(other: Position2d | number): Position2d; + + is(other: Position2d): boolean; + } + + export type Scale2d = Position2d; + export type Point2d = Position2d; + export type Movement2d = Position2d; + export type Magnitude2d = Position2d; + + export class Radians { + value: number; + + constructor(value: number); + + add(other: Radians): Radians; + subtract(other: Radians): Radians; + multiply(other: Radians | number): Radians; + divide(other: Radians | number): Radians; + + toNumber(): number; + static fromDegrees(degrees: number): Radians; + toDegrees(): number; + } + + export class Size2d { + width: number; + height: number; + + constructor(width: number, height: number); + + static zero(): Size2d; + + toString(): string; + + add(other: Size2d | number): Size2d; + subtract(other: Size2d | number): Size2d; + multiply(other: Size2d | number): Size2d; + divide(other: Size2d | number): Size2d; + + is(other: Size2d): boolean; + } +} + +declare module "atlas/audio" { + import { Component, Resource } from "atlas"; + import { Color, Position3d } from "atlas/units"; + import { AudioSource } from "finewave"; + + export class AudioPlayer extends Component { + constructor(); + + override init(): void; + play(): void; + pause(): void; + stop(): void; + setVolume(volume: number): void; + setLoop(loop: boolean): void; + + setSource(resource: Resource): void; + + override update(dt: number): void; + + setPosition(position: Position3d): void; + useSpatialAudio(enabled: boolean): void; + + source: AudioSource; + } +} + +declare module "atlas/input" { + import { Position2d } from "atlas/units"; + + export enum Key { + Unknown, + Space, + Apostrophe, + Comma, + Minus, + Period, + Slash, + Key0, + Key1, + Key2, + Key3, + Key4, + Key5, + Key6, + Key7, + Key8, + Key9, + Semicolon, + Equal, + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + LeftBracket, + Backslash, + RightBracket, + GraveAccent, + Escape, + Enter, + Tab, + Backspace, + Insert, + Delete, + Right, + Left, + Down, + Up, + PageUp, + PageDown, + Home, + End, + CapsLock, + ScrollLock, + NumLock, + PrintScreen, + Pause, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + F16, + F17, + F18, + F19, + F20, + F21, + F22, + F23, + F24, + F25, + KP0, + KP1, + KP2, + KP3, + KP4, + KP5, + KP6, + KP7, + KP8, + KP9, + KPDecimal, + KPDivide, + KPMultiply, + KPSubtract, + KPAdd, + KPEnter, + KPEqual, + LeftShift, + LeftControl, + LeftAlt, + LeftSuper, + RightShift, + RightControl, + RightAlt, + RightSuper, + Menu, + } + + export enum MouseButton { + Left, + Right, + Middle, + X1, + X2, + Button6, + Button7, + Button8, + Last, + } + + export enum TriggerType { + MouseButton, + Key, + ControllerButton, + } + + export type ControllerButtonTrigger = { + controllerID: number; + buttonIndex: number; + }; + + export class Trigger { + type: TriggerType; + mouseButton?: MouseButton; + key?: Key; + controllerButton?: ControllerButtonTrigger; + + static fromKey(key: Key): Trigger; + static fromMouseButton(mouseButton: MouseButton): Trigger; + static fromControllerButton( + controllerID: number, + buttonIndex: number, + ): Trigger; + } + + export enum AxisTriggerType { + MouseAxis, + KeyCustom, + ControllerAxis, + } + + export class AxisTrigger { + type: AxisTriggerType; + + positiveX: Trigger; + negativeX: Trigger; + positiveY: Trigger; + negativeY: Trigger; + + controllerId?: number; + controllerAxisSingle: boolean; + axisIndex?: number; + axisIndexY: number; + + isJoystick: boolean; + + static fromMouse(): AxisTrigger; + static fromKeys( + positiveX: Key, + negativeX: Key, + positiveY: Key, + negativeY: Key, + ): AxisTrigger; + static fromControllerAxis( + controllerId: number, + axisIndex: number, + single: boolean, + axisIndexY?: number, + ): AxisTrigger; + } + + export type AxisPacket = { + deltaX: number; + deltaY: number; + x: number; + y: number; + valueX: number; + valueY: number; + inputDeltaX: number; + inputDeltaY: number; + hasValueInput: boolean; + hasDeltaInput: boolean; + }; + + export type MousePacket = { + xpos: number; + ypos: number; + xoffset: number; + yoffset: number; + constrainPitch: boolean; + firstMouse: boolean; + }; + + export type MouseScrollPacket = { + xoffset: number; + yoffset: number; + }; + + export class InputAction { + triggers: Trigger[]; + axisTriggers: AxisTrigger[]; + name: string; + isAxis: boolean; + isAxisSingle: boolean; + normalized: boolean; + invertY: boolean; + + static createButtonAction( + name: string, + triggers: Trigger[], + ): InputAction; + static createAxisAction( + name: string, + axisTriggers: AxisTrigger[], + ): InputAction; + static createSingleAxisAction( + name: string, + positiveTrigger: Trigger, + negativeTrigger: Trigger, + ): InputAction; + } + + export const Input: { + addAction(action: InputAction): InputAction; + resetActions(): void; + + isKeyActive(key: Key): boolean; + isKeyPressed(key: Key): boolean; + isMouseButtonActive(button: MouseButton): boolean; + isMouseButtonPressed(button: MouseButton): boolean; + + getTextInput(): string; + startTextInput(): void; + stopTextInput(): void; + isTextInputActive(): boolean; + + isControllerButtonPressed( + controllerID: number, + buttonIndex: number, + ): boolean; + getControllerAxisValue(controllerID: number, axisIndex: number): number; + getControllerAxisPairValue( + controllerID: number, + axisIndexX: number, + axisIndexY: number, + ): Position2d; + + captureMouse(): void; + releaseMouse(): void; + getMousePosition(): Position2d; + + isActionTriggered(name: string): boolean; + isActionCurrentlyActive(name: string): boolean; + getAxisActionValue(name: string): AxisPacket; + }; + + export abstract class Interactive { + abstract onKeyPress(key: Key, dt: number): void; + abstract onKeyRelease(key: Key, dt: number): void; + abstract onMouseMove(packet: MousePacket, dt: number): void; + abstract onMouseButtonPress(button: MouseButton, dt: number): void; + abstract onMouseScroll(packet: MouseScrollPacket, dt: number): void; + abstract onEachFrame(dt: number): void; + } +} + +declare module "atlas/particle" { + import { + Position3d, + Color, + Magnitude3d, + Rotation3d, + Scale3d, + Normal3d, + } from "atlas/units"; + import { GameObject } from "atlas"; + import { Texture } from "atlas/graphics"; + + export enum ParticleEmissionType { + Fountain, + Ambient, + } + + export type ParticleSettings = { + minLifetime: number; + maxLifetime: number; + minSize: number; + maxSize: number; + fadeSpeed: number; + gravity: number; + spread: number; + speedVariation: number; + }; + + export type Particle = { + position: Position3d; + velocity: Magnitude3d; + color: Color; + lifetime: number; + maxLifetime: number; + size: number; + active: boolean; + }; + + export class ParticleEmitter extends GameObject { + settings: ParticleSettings; + constructor(maxParticles: number); + + override attachTexture(texture: Texture): void; + setColor(color: Color): void; + enableTexture(): void; + disableTexture(): void; + override setPosition(position: Position3d): void; + override move(position: Position3d): void; + getPosition(): Position3d; + + setEmissionType(type: ParticleEmissionType): void; + setDirection(direction: Magnitude3d): void; + setSpawnRadius(radius: number): void; + setSpawnRate(rate: number): void; + setParticleSettings(settings: ParticleSettings): void; + + emitOnce(): void; + emitContinuous(): void; + startEmission(): void; + stopEmission(): void; + emitBurst(count: number): void; + + // Disabled + override setRotation(rotation: Rotation3d): void; + override setScale(scale: Scale3d): void; + override lookAt(target: Position3d, up?: Normal3d): void; + override rotate(rotation: Rotation3d): void; + override scaleBy(scale: Scale3d): void; + override show(): void; + override hide(): void; + } +} + +declare module "bezel" { + import { + Position3d, + Normal3d, + Point3d, + Size3d, + Force3d, + Impulse3d, + Velocity3d, + } from "atlas/units"; + import { GameObject, Component } from "atlas"; + + export type RaycastHit = { + position: Position3d; + normal: Normal3d; + distance: number; + object: GameObject; + didHit: boolean; + }; + + export type RaycastResult = { + hits: RaycastHit[]; + hit: RaycastHit | null; + closestDistance: number; + }; + + export type OverlapHit = { + contactPoint: Position3d; + penerationAxis: Point3d; + penetrationDepth: number; + object: GameObject; + }; + + export type OverlapResult = { + hits: OverlapHit[]; + hitAny: boolean; + }; + + export type SweepHit = { + position: Position3d; + normal: Normal3d; + distance: number; + percentage: number; + object: GameObject; + }; + + export type SweepResult = { + hits: SweepHit[]; + closest: SweepHit | null; + hitAny: boolean; + endPosition: Position3d; + }; + + export enum QueryOperation { + RaycastAll, + Raycast, + RasycastWorld, + RaycastWorldAll, + RaycastTagged, + RaycastTaggedAll, + Movement, + Overlap, + MovementAll, + } + + export type QueryResult = { + operation: QueryOperation; + raycastResult?: RaycastResult; + overlapResult?: OverlapResult; + sweepResult?: SweepResult; + }; + + export type WorldBody = {}; + + export type JointMember = GameObject | WorldBody; + + export enum SpringMode { + FrequencyAndDamping, + StiffnessAndDamping, + } + + export enum Space { + Local, + Global, + } + + export type Spring = { + enabled: boolean; + mode: SpringMode; + frequency: number; + dampingRatio: number; + stiffness: number; + damping: number; + }; + + export type AngleLimits = { + enabled: boolean; + minAngle: number; + maxAngle: number; + }; + + export type Motor = { + enabled: boolean; + maxForce: number; + maxTorque: number; + }; + + export abstract class Joint extends Component { + parent: JointMember; + child: JointMember; + space: Space; + anchor: Position3d; + breakForce: number; + breakTorque: number; + + override init(): void; + override update(deltaTime: number): void; + + abstract override beforePhysics(): void; + abstract breakJoint(): void; + } + + export class FixedJoint extends Joint { + override beforePhysics(): void; + override breakJoint(): void; + } + + export class HingeJoint extends Joint { + axis1: Normal3d; + axis2: Normal3d; + angleLimits: AngleLimits; + motor: Motor; + + override beforePhysics(): void; + override breakJoint(): void; + } + + export class SpringJoint extends Joint { + anchorB: Position3d; + restLength: number; + useLimits: boolean; + minLength: number; + maxLength: number; + + spring: Spring; + + override beforePhysics(): void; + override breakJoint(): void; + } + + export type VehicleWheelSettings = { + position: Position3d; + enableSuspensionForcePoint: boolean; + suspensionForcePoint: Position3d; + + suspensionDirection: Normal3d; + steeringAxis: Normal3d; + wheelUp: Normal3d; + wheelForward: Normal3d; + + suspensionMinLength: number; + suspensionMaxLength: number; + suspensionPreloadLength: number; + suspensionFrequencyHz: number; + suspensionDampingRatio: number; + + radius: number; + width: number; + + inertia: number; + angularDamping: number; + maxSteerAngleDegrees: number; + maxBrakeTorque: number; + maxHandBrakeTorque: number; + }; + + export type VehicleDifferential = { + leftWheel: number; + rightWheel: number; + differentialRatio: number; + leftRightSplit: number; + limitedSlipRatio: number; + engineTorqueRatio: number; + }; + + export type VehicleEngine = { + maxTorque: number; + minRPM: number; + maxRPM: number; + inertia: number; + angularDamping: number; + }; + + export enum VehicleTransmissionMode { + Auto, + Manual, + } + + export type VehicleTransmission = { + mode: VehicleTransmissionMode; + gearRatios: number[]; + reverseGearRatios: number[]; + switchTime: number; + clutchReleaseTime: number; + switchLatency: number; + shiftUpRPM: number; + shiftDownRPM: number; + clutchStrength: number; + }; + + export type VehicleControllerSettings = { + engine: VehicleEngine; + transmission: VehicleTransmission; + differentials: VehicleDifferential[]; + differentialLimitedSlipRatio: number; + }; + + export type VehicleSettings = { + up: Normal3d; + forward: Normal3d; + + maxPitchRollAngleDeg: number; + + wheels: VehicleWheelSettings[]; + controller: VehicleControllerSettings; + + maxSlopAngleDeg: number; + }; + + export class Vehicle extends Component { + settings: VehicleSettings; + forward: number; + right: number; + brake: number; + handBrake: number; + + override atAttach(): void; + override beforePhysics(): void; + + requestRecreate(): void; + + override init(): void; + override update(deltaTime: number): void; + } + + export type CapsuleCollider = { + radius: number; + height: number; + }; + + export type BoxCollider = { + size: Size3d; + }; + + export type SphereCollider = { + radius: number; + }; + + export type MeshCollider = {}; + + export type Collider = + | CapsuleCollider + | BoxCollider + | SphereCollider + | MeshCollider; + + export class Rigidbody extends Component { + sendSignal: string; + isSensor: boolean; + + override atAttach(): void; + override init(): void; + override beforePhysics(): void; + override update(deltaTime: number): void; + + clone(): Rigidbody; + + addCollider(collider: Collider): void; + + setFriction(friction: number): void; + applyForce(force: Force3d): void; + applyForceAtPoint(force: Force3d, point: Position3d): void; + applyImpulse(impulse: Impulse3d): void; + + setLinearVelocity(velocity: Velocity3d): void; + addLinearVelocity(velocity: Velocity3d): void; + setAngularVelocity(velocity: Velocity3d): void; + addAngularVelocity(velocity: Velocity3d): void; + + setMaxLinearVelocity(velocity: number): void; + setMaxAngularVelocity(velocity: number): void; + + getLinearVelocity(): Velocity3d; + getAngularVelocity(): Velocity3d; + getVelocity(): Velocity3d; + + raycast(direction: Normal3d, maxDistance: number): RaycastResult; + raycastAll(direction: Normal3d, maxDistance: number): RaycastResult; + raycastWorld( + origin: Position3d, + direction: Normal3d, + maxDistance: number, + ): RaycastResult; + raycastWorldAll( + origin: Position3d, + direction: Normal3d, + maxDistance: number, + ): RaycastResult; + raycastTagged( + tags: string[], + direction: Normal3d, + maxDistance: number, + ): RaycastResult; + raycastTaggedAll( + tags: string[], + direction: Normal3d, + maxDistance: number, + ): RaycastResult; + + overlap(): OverlapResult; + overlapWithCollider(collider: Collider): OverlapResult; + overlapWithColliderWorld( + collider: Collider, + position: Position3d, + ): OverlapResult; + + predictMovementWithCollider( + endPosition: Position3d, + collider: Collider, + ): SweepResult; + predictMovement(endPosition: Position3d): SweepResult; + predictMovementWithColliderWorld( + startPosition: Position3d, + endPosition: Position3d, + collider: Collider, + ): SweepResult; + predictMovementWorld( + startPosition: Position3d, + endPosition: Position3d, + ): SweepResult; + predictMovementWithColliderAll( + endPosition: Position3d, + collider: Collider, + ): SweepResult; + predictMovementAll(endPosition: Position3d): SweepResult; + predictMovementWithColliderAllWorld( + startPosition: Position3d, + endPosition: Position3d, + collider: Collider, + ): SweepResult; + predictMovementAllWorld( + startPosition: Position3d, + endPosition: Position3d, + ): SweepResult; + + hasTag(tag: string): boolean; + addTag(tag: string): void; + removeTag(tag: string): void; + + setDamping(linearDamping: number, angularDamping: number): void; + setMass(mass: number): void; + setRestituition(restitution: number): void; + setMotionType(motionType: "Static" | "Dynamic" | "Kinematic"): void; + } + + export class Sensor extends Rigidbody { + constructor(); // sets isSensor to true + + setSignal(signal: string): void; + } +} + +declare module "aurora" { + import { GameObject, Resource } from "atlas"; + import { Texture } from "atlas/graphics"; + import { + Color, + Position3d, + Rotation3d, + Normal3d, + Scale3d, + } from "atlas/units"; + + export class PerlinNoise { + constructor(seed?: number); + noise(x: number, y: number): number; + } + + export class SimplexNoise { + static noise(xin: number, yin: number): number; + } + + export class WorleyNoise { + constructor(numPoints: number, seed?: number); + noise(x: number, y: number): number; + } + + export class FractalNoise { + constructor(o: number, p: number); + noise(x: number, y: number): number; + } + + export class Noise { + static perlin(x: number, y: number): number; + static simplex(x: number, y: number): number; + static worley(x: number, y: number): number; + static fractal( + x: number, + y: number, + octaves: number, + persistence: number, + ): number; + static seed: number; + static initializedSeed: boolean; + } + + export class Biome { + name: string; + texture: Texture; + color: Color; + useTexture: boolean; + + attachTexture(texture: Texture): void; + + minHeight: number; + maxHeight: number; + minMoisture: number; + maxMoisture: number; + minTemperature: number; + maxTemperature: number; + + constructor( + name: string, + texture: Texture, + color: Color, + useTexture: boolean, + ); + + condition: BiomeFunction; + } + + export type BiomeFunction = (biome: Biome) => void; + + export class Terrain extends GameObject { + attachTexture(texture: Texture): void; + setPosition(position: Position3d): void; + move(position: Position3d): void; + setRotation(rotation: Rotation3d): void; + lookAt(target: Position3d, up?: Normal3d): void; + rotate(rotation: Rotation3d): void; + setScale(scale: Scale3d): void; + scaleBy(scale: Scale3d): void; + show(): void; + hide(): void; + + heightmap: Resource; + moistureTexture: Texture; + temperatureTexture: Texture; + generator: TerrainGenerator; + + createdWithMap: boolean; + width: number; + length: number; + height: number; + + addBiome(biome: Biome): void; + + static fromGenerator(generator: T): Terrain; + static fromHeightmap(heightmap: Resource): Terrain; + + maxPeak: number; + seaLevel: number; + } + + export abstract class TerrainGenerator { + abstract generateHeight(x: number, y: number): number; + applyTo(terrain: Terrain): void; + } + + export class HillGenerator extends TerrainGenerator { + constructor(scale: number, amplitude: number); + + override generateHeight(x: number, y: number): number; + } + + export class MountainGenerator extends TerrainGenerator { + constructor( + scale: number, + amplitude: number, + octaves: number, + persistance: number, + ); + + override generateHeight(x: number, y: number): number; + } + + export class PlainGenerator extends TerrainGenerator { + constructor(scale: number, amplitude: number); + + override generateHeight(x: number, y: number): number; + } + + export class IslandGenerator extends TerrainGenerator { + constructor(numFeatures: number, scale: number); + + override generateHeight(x: number, y: number): number; + } + + export class CompoundGenerator extends TerrainGenerator { + addGenerator(generator: T): void; + override generateHeight(x: number, y: number): number; + } +} + +declare module "finewave" { + import { Position3d } from "atlas/units"; + import { Resource } from "atlas"; + + export class AudioEngine { + setListenerPosition(position: Position3d): void; + setListenerOrientation(forward: Position3d, up: Position3d): void; + setListenerVelocity(velocity: Position3d): void; + setMasterVolume(volume: number): void; + deviceName: string; + } + + export class AudioData { + static fromResource(resource: Resource): AudioData; + isMono: boolean; + resource: Resource; + } + + export class AudioSource { + setData(data: AudioData): void; + fromFile(resource: Resource): void; + play(): void; + pause(): void; + stop(): void; + setLoop(loop: boolean): void; + setVolume(volume: number): void; + setPitch(pitch: number): void; + setPosition(position: Position3d): void; + setVelocity(velocity: Position3d): void; + + isPlaying(): boolean; + playFrom(position: number): void; + disableSpatialization(): void; + applyEffect(effect: AudioEffect): void; + getPosition(): Position3d; + getListenerPosition(): Position3d; + useSpatialization(): void; + } + + export abstract class AudioEffect {} + + export class Reverb extends AudioEffect { + setRoomSize(size: number): void; + setDamping(damping: number): void; + setWetLevel(level: number): void; + setDryLevel(level: number): void; + setWidth(width: number): void; + } + + export class Echo extends AudioEffect { + setDelay(delay: number): void; + setDecay(decay: number): void; + setWetLevel(level: number): void; + setDryLevel(level: number): void; + } + + export class Distortion extends AudioEffect { + setEdge(edge: number): void; + setGain(gain: number): void; + setLowpassCutoff(cutoff: number): void; + } +} + +declare module "graphite" { + import { UIObject, Resource } from "atlas"; + import { Texture } from "atlas/graphics"; + import { Position2d, Position3d, Color, Size2d, Size3d } from "atlas/units"; + + export class Image extends UIObject { + texture: Texture; + position: Position3d; + size: Size2d; + tint: Color; + + constructor(); + constructor( + texture: Texture, + size: Size2d, + position: Position2d, + tint: Color, + ); + + override getSize(): Size2d; + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + style(): UIStyle; + setStyle(style: UIStyle): Image; + + setTexture(texture: Texture): void; + setSize(size: Size2d): void; + } + + export type TextFieldChangeEvent = { + text: string; + cursorPosition: number; + focused: boolean; + }; + + export type ButtonClickEvent = { + label: string; + }; + + export type CheckboxToggleEvent = { + label: string; + checked: boolean; + }; + + export namespace TextField { + export type ChangeCallback = (event: TextFieldChangeEvent) => void; + } + + export class TextField extends UIObject { + text: string; + placeholder: string; + font: Font; + position: Position3d; + fontSize: number; + padding: Size2d; + maximumWidth: number; + textColor: Color; + placeholderColor: Color; + backgroundColor: Color; + borderColor: Color; + focusedBorderColor: Color; + cursorColor: Color; + + constructor(); + + constructor( + font: Font, + maximumWidth: number, + position: Position2d, + text: string, + placeholder: string, + ); + + override getSize(): Size2d; + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + getText(): string; + isFocused(): boolean; + getCursorIndex(): number; + style(): UIStyle; + + setText(text: string): TextField; + setPlaceholder(placeholder: string): TextField; + setPadding(padding: Size2d): TextField; + setMaximumWidth(width: number): TextField; + setFontSize(size: number): TextField; + setStyle(style: UIStyle): TextField; + setOnChange(callback: TextField.ChangeCallback): TextField; + + focus(): void; + blur(): void; + } + + export namespace Button { + export type ClickCallback = (event: ButtonClickEvent) => void; + } + + export class Button extends UIObject { + label: string; + font: Font; + position: Position3d; + fontSize: number; + padding: Size2d; + minimumSize: Size2d; + textColor: Color; + backgroundColor: Color; + hoverBackgroundColor: Color; + pressedBackgroundColor: Color; + borderColor: Color; + hoverBorderColor: Color; + enabled: boolean; + + constructor(); + + constructor(font: Font, label: string, position: Position2d); + + override getSize(): Size2d; + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + getLabel(): string; + isHovered(): boolean; + isEnabled(): boolean; + + style(): UIStyle; + + setLabel(label: string): Button; + setPadding(padding: Size2d): Button; + setMinimumSize(size: Size2d): Button; + setFontSize(size: number): Button; + setStyle(style: UIStyle): Button; + setOnClick(callback: Button.ClickCallback): Button; + setEnabled(enabled: boolean): void; + } + + export namespace Checkbox { + export type ToggleCallback = (event: CheckboxToggleEvent) => void; + } + + export class Checkbox extends UIObject { + label: string; + font: Font; + position: Position3d; + fontSize: number; + padding: Size2d; + boxSize: number; + spacing: number; + checked: boolean; + enabled: boolean; + textColor: Color; + boxBackgroundColor: Color; + hoverBoxBackgroundColor: Color; + borderColor: Color; + activeBorderColor: Color; + checkColor: Color; + + constructor(); + + constructor(font: Font, label: string, position: Position2d); + + override getSize(): Size2d; + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + getLabel(): string; + isChecked(): boolean; + isHovered(): boolean; + isEnabled(): boolean; + + style(): UIStyle; + + setLabel(label: string): Checkbox; + setPadding(padding: Size2d): Checkbox; + setFontSize(size: number): Checkbox; + setBoxSize(size: number): Checkbox; + setSpacing(spacing: number): Checkbox; + setStyle(style: UIStyle): Checkbox; + setOnToggle(callback: Checkbox.ToggleCallback): Checkbox; + setChecked(checked: boolean): void; + setEnabled(enabled: boolean): void; + toggle(): void; + } + + export enum ElementAlignment { + Top, + Center, + Bottom, + } + + export enum LayoutAnchor { + TopLeft, + TopCenter, + TopRight, + CenterLeft, + Center, + CenterRight, + BottomLeft, + BottomCenter, + BottomRight, + } + + export class Column extends UIObject { + constructor(position: Position2d); + constructor( + children: UIObject[], + spacing: number, + padding: Size2d, + position: Position2d, + ); + + spacing: number; + maxSize: Size2d; + padding: Size2d; + children: UIObject[]; + position: Position3d; + alignment: ElementAlignment; + anchor: LayoutAnchor; + + addChild(child: UIObject): void; + setChildren(children: UIObject[]): void; + + override getSize(): Size2d; + + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + style: UIStyle; + setStyle(style: UIStyle): Column; + } + + export class Row extends UIObject { + constructor(position: Position2d); + constructor( + children: UIObject[], + spacing: number, + padding: Size2d, + position: Position2d, + ); + + spacing: number; + maxSize: Size2d; + padding: Size2d; + children: UIObject[]; + position: Position3d; + alignment: ElementAlignment; + anchor: LayoutAnchor; + + addChild(child: UIObject): void; + setChildren(children: UIObject[]): void; + + override getSize(): Size2d; + + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + style: UIStyle; + setStyle(style: UIStyle): Column; + } + + export class Stack extends UIObject { + constructor(position: Position2d); + constructor( + children: UIObject[], + padding: Size2d, + position: Position2d, + ); + + maxSize: Size2d; + padding: Size2d; + children: UIObject[]; + position: Position3d; + horizontalAlignment: ElementAlignment; + verticalAlignment: ElementAlignment; + anchor: LayoutAnchor; + + addChild(child: UIObject): void; + setChildren(children: UIObject[]): void; + + override getSize(): Size2d; + + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + style: UIStyle; + setStyle(style: UIStyle): Column; + } + + export enum UIStyleState { + Normal, + Hovered, + Pressed, + Disabled, + Focused, + Checked, + } + + export type UIStyleStateSnapshot = { + hovered: boolean; + pressed: boolean; + disabled: boolean; + focused: boolean; + checked: boolean; + }; + + export class UIStyleVariant { + paddingValue?: number; + cornerRadiusValue?: number; + borderWidthValue?: number; + backgroundColorValue?: Color; + borderColorValue?: Color; + foregroundColorValue?: Color; + tintColorValue?: Color; + fontValue?: Font; + fontSizeValue?: number; + + padding(value: Size2d): UIStyleVariant; + cornerRadius(value: number): UIStyleVariant; + borderWidth(value: number): UIStyleVariant; + backgroundColor(value: Color): UIStyleVariant; + borderColor(value: Color): UIStyleVariant; + foregroundColor(value: Color): UIStyleVariant; + tintColor(value: Color): UIStyleVariant; + font(value: Font): UIStyleVariant; + fontSize(value: number): UIStyleVariant; + } + + export type UIResolvedStyle = { + padding: Size2d; + cornerRadius: number; + borderWidth: number; + backgroundColor: Color; + borderColor: Color; + foregroundColor: Color; + tintColor: Color; + font: Font; + fontSize: number; + }; + + export class UIStyle { + normal(): UIStyleVariant; + hovered(): UIStyleVariant; + pressed(): UIStyleVariant; + disabled(): UIStyleVariant; + focused(): UIStyleVariant; + checked(): UIStyleVariant; + variant(state: UIStyleState): UIStyleVariant; + } + + export class Theme { + text: UIStyle; + image: UIStyle; + textField: UIStyle; + button: UIStyle; + checkbox: UIStyle; + row: UIStyle; + column: UIStyle; + stack: UIStyle; + + static current(): Theme; + static set(theme: Theme): void; + static reset(): void; + } + + export type Character = { + size: Size2d; + bearing: Position2d; + advance: number; + uvMin: Position2d; + uvMax: Position2d; + }; + + export type FontAtlas = Map; + + export class Font { + name: string; + atlas: Texture; + size: number; + resource: Resource; + texture: Texture; + + static fromResource(resource: Resource): Font; + static getFont(name: string): Font; + + changeSize(size: number): Font; + } + + export class Text extends UIObject { + content: string; + font: Font; + position: Position3d; + fontSize: number; + color: Color; + + constructor(); + constructor( + text: string, + font: Font, + color: Color, + position: Position2d, + ); + + override getSize(): Size2d; + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + style(): UIStyle; + setStyle(style: UIStyle): Text; + setFontSize(size: number): Text; + } +} + +declare module "hydra" { + import { + Position3d, + Size3d, + Force3d, + Magnitude3d, + Color, + Scale3d, + Rotation3d, + Size2d, + } from "atlas/units"; + import { Cubemap } from "atlas/graphics"; + import { ViewInformation, GameObject } from "atlas"; + import { Texture } from "atlas/graphics"; + + export class WorleyNoise3D { + constructor(frequency: number, numDivisions: number); + + getValue(x: number, y: number, z: number): number; + + get3dTexture(size: number): number; + getDetailTexture(size: number): number; + get3dTextureAtAllChannels(size: number): number; + } + + export class Clouds { + constructor(frequency: number, numDivisions: number); + + getCloudTexture(size: number): number; + + position: Position3d; + size: Size3d; + scale: number; + offset: Position3d; + density: number; + densityMultiplier: number; + absorption: number; + scattering: number; + phase: number; + clusterStrength: number; + primaryStepCount: number; + lightStepCount: number; + lightStepMultiplier: number; + minStepLength: number; + wind: Force3d; + } + + export enum WeatherCondition { + Clear, + Rain, + Snow, + Storm, + } + + export type WeatherState = { + condition: WeatherCondition; + intensity: number; + wind: Force3d; + }; + + export type WeatherDelegate = ( + information: ViewInformation, + ) => WeatherState; + + export class Atmosphere { + timeOfDay: number; + secondsPerHour: number; + wind: Magnitude3d; + weatherDelegate: WeatherDelegate; + + enable(): void; + disable(): void; + isEnabled(): boolean; + enableWeather(): void; + disableWeather(): void; + + getNormalizedTime(): number; + getSunAngle(): Magnitude3d; + getMoonAngle(): Magnitude3d; + getLightIntensity(): number; + getLightColor(): Color; + + clouds?: Clouds; + + getSkyboxColors(): Color[]; + createSkyCubemap(size: number): Cubemap; + updateSkyCubemap(cubemap: Cubemap): void; + + castShadowsFromSunlight(resolution: number): void; + useGlobalLight(): void; + + sunColor: Color; + moonColor: Color; + + sunSize: number; + moonSize: number; + sunTintStrength: number; + moonTintStrength: number; + starIntensity: number; + + isDaytime(): boolean; + setTime(hours: number, minutes: number, seconds: number): void; + + addClouds(frequency: number, numDivisions: number): void; + + cycle: boolean; + resetRuntimeState(): void; + } + + export class Fluid extends GameObject { + waveVelocity: number; + + constructor(); + create(extent: Size2d, color: Color): void; + + override move(position: Position3d): void; + override setPosition(position: Position3d): void; + override setRotation(rotation: Rotation3d): void; + override rotate(rotation: Rotation3d): void; + override setScale(scale: Scale3d): void; + + setExtent(extent: Size2d): void; + setWaveVelocity(velocity: number): void; + setWaterColor(color: Color): void; + getPosition(): Position3d; + getScale(): Scale3d; + + normalTexture: Texture; + movementTexture: Texture; + } +} diff --git a/tests/textures/main.ascene b/tests/textures/main.ascene new file mode 100644 index 00000000..bee6f790 --- /dev/null +++ b/tests/textures/main.ascene @@ -0,0 +1,93 @@ +{ + "camera": { + "actions": [], + "automaticMoving": false, + "controllerLookSensitivity": 180.0, + "farClip": 1000.0, + "focusDepth": 20.0, + "focusRange": 10.0, + "fov": 60.0, + "lookSmoothness": 0.15000000596046448, + "mouseSensitivity": 0.10000000149011612, + "movementSpeed": 2.0, + "nearClip": 0.5, + "orthoSize": 5.0, + "orthographic": false, + "position": [ + -5.00730037689209, + 5.268265247344971, + -4.450003623962402 + ], + "target": [ + 0.0, + 0.0, + 0.0 + ] + }, + "environment": { + "atmosphere": { + "enabled": true, + "globalLight": { + "castsShadows": true, + "enabled": true, + "shadowResolution": 4096 + } + }, + "atmosphereSky": true, + "automaticAmbient": true + }, + "id": "main_scene", + "lights": [ + { + "color": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "id": "ambientLight_0", + "intensity": 0.25, + "name": "ambientLight_0", + "position": [ + 0.0, + 0.0, + 0.0 + ], + "type": "ambientLight" + } + ], + "name": "Main Scene", + "objects": [ + { + "components": [], + "material": "materials/Cobblestone.amat", + "name": "Cube", + "position": [ + 0.0, + 0.0, + 0.0 + ], + "rotation": [ + 0.0, + 0.0, + 0.0 + ], + "scale": [ + 14.335800170898438, + 0.05000000074505806, + 14.335800170898438 + ], + "solid_type": "cube", + "type": "solid" + } + ], + "property_syncs": [], + "targets": [ + { + "display": true, + "name": "Main Target", + "render": true, + "type": "multisampled" + } + ] +} diff --git a/tests/textures/materials/Cobblestone.amat b/tests/textures/materials/Cobblestone.amat new file mode 100644 index 00000000..2544e6eb --- /dev/null +++ b/tests/textures/materials/Cobblestone.amat @@ -0,0 +1,38 @@ +{ + "material": { + "albedo": [ + 0.8, + 0.8, + 0.8, + 1 + ], + "albedoTexture": "../assets/textures/cobblestone_floor_09_diff_1k.jpg", + "ao": 1, + "aoTexture": "../assets/textures/cobblestone_floor_09_ao_1k.jpg", + "displacementTexture": "../assets/textures/cobblestone_floor_09_disp_1k.png", + "emissiveColor": [ + 0, + 0, + 0, + 1 + ], + "emissiveIntensity": 0, + "ior": 1.45, + "metallic": 0, + "normalMapStrength": 3.033, + "normalTexture": "../assets/textures/cobblestone_floor_09_nor_gl_1k.jpg", + "reflectivity": 0.5, + "roughness": 0.5, + "roughnessTexture": "../assets/textures/cobblestone_floor_09_rough_1k.jpg", + "textureOffset": [ + 0, + 0 + ], + "textureScale": [ + 5.885, + 4.492 + ], + "transmittance": 0, + "useNormalMap": true + } +} diff --git a/tests/textures/package-lock.json b/tests/textures/package-lock.json new file mode 100644 index 00000000..86d9f9ac --- /dev/null +++ b/tests/textures/package-lock.json @@ -0,0 +1,512 @@ +{ + "name": "textures", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "textures", + "devDependencies": { + "esbuild": "^0.25.5", + "typescript": "^5.9.2" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/tests/textures/package.json b/tests/textures/package.json new file mode 100644 index 00000000..1f7958ac --- /dev/null +++ b/tests/textures/package.json @@ -0,0 +1,13 @@ +{ + "devDependencies": { + "esbuild": "^0.25.5", + "typescript": "^5.9.2" + }, + "name": "textures", + "private": true, + "scripts": { + "atlas:compile": "atlas script compile", + "typecheck": "tsc --noEmit" + }, + "type": "module" +} diff --git a/tests/textures/project.atlas b/tests/textures/project.atlas new file mode 100644 index 00000000..45fef084 --- /dev/null +++ b/tests/textures/project.atlas @@ -0,0 +1,23 @@ +app_name = "textures" +atlas_version = "alpha9" +backend = "AUTO" +name = "textures" +platform = "DESKTOP" + +[game] +assets = ["assets/"] +main_scene = "main.ascene" + +[pack] +icon = "none" +supported_platforms = "all" + +[renderer] +default = "deferred" +global_illumination = false + +[window] +dimensions = [1280, 720] +mouse_capture = false +multisampling = true +ssaoScale = 0.5 diff --git a/tests/textures/tsconfig.json b/tests/textures/tsconfig.json new file mode 100644 index 00000000..186f01e9 --- /dev/null +++ b/tests/textures/tsconfig.json @@ -0,0 +1,49 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "ignoreDeprecations": "6.0", + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "paths": { + "atlas": [ + "lib/atlas.d.ts" + ], + "atlas/*": [ + "lib/*" + ] + }, + "skipLibCheck": true, + "strict": true, + "target": "ES2022", + "verbatimModuleSyntax": true + }, + "exclude": [ + ".git", + "node_modules", + "dist", + "build", + "target", + "extern", + "atlas", + "aurora", + "bezel", + "finewave", + "graphite", + "hydra", + "include", + "opal", + "photon", + "cli", + "docs", + "tests", + "runtime/lib", + "runtime/docs", + "runtime/executable" + ], + "include": [ + "**/*.ts", + "**/*.mts", + "**/*.cts" + ] +}