Cogs.Core
NormalUpdater.cpp
1#include "NormalUpdater.h"
2#include "ClipmapUpdate.h"
3#include "Effects.h"
4#include "RenderContext.h"
5#include "Raster/RasterLevel.h"
6
7#include "Rendering/IGraphicsDevice.h"
8#include "Rendering/IEffects.h"
9#include "Rendering/IContext.h"
10#include "Rendering/IBuffers.h"
11
12#include "Foundation/Logging/Logger.h"
13
14#include <cassert>
15
16namespace
17{
18 Cogs::Logging::Log logger = Cogs::Logging::getLogger("NormalUpdater");
19}
20
21namespace Cogs
22{
24 {
25 Matrix projectionMatrix;
26 Vector2 oneOverHeightMapSize;
27
28 float heightExaggeration;
29 float postDelta;
30 };
31
33 {
34 Vector2 updateSize;
35 Vector2 origin;
36 };
37}
38
39void Cogs::NormalUpdater::initialize(IGraphicsDevice * device)
40{
41 VertexElement positionElement = { 0, DataFormat::X32Y32_FLOAT, ElementSemantic::Position, 0, InputType::VertexData, 0 };
42 VertexFormatHandle positionFormat = device->getBuffers()->createVertexFormat(&positionElement, 1);
43 vertexBufferHandle = device->getBuffers()->loadVertexBuffer(quadVertices, 6, positionFormat);
44
45 IEffects * effects = device->getEffects();
46
47 LOG_DEBUG(logger, "Loading climpap normal update effect.");
48
49 computeNormalsEffectHandle = Terrain::EffectLoader::loadEffect(effects, "ClipmapComputeNormalsVS", "ClipmapComputeNormalsPS");
50
51 if (!HandleIsValid(computeNormalsEffectHandle)) {
52 LOG_ERROR(logger, "Error loading normal computation effect.");
53
54 return;
55 }
56 assert(effects->checkEffect(computeNormalsEffectHandle) == Cogs::ResourceStatus::Ready && "Expects synchronous effect loading");
57
58 auto buffers = device->getBuffers();
59
60 computeNormalsLayoutHandle = device->getBuffers()->loadInputLayout(&positionFormat, 1, computeNormalsEffectHandle);
61
62 normalLevelBuffer = buffers->loadBuffer(nullptr, sizeof(NormalLevelParameters), Usage::Dynamic, AccessMode::Write, BindFlags::ConstantBuffer);
63 normalLevelBinding = effects->getConstantBufferBinding(computeNormalsEffectHandle, "NormalLevelParameters");
64
65 normalUpdateBuffer = buffers->loadBuffer(nullptr, sizeof(NormalUpdateParameters), Usage::Dynamic, AccessMode::Write, BindFlags::ConstantBuffer);
66 normalUpdateBinding = effects->getConstantBufferBinding(computeNormalsEffectHandle, "NormalUpdateParameters");
67
68 SamplerState nearestRepeatState = {
73 SamplerState::ComparisonFunction::Never,
74 0,
75 { 0.0f, 0.0f, 0.0f, 0.0f }
76 };
77
78 nearestRepeatStateHandle = device->getTextures()->loadSamplerState(nearestRepeatState);
79 samplerStateBinding = effects->getSamplerStateBinding(computeNormalsEffectHandle, std::string(), 0);
80
81 levelTextureBinding = effects->getTextureBinding(computeNormalsEffectHandle, "heightTexture", 0);
82}
83
84void Cogs::NormalUpdater::updateNormalLevel(RenderContext & context, const WorldOptions & worldOptions, const ClipmapUpdate & clipmapUpdate, ClipmapLevel & normalLevel)
85{
86 // Normals at edges are incorrect, so include a one-post buffer around the update region
87 // when updating normals in order to update normals that were previously at the edge.
88 ClipmapUpdate updateWithBuffer = clipmapUpdate.addBufferWithinLevelNextExtent();
89
90 static std::vector<ClipmapUpdate> normalUpdates;
91
92 normalUpdates.clear();
93
94 ClipmapUpdate::splitUpdateToAvoidWrapping(updateWithBuffer, normalUpdates);
95
96 renderNormals(context, worldOptions, normalUpdates, normalLevel);
97}
98
99void Cogs::NormalUpdater::renderNormals(RenderContext & renderContext, const WorldOptions & worldOptions, const std::vector<ClipmapUpdate> & normalUpdates, ClipmapLevel & normalLevel)
100{
101 if (!normalUpdates.size() || !HandleIsValid(computeNormalsEffectHandle)) return;
102
103 IContext * context = renderContext.context;
104
105 const ClipmapLevel * terrainLevel = normalUpdates[0].getLevel();
106 auto & nextExtent = terrainLevel->nextExtent;
107
108 const float width = static_cast<float>(normalLevel.renderTexture.width);
109 const float height = static_cast<float>(normalLevel.renderTexture.height);
110
111 context->setRenderTarget(normalLevel.renderTexture.renderTarget, DepthStencilHandle::InvalidHandle);
112 context->setViewport(0, 0, width, height);
113
114 context->setEffect(computeNormalsEffectHandle);
115
116 context->setTexture(levelTextureBinding, terrainLevel->renderTexture.handle);
117 context->setSamplerState(samplerStateBinding, nearestRepeatStateHandle);
118
119 const uint32_t strides[] = { 2 * sizeof(float) };
120 context->setVertexBuffers(&vertexBufferHandle, 1, strides, nullptr);
121 context->setInputLayout(computeNormalsLayoutHandle);
122
123 context->setConstantBuffer(normalLevelBinding, normalLevelBuffer);
124 context->setConstantBuffer(normalUpdateBinding, normalUpdateBuffer);
125
126 {
127 MappedBuffer<NormalLevelParameters> levelParameters(context, normalLevelBuffer, MapMode::WriteDiscard);
128
129 if (levelParameters) {
130 levelParameters->heightExaggeration = worldOptions.heightExaggeration;
131
132 levelParameters->projectionMatrix = glm::ortho<float>(0, static_cast<float>(nextExtent.east - nextExtent.west), 0, static_cast<float>(nextExtent.north - nextExtent.south), -1.0f, 1.0f);
133
134 levelParameters->oneOverHeightMapSize = Vector2(
135 1.0f / (float) (terrainLevel->nextExtent.east - terrainLevel->nextExtent.west + 1),
136 1.0f / (float) (terrainLevel->nextExtent.north - terrainLevel->nextExtent.south + 1));
137
138 levelParameters->postDelta = static_cast<float>(terrainLevel->rasterLevel->getPostDeltaLongitude() * worldOptions.worldScale.x);
139 }
140 }
141
142 const int clipmapSize = terrainLevel->nextExtent.east - terrainLevel->nextExtent.west + 1;
143
144 for (size_t i = 0; i < normalUpdates.size(); ++i) {
145 const ClipmapUpdate & normalUpdate = normalUpdates[i];
146
147 const int west = (terrainLevel->origin.x + (normalUpdate.getWest() - terrainLevel->nextExtent.west)) % clipmapSize;
148 const int south = (terrainLevel->origin.y + (normalUpdate.getSouth() - terrainLevel->nextExtent.south)) % clipmapSize;
149
150 {
151 MappedBuffer<NormalUpdateParameters> updateParameters(context, normalUpdateBuffer, MapMode::WriteDiscard);
152
153 if (updateParameters) {
154 updateParameters->origin = Vector2(static_cast<float>(west), static_cast<float>(south));
155 updateParameters->updateSize = Vector2(static_cast<float>(normalUpdate.getWidth()), static_cast<float>(normalUpdate.getHeight()));
156 }
157 }
158
159 context->draw(PrimitiveType::TriangleList, 0, 6);
160 }
161}
Log implementation class.
Definition: LogManager.h:140
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
@ Ready
The resource has loaded successfully and is ready for use.
@ VertexData
Per vertex data.
@ TriangleList
List of triangles.
@ Position
Position semantic.
@ Write
The buffer can be mapped and written to by the CPU after creation.
Definition: Flags.h:50
@ ConstantBuffer
The buffer can be bound as input to effects as a constant buffer.
Definition: Flags.h:72
static const Handle_t InvalidHandle
Represents an invalid handle.
Definition: Common.h:81
@ WriteDiscard
Write access. When unmapping the graphics system will discard the old contents of the resource.
Definition: Flags.h:103
@ Wrap
Texture coordinates automatically wrap around to [0, 1] range.
Definition: SamplerState.h:19
@ MinMagMipPoint
Point sampling for both minification and magnification.
Definition: SamplerState.h:33
@ Dynamic
Buffer will be loaded and modified with some frequency.
Definition: Flags.h:30