Cogs.Core
InstancedMeshBuilderSystem.cpp
1#include "InstancedMeshBuilderSystem.h"
2#include "Components/Core/InstancedMeshRenderComponent.h"
3
4#include "Systems/Core/CameraSystem.h"
5#include "Systems/Core/TransformSystem.h"
6
7#include "Resources/Mesh.h"
8#include "Resources/MeshManager.h"
9#include "Resources/VertexFormats.h"
10
11#include "Services/DPIService.h"
12
13#include "Foundation/HashSequence.h"
14#include "Foundation/Geometry/Glm.hpp"
15
16#include "Context.h"
17
18using namespace Cogs::Core;
19
20namespace {
21
22 void buildMatrices(std::span<glm::mat4> matrices,
23 std::span<const glm::vec3> positions,
24 std::span<const glm::quat> rotations,
25 std::span<const glm::vec3> scales,
26 size_t instanceCount)
27 {
28 for (size_t i = 0; i < instanceCount; ++i) {
29 const glm::vec3 position = !positions.empty() ? positions[i] : glm::vec3(0.0f);
30 const glm::quat rotation = !rotations.empty() ? rotations[i] : glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
31 const glm::vec3 scale = !scales.empty() ? scales[i] : glm::vec3(1.0f);
32 matrices[i] = glm::translate(glm::mat4(1.0f), position) * glm::mat4_cast(rotation) * glm::scale(glm::mat4(1.0f), scale);
33 }
34 }
35
36 size_t scaleMatrices(std::span<glm::mat4> matricesOut,
37 std::span<glm::vec4> colorsOut,
38 std::span<const glm::mat4> matricesIn,
39 std::span<const glm::vec4> colorsIn,
40 const glm::mat4& localToWorld,
41 const glm::mat4& viewMatrix,
42 const glm::mat4& rawProjectionMatrix,
43 const float viewportHeight,
44 const float dpiScale,
45 const float maxDistance)
46 {
47 assert(matricesOut.size() == matricesIn.size());
48 assert(colorsOut.size() == colorsIn.size());
49
50 const glm::mat4 M = viewMatrix * localToWorld;
51 const glm::vec4 M_row2 = glm::transpose(M)[2];
52 const glm::vec4 PM_row3 = glm::transpose(rawProjectionMatrix * M)[3];
53 const float pixelScale = 2.0f * dpiScale / (rawProjectionMatrix[1][1] * viewportHeight);
54
55 size_t activeCount = 0;
56 for (size_t i = 0, N = matricesIn.size(); i < N; i++) {
57 glm::mat4 m = matricesIn[i];
58 const glm::vec4 pos = glm::vec4(glm::vec3(m[3]), 1.0f);
59
60 const float camZ = dot(M_row2, pos);
61 if (0 < maxDistance && camZ < -maxDistance) continue;
62
63 const float clipW = glm::dot(PM_row3, pos);
64 const float scale = pixelScale * clipW;
65 if (scale <= 0.f) continue;
66
67 matricesOut[activeCount] = glm::mat4(scale*m[0], scale*m[1], scale*m[2], m[3]);
68 if (!colorsIn.empty()) {
69 colorsOut[activeCount] = colorsIn[i];
70 }
71
72 activeCount++;
73 }
74 return activeCount;
75 }
76
77}
78
80{
81 const CameraData& mainCamData = context->cameraSystem->getMainCameraData();
82 const float dpiScale = context->getDefaultView()->dpiService->getScaleFactor();
83
84 for (const InstancedMeshBuilderComponent& comp : pool) {
85 InstancedMeshBuilderData& data = getData(&comp);
86
87 bool forceUpdate = false;
88
89 if (comp.hasChanged()) {
90 size_t positionCount = comp.positions.size();
91 size_t rotationCount = comp.rotations.size();
92 size_t scaleCount = comp.scales.size();
93 size_t colorCount = comp.colors.size();
94
95 data.instanceCount = 0;
96 for (size_t count : { positionCount, rotationCount, scaleCount, colorCount }) {
97 if (count) {
98 data.instanceCount = data.instanceCount ? std::min(data.instanceCount, count) : count;
99 }
100 }
101
102 if (data.instanceCount == 0) {
103 data.instanceMesh = MeshHandle::NoHandle;
104 data.useMatrix = false;
105 data.usePositions = false;
106 data.useColors = false;
107 data.dynamicScaling = false;
108 data.matrices.resize(0, false);
109 }
110 else {
111 if (!data.instanceMesh) {
112 data.instanceMesh = context->meshManager->create();
113 }
114 data.instanceMesh->clear();
115
116 data.useColors = colorCount;
117 data.dynamicScaling = comp.fixedSize;
118 if (data.dynamicScaling) {
119 data.useMatrix = true;
120 data.usePositions = false;
121 data.matrices.resize(sizeof(glm::mat4)*data.instanceCount, false);
122 buildMatrices(std::span(static_cast<glm::mat4*>(data.matrices.data()), data.instanceCount),
123 std::span(comp.positions.data(), comp.positions.size()),
124 std::span(comp.rotations.data(), comp.rotations.size()),
125 std::span(comp.scales.data(), comp.scales.size()),
126 data.instanceCount);
127 forceUpdate = true;
128 }
129 else {
130 data.useMatrix = rotationCount > 0 || scaleCount > 0;
131 data.usePositions = !data.useMatrix && positionCount;
132 if (data.useMatrix) {
133 MappedStream<glm::mat4> matrices = data.instanceMesh->map<glm::mat4>(VertexDataType::Positions, VertexFormats::InstanceMat4f, 0, data.instanceCount, true);
134 buildMatrices(std::span(matrices.data, data.instanceCount),
135 std::span(comp.positions.data(), comp.positions.size()),
136 std::span(comp.rotations.data(), comp.rotations.size()),
137 std::span(comp.scales.data(), comp.scales.size()),
138 data.instanceCount);
139 }
140 if (data.usePositions) {
141 data.instanceMesh->setInstancePositions(std::span<const glm::vec3>(comp.positions.data(), data.instanceCount));
142 }
143 if (data.useColors) {
144 data.instanceMesh->setInstanceColors(std::span<const glm::vec4>(comp.colors.data(), data.instanceCount));
145 }
146 data.instanceMesh->setInstanceCount(data.instanceCount);
147 }
148 }
149 }
150
151 if (data.dynamicScaling) {
152 assert(data.matrices.size() == sizeof(glm::mat4)*data.instanceCount);
153
154 if (const TransformComponent* trComp = comp.getComponent<TransformComponent>(); trComp) {
155 const glm::mat4& localToWorld = context->transformSystem->getLocalToWorld(trComp);
156 const size_t viewStateHash = Cogs::hashSequence(localToWorld,
157 mainCamData.viewMatrix,
158 mainCamData.rawProjectionMatrix,
159 mainCamData.viewportSize.y,
160 dpiScale);
161
162 if (forceUpdate || data.viewStateHash != viewStateHash) {
163 data.viewStateHash = viewStateHash;
164
165 size_t currentInstanceCount = 0;
166 MappedStream<glm::mat4> outMatrices = data.instanceMesh->map<glm::mat4>(VertexDataType::Positions, VertexFormats::InstanceMat4f, 0, data.instanceCount, true);
167 if (data.useColors) {
168 MappedStream<glm::vec4> outColors = data.instanceMesh->map<glm::vec4>(VertexDataType::Colors0, VertexFormats::InstanceColor4f, 0, data.instanceCount, true);
169 currentInstanceCount = scaleMatrices(std::span<glm::mat4>(outMatrices.data, data.instanceCount),
170 std::span<glm::vec4>(outColors.data, data.instanceCount),
171 std::span<const glm::mat4>(static_cast<const glm::mat4*>(data.matrices.data()), data.instanceCount),
172 std::span<const glm::vec4>(comp.colors.data(), data.instanceCount),
173 localToWorld,
174 mainCamData.viewMatrix,
175 mainCamData.rawProjectionMatrix,
176 mainCamData.viewportSize.y,
177 dpiScale,
178 comp.fixedSizeMaxDistance);
179 }
180 else {
181 currentInstanceCount = scaleMatrices(std::span<glm::mat4>(outMatrices.data, data.instanceCount),
182 std::span<glm::vec4>(),
183 std::span<const glm::mat4>(static_cast<const glm::mat4*>(data.matrices.data()), data.instanceCount),
184 std::span<const glm::vec4>(),
185 localToWorld,
186 mainCamData.viewMatrix,
187 mainCamData.rawProjectionMatrix,
188 mainCamData.viewportSize.y,
189 dpiScale,
190 comp.fixedSizeMaxDistance);
191 }
192 data.instanceMesh->setCount(currentInstanceCount);
193 data.instanceMesh->setInstanceCount(currentInstanceCount);
194 }
195 }
196 }
197
199 if (renderComp) {
200 if (renderComp->instanceMesh != data.instanceMesh) {
201 renderComp->instanceMesh = data.instanceMesh;
202 renderComp->setChanged();
203 }
204 if (renderComp->material) {
205 renderComp->material->setVariant("InstancedMatrix", data.useMatrix);
206 renderComp->material->setVariant("InstancedPositions", data.usePositions);
207 renderComp->material->setVariant("InstancedColors", data.useColors);
208 }
209 }
210 }
211}
void setChanged()
Sets the component to the ComponentFlags::Changed state with carry.
Definition: Component.h:202
ComponentType * getComponent() const
Definition: Component.h:159
Context * context
Pointer to the Context instance the system lives in.
void update()
Updates the system state to that of the current frame.
ComponentPool< ComponentType > pool
Pool of components managed by the system.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
MaterialInstanceHandle material
Material instance used to render the mesh.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
std::unique_ptr< class DPIService > dpiService
DPI service instance.
Definition: ViewContext.h:71
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr size_t hashSequence(const T &t, const U &u)
Hash the last two items in a sequence of objects.
Definition: HashSequence.h:8
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
Definition: CameraSystem.h:67
glm::mat4 rawProjectionMatrix
Projection matrix that has not been adjusted by the renderer, and is thus appropriate for direct scre...
Definition: CameraSystem.h:130
Wrapper for mapped stream data automatically updating the Mesh resource mapped from when destructed.
Definition: Mesh.h:126
Element * data
Pointer to the element data mapped from the Mesh data stream.
Definition: Mesh.h:180
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.