2#include "Foundation/Logging/Logger.h"
4#include "Utilities/FrustumClassification.h"
6#include "RayIntersection.h"
8#include "Math/RayTriangleIntersection.h"
9#include "Math/RayBoxIntersection.h"
10#include "Resources/Mesh.h"
11#include "Resources/Buffer.h"
13#include <glm/gtc/packing.hpp>
23 const uint8_t* ptr =
nullptr;
24 Cogs::DataFormat format = Cogs::DataFormat::Unknown;
31 const uint8_t* ptr =
nullptr;
36 struct SubMeshStreamRef
43 bool intersectThickRayAndPoint(glm::vec3& intersection,
const glm::mat4& localPickMatrix,
const glm::vec3& l0)
45 glm::vec4 h0 = localPickMatrix * glm::vec4(l0.x, l0.y, l0.z, 1.f);
46 glm::vec2 p0 = (1.f / h0.w) * glm::vec2(h0.x, h0.y);
47 if (glm::dot(p0, p0) <= 1.f) {
55 bool intersectThickRayAndLineSegment(glm::vec3& intersection, glm::vec2 barycentric,
const glm::mat4& localPickMatrix,
const glm::vec3& pt0,
const glm::vec3& pt1)
57 barycentric = glm::vec2(-1.0f);
61 glm::vec4 h0 = localPickMatrix * glm::vec4(l0.x, l0.y, l0.z, 1.f);
62 glm::vec4 h1 = localPickMatrix * glm::vec4(l1.x, l1.y, l1.z, 1.f);
65 if (h0.z < -h0.w && h1.z < -h1.w) {
68 else if (h0.z < -h0.w) {
74 float t = -(h0.w + h0.z) / (h1.z - h0.z + h1.w - h0.w);
75 h0 = glm::mix(h0, h1, t);
76 l0 = glm::inverse(localPickMatrix) * h0;
78 else if (h1.z < -h1.w) {
79 float t = -(h0.w + h0.z) / (h1.z - h0.z + h1.w - h0.w);
80 h1 = glm::mix(h0, h1, t);
81 l1 = glm::inverse(localPickMatrix) * h1;
85 glm::vec2 p0 = (1.f / h0.w) * glm::vec2(h0.x, h0.y);
86 glm::vec2 p1 = (1.f / h1.w) * glm::vec2(h1.x, h1.y);
87 glm::vec2 v01 = p1 - p0;
89 float d2 = glm::dot(v01, v01);
90 if (std::numeric_limits<float>::epsilon() < d2) {
92 t = glm::clamp(-glm::dot(p0, v01) / d2, 0.f, 1.f);
100 glm::vec2 pt = glm::mix(p0, p1, t);
103 if (glm::dot(pt, pt) <= 1.f) {
105 glm::vec2 w((1.f - t) / h0.w, t / h1.w);
106 glm::vec2 b = (1.f / (w.x + w.y)) * w;
107 intersection = b.x * l0 + b.y * l1;
115 bool intersectTriangleBarycentric(glm::vec3& barycentric,
const glm::mat4& localPickMatrix,
const glm::vec3& l0,
const glm::vec3& l1,
const glm::vec3& l2)
117 glm::vec4 h0 = localPickMatrix * glm::vec4(l0.x, l0.y, l0.z, 1.f);
118 glm::vec4 h1 = localPickMatrix * glm::vec4(l1.x, l1.y, l1.z, 1.f);
119 glm::vec4 h2 = localPickMatrix * glm::vec4(l2.x, l2.y, l2.z, 1.f);
121 if ((h0.z < -h0.w) && (h1.z < -h1.w) && (h2.z < -h2.w)) {
124 else if ((h0.z < -h0.w) || (h1.z < -h1.w) || (h2.z < -h2.w)) {
126 glm::mat4 Q = glm::inverse(localPickMatrix);
127 glm::vec4 R0 = Q * glm::vec4(0.f, 0.f, -1.f, 1.f);
128 glm::vec3 r0 = (1.f / R0.w) * glm::vec3(R0.x, R0.y, R0.z);
129 glm::vec4 R1 = Q * glm::vec4(0.f, 0.f, 1.f, 1.f);
130 glm::vec3 r1 = (1.f / R1.w) * glm::vec3(R1.x, R1.y, R1.z);
132 glm::vec3 intersection;
134 return intersect(l0, l1, l2, r0, r1 - r0, intersection, barycentric, front);
137 glm::vec2 p0 = (1.f / h0.w) * glm::vec2(h0.x, h0.y);
138 glm::vec2 p1 = (1.f / h1.w) * glm::vec2(h1.x, h1.y);
139 glm::vec2 p2 = (1.f / h2.w) * glm::vec2(h2.x, h2.y);
140 glm::vec2 v01 = p1 - p0;
141 glm::vec2 v12 = p2 - p1;
142 glm::vec2 v20 = p0 - p2;
145 glm::vec4 areas(v12.x * p1.y - p1.x * v12.y,
146 v20.x * p2.y - p2.x * v20.y,
147 v01.x * p0.y - p0.x * v01.y,
148 v01.x * v20.y - v20.x * v01.y);
151 if (std::numeric_limits<float>::epsilon() < glm::abs(areas.w)) {
154 glm::bvec4 signs = glm::lessThan(areas, glm::vec4(0.f));
157 if (glm::all(glm::equal(glm::bvec4(signs.w), signs))) {
158 glm::vec3 w = glm::vec3(areas.x / h0.w, areas.y / h1.w, areas.z / h2.w);
159 barycentric = (1.f / (w.x + w.y + w.z)) * w;
170 bool intersectTriangle(glm::vec3& intersection, glm::vec3 &barycenric,
const glm::mat4& localPickMatrix,
const glm::vec3& l0,
const glm::vec3& l1,
const glm::vec3& l2)
172 bool result = intersectTriangleBarycentric(barycenric, localPickMatrix, l0, l1, l2);
174 intersection = barycenric.x * l0 + barycenric.y * l1 + barycenric.z * l2;
179 template<
typename Fetch>
180 void classify(FrustumPlanes* vertexFlags,
181 const glm::mat4& localPickMatrix,
182 const PosStreamRef& positions)
184 for (
size_t i = 0; i < positions.count; i++) {
185 glm::vec4 h = localPickMatrix * glm::vec4(Fetch::fetch(positions, i), 1.f);
186 vertexFlags[i] = (( h.x <= h.w ? FrustumPlanes::InsidePosX : FrustumPlanes::InsideNone) |
187 (-h.w <= h.x ? FrustumPlanes::InsideNegX : FrustumPlanes::InsideNone) |
188 ( h.y <= h.w ? FrustumPlanes::InsidePosY : FrustumPlanes::InsideNone) |
189 (-h.w <= h.y ? FrustumPlanes::InsideNegY : FrustumPlanes::InsideNone) |
190 ( h.z <= h.w ? FrustumPlanes::InsidePosZ : FrustumPlanes::InsideNone) |
191 (-h.w <= h.z ? FrustumPlanes::InsideNegZ : FrustumPlanes::InsideNone));
196 template<
size_t IndexStr
ide,
typename Fetch>
197 void intersectPoints(std::vector<RayIntersectionHit>& hits,
198 const glm::mat4& localPickMatrix,
199 const FrustumPlanes* vertexFlags,
200 const PosStreamRef& positions,
201 const IxStreamRef& indices,
206 static_assert(IndexStride == 0 || IndexStride == 2 || IndexStride == 4);
207 for (
size_t i = a; i < b; i++) {
210 if constexpr (IndexStride == 2) {
211 ix0 =
reinterpret_cast<const uint16_t*
>(indices.ptr)[i];
212 if (positions.count <= ix0)
continue;
214 else if constexpr (IndexStride == 4) {
215 ix0 =
reinterpret_cast<const uint32_t*
>(indices.ptr)[i];
216 if (positions.count <= ix0)
continue;
222 if (vertexFlags[ix0] == FrustumPlanes::InsideAll) {
223 glm::vec3 intersection;
224 glm::vec3 barycentric = glm::vec3(1.0f, 0.0f, 0.0f);
225 if (intersectThickRayAndPoint(intersection, localPickMatrix,
226 Fetch::fetch(positions, ix0))) {
227 hits.emplace_back(
RayIntersectionHit{ scale * intersection, barycentric, glm::uvec3(ix0, ix0, ix0) });
234 template<
size_t IndexStr
ide,
typename Fetch>
235 void intersectLines(std::vector<RayIntersectionHit>& hits,
236 const glm::mat4& localPickMatrix,
237 const FrustumPlanes* vertexFlags,
238 const PosStreamRef& positions,
239 const IxStreamRef& indices,
242 const size_t indexStep,
245 static_assert(IndexStride == 0 || IndexStride == 2 || IndexStride == 4);
246 for (
size_t i = a; i < b; i += indexStep) {
249 if constexpr (IndexStride == 2) {
250 ix0 =
reinterpret_cast<const uint16_t*
>(indices.ptr)[i + 0];
251 ix1 =
reinterpret_cast<const uint16_t*
>(indices.ptr)[i + 1];
252 if (positions.count <= ix0 || positions.count <= ix1)
continue;
254 else if constexpr (IndexStride == 4) {
255 ix0 =
reinterpret_cast<const uint32_t*
>(indices.ptr)[i + 0];
256 ix1 =
reinterpret_cast<const uint32_t*
>(indices.ptr)[i + 1];
257 if (positions.count <= ix0 || positions.count <= ix1)
continue;
264 if ((vertexFlags[ix0] | vertexFlags[ix1]) == FrustumPlanes::InsideAll) {
266 glm::vec3 intersection;
267 glm::vec2 barycentric;
268 if (intersectThickRayAndLineSegment(intersection, barycentric, localPickMatrix,
269 Fetch::fetch(positions, ix0),
270 Fetch::fetch(positions, ix1))) {
271 hits.emplace_back(
RayIntersectionHit{ scale * intersection, glm::vec3(1.f, 0.f, 0.f), glm::uvec3(ix0, ix1, 0) });
277 template<
size_t IndexStr
ide,
typename Fetch>
278 void intersectTriangles(std::vector<RayIntersectionHit>& hits,
279 const glm::mat4& localPickMatrix,
280 const FrustumPlanes* vertexFlags,
281 const PosStreamRef& positions,
282 const IxStreamRef& indices,
285 const size_t indexStep,
288 static_assert(IndexStride == 0 || IndexStride == 2 || IndexStride == 4);
289 for (
size_t i = a; i + 2 < b; i += indexStep) {
291 size_t ix0, ix1, ix2;
292 if constexpr (IndexStride == 2) {
293 ix0 =
reinterpret_cast<const uint16_t*
>(indices.ptr)[i + 0];
294 ix1 =
reinterpret_cast<const uint16_t*
>(indices.ptr)[i + 1];
295 ix2 =
reinterpret_cast<const uint16_t*
>(indices.ptr)[i + 2];
296 if (positions.count <= ix0 || positions.count <= ix1 || positions.count <= ix2)
continue;
298 else if constexpr (IndexStride == 4) {
299 ix0 =
reinterpret_cast<const uint32_t*
>(indices.ptr)[i + 0];
300 ix1 =
reinterpret_cast<const uint32_t*
>(indices.ptr)[i + 1];
301 ix2 =
reinterpret_cast<const uint32_t*
>(indices.ptr)[i + 2];
302 if (positions.count <= ix0 || positions.count <= ix1 || positions.count <= ix2)
continue;
310 if ((vertexFlags[ix0] | vertexFlags[ix1] | vertexFlags[ix2]) == FrustumPlanes::InsideAll) {
312 glm::vec3 intersection;
313 glm::vec3 barycentric;
314 if (intersectTriangle(intersection, barycentric, localPickMatrix,
315 Fetch::fetch(positions, ix0),
316 Fetch::fetch(positions, ix1),
317 Fetch::fetch(positions, ix2))) {
318 hits.emplace_back(
RayIntersectionHit{ scale * intersection, barycentric, glm::uvec3(ix0, ix1, ix2) });
324 template<
size_t IndexStr
ide,
typename Fetch>
325 void intersectSubMesh(std::vector<RayIntersectionHit>& hits,
326 const glm::mat4& localPickMatrix,
327 const FrustumPlanes* vertexFlags,
328 const SubMeshStreamRef& subMeshes,
329 const IxStreamRef& indices,
330 const PosStreamRef& positions,
332 const float scale = 1.f)
334 for (
size_t i = 0; i < subMeshes.count; i++) {
335 const SubMesh& subMesh = subMeshes.ptr[i];
347 if constexpr (IndexStride == 0) {
348 if (positions.count < a || positions.count < b) {
349 LOG_ERROR_ONCE(logger,
"Draw range is out of bounds");
354 if (indices.count < a || indices.count < b) {
355 LOG_ERROR_ONCE(logger,
"Draw range is out of bounds");
362 intersectPoints<IndexStride, Fetch>(hits, localPickMatrix, vertexFlags, positions, indices, a, b, scale);
366 intersectLines<IndexStride, Fetch>(hits, localPickMatrix, vertexFlags, positions, indices, a, b, 2, scale);
370 intersectLines<IndexStride, Fetch>(hits, localPickMatrix, vertexFlags, positions, indices, a, b, 1, scale);
374 intersectTriangles<IndexStride, Fetch>(hits, localPickMatrix, vertexFlags, positions, indices, a, b, 3, scale);
378 intersectTriangles<IndexStride, Fetch>(hits, localPickMatrix, vertexFlags, positions, indices, a, b, 1, scale);
388 struct Fetch_R10G10B10_UINT {
static glm::vec3 fetch(
const PosStreamRef& pos,
size_t ix) {
return glm::unpackU3x10_1x2(*
reinterpret_cast<const uint32_t*
>(pos.ptr + pos.stride * ix)); } };
389 struct Fetch_R16G16B16_UINT {
static glm::vec3 fetch(
const PosStreamRef& pos,
size_t ix) {
return *
reinterpret_cast<const glm::u16vec3*
>(pos.ptr + pos.stride * ix); } };
390 struct Fetch_R16G16B16_FLOAT {
static glm::vec3 fetch(
const PosStreamRef& pos,
size_t ix) {
return glm::unpackHalf4x16(*
reinterpret_cast<const uint64_t*
>(pos.ptr + pos.stride * ix)); } };
391 struct Fetch_R32G32B32_FLOAT {
static glm::vec3 fetch(
const PosStreamRef& pos,
size_t ix) {
return *
reinterpret_cast<const glm::vec3*
>(pos.ptr + pos.stride * ix); } };
397bool Cogs::Core::intersectMesh(std::vector<RayIntersectionHit>& hits,
398 std::vector<FrustumPlanes>& scratch,
399 const glm::mat4& pickMatrix,
402 uint32_t vertexCount,
403 uint32_t subMeshIndex)
417 PosStreamRef positions;
418 for (
size_t i = 0; i < VertexDataType::LastVertexType; ++i) {
421 const DataStream& stream = mesh.streams[mesh.streamIndexes[vertexType]];
422 const VertexFormat* vertexFormat = Cogs::VertexFormats::getVertexFormat(stream.
format);
426 positions.format = element.
format;
427 positions.stride = stream.
stride;
435 LOG_ERROR_ONCE(logger,
"Mesh has no position data");
439 assert(positions.ptr);
443 if (mesh.
hasStream(VertexDataType::Indexes)) {
444 const DataStream& stream = mesh.streams[mesh.streamIndexes[VertexDataType::Indexes]];
445 indices.ptr =
static_cast<const uint8_t*
>(stream.
buffer->
data());
446 indices.stride = stream.
stride;
452 SubMeshStreamRef submeshes;
453 if (mesh.
hasStream(VertexDataType::SubMeshes)) {
454 const DataStream& stream = mesh.streams[mesh.streamIndexes[VertexDataType::SubMeshes]];
458 goto found_submeshes;
464 submeshes.ptr = &noSubMesh;
468 assert(submeshes.ptr && submeshes.count);
470 if (subMeshIndex != ~0u) {
471 if (submeshes.count <= subMeshIndex) {
472 LOG_ERROR_ONCE(logger,
"Illegal submesh index %u", subMeshIndex);
475 submeshes.ptr += subMeshIndex;
479 constexpr float scale10u = 1.f / 0x3FFu;
480 constexpr float scale16u = 1.f / 0xFFFFu;
481 scratch.resize(positions.count);
482 switch (positions.format) {
484 case DataFormat::R10G10B10A2_UNORM: {
485 glm::mat4 M = pickMatrix * glm::mat4(scale10u, 0.f, 0.f, 0.f,
486 0.f, scale10u, 0.f, 0.f,
487 0.f, 0.f, scale10u, 0.f,
489 classify<Fetch_R10G10B10_UINT>(scratch.data(), M, positions);
490 switch (indices.stride) {
491 case 0: intersectSubMesh<0, Fetch_R10G10B10_UINT>(hits, M, scratch.data(), submeshes, indices, positions, &mesh, scale10u);
break;
492 case 2: intersectSubMesh<2, Fetch_R10G10B10_UINT>(hits, M, scratch.data(), submeshes, indices, positions, &mesh, scale10u);
break;
493 case 4: intersectSubMesh<4, Fetch_R10G10B10_UINT>(hits, M, scratch.data(), submeshes, indices, positions, &mesh, scale10u);
break;
494 default: assert(
false);
break;
499 case DataFormat::R16G16B16_UNORM: {
500 glm::mat4 M = pickMatrix * glm::mat4(scale16u, 0.f, 0.f, 0.f,
501 0.f, scale16u, 0.f, 0.f,
502 0.f, 0.f, scale16u, 0.f,
504 classify<Fetch_R16G16B16_UINT>(scratch.data(), M, positions);
505 switch (indices.stride) {
506 case 0: intersectSubMesh<0, Fetch_R16G16B16_UINT>(hits, M, scratch.data(), submeshes, indices, positions, &mesh, scale16u);
break;
507 case 2: intersectSubMesh<2, Fetch_R16G16B16_UINT>(hits, M, scratch.data(), submeshes, indices, positions, &mesh, scale16u);
break;
508 case 4: intersectSubMesh<4, Fetch_R16G16B16_UINT>(hits, M, scratch.data(), submeshes, indices, positions, &mesh, scale16u);
break;
509 default: assert(
false);
break;
514 case DataFormat::R16G16B16_FLOAT:
515 classify<Fetch_R16G16B16_FLOAT>(scratch.data(), pickMatrix, positions);
516 switch (indices.stride) {
517 case 0: intersectSubMesh<0, Fetch_R16G16B16_FLOAT>(hits, pickMatrix, scratch.data(), submeshes, indices, positions, &mesh);
break;
518 case 2: intersectSubMesh<2, Fetch_R16G16B16_FLOAT>(hits, pickMatrix, scratch.data(), submeshes, indices, positions, &mesh);
break;
519 case 4: intersectSubMesh<4, Fetch_R16G16B16_FLOAT>(hits, pickMatrix, scratch.data(), submeshes, indices, positions, &mesh);
break;
520 default: assert(
false);
break;
524 case DataFormat::R32G32B32_FLOAT:
525 classify<Fetch_R32G32B32_FLOAT>(scratch.data(), pickMatrix, positions);
526 switch (indices.stride) {
527 case 0: intersectSubMesh<0, Fetch_R32G32B32_FLOAT>(hits, pickMatrix, scratch.data(), submeshes, indices, positions, &mesh);
break;
528 case 2: intersectSubMesh<2, Fetch_R32G32B32_FLOAT>(hits, pickMatrix, scratch.data(), submeshes, indices, positions, &mesh);
break;
529 case 4: intersectSubMesh<4, Fetch_R32G32B32_FLOAT>(hits, pickMatrix, scratch.data(), submeshes, indices, positions, &mesh);
break;
530 default: assert(
false);
break;
535 const FormatInfo* info = getFormatInfo(positions.format);
536 LOG_ERROR_ONCE(logger,
"Unsupported vertex format %s ", info ? info->
vName :
"null");
545bool Cogs::Core::frustumClassifyVertex(FrustumPlanes* dstPtr,
const glm::mat4& matrix,
const glm::vec3* positions,
const size_t count)
547 classify<Fetch_R32G32B32_FLOAT>(dstPtr, matrix, PosStreamRef{ .ptr = (
const uint8_t*)positions, .stride =
sizeof(glm::vec3), .count = uint32_t(count) });
Log implementation class.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
Contains geometry calculations and generation.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
void * data()
Get a pointer to the buffer data.
Contains a stream of data used by Mesh resources.
uint32_t numElements
Number of elements of the type given by format contained in data.
uint32_t offset
Byte offset from the start of the buffer.
VertexFormatHandle format
A pointer to the format describing the contents of the byte buffer.
uint32_t stride
Element stride.
Cogs::Core::ResourceBufferHandle buffer
Data buffer.
Meshes contain streams of vertex data in addition to index data and options defining geometry used fo...
bool hasStream(VertexDataType::EVertexDataType type) const
Check if the Mesh has a DataStream for the given type.
uint32_t getCount() const
Get the vertex count of the mesh.
Defines a sub-mesh of a Mesh resource.
PrimitiveType::EPrimitiveType primitiveType
Primitive type to use when drawing the sub-mesh.
uint32_t startIndex
Start index of the sub-mesh in the index array of the Mesh.
uint32_t numIndexes
Number of indexes to draw for the sub-mesh.
EVertexDataType
Contains data types.
@ TriangleStrip
Triangle strip.
@ PointList
List of points.
@ TriangleList
List of triangles.
Vertex element structure used to describe a single data element in a vertex for the input assembler.
DataFormat format
Format of the element.
uint16_t offset
Offset in bytes from the vertex position in memory.
uint16_t semanticIndex
Index for the semantic mapping.
ElementSemantic semantic
Semantic mapping of the element (position, normal, etc...).