mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-05-13 05:57:05 +03:00
733 lines
20 KiB
C++
733 lines
20 KiB
C++
#include "framework.h"
|
|
#include "Renderer/Renderer11.h"
|
|
#include "Game/animation.h"
|
|
#include "Game/camera.h"
|
|
#include "Game/effects/effects.h"
|
|
#include "Game/items.h"
|
|
#include "Game/Lara/lara.h"
|
|
#include "Game/spotcam.h"
|
|
#include "Specific/level.h"
|
|
#include "Specific/setup.h"
|
|
#include "RenderView/RenderView.h"
|
|
|
|
namespace TEN::Renderer
|
|
{
|
|
using namespace TEN::Renderer;
|
|
using TEN::Memory::LinearArrayBuffer;
|
|
using std::vector;
|
|
|
|
void Renderer11::CollectRooms(RenderView &renderView, bool onlyRooms)
|
|
{
|
|
short baseRoomIndex = renderView.camera.RoomNumber;
|
|
|
|
for (int i = 0; i < g_Level.Rooms.size(); i++)
|
|
{
|
|
m_rooms[i].ItemsToDraw.clear();
|
|
m_rooms[i].EffectsToDraw.clear();
|
|
m_rooms[i].TransparentFacesToDraw.clear();
|
|
m_rooms[i].StaticsToDraw.clear();
|
|
m_rooms[i].Visited = false;
|
|
m_rooms[i].Clip = RendererRectangle(m_screenWidth, m_screenHeight, 0, 0);
|
|
m_rooms[i].ClipTest = RendererRectangle(m_screenWidth, m_screenHeight, 0, 0);
|
|
m_rooms[i].BoundActive = 0;
|
|
}
|
|
|
|
GetVisibleObjects(renderView, onlyRooms);
|
|
}
|
|
|
|
void Renderer11::SetRoomBounds(ROOM_DOOR* door, short parentRoomNumber, RenderView& renderView)
|
|
{
|
|
RendererRoom* room = &m_rooms[door->room];
|
|
ROOM_INFO* nativeRoom = &g_Level.Rooms[door->room];
|
|
|
|
RendererRoom* parentRoom = &m_rooms[parentRoomNumber];
|
|
ROOM_INFO* parentNativeRoom = &g_Level.Rooms[parentRoomNumber];
|
|
|
|
// If parent's bounds are bigger than current bounds test, then don't do anything else
|
|
if (room->Clip.left <= parentRoom->ClipTest.left
|
|
&& room->Clip.right >= parentRoom->ClipTest.right
|
|
&& room->Clip.top <= parentRoom->ClipTest.top
|
|
&& room->Clip.bottom >= parentRoom->ClipTest.bottom)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int left = parentRoom->ClipTest.right;
|
|
int right = parentRoom->ClipTest.left;
|
|
int top = parentRoom->ClipTest.bottom;
|
|
int bottom = parentRoom->ClipTest.top;
|
|
|
|
int zBehind = 0;
|
|
int zTooFar = 0;
|
|
|
|
Vector4 p[4];
|
|
|
|
int xs = 0;
|
|
int ys = 0;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
// Project vertices of the door in clip space
|
|
Vector4 tmp = Vector4(
|
|
door->vertices[i].x + parentNativeRoom->x,
|
|
door->vertices[i].y,
|
|
door->vertices[i].z + parentNativeRoom->z,
|
|
1.0f);
|
|
|
|
Vector4::Transform(tmp, renderView.camera.ViewProjection, p[i]);
|
|
|
|
// Convert coordinates to screen space
|
|
if (p[i].w > 0.0f)
|
|
{
|
|
p[i].x *= (1.0f / p[i].w);
|
|
p[i].y *= (1.0f / p[i].w);
|
|
p[i].z *= (1.0f / p[i].w);
|
|
|
|
if (p[i].w > 0)
|
|
{
|
|
xs = 0.5f * (p[i].x + 1.0f) * m_screenWidth;
|
|
ys = 0.5f * (p[i].y + 1.0f) * m_screenHeight;
|
|
}
|
|
else
|
|
{
|
|
xs = (p[i].x >= 0) ? 0 : m_screenWidth;
|
|
ys = (p[i].y >= 0) ? m_screenHeight : 0;
|
|
}
|
|
|
|
// Has bound changed?
|
|
if (xs - 1 < left)
|
|
left = xs - 1;
|
|
if (xs + 1 > right)
|
|
right = xs + 1;
|
|
|
|
if (ys - 1 < top)
|
|
top = ys - 1;
|
|
if (ys + 1 > bottom)
|
|
bottom = ys + 1;
|
|
}
|
|
else
|
|
{
|
|
zBehind++;
|
|
}
|
|
}
|
|
|
|
// If all vertices of the door ar behind the camera, exit now
|
|
if (zBehind == 4)
|
|
return;
|
|
|
|
// If some vertices are behind the camera, we need to properly clip
|
|
if (zBehind > 0)
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
Vector4 a = p[i];
|
|
Vector4 b = p[(i + 1) % 4];
|
|
|
|
if ((a.z <= 0) ^ (b.z <= 0))
|
|
{
|
|
// X clip
|
|
if (a.x < 0 && b.x < 0)
|
|
left = 0;
|
|
else if (a.x > 0 && b.x > 0)
|
|
right = m_screenWidth;
|
|
else
|
|
{
|
|
left = 0;
|
|
right = m_screenWidth;
|
|
}
|
|
|
|
// Y clip
|
|
if (a.y < 0 && b.y < 0)
|
|
top = 0;
|
|
else if (a.y > 0 && b.y > 0)
|
|
bottom = m_screenHeight;
|
|
else
|
|
{
|
|
top = 0;
|
|
bottom = m_screenWidth;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clip bounds to parent bounds
|
|
if (left < parentRoom->ClipTest.left)
|
|
left = parentRoom->ClipTest.left;
|
|
if (right > parentRoom->ClipTest.right)
|
|
right = parentRoom->ClipTest.right;
|
|
if (top < parentRoom->ClipTest.top)
|
|
top = parentRoom->ClipTest.top;
|
|
if (bottom > parentRoom->ClipTest.bottom)
|
|
bottom = parentRoom->ClipTest.bottom;
|
|
|
|
if (left >= right || top >= bottom)
|
|
return;
|
|
|
|
// Store the new calculated bounds
|
|
if (room->BoundActive & 2)
|
|
{
|
|
// The room is already in the bounds list
|
|
|
|
if (left < room->ClipTest.left)
|
|
{
|
|
room->Clip.left = left;
|
|
room->ClipTest.left = left;
|
|
}
|
|
|
|
if (top < room->ClipTest.top)
|
|
{
|
|
room->Clip.top = top;
|
|
room->ClipTest.top = top;
|
|
}
|
|
|
|
if (right > room->ClipTest.right)
|
|
{
|
|
room->Clip.right = right;
|
|
room->ClipTest.right = right;
|
|
}
|
|
|
|
if (bottom > room->ClipTest.bottom)
|
|
{
|
|
room->Clip.bottom = bottom;
|
|
room->ClipTest.bottom = bottom;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The room must be added to the bounds list
|
|
|
|
m_boundList[(m_boundEnd++) % MAX_ROOM_BOUNDS] = door->room;
|
|
room->BoundActive |= 2;
|
|
|
|
room->ClipTest.left = left;
|
|
room->ClipTest.right = right;
|
|
room->ClipTest.top = top;
|
|
room->ClipTest.bottom = bottom;
|
|
|
|
room->Clip.left = left;
|
|
room->Clip.right = right;
|
|
room->Clip.top = top;
|
|
room->Clip.bottom = bottom;
|
|
}
|
|
}
|
|
|
|
void Renderer11::GetRoomBounds(RenderView& renderView, bool onlyRooms)
|
|
{
|
|
auto cameraPosition = Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z);
|
|
|
|
while (m_boundStart != m_boundEnd)
|
|
{
|
|
short roomNumber = m_boundList[(m_boundStart++) % MAX_ROOM_BOUNDS];
|
|
RendererRoom* room = &m_rooms[roomNumber];
|
|
ROOM_INFO* nativeRoom = &g_Level.Rooms[roomNumber];
|
|
|
|
float xRad = nativeRoom->xSize * SECTOR(1) / 2.0f;
|
|
float yRad = (nativeRoom->minfloor - nativeRoom->maxceiling) / 2.0f;
|
|
float zRad = nativeRoom->zSize * SECTOR(1) / 2.0f;
|
|
|
|
auto roomCentre = Vector3(nativeRoom->x + xRad, nativeRoom->minfloor - yRad, nativeRoom->z + zRad);
|
|
|
|
float roomRad = std::max(std::max(xRad, yRad), zRad);
|
|
float distance = std::max((roomCentre - cameraPosition).Length() - (roomRad * 1.5f), 0.0f);
|
|
|
|
room->BoundActive -= 2;
|
|
|
|
if (room->ClipTest.left < room->Clip.left)
|
|
room->Clip.left = room->ClipTest.left;
|
|
if (room->ClipTest.top < room->Clip.top)
|
|
room->Clip.top = room->ClipTest.top;
|
|
if (room->ClipTest.right > room->Clip.right)
|
|
room->Clip.right = room->ClipTest.right;
|
|
if (room->ClipTest.bottom > room->Clip.bottom)
|
|
room->Clip.bottom = room->ClipTest.bottom;
|
|
|
|
// Add room to the list of rooms to draw
|
|
if (!((room->BoundActive & 1) || distance > m_farView))
|
|
{
|
|
renderView.roomsToDraw.push_back(room);
|
|
|
|
room->BoundActive |= 1;
|
|
|
|
if (nativeRoom->flags & ENV_FLAG_OUTSIDE)
|
|
m_outside = true;
|
|
|
|
if (!m_rooms[roomNumber].Visited)
|
|
{
|
|
CollectLightsForRoom(roomNumber, renderView);
|
|
|
|
if (!onlyRooms)
|
|
{
|
|
CollectItems(roomNumber, renderView);
|
|
CollectStatics(roomNumber, renderView);
|
|
CollectEffects(roomNumber);
|
|
}
|
|
}
|
|
|
|
m_rooms[roomNumber].Distance = distance;
|
|
m_rooms[roomNumber].Visited = true;
|
|
}
|
|
|
|
if (nativeRoom->flags & ENV_FLAG_OUTSIDE)
|
|
{
|
|
if (room->Clip.left < m_outsideClip.left)
|
|
m_outsideClip.left = room->Clip.left;
|
|
if (room->Clip.right > m_outsideClip.right)
|
|
m_outsideClip.right = room->Clip.right;
|
|
if (room->Clip.top < m_outsideClip.top)
|
|
m_outsideClip.top = room->Clip.top;
|
|
if (room->Clip.bottom > m_outsideClip.bottom)
|
|
m_outsideClip.bottom = room->Clip.bottom;
|
|
}
|
|
|
|
for (int i = 0; i < nativeRoom->doors.size(); i++)
|
|
{
|
|
ROOM_DOOR* portal = &nativeRoom->doors[i];
|
|
|
|
Vector3Int n = Vector3Int(portal->normal.x, portal->normal.y, portal->normal.z);
|
|
Vector3Int v = Vector3Int(
|
|
Camera.pos.x - (nativeRoom->x + portal->vertices[0].x),
|
|
Camera.pos.y - (nativeRoom->y + portal->vertices[0].y),
|
|
Camera.pos.z - (nativeRoom->z + portal->vertices[0].z));
|
|
|
|
if (n.x * v.x + n.y * v.y + n.z * v.z < 0)
|
|
continue;
|
|
|
|
SetRoomBounds(portal, roomNumber, renderView);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer11::GetVisibleObjects(RenderView& renderView, bool onlyRooms)
|
|
{
|
|
RendererRoom* room = &m_rooms[Camera.pos.roomNumber];
|
|
ROOM_INFO* nativeRoom = &g_Level.Rooms[Camera.pos.roomNumber];
|
|
|
|
room->ClipTest = RendererRectangle(0, 0, m_screenWidth, m_screenHeight);
|
|
m_outside = nativeRoom->flags & ENV_FLAG_OUTSIDE;
|
|
m_cameraUnderwater = (nativeRoom->flags & ENV_FLAG_WATER);
|
|
|
|
room->BoundActive = 2;
|
|
|
|
// Initialise bounds list
|
|
m_boundList[0] = Camera.pos.roomNumber;
|
|
m_boundStart = 0;
|
|
m_boundEnd = 1;
|
|
|
|
// Horizon clipping
|
|
if (m_outside)
|
|
{
|
|
m_outsideClip = RendererRectangle(0, 0, m_screenWidth, m_screenHeight);
|
|
}
|
|
else
|
|
{
|
|
m_outsideClip = RendererRectangle(m_screenWidth, m_screenHeight, 0, 0);
|
|
}
|
|
|
|
// Get all rooms and objects to draw
|
|
GetRoomBounds(renderView, onlyRooms);
|
|
}
|
|
|
|
void Renderer11::CollectItems(short roomNumber, RenderView& renderView)
|
|
{
|
|
if (m_rooms.size() < roomNumber)
|
|
return;
|
|
|
|
RendererRoom& room = m_rooms[roomNumber];
|
|
ROOM_INFO* r = &g_Level.Rooms[room.RoomNumber];
|
|
|
|
short itemNum = NO_ITEM;
|
|
for (itemNum = r->itemNumber; itemNum != NO_ITEM; itemNum = g_Level.Items[itemNum].NextItem)
|
|
{
|
|
ItemInfo* item = &g_Level.Items[itemNum];
|
|
|
|
if (item->ObjectNumber == ID_LARA && itemNum == g_Level.Items[itemNum].NextItem)
|
|
break;
|
|
|
|
if (item->Status == ITEM_INVISIBLE)
|
|
continue;
|
|
|
|
if (item->ObjectNumber == ID_LARA && (BinocularRange || SpotcamOverlay || SpotcamDontDrawLara || CurrentLevel == 0))
|
|
continue;
|
|
|
|
if (!m_moveableObjects[item->ObjectNumber].has_value())
|
|
continue;
|
|
|
|
auto& obj = m_moveableObjects[item->ObjectNumber].value();
|
|
|
|
if (obj.DoNotDraw)
|
|
continue;
|
|
|
|
// Clip object by frustum only if it doesn't cast shadows. Otherwise we may see
|
|
// disappearing shadows if object gets out of frustum.
|
|
|
|
if (obj.ShadowType == ShadowMode::None)
|
|
{
|
|
auto bounds = TO_DX_BBOX(item->Pose, GetBoundsAccurate(item));
|
|
Vector3 min = bounds.Center - bounds.Extents;
|
|
Vector3 max = bounds.Center + bounds.Extents;
|
|
|
|
if (!renderView.camera.frustum.AABBInFrustum(min, max))
|
|
continue;
|
|
}
|
|
|
|
auto newItem = &m_items[itemNum];
|
|
|
|
newItem->ItemNumber = itemNum;
|
|
newItem->ObjectNumber = item->ObjectNumber;
|
|
newItem->Color = item->Color;
|
|
newItem->Translation = Matrix::CreateTranslation(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z);
|
|
newItem->Rotation = Matrix::CreateFromYawPitchRoll(TO_RAD(item->Pose.Orientation.y),
|
|
TO_RAD(item->Pose.Orientation.x),
|
|
TO_RAD(item->Pose.Orientation.z));
|
|
newItem->Scale = Matrix::CreateScale(1.0f);
|
|
newItem->World = newItem->Rotation * newItem->Translation;
|
|
|
|
CalculateLightFades(newItem);
|
|
CollectLightsForItem(newItem);
|
|
|
|
room.ItemsToDraw.push_back(newItem);
|
|
}
|
|
}
|
|
|
|
void Renderer11::CollectStatics(short roomNumber, RenderView& renderView)
|
|
{
|
|
if (m_rooms.size() < roomNumber)
|
|
return;
|
|
|
|
RendererRoom& room = m_rooms[roomNumber];
|
|
ROOM_INFO* r = &g_Level.Rooms[room.RoomNumber];
|
|
|
|
if (r->mesh.size() == 0)
|
|
return;
|
|
|
|
int numStatics = r->mesh.size();
|
|
for (int i = 0; i < numStatics; i++)
|
|
{
|
|
auto mesh = &r->mesh[i];
|
|
|
|
if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE))
|
|
continue;
|
|
|
|
if (!m_staticObjects[mesh->staticNumber].has_value())
|
|
continue;
|
|
|
|
auto& obj = *m_staticObjects[mesh->staticNumber];
|
|
|
|
if (obj.ObjectMeshes.size() == 0)
|
|
continue;
|
|
|
|
auto stat = &StaticObjects[mesh->staticNumber];
|
|
auto bounds = TO_DX_BBOX(mesh->pos, &stat->visibilityBox);
|
|
Vector3 min = bounds.Center - bounds.Extents;
|
|
Vector3 max = bounds.Center + bounds.Extents;
|
|
|
|
if (!renderView.camera.frustum.AABBInFrustum(min, max))
|
|
continue;
|
|
|
|
std::vector<RendererLight*> lights;
|
|
if (obj.ObjectMeshes.front()->LightMode != LIGHT_MODES::LIGHT_MODE_STATIC)
|
|
CollectLights(mesh->pos.Position.ToVector3(), ITEM_LIGHT_COLLECTION_RADIUS, room.RoomNumber, NO_ROOM, false, lights);
|
|
|
|
Matrix world = (Matrix::CreateFromYawPitchRoll(TO_RAD(mesh->pos.Orientation.y), TO_RAD(mesh->pos.Orientation.x), TO_RAD(mesh->pos.Orientation.z)) *
|
|
Matrix::CreateTranslation(mesh->pos.Position.x, mesh->pos.Position.y, mesh->pos.Position.z));
|
|
|
|
auto staticInfo = RendererStatic
|
|
{
|
|
mesh->staticNumber,
|
|
room.RoomNumber,
|
|
world,
|
|
mesh->color,
|
|
room.AmbientLight,
|
|
lights
|
|
};
|
|
|
|
room.StaticsToDraw.push_back(staticInfo);
|
|
}
|
|
}
|
|
|
|
void Renderer11::CollectLights(Vector3 position, float radius, int roomNumber, int prevRoomNumber, bool prioritizeShadowLight, std::vector<RendererLight*>& lights)
|
|
{
|
|
if (m_rooms.size() < roomNumber)
|
|
return;
|
|
|
|
// Now collect lights from dynamic list and from rooms
|
|
std::vector<RendererLight*> tempLights;
|
|
tempLights.reserve(MAX_LIGHTS_DRAW);
|
|
|
|
RendererRoom& room = m_rooms[roomNumber];
|
|
ROOM_INFO* nativeRoom = &g_Level.Rooms[room.RoomNumber];
|
|
|
|
auto roomsToCheck = GetRoomList(roomNumber);
|
|
|
|
RendererLight* brightestLight = nullptr;
|
|
float brightest = 0.0f;
|
|
|
|
// Dynamic lights have the priority
|
|
for (auto& light : dynamicLights)
|
|
{
|
|
float distance = (position - light.Position).Length();
|
|
|
|
// Collect only lights nearer than 20 sectors
|
|
if (distance >= SECTOR(20))
|
|
continue;
|
|
|
|
// Check the out radius
|
|
if (distance > light.Out + radius)
|
|
continue;
|
|
|
|
float attenuation = 1.0f - distance / light.Out;
|
|
float intensity = std::max(0.0f, attenuation * light.Intensity * Luma(light.Color));
|
|
|
|
light.LocalIntensity = intensity;
|
|
light.Distance = distance;
|
|
|
|
tempLights.push_back(&light);
|
|
}
|
|
|
|
// Check current room and also neighbour rooms
|
|
for (auto roomToCheck : roomsToCheck)
|
|
{
|
|
RendererRoom& currentRoom = m_rooms[roomToCheck];
|
|
int numLights = currentRoom.Lights.size();
|
|
|
|
for (int j = 0; j < numLights; j++)
|
|
{
|
|
RendererLight* light = ¤tRoom.Lights[j];
|
|
|
|
// Check only lights different from sun
|
|
if (light->Type == LIGHT_TYPE_SUN)
|
|
{
|
|
// Suns from non-adjacent rooms are not added!
|
|
if (roomToCheck != roomNumber && prevRoomNumber != NO_ROOM && prevRoomNumber != roomToCheck)
|
|
continue;
|
|
|
|
// Sun is added without distance checks
|
|
light->Distance = 0;
|
|
light->LocalIntensity = 0;
|
|
}
|
|
else if (light->Type == LIGHT_TYPE_POINT || light->Type == LIGHT_TYPE_SHADOW)
|
|
{
|
|
Vector3 lightPosition = Vector3(light->Position.x, light->Position.y, light->Position.z);
|
|
float distance = (position - lightPosition).Length();
|
|
|
|
// Collect only lights nearer than 20 sectors
|
|
if (distance >= SECTOR(20))
|
|
continue;
|
|
|
|
// Check the out radius
|
|
if (distance > light->Out + radius)
|
|
continue;
|
|
|
|
float attenuation = 1.0f - distance / light->Out;
|
|
float intensity = std::max(0.0f, attenuation * light->Intensity * Luma(light->Color));
|
|
|
|
light->LocalIntensity = intensity;
|
|
light->Distance = distance;
|
|
|
|
// If collecting shadows, try to collect shadow casting light
|
|
if (light->CastShadows && prioritizeShadowLight && light->Type == LIGHT_TYPE_POINT)
|
|
{
|
|
if (intensity >= brightest)
|
|
{
|
|
brightest = intensity;
|
|
brightestLight = light;
|
|
}
|
|
}
|
|
}
|
|
else if (light->Type == LIGHT_TYPE_SPOT)
|
|
{
|
|
Vector3 lightPosition = Vector3(light->Position.x, light->Position.y, light->Position.z);
|
|
float distance = (position - lightPosition).Length();
|
|
|
|
// Collect only lights nearer than 20 sectors
|
|
if (distance >= SECTOR(20))
|
|
continue;
|
|
|
|
// Check the range
|
|
if (distance > light->Out + radius)
|
|
continue;
|
|
|
|
float attenuation = 1.0f - distance / light->Out;
|
|
float intensity = std::max(0.0f, attenuation * light->Intensity * Luma(light->Color));
|
|
|
|
light->LocalIntensity = intensity;
|
|
|
|
// If shadow pointer provided, try to collect shadow casting light
|
|
if (light->CastShadows && prioritizeShadowLight)
|
|
{
|
|
if (intensity >= brightest)
|
|
{
|
|
brightest = intensity;
|
|
brightestLight = light;
|
|
}
|
|
}
|
|
|
|
light->Distance = distance;
|
|
}
|
|
else
|
|
{
|
|
// Invalid light type
|
|
continue;
|
|
}
|
|
|
|
tempLights.push_back(light);
|
|
}
|
|
}
|
|
|
|
// Sort lights by distance, if needed
|
|
|
|
if (tempLights.size() > MAX_LIGHTS_PER_ITEM)
|
|
{
|
|
std::sort(
|
|
tempLights.begin(),
|
|
tempLights.end(),
|
|
[](RendererLight* a, RendererLight* b)
|
|
{
|
|
return a->Distance < b->Distance;
|
|
}
|
|
);
|
|
}
|
|
|
|
// Now put actual lights to provided vector
|
|
lights.clear();
|
|
|
|
// Always add brightest light, if collecting shadow light is specified, even if it's far in range
|
|
if (prioritizeShadowLight && brightestLight)
|
|
lights.push_back(brightestLight);
|
|
|
|
// Add max 8 lights per item, including the shadow light for Lara eventually
|
|
for (auto l : tempLights)
|
|
{
|
|
if (prioritizeShadowLight && brightestLight == l)
|
|
continue;
|
|
|
|
lights.push_back(l);
|
|
|
|
if (lights.size() == MAX_LIGHTS_PER_ITEM)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Renderer11::CollectLightsForCamera()
|
|
{
|
|
std::vector<RendererLight*> lightsToDraw;
|
|
CollectLights(Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z), CAMERA_LIGHT_COLLECTION_RADIUS, Camera.pos.roomNumber, NO_ROOM, true, lightsToDraw);
|
|
|
|
if (lightsToDraw.size() > 0 && lightsToDraw.front()->CastShadows)
|
|
shadowLight = lightsToDraw.front();
|
|
else
|
|
shadowLight = nullptr;
|
|
}
|
|
|
|
void Renderer11::CollectLightsForEffect(short roomNumber, RendererEffect* effect)
|
|
{
|
|
CollectLights(effect->Effect->pos.Position.ToVector3(), ITEM_LIGHT_COLLECTION_RADIUS, roomNumber, NO_ROOM, false, effect->LightsToDraw);
|
|
}
|
|
|
|
void Renderer11::CollectLightsForItem(RendererItem* item)
|
|
{
|
|
auto pos = Vector3::Transform(Vector3::Zero, item->Translation);
|
|
CollectLights(pos, ITEM_LIGHT_COLLECTION_RADIUS, item->RoomNumber, item->PrevRoomNumber, false, item->LightsToDraw);
|
|
}
|
|
|
|
void Renderer11::CalculateLightFades(RendererItem *item)
|
|
{
|
|
ItemInfo* nativeItem = &g_Level.Items[item->ItemNumber];
|
|
|
|
// Interpolate ambient light between rooms
|
|
if (item->PrevRoomNumber == NO_ROOM)
|
|
{
|
|
item->PrevRoomNumber = nativeItem->RoomNumber;
|
|
item->RoomNumber = nativeItem->RoomNumber;
|
|
item->LightFade = 1.0f;
|
|
}
|
|
else if (nativeItem->RoomNumber != item->RoomNumber)
|
|
{
|
|
item->PrevRoomNumber = item->RoomNumber;
|
|
item->RoomNumber = nativeItem->RoomNumber;
|
|
item->LightFade = 0.0f;
|
|
}
|
|
else if (item->LightFade < 1.0f)
|
|
{
|
|
item->LightFade += AMBIENT_LIGHT_INTERPOLATION_STEP;
|
|
item->LightFade = std::clamp(item->LightFade, 0.0f, 1.0f);
|
|
}
|
|
|
|
if (item->PrevRoomNumber == NO_ROOM || item->LightFade == 1.0f)
|
|
item->AmbientLight = m_rooms[nativeItem->RoomNumber].AmbientLight;
|
|
else
|
|
{
|
|
auto prev = m_rooms[item->PrevRoomNumber].AmbientLight;
|
|
auto next = m_rooms[item->RoomNumber].AmbientLight;
|
|
|
|
item->AmbientLight.x = Lerp(prev.x, next.x, item->LightFade);
|
|
item->AmbientLight.y = Lerp(prev.y, next.y, item->LightFade);
|
|
item->AmbientLight.z = Lerp(prev.z, next.z, item->LightFade);
|
|
}
|
|
|
|
// Multiply calculated ambient light by object tint
|
|
item->AmbientLight *= nativeItem->Color;
|
|
}
|
|
|
|
void Renderer11::CollectLightsForRoom(short roomNumber, RenderView &renderView)
|
|
{
|
|
if (m_rooms.size() < roomNumber)
|
|
return;
|
|
|
|
RendererRoom& room = m_rooms[roomNumber];
|
|
ROOM_INFO* r = &g_Level.Rooms[roomNumber];
|
|
|
|
Vector3 boxMin = Vector3(r->x + WALL_SIZE, r->maxceiling - STEP_SIZE, r->z + WALL_SIZE);
|
|
Vector3 boxMax = Vector3(r->x + (r->xSize - 1) * WALL_SIZE, r->minfloor + STEP_SIZE, r->z + (r->zSize - 1) * WALL_SIZE);
|
|
|
|
// Collect dynamic lights for rooms
|
|
for (int i = 0; i < dynamicLights.size(); i++)
|
|
{
|
|
RendererLight* light = &dynamicLights[i];
|
|
|
|
Vector3 center = Vector3(light->Position.x, light->Position.y, light->Position.z);
|
|
|
|
if (renderView.lightsToDraw.size() < NUM_LIGHTS_PER_BUFFER - 1 &&
|
|
SphereBoxIntersection(boxMin, boxMax, center, 0.0f))
|
|
renderView.lightsToDraw.push_back(light);
|
|
}
|
|
}
|
|
|
|
void Renderer11::CollectEffects(short roomNumber)
|
|
{
|
|
if (m_rooms.size() < roomNumber)
|
|
return;
|
|
|
|
RendererRoom& room = m_rooms[roomNumber];
|
|
ROOM_INFO* r = &g_Level.Rooms[room.RoomNumber];
|
|
|
|
short fxNum = NO_ITEM;
|
|
for (fxNum = r->fxNumber; fxNum != NO_ITEM; fxNum = EffectList[fxNum].nextFx)
|
|
{
|
|
FX_INFO *fx = &EffectList[fxNum];
|
|
|
|
if (fx->objectNumber < 0)
|
|
continue;
|
|
|
|
ObjectInfo *obj = &Objects[fx->objectNumber];
|
|
|
|
RendererEffect *newEffect = &m_effects[fxNum];
|
|
|
|
newEffect->Effect = fx;
|
|
newEffect->Id = fxNum;
|
|
newEffect->World = Matrix::CreateFromYawPitchRoll(fx->pos.Orientation.y, fx->pos.Position.x, fx->pos.Position.z) * Matrix::CreateTranslation(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z);
|
|
newEffect->Mesh = GetMesh(obj->nmeshes ? obj->meshIndex : fx->frameNumber);
|
|
|
|
CollectLightsForEffect(fx->roomNumber, newEffect);
|
|
|
|
room.EffectsToDraw.push_back(newEffect);
|
|
}
|
|
}
|
|
|
|
void Renderer11::ResetAnimations()
|
|
{
|
|
for (int i = 0; i < NUM_ITEMS; i++)
|
|
m_items[i].DoneAnimations = false;
|
|
}
|
|
} // namespace TEN::Renderer
|
|
|