Alpha tint (#1572)

* Work

* Work

* Move RendererRoom out of union

* Update CHANGELOG.md

* Correctly mix alpha for all blend modes

* Introduce alpha threshold

* Cleanup and formatting; also add correct alpha convertion in RGBAColor8Byte to its Color() operator

---------

Co-authored-by: Sezz <sezzary@outlook.com>
This commit is contained in:
Lwmte 2025-02-28 01:44:27 +03:00 committed by GitHub
parent ae71ba601a
commit 9b6424f2d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 103 additions and 90 deletions

View file

@ -32,9 +32,10 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
* Added diary module.
* Added View.GetFlyByPosition() and View.GetFlyByRotation() functions to get flyby sequence parameters at a specified time point.
* Added Effects.EmitAirBubble() function to spawn air bubbles.
* Added additional arguments for Sprite object slot and starting rotation value for EmitParticle function.
* Added Lerp() function to the Rotation object to allow linear interpolation between rotations.
* Added extra arguments for sprite object slots and starting rotation value for EmitParticle function.
* Added various Translate() methods to Vec2 and Vec3 script objects.
* Added Lerp() function to the Rotation object to allow linear interpolation between rotations.
* Added alpha transparency functionality for statics and moveables by using SetColor() method.
## [Version 1.7.1](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.4) - 2025-04-01

View file

@ -580,9 +580,17 @@ namespace TEN::Renderer
static inline bool IsSortedBlendMode(BlendMode blendMode)
{
return !(blendMode == BlendMode::Opaque ||
blendMode == BlendMode::AlphaTest ||
blendMode == BlendMode::Additive ||
blendMode == BlendMode::FastAlphaBlend);
blendMode == BlendMode::AlphaTest ||
blendMode == BlendMode::Additive ||
blendMode == BlendMode::FastAlphaBlend);
}
static inline BlendMode GetBlendModeFromAlpha(BlendMode blendMode, float alpha)
{
if (alpha < ALPHA_BLEND_THRESHOLD && (blendMode == BlendMode::Opaque || blendMode == BlendMode::FastAlphaBlend))
return BlendMode::AlphaBlend;
return blendMode;
}
inline RendererObject& GetStaticRendererObject(short objectNumber)

View file

