#include "Rendering/RenderSystem.h" #include "Collision/Collision.h" #include "Core/Frustum.h" RenderSystem::RenderSystem(SystemParams params, const IRenderer* renderer, RenderFrame* renderFrame, Octree* frustumCullOctree) : System(params) , m_Renderer(renderer) , m_RenderFrame(renderFrame) , m_Octree(frustumCullOctree) { EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &RenderSystem::OnSetCamera); EVENT_SUBSCRIBE_MEMBER(m_EResolutionChanged, &RenderSystem::OnResolutionChanged); EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &RenderSystem::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &RenderSystem::OnPlayerSpawned); m_Camera = new Camera((float)m_Renderer->GetViewportSize().Width / m_Renderer->GetViewportSize().Height, glm::radians(45.f), 0.01f, 5000.f); } RenderSystem::~RenderSystem() { delete m_Camera; } bool RenderSystem::OnResolutionChanged(Events::ResolutionChanged& e) { // Update camera aspect ration on resolution change m_Camera->SetAspectRatio((float)e.NewResolution.Width / e.NewResolution.Height); return true; } bool RenderSystem::OnSetCamera(Events::SetCamera& e) { ComponentWrapper cTransform = e.CameraEntity["Transform"]; ComponentWrapper cCamera = e.CameraEntity["Camera"]; m_Camera->SetFOV(glm::radians((double)cCamera["FOV"])); m_Camera->SetNearClip((double)cCamera["NearClip"]); m_Camera->SetFarClip((double)cCamera["FarClip"]); m_Camera->SetPosition(cTransform["Position"]); m_Camera->SetOrientation(glm::quat((const glm::vec3&)cTransform["Orientation"])); m_CurrentCamera = e.CameraEntity; return true; } void RenderSystem::fillSprites(std::list>& jobs, World* world) { auto sprites = world->GetComponents("Sprite"); if (sprites == nullptr) { return; } for (auto& cSprite : *sprites) { bool visible = cSprite["Visible"]; if (!visible) { continue; } EntityWrapper entity(world, cSprite.EntityID); if (!isEntityVisible(entity)) { continue; } glm::mat4 modelMatrix; // See a sprite is an SpriteIndicator bool isIndicator = false; if (world->HasComponent(entity.ID, "SpriteIndicator")) { auto indicator = entity["SpriteIndicator"]; float minScale = (float)(double)indicator["MinScale"]; bool hasTeam = indicator["VisibleForSingleTeamOnly"]; isIndicator = true; glm::vec3 pos = TransformSystem::AbsolutePosition(entity); EntityWrapper entityTeam; if (hasTeam && (entity.HasComponent("Team") || entity.FirstParentWithComponent("Team").Valid()) && m_LocalPlayer.World != nullptr) { if (!entity.HasComponent("Team")) { entityTeam = entity.FirstParentWithComponent("Team"); } else { entityTeam = entity; } ComponentWrapper& entityTeamComponent = entityTeam["Team"]; ComponentWrapper& localComponent = m_LocalPlayer["Team"]; int entityTeamInt = entityTeamComponent["Team"]; int localComponentInt = localComponent["Team"]; int SpectatorInt = localComponent["Team"].Enum("Spectator"); if (entityTeamInt != localComponentInt && localComponentInt != SpectatorInt) { continue; } } // Code for check if sprite is inside or outside of screen //glm::vec4 projectedPos = m_Camera->ProjectionMatrix() * m_Camera->ViewMatrix() * glm::vec4(pos, 1.0f); //projectedPos /= projectedPos.w; //// Check if inside of outside of screen. //if (projectedPos.x < -1.0f || projectedPos.x > 1.0f || projectedPos.y < -1.0f || projectedPos.y > 1.0f) { // // is outside of screen //} else { // // is inside of screen //} glm::vec3 zAxis = glm::vec3(0.0f, 1.0f, 0.0f); glm::vec3 normal = pos - m_Camera->Position(); //float distance = glm::length(normal); //if (distance < minDistance) { // pos = pos - glm::normalize(normal) * (distance - minDistance); //} else if (distance > maxDistance) { // pos = pos - glm::normalize(normal) * (distance - maxDistance); //} normal.y = 0; normal = glm::normalize(normal); glm::vec3 right = glm::cross(normal, zAxis); glm::vec3 up = glm::cross(right, normal); modelMatrix[0][0] = right.x; modelMatrix[0][1] = right.y; modelMatrix[0][2] = right.z; modelMatrix[0][3] = 0.0f; modelMatrix[1][0] = zAxis.x; modelMatrix[1][1] = zAxis.y; modelMatrix[1][2] = zAxis.z; modelMatrix[1][3] = 0.0f; modelMatrix[2][0] = normal.x; modelMatrix[2][1] = normal.y; modelMatrix[2][2] = normal.z; modelMatrix[2][3] = 0.0f; modelMatrix[3][0] = pos.x; modelMatrix[3][1] = pos.y; modelMatrix[3][2] = pos.z; modelMatrix[3][3] = 1.0f; glm::mat4 tranformationMatrix = modelMatrix * glm::scale(TransformSystem::AbsoluteScale(entity)); glm::vec4 tmp = tranformationMatrix * glm::vec4(glm::vec3(0.5, 0.5, 0), 1.0f); glm::vec2 projectedTopRight = m_Camera->WorldToScreen(glm::vec3(tmp), m_Renderer->GetViewportSize()); tmp = tranformationMatrix * glm::vec4(glm::vec3(-0.5, -0.5, 0), 1.0f); glm::vec2 projectedBottomLeft = m_Camera->WorldToScreen(glm::vec3(tmp), m_Renderer->GetViewportSize()); float diag = glm::length(projectedBottomLeft - projectedTopRight); if (diag < minScale) { tranformationMatrix = tranformationMatrix * glm::scale(glm::vec3(minScale / diag, minScale / diag, minScale / diag)); } modelMatrix = tranformationMatrix; } else { modelMatrix = TransformSystem::ModelMatrix(entity.ID, world); } std::string diffuseResource = cSprite["DiffuseTexture"]; std::string glowResource = cSprite["GlowMap"]; bool depthSorted = cSprite["DepthSort"]; if (diffuseResource.empty() && glowResource.empty()) { continue; } float fillPercentage = 0.f; glm::vec4 fillColor = glm::vec4(0); if (world->HasComponent(entity.ID, "Fill")) { auto fillComponent = world->GetComponent(entity.ID, "Fill"); fillPercentage = (float)(double)fillComponent["Percentage"]; fillColor = (glm::vec4)fillComponent["Color"]; } std::shared_ptr spriteJob = std::shared_ptr(new SpriteJob(cSprite, m_Camera, modelMatrix, world, fillColor, fillPercentage, depthSorted, isIndicator)); jobs.push_back(spriteJob); } } bool RenderSystem::isEntityVisible(EntityWrapper& entity) { // Only render children of a camera if that camera is currently active if (isChildOfACamera(entity) && !isChildOfCurrentCamera(entity)) { return false; } // Hide things parented to local player if they have the HiddenFromLocalPlayer component bool outOfBodyExperience = false; // ResourceManager::Load("Config.ini")->Get("Debug.OutOfBodyExperience", false); if ( (entity.HasComponent("HiddenForLocalPlayer") || entity.FirstParentWithComponent("HiddenForLocalPlayer").Valid()) && (entity == m_LocalPlayer || entity.IsChildOf(m_LocalPlayer)) && !outOfBodyExperience ) { return false; } return true; } bool RenderSystem::isChildOfACamera(EntityWrapper entity) { return entity.FirstParentWithComponent("Camera").Valid(); } bool RenderSystem::isChildOfCurrentCamera(EntityWrapper entity) { return entity == m_CurrentCamera || entity.IsChildOf(m_CurrentCamera); } void RenderSystem::fillModels(RenderScene::Queues &Jobs) { Frustum frustum(m_Camera->ProjectionMatrix() * m_Camera->ViewMatrix()); std::vector seenEntities; m_Octree->ObjectsInFrustum(frustum, seenEntities); for (auto& seenEntity : seenEntities) { EntityWrapper entity = seenEntity.Entity; ComponentWrapper cModel = entity["Model"]; bool visible = cModel["Visible"]; if (!visible) { continue; } std::string resource = cModel["Resource"]; if (resource.empty()) { continue; } if (!isEntityVisible(entity)) { continue; } Model* model; try { model = ResourceManager::Load<::Model, true>(resource); } catch (const Resource::StillLoadingException&) { //continue; model = ResourceManager::Load<::Model>("Models/Core/UnitRaptor.mesh"); } catch (const std::exception&) { try { model = ResourceManager::Load<::Model>("Models/Core/Error.mesh"); } catch (const std::exception&) { continue; } } float fillPercentage = 0.f; glm::vec4 fillColor = glm::vec4(0); if(m_World->HasComponent(cModel.EntityID, "Fill")) { auto fillComponent = m_World->GetComponent(cModel.EntityID, "Fill"); fillPercentage = (float)(double)fillComponent["Percentage"]; fillColor = (glm::vec4)fillComponent["Color"]; } bool isShielded = m_World->HasComponent(cModel.EntityID, "Shielded") || m_World->HasComponent(cModel.EntityID, "Player"); glm::mat4 modelMatrix = TransformSystem::ModelMatrix(cModel.EntityID, m_World); //Loop through all materialgroups of a model for (auto matGroup : model->MaterialGroups()) { //If the model has an explosioneffect component, we will add an explosioneffectjob if (m_World->HasComponent(cModel.EntityID, "ExplosionEffect")) { auto explosionEffectComponent = m_World->GetComponent(cModel.EntityID, "ExplosionEffect"); std::shared_ptr explosionEffectJob = std::shared_ptr(new ExplosionEffectJob( explosionEffectComponent, model, m_Camera, modelMatrix, matGroup, cModel, m_World, fillColor, fillPercentage, isShielded, false )); if (m_World->HasComponent(cModel.EntityID, "Shield")){ explosionEffectJob->CalculateHash(); Jobs.ShieldObjects.push_back(explosionEffectJob); } else if (isShielded) { if (explosionEffectJob->Color.a != 1.f || explosionEffectJob->EndColor.a != 1.f || explosionEffectJob->DiffuseColor.a != 1.f) { cModel["Transparent"] = true; } if (cModel["Transparent"]) { Jobs.TransparentObjects.push_back(explosionEffectJob); } else { explosionEffectJob->CalculateHash(); Jobs.OpaqueShieldedObjects.push_back(explosionEffectJob); } } else { if (explosionEffectJob->Color.a != 1.f || explosionEffectJob->EndColor.a != 1.f || explosionEffectJob->DiffuseColor.a != 1.f) { cModel["Transparent"] = true; } if (cModel["Transparent"]) { Jobs.TransparentObjects.push_back(explosionEffectJob); } else { explosionEffectJob->CalculateHash(); Jobs.OpaqueObjects.push_back(explosionEffectJob); } } } else { std::shared_ptr modelJob = std::shared_ptr(new ModelJob( model, m_Camera, modelMatrix, matGroup, cModel, m_World, fillColor, fillPercentage, isShielded, (bool)cModel["Shadow"] )); if (m_World->HasComponent(cModel.EntityID, "Shield")) { modelJob->CalculateHash(); Jobs.ShieldObjects.push_back(modelJob); } else if (isShielded) { if (modelJob->Color.a != 1.f || modelJob->DiffuseColor.a != 1.f) { cModel["Transparent"] = true; } if (cModel["Transparent"]) { Jobs.TransparentObjects.push_back(modelJob); } else { modelJob->CalculateHash(); Jobs.OpaqueShieldedObjects.push_back(modelJob); } } else { if (modelJob->Color.a != 1.f || modelJob->DiffuseColor.a != 1.f) { cModel["Transparent"] = true; } if (cModel["Transparent"]) { Jobs.TransparentObjects.push_back(modelJob); } else { modelJob->CalculateHash(); Jobs.OpaqueObjects.push_back(modelJob); } } } } } } bool RenderSystem::OnPlayerSpawned(Events::PlayerSpawned& e) { if (e.PlayerID == -1) { m_LocalPlayer = e.Player; } return true; } void RenderSystem::fillPointLights(std::list>& jobs, World* world) { auto pointLights = m_World->GetComponents("PointLight"); if (pointLights != nullptr) { for (auto& pointlightC : *pointLights) { bool visible = pointlightC["Visible"]; if (!visible) { continue; } auto transformC = world->GetComponent(pointlightC.EntityID, "Transform"); if (&transformC == nullptr) { continue; } std::shared_ptr pointLightJob = std::shared_ptr(new PointLightJob(transformC, pointlightC, m_World)); jobs.push_back(pointLightJob); } } } void RenderSystem::fillDirectionalLights(std::list>& jobs, World* world) { auto directionalLights = world->GetComponents("DirectionalLight"); if (directionalLights != nullptr) { for (auto& directionalLightC : *directionalLights) { bool visable = directionalLightC["Visible"]; if (!visable) { continue; } auto transformC = world->GetComponent(directionalLightC.EntityID, "Transform"); if (&transformC == nullptr) { continue; } std::shared_ptr directionalLightJob = std::shared_ptr(new DirectionalLightJob(transformC, directionalLightC, m_World)); jobs.push_back(directionalLightJob); } } } void RenderSystem::fillText(std::list>& jobs, World* world) { auto texts = world->GetComponents("Text"); if (texts == nullptr) { return; } for (auto& textComponent : *texts) { bool visible = textComponent["Visible"]; if (!visible) { continue; } std::string resource = textComponent["Resource"]; if (resource.empty()) { continue; } EntityWrapper entity(world, textComponent.EntityID); if (!isEntityVisible(entity)) { continue; } Font* font; try { font = ResourceManager::Load(resource); } catch (const std::exception&) { try { font = ResourceManager::Load("Fonts/DroidSans.ttf,16"); } catch (const std::exception&) { continue; } } glm::mat4 modelMatrix = TransformSystem::ModelMatrix(textComponent.EntityID, world); std::shared_ptr textJob = std::shared_ptr(new TextJob(modelMatrix, font, textComponent)); jobs.push_back(textJob); } } bool RenderSystem::OnInputCommand(const Events::InputCommand& e) { return false; } void RenderSystem::Update(double dt) { m_EventBroker->Process(); // Update the current camera used for rendering if (m_CurrentCamera.Valid()) { m_Camera->SetPosition(TransformSystem::AbsolutePosition(m_CurrentCamera)); m_Camera->SetOrientation(TransformSystem::AbsoluteOrientation(m_CurrentCamera)); } RenderScene scene; scene.ShouldBlur = true; scene.Camera = m_Camera; scene.Viewport = Rectangle(1280, 720); auto cSceneLight = m_World->GetComponents("SceneLight"); if (cSceneLight != nullptr && cSceneLight->begin() != cSceneLight->end()) { m_RenderFrame->Gamma = (double)(*cSceneLight->begin())["Gamma"]; m_RenderFrame->Exposure = (double)(*cSceneLight->begin())["Exposure"]; scene.AmbientColor = (glm::vec4)(*cSceneLight->begin())["AmbientColor"]; } fillModels(scene.Jobs); fillPointLights(scene.Jobs.PointLight, m_World); //TODO: Make sure all objects needed are also sorted. scene.Jobs.OpaqueObjects.sort([](auto& a, auto& b) {return *a < *b; }); fillSprites(scene.Jobs.SpriteJob, m_World); fillDirectionalLights(scene.Jobs.DirectionalLight, m_World); fillText(scene.Jobs.Text, m_World); m_RenderFrame->Add(scene); }