Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions public/include/remix/remix_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ extern "C" {
REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_ALPHA_CHANNEL = 1 << 21,
REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_TRANSPARENCY_LAYER = 1 << 22,
REMIXAPI_INSTANCE_CATEGORY_BIT_PARTICLE_EMITTER = 1 << 23,
REMIXAPI_INSTANCE_CATEGORY_BIT_LEGACY_EMISSIVE = 1 << 24,
} remixapi_InstanceCategoryBit;

typedef uint32_t remixapi_InstanceCategoryFlags;
Expand Down
2 changes: 1 addition & 1 deletion src/d3d9/d3d9_rtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace dxvk {
const uint32_t kRenderTargetIndex = 0;

#define CATEGORIES_REQUIRE_DRAW_CALL_STATE InstanceCategories::Sky, InstanceCategories::Terrain
#define CATEGORIES_REQUIRE_GEOMETRY_COPY InstanceCategories::Terrain, InstanceCategories::WorldUI
#define CATEGORIES_REQUIRE_GEOMETRY_COPY InstanceCategories::Terrain, InstanceCategories::WorldUI, InstanceCategories::LegacyEmissive

D3D9Rtx::D3D9Rtx(D3D9DeviceEx* d3d9Device, bool enableDrawCallConversion)
: m_rtStagingData(d3d9Device->GetDXVKDevice(), "RtxStagingDataAlloc: D3D9", (VkMemoryPropertyFlagBits) (VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))
Expand Down
103 changes: 103 additions & 0 deletions src/dxvk/imgui/dxvk_imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ namespace dxvk {
{"uitextures", "UI Texture", &RtxOptions::uiTexturesObject()},
{"worldspaceuitextures", "World Space UI Texture", &RtxOptions::worldSpaceUiTexturesObject()},
{"worldspaceuibackgroundtextures", "World Space UI Background Texture", &RtxOptions::worldSpaceUiBackgroundTexturesObject()},
{"legacyemissivetextures", "Legacy Emissive Texture", &RtxOptions::legacyEmissiveTexturesObject()},
{"skytextures", "Sky Texture", &RtxOptions::skyBoxTexturesObject()},
{"ignoretextures", "Ignore Texture (optional)", &RtxOptions::ignoreTexturesObject()},
{"hidetextures", "Hide Texture Instance (optional)", &RtxOptions::hideInstanceTexturesObject()},
Expand Down Expand Up @@ -2133,6 +2134,107 @@ namespace dxvk {
if (IMGUI_ADD_TOOLTIP(ImGui::Checkbox(rtxOption.displayName, &rtxOption.bufferToggle), rtxOption.textureSetOption->getDescription())) {
toggleTextureSelection(texHash, rtxOption.uniqueId, rtxOption.textureSetOption);
}

// Show emissive strength control for Legacy Emissive Texture
if (strcmp(rtxOption.uniqueId, "legacyemissivetextures") == 0 && rtxOption.bufferToggle) {
ImGui::Indent();

// Get current intensity for this specific texture
auto legacyEmissiveIntensities = RtxOptions::parseLegacyEmissiveIntensities(RtxOptions::legacyEmissiveIntensitiesString());
float currentIntensity = 2.0f; // Default intensity
auto intensityIt = legacyEmissiveIntensities.find(texHash);
if (intensityIt != legacyEmissiveIntensities.end()) {
currentIntensity = intensityIt->second;
}

ImGui::Text("Emissive Strength:");
ImGui::PushItemWidth(150.0f);
if (ImGui::DragFloat("##emissive_strength", &currentIntensity, 0.1f, 0.0f, 20.0f, "%.1f")) {
// Update the intensity for this texture
legacyEmissiveIntensities[texHash] = currentIntensity;
std::string intensityString = RtxOptions::legacyEmissiveIntensitiesToString(legacyEmissiveIntensities);
RtxOptions::legacyEmissiveIntensitiesStringObject().setDeferred(intensityString);
}
ImGui::PopItemWidth();

if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Controls the brightness of emissive glow for this texture (default: 2.0)");
}

// Reset button
ImGui::SameLine();
if (ImGui::Button("Reset##emissive_reset")) {
legacyEmissiveIntensities.erase(texHash);
std::string intensityString = RtxOptions::legacyEmissiveIntensitiesToString(legacyEmissiveIntensities);
RtxOptions::legacyEmissiveIntensitiesStringObject().setDeferred(intensityString);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Reset to default intensity (2.0)");
}

// Get current color tint for this specific texture
auto legacyEmissiveColors = RtxOptions::parseLegacyEmissiveColors(RtxOptions::legacyEmissiveColorsString());
Vector3 currentColor = Vector3(1.0f, 1.0f, 1.0f); // Default white
auto colorIt = legacyEmissiveColors.find(texHash);
if (colorIt != legacyEmissiveColors.end()) {
currentColor = colorIt->second;
}

// Convert Vector3 to float array for ImGui color picker
float colorArray[3] = { currentColor.x, currentColor.y, currentColor.z };

ImGui::Text("Emissive Color Tint:");
ImGui::PushItemWidth(150.0f);
if (ImGui::ColorEdit3("##emissive_color", colorArray, ImGuiColorEditFlags_NoInputs)) {
// Update the color map
legacyEmissiveColors[texHash] = Vector3(colorArray[0], colorArray[1], colorArray[2]);

// Convert back to string and save
std::string colorString = RtxOptions::legacyEmissiveColorsToString(legacyEmissiveColors);
RtxOptions::legacyEmissiveColorsStringObject().setDeferred(colorString);

// DEBUG: Log the color string being saved
char hashStr[32];
sprintf_s(hashStr, "%016llX", texHash);
Logger::info(str::format("DEBUG ImGui: Setting color tint for hash ", hashStr, " -> color string: ", colorString));
}
ImGui::PopItemWidth();

if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Tints the emissive glow color for this texture (default: white)");
}

// Color reset button
ImGui::SameLine();
if (ImGui::Button("Reset##color_reset")) {
legacyEmissiveColors.erase(texHash);
std::string colorString = RtxOptions::legacyEmissiveColorsToString(legacyEmissiveColors);
RtxOptions::legacyEmissiveColorsStringObject().setDeferred(colorString);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Reset to default color (white)");
}

// Alpha invert toggle for Legacy Emissive Texture
auto legacyEmissiveAlphaInvert = RtxOptions::parseLegacyEmissiveAlphaInvert(RtxOptions::legacyEmissiveAlphaInvertString());
bool currentAlphaInvert = legacyEmissiveAlphaInvert.find(texHash) != legacyEmissiveAlphaInvert.end();

if (ImGui::Checkbox("Invert Alpha Mask", &currentAlphaInvert)) {
// Update the alpha invert set
if (currentAlphaInvert) {
legacyEmissiveAlphaInvert.insert(texHash);
} else {
legacyEmissiveAlphaInvert.erase(texHash);
}
std::string invertString = RtxOptions::legacyEmissiveAlphaInvertToString(legacyEmissiveAlphaInvert);
RtxOptions::legacyEmissiveAlphaInvertStringObject().setDeferred(invertString);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("When enabled: black alpha = full emission, white alpha = no emission\nWhen disabled: black alpha = no emission, white alpha = full emission");
}

ImGui::Unindent();
}
}
ImGui::EndPopup();
return texHash;
Expand Down Expand Up @@ -2653,6 +2755,7 @@ namespace dxvk {
ImGui::Indent();
ImGui::DragFloat("Force Cutout Alpha", &RtxOptions::forceCutoutAlphaObject(), 0.01f, 0.0f, 1.0f, "%.3f", sliderFlags);
ImGui::DragFloat("World Space UI Background Offset", &RtxOptions::worldSpaceUiBackgroundOffsetObject(), 0.01f, -FLT_MAX, FLT_MAX, "%.3f", sliderFlags);

ImGui::Checkbox("Ignore last texture stage", &RtxOptions::ignoreLastTextureStageObject());
ImGui::Checkbox("Enable Multiple Stage Texture Factor Blending", &RtxOptions::enableMultiStageTextureFactorBlendingObject());
ImGui::Unindent();
Expand Down
68 changes: 68 additions & 0 deletions src/dxvk/rtx_render/rtx_instance_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ namespace dxvk {
, m_isHidden(src.m_isHidden)
, m_isPlayerModel(src.m_isPlayerModel)
, m_isWorldSpaceUI(src.m_isWorldSpaceUI)
, m_isLegacyEmissive(src.m_isLegacyEmissive)
, m_isUnordered(src.m_isUnordered)
, m_isObjectToWorldMirrored(src.m_isObjectToWorldMirrored)
, m_linkedBlas(src.m_linkedBlas)
Expand Down Expand Up @@ -395,6 +396,7 @@ namespace dxvk {
"Is Hidden: ", m_isHidden ? "true" : "false", "\n",
"Is Player Model: ", m_isPlayerModel ? "true" : "false", "\n",
"Is World Space UI: ", m_isWorldSpaceUI ? "true" : "false", "\n",
"Is Legacy Emissive: ", m_isLegacyEmissive ? "true" : "false", "\n",
"Is Unordered: ", m_isUnordered ? "true" : "false", "\n",
"Is Object To World Mirrored: ", m_isObjectToWorldMirrored ? "true" : "false", "\n",
"Is Created By Renderer: ", m_isCreatedByRenderer ? "true" : "false", "\n",
Expand Down Expand Up @@ -587,6 +589,7 @@ namespace dxvk {

// Note: Even if the Alpha Test enable flag is set, we consider it disabled if the actual test type is set to always.
const bool forceAlphaTest = drawCall.getCategoryFlags().test(InstanceCategories::AlphaBlendToCutout);

const bool alphaTestEnabled = forceAlphaTest || (AlphaTestType)drawCall.getMaterialData().alphaTestCompareOp != AlphaTestType::kAlways;

// Note: Use the Opaque Material Data's alpha test state information directly if requested,
Expand Down Expand Up @@ -994,6 +997,7 @@ namespace dxvk {
currentInstance.m_isHidden = currentInstance.testCategoryFlags(InstanceCategories::Hidden);
currentInstance.m_isPlayerModel = currentInstance.testCategoryFlags(InstanceCategories::ThirdPersonPlayerModel);
currentInstance.m_isWorldSpaceUI = currentInstance.testCategoryFlags(InstanceCategories::WorldUI);
currentInstance.m_isLegacyEmissive = currentInstance.testCategoryFlags(InstanceCategories::LegacyEmissive);

// Hide the sky instance since it is not raytraced.
// Sky mesh and material are only good for capture and replacement purposes.
Expand Down Expand Up @@ -1078,6 +1082,70 @@ namespace dxvk {
materialData.getOpaqueMaterialData().setEnableEmission(true);
materialData.getOpaqueMaterialData().setEmissiveIntensity(2.0f);
materialData.getOpaqueMaterialData().setEmissiveColorTexture(materialData.getOpaqueMaterialData().getAlbedoOpacityTexture());
} else if (currentInstance.m_isLegacyEmissive) {
// For legacy emissive, use alpha channel if available, otherwise use albedo. Per-texture controllable intensity.
// Use the same texture hash that was used to categorize this as legacy emissive (from original material)
const XXH64_hash_t textureHash = drawCall.getMaterialData().getColorTexture().getImageHash();

// Get per-texture emissive intensity, default to 2.0f if not specified
float emissiveIntensity = 2.0f;
const auto intensityMap = RtxOptions::parseLegacyEmissiveIntensities(RtxOptions::legacyEmissiveIntensitiesString());
auto intensityIt = intensityMap.find(textureHash);
if (intensityIt != intensityMap.end()) {
emissiveIntensity = intensityIt->second;
}

materialData.getOpaqueMaterialData().setEnableEmission(true);
materialData.getOpaqueMaterialData().setEmissiveIntensity(emissiveIntensity);

// Get per-texture emissive color tint, default to white (1.0, 1.0, 1.0) if not specified
Vector3 emissiveColorTint = Vector3(1.0f, 1.0f, 1.0f);
const auto colorMap = RtxOptions::parseLegacyEmissiveColors(RtxOptions::legacyEmissiveColorsString());
auto colorIt = colorMap.find(textureHash);
if (colorIt != colorMap.end()) {
emissiveColorTint = colorIt->second;
}

materialData.getOpaqueMaterialData().setEmissiveColorTint(emissiveColorTint);

// Check for per-texture alpha invert setting
bool alphaInvert = false;
const auto invertSet = RtxOptions::parseLegacyEmissiveAlphaInvert(RtxOptions::legacyEmissiveAlphaInvertString());
alphaInvert = invertSet.find(textureHash) != invertSet.end();

// Try to use alpha channel for emissive if available, otherwise use albedo
const auto& albedoTexture = materialData.getOpaqueMaterialData().getAlbedoOpacityTexture();
if (albedoTexture.isValid() && albedoTexture.getImageView() && albedoTexture.getImageView()->info().format != VK_FORMAT_UNDEFINED) {
// Check if texture has alpha channel (includes uncompressed RGBA and compressed formats like BC3/DXT5)
const VkFormat format = albedoTexture.getImageView()->info().format;
const bool hasAlpha = (format == VK_FORMAT_R8G8B8A8_UNORM ||
format == VK_FORMAT_R8G8B8A8_SRGB ||
format == VK_FORMAT_B8G8R8A8_UNORM ||
format == VK_FORMAT_B8G8R8A8_SRGB ||
format == VK_FORMAT_A8B8G8R8_UNORM_PACK32 ||
format == VK_FORMAT_A8B8G8R8_SRGB_PACK32 ||
format == VK_FORMAT_BC3_UNORM_BLOCK || // DXT5 - commonly used by Source engine
format == VK_FORMAT_BC3_SRGB_BLOCK || // DXT5 sRGB variant
format == VK_FORMAT_BC7_UNORM_BLOCK || // BC7 with alpha
format == VK_FORMAT_BC7_SRGB_BLOCK);

if (hasAlpha) {
// Use the same texture, but the shader will use alpha channel for emissive masking
materialData.getOpaqueMaterialData().setEmissiveColorTexture(albedoTexture);
materialData.getOpaqueMaterialData().setEmissiveAlphaMask(true);
materialData.getOpaqueMaterialData().setEmissiveAlphaInvert(alphaInvert);
} else {
// No alpha channel, use albedo for emissive
materialData.getOpaqueMaterialData().setEmissiveColorTexture(albedoTexture);
materialData.getOpaqueMaterialData().setEmissiveAlphaMask(false);
materialData.getOpaqueMaterialData().setEmissiveAlphaInvert(false); // No invert needed without alpha
}
} else {
// Fallback to albedo if texture is invalid
materialData.getOpaqueMaterialData().setEmissiveColorTexture(materialData.getOpaqueMaterialData().getAlbedoOpacityTexture());
materialData.getOpaqueMaterialData().setEmissiveAlphaMask(false);
materialData.getOpaqueMaterialData().setEmissiveAlphaInvert(false); // No invert needed without alpha
}
} else if (currentInstance.surface.alphaState.emissiveBlend && RtxOptions::enableEmissiveBlendEmissiveOverride() && useLegacyAlphaState) {
// If the user has decided to override the legacy alpha state, assume they know what they are doing and allow for explicit emission controls.
materialData.getOpaqueMaterialData().setEnableEmission(true);
Expand Down
1 change: 1 addition & 0 deletions src/dxvk/rtx_render/rtx_instance_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ uint32_t getFirstBillboardIndex() const { return m_firstBillboard; }
bool m_isHidden = false;
bool m_isPlayerModel = false;
bool m_isWorldSpaceUI = false;
bool m_isLegacyEmissive = false;
bool m_isUnordered = false;
bool m_isObjectToWorldMirrored = false;
bool m_isCreatedByRenderer = false;
Expand Down
3 changes: 3 additions & 0 deletions src/dxvk/rtx_render/rtx_material_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
X(MetallicConstant, metallic_constant, float, 0.f, 1.f, 0.f) \
X(EmissiveColorConstant, emissive_color_constant, Vector3, Vector3(0.f), Vector3(1.f), Vector3(1.0f, 0.1f, 0.1f)) \
X(EnableEmission, enable_emission, bool, false, true, false) \
X(EmissiveAlphaMask, emissive_alpha_mask, bool, false, true, false) \
X(EmissiveAlphaInvert, emissive_alpha_invert, bool, false, true, false) \
X(EmissiveColorTint, emissive_color_tint, Vector3, Vector3(0.f), Vector3(10.f), Vector3(1.0f, 1.0f, 1.0f)) \
X(SpriteSheetRows, sprite_sheet_rows, uint8_t, 0, 255, 0) \
X(SpriteSheetCols, sprite_sheet_cols, uint8_t, 0, 255, 0) \
X(SpriteSheetFPS, sprite_sheet_fps, uint8_t, 0, 255, 0) \
Expand Down
Loading