@ -505,8 +505,9 @@ namespace TEN::Renderer
object.ObjectType = RendererObjectType::MoveableAsStatic;
object.Centre = centre;
object.Distance = distance;
object.BlendMode = bucket.BlendMode;
object.Bucket = &bucket;
object.Mesh = mesh;
object.LightMode = mesh->LightMode;
object.Polygon = &bucket.Polygons[p];
object.World = rat->Transform;
object.Room = &_rooms[rat->RoomNumber];
@ -632,8 +633,9 @@ namespace TEN::Renderer
object.ObjectType = RendererObjectType::MoveableAsStatic;
object.Centre = center;
object.Distance = dist;
object.BlendMode = bucket.BlendMode;
object.Bucket = &bucket;
object.Mesh = &mesh;
object.LightMode = mesh.LightMode;
object.Polygon = &poly;
object.World = worldMatrix;
object.Room = &_rooms[fish.RoomNumber];
@ -749,8 +751,9 @@ namespace TEN::Renderer
object.ObjectType = RendererObjectType::MoveableAsStatic;
object.Centre = centre;
object.Distance = dist;
object.BlendMode = bucket.BlendMode;
object.Bucket = &bucket;
object.Mesh = &mesh;
object.LightMode = mesh.LightMode;
object.Polygon = &bucket.Polygons[p];
object.World = transformMatrix;
object.Room = &_rooms[bat.RoomNumber];
@ -870,8 +873,9 @@ namespace TEN::Renderer
object.ObjectType = RendererObjectType::MoveableAsStatic;
object.Centre = centre;
object.Distance = dist;
object.BlendMode = bucket.BlendMode;
object.Bucket = &bucket;
object.Mesh = &mesh;
object.LightMode = mesh.LightMode;
object.Polygon = &bucket.Polygons[p];
object.World = transformMatrix;
object.Room = &_rooms[beetle.RoomNumber];
@ -999,8 +1003,9 @@ namespace TEN::Renderer
object.ObjectType = RendererObjectType::MoveableAsStatic;
object.Centre = centre;
object.Distance = dist;
object.BlendMode = bucket.BlendMode;
object.Bucket = &bucket;
object.Mesh = &mesh;
object.LightMode = mesh.LightMode;
object.Polygon = &bucket.Polygons[p];
object.World = transformMatrix;
object.Room = &_rooms[locust.roomNumber];
@ -2538,6 +2543,9 @@ namespace TEN::Renderer
if (IgnoreReflectionPassForRoom(current->RoomNumber))
continue;
if (current->Color.w < ALPHA_BLEND_THRESHOLD)
continue;
auto world = current->World;
ReflectMatrixOptionally(world);
@ -2608,7 +2616,9 @@ namespace TEN::Renderer
if (bucket.NumVertices == 0)
continue;
if (IsSortedBlendMode(bucket.BlendMode))
auto blendMode = GetBlendModeFromAlpha(bucket.BlendMode, statics[i]->Color.w);
if (IsSortedBlendMode(blendMode) || statics[i]->Color.w < ALPHA_BLEND_THRESHOLD)
{
for (int p = 0; p < bucket.Polygons.size(); p++)
{
@ -2619,7 +2629,8 @@ namespace TEN::Renderer
object.Static = statics[i];
object.Centre = Vector3::Transform(bucket.Polygons[p].Centre, statics[i]->World);
object.Distance = Vector3::Distance(object.Centre, view.Camera.WorldPosition);
object.Mesh = refMesh;
object.BlendMode = blendMode;
object.LightMode = refMesh->LightMode;
object.Polygon = &bucket.Polygons[p];
object.Room = &_rooms[object.Static->RoomNumber];
@ -2663,6 +2674,7 @@ namespace TEN::Renderer
object.ObjectType = RendererObjectType::Room;
object.Centre = bucket.Centre;
object.Distance = Vector3::Distance(view.Camera.WorldPosition, bucket.Polygons[p].Centre);
object.BlendMode = bucket.BlendMode;
object.Bucket = &bucket;
object.Room = room;
object.Polygon = &bucket.Polygons[p];
@ -3132,35 +3144,33 @@ namespace TEN::Renderer
void Renderer::DrawMoveableMesh(RendererItem* itemToDraw, RendererMesh* mesh, RendererRoom* room, int boneIndex, RenderView& view, RendererPass rendererPass)
{
auto cameraPos = Camera.pos.ToVector3();
if (rendererPass == RendererPass::CollectTransparentFaces)
{
for (int j = 0; j < mesh->Buckets.size(); j++)
for (auto& bucket : mesh->Buckets)
{
auto& bucket = mesh->Buckets[j];
if (bucket.NumVertices == 0)
continue;
if (rendererPass == RendererPass::CollectTransparentFaces)
auto blendMode = GetBlendModeFromAlpha(bucket.BlendMode, itemToDraw->Color.w);
if (IsSortedBlendMode(blendMode))
{
if (IsSortedBlendMode(bucket.BlendMode))
for (int p = 0; p < bucket.Polygons.size(); p++)
{
for (int p = 0; p < bucket.Polygons.size(); p++)
{
auto centre = Vector3::Transform(
bucket.Polygons[p].Centre, itemToDraw->InterpolatedAnimTransforms[boneIndex] * itemToDraw->InterpolatedWorld);
int distance = (centre - cameraPos).Length();
auto center = Vector3::Transform(bucket.Polygons[p].Centre, itemToDraw->InterpolatedAnimTransforms[boneIndex] * itemToDraw->InterpolatedWorld);
int dist = Vector3::Distance(center, Camera.pos.ToVector3());
RendererSortableObject object;
object.ObjectType = RendererObjectType::Moveable;
object.Centre = centre;
object.Distance = distance;
object.Bucket = &bucket;
object.Item = itemToDraw;
object.Mesh = mesh;
object.Polygon = &bucket.Polygons[p];
auto object = RendererSortableObject{};
object.ObjectType = RendererObjectType::Moveable;
object.Centre = center;
object.Distance = dist;
object.BlendMode = blendMode;
object.Bucket = &bucket;
object.Item = itemToDraw;
object.LightMode = mesh->LightMode;
object.Polygon = &bucket.Polygons[p];
view.TransparentObjectsToDraw.push_back(object);
}
view.TransparentObjectsToDraw.push_back(object);
}
}
}
@ -3171,6 +3181,8 @@ namespace TEN::Renderer
{
if (bucket.NumVertices == 0)
continue;
auto blendMode = GetBlendModeFromAlpha(bucket.BlendMode, itemToDraw->Color.w);
if (rendererPass == RendererPass::ShadowMap)
{
@ -3183,19 +3195,15 @@ namespace TEN::Renderer
}
else
{
int passes = rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest ? 2 : 1;
int passes = rendererPass == RendererPass::Opaque && blendMode == BlendMode::AlphaTest ? 2 : 1;
for (int p = 0; p < passes; p++)
{
if (!SetupBlendModeAndAlphaTest(bucket.BlendMode, rendererPass, p))
{
if (!SetupBlendModeAndAlphaTest(blendMode, rendererPass, p))
continue;
}
BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[bucket.Texture]),
SamplerStateRegister::AnisotropicClamp);
BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[bucket.Texture]),
SamplerStateRegister::AnisotropicClamp);
BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[bucket.Texture]), SamplerStateRegister::AnisotropicClamp);
BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[bucket.Texture]), SamplerStateRegister::AnisotropicClamp);
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
@ -3306,7 +3314,7 @@ namespace TEN::Renderer
view.TransparentObjectsToDraw[i].ObjectType == object->ObjectType &&
view.TransparentObjectsToDraw[i].Room->RoomNumber == object->Room->RoomNumber &&
view.TransparentObjectsToDraw[i].Bucket->Texture == object->Bucket->Texture &&
view.TransparentObjectsToDraw[i].Bucket->BlendMode == object->Bucket->BlendMode &&
view.TransparentObjectsToDraw[i].BlendMode == object->BlendMode &&
_sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES)
{
auto* currentObject = &view.TransparentObjectsToDraw[i];
@ -3330,7 +3338,7 @@ namespace TEN::Renderer
view.TransparentObjectsToDraw[i].ObjectType == object->ObjectType &&
view.TransparentObjectsToDraw[i].Item->ItemNumber == object->Item->ItemNumber &&
view.TransparentObjectsToDraw[i].Bucket->Texture == object->Bucket->Texture &&
view.TransparentObjectsToDraw[i].Bucket->BlendMode == object->Bucket->BlendMode &&
view.TransparentObjectsToDraw[i].BlendMode == object->BlendMode &&
_sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES)
{
auto* currentObject = &view.TransparentObjectsToDraw[i];
@ -3355,7 +3363,7 @@ namespace TEN::Renderer
view.TransparentObjectsToDraw[i].Static->RoomNumber == object->Static->RoomNumber &&
view.TransparentObjectsToDraw[i].Static->IndexInRoom == object->Static->IndexInRoom &&
view.TransparentObjectsToDraw[i].Bucket->Texture == object->Bucket->Texture &&
view.TransparentObjectsToDraw[i].Bucket->BlendMode == object->Bucket->BlendMode &&
view.TransparentObjectsToDraw[i].BlendMode == object->BlendMode &&
_sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES)
{
auto* currentObject = &view.TransparentObjectsToDraw[i];
@ -3379,7 +3387,7 @@ namespace TEN::Renderer
view.TransparentObjectsToDraw[i].ObjectType == object->ObjectType &&
view.TransparentObjectsToDraw[i].Room->RoomNumber == object->Room->RoomNumber &&
view.TransparentObjectsToDraw[i].Bucket->Texture == object->Bucket->Texture &&
view.TransparentObjectsToDraw[i].Bucket->BlendMode == object->Bucket->BlendMode &&
view.TransparentObjectsToDraw[i].BlendMode == object->BlendMode &&
_sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES)
{
auto* currentObject = &view.TransparentObjectsToDraw[i];
@ -3512,7 +3520,7 @@ namespace TEN::Renderer
if (objectInfo->Bucket->Animated != 0)
_shaders.Bind(Shader::RoomsAnimated);
SetBlendMode(objectInfo->Bucket->BlendMode);
SetBlendMode(objectInfo->BlendMode);
SetAlphaTest(AlphaTestMode::None, ALPHA_TEST_THRESHOLD);
// Draw geometry
@ -3573,7 +3581,7 @@ namespace TEN::Renderer
SetDepthState(DepthState::Read);
SetCullMode(CullMode::CounterClockwise);
SetBlendMode(objectInfo->Bucket->BlendMode);
SetBlendMode(objectInfo->BlendMode);
SetAlphaTest(AlphaTestMode::None, ALPHA_TEST_THRESHOLD);
_shaders.Bind(Shader::Items);
@ -3630,7 +3638,7 @@ namespace TEN::Renderer
SetDepthState(DepthState::Read);
SetCullMode(CullMode::CounterClockwise);
SetBlendMode(objectInfo->Bucket->BlendMode);
SetBlendMode(objectInfo->BlendMode);
SetAlphaTest(AlphaTestMode::None, ALPHA_TEST_THRESHOLD);
BindTexture(TextureRegister::ColorMap, &std::get<0>(_staticTextures[objectInfo->Bucket->Texture]),
@ -3662,13 +3670,13 @@ namespace TEN::Renderer
_stStatic.Color = Vector4::One;
_stStatic.AmbientLight = objectInfo->Room->AmbientLight;
_stStatic.LightMode = (int)objectInfo->Mesh->LightMode;
_stStatic.LightMode = (int)objectInfo->LightMode;
BindStaticLights(objectInfo->Room->LightsToDraw);
_cbStatic.UpdateData(_stStatic, _context.Get());
SetDepthState(DepthState::Read);
SetCullMode(CullMode::CounterClockwise);
SetBlendMode(objectInfo->Bucket->BlendMode);
SetBlendMode(objectInfo->BlendMode);
SetAlphaTest(AlphaTestMode::GreatherThan, ALPHA_TEST_THRESHOLD);
BindTexture(TextureRegister::ColorMap, &std::get<0>(_staticTextures[objectInfo->Bucket->Texture]),

View file

@ -29,12 +29,13 @@ constexpr auto MAX_DYNAMIC_LIGHTS = 1024;
constexpr auto ITEM_LIGHT_COLLECTION_RADIUS = BLOCK(1);
constexpr auto CAMERA_LIGHT_COLLECTION_RADIUS = BLOCK(4);
constexpr auto MAX_TRANSPARENT_FACES = 16384;
constexpr auto MAX_TRANSPARENT_VERTICES = (MAX_TRANSPARENT_FACES * 6);
constexpr auto MAX_TRANSPARENT_FACES = 16384;
constexpr auto MAX_TRANSPARENT_VERTICES = MAX_TRANSPARENT_FACES * 6;
constexpr auto MAX_TRANSPARENT_FACES_PER_ROOM = 16384;
constexpr auto TRANSPARENT_BUCKET_SIZE = (3840 * 16);
constexpr auto ALPHA_TEST_THRESHOLD = 0.5f;
constexpr auto FAST_ALPHA_BLEND_THRESHOLD = 0.5f;
constexpr auto TRANSPARENT_BUCKET_SIZE = 3840 * 16;
constexpr auto ALPHA_TEST_THRESHOLD = 0.5f;
constexpr auto ALPHA_BLEND_THRESHOLD = 0.95f;
constexpr auto FAST_ALPHA_BLEND_THRESHOLD = 0.5f;
constexpr auto MAX_BONES = 32;

View file

@ -1,4 +1,5 @@
#pragma once
#include "Renderer/Graphics/Vertices/Vertex.h"
#include "Renderer/RendererEnums.h"
#include "Renderer/Structures/RendererBucket.h"
@ -9,41 +10,30 @@
#include "Renderer/Structures/RendererSpriteToDraw.h"
#include "Renderer/Structures/RendererStatic.h"
#include "Specific/fast_vector.h"
#include <SimpleMath.h>
namespace TEN::Renderer::Structures
{
using namespace DirectX::SimpleMath;
using namespace TEN::Renderer::Graphics::Vertices;
struct RendererSortableObject
{
RendererObjectType ObjectType;
int Distance;
Vector3 Centre;
RendererMesh* Mesh;
RendererBucket* Bucket;
RendererRoom* Room;
RendererStatic* Static;
RendererItem* Item;
RendererEffect* Effect;
RendererSpriteToDraw* Sprite;
RendererPolygon* Polygon;
Matrix World;
};
struct RendererSortedBucket
{
RendererObjectType ObjectType;
RendererMesh* Mesh;
RendererBucket* Bucket;
RendererRoom* Room;
RendererStatic* Static;
RendererItem* Item;
RendererEffect* Effect;
RendererSpriteToDraw* Sprite;
RendererPolygon* Polygon;
fast_vector<Vertex> Vertices;
fast_vector<int> Indices;
Matrix World = Matrix::Identity;
Vector3 Centre = Vector3::Zero; // TODO: Rename to Center.
int Distance = 0;
BlendMode BlendMode = BlendMode::Opaque;
LightMode LightMode = LightMode::Dynamic;
RendererRoom* Room = nullptr;
RendererBucket* Bucket = nullptr;
RendererPolygon* Polygon = nullptr;
union
{
RendererItem* Item;
RendererStatic* Static;
RendererEffect* Effect;
RendererSpriteToDraw* Sprite;
};
};
}
}

View file

@ -131,6 +131,7 @@ PixelShaderOutput PS(PixelShaderInput input)
output.Color = float4(color * occlusion, tex.w);
output.Color = DoFogBulbsForPixel(output.Color, float4(input.FogBulbs.xyz, 1.0f));
output.Color = DoDistanceFogForPixel(output.Color, FogColor, input.DistanceFog);
output.Color.w *= input.Color.w;
return output;
}
}

