Path: blob/master/thirdparty/jolt_physics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h
9913 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56#include <Jolt/Geometry/RayTriangle.h>78JPH_NAMESPACE_BEGIN910/// Store vertices in 64 bits and indices in 8 bits + 8 bit of flags per triangle like this:11///12/// TriangleBlockHeader,13/// TriangleBlock (4 triangles and their flags in 16 bytes),14/// TriangleBlock...15/// [Optional] UserData (4 bytes per triangle)16///17/// Vertices are stored:18///19/// VertexData (1 vertex in 64 bits),20/// VertexData...21///22/// They're compressed relative to the bounding box as provided by the node codec.23class TriangleCodecIndexed8BitPackSOA4Flags24{25public:26class TriangleHeader27{28public:29Float3 mOffset; ///< Offset of all vertices30Float3 mScale; ///< Scale of all vertices, vertex_position = mOffset + mScale * compressed_vertex_position31};3233/// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable)34static constexpr int TriangleHeaderSize = sizeof(TriangleHeader);3536/// If this codec could return a different offset than the current buffer size when calling Pack()37static constexpr bool ChangesOffsetOnPack = false;3839/// Amount of bits per component40enum EComponentData : uint3241{42COMPONENT_BITS = 21,43COMPONENT_MASK = (1 << COMPONENT_BITS) - 1,44};4546/// Packed X and Y coordinate47enum EVertexXY : uint3248{49COMPONENT_X = 0,50COMPONENT_Y1 = COMPONENT_BITS,51COMPONENT_Y1_BITS = 32 - COMPONENT_BITS,52};5354/// Packed Z and Y coordinate55enum EVertexZY : uint3256{57COMPONENT_Z = 0,58COMPONENT_Y2 = COMPONENT_BITS,59COMPONENT_Y2_BITS = 31 - COMPONENT_BITS,60};6162/// A single packed vertex63struct VertexData64{65uint32 mVertexXY;66uint32 mVertexZY;67};6869static_assert(sizeof(VertexData) == 8, "Compiler added padding");7071/// A block of 4 triangles72struct TriangleBlock73{74uint8 mIndices[3][4]; ///< 8 bit indices to triangle vertices for 4 triangles in the form mIndices[vertex][triangle] where vertex in [0, 2] and triangle in [0, 3]75uint8 mFlags[4]; ///< Triangle flags (could contain material and active edges)76};7778static_assert(sizeof(TriangleBlock) == 16, "Compiler added padding");7980enum ETriangleBlockHeaderFlags : uint3281{82OFFSET_TO_VERTICES_BITS = 29, ///< Offset from current block to start of vertices in bytes83OFFSET_TO_VERTICES_MASK = (1 << OFFSET_TO_VERTICES_BITS) - 1,84OFFSET_NON_SIGNIFICANT_BITS = 2, ///< The offset from the current block to the start of the vertices must be a multiple of 4 bytes85OFFSET_NON_SIGNIFICANT_MASK = (1 << OFFSET_NON_SIGNIFICANT_BITS) - 1,86OFFSET_TO_USERDATA_BITS = 3, ///< When user data is stored, this is the number of blocks to skip to get to the user data (0 = no user data)87OFFSET_TO_USERDATA_MASK = (1 << OFFSET_TO_USERDATA_BITS) - 1,88};8990/// A triangle header, will be followed by one or more TriangleBlocks91struct TriangleBlockHeader92{93const VertexData * GetVertexData() const { return reinterpret_cast<const VertexData *>(reinterpret_cast<const uint8 *>(this) + ((mFlags & OFFSET_TO_VERTICES_MASK) << OFFSET_NON_SIGNIFICANT_BITS)); }94const TriangleBlock * GetTriangleBlock() const { return reinterpret_cast<const TriangleBlock *>(reinterpret_cast<const uint8 *>(this) + sizeof(TriangleBlockHeader)); }95const uint32 * GetUserData() const { uint32 offset = mFlags >> OFFSET_TO_VERTICES_BITS; return offset == 0? nullptr : reinterpret_cast<const uint32 *>(GetTriangleBlock() + offset); }9697uint32 mFlags;98};99100static_assert(sizeof(TriangleBlockHeader) == 4, "Compiler added padding");101102/// This class is used to validate that the triangle data will not be degenerate after compression103class ValidationContext104{105public:106/// Constructor107ValidationContext(const IndexedTriangleList &inTriangles, const VertexList &inVertices) :108mVertices(inVertices)109{110// Only used the referenced triangles, just like EncodingContext::Finalize does111for (const IndexedTriangle &i : inTriangles)112for (uint32 idx : i.mIdx)113mBounds.Encapsulate(Vec3(inVertices[idx]));114}115116/// Test if a triangle will be degenerate after quantization117bool IsDegenerate(const IndexedTriangle &inTriangle) const118{119// Quantize the triangle in the same way as EncodingContext::Finalize does120UVec4 quantized_vertex[3];121Vec3 compress_scale = Vec3::sReplicate(COMPONENT_MASK) / Vec3::sMax(mBounds.GetSize(), Vec3::sReplicate(1.0e-20f));122for (int i = 0; i < 3; ++i)123quantized_vertex[i] = ((Vec3(mVertices[inTriangle.mIdx[i]]) - mBounds.mMin) * compress_scale + Vec3::sReplicate(0.5f)).ToInt();124return quantized_vertex[0] == quantized_vertex[1] || quantized_vertex[1] == quantized_vertex[2] || quantized_vertex[0] == quantized_vertex[2];125}126127private:128const VertexList & mVertices;129AABox mBounds;130};131132/// This class is used to encode and compress triangle data into a byte buffer133class EncodingContext134{135public:136/// Indicates a vertex hasn't been seen yet in the triangle list137static constexpr uint32 cNotFound = 0xffffffff;138139/// Construct the encoding context140explicit EncodingContext(const VertexList &inVertices) :141mVertexMap(inVertices.size(), cNotFound)142{143}144145/// Mimics the size a call to Pack() would add to the buffer146void PreparePack(const IndexedTriangle *inTriangles, uint inNumTriangles, bool inStoreUserData, uint64 &ioBufferSize)147{148// Add triangle block header149ioBufferSize += sizeof(TriangleBlockHeader);150151// Compute first vertex that this batch will use (ensuring there's enough room if none of the vertices are shared)152uint start_vertex = Clamp((int)mVertexCount - 256 + (int)inNumTriangles * 3, 0, (int)mVertexCount);153154// Pack vertices155uint padded_triangle_count = AlignUp(inNumTriangles, 4);156for (uint t = 0; t < padded_triangle_count; t += 4)157{158// Add triangle block header159ioBufferSize += sizeof(TriangleBlock);160161for (uint vertex_nr = 0; vertex_nr < 3; ++vertex_nr)162for (uint block_tri_idx = 0; block_tri_idx < 4; ++block_tri_idx)163{164// Fetch vertex index. Create degenerate triangles for padding triangles.165bool triangle_available = t + block_tri_idx < inNumTriangles;166uint32 src_vertex_index = triangle_available? inTriangles[t + block_tri_idx].mIdx[vertex_nr] : inTriangles[inNumTriangles - 1].mIdx[0];167168// Check if we've seen this vertex before and if it is in the range that we can encode169uint32 &vertex_index = mVertexMap[src_vertex_index];170if (vertex_index == cNotFound || vertex_index < start_vertex)171{172// Add vertex173vertex_index = mVertexCount;174mVertexCount++;175}176}177}178179// Add user data180if (inStoreUserData)181ioBufferSize += inNumTriangles * sizeof(uint32);182}183184/// Mimics the size the Finalize() call would add to ioBufferSize185void FinalizePreparePack(uint64 &ioBufferSize)186{187// Remember where the vertices are going to start in the output buffer188JPH_ASSERT(IsAligned(ioBufferSize, 4));189mVerticesStartIdx = size_t(ioBufferSize);190191// Add vertices to buffer192ioBufferSize += uint64(mVertexCount) * sizeof(VertexData);193194// Reserve the amount of memory we need for the vertices195mVertices.reserve(mVertexCount);196197// Set vertex map back to 'not found'198for (uint32 &v : mVertexMap)199v = cNotFound;200}201202/// Pack the triangles in inContainer to ioBuffer. This stores the mMaterialIndex of a triangle in the 8 bit flags.203/// Returns size_t(-1) on error.204size_t Pack(const IndexedTriangle *inTriangles, uint inNumTriangles, bool inStoreUserData, ByteBuffer &ioBuffer, const char *&outError)205{206JPH_ASSERT(inNumTriangles > 0);207208// Determine position of triangles start209size_t triangle_block_start = ioBuffer.size();210211// Allocate triangle block header212TriangleBlockHeader *header = ioBuffer.Allocate<TriangleBlockHeader>();213214// Compute first vertex that this batch will use (ensuring there's enough room if none of the vertices are shared)215uint start_vertex = Clamp((int)mVertices.size() - 256 + (int)inNumTriangles * 3, 0, (int)mVertices.size());216217// Store the start vertex offset relative to TriangleBlockHeader218size_t offset_to_vertices = mVerticesStartIdx - triangle_block_start + size_t(start_vertex) * sizeof(VertexData);219if (offset_to_vertices & OFFSET_NON_SIGNIFICANT_MASK)220{221outError = "TriangleCodecIndexed8BitPackSOA4Flags: Internal Error: Offset has non-significant bits set";222return size_t(-1);223}224offset_to_vertices >>= OFFSET_NON_SIGNIFICANT_BITS;225if (offset_to_vertices > OFFSET_TO_VERTICES_MASK)226{227outError = "TriangleCodecIndexed8BitPackSOA4Flags: Offset to vertices doesn't fit. Too much data.";228return size_t(-1);229}230header->mFlags = uint32(offset_to_vertices);231232// When we store user data we need to store the offset to the user data in TriangleBlocks233uint padded_triangle_count = AlignUp(inNumTriangles, 4);234if (inStoreUserData)235{236uint32 num_blocks = padded_triangle_count >> 2;237JPH_ASSERT(num_blocks <= OFFSET_TO_USERDATA_MASK);238header->mFlags |= num_blocks << OFFSET_TO_VERTICES_BITS;239}240241// Pack vertices242for (uint t = 0; t < padded_triangle_count; t += 4)243{244TriangleBlock *block = ioBuffer.Allocate<TriangleBlock>();245for (uint vertex_nr = 0; vertex_nr < 3; ++vertex_nr)246for (uint block_tri_idx = 0; block_tri_idx < 4; ++block_tri_idx)247{248// Fetch vertex index. Create degenerate triangles for padding triangles.249bool triangle_available = t + block_tri_idx < inNumTriangles;250uint32 src_vertex_index = triangle_available? inTriangles[t + block_tri_idx].mIdx[vertex_nr] : inTriangles[inNumTriangles - 1].mIdx[0];251252// Check if we've seen this vertex before and if it is in the range that we can encode253uint32 &vertex_index = mVertexMap[src_vertex_index];254if (vertex_index == cNotFound || vertex_index < start_vertex)255{256// Add vertex257vertex_index = (uint32)mVertices.size();258mVertices.push_back(src_vertex_index);259}260261// Store vertex index262uint32 vertex_offset = vertex_index - start_vertex;263if (vertex_offset > 0xff)264{265outError = "TriangleCodecIndexed8BitPackSOA4Flags: Offset doesn't fit in 8 bit";266return size_t(-1);267}268block->mIndices[vertex_nr][block_tri_idx] = (uint8)vertex_offset;269270// Store flags271uint32 flags = triangle_available? inTriangles[t + block_tri_idx].mMaterialIndex : 0;272if (flags > 0xff)273{274outError = "TriangleCodecIndexed8BitPackSOA4Flags: Material index doesn't fit in 8 bit";275return size_t(-1);276}277block->mFlags[block_tri_idx] = (uint8)flags;278}279}280281// Store user data282if (inStoreUserData)283{284uint32 *user_data = ioBuffer.Allocate<uint32>(inNumTriangles);285for (uint t = 0; t < inNumTriangles; ++t)286user_data[t] = inTriangles[t].mUserData;287}288289return triangle_block_start;290}291292/// After all triangles have been packed, this finalizes the header and triangle buffer293void Finalize(const VertexList &inVertices, TriangleHeader *ioHeader, ByteBuffer &ioBuffer) const294{295// Assert that our reservations were correct296JPH_ASSERT(mVertices.size() == mVertexCount);297JPH_ASSERT(ioBuffer.size() == mVerticesStartIdx);298299// Check if anything to do300if (mVertices.empty())301return;302303// Calculate bounding box304AABox bounds;305for (uint32 v : mVertices)306bounds.Encapsulate(Vec3(inVertices[v]));307308// Compress vertices309VertexData *vertices = ioBuffer.Allocate<VertexData>(mVertices.size());310Vec3 compress_scale = Vec3::sReplicate(COMPONENT_MASK) / Vec3::sMax(bounds.GetSize(), Vec3::sReplicate(1.0e-20f));311for (uint32 v : mVertices)312{313UVec4 c = ((Vec3(inVertices[v]) - bounds.mMin) * compress_scale + Vec3::sReplicate(0.5f)).ToInt();314JPH_ASSERT(c.GetX() <= COMPONENT_MASK);315JPH_ASSERT(c.GetY() <= COMPONENT_MASK);316JPH_ASSERT(c.GetZ() <= COMPONENT_MASK);317vertices->mVertexXY = c.GetX() + (c.GetY() << COMPONENT_Y1);318vertices->mVertexZY = c.GetZ() + ((c.GetY() >> COMPONENT_Y1_BITS) << COMPONENT_Y2);319++vertices;320}321322// Store decompression information323bounds.mMin.StoreFloat3(&ioHeader->mOffset);324(bounds.GetSize() / Vec3::sReplicate(COMPONENT_MASK)).StoreFloat3(&ioHeader->mScale);325}326327private:328using VertexMap = Array<uint32>;329330uint32 mVertexCount = 0; ///< Number of vertices calculated during PreparePack331size_t mVerticesStartIdx = 0; ///< Start of the vertices in the output buffer, calculated during PreparePack332Array<uint32> mVertices; ///< Output vertices as an index into the original vertex list (inVertices), sorted according to occurrence333VertexMap mVertexMap; ///< Maps from the original mesh vertex index (inVertices) to the index in our output vertices (mVertices)334};335336/// This class is used to decode and decompress triangle data packed by the EncodingContext337class DecodingContext338{339private:340/// Private helper function to unpack the 1 vertex of 4 triangles (outX contains the x coordinate of triangle 0 .. 3 etc.)341JPH_INLINE void Unpack(const VertexData *inVertices, UVec4Arg inIndex, Vec4 &outX, Vec4 &outY, Vec4 &outZ) const342{343// Get compressed data344UVec4 c1 = UVec4::sGatherInt4<8>(&inVertices->mVertexXY, inIndex);345UVec4 c2 = UVec4::sGatherInt4<8>(&inVertices->mVertexZY, inIndex);346347// Unpack the x y and z component348UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK));349UVec4 yc = UVec4::sOr(c1.LogicalShiftRight<COMPONENT_Y1>(), c2.LogicalShiftRight<COMPONENT_Y2>().LogicalShiftLeft<COMPONENT_Y1_BITS>());350UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK));351352// Convert to float353outX = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX);354outY = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY);355outZ = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ);356}357358/// Private helper function to unpack 4 triangles from a triangle block359JPH_INLINE void Unpack(const TriangleBlock *inBlock, const VertexData *inVertices, Vec4 &outX1, Vec4 &outY1, Vec4 &outZ1, Vec4 &outX2, Vec4 &outY2, Vec4 &outZ2, Vec4 &outX3, Vec4 &outY3, Vec4 &outZ3) const360{361// Get the indices for the three vertices (reads 4 bytes extra, but these are the flags so that's ok)362UVec4 indices = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&inBlock->mIndices[0]));363UVec4 iv1 = indices.Expand4Byte0();364UVec4 iv2 = indices.Expand4Byte4();365UVec4 iv3 = indices.Expand4Byte8();366367#ifdef JPH_CPU_BIG_ENDIAN368// On big endian systems we need to reverse the bytes369iv1 = iv1.Swizzle<SWIZZLE_W, SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X>();370iv2 = iv2.Swizzle<SWIZZLE_W, SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X>();371iv3 = iv3.Swizzle<SWIZZLE_W, SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X>();372#endif373374// Decompress the triangle data375Unpack(inVertices, iv1, outX1, outY1, outZ1);376Unpack(inVertices, iv2, outX2, outY2, outZ2);377Unpack(inVertices, iv3, outX3, outY3, outZ3);378}379380public:381JPH_INLINE explicit DecodingContext(const TriangleHeader *inHeader) :382mOffsetX(Vec4::sReplicate(inHeader->mOffset.x)),383mOffsetY(Vec4::sReplicate(inHeader->mOffset.y)),384mOffsetZ(Vec4::sReplicate(inHeader->mOffset.z)),385mScaleX(Vec4::sReplicate(inHeader->mScale.x)),386mScaleY(Vec4::sReplicate(inHeader->mScale.y)),387mScaleZ(Vec4::sReplicate(inHeader->mScale.z))388{389}390391/// Unpacks triangles in the format t1v1,t1v2,t1v3, t2v1,t2v2,t2v3, ...392JPH_INLINE void Unpack(const void *inTriangleStart, uint32 inNumTriangles, Vec3 *outTriangles) const393{394JPH_ASSERT(inNumTriangles > 0);395const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);396const VertexData *vertices = header->GetVertexData();397const TriangleBlock *t = header->GetTriangleBlock();398const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);399400int triangles_left = inNumTriangles;401402do403{404// Unpack the vertices for 4 triangles405Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z;406Unpack(t, vertices, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z);407408// Transpose it so we get normal vectors409Mat44 v1 = Mat44(v1x, v1y, v1z, Vec4::sZero()).Transposed();410Mat44 v2 = Mat44(v2x, v2y, v2z, Vec4::sZero()).Transposed();411Mat44 v3 = Mat44(v3x, v3y, v3z, Vec4::sZero()).Transposed();412413// Store triangle data414for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left)415{416*outTriangles++ = v1.GetColumn3(i);417*outTriangles++ = v2.GetColumn3(i);418*outTriangles++ = v3.GetColumn3(i);419}420421++t;422}423while (t < end);424}425426/// Tests a ray against the packed triangles427JPH_INLINE float TestRay(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, const void *inTriangleStart, uint32 inNumTriangles, float inClosest, uint32 &outClosestTriangleIndex) const428{429JPH_ASSERT(inNumTriangles > 0);430const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);431const VertexData *vertices = header->GetVertexData();432const TriangleBlock *t = header->GetTriangleBlock();433const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);434435Vec4 closest = Vec4::sReplicate(inClosest);436UVec4 closest_triangle_idx = UVec4::sZero();437438UVec4 start_triangle_idx = UVec4::sZero();439do440{441// Unpack the vertices for 4 triangles442Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z;443Unpack(t, vertices, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z);444445// Perform ray vs triangle test446Vec4 distance = RayTriangle4(inRayOrigin, inRayDirection, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z);447448// Update closest with the smaller values449UVec4 smaller = Vec4::sLess(distance, closest);450closest = Vec4::sSelect(closest, distance, smaller);451452// Update triangle index with the smallest values453UVec4 triangle_idx = start_triangle_idx + UVec4(0, 1, 2, 3);454closest_triangle_idx = UVec4::sSelect(closest_triangle_idx, triangle_idx, smaller);455456// Next block457++t;458start_triangle_idx += UVec4::sReplicate(4);459}460while (t < end);461462// Get the smallest component463Vec4::sSort4(closest, closest_triangle_idx);464outClosestTriangleIndex = closest_triangle_idx.GetX();465return closest.GetX();466}467468/// Decode a single triangle469inline void GetTriangle(const void *inTriangleStart, uint32 inTriangleIdx, Vec3 &outV1, Vec3 &outV2, Vec3 &outV3) const470{471const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);472const VertexData *vertices = header->GetVertexData();473const TriangleBlock *block = header->GetTriangleBlock() + (inTriangleIdx >> 2);474uint32 block_triangle_idx = inTriangleIdx & 0b11;475476// Get the 3 vertices477const VertexData &v1 = vertices[block->mIndices[0][block_triangle_idx]];478const VertexData &v2 = vertices[block->mIndices[1][block_triangle_idx]];479const VertexData &v3 = vertices[block->mIndices[2][block_triangle_idx]];480481// Pack the vertices482UVec4 c1(v1.mVertexXY, v2.mVertexXY, v3.mVertexXY, 0);483UVec4 c2(v1.mVertexZY, v2.mVertexZY, v3.mVertexZY, 0);484485// Unpack the x y and z component486UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK));487UVec4 yc = UVec4::sOr(c1.LogicalShiftRight<COMPONENT_Y1>(), c2.LogicalShiftRight<COMPONENT_Y2>().LogicalShiftLeft<COMPONENT_Y1_BITS>());488UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK));489490// Convert to float491Vec4 vx = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX);492Vec4 vy = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY);493Vec4 vz = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ);494495// Transpose it so we get normal vectors496Mat44 trans = Mat44(vx, vy, vz, Vec4::sZero()).Transposed();497outV1 = trans.GetAxisX();498outV2 = trans.GetAxisY();499outV3 = trans.GetAxisZ();500}501502/// Get user data for a triangle503JPH_INLINE uint32 GetUserData(const void *inTriangleStart, uint32 inTriangleIdx) const504{505const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);506const uint32 *user_data = header->GetUserData();507return user_data != nullptr? user_data[inTriangleIdx] : 0;508}509510/// Get flags for entire triangle block511JPH_INLINE static void sGetFlags(const void *inTriangleStart, uint32 inNumTriangles, uint8 *outTriangleFlags)512{513JPH_ASSERT(inNumTriangles > 0);514const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);515const TriangleBlock *t = header->GetTriangleBlock();516const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);517518int triangles_left = inNumTriangles;519do520{521for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left)522*outTriangleFlags++ = t->mFlags[i];523524++t;525}526while (t < end);527}528529/// Get flags for a particular triangle530JPH_INLINE static uint8 sGetFlags(const void *inTriangleStart, int inTriangleIndex)531{532const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);533const TriangleBlock *first_block = header->GetTriangleBlock();534return first_block[inTriangleIndex >> 2].mFlags[inTriangleIndex & 0b11];535}536537/// Unpacks triangles and flags, convenience function538JPH_INLINE void Unpack(const void *inTriangleStart, uint32 inNumTriangles, Vec3 *outTriangles, uint8 *outTriangleFlags) const539{540Unpack(inTriangleStart, inNumTriangles, outTriangles);541sGetFlags(inTriangleStart, inNumTriangles, outTriangleFlags);542}543544private:545Vec4 mOffsetX;546Vec4 mOffsetY;547Vec4 mOffsetZ;548Vec4 mScaleX;549Vec4 mScaleY;550Vec4 mScaleZ;551};552};553554JPH_NAMESPACE_END555556557