Chira Engine
A customizable MIT-licensed game engine.
Viewport.cpp
1 #include "Viewport.h"
2 
3 #include <core/Assertions.h>
4 #include <render/shader/UBO.h>
5 #include <utility/Types.h>
6 #include "component/AudioSpeechComponent.h"
7 #include "component/BillboardComponent.h"
8 #include "component/MeshComponent.h"
9 #include "component/MeshDynamicComponent.h"
10 #include "component/SkyboxComponent.h"
11 #include "component/TagComponents.h"
12 #include "component/TransformComponent.h"
13 #include "component/MeshSpriteComponent.h"
14 #include "component/LayerComponents.h"
15 
16 using namespace chira;
17 
18 Viewport::Viewport(glm::vec2i size_, ColorRGB backgroundColor_, bool linearFiltering_)
19  : size(size_)
20  , backgroundColor(backgroundColor_)
21  , linearFiltering(linearFiltering_) {
22  this->recreateFrameBuffer();
23 }
24 
25 Scene* Viewport::addScene() {
26  auto uuid = UUIDGenerator::getNewUUID();
27  return this->addScene(uuid);
28 }
29 
30 Scene* Viewport::addScene(const std::string& name) {
31  auto* scene = this->addScene();
32  scene->getRegistry().emplace<NameComponent>(scene->getRawHandle(), name);
33  return scene;
34 }
35 
36 Scene* Viewport::addScene(uuids::uuid uuid) {
37  runtime_assert(!this->scenes.contains(uuid), "Scene UUID is already present!");
38  this->scenes[uuid] = std::unique_ptr<Scene>(new Scene{});
39  auto& scene = this->scenes.at(uuid);
40  scene->getRegistry().get<UUIDComponent>(scene->getRawHandle()).uuid = uuid;
41  return scene.get();
42 }
43 
44 Scene* Viewport::addScene(uuids::uuid uuid, const std::string& name) {
45  auto* scene = this->addScene(uuid);
46  scene->getRegistry().emplace<NameComponent>(scene->getRawHandle(), name);
47  return scene;
48 }
49 
50 Scene* Viewport::getScene(uuids::uuid sceneID) {
51  if (this->hasScene(sceneID)) {
52  return nullptr;
53  }
54  return this->scenes.at(sceneID).get();
55 }
56 
57 bool Viewport::hasScene(uuids::uuid sceneID) {
58  return this->scenes.contains(sceneID);
59 }
60 
61 void Viewport::removeScene(uuids::uuid sceneID) {
62  runtime_assert(this->hasScene(sceneID), "Trying to remove a scene with a UUID that doesn't exist!");
63  this->scenes.erase(sceneID);
64 }
65 
66 void Viewport::removeAllScenes() {
67  for (const auto& [uuid, scene] : this->scenes) {
68  this->removeScene(uuid);
69  }
70 }
71 
72 void Viewport::update() {
73  for (const auto& [uuid, scene] : this->scenes) {
74  if (auto* camera = this->getCamera()) {
75  // Update BillboardComponent
76  auto billboardView = scene->getEntities<BillboardComponent>();
77  for (const auto [entity, billboardComponent] : billboardView.each()) {
78  if (billboardComponent.x)
79  billboardComponent.transform->setPitch(camera->transform->getPitch());
80  if (billboardComponent.y)
81  billboardComponent.transform->setYaw(camera->transform->getYaw());
82  if (billboardComponent.z)
83  billboardComponent.transform->setRoll(camera->transform->getRoll());
84  }
85  }
86 
87 #if 0
88  // Update AngelScriptComponent
89  auto angelScriptView = scene->getEntities<AngelScriptComponent>();
90  for (const auto [entity, angelScriptComponent] : angelScriptView.each()) {
91  angelScriptComponent.update();
92  }
93 #endif
94  }
95 }
96 
97 void Viewport::render() {
98  Renderer::setClearColor({this->backgroundColor, 1.f});
99  Renderer::pushFrameBuffer(this->frameBufferHandle);
100 
101  // Set up lighting
102  DirectionalLightComponent* directionalLightComponentArray[DIRECTIONAL_LIGHT_MAX] {nullptr};
103  int directionalLightsCount = 0;
104  PointLightComponent* pointLightComponentArray[POINT_LIGHT_MAX] {nullptr};
105  int pointLightsCount = 0;
106  SpotLightComponent* spotLightComponentArray[SPOT_LIGHT_MAX] {nullptr};
107  int spotLightsCount = 0;
108 
109  for (const auto& [uuid, scene] : this->scenes) {
110  auto directionalLightsView = scene->getRegistry().view<DirectionalLightComponent>();
111  for (auto [entity, directionalLightComponent] : directionalLightsView.each()) {
112  if (directionalLightsCount < DIRECTIONAL_LIGHT_MAX) {
113  directionalLightComponentArray[directionalLightsCount] = &directionalLightComponent;
114  }
115  directionalLightsCount++;
116  }
117 
118  auto pointLightsView = scene->getRegistry().view<PointLightComponent>();
119  for (auto [entity, pointLightComponent] : pointLightsView.each()) {
120  if (pointLightsCount < POINT_LIGHT_MAX) {
121  pointLightComponentArray[pointLightsCount] = &pointLightComponent;
122  }
123  pointLightsCount++;
124  }
125 
126  auto spotLightsView = scene->getRegistry().view<SpotLightComponent>();
127  for (auto [entity, spotLightComponent] : spotLightsView.each()) {
128  if (spotLightsCount < SPOT_LIGHT_MAX) {
129  spotLightComponentArray[spotLightsCount] = &spotLightComponent;
130  }
131  spotLightsCount++;
132  }
133  }
134  LightsUBO::get().update(directionalLightComponentArray, pointLightComponentArray, spotLightComponentArray,
135  {directionalLightsCount, pointLightsCount, spotLightsCount});
136 
137  foreach(LAYER_COMPONENTS, [&](auto layer) {
138  if (!(this->getCamera()->activeLayers & layer.index)) {
139  return;
140  }
141 
142  using CurrentLayer = decltype(layer);
143  for (const auto& [uuid, scene] : this->scenes) {
144  auto& registry = scene->getRegistry();
145 
146  // Set up camera
147  scene->setupForRender(this->size);
148 
149  auto meshView = scene->template getEntities<MeshComponent, CurrentLayer>(entt::exclude<NoRenderTagComponent>);
150  for (auto entity : meshView) {
151  auto& transformComponent = registry.template get<TransformComponent>(entity);
152  auto& meshComponent = registry.template get<MeshComponent>(entity);
153  meshComponent.mesh->render(transformComponent.getMatrix());
154  }
155 
156  // Render MeshDynamicComponent
157  auto meshDynamicView = scene->template getEntities<MeshDynamicComponent, CurrentLayer>(entt::exclude<NoRenderTagComponent>);
158  for (auto entity : meshDynamicView) {
159  auto& transformComponent = registry.template get<TransformComponent>(entity);
160  auto& meshDynamicComponent = registry.template get<MeshDynamicComponent>(entity);
161  meshDynamicComponent.meshBuilder.render(transformComponent.getMatrix());
162  }
163 
164  // Render MeshSpriteComponent
165  auto meshSpriteView = scene->template getEntities<MeshSpriteComponent, CurrentLayer>(entt::exclude<NoRenderTagComponent>);
166  for (auto entity : meshSpriteView) {
167  auto& transformComponent = registry.template get<TransformComponent>(entity);
168  auto& meshSpriteComponent = registry.template get<MeshSpriteComponent>(entity);
169  meshSpriteComponent.sprite.render(transformComponent.getMatrix());
170  }
171  }
172  });
173 
174  // Render scenes
175  for (const auto& [uuid, scene] : this->scenes) {
176  auto& registry = scene->getRegistry();
177 
178  // Render SkyboxComponent
179  auto skyboxView = registry.view<SkyboxComponent>();
180  if (!skyboxView.empty()) {
181  auto& skyboxComponent = skyboxView.get<SkyboxComponent>(skyboxView.front());
182  skyboxComponent.skybox.render(glm::identity<glm::mat4>());
183  }
184  }
185  Renderer::popFrameBuffer();
186 }
187 
188 void Viewport::recreateFrameBuffer() {
189  if (this->frameBufferHandle) {
190  Renderer::destroyFrameBuffer(this->frameBufferHandle);
191  }
192  this->frameBufferHandle = Renderer::createFrameBuffer(this->size.x, this->size.y, WrapMode::REPEAT, WrapMode::REPEAT,
193  this->linearFiltering ? FilterMode::LINEAR : FilterMode::NEAREST, true);
194 }
void setPitch(float pitch)
Takes an angle in radians.