View file

@ -163,6 +163,7 @@ PixelShaderOutput PS(PixelShaderInput input)
output.Color = saturate(float4(color * occlusion, tex.w));
output.Color = DoFogBulbsForPixel(output.Color, float4(input.FogBulbs.xyz, 1.0f));
output.Color = DoDistanceFogForPixel(output.Color, FogColor, input.DistanceFog);
output.Color.w *= input.Color.w;
return output;
}
}

View file

@ -99,6 +99,7 @@ PixelShaderOutput PS(PixelShaderInput input)
output.Color = float4(color, tex.w);
output.Color = DoFogBulbsForPixel(output.Color, float4(input.FogBulbs.xyz, 1.0f));
output.Color = DoDistanceFogForPixel(output.Color, FogColor, input.DistanceFog);
output.Color.w *= input.Color.w;
return output;
}
}

View file

@ -96,7 +96,8 @@ void RGBAColor8Byte::SetA(byte v)
RGBAColor8Byte::operator Color() const
{
return Color(ByteComponentToFloat(r), ByteComponentToFloat(g), ByteComponentToFloat(b));
// Alpha exists on normalized range [0.0f, 1.0f], unlike color components which exist on range [0.0f, 2.0f].
return Color(ByteComponentToFloat(r), ByteComponentToFloat(g), ByteComponentToFloat(b), ByteComponentToFloat(a) / 2.0f);
}
RGBAColor8Byte::operator Vector3() const
@ -106,7 +107,8 @@ RGBAColor8Byte::operator Vector3() const
RGBAColor8Byte::operator Vector4() const
{
return Vector4(ByteComponentToFloat(r), ByteComponentToFloat(g), ByteComponentToFloat(b), ByteComponentToFloat(a));
// Alpha exists on normalized range [0.0f, 1.0f], unlike color components which exist on range [0.0f, 2.0f].
return Vector4(ByteComponentToFloat(r), ByteComponentToFloat(g), ByteComponentToFloat(b), ByteComponentToFloat(a) / 2.0f);
}
RGBAColor8Byte::operator D3DCOLOR() const