mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00
Initial Commit
This commit is contained in:
parent
6c0eeff41c
commit
1089ef9463
4 changed files with 109 additions and 34 deletions
|
@ -8,6 +8,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
|
|||
## New features
|
||||
* Added video playback support.
|
||||
* Added muzzle glow effect for firearms.
|
||||
* Added weather particle clustering and increase weather particle performance.
|
||||
|
||||
### Bug fixes
|
||||
* Fixed Teleporter object.
|
||||
|
@ -25,7 +26,6 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
|
|||
* Fixed crashes when Lara is on a vehicle unreachable by friendly NPCs.
|
||||
* Removed legacy TR5 search object code which caused issues with meshswaps.
|
||||
* Removed excessive HK nerfing in running state.
|
||||
* Optimized weather particle rendering.
|
||||
|
||||
### Lua API changes
|
||||
* Added `View.PlayVideo`, `View.StopVideo`, and other helper functions for the video playback.
|
||||
|
|
|
@ -433,13 +433,17 @@ namespace TEN::Effects::Environment
|
|||
|
||||
// Produce ripples if particle got into substance (water or swamp).
|
||||
if (inSubstance)
|
||||
SpawnRipple(part.Position, part.RoomNumber, Random::GenerateFloat(16.0f, 24.0f), (int)RippleFlags::SlowFade | (int)RippleFlags::LowOpacity);
|
||||
{
|
||||
auto ripplePos = part.Position;
|
||||
ripplePos.y = pointColl.GetWaterSurfaceHeight();
|
||||
SpawnRipple(ripplePos, part.RoomNumber, Random::GenerateFloat(16.0f, 24.0f), (int)RippleFlags::SlowFade | (int)RippleFlags::LowOpacity);
|
||||
}
|
||||
|
||||
// Immediately disable rain particle because it doesn't need fading out.
|
||||
if (part.Type == WeatherType::Rain)
|
||||
{
|
||||
part.Enabled = false;
|
||||
AddWaterSparks(prevPos.x, prevPos.y, prevPos.z, 6);
|
||||
AddWaterSparks(prevPos.x, inSubstance ? pointColl.GetWaterSurfaceHeight() : pointColl.GetFloorHeight() - 32, prevPos.z, 6);
|
||||
}
|
||||
|
||||
continue;
|
||||
|
@ -587,7 +591,7 @@ namespace TEN::Effects::Environment
|
|||
|
||||
auto xPos = Camera.pos.x + ((int)(phd_cos(angle) * radius));
|
||||
auto zPos = Camera.pos.z + ((int)(phd_sin(angle) * radius));
|
||||
auto yPos = Camera.pos.y - (BLOCK(4) + Random::GenerateInt() & (BLOCK(4) - 1));
|
||||
auto yPos = Camera.pos.y - (BLOCK(3) + Random::GenerateInt() & (BLOCK(4) - 1));
|
||||
|
||||
auto outsideRoom = IsRoomOutside(xPos, yPos, zPos);
|
||||
|
||||
|
@ -607,12 +611,14 @@ namespace TEN::Effects::Environment
|
|||
switch (level.GetWeatherType())
|
||||
{
|
||||
case WeatherType::Snow:
|
||||
part.ClusterSize = (int)(level.GetWeatherStrength() * WEATHER_PARTICLE_CLUSTER_MULT / 2);
|
||||
part.Size = Random::GenerateFloat(SNOW_SIZE_MAX / 3, SNOW_SIZE_MAX);
|
||||
part.Velocity.y = Random::GenerateFloat(SNOW_VELOCITY_MAX / 4, SNOW_VELOCITY_MAX) * (part.Size / SNOW_SIZE_MAX);
|
||||
part.Life = (SNOW_VELOCITY_MAX / 3) + ((SNOW_VELOCITY_MAX / 2) - ((int)part.Velocity.y >> 2));
|
||||
break;
|
||||
|
||||
case WeatherType::Rain:
|
||||
part.ClusterSize = (int)(level.GetWeatherStrength() * WEATHER_PARTICLE_CLUSTER_MULT);
|
||||
part.Size = Random::GenerateFloat(RAIN_SIZE_MAX / 2, RAIN_SIZE_MAX);
|
||||
part.Velocity.y = Random::GenerateFloat(RAIN_VELOCITY_MAX / 2, RAIN_VELOCITY_MAX) * (part.Size / RAIN_SIZE_MAX) * std::clamp(level.GetWeatherStrength(), 0.6f, 1.0f);
|
||||
part.Life = (RAIN_VELOCITY_MAX * 2) - part.Velocity.y;
|
||||
|
@ -622,6 +628,7 @@ namespace TEN::Effects::Environment
|
|||
part.Velocity.x = Random::GenerateFloat(WEATHER_PARTICLE_HORIZONTAL_VELOCITY / 2, WEATHER_PARTICLE_HORIZONTAL_VELOCITY);
|
||||
part.Velocity.z = Random::GenerateFloat(WEATHER_PARTICLE_HORIZONTAL_VELOCITY / 2, WEATHER_PARTICLE_HORIZONTAL_VELOCITY);
|
||||
|
||||
part.UniqueID = (int)Particles.size();
|
||||
part.Type = level.GetWeatherType();
|
||||
part.RoomNumber = outsideRoom;
|
||||
part.Position.x = xPos;
|
||||
|
|
|
@ -9,6 +9,7 @@ using namespace TEN::Entities::Effects;
|
|||
namespace TEN::Effects::Environment
|
||||
{
|
||||
constexpr auto WEATHER_PARTICLE_SPAWN_DENSITY = 32;
|
||||
constexpr auto WEATHER_PARTICLE_CLUSTER_MULT = 16.0f;
|
||||
constexpr auto WEATHER_PARTICLE_COUNT_MAX = 4096;
|
||||
constexpr auto WEATHER_PARTICLE_COLL_CHECK_DELAY_MAX = 5.0f;
|
||||
|
||||
|
@ -70,6 +71,7 @@ namespace TEN::Effects::Environment
|
|||
struct WeatherParticle
|
||||
{
|
||||
WeatherType Type = WeatherType::None;
|
||||
int UniqueID = 0;
|
||||
|
||||
Vector3 Position = Vector3::Zero;
|
||||
int RoomNumber = NO_VALUE;
|
||||
|
@ -79,6 +81,7 @@ namespace TEN::Effects::Environment
|
|||
float Life = 0.0f;
|
||||
float CollisionCheckDelay = 0.0f;
|
||||
float Size = 0.0f;
|
||||
int ClusterSize = 1;
|
||||
|
||||
bool Enabled = false;
|
||||
bool Stopped = false;
|
||||
|
|
|
@ -1022,6 +1022,8 @@ namespace TEN::Renderer
|
|||
void Renderer::PrepareWeatherParticles(RenderView& view)
|
||||
{
|
||||
constexpr auto RAIN_WIDTH = 4.0f;
|
||||
constexpr auto SNOW_CLUSTER_SPREAD = BLOCK(1.0f);
|
||||
constexpr auto RAIN_CLUSTER_SPREAD = BLOCK(0.35f);
|
||||
|
||||
for (const auto& part : Weather.GetParticles())
|
||||
{
|
||||
|
@ -1031,15 +1033,14 @@ namespace TEN::Renderer
|
|||
auto pos = Vector3::Lerp(part.PrevPosition, part.Position, GetInterpolationFactor());
|
||||
auto size = Lerp(part.PrevSize, part.Size, GetInterpolationFactor());
|
||||
|
||||
// Underwater dust does not need clustering.
|
||||
if (part.Type == WeatherType::None)
|
||||
{
|
||||
if (!view.Camera.Frustum.SphereInFrustum(pos, size))
|
||||
continue;
|
||||
|
||||
switch (part.Type)
|
||||
{
|
||||
case WeatherType::None:
|
||||
|
||||
if (!CheckIfSlotExists(ID_DEFAULT_SPRITES, "Underwater dust rendering"))
|
||||
return;
|
||||
continue;
|
||||
|
||||
AddSpriteBillboard(
|
||||
&_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_UNDERWATERDUST],
|
||||
|
@ -1048,8 +1049,71 @@ namespace TEN::Renderer
|
|||
0.0f, 1.0f, Vector2(size),
|
||||
BlendMode::Additive, true, view);
|
||||
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Clamp cluster size to 1.
|
||||
int clusterSize = std::max(1, part.ClusterSize);
|
||||
|
||||
// If particle is dying, immediately cancel the cluster.
|
||||
if (part.Stopped)
|
||||
clusterSize = 1;
|
||||
|
||||
auto finalPos = pos;
|
||||
auto finalScale = size;
|
||||
|
||||
for (int i = 0; i < clusterSize; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
// Combine particle index and cluster index for unique seeding.
|
||||
int uniqueSeed = part.UniqueID + i;
|
||||
|
||||
// Use bits from uniqueSeed to determine distribution pattern.
|
||||
float spread = part.Type == WeatherType::Snow ? SNOW_CLUSTER_SPREAD : RAIN_CLUSTER_SPREAD;
|
||||
float offsetBase = spread * ((i + 1) / (float)clusterSize);
|
||||
|
||||
// Use bits 0, 1, 2 for axis signs.
|
||||
// Snow Y axis is always negative, so that snowflakes don't sink into room geometry.
|
||||
float xSign = (uniqueSeed & 1) ? 1.0f : -1.0f;
|
||||
float zSign = (uniqueSeed & 4) ? 1.0f : -1.0f;
|
||||
|
||||
// Use bits 3, 4 for axis emphasis.
|
||||
int axisEmphasis = uniqueSeed & 3;
|
||||
float xScale = (axisEmphasis == 0) ? 1.1f : 0.4f;
|
||||
float yScale = (axisEmphasis == 1) ? 1.2f : 0.5f;
|
||||
float zScale = (axisEmphasis == 2) ? 1.0f : 0.6f;
|
||||
|
||||
Vector3 positionOffset(
|
||||
xSign * offsetBase * xScale,
|
||||
-(offsetBase * yScale),
|
||||
zSign * offsetBase * zScale
|
||||
);
|
||||
|
||||
// Apply deterministic offset.
|
||||
finalPos = pos + positionOffset;
|
||||
finalScale = size * (1.0f + abs(phd_sin(part.UniqueID + i)));
|
||||
|
||||
constexpr auto SNOW_SPIN_RATE = 0.05f;
|
||||
constexpr auto SNOW_SPIN_RADIUS = 0.3f;
|
||||
|
||||
if (part.Type == WeatherType::Snow)
|
||||
{
|
||||
// Calculate spin angle based on vertical position.
|
||||
// Wrap the vertical position to 3 blocks and multiply by 21 to get full unsigned short value.
|
||||
unsigned short spinAngle = ((int)abs(finalPos.y) % BLOCK(3)) * 21;
|
||||
|
||||
// Apply circular motion in XZ plane.
|
||||
finalPos.x += positionOffset.x * phd_sin((short)spinAngle);
|
||||
finalPos.z += positionOffset.z * phd_cos((short)spinAngle);
|
||||
}
|
||||
}
|
||||
|
||||
if (!view.Camera.Frustum.SphereInFrustum(finalPos, finalScale))
|
||||
continue;
|
||||
|
||||
switch (part.Type)
|
||||
{
|
||||
case WeatherType::Snow:
|
||||
|
||||
if (!CheckIfSlotExists(ID_DEFAULT_SPRITES, "Snow rendering"))
|
||||
|
@ -1057,10 +1121,10 @@ namespace TEN::Renderer
|
|||
|
||||
AddSpriteBillboard(
|
||||
&_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_UNDERWATERDUST],
|
||||
pos,
|
||||
finalPos,
|
||||
Color(1.0f, 1.0f, 1.0f, part.Transparency()),
|
||||
0.0f, 1.0f, Vector2(size),
|
||||
BlendMode::Additive, true, view);
|
||||
0.0f, 1.0f, Vector2(finalScale),
|
||||
BlendMode::Additive, false, view);
|
||||
|
||||
break;
|
||||
|
||||
|
@ -1074,16 +1138,17 @@ namespace TEN::Renderer
|
|||
|
||||
AddSpriteBillboardConstrained(
|
||||
&_sprites[Objects[ID_DRIP_SPRITE].meshIndex],
|
||||
pos,
|
||||
finalPos,
|
||||
Color(0.8f, 1.0f, 1.0f, part.Transparency()),
|
||||
0.0f, 1.0f,
|
||||
Vector2(RAIN_WIDTH, size),
|
||||
BlendMode::Additive, -v, true, view);
|
||||
Vector2(RAIN_WIDTH, finalScale),
|
||||
BlendMode::Additive, -v, false, view);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Renderer::DrawGunFlashes(RenderView& view)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue