Cogs.Core
TerrainSystem.cpp
1#include "TerrainSystem.h"
2
3#include "Terrain.h"
4
5#include "Context.h"
6
7#include "Components/Core/RenderComponent.h"
8
9#include "Systems/Core/CameraSystem.h"
10#include "Systems/Core/TransformSystem.h"
11
12#include "Resources/ResourceStore.h"
13#include "Resources/MaterialManager.h"
14
15#include "TerrainRenderer.h"
16#include "OceanSystem.h"
17
18#include "Foundation/Logging/Logger.h"
19#include "Foundation/Platform/IO.h"
20
21using namespace Cogs::Geometry;
22namespace
23{
24 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("TerrainSystem");
25}
26
28{
30
31 VertexElement format{ 0, DataFormat::X32Y32_FLOAT, ElementSemantic::Position, 0, {}, {} };
32 terrainFormat = VertexFormats::createVertexFormat(format);
33
34 if (!terrainRenderer) {
35 terrainRenderer = new TerrainRenderer();
36 context->renderer->registerExtension(terrainRenderer);
37 }
38
39 const auto dependencyDir = IO::expandEnvironmentVariable("CogsDir");
40 context->resourceStore->addSearchPath(dependencyDir + "/");
41
42 context->resourceStore->addSearchPath("../Extensions/Terrain/Data");
43
44 // Register empty custom snippets to avoid getting errors when resolving contents.
45 context->resourceStore->addResource("Shaders/Terrain/CustomComputeColor.hlsl", "");
46 context->resourceStore->addResource("Shaders/Terrain/CustomProcessColor.hlsl", "");
47}
48
50{
51 assert(terrainRenderer);
52 if (context->renderer) {
53 context->renderer->unregisterExtension(terrainRenderer);
54 }
55 delete terrainRenderer;
56 terrainRenderer = nullptr;
58}
59
60
62{
63 for (TerrainComponent& terrain : pool) {
64 auto & terrainData = getData(&terrain);
65
66 if (!terrainData.initialized) {
67 if (context->device->getType() == GraphicsDeviceType::OpenGL20 || context->device->getType() == GraphicsDeviceType::Direct3D11 || context->device->getType() == GraphicsDeviceType::Direct3D12) {
68 terrainInitializeInstance(terrainData.context, context->device);
69
70 terrainData.getBounds = std::make_unique<TerrainBounds>(&terrain, &terrainData);
71 context->bounds->addBoundsExtension(terrainData.getBounds.get());
72
73 terrainData.initialized = true;
74 }
75
76 terrainData.terrainStreamsLayout.numStreams = 1;
77 terrainData.terrainStreamsLayout.vertexFormats[0] = terrainFormat;
78 terrainData.terrainStreamsLayout.updateHash();
79 }
80
81 if (terrainData.initialized) {
82 bool materialChanged = terrainData.depthMaterial != terrain.depthMaterial || terrainData.material != terrain.material;
83
84 auto customTextureKey = terrain.material->material->getTextureKey("customTexture");
85 terrain.material->setTextureProperty(customTextureKey, terrain.customTexture);
86 terrain.material->setTextureAddressMode(customTextureKey, terrain.addressMode);
87
88 terrainData.offset = terrain.getComponent<TransformComponent>()->coordinates;
89
90 terrainUpdate(terrainData.context);
91
93 terrainGetEffectParameters(terrainData.context, &p);
94
95 if (p.changed || materialChanged) {
96 terrainData.depthMaterial = terrain.depthMaterial;
97 terrainData.material = terrain.material;
98
99 terrain.material->setVariant("NumTextures", p.numTextures);
100 terrainData.depthMaterial->setVariant("NumTextures", p.numTextures);
101 terrain.material->setVariant("NumActiveTextures", p.numActiveImagery);
102 terrainData.depthMaterial->setVariant("NumActiveTextures", p.numActiveImagery);
103
104 if (terrainData.customComputeColor.size()) {
105 terrain.material->setVariant("CustomComputeColor", terrainData.customComputeColor);
106 terrainData.depthMaterial->setVariant("CustomComputeColor", terrainData.customComputeColor);
107 }
108
109 if (p.precomputeNormals) {
110 terrain.material->setVariant("PrecomputeNormals", p.precomputeNormals);
111 terrainData.depthMaterial->setVariant("PrecomputeNormals", p.precomputeNormals);
112 }
113 }
114
115 float nearPlaneLimit = terrainGetNearestSample(terrainData.context);
116
117 terrainData.nearPlaneLimit = nearPlaneLimit;
118 }
119 }
120}
121
123{
125 TerrainComponent* terrainPtr = component.resolveComponent<TerrainComponent>();
126 auto & terrainData = getData(terrainPtr);
127 terrainData.context = terrainCreate();
128
129 MaterialHandle terrainMaterial = context->materialManager->loadMaterial("TerrainMaterial.material", MaterialLoadFlags::DelayVariantLoading);
130 context->materialManager->processLoading();
131
132 TerrainComponent& terrainComponent = *terrainPtr;
133 terrainComponent.material = context->materialInstanceManager->createMaterialInstance(terrainMaterial);
134 terrainComponent.material->setPermutation("Normal");
135 terrainComponent.depthMaterial = context->materialInstanceManager->createMaterialInstance(terrainMaterial);
136 terrainComponent.depthMaterial->setPermutation("Depth");
137
138 if (pool.size() == 1) {
139 assert(picker == nullptr);
140 picker = new TerrainPicker(this);
141 context->rayPicking->addPickable(picker);
142
143 LOG_DEBUG(logger, "Registered terrain picker extension");
144 }
145
146 return component;
147}
148
150{
151 auto & terrainData = getData(component.resolveComponent<TerrainComponent>());
152
153 if (terrainData.initialized) {
154 terrainDestroy(terrainData.context);
155
156 context->bounds->removeBoundsExtension(terrainData.getBounds.get());
157 }
158
160
161 if (pool.size() == 0) {
162 assert(picker != nullptr);
163 context->rayPicking->removePickable(picker);
164 delete picker;
165 picker = nullptr;
166
167 LOG_DEBUG(logger, "Last component destroyed, unregistered terrain picker extension");
168 }
169}
170
172 const CameraComponent& camera,
173 const glm::vec2& normPosition,
174 float /*rayLength*/,
175 float /*radius*/,
176 PickingFlags pickingFlags,
177 PicksReturned returnFlag,
178 const RayPicking::RayPickFilter& filter,
179 std::vector<RayPicking::RayPickHit>& hits)
180{
181 // Convert the normalized screen coordinates into [0..1] range which is needed by the terrain engine.
182 glm::vec2 screenPos = normPosition * .5f + .5f;
183
184 auto & cameraData = context->cameraSystem->getData(&camera);
185 for (const TerrainComponent& terrain : terrainSystem->pool) {
186
187 auto& terrainData = terrainSystem->getData(&terrain);
188
189 if (!terrainData.initialized) {
190 return false;
191 }
192
193 const RenderComponent* renderComponent = terrain.getComponent<RenderComponent>();
194 if (renderComponent && !renderComponent->isVisibleInLayer(filter.layerMask) && !filter.isUnwantedType(*renderComponent)) {
195 return false;
196 }
197
198 glm::vec4 pickedCoordinates;
199 if (cameraData.rayPickId == -1 && &camera != context->cameraSystem->getMainCamera())
200 {
201 //Either the camera doesn't support picking or the depth texture hasn't been rendered yet.
202 return false;
203 }
204
205 if (terrainRayPick(terrainData.context, glm::value_ptr(screenPos), glm::value_ptr(cameraData.inverseViewProjectionMatrix), glm::value_ptr(pickedCoordinates), cameraData.rayPickId)) {
206 glm::vec3 coordinates = glm::vec3(pickedCoordinates);
207 glm::vec4 clipPos = cameraData.rawViewProjection * glm::vec4(coordinates, 1.f);
208 if ((-clipPos.w <= clipPos.z) && (clipPos.z <= clipPos.w)) {
209
210 glm::vec4 viewPos = cameraData.viewMatrix * glm::vec4(glm::vec3(coordinates), 1.f);
211 float viewDist = -viewPos.z;
212
213 if (returnFlag == PicksReturned::Closest && !hits.empty()) {
214 if (viewDist < hits[0].distance) {
215 hits[0] = { terrain, (pickingFlags & PickingFlags::ReturnChildEntity) == PickingFlags::ReturnChildEntity, coordinates, viewDist };
216 return true;
217 }
218 // Else the intersection we found is further, so don't report a hit
219 return false;
220 }
221
222 // Returning just the first intersected object should be enough for terrain.
223 hits.emplace_back(terrain, (pickingFlags & PickingFlags::ReturnChildEntity) == PickingFlags::ReturnChildEntity, coordinates, viewDist);
224 return true;
225 }
226 }
227 }
228 return false;
229}
230
231inline void Cogs::Core::TerrainBounds::getBounds(Context * context, Cogs::Geometry::BoundingBox & bounds, float& nearPlaneLimit)
232{
233 auto renderComponent = terrain->getComponent<RenderComponent>();
234
235 if (!renderComponent->isVisible()) {
236 if (terrainData->oceanData) {
237 auto renderComponent_ = terrainData->oceanData->oceanComponent->getComponent<RenderComponent>();
238
239 if (!renderComponent_->isVisible()) return;
240 } else {
241 return;
242 }
243 }
244
245 if (terrainData->initialized) {
246 auto offsetFromOrigin = terrain->getComponent<TransformComponent>()->coordinates - context->transformSystem->getOrigin();
247
248 glm::dvec3 globalBounds[2];
249 terrainGetBounds(terrainData->context, (double *)globalBounds);
250
251 Cogs::BoundingBox terrainBounds = {
252 glm::vec3(globalBounds[0] + offsetFromOrigin),
253 glm::vec3(globalBounds[1] + offsetFromOrigin),
254 };
255
256 if (!isEmpty(terrainBounds)) {
257 //nearPlaneLimit = terrain->getComponent<NearLimitComponent>()->nearPlaneLimit;
258 nearPlaneLimit = terrainData->nearPlaneLimit * 0.85f;
259 bounds += terrainBounds;
260 }
261 }
262}
ComponentType * getComponent() const
Definition: Component.h:159
virtual void cleanup(Context *)
Provided for custom cleanup logic in derived systems.
virtual ComponentHandle createComponent()
Create a new component instance.
Context * context
Pointer to the Context instance the system lives in.
virtual void initialize(Context *context)
Initialize the system.
virtual void destroyComponent(ComponentHandle)
Destroy the component held by the given handle.
void update()
Updates the system state to that of the current frame.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
class IRenderer * renderer
Renderer.
Definition: Context.h:228
std::unique_ptr< class Bounds > bounds
Bounds service instance.
Definition: Context.h:216
std::unique_ptr< class ResourceStore > resourceStore
ResourceStore service instance.
Definition: Context.h:210
virtual void registerExtension(IRendererExtension *extension)=0
Register an extension with the renderer.
virtual void unregisterExtension(IRendererExtension *extension)=0
Unregister an extension with the renderer.
Base component for all rendering content.
constexpr bool isVisibleInLayer(RenderLayers layerMask) const
Check if the entity should be visible in the given layer mask.
void getBounds(Context *context, Cogs::Geometry::BoundingBox &bounds, float &nearPlaneLimit) final
bool pickCamera(Context *context, const CameraComponent &camera, const glm::vec2 &normPosition, float, float, PickingFlags pickingFlags, PicksReturned returnFlag, const RayPicking::RayPickFilter &filter, std::vector< RayPicking::RayPickHit > &hits) override
Do a ray pick from a normalized screen space position in the camera direction and return all hits.
ComponentModel::ComponentHandle createComponent() override
void cleanup(Context *context) override
Provided for custom cleanup logic in derived systems.
void destroyComponent(ComponentHandle component) override
void initialize(Context *context) override
Initialize the system.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
glm::dvec3 getOrigin() const
Gets the Origin offset of the scene.
Log implementation class.
Definition: LogManager.h:139
PicksReturned
  * Options for returning picking hits.
Definition: PickingFlags.h:40
@ Closest
Return just the closest hit.
PickingFlags
Options for COGS picking.
Definition: PickingFlags.h:12
@ ReturnChildEntity
Return ID if sub-entity picked, not set: return root parent entity.
@ DelayVariantLoading
Do not load any material variants, will be specified later.
Contains geometry calculations and generation.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
@ Direct3D12
Graphics device using the Direct3D 12 API.
@ Direct3D11
Graphics device using the Direct3D 11 API.
@ OpenGL20
Graphics device using OpenGL, supporting at least OpenGL 2.0.
@ Position
Position semantic.
Handle to a Component instance.
Definition: Component.h:67
ComponentType * resolveComponent() const
Definition: Component.h:90
bool isUnwantedType(const ComponentModel::Component &comp) const
Helper function used to determine if a given component belongs to an accepted entity type.
Definition: RayPick.cpp:187
RenderLayers layerMask
Limit picking to the specified render layers. Pick all layers by default.
Definition: RayPick.h:70
MaterialInstanceHandle depthMaterial
The terrain material instance used for the depth buffer.
MaterialInstanceHandle material
The terrain material instance.
Vertex element structure used to describe a single data element in a vertex for the input assembler.
Definition: VertexFormat.h:38