Cogs.Core
BasicAPI.cpp
1#include <algorithm>
2#include "../Extensions/GeometryProcessing/GeometryProcessing.h"
3#include "Resources/MeshManager.h"
4#include "Context.h"
5#include "IsoSurfaces.h"
6
7using namespace Cogs::Core;
8
9namespace {
10
11 struct VertexEmitStore
12 {
13 glm::vec3* P = nullptr;
14 glm::vec3* N = nullptr;
15 glm::vec2* T = nullptr;
16 const float* F = nullptr;
17 const float* DTA = nullptr;
18 glm::ivec3 maxFieldIndex;
19 glm::uvec3 fieldSize;
20 glm::vec3 scale;
21 glm::vec3 offset;
22 float threshold = 0.f;
23 float normalizedLayer = 0.f;
24 unsigned vertexOffset = 0;
25 };
26
27 struct VertexEmitNoData {
28 VertexEmitStore s;
29 VertexEmitNoData(VertexEmitStore& s) : s(s) {}
30 VertexEmitNoData(const VertexEmitNoData& o) : s(o.s) {}
31
32 void operator()(int32_t ixIndex, glm::ivec4* samples, const uint32_t ixN)
33 {
34 auto Fx = s.fieldSize.x;
35 auto Fy = s.fieldSize.y;
36
37 auto I0 = glm::clamp(glm::ivec3(samples[0][0],
38 samples[1][0],
39 samples[2][0]), glm::ivec3(0), s.maxFieldIndex);
40 auto value0 = s.F[((I0.z*Fy) + I0.y)*Fx + I0.x] - s.threshold;
41 auto pos0 = s.scale*glm::vec3(I0) + s.offset;
42
43 for (uint32_t k = 0; k < ixN; k++) {
44 auto I1 = glm::clamp(glm::ivec3(samples[0][1 + k],
45 samples[1][1 + k],
46 samples[2][1 + k]), glm::ivec3(0), s.maxFieldIndex);
47 auto value1 = s.F[((I1.z*Fy) + I1.y)*Fx + I1.x] - s.threshold;
48 auto pos1 = s.scale*glm::vec3(I1) + s.offset;
49
50 float den = (value1 - value0);
51 float t = abs(den) < std::numeric_limits<float>::min() ? 0.5f : static_cast<float>(-value0 / den);
52
53 int vo = s.vertexOffset + ixIndex + k;
54 s.P[vo] = (1.f - t)*pos0 + t*pos1;
55 s.T[vo] = glm::vec2(s.normalizedLayer, 0.5f);
56 }
57 }
58 };
59
60 struct VertexEmitWithData {
61 VertexEmitStore s;
62 VertexEmitWithData(VertexEmitStore& s) : s(s) {}
63 VertexEmitWithData(const VertexEmitWithData& o) : s(o.s) {}
64
65 void operator()(int32_t ixIndex, glm::ivec4* samples, const uint32_t ixN)
66 {
67 auto Fx = s.fieldSize.x;
68 auto Fy = s.fieldSize.y;
69
70 auto I0 = glm::clamp(glm::ivec3(samples[0][0],
71 samples[1][0],
72 samples[2][0]), glm::ivec3(0), s.maxFieldIndex);
73 auto value0_ = s.F[((I0.z*Fy) + I0.y)*Fx + I0.x];
74 auto value0 = value0_ - s.threshold;
75 auto pos0 = s.scale*glm::vec3(I0) + s.offset;
76 auto data0 = s.DTA[((I0.z*Fy) + I0.y)*Fx + I0.x];
77
78 for (uint32_t k = 0; k < ixN; k++) {
79 auto I1 = glm::clamp(glm::ivec3(samples[0][1 + k],
80 samples[1][1 + k],
81 samples[2][1 + k]), glm::ivec3(0), s.maxFieldIndex);
82 auto value1_ = s.F[((I1.z*Fy) + I1.y)*Fx + I1.x];
83 auto value1 = value1_ - s.threshold;
84 auto pos1 = s.scale*glm::vec3(I1) + s.offset;
85 auto data1 = s.DTA[((I1.z*Fy) + I1.y)*Fx + I1.x];
86
87 float den = value1 - value0;
88 float t = abs(den) < std::numeric_limits<float>::min() ? 0.5f : static_cast<float>(-value0 / den);
89
90 float dta = ((1.f - t)*data0 + t*data1) / s.threshold;
91
92 int vo = s.vertexOffset + ixIndex + k;
93 s.P[vo] = (1.f - t)*pos0 + t*pos1;
94 s.T[vo] = glm::vec2(s.normalizedLayer, dta);
95 }
96 }
97 };
98
99 struct IndexEmitStore
100 {
101 uint32_t* indices;
102 uint32_t vertexOffset;
103 };
104
105 struct IndexEmit
106 {
107 IndexEmitStore s;
108 IndexEmit(IndexEmitStore& s) : s(s) {}
109 IndexEmit(const IndexEmit& o) : s(o.s) {}
110
111 void operator()(uint32_t ixIndex, glm::ivec3 /*cell*/, const uint32_t* cellIndices, const uint32_t ixN)
112 {
113 for (uint32_t k = 0; k < ixN; k += 3) {
114 s.indices[ixIndex + k + 0] = cellIndices[k + 0] + s.vertexOffset;
115 s.indices[ixIndex + k + 1] = cellIndices[k + 1] + s.vertexOffset;
116 s.indices[ixIndex + k + 2] = cellIndices[k + 2] + s.vertexOffset;
117 }
118 }
119 };
120
121 struct IndexEmitFlip
122 {
123 IndexEmitStore s;
124 IndexEmitFlip(IndexEmitStore& s) : s(s) {}
125 IndexEmitFlip(const IndexEmitFlip& o) : s(o.s) {}
126
127 void operator()(uint32_t ixIndex, glm::ivec3 /*cell*/, const uint32_t* cellIndices, const uint32_t ixN)
128 {
129 for (uint32_t k = 0; k < ixN; k += 3) {
130 s.indices[ixIndex + k + 0] = cellIndices[k + 0] + s.vertexOffset;
131 s.indices[ixIndex + k + 1] = cellIndices[k + 2] + s.vertexOffset;
132 s.indices[ixIndex + k + 2] = cellIndices[k + 1] + s.vertexOffset;
133 }
134 }
135 };
136
137}
138
139
140MeshHandle IsoSurfaces::createIsoSurfacesMesh(Context* context,
141 IsoSurfaces::Scratch& scratch,
142 const std::vector<float>& thresholds,
143 const float* densityField,
144 const float* dataField,
145 const glm::uvec3 fieldSize,
146 const glm::vec3 minCorner,
147 const glm::vec3 maxCorner,
148 const bool /*exteriorIsInside*/,
149 const bool flipOrientation,
150 const bool closeZNeg,
151 const bool closeZPos,
152 const bool closeYNeg,
153 const bool closeYPos,
154 const bool closeXNeg,
155 const bool closeXPos,
156 MeshHandle mesh)
157{
158 const glm::ivec3 gridA(closeXNeg ? -1 : 0,
159 closeYNeg ? -1 : 0,
160 closeZNeg ? -1 : 0);
161 const glm::ivec3 gridB(fieldSize.x + (closeXPos ? 1 : 0),
162 fieldSize.y + (closeYPos ? 1 : 0),
163 fieldSize.z + (closeZPos ? 1 : 0));
164 const auto M = gridB - gridA;
165
166 const uint32_t layers = static_cast<uint32_t>(std::min(size_t(16),thresholds.size()));
167
168 std::vector<int32_t> vertexOffsets;
169 std::vector<int32_t> indexOffsets;
170 std::vector<int32_t> cellOffsets;
171
172 analyze(context,
173 vertexOffsets,
174 indexOffsets,
175 cellOffsets,
176 scratch.cellMap,
177 scratch.activeCellCases,
178 scratch.activeCellVertexOffsets,
179 scratch.activeCellIndexOffsets,
180 scratch.activeCellIndices,
181 scratch.activeCellIJK,
182 thresholds, true, densityField, glm::ivec3(fieldSize), gridA, gridB, nullptr);
183
184 const auto vertexCount = vertexOffsets.back();
185 const auto indexCount = indexOffsets.back();
186
187 if (0 < vertexCount) {
188 if (!HandleIsValid(mesh)) {
189 mesh = context->meshManager->create();
190 }
191
192 auto proxy = context->meshManager->lock(mesh);
193
194 auto P = proxy->mapPositions(0, vertexCount);
195 auto N = proxy->map<glm::vec3>(VertexDataType::Normals, VertexFormats::Norm3f, 0, vertexCount);
196 auto T = proxy->map<glm::vec2>(VertexDataType::TexCoords0, VertexFormats::Tex2f, 0, vertexCount);
197 scratch.indices.resize(indexCount);
198
199 auto extractGroup = context->taskManager->createGroup();
200 for (size_t i = 0; i < layers; i++) {
201
202 VertexEmitFunc vertexEmit;
203
204 VertexEmitStore vtxStore;
205 vtxStore.P = P.data;
206 vtxStore.N = N.data;
207 vtxStore.T = T.data;
208 vtxStore.F = densityField;
209 vtxStore.DTA = dataField;
210 vtxStore.maxFieldIndex = glm::max(glm::uvec3(1), fieldSize) - glm::uvec3(1);
211 vtxStore.fieldSize = fieldSize;
212 vtxStore.scale = (maxCorner - minCorner) / glm::vec3(fieldSize - glm::uvec3(1));
213 vtxStore.offset = minCorner;
214 vtxStore.threshold = thresholds[i];
215 vtxStore.normalizedLayer = (i + 0.5f) / layers;
216 vtxStore.vertexOffset = vertexOffsets[i];
217 if (dataField == nullptr) {
218 vertexEmit = VertexEmitNoData(vtxStore);
219 }
220 else {
221 vertexEmit = VertexEmitWithData(vtxStore);
222 }
223
224 extractVertices(context,
225 extractGroup,
226 vertexEmit,
227 scratch.activeCellCases.data() + cellOffsets[i],
228 scratch.activeCellVertexOffsets.data() + cellOffsets[i] + i,
229 scratch.activeCellIndices.data() + cellOffsets[i],
230 scratch.activeCellIJK.data() + cellOffsets[i],
231 cellOffsets[i + 1] - cellOffsets[i],
232 gridA, gridB);
233
234 IndexEmitFunc indexEmit;
235
236 IndexEmitStore idxStore;
237 idxStore.indices = scratch.indices.data() + indexOffsets[i];
238 idxStore.vertexOffset = vertexOffsets[i];
239 if (flipOrientation == false) {
240 indexEmit = IndexEmit(idxStore);
241 }
242 else {
243 indexEmit = IndexEmitFlip(idxStore);
244 }
245
246 extractIndices(context,
247 extractGroup,
248 indexEmit,
249 scratch.cellMap.data() + i * M.x*M.y*M.z,
250 scratch.activeCellCases.data() + cellOffsets[i],
251 scratch.activeCellVertexOffsets.data() + cellOffsets[i] + i,
252 scratch.activeCellIndexOffsets.data() + cellOffsets[i] + i,
253 scratch.activeCellIndices.data() + cellOffsets[i],
254 scratch.activeCellIJK.data() + cellOffsets[i],
255 cellOffsets[i + 1] - cellOffsets[i],
256 gridA, gridB);
257
258 }
259 context->taskManager->wait(extractGroup);
260 context->taskManager->destroy(extractGroup);
261
262 TaskId normalVecGroup = context->taskManager->createGroup();
263 context->taskManager->enqueueChild(normalVecGroup, [&]
264 {
265 auto * indicesPtr = scratch.indices.data();
266 GeometryProcessing::normalsFromIndexedTriangles(context,
267 reinterpret_cast<float*>(N.data), 3,
268 reinterpret_cast<const float*>(P.data), 3,
269 vertexOffsets.back(),
270 indicesPtr,
271 indexOffsets.back(),
272 10000);
273 });
274 context->taskManager->wait(normalVecGroup);
275 context->taskManager->destroy(normalVecGroup);
276
277 auto bbox = Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::BoundingBox>();
278 bbox.min = minCorner;
279 bbox.max = maxCorner;
280 proxy->setBounds(bbox);
281 proxy->setIndexData(scratch.indices.data(), indexOffsets.back());
282 proxy->mapSubMeshes(layers);
283 for (size_t l = 0; l < layers; l++) {
284 auto & subMesh = proxy->getSubMeshes()[l];
285 subMesh.startIndex = indexOffsets[l];
286 subMesh.numIndexes = indexOffsets[l + 1] - indexOffsets[l];
287 subMesh.primitiveType = Cogs::PrimitiveType::TriangleList;
288 }
289 proxy->setMeshFlag(MeshFlags::ClockwiseWinding);
290 }
291
292 return mesh;
293}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
Definition: Context.h:186
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
Struct that holds temporary data between the different passes.
Definition: IsoSurfaces.h:24
@ ClockwiseWinding
The mesh uses clockwise winding order for it's triangles as opposed to the counter-clockwise default.
Definition: Mesh.h:63
Task id struct used to identify unique Task instances.
Definition: TaskManager.h:20
@ TriangleList
List of triangles.
Definition: Common.h:116