Cogs.Core
MeshHelper.cpp
1#include "Mesh.h"
2
3#include "Foundation/Logging/Logger.h"
4
5#include "Resources/Buffer.h"
6
7namespace
8{
9 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("MeshHelper");
10}
11
12std::span<const glm::vec3> Cogs::Core::extractSemanticStreamVec3(Memory::MemoryBuffer& backing, MeshHandle mesh, const Cogs::ElementSemantic semantic, size_t semanticIndex)
13{
14 for (size_t i = 0; i < VertexDataType::LastVertexType; ++i) {
15 VertexDataType::EVertexDataType type = VertexDataType::EVertexDataType(i);
16 if (mesh->hasStream(type)) {
17 const Cogs::Core::DataStream& stream = mesh->getStream(type);
18 assert(HandleIsValid(stream.format));
19 assert(HandleIsValid(stream.buffer));
20
21 const Cogs::VertexFormat* format = Cogs::VertexFormats::getVertexFormat(stream.format);
22 assert(format);
23
24 for (const Cogs::VertexElement& element : format->elements) {
25 if (element.semantic == semantic && element.semanticIndex == semanticIndex) {
26 const Cogs::Core::ResourceBufferHandle& buffer = stream.buffer;
27
28 size_t bufferByteSize = buffer->size();
29 if (bufferByteSize <= stream.offset || bufferByteSize < stream.offset + size_t(stream.stride) * stream.numElements) {
30 LOG_ERROR(logger, "Vertex stream (offset=%u, stride=%u, num=%u) spans outside backing buffer of size %zu",
31 stream.offset, stream.stride, stream.numElements, bufferByteSize);
32 goto out;
33 }
34 const uint8_t* src = static_cast<const uint8_t*>(buffer->data()) + stream.offset;
35
36
37 backing.resize(sizeof(glm::vec3) * stream.numElements, false);
38 std::span<glm::vec3> dst(static_cast<glm::vec3*>(backing.data()), stream.numElements);
39
40 switch (element.format) {
41
42 case Cogs::DataFormat::X32Y32Z32_FLOAT:
43
44 if (stream.stride == sizeof(glm::vec3)) {
45 return std::span<const glm::vec3>(reinterpret_cast<const glm::vec3*>(src), stream.numElements);
46 }
47
48 for (size_t k = 0; k < stream.numElements; k++) {
49 dst[k] = *reinterpret_cast<const glm::vec3*>(src + stream.stride * k);
50 }
51 return dst;
52
53 case Cogs::DataFormat::R16G16B16_UNORM:
54 for (size_t k = 0; k < stream.numElements; k++) {
55 dst[k] = (1.f / 0xffffu) * glm::vec3(*reinterpret_cast<const glm::u16vec3*>(src + stream.stride * k));
56 }
57 return dst;
58
59 default: {
60 const FormatInfo* formatInfo = Cogs::getFormatInfo(element.format);
61 LOG_DEBUG(logger, "extractSemanticStreamVec3: Unsupported vertex format %s", formatInfo->vName);
62 goto out;
63 }
64 }
65 }
66 }
67 }
68 }
69out:
70 return std::span<const glm::vec3>();
71}
72
73std::span<const uint32_t> Cogs::Core::extractIndexStreamUint32(Memory::MemoryBuffer& backing, MeshHandle mesh)
74{
75 if (mesh->hasStream(VertexDataType::Indexes)) {
76 const Cogs::Core::DataStream& stream = mesh->getStream(VertexDataType::Indexes);
77 assert(HandleIsValid(stream.buffer));
78
79 const Cogs::Core::ResourceBufferHandle& buffer = stream.buffer;
80 size_t bufferByteSize = buffer->size();
81 if (bufferByteSize <= stream.offset || bufferByteSize < stream.offset + size_t(stream.stride) * stream.numElements) {
82 LOG_ERROR(logger, "Vertex stream (offset=%u, stride=%u, num=%u) spans outside backing buffer of size %zu",
83 stream.offset, stream.stride, stream.numElements, bufferByteSize);
84 }
85 else {
86 const uint8_t* src = static_cast<const uint8_t*>(buffer->data()) + stream.offset;
87 switch (stream.stride) {
88
89 case 2: {
90 backing.resize(sizeof(glm::vec3) * stream.numElements, false);
91 std::span<uint32_t> dst(static_cast<uint32_t*>(backing.data()), stream.numElements);
92 for (size_t k = 0; k < stream.numElements; k++) {
93 dst[k] = reinterpret_cast<const uint16_t*>(src)[k];
94 }
95 return dst;
96 }
97
98 case 4:
99 return std::span<const uint32_t>(reinterpret_cast<const uint32_t*>(src), stream.numElements);
100
101 default:
102 LOG_ERROR(logger, "extractIndexStreamUint32: Mesh index stream has illegal stride %u", stream.stride);
103 break;
104 }
105 }
106 }
107 return std::span<const uint32_t>();
108}
109
110
111
113{
114 if (!mesh->hasStream(VertexDataType::EVertexDataType::Positions)) {
115 LOG_ERROR(logger, "Unable to generate normals (no positions)\n");
116 return;
117 }
118
119 if (mesh->primitiveType != Cogs::PrimitiveType::TriangleList) {
120 LOG_ERROR(logger, "Unable to generate normals (not TriangleList)\n");
121 return;
122 }
123
124 size_t vertex_count = mesh->getStream(VertexDataType::EVertexDataType::Positions).numElements;
125 const MappedStreamReadOnly<glm::vec3> pos = mesh->mapPositionsReadOnly(0, vertex_count);
126 std::vector<glm::vec3> normals(vertex_count, glm::vec3(0.0));
127 if (mesh->isIndexed()) {
128 const auto indices = mesh->getIndexes();
129 size_t index_count = indices.size();
130 uint32_t index = 0;
131 while (index + 3 <= index_count) {
132 uint32_t i0 = indices[index + 0];
133 uint32_t i1 = indices[index + 1];
134 uint32_t i2 = indices[index + 2];
135 glm::vec3 a = pos[i0];
136 glm::vec3 b = pos[i1];
137 glm::vec3 c = pos[i2];
138 glm::vec3 n;
139 if (mesh->isCCW()) n = glm::cross(b - a, c - a);
140 else n = glm::cross(c - a, b - a);
141 normals[i0] += n;
142 normals[i1] += n;
143 normals[i2] += n;
144 index += 3;
145 }
146 } else {
147 uint32_t index = 0;
148 while (index + 3 <= vertex_count) {
149 uint32_t i0 = index + 0;
150 uint32_t i1 = index + 1;
151 uint32_t i2 = index + 2;
152 glm::vec3 a = pos[i0];
153 glm::vec3 b = pos[i1];
154 glm::vec3 c = pos[i2];
155 glm::vec3 n;
156 if (mesh->isCCW()) n = glm::cross(b - a, c - a);
157 else n = glm::cross(c - a, b - a);
158 normals[i0] += n;
159 normals[i1] += n;
160 normals[i2] += n;
161 index += 3;
162 }
163 }
164 for (uint32_t i = 0; i < vertex_count; i++) {
165 normals[i] = glm::normalize(normals[i]);
166 }
167
168 mesh->setNormals(std::move(normals));
169}
170
172{
173 if (!mesh->hasStream(VertexDataType::EVertexDataType::Positions)) {
174 LOG_ERROR(logger, "Unable to generate tangents (no positions)\n");
175 return;
176 }
177
178 if (!mesh->hasStream(VertexDataType::EVertexDataType::Normals)) {
180 }
181
182 if (!mesh->hasStream(VertexDataType::EVertexDataType::TexCoords0)) {
183 LOG_ERROR(logger, "Unable to generate tangents (no TexCoords)\n");
184 return;
185 }
186
187 if (mesh->primitiveType != Cogs::PrimitiveType::TriangleList) {
188 LOG_ERROR(logger, "Unable to generate tangents (not TriangleList)\n");
189 return;
190 }
191
192 size_t vertex_count = mesh->getCount();
193 const MappedStreamReadOnly<glm::vec3> pos = mesh->mapPositionsReadOnly(0, vertex_count);
194 const MappedStreamReadOnly<glm::vec3> normals = mesh->mapNormalsReadOnly(0, vertex_count);
195 const MappedStreamReadOnly<glm::vec2> texCoord = mesh->mapTexCoordsReadOnly(0, vertex_count);
196 if (!pos.isValid() || !normals.isValid() || !texCoord.isValid()) {
197 LOG_ERROR(logger, "Invalid data in Stream, not enough values\n");
198 return;
199 }
200
201 std::vector<glm::vec3> tangents(vertex_count, glm::vec3(0.0));
202
203 if (mesh->isIndexed()) {
204 const auto indices = mesh->getIndexes();
205 size_t index_count = indices.size();
206 uint32_t index = 0;
207 while (index + 3 <= index_count) {
208 uint32_t i0 = indices[index + 0];
209 uint32_t i1 = indices[index + 1];
210 uint32_t i2 = indices[index + 2];
211 glm::vec3 a = pos[i0];
212 glm::vec3 b = pos[i1];
213 glm::vec3 c = pos[i2];
214 glm::vec2 ta = texCoord[i0];
215 glm::vec2 tb = texCoord[i1];
216 glm::vec2 tc = texCoord[i2];
217
218 glm::vec2 u0, u1;
219 glm::vec3 v0, v1;
220 if (mesh->isCCW()) {
221 u0 = tb - ta;
222 u1 = tc - ta;
223 v0 = b - a;
224 v1 = c - a;
225 } else {
226 u0 = tc - ta;
227 u1 = tb - ta;
228 v0 = c - a;
229 v1 = b - a;
230 }
231
232 float den = 1.f / (u0.x * u1.y - u0.y * u1.x);
233 glm::vec3 t0 = u1.y*den * v0 - u0.y*den * v1;
234
235 tangents[i0] += t0;
236 tangents[i1] += t0;
237 tangents[i2] += t0;
238 index += 3;
239 }
240 } else {
241 uint32_t index = 0;
242 while (index + 3 <= vertex_count) {
243 uint32_t i0 = index + 0;
244 uint32_t i1 = index + 1;
245 uint32_t i2 = index + 2;
246 glm::vec3 a = pos[i0];
247 glm::vec3 b = pos[i1];
248 glm::vec3 c = pos[i2];
249 glm::vec2 ta = texCoord[i0];
250 glm::vec2 tb = texCoord[i1];
251 glm::vec2 tc = texCoord[i2];
252
253 glm::vec2 u0, u1;
254 glm::vec3 v0, v1;
255 if (mesh->isCCW()) {
256 u0 = tb - ta;
257 u1 = tc - ta;
258 v0 = b - a;
259 v1 = c - a;
260 } else {
261 u0 = tc - ta;
262 u1 = tb - ta;
263 v0 = c - a;
264 v1 = b - a;
265 }
266
267 float den = 1.f / (u0.x * u1.y - u0.y * u1.x);
268 glm::vec3 t0 = u1.y*den * v0 - u0.y*den * v1;
269
270 tangents[i0] += t0;
271 tangents[i1] += t0;
272 tangents[i2] += t0;
273 index += 3;
274 }
275 }
276
277 for (uint32_t i = 0; i < vertex_count; i++) {
278 tangents[i] = glm::normalize(tangents[i] - glm::dot(normals[i], tangents[i])*normals[i]);
279 }
280
281 mesh->setTangents(std::move(tangents));
282}
283Cogs::Geometry::BoundingBox Cogs::Core::calculateBounds(Mesh * mesh)
284{
285 return calculateBounds(mesh, 0, uint32_t(mesh->getCount()));
286}
287
288Cogs::Geometry::BoundingBox Cogs::Core::calculateBounds(Mesh * mesh, uint32_t startIndex, uint32_t vertexCount)
289{
290 auto numPositions = vertexCount != static_cast<uint32_t>(-1) ? vertexCount : mesh->getCount();
291
292 if (!numPositions) return {};
293
294 const auto[pData, pCount, pStride] = mesh->getPositionStream();
295
296 if (!pData) {
297 LOG_ERROR(logger, "Position data not valid for bounding box calculation.");
298 return {};
299 }
300
301 Geometry::BoundingBox box;
302
303 if (mesh->isIndexed()) {
304 if (mesh->hasIndexesU16()) {
305 auto indexes = mesh->getIndexesU16();
306
307 for (size_t i = 0; i < numPositions; ++i) {
308 const uint16_t index = indexes[startIndex + i];
309 glm::vec3 pos = *reinterpret_cast<const glm::vec3 *>(pData + index * pStride);
310 box.min = glm::min(box.min, pos);
311 box.max = glm::max(box.max, pos);
312 }
313 } else {
314 auto indexes = mesh->getIndexes();
315
316 for (size_t i = 0; i < numPositions; ++i) {
317 const uint32_t index = indexes[startIndex + i];
318 glm::vec3 pos = *reinterpret_cast<const glm::vec3 *>(pData + index * pStride);
319 box.min = glm::min(box.min, pos);
320 box.max = glm::max(box.max, pos);
321 }
322 }
323 } else {
324 for (size_t i = 0; i < numPositions; ++i) {
325 auto pos = *reinterpret_cast<const glm::vec3 *>(pData + i * pStride);
326 box.min = glm::min(box.min, pos);
327 box.max = glm::max(box.max, pos);
328 }
329 }
330
331 return box;
332}
Log implementation class.
Definition: LogManager.h:139
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
void COGSCORE_DLL_API generateMeshNormals(Mesh *mesh)
Generate normals.
Definition: MeshHelper.cpp:112
void COGSCORE_DLL_API generateMeshTangents(Mesh *mesh)
Generate tangents.
Definition: MeshHelper.cpp:171
Cogs::Geometry::BoundingBox COGSCORE_DLL_API calculateBounds(Mesh *mesh)
Calculate a bounding box for the given mesh.
Definition: MeshHelper.cpp:283
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
ElementSemantic
Element semantics used to map data to the shader stage.
Definition: VertexFormat.h:14
size_t size() const
Size of the buffer in bytes.
Definition: Buffer.h:62
void * data()
Get a pointer to the buffer data.
Definition: Buffer.h:74
Contains a stream of data used by Mesh resources.
Definition: Mesh.h:80
uint32_t numElements
Number of elements of the type given by format contained in data.
Definition: Mesh.h:108
uint32_t offset
Byte offset from the start of the buffer.
Definition: Mesh.h:102
VertexFormatHandle format
A pointer to the format describing the contents of the byte buffer.
Definition: Mesh.h:99
uint32_t stride
Element stride.
Definition: Mesh.h:105
Cogs::Core::ResourceBufferHandle buffer
Data buffer.
Definition: Mesh.h:96
Wrapper for read-only access to mapped stream data.
Definition: Mesh.h:197
bool isValid() const
Returns true if stream mapping successful.
Definition: Mesh.h:212
Meshes contain streams of vertex data in addition to index data and options defining geometry used fo...
Definition: Mesh.h:265
void setTangents(std::span< const glm::vec3 > tangents)
Set the tangent data of the Mesh.
Definition: Mesh.h:448
bool isCCW() const
If triangles in the mesh are specified counter-clockwise, which is the default.
Definition: Mesh.h:943
bool isIndexed() const
If the mesh uses indexed geometry.
Definition: Mesh.h:953
bool hasStream(VertexDataType::EVertexDataType type) const
Check if the Mesh has a DataStream for the given type.
Definition: Mesh.h:933
MappedStreamReadOnly< glm::vec3 > mapNormalsReadOnly(const size_t start, const size_t end)
Map the normal stream for read access, range between start and end.
Definition: Mesh.h:438
void setNormals(std::span< const glm::vec3 > normals)
Set the normal data of the Mesh.
Definition: Mesh.h:392
DataStream & getStream(const VertexDataType::EVertexDataType dataType)
Get the stream corresponding to the given dataType.
Definition: Mesh.cpp:85
MappedStreamReadOnly< glm::vec3 > mapPositionsReadOnly(const size_t start, const size_t end)
Map the position stream for read access, range between start and end.
Definition: Mesh.h:382
uint32_t getCount() const
Get the vertex count of the mesh.
Definition: Mesh.h:1012
MappedStreamReadOnly< glm::vec2 > mapTexCoordsReadOnly(const size_t start, const size_t end)
Map the texture coordinate stream for read access, range between start and end.
Definition: Mesh.h:550
@ TriangleList
List of triangles.
Definition: Common.h:116
Vertex element structure used to describe a single data element in a vertex for the input assembler.
Definition: VertexFormat.h:38
DataFormat format
Format of the element.
Definition: VertexFormat.h:40
uint16_t semanticIndex
Index for the semantic mapping.
Definition: VertexFormat.h:42
ElementSemantic semantic
Semantic mapping of the element (position, normal, etc...).
Definition: VertexFormat.h:41
Vertex format structure used to describe a single vertex for the input assembler.
Definition: VertexFormat.h:60
std::vector< VertexElement > elements
Vector containing all vertex elements of this format.
Definition: VertexFormat.h:62