2#include "../Extensions/GeometryProcessing/GeometryProcessing.h"
3#include "Resources/MeshManager.h"
5#include "IsoSurfaces.h"
11 struct VertexEmitStore
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;
22 float threshold = 0.f;
23 float normalizedLayer = 0.f;
24 unsigned vertexOffset = 0;
27 struct VertexEmitNoData {
29 VertexEmitNoData(VertexEmitStore& s) : s(s) {}
30 VertexEmitNoData(
const VertexEmitNoData& o) : s(o.s) {}
32 void operator()(int32_t ixIndex, glm::ivec4* samples,
const uint32_t ixN)
34 auto Fx = s.fieldSize.x;
35 auto Fy = s.fieldSize.y;
37 auto I0 = glm::clamp(glm::ivec3(samples[0][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;
43 for (uint32_t k = 0; k < ixN; k++) {
44 auto I1 = glm::clamp(glm::ivec3(samples[0][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;
50 float den = (value1 - value0);
51 float t = abs(den) < std::numeric_limits<float>::min() ? 0.5f :
static_cast<float>(-value0 / den);
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);
60 struct VertexEmitWithData {
62 VertexEmitWithData(VertexEmitStore& s) : s(s) {}
63 VertexEmitWithData(
const VertexEmitWithData& o) : s(o.s) {}
65 void operator()(int32_t ixIndex, glm::ivec4* samples,
const uint32_t ixN)
67 auto Fx = s.fieldSize.x;
68 auto Fy = s.fieldSize.y;
70 auto I0 = glm::clamp(glm::ivec3(samples[0][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];
78 for (uint32_t k = 0; k < ixN; k++) {
79 auto I1 = glm::clamp(glm::ivec3(samples[0][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];
87 float den = value1 - value0;
88 float t = abs(den) < std::numeric_limits<float>::min() ? 0.5f :
static_cast<float>(-value0 / den);
90 float dta = ((1.f - t)*data0 + t*data1) / s.threshold;
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);
102 uint32_t vertexOffset;
108 IndexEmit(IndexEmitStore& s) : s(s) {}
109 IndexEmit(
const IndexEmit& o) : s(o.s) {}
111 void operator()(uint32_t ixIndex, glm::ivec3 ,
const uint32_t* cellIndices,
const uint32_t ixN)
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;
124 IndexEmitFlip(IndexEmitStore& s) : s(s) {}
125 IndexEmitFlip(
const IndexEmitFlip& o) : s(o.s) {}
127 void operator()(uint32_t ixIndex, glm::ivec3 ,
const uint32_t* cellIndices,
const uint32_t ixN)
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;
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,
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,
158 const glm::ivec3 gridA(closeXNeg ? -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;
166 const uint32_t layers =
static_cast<uint32_t
>(std::min(
size_t(16),thresholds.size()));
168 std::vector<int32_t> vertexOffsets;
169 std::vector<int32_t> indexOffsets;
170 std::vector<int32_t> cellOffsets;
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);
184 const auto vertexCount = vertexOffsets.back();
185 const auto indexCount = indexOffsets.back();
187 if (0 < vertexCount) {
189 mesh = context->meshManager->create();
192 auto proxy = context->meshManager->lock(mesh);
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);
199 auto extractGroup = context->
taskManager->createGroup();
200 for (
size_t i = 0; i < layers; i++) {
202 VertexEmitFunc vertexEmit;
204 VertexEmitStore vtxStore;
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);
221 vertexEmit = VertexEmitWithData(vtxStore);
224 extractVertices(context,
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],
234 IndexEmitFunc indexEmit;
236 IndexEmitStore idxStore;
237 idxStore.indices = scratch.indices.data() + indexOffsets[i];
238 idxStore.vertexOffset = vertexOffsets[i];
239 if (flipOrientation ==
false) {
240 indexEmit = IndexEmit(idxStore);
243 indexEmit = IndexEmitFlip(idxStore);
246 extractIndices(context,
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],
263 context->
taskManager->enqueueChild(normalVecGroup, [&]
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(),
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];
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
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.
@ ClockwiseWinding
The mesh uses clockwise winding order for it's triangles as opposed to the counter-clockwise default.
Task id struct used to identify unique Task instances.
@ TriangleList
List of triangles.