mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00
3859 lines
126 KiB
C++
3859 lines
126 KiB
C++
#include "framework.h"
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <execution>
|
|
#include <filesystem>
|
|
|
|
#include "ConstantBuffers/CameraMatrixBuffer.h"
|
|
#include "Game/animation.h"
|
|
#include "Game/camera.h"
|
|
#include "Game/control/control.h"
|
|
#include "Game/control/volume.h"
|
|
#include "Game/effects/DisplaySprite.h"
|
|
#include "Game/effects/Hair.h"
|
|
#include "Game/effects/tomb4fx.h"
|
|
#include "Game/effects/weather.h"
|
|
#include "Game/Gui.h"
|
|
#include "Game/Hud/Hud.h"
|
|
#include "Game/items.h"
|
|
#include "Game/Lara/lara.h"
|
|
#include "Game/savegame.h"
|
|
#include "Game/Setup.h"
|
|
#include "Objects/Effects/tr4_locusts.h"
|
|
#include "Objects/Generic/Object/rope.h"
|
|
#include "Objects/TR3/Entity/FishSwarm.h"
|
|
#include "Objects/TR4/Entity/tr4_beetle_swarm.h"
|
|
#include "Objects/TR5/Emitter/tr5_bats_emitter.h"
|
|
#include "Objects/TR5/Emitter/tr5_rats_emitter.h"
|
|
#include "Renderer/RenderView.h"
|
|
#include "Renderer/Renderer.h"
|
|
#include "Renderer/Structures/RendererSortableObject.h"
|
|
#include "Specific/configuration.h"
|
|
#include "Specific/level.h"
|
|
#include "Specific/winmain.h"
|
|
|
|
using namespace std::chrono;
|
|
using namespace TEN::Effects::Hair;
|
|
using namespace TEN::Entities::Creatures::TR3;
|
|
using namespace TEN::Entities::Generic;
|
|
using namespace TEN::Hud;
|
|
using namespace TEN::Renderer::Structures;
|
|
using namespace TEN::Effects::Environment;
|
|
using namespace TEN::Effects::DisplaySprite;
|
|
|
|
extern GUNSHELL_STRUCT Gunshells[MAX_GUNSHELL];
|
|
|
|
namespace TEN::Renderer
|
|
{
|
|
void Renderer::RenderBlobShadows(RenderView& renderView)
|
|
{
|
|
auto nearestSpheres = std::vector<Sphere>{};
|
|
nearestSpheres.reserve(g_Configuration.ShadowBlobsMax);
|
|
|
|
// Collect player spheres.
|
|
static const std::array<LARA_MESHES, 4> sphereMeshes = { LM_HIPS, LM_TORSO, LM_LFOOT, LM_RFOOT };
|
|
static const std::array<float, 4> sphereScaleFactors = { 6.0f, 3.2f, 2.8f, 2.8f };
|
|
|
|
for (auto& room : renderView.RoomsToDraw)
|
|
{
|
|
for (auto& i : room->ItemsToDraw)
|
|
{
|
|
auto& nativeItem = g_Level.Items[i->ItemNumber];
|
|
|
|
// Skip everything that isn't alive or a vehicle.
|
|
if (Objects[nativeItem.ObjectNumber].shadowType == ShadowMode::None)
|
|
continue;
|
|
|
|
if (nativeItem.IsLara())
|
|
{
|
|
for (auto i = 0; i < sphereMeshes.size(); i++)
|
|
{
|
|
if (!nativeItem.MeshBits.Test(sphereMeshes[i]))
|
|
continue;
|
|
|
|
auto& mesh = g_Level.Meshes[nativeItem.Model.MeshIndex[sphereMeshes[i]]];
|
|
|
|
// Push foot spheres a little lower.
|
|
auto offset = Vector3i(mesh.sphere.Center.x, mesh.sphere.Center.y, mesh.sphere.Center.z);
|
|
if (sphereMeshes[i] == LM_LFOOT || sphereMeshes[i] == LM_RFOOT)
|
|
offset.y += 8;
|
|
|
|
auto& newSphere = nearestSpheres.emplace_back();
|
|
newSphere.position = GetJointPosition(LaraItem, sphereMeshes[i], offset).ToVector3();;
|
|
newSphere.radius = mesh.sphere.Radius * sphereScaleFactors[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto bounds = GameBoundingBox(&nativeItem);
|
|
auto extents = bounds.GetExtents();
|
|
auto center = Geometry::TranslatePoint(nativeItem.Pose.Position.ToVector3(), nativeItem.Pose.Orientation, bounds.GetCenter());
|
|
center.y = nativeItem.Pose.Position.y;
|
|
|
|
auto& newSphere = nearestSpheres.emplace_back();
|
|
newSphere.position = center;
|
|
newSphere.radius = std::max(abs(extents.x), abs(extents.z)) * 1.5f;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nearestSpheres.size() > g_Configuration.ShadowBlobsMax)
|
|
{
|
|
std::sort(nearestSpheres.begin(), nearestSpheres.end(), [](const Sphere& a, const Sphere& b)
|
|
{
|
|
auto& laraPos = LaraItem->Pose.Position;
|
|
return Vector3::Distance(laraPos.ToVector3(), a.position) < Vector3::Distance(laraPos.ToVector3(), b.position);
|
|
});
|
|
|
|
std::copy(nearestSpheres.begin(), nearestSpheres.begin() + g_Configuration.ShadowBlobsMax, _stShadowMap.Spheres);
|
|
_stShadowMap.NumSpheres = g_Configuration.ShadowBlobsMax;
|
|
}
|
|
else
|
|
{
|
|
std::copy(nearestSpheres.begin(), nearestSpheres.end(), _stShadowMap.Spheres);
|
|
_stShadowMap.NumSpheres = (int)nearestSpheres.size();
|
|
}
|
|
}
|
|
|
|
void Renderer::ClearShadowMap()
|
|
{
|
|
for (int step = 0; step < _shadowMap.RenderTargetView.size(); step++)
|
|
{
|
|
_context->ClearRenderTargetView(_shadowMap.RenderTargetView[step].Get(), Colors::White);
|
|
_context->ClearDepthStencilView(_shadowMap.DepthStencilView[step].Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL,
|
|
1.0f, 0);
|
|
}
|
|
}
|
|
|
|
void Renderer::RenderShadowMap(RendererItem* item, RenderView& renderView)
|
|
{
|
|
// Doesn't cast shadow
|
|
if (_moveableObjects[item->ObjectID].value().ShadowType == ShadowMode::None)
|
|
return;
|
|
|
|
// Only render for Lara if such setting is active
|
|
if (g_Configuration.ShadowType == ShadowMode::Lara && _moveableObjects[item->ObjectID].value().ShadowType != ShadowMode::Lara)
|
|
return;
|
|
|
|
// No shadow light found
|
|
if (_shadowLight == nullptr)
|
|
return;
|
|
|
|
// Shadow light found but type is incorrect
|
|
if (_shadowLight->Type != LightType::Point && _shadowLight->Type != LightType::Spot)
|
|
return;
|
|
|
|
// Reset GPU state
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
|
|
auto shadowLightPos = _shadowLight->Hash == 0 ? _shadowLight->Position :
|
|
Vector3::Lerp(_shadowLight->PrevPosition, _shadowLight->Position, GetInterpolationFactor());
|
|
|
|
for (int step = 0; step < 6; step++)
|
|
{
|
|
// Bind render target
|
|
_context->OMSetRenderTargets(1, _shadowMap.RenderTargetView[step].GetAddressOf(),
|
|
_shadowMap.DepthStencilView[step].Get());
|
|
|
|
_context->RSSetViewports(1, &_shadowMapViewport);
|
|
ResetScissor();
|
|
|
|
if (shadowLightPos == item->Position)
|
|
return;
|
|
|
|
UINT stride = sizeof(Vertex);
|
|
UINT offset = 0;
|
|
|
|
// Set shaders
|
|
_context->VSSetShader(_vsShadowMap.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psShadowMap.Get(), nullptr, 0);
|
|
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
_context->IASetInputLayout(_inputLayout.Get());
|
|
_context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
// Set texture
|
|
BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp);
|
|
BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp);
|
|
|
|
// Set camera matrices
|
|
Matrix view = Matrix::CreateLookAt(shadowLightPos, shadowLightPos +
|
|
RenderTargetCube::forwardVectors[step] * BLOCK(10),
|
|
RenderTargetCube::upVectors[step]);
|
|
|
|
Matrix projection = Matrix::CreatePerspectiveFieldOfView(90.0f * PI / 180.0f, 1.0f, 16.0f, _shadowLight->Out);
|
|
|
|
CCameraMatrixBuffer shadowProjection;
|
|
shadowProjection.ViewProjection = view * projection;
|
|
_cbCameraMatrices.UpdateData(shadowProjection, _context.Get());
|
|
BindConstantBufferVS(ConstantBufferRegister::Camera, _cbCameraMatrices.get());
|
|
|
|
_stShadowMap.LightViewProjections[step] = (view * projection);
|
|
|
|
SetAlphaTest(AlphaTestMode::GreatherThan, ALPHA_TEST_THRESHOLD);
|
|
|
|
RendererObject& obj = GetRendererObject((GAME_OBJECT_ID)item->ObjectID);
|
|
|
|
_stItem.World = item->InterpolatedWorld;
|
|
_stItem.Color = item->Color;
|
|
_stItem.AmbientLight = item->AmbientLight;
|
|
memcpy(_stItem.BonesMatrices, item->InterpolatedAnimTransforms, sizeof(Matrix) * MAX_BONES);
|
|
for (int k = 0; k < MAX_BONES; k++)
|
|
_stItem.BoneLightModes[k] = (int)LightMode::Static;
|
|
|
|
_cbItem.UpdateData(_stItem, _context.Get());
|
|
BindConstantBufferVS(ConstantBufferRegister::Item, _cbItem.get());
|
|
BindConstantBufferPS(ConstantBufferRegister::Item, _cbItem.get());
|
|
|
|
for (int k = 0; k < obj.ObjectMeshes.size(); k++)
|
|
{
|
|
auto* mesh = GetMesh(item->MeshIds[k]);
|
|
|
|
for (auto& bucket : mesh->Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
continue;
|
|
|
|
if (bucket.BlendMode != BlendMode::Opaque && bucket.BlendMode != BlendMode::AlphaTest)
|
|
continue;
|
|
|
|
// Draw vertices
|
|
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
|
|
|
|
_numShadowMapDrawCalls++;
|
|
}
|
|
}
|
|
|
|
if (item->ObjectID == ID_LARA)
|
|
{
|
|
RendererRoom& room = _rooms[item->RoomNumber];
|
|
|
|
DrawLaraHolsters(item, &room, renderView, RendererPass::ShadowMap);
|
|
DrawLaraJoints(item, &room, renderView, RendererPass::ShadowMap);
|
|
DrawLaraHair(item, &room, renderView, RendererPass::ShadowMap);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawGunShells(RenderView& view, RendererPass rendererPass)
|
|
{
|
|
auto& room = _rooms[LaraItem->RoomNumber];
|
|
auto* item = &_items[LaraItem->Index];
|
|
|
|
int gunShellCount = 0;
|
|
int objectID = 0;
|
|
|
|
for (int i = 0; i < MAX_GUNSHELL; i++)
|
|
{
|
|
auto* gunshell = &Gunshells[i];
|
|
|
|
if (gunshell->counter <= 0)
|
|
continue;
|
|
|
|
if (IgnoreMirrorPassForRoom(gunshell->roomNumber))
|
|
continue;
|
|
|
|
objectID = gunshell->objectNumber;
|
|
|
|
auto translation = Matrix::CreateTranslation(gunshell->pos.Position.ToVector3());
|
|
auto rotMatrix = gunshell->pos.Orientation.ToRotationMatrix();
|
|
auto worldMatrix = rotMatrix * translation;
|
|
|
|
auto prevTranslation = Matrix::CreateTranslation(
|
|
gunshell->oldPos.Position.x,
|
|
gunshell->oldPos.Position.y,
|
|
gunshell->oldPos.Position.z);
|
|
auto prevRotMatrix = gunshell->oldPos.Orientation.ToRotationMatrix();
|
|
auto prevWorldMatrix = prevRotMatrix * prevTranslation;
|
|
|
|
worldMatrix = Matrix::Lerp(prevWorldMatrix, worldMatrix, GetInterpolationFactor());
|
|
ReflectMatrixOptionally(worldMatrix);
|
|
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[gunShellCount].World = worldMatrix;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[gunShellCount].Ambient = room.AmbientLight;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[gunShellCount].Color = room.AmbientLight;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[gunShellCount].LightMode = (int)LightMode::Dynamic;
|
|
BindInstancedStaticLights(item->LightsToDraw, gunShellCount);
|
|
|
|
gunShellCount++;
|
|
}
|
|
|
|
if (gunShellCount > 0)
|
|
{
|
|
auto& moveableObject = *_moveableObjects[objectID];
|
|
|
|
_context->VSSetShader(_vsInstancedStaticMeshes.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psInstancedStaticMeshes.Get(), nullptr, 0);
|
|
|
|
unsigned int stride = sizeof(Vertex);
|
|
unsigned int offset = 0;
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetAlphaTest(AlphaTestMode::GreatherThan, ALPHA_TEST_THRESHOLD);
|
|
|
|
_cbInstancedStaticMeshBuffer.UpdateData(_stInstancedStaticMeshBuffer, _context.Get());
|
|
|
|
const auto& mesh = *moveableObject.ObjectMeshes[0];
|
|
for (const auto& bucket : mesh.Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
continue;
|
|
|
|
BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[bucket.Texture]), SamplerStateRegister::AnisotropicClamp);
|
|
BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[bucket.Texture]), SamplerStateRegister::AnisotropicClamp);
|
|
|
|
DrawIndexedInstancedTriangles(bucket.NumIndices, gunShellCount, bucket.StartIndex, 0);
|
|
|
|
_numInstancedStaticsDrawCalls++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::PrepareRopes(RenderView& view)
|
|
{
|
|
for (const auto& rope : Ropes)
|
|
{
|
|
if (!rope.active)
|
|
continue;
|
|
|
|
auto translationMatrix = Matrix::CreateTranslation(rope.position.ToVector3());
|
|
auto absolutePoints = std::array<Vector3, 24>{};
|
|
|
|
for (int i = 0; i < rope.segment.size(); i++)
|
|
{
|
|
const auto* segment = &rope.PrevMeshSegments[i];
|
|
|
|
auto relPos = Vector3(segment->x >> FP_SHIFT, segment->y >> FP_SHIFT, segment->z >> FP_SHIFT);
|
|
auto prevOutput = Vector3::Transform(relPos, translationMatrix);
|
|
|
|
segment = &rope.meshSegment[i];
|
|
|
|
relPos = Vector3(segment->x >> FP_SHIFT, segment->y >> FP_SHIFT, segment->z >> FP_SHIFT);
|
|
auto currentOutput = Vector3::Transform(relPos, translationMatrix);
|
|
|
|
auto absolutePos = Vector3::Lerp(prevOutput, currentOutput, GetInterpolationFactor());
|
|
absolutePoints[i] = absolutePos;
|
|
}
|
|
|
|
for (int i = 0; i < (rope.segment.size() - 1); i++)
|
|
{
|
|
const auto& pos0 = absolutePoints[i];
|
|
const auto& pos1 = absolutePoints[i + 1];
|
|
auto pos = (pos0 + pos1) / 2;
|
|
|
|
auto dir = pos1 - pos0;
|
|
dir.Normalize();
|
|
|
|
AddSpriteBillboardConstrained(
|
|
&_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_EMPTY1],
|
|
pos, _rooms[rope.room].AmbientLight,
|
|
PI_DIV_2, 1.0f, Vector2(32.0f, Vector3::Distance(pos0, pos1)), BlendMode::AlphaBlend, dir, false, view);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawLines2D()
|
|
{
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetDepthState(DepthState::Read);
|
|
SetCullMode(CullMode::None);
|
|
|
|
_context->VSSetShader(_vsSolid.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psSolid.Get(), nullptr, 0);
|
|
auto worldMatrix = Matrix::CreateOrthographicOffCenter(0, _screenWidth, _screenHeight, 0, _viewport.MinDepth, _viewport.MaxDepth);
|
|
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST);
|
|
|
|
_primitiveBatch->Begin();
|
|
|
|
for (const auto& line : _lines2DToDraw)
|
|
{
|
|
auto vertex0 = Vertex{};
|
|
vertex0.Position.x = line.Origin.x;
|
|
vertex0.Position.y = line.Origin.y;
|
|
vertex0.Position.z = 1.0f;
|
|
vertex0.Color = line.Color;
|
|
|
|
auto vertex1 = Vertex{};
|
|
vertex1.Position.x = line.Target.x;
|
|
vertex1.Position.y = line.Target.y;
|
|
vertex1.Position.z = 1.0f;
|
|
vertex1.Color = line.Color;
|
|
|
|
vertex0.Position = Vector3::Transform(vertex0.Position, worldMatrix);
|
|
vertex1.Position = Vector3::Transform(vertex1.Position, worldMatrix);
|
|
vertex0.Position.z = 0.5f;
|
|
vertex1.Position.z = 0.5f;
|
|
|
|
_numLinesDrawCalls++;
|
|
_numDrawCalls++;
|
|
}
|
|
|
|
_primitiveBatch->End();
|
|
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
}
|
|
|
|
void Renderer::DrawSpiders(RenderView& view, RendererPass rendererPass)
|
|
{
|
|
/*XMMATRIX world;
|
|
UINT cPasses = 1;
|
|
|
|
if (Objects[ID_SPIDERS_EMITTER].loaded)
|
|
{
|
|
ObjectInfo* obj = &Objects[ID_SPIDERS_EMITTER];
|
|
RendererObject* moveableObj = m_moveableObjects[ID_SPIDERS_EMITTER].get();
|
|
short* meshPtr = Meshes[Objects[ID_SPIDERS_EMITTER].meshIndex + ((Wibble >> 2) & 2)];
|
|
RendererMesh* mesh = m_meshPointersToMesh[meshPtr];
|
|
RendererBucket* bucket = mesh->GetBucket(bucketIndex);
|
|
|
|
if (bucket->NumVertices == 0)
|
|
return true;
|
|
|
|
setGpuStateForBucket(bucketIndex);
|
|
|
|
m_device->SetStreamSource(0, bucket->GetVertexBuffer(), 0, sizeof(Vertex));
|
|
m_device->SetIndices(bucket->GetIndexBuffer());
|
|
|
|
LPD3DXEFFECT effect;
|
|
if (pass == RENDERER_PASS_SHADOW_MAP)
|
|
effect = m_shaderDepth->GetEffect();
|
|
else if (pass == RENDERER_PASS_RECONSTRUCT_DEPTH)
|
|
effect = m_shaderReconstructZBuffer->GetEffect();
|
|
else if (pass == RENDERER_PASS_GBUFFER)
|
|
effect = m_shaderFillGBuffer->GetEffect();
|
|
else
|
|
effect = m_shaderTransparent->GetEffect();
|
|
|
|
effect->SetBool(effect->GetParameterByName(nullptr, "UseSkinning"), false);
|
|
effect->SetInt(effect->GetParameterByName(nullptr, "ModelType"), MODEL_TYPE_MOVEABLE);
|
|
|
|
if (bucketIndex == RENDERER_BUCKET_SOLID || bucketIndex == RENDERER_BUCKET_SOLID_DS)
|
|
effect->SetInt(effect->GetParameterByName(nullptr, "BlendMode"), BlendMode::Opaque);
|
|
else
|
|
effect->SetInt(effect->GetParameterByName(nullptr, "BlendMode"), BlendMode::AlphaTest);
|
|
|
|
for (int i = 0; i < NUM_SPIDERS; i++)
|
|
{
|
|
SPIDER_STRUCT* spider = &Spiders[i];
|
|
|
|
if (spider->on)
|
|
{
|
|
XMMATRIXTranslation(&m_tempTranslation, spider->pos.xPos, spider->pos.yPos, spider->pos.zPos);
|
|
XMMATRIXRotationYawPitchRoll(&m_tempRotation, spider->pos.yRot, spider->pos.xRot, spider->pos.zRot);
|
|
XMMATRIXMultiply(&m_tempWorld, &m_tempRotation, &m_tempTranslation);
|
|
effect->SetMatrix(effect->GetParameterByName(nullptr, "World"), &m_tempWorld);
|
|
|
|
effect->SetVector(effect->GetParameterByName(nullptr, "AmbientLight"), &rooms[spider->roomNumber]->AmbientLight);
|
|
|
|
for (int iPass = 0; iPass < cPasses; iPass++)
|
|
{
|
|
effect->BeginPass(iPass);
|
|
effect->CommitChanges();
|
|
|
|
drawPrimitives(D3DPT_TRIANGLELIST, 0, 0, bucket->NumVertices, 0, bucket->Indices.size() / 3);
|
|
|
|
effect->EndPass();
|
|
}
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
|
|
void Renderer::DrawRats(RenderView& view, RendererPass rendererPass)
|
|
{
|
|
if (!Objects[ID_RATS_EMITTER].loaded)
|
|
return;
|
|
|
|
if (rendererPass == RendererPass::CollectTransparentFaces)
|
|
{
|
|
for (int i = 0; i < NUM_RATS; i++)
|
|
{
|
|
auto* rat = &Rats[i];
|
|
|
|
if (IgnoreMirrorPassForRoom(rat->RoomNumber))
|
|
continue;
|
|
|
|
if (rat->On)
|
|
{
|
|
RendererMesh* mesh = GetMesh(Objects[ID_RATS_EMITTER].meshIndex + (rand() % 8));
|
|
|
|
for (int j = 0; j < mesh->Buckets.size(); j++)
|
|
{
|
|
auto& bucket = mesh->Buckets[j];
|
|
|
|
if (IsSortedBlendMode(bucket.BlendMode))
|
|
{
|
|
for (int p = 0; p < bucket.Polygons.size(); p++)
|
|
{
|
|
auto centre = Vector3::Transform(
|
|
bucket.Polygons[p].Centre, rat->Transform);
|
|
int distance = (centre - view.Camera.WorldPosition).Length();
|
|
|
|
RendererSortableObject object;
|
|
object.ObjectType = RendererObjectType::MoveableAsStatic;
|
|
object.Centre = centre;
|
|
object.Distance = distance;
|
|
object.Bucket = &bucket;
|
|
object.Mesh = mesh;
|
|
object.Polygon = &bucket.Polygons[p];
|
|
object.World = rat->Transform;
|
|
object.Room = &_rooms[rat->RoomNumber];
|
|
|
|
view.TransparentObjectsToDraw.push_back(object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool activeRatsExist = false;
|
|
for (int i = 0; i < NUM_RATS; i++)
|
|
{
|
|
auto* rat = &Rats[i];
|
|
|
|
if (IgnoreMirrorPassForRoom(rat->RoomNumber))
|
|
continue;
|
|
|
|
if (rat->On)
|
|
{
|
|
activeRatsExist = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (activeRatsExist)
|
|
{
|
|
if (rendererPass == RendererPass::GBuffer)
|
|
{
|
|
_context->VSSetShader(_vsGBufferStatics.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psGBuffer.Get(), nullptr, 0);
|
|
}
|
|
else
|
|
{
|
|
_context->VSSetShader(_vsStatics.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psStatics.Get(), nullptr, 0);
|
|
}
|
|
|
|
UINT stride = sizeof(Vertex);
|
|
UINT offset = 0;
|
|
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
RendererObject& moveableObj = *_moveableObjects[ID_RATS_EMITTER];
|
|
|
|
_stStatic.LightMode = (int)moveableObj.ObjectMeshes[0]->LightMode;
|
|
|
|
for (int i = 0; i < NUM_RATS; i++)
|
|
{
|
|
auto* rat = &Rats[i];
|
|
|
|
if (rat->On)
|
|
{
|
|
RendererMesh* mesh = GetMesh(Objects[ID_RATS_EMITTER].meshIndex + (rand() % 8));
|
|
|
|
Matrix world = rat->Transform;
|
|
ReflectMatrixOptionally(world);
|
|
|
|
_stStatic.World = world;
|
|
_stStatic.Color = Vector4::One;
|
|
_stStatic.AmbientLight = _rooms[rat->RoomNumber].AmbientLight;
|
|
|
|
if (rendererPass != RendererPass::GBuffer)
|
|
{
|
|
BindStaticLights(_rooms[rat->RoomNumber].LightsToDraw);
|
|
}
|
|
|
|
_cbStatic.UpdateData(_stStatic, _context.Get());
|
|
|
|
for (auto& bucket : mesh->Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int passes = rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest ? 2 : 1;
|
|
|
|
for (int p = 0; p < passes; p++)
|
|
{
|
|
if (!SetupBlendModeAndAlphaTest(bucket.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);
|
|
|
|
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
|
|
|
|
_numMoveablesDrawCalls++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawFishSwarm(RenderView& view, RendererPass rendererPass)
|
|
{
|
|
if (!Objects[ID_FISH_EMITTER].loaded)
|
|
return;
|
|
|
|
if (rendererPass == RendererPass::CollectTransparentFaces)
|
|
{
|
|
for (const auto& fish : FishSwarm)
|
|
{
|
|
if (fish.Life <= 0.0f)
|
|
continue;
|
|
|
|
auto& mesh = *GetMesh(Objects[ID_FISH_EMITTER].meshIndex + fish.MeshIndex);
|
|
for (auto& bucket : mesh.Buckets)
|
|
{
|
|
if (!IsSortedBlendMode(bucket.BlendMode))
|
|
continue;
|
|
|
|
for (auto& poly : bucket.Polygons)
|
|
{
|
|
auto worldMatrix = Matrix::Lerp(fish.PrevTransform, fish.Transform, GetInterpolationFactor());
|
|
auto center = Vector3::Transform(poly.Centre, worldMatrix);
|
|
float dist = Vector3::Distance(center, view.Camera.WorldPosition);
|
|
|
|
auto object = RendererSortableObject{};
|
|
object.ObjectType = RendererObjectType::MoveableAsStatic;
|
|
object.Centre = center;
|
|
object.Distance = dist;
|
|
object.Bucket = &bucket;
|
|
object.Mesh = &mesh;
|
|
object.Polygon = &poly;
|
|
object.World = worldMatrix;
|
|
object.Room = &_rooms[fish.RoomNumber];
|
|
|
|
view.TransparentObjectsToDraw.push_back(object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool doesActiveFishExist = false;
|
|
for (const auto& fish : FishSwarm)
|
|
{
|
|
if (fish.Life > 0.0f)
|
|
{
|
|
doesActiveFishExist = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (doesActiveFishExist)
|
|
{
|
|
if (rendererPass == RendererPass::GBuffer)
|
|
{
|
|
_context->VSSetShader(_vsGBufferStatics.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psGBuffer.Get(), nullptr, 0);
|
|
}
|
|
else
|
|
{
|
|
_context->VSSetShader(_vsStatics.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psStatics.Get(), nullptr, 0);
|
|
}
|
|
|
|
unsigned int stride = sizeof(Vertex);
|
|
unsigned int offset = 0;
|
|
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
const auto& moveableObj = *_moveableObjects[ID_FISH_EMITTER];
|
|
|
|
_stStatic.LightMode = (int)moveableObj.ObjectMeshes[0]->LightMode;
|
|
|
|
for (const auto& fish : FishSwarm)
|
|
{
|
|
if (fish.Life <= 0.0f)
|
|
continue;
|
|
|
|
const auto& mesh = *GetMesh(Objects[ID_FISH_EMITTER].meshIndex + fish.MeshIndex);
|
|
|
|
_stStatic.World = Matrix::Lerp(fish.PrevTransform, fish.Transform, GetInterpolationFactor());
|
|
_stStatic.Color = Vector4::One;
|
|
_stStatic.AmbientLight = _rooms[fish.RoomNumber].AmbientLight;
|
|
|
|
if (rendererPass != RendererPass::GBuffer)
|
|
BindStaticLights(_rooms[fish.RoomNumber].LightsToDraw);
|
|
|
|
_cbStatic.UpdateData(_stStatic, _context.Get());
|
|
|
|
for (const auto& bucket : mesh.Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
continue;
|
|
|
|
int passCount = (rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest) ? 2 : 1;
|
|
for (int p = 0; p < passCount; p++)
|
|
{
|
|
if (!SetupBlendModeAndAlphaTest(bucket.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);
|
|
|
|
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
|
|
|
|
_numMoveablesDrawCalls++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawBats(RenderView& view, RendererPass rendererPass)
|
|
{
|
|
if (!Objects[ID_BATS_EMITTER].loaded)
|
|
return;
|
|
|
|
auto& mesh = *GetMesh(Objects[ID_BATS_EMITTER].meshIndex + (GlobalCounter & 3));
|
|
|
|
if (rendererPass == RendererPass::CollectTransparentFaces)
|
|
{
|
|
for (const auto& bat : Bats)
|
|
{
|
|
if (!bat.On)
|
|
continue;
|
|
|
|
if (IgnoreMirrorPassForRoom(bat.RoomNumber))
|
|
continue;
|
|
|
|
for (auto& bucket : mesh.Buckets)
|
|
{
|
|
if (!IsSortedBlendMode(bucket.BlendMode))
|
|
continue;
|
|
|
|
for (int p = 0; p < bucket.Polygons.size(); p++)
|
|
{
|
|
auto transformMatrix = Matrix::Lerp(bat.PrevTransform, bat.Transform, GetInterpolationFactor());
|
|
auto centre = Vector3::Transform(bucket.Polygons[p].Centre, transformMatrix);
|
|
float dist = (centre - view.Camera.WorldPosition).Length();
|
|
|
|
auto object = RendererSortableObject{};
|
|
object.ObjectType = RendererObjectType::MoveableAsStatic;
|
|
object.Centre = centre;
|
|
object.Distance = dist;
|
|
object.Bucket = &bucket;
|
|
object.Mesh = &mesh;
|
|
object.Polygon = &bucket.Polygons[p];
|
|
object.World = transformMatrix;
|
|
object.Room = &_rooms[bat.RoomNumber];
|
|
|
|
view.TransparentObjectsToDraw.push_back(object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int batCount = 0;
|
|
for (int i = 0; i < NUM_BATS; i++)
|
|
{
|
|
const auto& bat = Bats[i];
|
|
|
|
if (IgnoreMirrorPassForRoom(bat.RoomNumber))
|
|
continue;
|
|
|
|
if (bat.On)
|
|
{
|
|
auto& room = _rooms[bat.RoomNumber];
|
|
|
|
auto transformMatrix = Matrix::Lerp(bat.PrevTransform, bat.Transform, GetInterpolationFactor());
|
|
|
|
Matrix world = transformMatrix;
|
|
ReflectMatrixOptionally(world);
|
|
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[batCount].World = world;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[batCount].Ambient = room.AmbientLight;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[batCount].Color = Vector4::One;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[batCount].LightMode = (int)mesh.LightMode;
|
|
|
|
if (rendererPass != RendererPass::GBuffer)
|
|
BindInstancedStaticLights(room.LightsToDraw, batCount);
|
|
|
|
batCount++;
|
|
}
|
|
|
|
if (batCount == INSTANCED_STATIC_MESH_BUCKET_SIZE ||
|
|
(i == (NUM_BATS - 1) && batCount > 0))
|
|
{
|
|
if (rendererPass == RendererPass::GBuffer)
|
|
{
|
|
_context->VSSetShader(_vsGBufferInstancedStatics.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psGBuffer.Get(), nullptr, 0);
|
|
}
|
|
else
|
|
{
|
|
_context->VSSetShader(_vsInstancedStaticMeshes.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psInstancedStaticMeshes.Get(), nullptr, 0);
|
|
}
|
|
|
|
unsigned int stride = sizeof(Vertex);
|
|
unsigned int offset = 0;
|
|
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
_cbInstancedStaticMeshBuffer.UpdateData(_stInstancedStaticMeshBuffer, _context.Get());
|
|
|
|
for (auto& bucket : mesh.Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
continue;
|
|
|
|
int passCount = (rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest) ? 2 : 1;
|
|
for (int p = 0; p < passCount; p++)
|
|
{
|
|
if (!SetupBlendModeAndAlphaTest(bucket.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);
|
|
|
|
DrawIndexedInstancedTriangles(bucket.NumIndices, batCount, bucket.StartIndex, 0);
|
|
|
|
_numMoveablesDrawCalls++;
|
|
}
|
|
}
|
|
|
|
batCount = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawScarabs(RenderView& view, RendererPass rendererPass)
|
|
{
|
|
if (!Objects[ID_LITTLE_BEETLE].loaded)
|
|
return;
|
|
|
|
auto& mesh = *GetMesh(Objects[ID_LITTLE_BEETLE].meshIndex + ((Wibble >> 2) % 2));
|
|
|
|
if (rendererPass == RendererPass::CollectTransparentFaces)
|
|
{
|
|
for (const auto& beetle : TEN::Entities::TR4::BeetleSwarm)
|
|
{
|
|
if (!beetle.On)
|
|
continue;
|
|
|
|
if (IgnoreMirrorPassForRoom(beetle.RoomNumber))
|
|
continue;
|
|
|
|
auto transformMatrix = Matrix::Lerp(beetle.PrevTransform, beetle.Transform, GetInterpolationFactor());
|
|
|
|
for (auto& bucket : mesh.Buckets)
|
|
{
|
|
if (!IsSortedBlendMode(bucket.BlendMode))
|
|
continue;
|
|
|
|
for (int p = 0; p < bucket.Polygons.size(); p++)
|
|
{
|
|
auto centre = Vector3::Transform(bucket.Polygons[p].Centre, transformMatrix);
|
|
float dist = (centre - view.Camera.WorldPosition).Length();
|
|
|
|
auto object = RendererSortableObject{};
|
|
object.ObjectType = RendererObjectType::MoveableAsStatic;
|
|
object.Centre = centre;
|
|
object.Distance = dist;
|
|
object.Bucket = &bucket;
|
|
object.Mesh = &mesh;
|
|
object.Polygon = &bucket.Polygons[p];
|
|
object.World = transformMatrix;
|
|
object.Room = &_rooms[beetle.RoomNumber];
|
|
|
|
view.TransparentObjectsToDraw.push_back(object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int beetleCount = 0;
|
|
for (int i = 0; i < TEN::Entities::TR4::NUM_BEETLES; i++)
|
|
{
|
|
const auto& beetle = TEN::Entities::TR4::BeetleSwarm[i];
|
|
|
|
if (!beetle.On)
|
|
continue;
|
|
|
|
if (IgnoreMirrorPassForRoom(beetle.RoomNumber))
|
|
continue;
|
|
|
|
auto& room = _rooms[beetle.RoomNumber];
|
|
|
|
auto transformMatrix = Matrix::Lerp(beetle.PrevTransform, beetle.Transform, GetInterpolationFactor());
|
|
|
|
Matrix world = transformMatrix;
|
|
ReflectMatrixOptionally(world);
|
|
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].World = world;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].Ambient = room.AmbientLight;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].Color = Vector4::One;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].LightMode = (int)mesh.LightMode;
|
|
|
|
if (rendererPass != RendererPass::GBuffer)
|
|
{
|
|
auto lights = std::vector<RendererLight*>{};
|
|
for (int i = 0; i < std::min((int)room.LightsToDraw.size(), MAX_LIGHTS_PER_ITEM); i++)
|
|
lights.push_back(room.LightsToDraw[i]);
|
|
|
|
BindInstancedStaticLights(lights, beetleCount);
|
|
}
|
|
|
|
beetleCount++;
|
|
|
|
if (beetleCount == INSTANCED_STATIC_MESH_BUCKET_SIZE ||
|
|
(i == TEN::Entities::TR4::NUM_BEETLES - 1 && beetleCount > 0))
|
|
{
|
|
if (rendererPass == RendererPass::GBuffer)
|
|
{
|
|
_context->VSSetShader(_vsGBufferInstancedStatics.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psGBuffer.Get(), nullptr, 0);
|
|
}
|
|
else
|
|
{
|
|
_context->VSSetShader(_vsInstancedStaticMeshes.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psInstancedStaticMeshes.Get(), nullptr, 0);
|
|
}
|
|
|
|
unsigned int stride = sizeof(Vertex);
|
|
unsigned int offset = 0;
|
|
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
_cbInstancedStaticMeshBuffer.UpdateData(_stInstancedStaticMeshBuffer, _context.Get());
|
|
|
|
for (auto& bucket : mesh.Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
continue;
|
|
|
|
int passCount = (rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest) ? 2 : 1;
|
|
for (int p = 0; p < passCount; p++)
|
|
{
|
|
if (!SetupBlendModeAndAlphaTest(bucket.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);
|
|
|
|
DrawIndexedInstancedTriangles(bucket.NumIndices, beetleCount, bucket.StartIndex, 0);
|
|
|
|
_numInstancedStaticsDrawCalls++;
|
|
}
|
|
}
|
|
|
|
beetleCount = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawLocusts(RenderView& view, RendererPass rendererPass)
|
|
{
|
|
if (!Objects[ID_LOCUSTS].loaded)
|
|
return;
|
|
|
|
if (rendererPass == RendererPass::CollectTransparentFaces)
|
|
{
|
|
auto* obj = &Objects[ID_LOCUSTS];
|
|
auto& moveableObj = *_moveableObjects[ID_LOCUSTS];
|
|
|
|
for (const auto& locust : TEN::Entities::TR4::Locusts)
|
|
{
|
|
if (!locust.on)
|
|
continue;
|
|
|
|
if (IgnoreMirrorPassForRoom(locust.roomNumber))
|
|
continue;
|
|
|
|
auto& mesh = *GetMesh(Objects[ID_LOCUSTS].meshIndex + (-locust.counter & 3));
|
|
|
|
for (auto& bucket : mesh.Buckets)
|
|
{
|
|
if (!IsSortedBlendMode(bucket.BlendMode))
|
|
continue;
|
|
|
|
for (int p = 0; p < bucket.Polygons.size(); p++)
|
|
{
|
|
auto transformMatrix = Matrix::Lerp(locust.PrevTransform, locust.Transform, GetInterpolationFactor());
|
|
auto centre = Vector3::Transform(bucket.Polygons[p].Centre, transformMatrix);
|
|
float dist = (centre - view.Camera.WorldPosition).Length();
|
|
|
|
auto object = RendererSortableObject{};
|
|
object.ObjectType = RendererObjectType::MoveableAsStatic;
|
|
object.Centre = centre;
|
|
object.Distance = dist;
|
|
object.Bucket = &bucket;
|
|
object.Mesh = &mesh;
|
|
object.Polygon = &bucket.Polygons[p];
|
|
object.World = transformMatrix;
|
|
object.Room = &_rooms[locust.roomNumber];
|
|
|
|
view.TransparentObjectsToDraw.push_back(object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int activeLocustsExist = false;
|
|
for (const auto& locust : TEN::Entities::TR4::Locusts)
|
|
{
|
|
if (!locust.on)
|
|
continue;
|
|
|
|
if (IgnoreMirrorPassForRoom(locust.roomNumber))
|
|
continue;
|
|
|
|
activeLocustsExist = true;
|
|
break;
|
|
}
|
|
|
|
if (activeLocustsExist)
|
|
{
|
|
if (rendererPass == RendererPass::GBuffer)
|
|
{
|
|
_context->VSSetShader(_vsGBufferStatics.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psGBuffer.Get(), nullptr, 0);
|
|
}
|
|
else
|
|
{
|
|
_context->VSSetShader(_vsStatics.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psStatics.Get(), nullptr, 0);
|
|
}
|
|
|
|
unsigned int stride = sizeof(Vertex);
|
|
unsigned int offset = 0;
|
|
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
auto* obj = &Objects[ID_LOCUSTS];
|
|
auto& moveableObj = *_moveableObjects[ID_LOCUSTS];
|
|
|
|
_stStatic.LightMode = (int)moveableObj.ObjectMeshes[0]->LightMode;
|
|
|
|
for (const auto& locust : TEN::Entities::TR4::Locusts)
|
|
{
|
|
if (!locust.on)
|
|
continue;
|
|
|
|
auto& mesh = *GetMesh(Objects[ID_LOCUSTS].meshIndex + (-locust.counter & 3));
|
|
|
|
Matrix world = Matrix::Lerp(locust.PrevTransform, locust.Transform, GetInterpolationFactor());
|
|
ReflectMatrixOptionally(world);
|
|
|
|
_stStatic.World = world;
|
|
_stStatic.Color = Vector4::One;
|
|
_stStatic.AmbientLight = _rooms[locust.roomNumber].AmbientLight;
|
|
_cbStatic.UpdateData(_stStatic, _context.Get());
|
|
|
|
for (auto& bucket : mesh.Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
continue;
|
|
|
|
int passCount = (rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest) ? 2 : 1;
|
|
for (int p = 0; p < passCount; p++)
|
|
{
|
|
if (!SetupBlendModeAndAlphaTest(bucket.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);
|
|
|
|
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
|
|
|
|
_numMoveablesDrawCalls++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawLines3D(RenderView& view)
|
|
{
|
|
SetBlendMode(BlendMode::Additive);
|
|
SetCullMode(CullMode::None);
|
|
|
|
_context->VSSetShader(_vsSolid.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psSolid.Get(), nullptr, 0);
|
|
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST);
|
|
|
|
_primitiveBatch->Begin();
|
|
|
|
for (const auto& line : _lines3DToDraw)
|
|
{
|
|
auto vertex0 = Vertex{};
|
|
vertex0.Position = line.Origin;
|
|
vertex0.Color = line.Color;
|
|
|
|
auto vertex1 = Vertex{};
|
|
vertex1.Position = line.Target;
|
|
vertex1.Color = line.Color;
|
|
|
|
_primitiveBatch->DrawLine(vertex0, vertex1);
|
|
|
|
_numLinesDrawCalls++;
|
|
_numDrawCalls++;
|
|
}
|
|
|
|
_primitiveBatch->End();
|
|
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
}
|
|
|
|
void Renderer::DrawTriangles3D(RenderView& view)
|
|
{
|
|
SetBlendMode(BlendMode::Additive);
|
|
SetCullMode(CullMode::None);
|
|
|
|
_context->VSSetShader(_vsSolid.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psSolid.Get(), nullptr, 0);
|
|
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
_context->IASetInputLayout(_inputLayout.Get());
|
|
|
|
_primitiveBatch->Begin();
|
|
|
|
for (const auto& tri : _triangles3DToDraw)
|
|
{
|
|
auto rVertices = std::vector<Vertex>{};
|
|
rVertices.reserve(tri.Vertices.size());
|
|
|
|
for (const auto& vertex : tri.Vertices)
|
|
{
|
|
auto rVertex = Vertex{};
|
|
rVertex.Position = vertex;
|
|
rVertex.Color = tri.Color;
|
|
|
|
rVertices.push_back(rVertex);
|
|
}
|
|
|
|
_primitiveBatch->DrawTriangle(rVertices[0], rVertices[1], rVertices[2]);
|
|
|
|
_numLinesDrawCalls++;
|
|
_numDrawCalls++;
|
|
}
|
|
|
|
_primitiveBatch->End();
|
|
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
}
|
|
|
|
void Renderer::AddDebugLine(const Vector3& origin, const Vector3& target, const Color& color, RendererDebugPage page)
|
|
{
|
|
if (_isLocked)
|
|
return;
|
|
|
|
if (!DebugMode || (_debugPage != page && page != RendererDebugPage::None))
|
|
return;
|
|
|
|
auto line = RendererLine3D{};
|
|
line.Origin = origin;
|
|
line.Target = target;
|
|
line.Color = color;
|
|
|
|
_lines3DToDraw.push_back(line);
|
|
}
|
|
|
|
void Renderer::AddDebugTriangle(const Vector3& vertex0, const Vector3& vertex1, const Vector3& vertex2, const Color& color, RendererDebugPage page)
|
|
{
|
|
if (_isLocked)
|
|
return;
|
|
|
|
if (!DebugMode || (_debugPage != page && page != RendererDebugPage::None))
|
|
return;
|
|
|
|
auto tri = RendererTriangle3D{};
|
|
tri.Vertices = { vertex0, vertex1, vertex2 };
|
|
tri.Color = color;
|
|
|
|
_triangles3DToDraw.push_back(tri);
|
|
}
|
|
|
|
void Renderer::AddDebugTarget(const Vector3& center, const Quaternion& orient, float radius, const Color& color, RendererDebugPage page)
|
|
{
|
|
if (_isLocked)
|
|
return;
|
|
|
|
if (!DebugMode || (_debugPage != page && page != RendererDebugPage::None))
|
|
return;
|
|
|
|
auto rotMatrix = Matrix::CreateFromQuaternion(orient);
|
|
|
|
auto origin0 = center + Vector3::Transform(Vector3(radius, 0.0f, 0.0f), rotMatrix);
|
|
auto target0 = center + Vector3::Transform(Vector3(-radius, 0.0f, 0.0f), rotMatrix);
|
|
AddDebugLine(origin0, target0, color);
|
|
|
|
auto origin1 = center + Vector3::Transform(Vector3(0.0f, radius, 0.0f), rotMatrix);
|
|
auto target1 = center + Vector3::Transform(Vector3(0.0f, -radius, 0.0f), rotMatrix);
|
|
AddDebugLine(origin1, target1, color);
|
|
|
|
auto origin2 = center + Vector3::Transform(Vector3(0.0f, 0.0f, radius), rotMatrix);
|
|
auto target2 = center + Vector3::Transform(Vector3(0.0f, 0.0f, -radius), rotMatrix);
|
|
AddDebugLine(origin2, target2, color);
|
|
}
|
|
|
|
void Renderer::AddDebugBox(const std::array<Vector3, BOX_VERTEX_COUNT>& corners, const Color& color, RendererDebugPage page, bool isWireframe)
|
|
{
|
|
if (_isLocked)
|
|
return;
|
|
|
|
if (!DebugMode || (_debugPage != page && page != RendererDebugPage::None))
|
|
return;
|
|
|
|
// Construct box.
|
|
if (isWireframe)
|
|
{
|
|
for (int i = 0; i < BOX_EDGE_COUNT; i++)
|
|
{
|
|
switch (i)
|
|
{
|
|
case 0:
|
|
AddDebugLine(corners[0], corners[1], color, page);
|
|
break;
|
|
case 1:
|
|
AddDebugLine(corners[1], corners[2], color, page);
|
|
break;
|
|
case 2:
|
|
AddDebugLine(corners[2], corners[3], color, page);
|
|
break;
|
|
case 3:
|
|
AddDebugLine(corners[3], corners[0], color, page);
|
|
break;
|
|
|
|
case 4:
|
|
AddDebugLine(corners[4], corners[5], color, page);
|
|
break;
|
|
case 5:
|
|
AddDebugLine(corners[5], corners[6], color, page);
|
|
break;
|
|
case 6:
|
|
AddDebugLine(corners[6], corners[7], color, page);
|
|
break;
|
|
case 7:
|
|
AddDebugLine(corners[7], corners[4], color, page);
|
|
break;
|
|
|
|
case 8:
|
|
AddDebugLine(corners[0], corners[4], color, page);
|
|
break;
|
|
case 9:
|
|
AddDebugLine(corners[1], corners[5], color, page);
|
|
break;
|
|
case 10:
|
|
AddDebugLine(corners[2], corners[6], color, page);
|
|
break;
|
|
case 11:
|
|
AddDebugLine(corners[3], corners[7], color, page);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < BOX_FACE_COUNT; i++)
|
|
{
|
|
switch (i)
|
|
{
|
|
case 0:
|
|
AddDebugTriangle(corners[0], corners[1], corners[2], color, page);
|
|
AddDebugTriangle(corners[0], corners[2], corners[3], color, page);
|
|
break;
|
|
|
|
case 1:
|
|
AddDebugTriangle(corners[4], corners[5], corners[6], color, page);
|
|
AddDebugTriangle(corners[4], corners[6], corners[7], color, page);
|
|
break;
|
|
|
|
case 2:
|
|
AddDebugTriangle(corners[0], corners[1], corners[4], color, page);
|
|
AddDebugTriangle(corners[1], corners[4], corners[5], color, page);
|
|
break;
|
|
|
|
case 3:
|
|
AddDebugTriangle(corners[1], corners[2], corners[5], color, page);
|
|
AddDebugTriangle(corners[2], corners[5], corners[6], color, page);
|
|
break;
|
|
|
|
case 4:
|
|
AddDebugTriangle(corners[2], corners[3], corners[6], color, page);
|
|
AddDebugTriangle(corners[3], corners[6], corners[7], color, page);
|
|
break;
|
|
|
|
case 5:
|
|
AddDebugTriangle(corners[0], corners[3], corners[4], color, page);
|
|
AddDebugTriangle(corners[3], corners[4], corners[7], color, page);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::AddDebugBox(const Vector3& min, const Vector3& max, const Color& color, RendererDebugPage page, bool isWireframe)
|
|
{
|
|
if (_isLocked)
|
|
return;
|
|
|
|
if (!DebugMode || (_debugPage != page && page != RendererDebugPage::None))
|
|
return;
|
|
|
|
auto box = BoundingBox((max + min) / 2, (max - min) / 2);
|
|
AddDebugBox(box, color, page, isWireframe);
|
|
}
|
|
|
|
void Renderer::AddDebugBox(const BoundingOrientedBox& box, const Color& color, RendererDebugPage page, bool isWireframe)
|
|
{
|
|
if (_isLocked)
|
|
return;
|
|
|
|
if (!DebugMode || (_debugPage != page && page != RendererDebugPage::None))
|
|
return;
|
|
|
|
auto corners = std::array<Vector3, BOX_VERTEX_COUNT>{};
|
|
box.GetCorners(corners.data());
|
|
|
|
AddDebugBox(corners, color, page, isWireframe);
|
|
}
|
|
|
|
void Renderer::AddDebugBox(const BoundingBox& box, const Color& color, RendererDebugPage page, bool isWireframe)
|
|
{
|
|
if (_isLocked)
|
|
return;
|
|
|
|
if (!DebugMode || (_debugPage != page && page != RendererDebugPage::None))
|
|
return;
|
|
|
|
auto corners = std::array<Vector3, BOX_VERTEX_COUNT>{};
|
|
box.GetCorners(corners.data());
|
|
|
|
AddDebugBox(corners, color, page, isWireframe);
|
|
}
|
|
|
|
void Renderer::AddDebugCone(const Vector3& center, const Quaternion& orient, float radius, float length, const Vector4& color, RendererDebugPage page, bool isWireframe)
|
|
{
|
|
constexpr auto SUBDIVISION_COUNT = 32;
|
|
|
|
if (_isLocked)
|
|
return;
|
|
|
|
if (!DebugMode || (_debugPage != page && page != RendererDebugPage::None))
|
|
return;
|
|
|
|
auto rotMatrix = Matrix::CreateFromQuaternion(orient);
|
|
|
|
auto baseVertices = std::vector<Vector3>{};
|
|
baseVertices.reserve(SUBDIVISION_COUNT);
|
|
float angle = 0.0f;
|
|
|
|
// Calculate base circle vertices.
|
|
for (int i = 0; i <= SUBDIVISION_COUNT; i++)
|
|
{
|
|
float sinAngle = sin(angle);
|
|
float cosAngle = cos(angle);
|
|
|
|
auto vertex = center + Vector3::Transform(Vector3(radius * sinAngle, radius * cosAngle, 0.0f), rotMatrix);
|
|
baseVertices.push_back(vertex);
|
|
|
|
angle += PI_MUL_2 / SUBDIVISION_COUNT;
|
|
}
|
|
|
|
auto dir = Geometry::ConvertQuatToDirection(orient);
|
|
auto topCenter = Geometry::TranslatePoint(center, dir, length);
|
|
|
|
// Construct cone.
|
|
for (int i = 0; i < baseVertices.size(); i++)
|
|
{
|
|
const auto& vertex0 = baseVertices[i];
|
|
const auto& vertex1 = baseVertices[(i == (baseVertices.size() - 1)) ? 0 : (i + 1)];
|
|
|
|
if (isWireframe)
|
|
{
|
|
AddDebugLine(vertex0, vertex1, color);
|
|
|
|
if ((i % (SUBDIVISION_COUNT / 8)) == 0)
|
|
AddDebugLine(vertex0, topCenter, color);
|
|
}
|
|
else
|
|
{
|
|
AddDebugTriangle(vertex0, vertex1, center, color);
|
|
AddDebugTriangle(vertex0, vertex1, topCenter, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::AddDebugCylinder(const Vector3& center, const Quaternion& orient, float radius, float length, const Color& color, RendererDebugPage page, bool isWireframe)
|
|
{
|
|
constexpr auto SUBDIVISION_COUNT = 32;
|
|
|
|
if (_isLocked)
|
|
return;
|
|
|
|
if (!DebugMode || (_debugPage != page && page != RendererDebugPage::None))
|
|
return;
|
|
|
|
auto rotMatrix = Matrix::CreateFromQuaternion(orient);
|
|
|
|
auto baseVertices = std::vector<Vector3>{};
|
|
baseVertices.reserve(SUBDIVISION_COUNT);
|
|
float angle = 0.0f;
|
|
|
|
// Calculate base circle vertices.
|
|
for (int i = 0; i <= SUBDIVISION_COUNT; i++)
|
|
{
|
|
float sinAngle = sin(angle);
|
|
float cosAngle = cos(angle);
|
|
|
|
auto vertex = center + Vector3::Transform(Vector3(radius * sinAngle, radius * cosAngle, 0.0f), rotMatrix);
|
|
baseVertices.push_back(vertex);
|
|
|
|
angle += PI_MUL_2 / SUBDIVISION_COUNT;
|
|
}
|
|
|
|
auto dir = Geometry::ConvertQuatToDirection(orient);
|
|
|
|
// Calculate top circle vertices.
|
|
auto topVertices = baseVertices;
|
|
for (auto& vertex : topVertices)
|
|
vertex = Geometry::TranslatePoint(vertex, dir, length);
|
|
|
|
// Construct cylinder.
|
|
auto topCenter = Geometry::TranslatePoint(center, dir, length);
|
|
for (int i = 0; i < baseVertices.size(); i++)
|
|
{
|
|
const auto& baseVertex0 = baseVertices[i];
|
|
const auto& baseVertex1 = baseVertices[(i == (baseVertices.size() - 1)) ? 0 : (i + 1)];
|
|
|
|
const auto& topVertex0 = topVertices[i];
|
|
const auto& topVertex1 = topVertices[(i == (topVertices.size() - 1)) ? 0 : (i + 1)];
|
|
|
|
if (isWireframe)
|
|
{
|
|
AddDebugLine(baseVertex0, baseVertex1, color);
|
|
AddDebugLine(topVertex0, topVertex1, color);
|
|
|
|
if ((i % (SUBDIVISION_COUNT / 8)) == 0)
|
|
AddDebugLine(baseVertex0, topVertex0, color);
|
|
}
|
|
else
|
|
{
|
|
AddDebugTriangle(baseVertex0, baseVertex1, center, color);
|
|
AddDebugTriangle(topVertex0, topVertex1, topCenter, color);
|
|
AddDebugTriangle(baseVertex0, baseVertex1, topVertex1, color);
|
|
AddDebugTriangle(baseVertex0, topVertex0, topVertex1, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::AddDebugSphere(const Vector3& center, float radius, const Color& color, RendererDebugPage page, bool isWireframe)
|
|
{
|
|
constexpr auto AXIS_COUNT = 3;
|
|
constexpr auto SUBDIVISION_COUNT = 16;
|
|
constexpr auto STEP_ANGLE = PI / (SUBDIVISION_COUNT / 4);
|
|
|
|
if (_isLocked)
|
|
return;
|
|
|
|
if (!DebugMode || (_debugPage != page && page != RendererDebugPage::None))
|
|
return;
|
|
|
|
// Construct sphere.
|
|
if (isWireframe)
|
|
{
|
|
auto prevVertices = std::array<Vector3, AXIS_COUNT>{};
|
|
for (int i = 0; i < (SUBDIVISION_COUNT / 4); i++)
|
|
{
|
|
float x = sin(STEP_ANGLE * i) * radius;
|
|
float z = cos(STEP_ANGLE * i) * radius;
|
|
|
|
float angle = 0.0f;
|
|
|
|
for (int j = 0; j < SUBDIVISION_COUNT; j++)
|
|
{
|
|
auto vertices = std::array<Vector3, AXIS_COUNT>
|
|
{
|
|
center + Vector3(sin(angle) * abs(x), z, cos(angle) * abs(x)),
|
|
center + Vector3(cos(angle) * abs(x), sin(angle) * abs(x), z),
|
|
center + Vector3(z, sin(angle) * abs(x), cos(angle) * abs(x))
|
|
};
|
|
|
|
if (j > 0)
|
|
{
|
|
for (int k = 0; k < vertices.size(); k++)
|
|
AddDebugLine(prevVertices[k], vertices[k], color);
|
|
}
|
|
|
|
prevVertices = vertices;
|
|
angle += PI_MUL_2 / (SUBDIVISION_COUNT - 1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto vertices = std::vector<Vector3>{};
|
|
for (int i = 0; i <= SUBDIVISION_COUNT; i++)
|
|
{
|
|
float pitch = ((float)i / SUBDIVISION_COUNT) * PI;
|
|
float y = cos(pitch) * radius;
|
|
float radiusAtY = sin(pitch) * radius;
|
|
|
|
for (int j = 0; j < SUBDIVISION_COUNT; j++)
|
|
{
|
|
float theta = ((float)j / SUBDIVISION_COUNT) * PI_MUL_2;
|
|
float x = cos(theta) * radiusAtY;
|
|
float z = sin(theta) * radiusAtY;
|
|
|
|
auto vertex = center + Vector3(x, y, z);
|
|
vertices.push_back(vertex);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < SUBDIVISION_COUNT; i++)
|
|
{
|
|
for (int j = 0; j < SUBDIVISION_COUNT; j++)
|
|
{
|
|
int index0 = i * SUBDIVISION_COUNT + j;
|
|
int index1 = ((i + 1) * SUBDIVISION_COUNT) + j;
|
|
int index2 = ((i + 1) * SUBDIVISION_COUNT) + (j + 1) % SUBDIVISION_COUNT;
|
|
int index3 = (i * SUBDIVISION_COUNT) + ((j + 1) % SUBDIVISION_COUNT);
|
|
|
|
AddDebugTriangle(vertices[index0], vertices[index1], vertices[index2], color);
|
|
AddDebugTriangle(vertices[index0], vertices[index2], vertices[index3], color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::AddDebugSphere(const BoundingSphere& sphere, const Color& color, RendererDebugPage page, bool isWireframe)
|
|
{
|
|
if (_isLocked)
|
|
return;
|
|
|
|
if (!DebugMode || (_debugPage != page && page != RendererDebugPage::None))
|
|
return;
|
|
|
|
AddDebugSphere(sphere.Center, sphere.Radius, color, page, isWireframe);
|
|
}
|
|
|
|
void Renderer::AddDynamicSpotLight(const Vector3& pos, const Vector3& dir, float radius, float falloff, float distance, const Color& color, bool castShadows, int hash)
|
|
{
|
|
if (_isLocked || g_GameFlow->LastFreezeMode != FreezeMode::None)
|
|
return;
|
|
|
|
RendererLight dynamicLight = {};
|
|
|
|
dynamicLight.Color = Vector3(color.x, color.y, color.z) * 2.0f;
|
|
if (falloff < 8)
|
|
dynamicLight.Color *= (falloff / 8.0f);
|
|
|
|
// Calculate outer cone angle (degrees) based on radius at the cone's end distance.
|
|
float outerConeAngle = atan(radius / distance) * (180.0f / PI);
|
|
float innerConeAngle = atan((radius - falloff) / distance) * (180.0f / PI);
|
|
float innerDistance = std::max(0.0f, distance - distance * (falloff / radius));
|
|
|
|
// Normalize direction for safety.
|
|
auto normalizedDirection = dir;
|
|
normalizedDirection.Normalize();
|
|
|
|
dynamicLight.RoomNumber = NO_VALUE;
|
|
dynamicLight.Intensity = 1.0f;
|
|
dynamicLight.Position = pos;
|
|
dynamicLight.Direction = normalizedDirection;
|
|
dynamicLight.In = innerDistance;
|
|
dynamicLight.Out = distance;
|
|
dynamicLight.InRange = innerConeAngle > 0.0f ? innerConeAngle : 0.5f;
|
|
dynamicLight.OutRange = outerConeAngle;
|
|
dynamicLight.Type = LightType::Spot;
|
|
dynamicLight.CastShadows = castShadows;
|
|
dynamicLight.BoundingSphere = BoundingSphere(pos, distance);
|
|
dynamicLight.Luma = Luma(dynamicLight.Color);
|
|
dynamicLight.Hash = hash;
|
|
|
|
StoreInterpolatedDynamicLightData(dynamicLight);
|
|
_dynamicLights[_dynamicLightList].push_back(dynamicLight);
|
|
|
|
for (auto& mirror : g_Level.Mirrors)
|
|
{
|
|
if (Camera.pos.RoomNumber == mirror.RealRoom && IsPointInRoom(pos, mirror.RealRoom))
|
|
{
|
|
dynamicLight.Position = Vector3::Transform(pos, mirror.ReflectionMatrix);
|
|
dynamicLight.Direction = Vector3::Transform(dynamicLight.Direction, mirror.ReflectionMatrix);
|
|
dynamicLight.Hash = 0;
|
|
|
|
_dynamicLights[_dynamicLightList].push_back(dynamicLight);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::AddDynamicPointLight(const Vector3& pos, float radius, const Color& color, bool castShadows, int hash)
|
|
{
|
|
if (_isLocked || g_GameFlow->LastFreezeMode != FreezeMode::None)
|
|
return;
|
|
|
|
RendererLight dynamicLight = {};
|
|
|
|
dynamicLight.Color = Vector3(color.x, color.y, color.z) * 2.0f;
|
|
if (radius < BLOCK(2))
|
|
dynamicLight.Color *= (radius / BLOCK(2));
|
|
|
|
dynamicLight.RoomNumber = NO_VALUE;
|
|
dynamicLight.Intensity = 1.0f;
|
|
dynamicLight.Position = pos;
|
|
dynamicLight.In = 1.0f;
|
|
dynamicLight.Out = radius;
|
|
dynamicLight.Type = LightType::Point;
|
|
dynamicLight.CastShadows = castShadows;
|
|
dynamicLight.BoundingSphere = BoundingSphere(pos, radius);
|
|
dynamicLight.Luma = Luma(dynamicLight.Color);
|
|
dynamicLight.Hash = hash;
|
|
|
|
StoreInterpolatedDynamicLightData(dynamicLight);
|
|
_dynamicLights[_dynamicLightList].push_back(dynamicLight);
|
|
|
|
for (auto& mirror : g_Level.Mirrors)
|
|
{
|
|
if (Camera.pos.RoomNumber == mirror.RealRoom && IsPointInRoom(pos, mirror.RealRoom))
|
|
{
|
|
dynamicLight.Position = Vector3::Transform(pos, mirror.ReflectionMatrix);
|
|
dynamicLight.Hash = 0;
|
|
|
|
_dynamicLights[_dynamicLightList].push_back(dynamicLight);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::StoreInterpolatedDynamicLightData(RendererLight& light)
|
|
{
|
|
// Hash is not provided, do not search for same light in old buffer.
|
|
if (light.Hash == 0)
|
|
return;
|
|
|
|
// Determine the previous buffer index.
|
|
const auto& previousList = _dynamicLights[1 - _dynamicLightList];
|
|
|
|
// Find a light in the previous buffer with the same Hash.
|
|
auto it = std::find_if(previousList.begin(), previousList.end(),
|
|
[&light](const auto& prevLight)
|
|
{
|
|
return prevLight.Hash == light.Hash;
|
|
});
|
|
|
|
if (it == previousList.end())
|
|
return;
|
|
|
|
// If a matching light is found, copy its data.
|
|
const auto& prevLight = *it;
|
|
light.PrevPosition = prevLight.Position;
|
|
light.PrevDirection = prevLight.Direction;
|
|
}
|
|
|
|
void Renderer::PrepareScene()
|
|
{
|
|
if (g_GameFlow->CurrentFreezeMode == FreezeMode::None)
|
|
{
|
|
_dynamicLightList ^= 1;
|
|
_dynamicLights[_dynamicLightList].clear();
|
|
}
|
|
|
|
_lines2DToDraw.clear();
|
|
_lines3DToDraw.clear();
|
|
_triangles3DToDraw.clear();
|
|
_stringsToDraw.clear();
|
|
|
|
_currentCausticsFrame++;
|
|
_currentCausticsFrame %= 32;
|
|
|
|
constexpr auto BLINK_VALUE_MAX = 1.0f;
|
|
constexpr auto BLINK_VALUE_MIN = 0.1f;
|
|
constexpr auto BLINK_TIME_STEP = 0.2f;
|
|
|
|
// Calculate blink increment based on sine wave.
|
|
float blink = ((sin(_blinkTime) + BLINK_VALUE_MAX) * 0.5f) + BLINK_VALUE_MIN;
|
|
_blinkColorValue = Vector4(blink, blink, blink, 1.0f);
|
|
|
|
// Update blink time.
|
|
_blinkTime += BLINK_TIME_STEP;
|
|
if (_blinkTime > PI_MUL_2)
|
|
_blinkTime -= PI_MUL_2;
|
|
|
|
_oldGameCamera = _currentGameCamera;
|
|
Camera.DisableInterpolation = false;
|
|
|
|
_isLocked = false;
|
|
}
|
|
|
|
void Renderer::ClearScene()
|
|
{
|
|
_gameCamera.Clear();
|
|
|
|
ResetItems();
|
|
ClearShadowMap();
|
|
}
|
|
|
|
void Renderer::RenderScene(RenderTarget2D* renderTarget, RenderView& view, SceneRenderMode renderMode)
|
|
{
|
|
using ns = std::chrono::nanoseconds;
|
|
using get_time = std::chrono::steady_clock;
|
|
|
|
ResetDebugVariables();
|
|
|
|
_doingFullscreenPass = false;
|
|
|
|
auto& level = *g_GameFlow->GetLevel(CurrentLevel);
|
|
|
|
// Prepare scene to draw.
|
|
auto time1 = std::chrono::high_resolution_clock::now();
|
|
CollectRooms(view, false);
|
|
auto time = std::chrono::high_resolution_clock::now();
|
|
_timeRoomsCollector = (std::chrono::duration_cast<ns>(time - time1)).count() / 1000000;
|
|
time1 = time;
|
|
|
|
UpdateLaraAnimations(false);
|
|
UpdateItemAnimations(view);
|
|
|
|
_stBlending.AlphaTest = -1;
|
|
_stBlending.AlphaThreshold = -1;
|
|
|
|
CollectLightsForCamera();
|
|
RenderItemShadows(view);
|
|
|
|
// Prepare all sprites for later.
|
|
PrepareFires(view);
|
|
PrepareSmokes(view);
|
|
PrepareSmokeParticles(view);
|
|
PrepareSimpleParticles(view);
|
|
PrepareSparkParticles(view);
|
|
PrepareExplosionParticles(view);
|
|
PrepareFootprints(view);
|
|
PrepareBlood(view);
|
|
PrepareWeatherParticles(view);
|
|
PrepareParticles(view);
|
|
PrepareBubbles(view);
|
|
PrepareDrips(view);
|
|
PrepareRipples(view);
|
|
PrepareUnderwaterBloodParticles(view);
|
|
PrepareSplashes(view);
|
|
PrepareShockwaves(view);
|
|
PrepareElectricity(view);
|
|
PrepareHelicalLasers(view);
|
|
PrepareRopes(view);
|
|
PrepareStreamers(view);
|
|
PrepareLaserBarriers(view);
|
|
PrepareSingleLaserBeam(view);
|
|
|
|
// Sprites grouped in buckets for instancing. Non-commutative sprites are collected at a later stage.
|
|
SortAndPrepareSprites(view);
|
|
|
|
auto time2 = std::chrono::high_resolution_clock::now();
|
|
_timeUpdate = (std::chrono::duration_cast<ns>(time2 - time1)).count() / 1000000;
|
|
time1 = time2;
|
|
|
|
// Reset GPU state.
|
|
SetBlendMode(BlendMode::Opaque, true);
|
|
SetDepthState(DepthState::Write, true);
|
|
SetCullMode(CullMode::CounterClockwise, true);
|
|
|
|
// Bind constant buffers.
|
|
BindConstantBufferVS(ConstantBufferRegister::Camera, _cbCameraMatrices.get());
|
|
BindConstantBufferVS(ConstantBufferRegister::Item, _cbItem.get());
|
|
BindConstantBufferVS(ConstantBufferRegister::InstancedStatics, _cbInstancedStaticMeshBuffer.get());
|
|
BindConstantBufferVS(ConstantBufferRegister::ShadowLight, _cbShadowMap.get());
|
|
BindConstantBufferVS(ConstantBufferRegister::Room, _cbRoom.get());
|
|
BindConstantBufferVS(ConstantBufferRegister::AnimatedTextures, _cbAnimated.get());
|
|
BindConstantBufferVS(ConstantBufferRegister::Static, _cbStatic.get());
|
|
BindConstantBufferVS(ConstantBufferRegister::Sprite, _cbSprite.get());
|
|
BindConstantBufferVS(ConstantBufferRegister::Blending, _cbBlending.get());
|
|
BindConstantBufferVS(ConstantBufferRegister::InstancedSprites, _cbInstancedSpriteBuffer.get());
|
|
BindConstantBufferVS(ConstantBufferRegister::PostProcess, _cbPostProcessBuffer.get());
|
|
|
|
BindConstantBufferPS(ConstantBufferRegister::Camera, _cbCameraMatrices.get());
|
|
BindConstantBufferPS(ConstantBufferRegister::Item, _cbItem.get());
|
|
BindConstantBufferPS(ConstantBufferRegister::InstancedStatics, _cbInstancedStaticMeshBuffer.get());
|
|
BindConstantBufferPS(ConstantBufferRegister::ShadowLight, _cbShadowMap.get());
|
|
BindConstantBufferPS(ConstantBufferRegister::Room, _cbRoom.get());
|
|
BindConstantBufferPS(ConstantBufferRegister::AnimatedTextures, _cbAnimated.get());
|
|
BindConstantBufferPS(ConstantBufferRegister::Static, _cbStatic.get());
|
|
BindConstantBufferPS(ConstantBufferRegister::Sprite, _cbSprite.get());
|
|
BindConstantBufferPS(ConstantBufferRegister::Blending, _cbBlending.get());
|
|
BindConstantBufferPS(ConstantBufferRegister::InstancedSprites, _cbInstancedSpriteBuffer.get());
|
|
BindConstantBufferPS(ConstantBufferRegister::PostProcess, _cbPostProcessBuffer.get());
|
|
|
|
// Set up vertex parameters.
|
|
_context->IASetInputLayout(_inputLayout.Get());
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
|
|
// TODO: Needs improvements before enabling it.
|
|
// RenderSimpleSceneToParaboloid(&_roomAmbientMapFront, LaraItem->Pose.Position.ToVector3(), 1);
|
|
// RenderSimpleSceneToParaboloid(&_roomAmbientMapBack, LaraItem->Pose.Position.ToVector3(), -1);
|
|
|
|
// Bind and clear render target.
|
|
_context->ClearRenderTargetView(_renderTarget.RenderTargetView.Get(), _debugPage == RendererDebugPage::WireframeMode ? Colors::DimGray : Colors::Black);
|
|
_context->ClearDepthStencilView(_renderTarget.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
|
|
|
// Reset viewport and scissor.
|
|
_context->RSSetViewports(1, &view.Viewport);
|
|
ResetScissor();
|
|
|
|
// Camera constant buffer contains matrices, camera position, fog values, and other things shared for all shaders.
|
|
CCameraMatrixBuffer cameraConstantBuffer;
|
|
view.FillConstantBuffer(cameraConstantBuffer);
|
|
cameraConstantBuffer.Frame = GlobalCounter;
|
|
cameraConstantBuffer.RefreshRate = _refreshRate;
|
|
cameraConstantBuffer.CameraUnderwater = g_Level.Rooms[cameraConstantBuffer.RoomNumber].flags & ENV_FLAG_WATER;
|
|
cameraConstantBuffer.DualParaboloidView = Matrix::CreateLookAt(LaraItem->Pose.Position.ToVector3(), LaraItem->Pose.Position.ToVector3() + Vector3(0, 0, 1024), -Vector3::UnitY);
|
|
|
|
if (level.GetFogEnabled())
|
|
{
|
|
auto fogColor = level.GetFogColor();
|
|
cameraConstantBuffer.FogColor = Vector4(fogColor.GetR() / 255.0f, fogColor.GetG() / 255.0f, fogColor.GetB() / 255.0f, 1.0f);
|
|
cameraConstantBuffer.FogMinDistance = level.GetFogMinDistance();
|
|
cameraConstantBuffer.FogMaxDistance = level.GetFogMaxDistance();
|
|
}
|
|
else
|
|
{
|
|
cameraConstantBuffer.FogMaxDistance = 0;
|
|
cameraConstantBuffer.FogColor = Vector4::Zero;
|
|
}
|
|
|
|
cameraConstantBuffer.AmbientOcclusion = g_Configuration.EnableAmbientOcclusion ? 1 : 0;
|
|
cameraConstantBuffer.AmbientOcclusionExponent = 2;
|
|
|
|
// Set fog bulbs.
|
|
cameraConstantBuffer.NumFogBulbs = (int)view.FogBulbsToDraw.size();
|
|
for (int i = 0; i < view.FogBulbsToDraw.size(); i++)
|
|
{
|
|
cameraConstantBuffer.FogBulbs[i].Position = view.FogBulbsToDraw[i].Position;
|
|
cameraConstantBuffer.FogBulbs[i].Density = view.FogBulbsToDraw[i].Density * (CLICK(1) / view.FogBulbsToDraw[i].Radius);
|
|
cameraConstantBuffer.FogBulbs[i].SquaredRadius = SQUARE(view.FogBulbsToDraw[i].Radius);
|
|
cameraConstantBuffer.FogBulbs[i].Color = view.FogBulbsToDraw[i].Color;
|
|
cameraConstantBuffer.FogBulbs[i].SquaredCameraToFogBulbDistance = SQUARE(view.FogBulbsToDraw[i].Distance);
|
|
cameraConstantBuffer.FogBulbs[i].FogBulbToCameraVector = view.FogBulbsToDraw[i].FogBulbToCameraVector;
|
|
}
|
|
|
|
_cbCameraMatrices.UpdateData(cameraConstantBuffer, _context.Get());
|
|
|
|
ID3D11RenderTargetView* pRenderViewPtrs[2];
|
|
|
|
// Bind main render target.
|
|
_context->OMSetRenderTargets(1, _renderTarget.RenderTargetView.GetAddressOf(), _renderTarget.DepthStencilView.Get());
|
|
|
|
// Draw horizon and sky.
|
|
DrawHorizonAndSky(view, _renderTarget.DepthStencilView.Get());
|
|
|
|
// Build G-Buffer (normals + depth).
|
|
_context->ClearRenderTargetView(_normalsRenderTarget.RenderTargetView.Get(), Colors::Black);
|
|
_context->ClearRenderTargetView(_depthRenderTarget.RenderTargetView.Get(), Colors::White);
|
|
|
|
pRenderViewPtrs[0] = _normalsRenderTarget.RenderTargetView.Get();
|
|
pRenderViewPtrs[1] = _depthRenderTarget.RenderTargetView.Get();
|
|
_context->OMSetRenderTargets(2, &pRenderViewPtrs[0], _renderTarget.DepthStencilView.Get());
|
|
|
|
// Lambda function to perform normal render pass and mirror passes, if needed.
|
|
auto doRenderPass = [&](RendererPass pass, bool drawMirrors)
|
|
{
|
|
// Draw room geometry first, if applicable for a given pass.
|
|
if (pass != RendererPass::Transparent && pass != RendererPass::GunFlashes)
|
|
DrawRooms(view, pass);
|
|
|
|
// Draw all objects, and additionally draw all reflections for them.
|
|
auto renderObjects = [&](bool player, bool moveables, bool statics)
|
|
{
|
|
switch (pass)
|
|
{
|
|
case RendererPass::Transparent:
|
|
DrawSortedFaces(view);
|
|
break;
|
|
|
|
case RendererPass::GunFlashes:
|
|
DrawGunFlashes(view);
|
|
DrawBaddyGunflashes(view);
|
|
break;
|
|
|
|
default:
|
|
if (statics)
|
|
{
|
|
DrawStatics(view, pass);
|
|
DrawDebris(view, pass); // Debris mostly originate from shatter statics.
|
|
}
|
|
|
|
if (moveables)
|
|
{
|
|
DrawItems(view, pass);
|
|
DrawEffects(view, pass);
|
|
DrawGunShells(view, pass);
|
|
DrawSpiders(view, pass);
|
|
DrawScarabs(view, pass);
|
|
DrawBats(view, pass);
|
|
DrawRats(view, pass);
|
|
DrawLocusts(view, pass);
|
|
DrawFishSwarm(view, pass);
|
|
}
|
|
else if (player)
|
|
{
|
|
DrawItems(view, pass, true);
|
|
DrawGunShells(view, pass);
|
|
}
|
|
|
|
// Sorted sprites already collected at beginning of frame.
|
|
if (pass != RendererPass::CollectTransparentFaces)
|
|
DrawSprites(view, pass);
|
|
|
|
break;
|
|
}
|
|
};
|
|
|
|
// Draw original objects.
|
|
renderObjects(true, true, true);
|
|
|
|
// Draw mirrored objects for all visible mirrors.
|
|
if (drawMirrors && !view.Mirrors.empty())
|
|
{
|
|
SetCullMode(CullMode::Clockwise);
|
|
for (auto& mirror : view.Mirrors)
|
|
{
|
|
_currentMirror = &mirror;
|
|
renderObjects(mirror.ReflectLara, mirror.ReflectMoveables, mirror.ReflectStatics);
|
|
_currentMirror = nullptr;
|
|
}
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
}
|
|
};
|
|
|
|
// Render G-Buffer pass.
|
|
doRenderPass(RendererPass::GBuffer, true);
|
|
|
|
// Calculate ambient occlusion.
|
|
if (g_Configuration.EnableAmbientOcclusion)
|
|
{
|
|
_doingFullscreenPass = true;
|
|
CalculateSSAO(view);
|
|
_doingFullscreenPass = false;
|
|
}
|
|
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
_context->IASetInputLayout(_inputLayout.Get());
|
|
|
|
_context->RSSetViewports(1, &view.Viewport);
|
|
ResetScissor();
|
|
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
SetDepthState(DepthState::Write);
|
|
|
|
// Bind main render target again. Main depth buffer is already filled and avoids overdraw in following steps.
|
|
_context->OMSetRenderTargets(1, _renderTarget.RenderTargetView.GetAddressOf(), _renderTarget.DepthStencilView.Get());
|
|
|
|
doRenderPass(RendererPass::Opaque, true);
|
|
doRenderPass(RendererPass::Additive, true);
|
|
doRenderPass(RendererPass::CollectTransparentFaces, false);
|
|
SortTransparentFaces(view);
|
|
|
|
doRenderPass(RendererPass::Transparent, true);
|
|
doRenderPass(RendererPass::GunFlashes, true); // HACK: Gunflashes are drawn after everything because they are near the camera.
|
|
|
|
// Draw 3D debug lines and triangles.
|
|
DrawLines3D(view);
|
|
DrawTriangles3D(view);
|
|
|
|
// Draw HUD.
|
|
ClearDrawPhaseDisplaySprites();
|
|
|
|
_context->ClearDepthStencilView(_renderTarget.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
|
|
|
// HUD must be drawn before post-processing to be antialiased.
|
|
if (renderMode == SceneRenderMode::Full && g_GameFlow->LastGameStatus == GameStatus::Normal)
|
|
g_Hud.Draw(*LaraItem);
|
|
|
|
if (renderMode != SceneRenderMode::NoPostprocess)
|
|
{
|
|
_doingFullscreenPass = true;
|
|
|
|
// Apply antialiasing.
|
|
switch (g_Configuration.AntialiasingMode)
|
|
{
|
|
case AntialiasingMode::None:
|
|
break;
|
|
|
|
case AntialiasingMode::Low:
|
|
ApplyFXAA(&_renderTarget, view);
|
|
break;
|
|
|
|
case AntialiasingMode::Medium:
|
|
case AntialiasingMode::High:
|
|
ApplySMAA(&_renderTarget, view);
|
|
break;
|
|
}
|
|
|
|
// Draw post-process effects (cinematic bars, fade, flash, HDR, tone mapping, etc.).
|
|
DrawPostprocess(renderTarget, view, renderMode);
|
|
|
|
_doingFullscreenPass = false;
|
|
|
|
// Draw binoculars or lasersight overlay.
|
|
DrawOverlays(view);
|
|
|
|
// Draw 2D debug lines.
|
|
DrawLines2D();
|
|
}
|
|
|
|
if (renderMode == SceneRenderMode::Full && g_GameFlow->LastGameStatus == GameStatus::Normal)
|
|
{
|
|
// Draw display sprites sorted by priority.
|
|
CollectDisplaySprites(view);
|
|
DrawDisplaySprites(view);
|
|
|
|
DrawDebugInfo(view);
|
|
DrawAllStrings();
|
|
}
|
|
|
|
time2 = std::chrono::high_resolution_clock::now();
|
|
_timeFrame = (std::chrono::duration_cast<ns>(time2 - time1)).count() / 1000000;
|
|
time1 = time2;
|
|
|
|
ClearScene();
|
|
CalculateFrameRate();
|
|
}
|
|
|
|
void Renderer::RenderSimpleSceneToParaboloid(RenderTarget2D* renderTarget, Vector3 position, int emisphere)
|
|
{
|
|
// Reset GPU state
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
|
|
_context->PSSetShader(_psRoomAmbient.Get(), nullptr, 0);
|
|
|
|
// Bind and clear render target
|
|
_context->ClearRenderTargetView(renderTarget->RenderTargetView.Get(), Colors::Black);
|
|
_context->ClearDepthStencilView(renderTarget->DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
|
_context->OMSetRenderTargets(1, renderTarget->RenderTargetView.GetAddressOf(), renderTarget->DepthStencilView.Get());
|
|
|
|
D3D11_VIEWPORT viewport;
|
|
viewport.TopLeftX = 0;
|
|
viewport.TopLeftY = 0;
|
|
viewport.Width = ROOM_AMBIENT_MAP_SIZE;
|
|
viewport.Height = ROOM_AMBIENT_MAP_SIZE;
|
|
viewport.MinDepth = 0;
|
|
viewport.MaxDepth = 1;
|
|
|
|
_context->RSSetViewports(1, &viewport);
|
|
|
|
D3D11_RECT rects[1];
|
|
rects[0].left = 0;
|
|
rects[0].right = ROOM_AMBIENT_MAP_SIZE;
|
|
rects[0].top = 0;
|
|
rects[0].bottom = ROOM_AMBIENT_MAP_SIZE;
|
|
|
|
_context->RSSetScissorRects(1, rects);
|
|
|
|
// Opaque geometry
|
|
SetBlendMode(BlendMode::Opaque);
|
|
|
|
if (emisphere == -1)
|
|
{
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
}
|
|
else
|
|
{
|
|
SetCullMode(CullMode::Clockwise);
|
|
}
|
|
|
|
auto view = RenderView(&Camera, 0, PI / 2.0f, 32, DEFAULT_FAR_VIEW, ROOM_AMBIENT_MAP_SIZE, ROOM_AMBIENT_MAP_SIZE);
|
|
|
|
CCameraMatrixBuffer cameraConstantBuffer;
|
|
cameraConstantBuffer.DualParaboloidView = Matrix::CreateLookAt(position, position + Vector3(0, 0, 1024), -Vector3::UnitY);
|
|
cameraConstantBuffer.Emisphere = emisphere;
|
|
view.FillConstantBuffer(cameraConstantBuffer);
|
|
_cbCameraMatrices.UpdateData(cameraConstantBuffer, _context.Get());
|
|
|
|
// Draw horizon and the sky
|
|
auto* levelPtr = g_GameFlow->GetLevel(CurrentLevel);
|
|
|
|
if (levelPtr->Horizon)
|
|
{
|
|
_context->VSSetShader(_vsRoomAmbientSky.Get(), nullptr, 0);
|
|
|
|
if (Lara.Control.Look.OpticRange != 0)
|
|
AlterFOV(ANGLE(DEFAULT_FOV) - Lara.Control.Look.OpticRange, false);
|
|
|
|
unsigned int stride = sizeof(Vertex);
|
|
unsigned int offset = 0;
|
|
|
|
// Draw sky.
|
|
auto rotation = Matrix::CreateRotationX(PI);
|
|
|
|
BindTexture(TextureRegister::ColorMap, &_skyTexture, SamplerStateRegister::AnisotropicClamp);
|
|
|
|
_context->IASetVertexBuffers(0, 1, _skyVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_skyIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
SetBlendMode(BlendMode::Additive);
|
|
|
|
for (int s = 0; s < 2; s++)
|
|
{
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
auto weather = TEN::Effects::Environment::Weather;
|
|
|
|
auto translation = Matrix::CreateTranslation(Camera.pos.x + weather.SkyPosition(s) - i * SKY_SIZE,
|
|
Camera.pos.y - 1536.0f, Camera.pos.z);
|
|
auto world = rotation * translation;
|
|
|
|
_stStatic.World = (rotation * translation);
|
|
_stStatic.Color = weather.SkyColor(s);
|
|
_stStatic.ApplyFogBulbs = s == 0 ? 1 : 0;
|
|
_cbStatic.UpdateData(_stStatic, _context.Get());
|
|
|
|
DrawIndexedTriangles(SKY_INDICES_COUNT, 0, 0);
|
|
}
|
|
}
|
|
|
|
_context->ClearDepthStencilView(renderTarget->DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1, 0);
|
|
|
|
// Draw horizon.
|
|
if (_moveableObjects[ID_HORIZON].has_value())
|
|
{
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
auto& moveableObj = *_moveableObjects[ID_HORIZON];
|
|
|
|
_stStatic.World = Matrix::CreateTranslation(LaraItem->Pose.Position.ToVector3());
|
|
_stStatic.Color = Vector4::One;
|
|
_stStatic.ApplyFogBulbs = 1;
|
|
_cbStatic.UpdateData(_stStatic, _context.Get());
|
|
|
|
for (int k = 0; k < moveableObj.ObjectMeshes.size(); k++)
|
|
{
|
|
auto* meshPtr = moveableObj.ObjectMeshes[k];
|
|
|
|
for (auto& bucket : meshPtr->Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[bucket.Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
|
|
// Always render horizon as alpha-blended surface.
|
|
SetBlendMode(bucket.BlendMode == BlendMode::AlphaTest ? BlendMode::AlphaBlend : bucket.BlendMode);
|
|
SetAlphaTest(AlphaTestMode::None, ALPHA_TEST_THRESHOLD);
|
|
|
|
// Draw vertices.
|
|
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
|
|
|
|
_numMoveablesDrawCalls++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear just the Z-buffer to start drawing on top of horizon.
|
|
_context->ClearDepthStencilView(renderTarget->DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
|
}
|
|
|
|
_context->VSSetShader(_vsRoomAmbient.Get(), nullptr, 0);
|
|
|
|
// Draw rooms
|
|
UINT stride = sizeof(Vertex);
|
|
UINT offset = 0;
|
|
|
|
// Bind vertex and index buffer.
|
|
_context->IASetVertexBuffers(0, 1, _roomsVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_roomsIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
for (int i = 0; i < _rooms.size(); i++)
|
|
{
|
|
int index = i;
|
|
RendererRoom* room = &_rooms[index];
|
|
ROOM_INFO* nativeRoom = &g_Level.Rooms[room->RoomNumber];
|
|
|
|
// Avoid drawing of too far rooms... Environment map is tiny, blurred, so very far rooms would not contribute to the
|
|
// final pixel colors
|
|
if (Vector3::Distance(room->BoundingBox.Center, LaraItem->Pose.Position.ToVector3()) >= BLOCK(40))
|
|
{
|
|
//continue;
|
|
}
|
|
|
|
cameraConstantBuffer.CameraUnderwater = g_Level.Rooms[_rooms[i].RoomNumber].flags & ENV_FLAG_WATER;
|
|
_cbCameraMatrices.UpdateData(cameraConstantBuffer, _context.Get());
|
|
|
|
_stRoom.Caustics = 0;
|
|
_stRoom.AmbientColor = room->AmbientLight;
|
|
_stRoom.NumRoomLights = 0;
|
|
_stRoom.Water = (nativeRoom->flags & ENV_FLAG_WATER) != 0 ? 1 : 0;
|
|
_cbRoom.UpdateData(_stRoom, _context.Get());
|
|
|
|
for (auto& bucket : room->Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
SetBlendMode(bucket.BlendMode);
|
|
SetAlphaTest(AlphaTestMode::GreatherThan, ALPHA_TEST_THRESHOLD);
|
|
|
|
if (bucket.Animated)
|
|
{
|
|
BindTexture(TextureRegister::ColorMap, &std::get<0>(_animatedTextures[bucket.Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
}
|
|
else
|
|
{
|
|
BindTexture(TextureRegister::ColorMap, &std::get<0>(_roomTextures[bucket.Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
}
|
|
|
|
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
|
|
|
|
_numRoomsDrawCalls++;
|
|
}
|
|
}
|
|
|
|
SetCullMode(CullMode::CounterClockwise, true);
|
|
SetDepthState(DepthState::Write, true);
|
|
SetBlendMode(BlendMode::Opaque, true);
|
|
|
|
// TODO: to finish
|
|
/*
|
|
// Smooth the ambient map with guassian 5x5 filter
|
|
_context->ClearRenderTargetView(_tempRoomAmbientRenderTarget1.RenderTargetView.Get(), Colors::Black);
|
|
_context->ClearDepthStencilView(_tempRoomAmbientRenderTarget1.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
|
_context->OMSetRenderTargets(1, _tempRoomAmbientRenderTarget1.RenderTargetView.GetAddressOf(), _tempRoomAmbientRenderTarget1.DepthStencilView.Get());
|
|
|
|
_postProcess->SetSourceTexture(renderTarget->ShaderResourceView.Get());
|
|
_postProcess->SetEffect(BasicPostProcess::GaussianBlur_5x5);
|
|
_postProcess->SetGaussianParameter(1);
|
|
_postProcess->Process(_context.Get());
|
|
|
|
_context->ClearRenderTargetView(_tempRoomAmbientRenderTarget2.RenderTargetView.Get(), Colors::Black);
|
|
_context->ClearDepthStencilView(_tempRoomAmbientRenderTarget2.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
|
_context->OMSetRenderTargets(1, _tempRoomAmbientRenderTarget2.RenderTargetView.GetAddressOf(), _tempRoomAmbientRenderTarget2.DepthStencilView.Get());
|
|
|
|
_postProcess->SetSourceTexture(_tempRoomAmbientRenderTarget1.ShaderResourceView.Get());
|
|
_postProcess->SetEffect(BasicPostProcess::GaussianBlur_5x5);
|
|
_postProcess->SetGaussianParameter(1);
|
|
_postProcess->Process(_context.Get());
|
|
|
|
_context->ClearRenderTargetView(_tempRoomAmbientRenderTarget3.RenderTargetView.Get(), Colors::Black);
|
|
_context->ClearDepthStencilView(_tempRoomAmbientRenderTarget3.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
|
_context->OMSetRenderTargets(1, _tempRoomAmbientRenderTarget3.RenderTargetView.GetAddressOf(), _tempRoomAmbientRenderTarget3.DepthStencilView.Get());
|
|
|
|
_postProcess->SetSourceTexture(_tempRoomAmbientRenderTarget2.ShaderResourceView.Get());
|
|
_postProcess->SetEffect(BasicPostProcess::GaussianBlur_5x5);
|
|
_postProcess->SetGaussianParameter(1);
|
|
_postProcess->Process(_context.Get());
|
|
|
|
_context->ClearRenderTargetView(_tempRoomAmbientRenderTarget4.RenderTargetView.Get(), Colors::Black);
|
|
_context->ClearDepthStencilView(_tempRoomAmbientRenderTarget4.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
|
_context->OMSetRenderTargets(1, _tempRoomAmbientRenderTarget4.RenderTargetView.GetAddressOf(), _tempRoomAmbientRenderTarget4.DepthStencilView.Get());
|
|
|
|
_postProcess->SetSourceTexture(_tempRoomAmbientRenderTarget3.ShaderResourceView.Get());
|
|
_postProcess->SetEffect(BasicPostProcess::GaussianBlur_5x5);
|
|
_postProcess->SetGaussianParameter(1);
|
|
_postProcess->Process(_context.Get());
|
|
|
|
_context->ClearRenderTargetView(renderTarget->RenderTargetView.Get(), Colors::Black);
|
|
_context->ClearDepthStencilView(renderTarget->DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
|
_context->OMSetRenderTargets(1, renderTarget->RenderTargetView.GetAddressOf(), renderTarget->DepthStencilView.Get());
|
|
|
|
// Copy back the filtered map to the render target
|
|
_postProcess->SetSourceTexture(_tempRoomAmbientRenderTarget4.ShaderResourceView.Get());
|
|
_postProcess->SetEffect(BasicPostProcess::Copy);
|
|
_postProcess->Process(_context.Get());
|
|
|
|
SetCullMode(CullMode::CounterClockwise, true);
|
|
SetDepthState(DepthState::Write, true);
|
|
SetBlendMode(BlendMode::Opaque, true);*/
|
|
}
|
|
|
|
void Renderer::DumpGameScene(SceneRenderMode renderMode)
|
|
{
|
|
RenderScene(&_dumpScreenRenderTarget, _gameCamera, renderMode);
|
|
}
|
|
|
|
void Renderer::DrawItems(RenderView& view, RendererPass rendererPass, bool onlyPlayer)
|
|
{
|
|
unsigned int stride = sizeof(Vertex);
|
|
unsigned int offset = 0;
|
|
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
// Set shaders.
|
|
if (rendererPass == RendererPass::GBuffer)
|
|
{
|
|
_context->VSSetShader(_vsGBufferItems.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psGBuffer.Get(), nullptr, 0);
|
|
}
|
|
else
|
|
{
|
|
_context->VSSetShader(_vsItems.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psItems.Get(), nullptr, 0);
|
|
}
|
|
|
|
BindRenderTargetAsTexture(TextureRegister::SSAO, &_SSAOBlurredRenderTarget, SamplerStateRegister::PointWrap);
|
|
|
|
for (auto room : view.RoomsToDraw)
|
|
{
|
|
if (IgnoreMirrorPassForRoom(room->RoomNumber))
|
|
continue;
|
|
|
|
for (auto itemToDraw : room->ItemsToDraw)
|
|
{
|
|
if (_currentMirror != nullptr && (g_Level.Items[itemToDraw->ItemNumber].Flags & IFLAG_CLEAR_BODY))
|
|
continue;
|
|
|
|
if (onlyPlayer && itemToDraw->ObjectID != ID_LARA)
|
|
continue;
|
|
|
|
switch (itemToDraw->ObjectID)
|
|
{
|
|
case ID_LARA:
|
|
DrawLara(view, rendererPass);
|
|
continue;
|
|
|
|
case ID_WATERFALL1:
|
|
case ID_WATERFALL2:
|
|
case ID_WATERFALL3:
|
|
case ID_WATERFALL4:
|
|
case ID_WATERFALL5:
|
|
case ID_WATERFALL6:
|
|
case ID_WATERFALLSS1:
|
|
case ID_WATERFALLSS2:
|
|
DrawWaterfalls(itemToDraw, view, 10, rendererPass);
|
|
continue;
|
|
|
|
default:
|
|
DrawAnimatingItem(itemToDraw, view, rendererPass);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::RenderItemShadows(RenderView& renderView)
|
|
{
|
|
RenderBlobShadows(renderView);
|
|
|
|
if (g_Configuration.ShadowType != ShadowMode::None)
|
|
{
|
|
for (auto room : renderView.RoomsToDraw)
|
|
for (auto itemToDraw : room->ItemsToDraw)
|
|
RenderShadowMap(itemToDraw, renderView);
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawWaterfalls(RendererItem* item, RenderView& view, int fps, RendererPass rendererPass)
|
|
{
|
|
// Extremely hacky function to get first rendered face of a waterfall object mesh, calculate
|
|
// its texture height and scroll all the textures according to that height.
|
|
|
|
RendererObject& moveableObj = *_moveableObjects[item->ObjectID];
|
|
|
|
// No mesh or bucket, abort
|
|
if (!moveableObj.ObjectMeshes.size() || !moveableObj.ObjectMeshes[0]->Buckets.size())
|
|
return;
|
|
|
|
// Get first three vertices of a waterfall object, meaning the very first triangle
|
|
const auto& v1 = _moveablesVertices[moveableObj.ObjectMeshes[0]->Buckets[0].StartVertex + 0];
|
|
const auto& v2 = _moveablesVertices[moveableObj.ObjectMeshes[0]->Buckets[0].StartVertex + 1];
|
|
const auto& v3 = _moveablesVertices[moveableObj.ObjectMeshes[0]->Buckets[0].StartVertex + 2];
|
|
|
|
// Calculate height of the texture by getting min/max UV.y coords of all three vertices
|
|
auto minY = std::min(std::min(v1.UV.y, v2.UV.y), v3.UV.y);
|
|
auto maxY = std::max(std::max(v1.UV.y, v2.UV.y), v3.UV.y);
|
|
auto minX = std::min(std::min(v1.UV.x, v2.UV.x), v3.UV.x);
|
|
auto maxX = std::max(std::max(v1.UV.x, v2.UV.x), v3.UV.x);
|
|
|
|
// Setup animated buffer
|
|
_stAnimated.Fps = fps;
|
|
_stAnimated.NumFrames = 1;
|
|
_stAnimated.Type = 1; // UVRotate
|
|
|
|
// We need only top/bottom Y coordinate for UVRotate, but we pass whole
|
|
// rectangle anyway, in case later we may want to implement different UVRotate modes.
|
|
_stAnimated.Textures[0].TopLeft = Vector2(minX, minY);
|
|
_stAnimated.Textures[0].TopRight = Vector2(maxX, minY);
|
|
_stAnimated.Textures[0].BottomLeft = Vector2(minX, maxY);
|
|
_stAnimated.Textures[0].BottomRight = Vector2(maxX, maxY);
|
|
|
|
_cbAnimated.UpdateData(_stAnimated, _context.Get());
|
|
|
|
DrawAnimatingItem(item, view, rendererPass);
|
|
|
|
// Reset animated buffer after rendering just in case
|
|
_stAnimated.Fps = _stAnimated.NumFrames = _stAnimated.Type = 0;
|
|
_cbAnimated.UpdateData(_stAnimated, _context.Get());
|
|
}
|
|
|
|
void Renderer::DrawAnimatingItem(RendererItem* item, RenderView& view, RendererPass rendererPass)
|
|
{
|
|
ItemInfo* nativeItem = &g_Level.Items[item->ItemNumber];
|
|
RendererRoom* room = &_rooms[item->RoomNumber];
|
|
RendererObject& moveableObj = *_moveableObjects[item->ObjectID];
|
|
|
|
// Bind item main properties
|
|
_stItem.World = item->InterpolatedWorld;
|
|
ReflectMatrixOptionally(_stItem.World);
|
|
|
|
_stItem.Color = item->Color;
|
|
_stItem.AmbientLight = item->AmbientLight;
|
|
memcpy(_stItem.BonesMatrices, item->InterpolatedAnimTransforms, sizeof(Matrix) * MAX_BONES);
|
|
|
|
for (int k = 0; k < moveableObj.ObjectMeshes.size(); k++)
|
|
_stItem.BoneLightModes[k] = (int)moveableObj.ObjectMeshes[k]->LightMode;
|
|
|
|
BindMoveableLights(item->LightsToDraw, item->RoomNumber, item->PrevRoomNumber, item->LightFade);
|
|
_cbItem.UpdateData(_stItem, _context.Get());
|
|
|
|
for (int k = 0; k < moveableObj.ObjectMeshes.size(); k++)
|
|
{
|
|
if (!nativeItem->MeshBits.Test(k))
|
|
continue;
|
|
|
|
DrawMoveableMesh(item, GetMesh(item->MeshIds[k]), room, k, view, rendererPass);
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawStatics(RenderView& view, RendererPass rendererPass)
|
|
{
|
|
if (_staticTextures.size() == 0 || view.SortedStaticsToDraw.size() == 0)
|
|
return;
|
|
|
|
if (rendererPass != RendererPass::CollectTransparentFaces)
|
|
{
|
|
#ifdef DISABLE_INSTANCING
|
|
if (rendererPass == RendererPass::GBuffer)
|
|
{
|
|
_context->VSSetShader(_vsGBufferStatics.Get(), NULL, 0);
|
|
_context->PSSetShader(_psGBuffer.Get(), NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
_context->VSSetShader(_vsStatics.Get(), NULL, 0);
|
|
_context->PSSetShader(_psStatics.Get(), NULL, 0);
|
|
}
|
|
|
|
// Bind vertex and index buffer
|
|
UINT stride = sizeof(Vertex);
|
|
UINT offset = 0;
|
|
_context->IASetVertexBuffers(0, 1, _staticsVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_staticsIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
BindRenderTargetAsTexture(TextureRegister::SSAO, &_SSAOBlurredRenderTarget, SamplerStateRegister::PointWrap);
|
|
|
|
for (auto it = view.SortedStaticsToDraw.begin(); it != view.SortedStaticsToDraw.end(); it++)
|
|
{
|
|
std::vector<RendererStatic*> statics = it->second;
|
|
|
|
RendererStatic* refStatic = statics[0];
|
|
RendererObject& refStaticObj = *_staticObjects[refStatic->ObjectNumber];
|
|
if (refStaticObj.ObjectMeshes.size() == 0)
|
|
continue;
|
|
|
|
RendererMesh* refMesh = refStaticObj.ObjectMeshes[0];
|
|
|
|
int staticsCount = (int)statics.size();
|
|
|
|
for (int s = 0; s < staticsCount; s++)
|
|
{
|
|
RendererStatic* current = statics[s];
|
|
RendererRoom* room = &_rooms[current->RoomNumber];
|
|
|
|
_stStatic.World = current->World;
|
|
_stStatic.Color = current->Color;
|
|
_stStatic.AmbientLight = room->AmbientLight;
|
|
_stStatic.LightMode = (int)refMesh->LightMode;
|
|
|
|
if (rendererPass != RendererPass::GBuffer)
|
|
{
|
|
BindStaticLights(current->LightsToDraw);
|
|
}
|
|
|
|
_cbStatic.UpdateData(_stStatic, _context.Get());
|
|
|
|
for (auto& bucket : refMesh->Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int passes = rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest ? 2 : 1;
|
|
|
|
for (int p = 0; p < passes; p++)
|
|
{
|
|
if (!SetupBlendModeAndAlphaTest(bucket.BlendMode, rendererPass, p))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
BindTexture(TextureRegister::ColorMap,
|
|
&std::get<0>(_staticTextures[bucket.Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
BindTexture(TextureRegister::NormalMap,
|
|
&std::get<1>(_staticTextures[bucket.Texture]), SamplerStateRegister::AnisotropicClamp);
|
|
|
|
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
|
|
|
|
_numInstancedStaticsDrawCalls++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
if (rendererPass == RendererPass::GBuffer)
|
|
{
|
|
_context->VSSetShader(_vsGBufferInstancedStatics.Get(), NULL, 0);
|
|
_context->PSSetShader(_psGBuffer.Get(), NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
_context->VSSetShader(_vsInstancedStaticMeshes.Get(), NULL, 0);
|
|
_context->PSSetShader(_psInstancedStaticMeshes.Get(), NULL, 0);
|
|
}
|
|
|
|
// Bind vertex and index buffer
|
|
UINT stride = sizeof(Vertex);
|
|
UINT offset = 0;
|
|
_context->IASetVertexBuffers(0, 1, _staticsVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_staticsIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
BindRenderTargetAsTexture(TextureRegister::SSAO, &_SSAOBlurredRenderTarget, SamplerStateRegister::PointWrap);
|
|
|
|
for (auto it = view.SortedStaticsToDraw.begin(); it != view.SortedStaticsToDraw.end(); it++)
|
|
{
|
|
std::vector<RendererStatic*> statics = it->second;
|
|
|
|
RendererStatic* refStatic = statics[0];
|
|
RendererObject& refStaticObj = GetStaticRendererObject(refStatic->ObjectNumber);
|
|
if (refStaticObj.ObjectMeshes.size() == 0)
|
|
continue;
|
|
|
|
RendererMesh* refMesh = refStaticObj.ObjectMeshes[0];
|
|
|
|
int staticsCount = (int)statics.size();
|
|
int bucketSize = INSTANCED_STATIC_MESH_BUCKET_SIZE;
|
|
int baseStaticIndex = 0;
|
|
|
|
while (baseStaticIndex < staticsCount)
|
|
{
|
|
int instancesCount = 0;
|
|
int max = std::min(baseStaticIndex + bucketSize, staticsCount);
|
|
|
|
for (int s = baseStaticIndex; s < max; s++)
|
|
{
|
|
RendererStatic* current = statics[s];
|
|
RendererRoom* room = &_rooms[current->RoomNumber];
|
|
|
|
if (IgnoreMirrorPassForRoom(current->RoomNumber))
|
|
continue;
|
|
|
|
Matrix world = current->World;
|
|
ReflectMatrixOptionally(world);
|
|
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[instancesCount].World = world;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[instancesCount].Color = current->Color;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[instancesCount].Ambient = room->AmbientLight;
|
|
_stInstancedStaticMeshBuffer.StaticMeshes[instancesCount].LightMode = (int)refMesh->LightMode;
|
|
|
|
if (rendererPass != RendererPass::GBuffer)
|
|
{
|
|
BindInstancedStaticLights(current->LightsToDraw, instancesCount);
|
|
}
|
|
|
|
instancesCount++;
|
|
}
|
|
|
|
baseStaticIndex += bucketSize;
|
|
|
|
if (instancesCount > 0)
|
|
{
|
|
_cbInstancedStaticMeshBuffer.UpdateData(_stInstancedStaticMeshBuffer, _context.Get());
|
|
|
|
for (auto& bucket : refMesh->Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int passes = rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest ? 2 : 1;
|
|
|
|
for (int p = 0; p < passes; p++)
|
|
{
|
|
if (!SetupBlendModeAndAlphaTest(bucket.BlendMode, rendererPass, p))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
BindTexture(TextureRegister::ColorMap,
|
|
&std::get<0>(_staticTextures[bucket.Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
BindTexture(TextureRegister::NormalMap,
|
|
&std::get<1>(_staticTextures[bucket.Texture]), SamplerStateRegister::AnisotropicClamp);
|
|
|
|
DrawIndexedInstancedTriangles(bucket.NumIndices, instancesCount, bucket.StartIndex, 0);
|
|
|
|
_numInstancedStaticsDrawCalls++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// Collect sorted blend modes faces ordered by room, if transparent pass
|
|
|
|
for (auto it = view.SortedStaticsToDraw.begin(); it != view.SortedStaticsToDraw.end(); it++)
|
|
{
|
|
std::vector<RendererStatic*> statics = it->second;
|
|
|
|
RendererStatic* refStatic = statics[0];
|
|
RendererObject& refStaticObj = GetStaticRendererObject(refStatic->ObjectNumber);
|
|
if (refStaticObj.ObjectMeshes.size() == 0)
|
|
continue;
|
|
|
|
RendererMesh* refMesh = refStaticObj.ObjectMeshes[0];
|
|
|
|
for (int i = 0; i < statics.size(); i++)
|
|
{
|
|
for (int j = 0; j < refMesh->Buckets.size(); j++)
|
|
{
|
|
auto& bucket = refMesh->Buckets[j];
|
|
|
|
if (bucket.NumVertices == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (IsSortedBlendMode(bucket.BlendMode))
|
|
{
|
|
for (int p = 0; p < bucket.Polygons.size(); p++)
|
|
{
|
|
RendererSortableObject object;
|
|
|
|
object.ObjectType = RendererObjectType::Static;
|
|
object.Bucket = &bucket;
|
|
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.Polygon = &bucket.Polygons[p];
|
|
object.Room = &_rooms[object.Static->RoomNumber];
|
|
|
|
view.TransparentObjectsToDraw.push_back(object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawRooms(RenderView& view, RendererPass rendererPass)
|
|
{
|
|
if (rendererPass == RendererPass::CollectTransparentFaces)
|
|
{
|
|
for (int i = (int)view.RoomsToDraw.size() - 1; i >= 0; i--)
|
|
{
|
|
int index = i;
|
|
RendererRoom* room = view.RoomsToDraw[index];
|
|
|
|
for (int animated = 0; animated < 2; animated++)
|
|
{
|
|
for (int j = 0; j < room->Buckets.size(); j++)
|
|
{
|
|
auto& bucket = room->Buckets[j];
|
|
|
|
if ((animated == 1) ^ bucket.Animated || bucket.NumVertices == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (rendererPass == RendererPass::CollectTransparentFaces)
|
|
{
|
|
if (IsSortedBlendMode(bucket.BlendMode))
|
|
{
|
|
for (int p = 0; p < bucket.Polygons.size(); p++)
|
|
{
|
|
RendererSortableObject object;
|
|
|
|
object.ObjectType = RendererObjectType::Room;
|
|
object.Centre = bucket.Centre;
|
|
object.Distance = Vector3::Distance(view.Camera.WorldPosition, bucket.Polygons[p].Centre);
|
|
object.Bucket = &bucket;
|
|
object.Room = room;
|
|
object.Polygon = &bucket.Polygons[p];
|
|
|
|
view.TransparentObjectsToDraw.push_back(object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rendererPass == RendererPass::GBuffer)
|
|
{
|
|
_context->PSSetShader(_psGBuffer.Get(), nullptr, 0);
|
|
}
|
|
else
|
|
{
|
|
_context->PSSetShader(_psRooms.Get(), nullptr, 0);
|
|
}
|
|
|
|
UINT stride = sizeof(Vertex);
|
|
UINT offset = 0;
|
|
|
|
// Bind vertex and index buffer.
|
|
_context->IASetVertexBuffers(0, 1, _roomsVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_roomsIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
if (rendererPass != RendererPass::GBuffer)
|
|
{
|
|
// Bind caustics texture.
|
|
if (_causticTextures.size() > 0)
|
|
{
|
|
int nmeshes = -Objects[ID_CAUSTICS_TEXTURES].nmeshes;
|
|
int meshIndex = Objects[ID_CAUSTICS_TEXTURES].meshIndex;
|
|
int causticsFrame = GlobalCounter % _causticTextures.size();
|
|
BindTexture(TextureRegister::CausticsMap, &_causticTextures[causticsFrame], SamplerStateRegister::AnisotropicClamp);
|
|
}
|
|
|
|
// Set shadow map data and bind shadow map texture.
|
|
if (_shadowLight != nullptr)
|
|
{
|
|
BindLight(*_shadowLight, &_stShadowMap.Light, 0);
|
|
_stShadowMap.ShadowMapSize = g_Configuration.ShadowMapSize;
|
|
_stShadowMap.CastShadows = true;
|
|
|
|
BindTexture(TextureRegister::ShadowMap, &_shadowMap, SamplerStateRegister::ShadowMap);
|
|
}
|
|
else
|
|
{
|
|
_stShadowMap.CastShadows = false;
|
|
}
|
|
|
|
_cbShadowMap.UpdateData(_stShadowMap, _context.Get());
|
|
}
|
|
|
|
if (g_Configuration.EnableAmbientOcclusion && rendererPass != RendererPass::GBuffer)
|
|
{
|
|
BindRenderTargetAsTexture(TextureRegister::SSAO, &_SSAOBlurredRenderTarget, SamplerStateRegister::PointWrap);
|
|
}
|
|
|
|
for (int i = (int)view.RoomsToDraw.size() - 1; i >= 0; i--)
|
|
{
|
|
const auto& room = *view.RoomsToDraw[i];
|
|
const auto& nativeRoom = g_Level.Rooms[room.RoomNumber];
|
|
|
|
if (rendererPass != RendererPass::GBuffer)
|
|
{
|
|
_stRoom.Caustics = int(g_Configuration.EnableCaustics && (nativeRoom.flags & ENV_FLAG_WATER));
|
|
_stRoom.AmbientColor = room.AmbientLight;
|
|
BindRoomLights(view.LightsToDraw);
|
|
}
|
|
|
|
_stRoom.Water = (nativeRoom.flags & ENV_FLAG_WATER) != 0 ? 1 : 0;
|
|
_cbRoom.UpdateData(_stRoom, _context.Get());
|
|
|
|
SetScissor(room.ClipBounds);
|
|
|
|
for (int animated = 0; animated < 2; animated++)
|
|
{
|
|
if (rendererPass != RendererPass::GBuffer)
|
|
{
|
|
_context->VSSetShader((animated == 0) ? _vsRooms.Get() : _vsRoomsAnimatedTextures.Get(), nullptr, 0);
|
|
}
|
|
else
|
|
{
|
|
_context->VSSetShader((animated == 0) ? _vsGBufferRooms.Get() : _vsGBufferRoomsAnimated.Get(), nullptr, 0);
|
|
}
|
|
|
|
for (const auto& bucket : room.Buckets)
|
|
{
|
|
if ((animated == 1) ^ bucket.Animated)
|
|
continue;
|
|
|
|
if (bucket.NumVertices == 0)
|
|
continue;
|
|
|
|
int passes = rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest ? 2 : 1;
|
|
|
|
for (int p = 0; p < passes; p++)
|
|
{
|
|
if (!SetupBlendModeAndAlphaTest(bucket.BlendMode, rendererPass, p))
|
|
continue;
|
|
|
|
// Draw geometry.
|
|
if (animated)
|
|
{
|
|
BindTexture(TextureRegister::ColorMap,
|
|
&std::get<0>(_animatedTextures[bucket.Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
BindTexture(TextureRegister::NormalMap,
|
|
&std::get<1>(_animatedTextures[bucket.Texture]), SamplerStateRegister::AnisotropicClamp);
|
|
|
|
const auto& set = _animatedTextureSets[bucket.Texture];
|
|
_stAnimated.NumFrames = set.NumTextures;
|
|
_stAnimated.Type = 0;
|
|
_stAnimated.Fps = set.Fps;
|
|
|
|
for (unsigned char j = 0; j < set.NumTextures; j++)
|
|
{
|
|
if (j >= _stAnimated.Textures.size())
|
|
{
|
|
TENLog("Animated frame " + std::to_string(j) + " out of bounds. Too many frames in sequence.");
|
|
break;
|
|
}
|
|
|
|
_stAnimated.Textures[j].TopLeft = set.Textures[j].UV[0];
|
|
_stAnimated.Textures[j].TopRight = set.Textures[j].UV[1];
|
|
_stAnimated.Textures[j].BottomRight = set.Textures[j].UV[2];
|
|
_stAnimated.Textures[j].BottomLeft = set.Textures[j].UV[3];
|
|
}
|
|
_cbAnimated.UpdateData(_stAnimated, _context.Get());
|
|
}
|
|
else
|
|
{
|
|
BindTexture(
|
|
TextureRegister::ColorMap, &std::get<0>(_roomTextures[bucket.Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
BindTexture(
|
|
TextureRegister::NormalMap, &std::get<1>(_roomTextures[bucket.Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
}
|
|
|
|
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
|
|
|
|
_numRoomsDrawCalls++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ResetScissor();
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawHorizonAndSky(RenderView& renderView, ID3D11DepthStencilView* depthTarget)
|
|
{
|
|
constexpr auto STAR_SIZE = 2;
|
|
constexpr auto SUN_SIZE = 64;
|
|
|
|
auto* levelPtr = g_GameFlow->GetLevel(CurrentLevel);
|
|
|
|
bool anyOutsideRooms = false;
|
|
for (int k = 0; k < renderView.RoomsToDraw.size(); k++)
|
|
{
|
|
const auto& nativeRoom = g_Level.Rooms[renderView.RoomsToDraw[k]->RoomNumber];
|
|
if (nativeRoom.flags & ENV_FLAG_SKYBOX)
|
|
{
|
|
anyOutsideRooms = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!levelPtr->Horizon || !anyOutsideRooms)
|
|
return;
|
|
|
|
if (Lara.Control.Look.OpticRange != 0)
|
|
AlterFOV(ANGLE(DEFAULT_FOV) - Lara.Control.Look.OpticRange, false);
|
|
|
|
UINT stride = sizeof(Vertex);
|
|
UINT offset = 0;
|
|
|
|
// Draw sky.
|
|
auto rotation = Matrix::CreateRotationX(PI);
|
|
|
|
_context->VSSetShader(_vsSky.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psSky.Get(), nullptr, 0);
|
|
|
|
BindTexture(TextureRegister::ColorMap, &_skyTexture, SamplerStateRegister::AnisotropicClamp);
|
|
|
|
_context->IASetVertexBuffers(0, 1, _skyVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_skyIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
SetBlendMode(BlendMode::Additive);
|
|
|
|
for (int s = 0; s < 2; s++)
|
|
{
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
auto weather = TEN::Effects::Environment::Weather;
|
|
|
|
auto translation = Matrix::CreateTranslation(
|
|
renderView.Camera.WorldPosition.x + weather.SkyPosition(s) - i * SKY_SIZE,
|
|
renderView.Camera.WorldPosition.y - 1536.0f,
|
|
renderView.Camera.WorldPosition.z);
|
|
auto world = rotation * translation;
|
|
|
|
_stStatic.World = (rotation * translation);
|
|
_stStatic.Color = weather.SkyColor(s);
|
|
_stStatic.ApplyFogBulbs = s == 0 ? 1 : 0;
|
|
_cbStatic.UpdateData(_stStatic, _context.Get());
|
|
|
|
DrawIndexedTriangles(SKY_INDICES_COUNT, 0, 0);
|
|
|
|
_numMoveablesDrawCalls++;
|
|
}
|
|
}
|
|
|
|
_context->ClearDepthStencilView(depthTarget, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1, 0);
|
|
|
|
if (Weather.GetStars().size() > 0)
|
|
{
|
|
SetDepthState(DepthState::Read);
|
|
SetBlendMode(BlendMode::Additive);
|
|
SetCullMode(CullMode::None);
|
|
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
|
|
|
_context->VSSetShader(_vsInstancedSprites.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psInstancedSprites.Get(), nullptr, 0);
|
|
|
|
// Set up vertex buffer and parameters.
|
|
UINT stride = sizeof(Vertex);
|
|
UINT offset = 0;
|
|
_context->IASetVertexBuffers(0, 1, _quadVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
|
|
BindTexture(TextureRegister::ColorMap, _sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_LENS_FLARE_3].Texture, SamplerStateRegister::LinearClamp);
|
|
|
|
int drawnStars = 0;
|
|
int starCount = (int)Weather.GetStars().size();
|
|
|
|
while (drawnStars < starCount)
|
|
{
|
|
int starsToDraw =
|
|
(starCount - drawnStars) > INSTANCED_SPRITES_BUCKET_SIZE ?
|
|
INSTANCED_SPRITES_BUCKET_SIZE :
|
|
(starCount - drawnStars);
|
|
int i = 0;
|
|
|
|
for (int i = 0; i < starsToDraw; i++)
|
|
{
|
|
auto& star = Weather.GetStars()[drawnStars + i];
|
|
|
|
RendererSpriteToDraw rDrawSprite;
|
|
rDrawSprite.Sprite = &_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_LENS_FLARE_3];
|
|
|
|
rDrawSprite.Type = SpriteType::Billboard;
|
|
rDrawSprite.pos = renderView.Camera.WorldPosition + star.Direction * BLOCK(1);
|
|
rDrawSprite.Rotation = 0;
|
|
rDrawSprite.Scale = 1;
|
|
rDrawSprite.Width = STAR_SIZE * star.Scale;
|
|
rDrawSprite.Height = STAR_SIZE * star.Scale;
|
|
|
|
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(&rDrawSprite, nullptr, renderView);
|
|
_stInstancedSpriteBuffer.Sprites[i].Color = Vector4(
|
|
star.Color.x,
|
|
star.Color.y,
|
|
star.Color.z,
|
|
star.Blinking * star.Extinction);
|
|
_stInstancedSpriteBuffer.Sprites[i].IsBillboard = 1;
|
|
_stInstancedSpriteBuffer.Sprites[i].IsSoftParticle = 0;
|
|
|
|
// NOTE: Strange packing due to particular HLSL 16 byte alignment requirements.
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].x = rDrawSprite.Sprite->UV[0].x;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].y = rDrawSprite.Sprite->UV[1].x;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].z = rDrawSprite.Sprite->UV[2].x;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].w = rDrawSprite.Sprite->UV[3].x;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].x = rDrawSprite.Sprite->UV[0].y;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].y = rDrawSprite.Sprite->UV[1].y;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].z = rDrawSprite.Sprite->UV[2].y;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].w = rDrawSprite.Sprite->UV[3].y;
|
|
}
|
|
|
|
_cbInstancedSpriteBuffer.UpdateData(_stInstancedSpriteBuffer, _context.Get());
|
|
|
|
// Draw sprites with instancing.
|
|
DrawInstancedTriangles(4, starsToDraw, 0);
|
|
|
|
drawnStars += starsToDraw;
|
|
}
|
|
|
|
// Draw meteors
|
|
if (Weather.GetMeteors().size() > 0)
|
|
{
|
|
RendererSpriteToDraw rDrawSprite;
|
|
rDrawSprite.Sprite = &_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_LENS_FLARE_3];
|
|
BindTexture(TextureRegister::ColorMap, rDrawSprite.Sprite->Texture, SamplerStateRegister::LinearClamp);
|
|
|
|
int drawnMeteors = 0;
|
|
int meteorsCount = (int)Weather.GetMeteors().size();
|
|
|
|
while (drawnMeteors < meteorsCount)
|
|
{
|
|
int meteorsToDraw =
|
|
(meteorsCount - drawnMeteors) > INSTANCED_SPRITES_BUCKET_SIZE ?
|
|
INSTANCED_SPRITES_BUCKET_SIZE :
|
|
(meteorsCount - drawnMeteors);
|
|
int i = 0;
|
|
|
|
for (int i = 0; i < meteorsToDraw; i++)
|
|
{
|
|
auto meteor = Weather.GetMeteors()[drawnMeteors + i];
|
|
|
|
if (meteor.Active == false)
|
|
continue;
|
|
|
|
rDrawSprite.Type = SpriteType::CustomBillboard;
|
|
rDrawSprite.pos =
|
|
renderView.Camera.WorldPosition +
|
|
Vector3::Lerp(meteor.PrevPosition, meteor.Position, GetInterpolationFactor());
|
|
rDrawSprite.Rotation = 0;
|
|
rDrawSprite.Scale = 1;
|
|
rDrawSprite.Width = 2;
|
|
rDrawSprite.Height = 192;
|
|
rDrawSprite.ConstrainAxis = meteor.Direction;
|
|
|
|
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(&rDrawSprite, nullptr, renderView);
|
|
_stInstancedSpriteBuffer.Sprites[i].Color = Vector4(
|
|
meteor.Color.x,
|
|
meteor.Color.y,
|
|
meteor.Color.z,
|
|
Lerp(meteor.PrevFade, meteor.Fade, GetInterpolationFactor()));
|
|
_stInstancedSpriteBuffer.Sprites[i].IsBillboard = 1;
|
|
_stInstancedSpriteBuffer.Sprites[i].IsSoftParticle = 0;
|
|
|
|
// NOTE: Strange packing due to particular HLSL 16 byte alignment requirements.
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].x = rDrawSprite.Sprite->UV[0].x;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].y = rDrawSprite.Sprite->UV[1].x;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].z = rDrawSprite.Sprite->UV[2].x;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].w = rDrawSprite.Sprite->UV[3].x;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].x = rDrawSprite.Sprite->UV[0].y;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].y = rDrawSprite.Sprite->UV[1].y;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].z = rDrawSprite.Sprite->UV[2].y;
|
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].w = rDrawSprite.Sprite->UV[3].y;
|
|
}
|
|
|
|
_cbInstancedSpriteBuffer.UpdateData(_stInstancedSpriteBuffer, _context.Get());
|
|
|
|
// Draw sprites with instancing.
|
|
DrawInstancedTriangles(4, meteorsToDraw, 0);
|
|
|
|
drawnMeteors += meteorsToDraw;
|
|
}
|
|
}
|
|
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
}
|
|
|
|
// Draw horizon.
|
|
if (_moveableObjects[ID_HORIZON].has_value())
|
|
{
|
|
SetDepthState(DepthState::None);
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetIndexBuffer(_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
_context->VSSetShader(_vsSky.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psSky.Get(), nullptr, 0);
|
|
|
|
auto& moveableObj = *_moveableObjects[ID_HORIZON];
|
|
|
|
_stStatic.World = Matrix::CreateTranslation(renderView.Camera.WorldPosition);
|
|
_stStatic.Color = Vector4::One;
|
|
_stStatic.ApplyFogBulbs = 1;
|
|
_cbStatic.UpdateData(_stStatic, _context.Get());
|
|
|
|
for (int k = 0; k < moveableObj.ObjectMeshes.size(); k++)
|
|
{
|
|
auto* meshPtr = moveableObj.ObjectMeshes[k];
|
|
|
|
for (auto& bucket : meshPtr->Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[bucket.Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
|
|
// Always render horizon as alpha-blended surface.
|
|
SetBlendMode(bucket.BlendMode == BlendMode::AlphaTest ? BlendMode::AlphaBlend : bucket.BlendMode);
|
|
SetAlphaTest(AlphaTestMode::None, ALPHA_TEST_THRESHOLD);
|
|
|
|
// Draw vertices.
|
|
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
|
|
|
|
_numMoveablesDrawCalls++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Eventually draw the sun sprite.
|
|
if (!renderView.LensFlaresToDraw.empty() && renderView.LensFlaresToDraw[0].IsGlobal)
|
|
{
|
|
SetDepthState(DepthState::Read);
|
|
SetBlendMode(BlendMode::Additive);
|
|
SetCullMode(CullMode::None);
|
|
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
|
|
|
_context->VSSetShader(_vsInstancedSprites.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psInstancedSprites.Get(), nullptr, 0);
|
|
|
|
// Set up vertex buffer and parameters.
|
|
unsigned int stride = sizeof(Vertex);
|
|
unsigned int offset = 0;
|
|
_context->IASetVertexBuffers(0, 1, _quadVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
|
|
auto rDrawSprite = RendererSpriteToDraw{};
|
|
rDrawSprite.Sprite = &_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + renderView.LensFlaresToDraw[0].SpriteID];
|
|
|
|
rDrawSprite.Type = SpriteType::Billboard;
|
|
rDrawSprite.pos = renderView.Camera.WorldPosition + renderView.LensFlaresToDraw[0].Direction * BLOCK(1);
|
|
rDrawSprite.Rotation = 0.0f;
|
|
rDrawSprite.Scale = 1.0f;
|
|
rDrawSprite.Width = SUN_SIZE;
|
|
rDrawSprite.Height = SUN_SIZE;
|
|
rDrawSprite.color = renderView.LensFlaresToDraw[0].Color;
|
|
|
|
_stInstancedSpriteBuffer.Sprites[0].World = GetWorldMatrixForSprite(&rDrawSprite, nullptr, renderView);
|
|
_stInstancedSpriteBuffer.Sprites[0].Color = renderView.LensFlaresToDraw[0].Color;
|
|
_stInstancedSpriteBuffer.Sprites[0].IsBillboard = 1;
|
|
_stInstancedSpriteBuffer.Sprites[0].IsSoftParticle = 0;
|
|
|
|
// NOTE: Strange packing due to particular HLSL 16 byte alignment requirements.
|
|
_stInstancedSpriteBuffer.Sprites[0].UV[0].x = rDrawSprite.Sprite->UV[0].x;
|
|
_stInstancedSpriteBuffer.Sprites[0].UV[0].y = rDrawSprite.Sprite->UV[1].x;
|
|
_stInstancedSpriteBuffer.Sprites[0].UV[0].z = rDrawSprite.Sprite->UV[2].x;
|
|
_stInstancedSpriteBuffer.Sprites[0].UV[0].w = rDrawSprite.Sprite->UV[3].x;
|
|
_stInstancedSpriteBuffer.Sprites[0].UV[1].x = rDrawSprite.Sprite->UV[0].y;
|
|
_stInstancedSpriteBuffer.Sprites[0].UV[1].y = rDrawSprite.Sprite->UV[1].y;
|
|
_stInstancedSpriteBuffer.Sprites[0].UV[1].z = rDrawSprite.Sprite->UV[2].y;
|
|
_stInstancedSpriteBuffer.Sprites[0].UV[1].w = rDrawSprite.Sprite->UV[3].y;
|
|
|
|
BindTexture(TextureRegister::ColorMap, rDrawSprite.Sprite->Texture, SamplerStateRegister::LinearClamp);
|
|
|
|
_cbInstancedSpriteBuffer.UpdateData(_stInstancedSpriteBuffer, _context.Get());
|
|
|
|
// Draw sprites with instancing.
|
|
DrawInstancedTriangles(4, 1, 0);
|
|
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
}
|
|
|
|
// Clear just the Z-buffer to start drawing on top of horizon.
|
|
_context->ClearDepthStencilView(depthTarget, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
|
|
|
// Reset the GPU state
|
|
SetDepthState(DepthState::Write);
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
}
|
|
|
|
void Renderer::Render(float interpFactor)
|
|
{
|
|
InterpolateCamera(interpFactor);
|
|
RenderScene(&_backBuffer, _gameCamera);
|
|
|
|
_context->ClearState();
|
|
_swapChain->Present(1, 0);
|
|
}
|
|
|
|
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++)
|
|
{
|
|
auto& bucket = mesh->Buckets[j];
|
|
|
|
if (rendererPass == RendererPass::CollectTransparentFaces)
|
|
{
|
|
if (IsSortedBlendMode(bucket.BlendMode))
|
|
{
|
|
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();
|
|
|
|
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];
|
|
|
|
view.TransparentObjectsToDraw.push_back(object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (auto& bucket : mesh->Buckets)
|
|
{
|
|
if (bucket.NumVertices == 0)
|
|
continue;
|
|
|
|
if (rendererPass == RendererPass::ShadowMap)
|
|
{
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetAlphaTest(AlphaTestMode::None, ALPHA_TEST_THRESHOLD);
|
|
|
|
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
|
|
|
|
_numShadowMapDrawCalls++;
|
|
}
|
|
else
|
|
{
|
|
int passes = rendererPass == RendererPass::Opaque && bucket.BlendMode == BlendMode::AlphaTest ? 2 : 1;
|
|
|
|
for (int p = 0; p < passes; p++)
|
|
{
|
|
if (!SetupBlendModeAndAlphaTest(bucket.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);
|
|
|
|
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
|
|
|
|
_numMoveablesDrawCalls++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Renderer::SetupBlendModeAndAlphaTest(BlendMode blendMode, RendererPass rendererPass, int drawPass)
|
|
{
|
|
switch (rendererPass)
|
|
{
|
|
case RendererPass::GBuffer:
|
|
if (blendMode != BlendMode::Opaque &&
|
|
blendMode != BlendMode::AlphaTest &&
|
|
blendMode != BlendMode::FastAlphaBlend)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (blendMode == BlendMode::Opaque)
|
|
{
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetAlphaTest(AlphaTestMode::None, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
SetBlendMode(BlendMode::AlphaTest);
|
|
SetAlphaTest(AlphaTestMode::GreatherThan, FAST_ALPHA_BLEND_THRESHOLD);
|
|
}
|
|
break;
|
|
|
|
case RendererPass::Opaque:
|
|
if (blendMode != BlendMode::Opaque &&
|
|
blendMode != BlendMode::AlphaTest)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (blendMode == BlendMode::Opaque)
|
|
{
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetAlphaTest(AlphaTestMode::None, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
if (drawPass == 0)
|
|
{
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetAlphaTest(AlphaTestMode::GreatherThan, ALPHA_TEST_THRESHOLD);
|
|
}
|
|
else
|
|
{
|
|
SetBlendMode(BlendMode::AlphaBlend);
|
|
SetAlphaTest(AlphaTestMode::LessThan, ALPHA_TEST_THRESHOLD);
|
|
};
|
|
}
|
|
break;
|
|
|
|
case RendererPass::Additive:
|
|
if (blendMode != BlendMode::Additive)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SetBlendMode(BlendMode::Additive);
|
|
SetAlphaTest(AlphaTestMode::None, 1.0f);
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Renderer::SortTransparentFaces(RenderView& view)
|
|
{
|
|
std::sort(
|
|
view.TransparentObjectsToDraw.begin(),
|
|
view.TransparentObjectsToDraw.end(),
|
|
[](RendererSortableObject& a, RendererSortableObject& b)
|
|
{
|
|
return (a.Distance > b.Distance);
|
|
}
|
|
);
|
|
}
|
|
|
|
void Renderer::DrawSortedFaces(RenderView& view)
|
|
{
|
|
for (int i = 0; i < view.TransparentObjectsToDraw.size(); i++)
|
|
{
|
|
RendererSortableObject* object = &view.TransparentObjectsToDraw[i];
|
|
RendererObjectType lastObjectType = (i > 0 ? view.TransparentObjectsToDraw[i - 1].ObjectType : RendererObjectType::Unknown);
|
|
|
|
_sortedPolygonsVertices.clear();
|
|
_sortedPolygonsIndices.clear();
|
|
|
|
if (_currentMirror != nullptr && object->ObjectType == RendererObjectType::Room)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (object->ObjectType == RendererObjectType::Room)
|
|
{
|
|
while (i < view.TransparentObjectsToDraw.size() &&
|
|
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 &&
|
|
_sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES)
|
|
{
|
|
RendererSortableObject* currentObject = &view.TransparentObjectsToDraw[i];
|
|
_sortedPolygonsIndices.bulk_push_back(
|
|
_roomsIndices.data(),
|
|
currentObject->Polygon->BaseIndex,
|
|
currentObject->Polygon->Shape == 0 ? 6 : 3);
|
|
i++;
|
|
}
|
|
|
|
DrawRoomSorted(object, lastObjectType, view);
|
|
|
|
if (i == view.TransparentObjectsToDraw.size())
|
|
{
|
|
return;
|
|
}
|
|
|
|
i--;
|
|
}
|
|
else if (object->ObjectType == RendererObjectType::Moveable)
|
|
{
|
|
while (i < view.TransparentObjectsToDraw.size() &&
|
|
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 &&
|
|
_sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES)
|
|
{
|
|
RendererSortableObject* currentObject = &view.TransparentObjectsToDraw[i];
|
|
_sortedPolygonsIndices.bulk_push_back(
|
|
_moveablesIndices.data(),
|
|
currentObject->Polygon->BaseIndex,
|
|
currentObject->Polygon->Shape == 0 ? 6 : 3);
|
|
i++;
|
|
}
|
|
|
|
DrawItemSorted(object, lastObjectType, view);
|
|
|
|
if (i == view.TransparentObjectsToDraw.size())
|
|
{
|
|
return;
|
|
}
|
|
|
|
i--;
|
|
}
|
|
else if (object->ObjectType == RendererObjectType::Static)
|
|
{
|
|
while (i < view.TransparentObjectsToDraw.size() &&
|
|
view.TransparentObjectsToDraw[i].ObjectType == object->ObjectType &&
|
|
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 &&
|
|
_sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES)
|
|
{
|
|
RendererSortableObject* currentObject = &view.TransparentObjectsToDraw[i];
|
|
_sortedPolygonsIndices.bulk_push_back(
|
|
_staticsIndices.data(),
|
|
currentObject->Polygon->BaseIndex,
|
|
currentObject->Polygon->Shape == 0 ? 6 : 3);
|
|
i++;
|
|
}
|
|
|
|
DrawStaticSorted(object, lastObjectType, view);
|
|
|
|
if (i == view.TransparentObjectsToDraw.size())
|
|
{
|
|
return;
|
|
}
|
|
|
|
i--;
|
|
}
|
|
else if (object->ObjectType == RendererObjectType::MoveableAsStatic)
|
|
{
|
|
while (i < view.TransparentObjectsToDraw.size() &&
|
|
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 &&
|
|
_sortedPolygonsIndices.size() + (view.TransparentObjectsToDraw[i].Polygon->Shape == 0 ? 6 : 3) < MAX_TRANSPARENT_VERTICES)
|
|
{
|
|
RendererSortableObject* currentObject = &view.TransparentObjectsToDraw[i];
|
|
_sortedPolygonsIndices.bulk_push_back(
|
|
_staticsIndices.data(),
|
|
currentObject->Polygon->BaseIndex,
|
|
currentObject->Polygon->Shape == 0 ? 6 : 3);
|
|
i++;
|
|
}
|
|
|
|
DrawMoveableAsStaticSorted(object, lastObjectType, view);
|
|
|
|
if (i == view.TransparentObjectsToDraw.size())
|
|
{
|
|
return;
|
|
}
|
|
|
|
i--;
|
|
}
|
|
else if (object->ObjectType == RendererObjectType::Sprite)
|
|
{
|
|
while (i < view.TransparentObjectsToDraw.size() &&
|
|
view.TransparentObjectsToDraw[i].ObjectType == object->ObjectType &&
|
|
view.TransparentObjectsToDraw[i].Sprite->Type == object->Sprite->Type &&
|
|
view.TransparentObjectsToDraw[i].Sprite->SoftParticle == object->Sprite->SoftParticle &&
|
|
view.TransparentObjectsToDraw[i].Sprite->Sprite->Texture == object->Sprite->Sprite->Texture &&
|
|
view.TransparentObjectsToDraw[i].Sprite->BlendMode == object->Sprite->BlendMode &&
|
|
_sortedPolygonsIndices.size() + 6 < MAX_TRANSPARENT_VERTICES)
|
|
{
|
|
RendererSortableObject* currentObject = &view.TransparentObjectsToDraw[i];
|
|
RendererSpriteToDraw* spr = currentObject->Sprite;
|
|
|
|
Vector3 p0t;
|
|
Vector3 p1t;
|
|
Vector3 p2t;
|
|
Vector3 p3t;
|
|
|
|
Vector2 uv0;
|
|
Vector2 uv1;
|
|
Vector2 uv2;
|
|
Vector2 uv3;
|
|
|
|
if (spr->Type == SpriteType::ThreeD)
|
|
{
|
|
p0t = spr->vtx1;
|
|
p1t = spr->vtx2;
|
|
p2t = spr->vtx3;
|
|
p3t = spr->vtx4;
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
p0t = Vector3(-0.5, 0.5, 0);
|
|
p1t = Vector3(0.5, 0.5, 0);
|
|
p2t = Vector3(0.5, -0.5, 0);
|
|
p3t = Vector3(-0.5, -0.5, 0);
|
|
}
|
|
|
|
uv0 = spr->Sprite->UV[0];
|
|
uv1 = spr->Sprite->UV[1];
|
|
uv2 = spr->Sprite->UV[2];
|
|
uv3 = spr->Sprite->UV[3];
|
|
|
|
Matrix world = GetWorldMatrixForSprite(currentObject->Sprite, _currentMirror, view);
|
|
|
|
Vertex v0;
|
|
v0.Position = Vector3::Transform(p0t, world);
|
|
v0.UV = uv0;
|
|
v0.Color = spr->c1;
|
|
|
|
Vertex v1;
|
|
v1.Position = Vector3::Transform(p1t, world);
|
|
v1.UV = uv1;
|
|
v1.Color = spr->c2;
|
|
|
|
Vertex v2;
|
|
v2.Position = Vector3::Transform(p2t, world);
|
|
v2.UV = uv2;
|
|
v2.Color = spr->c3;
|
|
|
|
Vertex v3;
|
|
v3.Position = Vector3::Transform(p3t, world);
|
|
v3.UV = uv3;
|
|
v3.Color = spr->c4;
|
|
|
|
_sortedPolygonsVertices.push_back(v0);
|
|
_sortedPolygonsVertices.push_back(v1);
|
|
_sortedPolygonsVertices.push_back(v3);
|
|
_sortedPolygonsVertices.push_back(v2);
|
|
_sortedPolygonsVertices.push_back(v3);
|
|
_sortedPolygonsVertices.push_back(v1);
|
|
|
|
i++;
|
|
}
|
|
|
|
DrawSpriteSorted(object, lastObjectType, view);
|
|
|
|
if (i == view.TransparentObjectsToDraw.size())
|
|
{
|
|
return;
|
|
}
|
|
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::DrawRoomSorted(RendererSortableObject* objectInfo, RendererObjectType lastObjectType, RenderView& view)
|
|
{
|
|
SetDepthState(DepthState::Read);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
|
|
ROOM_INFO* nativeRoom = &g_Level.Rooms[objectInfo->Room->RoomNumber];
|
|
|
|
_context->PSSetShader(_psRooms.Get(), nullptr, 0);
|
|
|
|
UINT stride = sizeof(Vertex);
|
|
UINT offset = 0;
|
|
_context->IASetVertexBuffers(0, 1, _roomsVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
_context->IASetInputLayout(_inputLayout.Get());
|
|
|
|
_stRoom.Caustics = (int)(g_Configuration.EnableCaustics && (nativeRoom->flags & ENV_FLAG_WATER));
|
|
_stRoom.AmbientColor = objectInfo->Room->AmbientLight;
|
|
BindRoomLights(view.LightsToDraw);
|
|
_stRoom.Water = (nativeRoom->flags & ENV_FLAG_WATER) != 0 ? 1 : 0;
|
|
_cbRoom.UpdateData(_stRoom, _context.Get());
|
|
|
|
SetScissor(objectInfo->Room->ClipBounds);
|
|
|
|
if (objectInfo->Bucket->Animated == 0)
|
|
{
|
|
_context->VSSetShader(_vsRooms.Get(), nullptr, 0);
|
|
}
|
|
else
|
|
{
|
|
_context->VSSetShader(_vsRoomsAnimatedTextures.Get(), nullptr, 0);
|
|
}
|
|
|
|
SetBlendMode(objectInfo->Bucket->BlendMode);
|
|
SetAlphaTest(AlphaTestMode::None, ALPHA_TEST_THRESHOLD);
|
|
|
|
// Draw geometry
|
|
if (objectInfo->Bucket->Animated)
|
|
{
|
|
BindTexture(TextureRegister::ColorMap,
|
|
&std::get<0>(_animatedTextures[objectInfo->Bucket->Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
BindTexture(TextureRegister::NormalMap,
|
|
&std::get<1>(_animatedTextures[objectInfo->Bucket->Texture]), SamplerStateRegister::AnisotropicClamp);
|
|
|
|
RendererAnimatedTextureSet& set = _animatedTextureSets[objectInfo->Bucket->Texture];
|
|
_stAnimated.NumFrames = set.NumTextures;
|
|
_stAnimated.Type = 0;
|
|
_stAnimated.Fps = set.Fps;
|
|
|
|
for (unsigned char j = 0; j < set.NumTextures; j++)
|
|
{
|
|
if (j >= _stAnimated.Textures.size())
|
|
{
|
|
TENLog("Animated frame " + std::to_string(j) + " is out of bounds, too many frames in sequence.");
|
|
break;
|
|
}
|
|
|
|
_stAnimated.Textures[j].TopLeft = set.Textures[j].UV[0];
|
|
_stAnimated.Textures[j].TopRight = set.Textures[j].UV[1];
|
|
_stAnimated.Textures[j].BottomRight = set.Textures[j].UV[2];
|
|
_stAnimated.Textures[j].BottomLeft = set.Textures[j].UV[3];
|
|
}
|
|
_cbAnimated.UpdateData(_stAnimated, _context.Get());
|
|
}
|
|
else
|
|
{
|
|
BindTexture(TextureRegister::ColorMap, &std::get<0>(_roomTextures[objectInfo->Bucket->Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
BindTexture(TextureRegister::NormalMap,
|
|
&std::get<1>(_roomTextures[objectInfo->Bucket->Texture]), SamplerStateRegister::AnisotropicClamp);
|
|
}
|
|
|
|
_sortedPolygonsIndexBuffer.Update(_context.Get(), _sortedPolygonsIndices, 0, (int)_sortedPolygonsIndices.size());
|
|
_context->IASetIndexBuffer(_sortedPolygonsIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
DrawIndexedTriangles((int)_sortedPolygonsIndices.size(), 0, 0);
|
|
|
|
_numSortedRoomsDrawCalls++;
|
|
_numSortedTriangles += (int)_sortedPolygonsIndices.size() / 3;
|
|
|
|
ResetScissor();
|
|
}
|
|
|
|
void Renderer::DrawItemSorted(RendererSortableObject* objectInfo, RendererObjectType lastObjectType, RenderView& view)
|
|
{
|
|
unsigned int stride = sizeof(Vertex);
|
|
unsigned int offset = 0;
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
_context->IASetInputLayout(_inputLayout.Get());
|
|
|
|
SetDepthState(DepthState::Read);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
SetBlendMode(objectInfo->Bucket->BlendMode);
|
|
SetAlphaTest(AlphaTestMode::None, ALPHA_TEST_THRESHOLD);
|
|
|
|
_context->VSSetShader(_vsItems.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psItems.Get(), nullptr, 0);
|
|
|
|
// Bind main item properties.
|
|
Matrix world = objectInfo->Item->InterpolatedWorld;
|
|
_stItem.World = world;
|
|
_stItem.Color = objectInfo->Item->Color;
|
|
_stItem.AmbientLight = objectInfo->Item->AmbientLight;
|
|
memcpy(_stItem.BonesMatrices, objectInfo->Item->InterpolatedAnimTransforms, sizeof(Matrix) * MAX_BONES);
|
|
|
|
const auto& moveableObj = *_moveableObjects[objectInfo->Item->ObjectID];
|
|
for (int k = 0; k < moveableObj.ObjectMeshes.size(); k++)
|
|
_stItem.BoneLightModes[k] = (int)moveableObj.ObjectMeshes[k]->LightMode;
|
|
|
|
BindMoveableLights(objectInfo->Item->LightsToDraw, objectInfo->Item->RoomNumber, objectInfo->Item->PrevRoomNumber, objectInfo->Item->LightFade);
|
|
_cbItem.UpdateData(_stItem, _context.Get());
|
|
|
|
BindTexture(
|
|
TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[objectInfo->Bucket->Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
BindTexture(
|
|
TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[objectInfo->Bucket->Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
|
|
_sortedPolygonsIndexBuffer.Update(_context.Get(), _sortedPolygonsIndices, 0, (int)_sortedPolygonsIndices.size());
|
|
_context->IASetIndexBuffer(_sortedPolygonsIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
DrawIndexedTriangles((int)_sortedPolygonsIndices.size(), 0, 0);
|
|
|
|
_numSortedMoveablesDrawCalls++;
|
|
_numSortedTriangles += (int)_sortedPolygonsIndices.size() / 3;
|
|
}
|
|
|
|
void Renderer::DrawStaticSorted(RendererSortableObject* objectInfo, RendererObjectType lastObjectType, RenderView& view)
|
|
{
|
|
UINT stride = sizeof(Vertex);
|
|
UINT offset = 0;
|
|
_context->IASetVertexBuffers(0, 1, _staticsVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
_context->IASetInputLayout(_inputLayout.Get());
|
|
|
|
_context->VSSetShader(_vsStatics.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psStatics.Get(), nullptr, 0);
|
|
|
|
Matrix world = objectInfo->Static->World;
|
|
_stStatic.World = world;
|
|
|
|
_stStatic.Color = objectInfo->Static->Color;
|
|
_stStatic.AmbientLight = objectInfo->Room->AmbientLight;
|
|
_stStatic.LightMode = (int)GetStaticRendererObject(objectInfo->Static->ObjectNumber).ObjectMeshes[0]->LightMode;
|
|
BindStaticLights(objectInfo->Static->LightsToDraw);
|
|
_cbStatic.UpdateData(_stStatic, _context.Get());
|
|
|
|
SetDepthState(DepthState::Read);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
SetBlendMode(objectInfo->Bucket->BlendMode);
|
|
SetAlphaTest(AlphaTestMode::None, ALPHA_TEST_THRESHOLD);
|
|
|
|
BindTexture(TextureRegister::ColorMap, &std::get<0>(_staticTextures[objectInfo->Bucket->Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
BindTexture(TextureRegister::NormalMap, &std::get<1>(_staticTextures[objectInfo->Bucket->Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
|
|
_sortedPolygonsIndexBuffer.Update(_context.Get(), _sortedPolygonsIndices, 0, (int)_sortedPolygonsIndices.size());
|
|
_context->IASetIndexBuffer(_sortedPolygonsIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
DrawIndexedTriangles((int)_sortedPolygonsIndices.size(), 0, 0);
|
|
|
|
_numSortedStaticsDrawCalls++;
|
|
_numSortedTriangles += (int)_sortedPolygonsIndices.size() / 3;
|
|
}
|
|
|
|
void Renderer::DrawMoveableAsStaticSorted(RendererSortableObject* objectInfo, RendererObjectType lastObjectType, RenderView& view)
|
|
{
|
|
UINT stride = sizeof(Vertex);
|
|
UINT offset = 0;
|
|
_context->IASetVertexBuffers(0, 1, _moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
_context->IASetInputLayout(_inputLayout.Get());
|
|
|
|
_context->VSSetShader(_vsStatics.Get(), nullptr, 0);
|
|
_context->PSSetShader(_psStatics.Get(), nullptr, 0);
|
|
|
|
Matrix world = objectInfo->World;
|
|
_stStatic.World = world;
|
|
|
|
_stStatic.Color = Vector4::One;
|
|
_stStatic.AmbientLight = objectInfo->Room->AmbientLight;
|
|
_stStatic.LightMode = (int)objectInfo->Mesh->LightMode;
|
|
BindStaticLights(objectInfo->Room->LightsToDraw);
|
|
_cbStatic.UpdateData(_stStatic, _context.Get());
|
|
|
|
SetDepthState(DepthState::Read);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
SetBlendMode(objectInfo->Bucket->BlendMode);
|
|
SetAlphaTest(AlphaTestMode::GreatherThan, ALPHA_TEST_THRESHOLD);
|
|
|
|
BindTexture(TextureRegister::ColorMap, &std::get<0>(_staticTextures[objectInfo->Bucket->Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
BindTexture(TextureRegister::NormalMap, &std::get<1>(_staticTextures[objectInfo->Bucket->Texture]),
|
|
SamplerStateRegister::AnisotropicClamp);
|
|
|
|
_sortedPolygonsIndexBuffer.Update(_context.Get(), _sortedPolygonsIndices, 0, (int)_sortedPolygonsIndices.size());
|
|
_context->IASetIndexBuffer(_sortedPolygonsIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
|
|
|
|
DrawIndexedTriangles((int)_sortedPolygonsIndices.size(), 0, 0);
|
|
|
|
_numSortedStaticsDrawCalls++;
|
|
_numSortedTriangles += (int)_sortedPolygonsIndices.size() / 3;
|
|
}
|
|
|
|
void Renderer::CalculateSSAO(RenderView& view)
|
|
{
|
|
SetBlendMode(BlendMode::Opaque);
|
|
SetCullMode(CullMode::CounterClockwise);
|
|
SetDepthState(DepthState::Write);
|
|
|
|
// Common vertex shader to all full screen effects
|
|
_context->VSSetShader(_vsPostProcess.Get(), nullptr, 0);
|
|
|
|
// SSAO pixel shader
|
|
_context->PSSetShader(_psSSAO.Get(), nullptr, 0);
|
|
|
|
_context->ClearRenderTargetView(_SSAORenderTarget.RenderTargetView.Get(), Colors::White);
|
|
_context->OMSetRenderTargets(1, _SSAORenderTarget.RenderTargetView.GetAddressOf(), nullptr);
|
|
|
|
// Need to set correctly the viewport because SSAO is done at 1/4 screen resolution
|
|
D3D11_VIEWPORT viewport;
|
|
viewport.TopLeftX = 0;
|
|
viewport.TopLeftY = 0;
|
|
viewport.Width = _screenWidth;
|
|
viewport.Height = _screenHeight;
|
|
viewport.MinDepth = 0.0f;
|
|
viewport.MaxDepth = 1.0f;
|
|
|
|
_context->RSSetViewports(1, &viewport);
|
|
|
|
D3D11_RECT rects[1];
|
|
rects[0].left = 0;
|
|
rects[0].right = viewport.Width;
|
|
rects[0].top = 0;
|
|
rects[0].bottom = viewport.Height;
|
|
|
|
_context->RSSetScissorRects(1, rects);
|
|
|
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
_context->IASetInputLayout(_fullscreenTriangleInputLayout.Get());
|
|
|
|
UINT stride = sizeof(PostProcessVertex);
|
|
UINT offset = 0;
|
|
|
|
_context->IASetVertexBuffers(0, 1, _fullscreenTriangleVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
|
|
|
BindRenderTargetAsTexture(static_cast<TextureRegister>(0), &_depthRenderTarget, SamplerStateRegister::PointWrap);
|
|
BindRenderTargetAsTexture(static_cast<TextureRegister>(1), &_normalsRenderTarget, SamplerStateRegister::PointWrap);
|
|
BindTexture(static_cast<TextureRegister>(2), &_SSAONoiseTexture, SamplerStateRegister::PointWrap);
|
|
|
|
_stPostProcessBuffer.ViewportWidth = viewport.Width;
|
|
_stPostProcessBuffer.ViewportHeight = viewport.Height;
|
|
memcpy(_stPostProcessBuffer.SSAOKernel, _SSAOKernel.data(), 16 * _SSAOKernel.size());
|
|
_cbPostProcessBuffer.UpdateData(_stPostProcessBuffer, _context.Get());
|
|
|
|
DrawTriangles(3, 0);
|
|
|
|
// Blur step
|
|
_context->PSSetShader(_psSSAOBlur.Get(), nullptr, 0);
|
|
|
|
_context->ClearRenderTargetView(_SSAOBlurredRenderTarget.RenderTargetView.Get(), Colors::Black);
|
|
_context->OMSetRenderTargets(1, _SSAOBlurredRenderTarget.RenderTargetView.GetAddressOf(), nullptr);
|
|
|
|
BindRenderTargetAsTexture(TextureRegister::SSAO, &_SSAORenderTarget, SamplerStateRegister::PointWrap);
|
|
|
|
DrawTriangles(3, 0);
|
|
}
|
|
|
|
void Renderer::InterpolateCamera(float interpFactor)
|
|
{
|
|
_interpolationFactor = interpFactor;
|
|
|
|
// Interpolate camera.
|
|
if (!Camera.DisableInterpolation)
|
|
{
|
|
_gameCamera.Camera.WorldPosition = Vector3::Lerp(_oldGameCamera.Camera.WorldPosition, _currentGameCamera.Camera.WorldPosition, interpFactor);
|
|
_gameCamera.Camera.WorldDirection = Vector3::Lerp(_oldGameCamera.Camera.WorldDirection, _currentGameCamera.Camera.WorldDirection, interpFactor);
|
|
_gameCamera.Camera.View = Matrix::Lerp(_oldGameCamera.Camera.View, _currentGameCamera.Camera.View, interpFactor);
|
|
_gameCamera.Camera.Projection = Matrix::Lerp(_oldGameCamera.Camera.Projection, _currentGameCamera.Camera.Projection, interpFactor);
|
|
_gameCamera.Camera.ViewProjection = _gameCamera.Camera.View * _gameCamera.Camera.Projection;
|
|
_gameCamera.Camera.FOV = Lerp(_oldGameCamera.Camera.FOV, _currentGameCamera.Camera.FOV, interpFactor);
|
|
_gameCamera.Camera.Frustum.Update(_gameCamera.Camera.View, _gameCamera.Camera.Projection);
|
|
}
|
|
else
|
|
{
|
|
_gameCamera.Camera.WorldPosition = _currentGameCamera.Camera.WorldPosition;
|
|
_gameCamera.Camera.WorldDirection = _currentGameCamera.Camera.WorldDirection;
|
|
_gameCamera.Camera.View = _currentGameCamera.Camera.View;
|
|
_gameCamera.Camera.Projection = _currentGameCamera.Camera.Projection;
|
|
_gameCamera.Camera.ViewProjection = _gameCamera.Camera.View * _gameCamera.Camera.Projection;
|
|
_gameCamera.Camera.FOV = _currentGameCamera.Camera.FOV;
|
|
_gameCamera.Camera.Frustum = _currentGameCamera.Camera.Frustum;
|
|
}
|
|
|
|
_gameCamera.Camera.ViewSize = _currentGameCamera.Camera.ViewSize;
|
|
_gameCamera.Camera.InvViewSize = _currentGameCamera.Camera.InvViewSize;
|
|
_gameCamera.Camera.NearPlane = _currentGameCamera.Camera.NearPlane;
|
|
_gameCamera.Camera.FarPlane = _currentGameCamera.Camera.FarPlane;
|
|
}
|
|
}
|