Skip to content

Conversation

@kevyuu
Copy link
Contributor

@kevyuu kevyuu commented Dec 28, 2025

Description

Obb calculation using DiTO-14

Testing

Using an example demo pull request

@devshgraphicsprogramming
Copy link
Member

@kevyuu link to old PR in description

Comment on lines +14 to +16
class COBBGenerator
{
public:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation

Comment on lines +237 to +238
using FetchFn = std::function<hlsl::float32_t3(size_t vertexIndex)>;
FetchFn fetch;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is always runtime polymorphic (wont specialize nicely based on a lambda), VertexCollection should be templated, the default template arg can be std::function

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw I don't think all the OBB stuff should be nested inside CPolygonGeometryManipulator

Comment on lines +241 to +250
static auto fromSpan(std::span<const hlsl::float32_t3> vertices) -> VertexCollection
{
return VertexCollection{
.fetch = [data = vertices.data()](size_t vertexIndex)-> hlsl::float32_t3
{
return data[vertexIndex];
},
.size = vertices.size()
};
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a different template instantiation of VertexCollection

Comment on lines +33 to +35
point_t mid;
std::array<point_t, D> axes;
point_t ext;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a matrix<float32_t,D,D+1> instead

@karimsayedre which OBB is more useful for you in general, the one that transforms a [0,1]^3 or [-1,1]^3 obb ?

Comment on lines +531 to +585
//
core::smart_refctd_ptr<ICPUMeshBuffer> IMeshManipulator::calculateSmoothNormals(ICPUMeshBuffer* inbuffer, bool makeNewMesh, float epsilon, uint32_t normalAttrID, VxCmpFunction vxcmp)
{
if (inbuffer == nullptr)
{
_NBL_DEBUG_BREAK_IF(true);
return nullptr;
}

//Mesh has to have unique primitives
if (inbuffer->getIndexType() != E_INDEX_TYPE::EIT_UNKNOWN)
{
_NBL_DEBUG_BREAK_IF(true);
return nullptr;
}

core::smart_refctd_ptr<ICPUMeshBuffer> outbuffer;
if (makeNewMesh)
{
outbuffer = core::move_and_static_cast<ICPUMeshBuffer>(inbuffer->clone(0u));

const auto normalAttr = inbuffer->getNormalAttributeIx();
auto normalBinding = inbuffer->getBindingNumForAttribute(normalAttr);
const auto oldPipeline = inbuffer->getPipeline();
auto vertexParams = oldPipeline->getCachedCreationParams().vertexInput;
bool notUniqueBinding = false;
for (uint16_t attr=0u; attr<SVertexInputParams::MAX_VERTEX_ATTRIB_COUNT; attr++)
if (attr!=normalAttr && (vertexParams.enabledAttribFlags&(0x1u<<attr))!=0u && vertexParams.attributes[attr].binding==normalBinding)
notUniqueBinding = true;
if (notUniqueBinding)
{
int32_t firstBindingNotUsed = hlsl::findLSB(vertexParams.enabledBindingFlags^0xffffu);
assert(firstBindingNotUsed>0 && firstBindingNotUsed<SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT);
normalBinding = static_cast<uint32_t>(firstBindingNotUsed);

vertexParams.attributes[normalAttr].binding = normalBinding;
vertexParams.enabledBindingFlags |= 0x1u<<normalBinding;
}

const auto normalFormatBytesize = asset::getTexelOrBlockBytesize(inbuffer->getAttribFormat(normalAttr));
auto normalBuf = ICPUBuffer::create({ normalFormatBytesize*IMeshManipulator::upperBoundVertexID(inbuffer) });
outbuffer->setVertexBufferBinding({0ull,std::move(normalBuf)},normalBinding);

auto pipeline = core::move_and_static_cast<ICPURenderpassIndependentPipeline>(oldPipeline->clone(0u));
vertexParams.bindings[normalBinding].stride = normalFormatBytesize;
vertexParams.attributes[normalAttr].relativeOffset = 0u;
pipeline->getCachedCreationParams().vertexInput = vertexParams;
outbuffer->setPipeline(std::move(pipeline));
}
else
outbuffer = core::smart_refctd_ptr<ICPUMeshBuffer>(inbuffer);
CSmoothNormalGenerator::calculateNormals(outbuffer.get(), epsilon, normalAttrID, vxcmp);

return outbuffer;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can delete this because we've reimplemented

Comment on lines +587 to +659
// Used by createMeshBufferWelded only
static bool cmpVertices(ICPUMeshBuffer* _inbuf, const void* _va, const void* _vb, size_t _vsize, const IMeshManipulator::SErrorMetric* _errMetrics)
{
auto cmpInteger = [](uint32_t* _a, uint32_t* _b, size_t _n) -> bool {
return !memcmp(_a, _b, _n*4);
};

constexpr uint32_t MAX_ATTRIBS = ICPUMeshBuffer::MAX_VERTEX_ATTRIB_COUNT;

const uint8_t* va = reinterpret_cast<const uint8_t*>(_va), *vb = reinterpret_cast<const uint8_t*>(_vb);
for (size_t i = 0u; i < MAX_ATTRIBS; ++i)
{
if (!_inbuf->isAttributeEnabled(i))
continue;

const auto atype = _inbuf->getAttribFormat(i);
const auto cpa = getFormatChannelCount(atype);

if (isIntegerFormat(atype) || isScaledFormat(atype))
{
uint32_t attr[8];
ICPUMeshBuffer::getAttribute(attr, va, atype);
ICPUMeshBuffer::getAttribute(attr+4, vb, atype);
if (!cmpInteger(attr, attr+4, cpa))
return false;
}
else
{
core::vectorSIMDf attr[2];
ICPUMeshBuffer::getAttribute(attr[0], va, atype);
ICPUMeshBuffer::getAttribute(attr[1], vb, atype);
if (!IMeshManipulator::compareFloatingPointAttribute(attr[0], attr[1], cpa, _errMetrics[i]))
return false;
}

const uint32_t sz = getTexelOrBlockBytesize(atype);
va += sz;
vb += sz;
}

return true;
}

//! Creates a copy of a mesh, which will have identical vertices welded together
core::smart_refctd_ptr<ICPUMeshBuffer> IMeshManipulator::createMeshBufferWelded(ICPUMeshBuffer *inbuffer, const SErrorMetric* _errMetrics, const bool& optimIndexType, const bool& makeNewMesh)
{
if (!inbuffer || !inbuffer->getPipeline())
return nullptr;

constexpr uint32_t MAX_ATTRIBS = ICPUMeshBuffer::MAX_VERTEX_ATTRIB_COUNT;

bool bufferPresent[MAX_ATTRIBS];

size_t vertexAttrSize[MAX_ATTRIBS];
size_t vertexSize = 0;
for (size_t i=0; i<MAX_ATTRIBS; i++)
{
const auto& buf = inbuffer->getAttribBoundBuffer(i).buffer;
bufferPresent[i] = inbuffer->isAttributeEnabled(i);
if (bufferPresent[i] && buf)
{
const E_FORMAT componentType = inbuffer->getAttribFormat(i);
vertexAttrSize[i] = getTexelOrBlockBytesize(componentType);
vertexSize += vertexAttrSize[i];
}
}

auto cmpfunc = [&, inbuffer, vertexSize, _errMetrics](const void* _va, const void* _vb) {
return cmpVertices(inbuffer, _va, _vb, vertexSize, _errMetrics);
};

const uint32_t vertexCount = IMeshManipulator::upperBoundVertexID(inbuffer);
const E_INDEX_TYPE oldIndexType = inbuffer->getIndexType();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can delete this because we've reimplemented

Comment on lines +1663 to +1875
float IMeshManipulator::DistanceToLine(core::vectorSIMDf P0, core::vectorSIMDf P1, core::vectorSIMDf InPoint)
{
core::vectorSIMDf PointToStart = InPoint - P0;
core::vectorSIMDf Diff = core::cross(P0 - P1, PointToStart);

return core::dot(Diff, Diff).x;
}

float IMeshManipulator::DistanceToPlane(core::vectorSIMDf InPoint, core::vectorSIMDf PlanePoint, core::vectorSIMDf PlaneNormal)
{
core::vectorSIMDf PointToPlane = InPoint - PlanePoint;

return (core::dot(PointToPlane, PlaneNormal).x >= 0) ? core::abs(core::dot(PointToPlane, PlaneNormal).x) : 0;
}

core::matrix3x4SIMD IMeshManipulator::calculateOBB(const nbl::asset::ICPUMeshBuffer* meshbuffer)
{
auto FindMinMaxProj = [&](const core::vectorSIMDf& Dir, const core::vectorSIMDf Extrema[]) -> core::vectorSIMDf
{
float MinPoint, MaxPoint;
MinPoint = MaxPoint = core::dot(Dir, Extrema[0]).x;

for (int i = 1; i < 12; i++) {
float Proj = core::dot(Dir, Extrema[i]).x;
if (MinPoint > Proj) MinPoint = Proj;
if (MaxPoint < Proj) MaxPoint = Proj;
}

return core::vectorSIMDf(MaxPoint, MinPoint, 0);
};

auto ComputeAxis = [&](const core::vectorSIMDf& P0, const core::vectorSIMDf& P1, const core::vectorSIMDf& P2, core::vectorSIMDf* AxesEdge, float& PrevQuality, const core::vectorSIMDf Extrema[]) -> void
{
core::vectorSIMDf e0 = P1 - P0;
core::vectorSIMDf Edges[3];
Edges[0] = e0 / core::length(e0);
Edges[1] = core::cross(P2 - P1, P1 - P0);
Edges[1] = Edges[1] / core::length(Edges[1]);
Edges[2] = core::cross(Edges[0], Edges[1]);

core::vectorSIMDf Edge10Proj = FindMinMaxProj(Edges[0], Extrema);
core::vectorSIMDf Edge20Proj = FindMinMaxProj(Edges[1], Extrema);
core::vectorSIMDf Edge30Proj = FindMinMaxProj(Edges[2], Extrema);
core::vectorSIMDf Max2 = core::vectorSIMDf(Edge10Proj.x, Edge20Proj.x, Edge30Proj.x);
core::vectorSIMDf Min2 = core::vectorSIMDf(Edge10Proj.y, Edge20Proj.y, Edge30Proj.y);
core::vectorSIMDf Diff = Max2 - Min2;
float Quality = Diff.x * Diff.y + Diff.x * Diff.z + Diff.y * Diff.z;

if (Quality < PrevQuality) {
PrevQuality = Quality;
for (int i = 0; i < 3; i++) {
AxesEdge[i] = Edges[i];
}
}
};

core::vectorSIMDf Extrema[12];
float A = (core::sqrt(5.0f) - 1.0f) / 2.0f;
core::vectorSIMDf N[6];
N[0] = core::vectorSIMDf(0, 1, A);
N[1] = core::vectorSIMDf(0, 1, -A);
N[2] = core::vectorSIMDf(1, A, 0);
N[3] = core::vectorSIMDf(1, -A, 0);
N[4] = core::vectorSIMDf(A, 0, 1);
N[5] = core::vectorSIMDf(A, 0, -1);
float Bs[12];
float B;
int indexcount = meshbuffer->getIndexCount();
core::vectorSIMDf CachedVertex = meshbuffer->getPosition(meshbuffer->getIndexValue(0));
core::vectorSIMDf AABBMax = CachedVertex;
core::vectorSIMDf AABBMin = CachedVertex;
for (int k = 0; k < 12; k += 2) {
B = core::dot(N[k / 2], CachedVertex).x;
Extrema[k] = core::vectorSIMDf(CachedVertex.x, CachedVertex.y, CachedVertex.z); Bs[k] = B;
Extrema[k + 1] = core::vectorSIMDf(CachedVertex.x, CachedVertex.y, CachedVertex.z); Bs[k + 1] = B;
}
for (uint32_t j = 1u; j < indexcount; j += 1u) {
CachedVertex = meshbuffer->getPosition(meshbuffer->getIndexValue(j));
for (int k = 0; k < 12; k += 2) {
B = core::dot(N[k / 2], CachedVertex).x;
if (B > Bs[k] || j == 0) { Extrema[k] = core::vectorSIMDf(CachedVertex.x, CachedVertex.y, CachedVertex.z); Bs[k] = B; }
if (B < Bs[k + 1] || j == 0) { Extrema[k + 1] = core::vectorSIMDf(CachedVertex.x, CachedVertex.y, CachedVertex.z); Bs[k + 1] = B; }
}
AABBMax = core::max(AABBMax, CachedVertex);
AABBMin = core::min(AABBMin, CachedVertex);
}

int LBTE1 = -1;
float MaxDiff = 0;
for (int i = 0; i < 12; i += 2) {
core::vectorSIMDf C = (Extrema[i]) - (Extrema[i + 1]); float TempDiff = core::dot(C, C).x; if (TempDiff > MaxDiff) { MaxDiff = TempDiff; LBTE1 = i; }
}
assert(LBTE1 != -1);

core::vectorSIMDf P0 = Extrema[LBTE1];
core::vectorSIMDf P1 = Extrema[LBTE1 + 1];

int LBTE3 = 0;
float MaxDist = 0;
int RemoveAt = 0;

for (int i = 0; i < 10; i++) {
int index = i;
if (index >= LBTE1) index += 2;
float TempDist = DistanceToLine(P0, P1, core::vectorSIMDf(Extrema[index].x, Extrema[index].y, Extrema[index].z));
if (TempDist > MaxDist || i == 0) {
MaxDist = TempDist;
LBTE3 = index;
RemoveAt = i;
}
}

core::vectorSIMDf P2 = Extrema[LBTE3];
core::vectorSIMDf ExtremaRemainingTemp[9];
for (int i = 0; i < 9; i++) {
int index = i;
if (index >= RemoveAt) index += 1;
if (index >= LBTE1) index += 2;
ExtremaRemainingTemp[i] = core::vectorSIMDf(Extrema[index].x, Extrema[index].y, Extrema[index].z, index);
}

float MaxDistPlane = -9999999.0f;
float MinDistPlane = -9999999.0f;
float TempDistPlane = 0;
core::vectorSIMDf Q0 = core::vectorSIMDf(0, 0, 0);
core::vectorSIMDf Q1 = core::vectorSIMDf(0, 0, 0);
core::vectorSIMDf Norm = core::cross(P2 - P1, P2 - P0);
Norm /= core::length(Norm);
for (int i = 0; i < 9; i++) {
TempDistPlane = DistanceToPlane(core::vectorSIMDf(ExtremaRemainingTemp[i].x, ExtremaRemainingTemp[i].y, ExtremaRemainingTemp[i].z), P0, Norm);
if (TempDistPlane > MaxDistPlane || i == 0) {
MaxDistPlane = TempDistPlane;
Q0 = Extrema[(int)ExtremaRemainingTemp[i].w];
}
TempDistPlane = DistanceToPlane(core::vectorSIMDf(ExtremaRemainingTemp[i].x, ExtremaRemainingTemp[i].y, ExtremaRemainingTemp[i].z), P0, -Norm);
if (TempDistPlane > MinDistPlane || i == 0) {
MinDistPlane = TempDistPlane;
Q1 = Extrema[(int)ExtremaRemainingTemp[i].w];
}
}

float BestQuality = 99999999999999.0f;
core::vectorSIMDf BestAxis[3];
ComputeAxis(P0, P1, P2, BestAxis, BestQuality, Extrema);
ComputeAxis(P2, P0, P1, BestAxis, BestQuality, Extrema);
ComputeAxis(P1, P2, P0, BestAxis, BestQuality, Extrema);

ComputeAxis(P1, Q0, P0, BestAxis, BestQuality, Extrema);
ComputeAxis(P0, P1, Q0, BestAxis, BestQuality, Extrema);
ComputeAxis(Q0, P0, P1, BestAxis, BestQuality, Extrema);

ComputeAxis(P2, Q0, P0, BestAxis, BestQuality, Extrema);
ComputeAxis(P0, P2, Q0, BestAxis, BestQuality, Extrema);
ComputeAxis(Q0, P0, P2, BestAxis, BestQuality, Extrema);

ComputeAxis(P1, Q0, P2, BestAxis, BestQuality, Extrema);
ComputeAxis(P2, P1, Q0, BestAxis, BestQuality, Extrema);
ComputeAxis(Q0, P2, P1, BestAxis, BestQuality, Extrema);

ComputeAxis(P1, Q1, P0, BestAxis, BestQuality, Extrema);
ComputeAxis(P0, P1, Q1, BestAxis, BestQuality, Extrema);
ComputeAxis(Q1, P0, P1, BestAxis, BestQuality, Extrema);

ComputeAxis(P2, Q1, P0, BestAxis, BestQuality, Extrema);
ComputeAxis(P0, P2, Q1, BestAxis, BestQuality, Extrema);
ComputeAxis(Q1, P0, P2, BestAxis, BestQuality, Extrema);

ComputeAxis(P1, Q1, P2, BestAxis, BestQuality, Extrema);
ComputeAxis(P2, P1, Q1, BestAxis, BestQuality, Extrema);
ComputeAxis(Q1, P2, P1, BestAxis, BestQuality, Extrema);

core::matrix3x4SIMD TransMat = core::matrix3x4SIMD(
BestAxis[0].x, BestAxis[1].x, BestAxis[2].x, 0,
BestAxis[0].y, BestAxis[1].y, BestAxis[2].y, 0,
BestAxis[0].z, BestAxis[1].z, BestAxis[2].z, 0);

core::vectorSIMDf MinPoint;
core::vectorSIMDf MaxPoint;
CachedVertex = meshbuffer->getPosition(meshbuffer->getIndexValue(0));
MinPoint = core::vectorSIMDf(core::dot(BestAxis[0], CachedVertex).x, core::dot(BestAxis[1], CachedVertex).x, core::dot(BestAxis[2], CachedVertex).x);
MaxPoint = MinPoint;
for (uint32_t j = 1u; j < indexcount; j += 1u)
{
CachedVertex = meshbuffer->getPosition(meshbuffer->getIndexValue(j));
core::vectorSIMDf Proj = core::vectorSIMDf(core::dot(BestAxis[0], CachedVertex).x, core::dot(BestAxis[1], CachedVertex).x, core::dot(BestAxis[2], CachedVertex).x);
MinPoint = core::min(MinPoint, Proj);
MaxPoint = core::max(MaxPoint, Proj);
}

core::vectorSIMDf OBBDiff = MaxPoint - MinPoint;
float OBBQuality = OBBDiff.x * OBBDiff.y + OBBDiff.y * OBBDiff.z + OBBDiff.z * OBBDiff.x;

core::vectorSIMDf ABBDiff = AABBMax - AABBMin;
float ABBQuality = ABBDiff.x * ABBDiff.y + ABBDiff.y * ABBDiff.z + ABBDiff.z * ABBDiff.x;
core::matrix3x4SIMD scaleMat;
core::matrix3x4SIMD translationMat;
translationMat.setTranslation(-(MinPoint) / OBBDiff);
scaleMat.setScale(OBBDiff);
TransMat = core::concatenateBFollowedByA(TransMat, scaleMat);
TransMat = core::concatenateBFollowedByA(TransMat, translationMat);
if (ABBQuality < OBBQuality) {
translationMat.setTranslation(-(AABBMin) / ABBDiff);
scaleMat.setScale(ABBDiff);
TransMat = core::matrix3x4SIMD(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0);
TransMat = core::concatenateBFollowedByA(TransMat, scaleMat);
TransMat = core::concatenateBFollowedByA(TransMat, translationMat);
}

return TransMat;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnastaZIuk old code

Comment on lines +37 to +57
hlsl::shapes::OBB<> COBBGenerator::compute(const VertexCollection& vertices)
{
constexpr size_t SAMPLE_DIR_COUNT = 7; // Number of sample directions
constexpr size_t SAMPLE_COUNT = SAMPLE_DIR_COUNT * 2;

if (vertices.size <= 0)
{
return hlsl::shapes::OBB<>::createAxisAligned({}, {});
}

static auto getQualityValue = [](hlsl::float32_t3 len) -> hlsl::float32_t
{
return len.x * len.y + len.x * len.z + len.y * len.z; //half box area
};

using ExtremalVertices = Extremals<hlsl::float32_t3, SAMPLE_DIR_COUNT>;
using ExtremalProjections = Extremals<hlsl::float32_t, SAMPLE_DIR_COUNT>;
using Axes = std::array<hlsl::float32_t3, 3>;
using Edges = std::array<hlsl::float32_t3, 3>;

struct ExtremalSamples

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnastaZIuk new code

Comment on lines +22 to +25
hlsl::shapes::OBB<> CPolygonGeometryManipulator::calculateOBB(const VertexCollection& vertices)
{
return COBBGenerator::compute(vertices);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be inline, or actually why is anything to do with OBBs in CpolygonGeometryManitpulator ?

COBBGenerator can be completely separate (also it works on anything that has vertices which can be turned into a convex hull of an object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants