Skip to content

Asset API updates, parallel blake3, inline AABB extender, IO policies, runtime tunning, mesh loaders & writers#1000

Open
AnastaZIuk wants to merge 42 commits intomasterfrom
loaders
Open

Asset API updates, parallel blake3, inline AABB extender, IO policies, runtime tunning, mesh loaders & writers#1000
AnastaZIuk wants to merge 42 commits intomasterfrom
loaders

Conversation

@AnastaZIuk
Copy link
Member

No description provided.

Comment on lines +404 to 472
double stlNormalizeColorComponentToUnit(double value)
{
if (!std::isfinite(value))
return 0.0;
if (value > 1.0)
value /= 255.0;
return std::clamp(value, 0.0, 1.0);
}

{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, "endsolid ", context->fileOffset, 9);
uint16_t stlPackViscamColorFromB8G8R8A8(const uint32_t color)
{
const void* src[4] = { &color, nullptr, nullptr, nullptr };
uint16_t packed = 0u;
convertColor<EF_B8G8R8A8_UNORM, EF_A1R5G5B5_UNORM_PACK16>(src, &packed, 0u, 0u);
packed |= 0x8000u;
return packed;
}

context->fileOffset += success.getBytesProcessed();
}
const ICPUPolygonGeometry::SDataView* stlFindColorView(const ICPUPolygonGeometry* geom, const size_t vertexCount)
{
if (!geom)
return nullptr;

const auto& auxViews = geom->getAuxAttributeViews();
const ICPUPolygonGeometry::SDataView* fallback = nullptr;
for (const auto& view : auxViews)
{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, headerTxt, context->fileOffset, sizeof(headerTxt) - 1);

context->fileOffset += success.getBytesProcessed();
if (!view || view.getElementCount() != vertexCount)
continue;
const uint32_t channels = getFormatChannelCount(view.composed.format);
if (channels < 3u)
continue;
if (view.composed.format == EF_B8G8R8A8_UNORM)
return &view;
if (!fallback)
fallback = &view;
}
return fallback;
}

bool stlDecodeColorB8G8R8A8(const ICPUPolygonGeometry::SDataView& colorView, const uint32_t ix, uint32_t& outColor)
{
if (colorView.composed.format == EF_B8G8R8A8_UNORM && colorView.composed.getStride() == sizeof(uint32_t))
{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, name.c_str(), context->fileOffset, name.size());

context->fileOffset += success.getBytesProcessed();
const auto* const ptr = reinterpret_cast<const uint8_t*>(colorView.getPointer());
if (!ptr)
return false;
std::memcpy(&outColor, ptr + static_cast<size_t>(ix) * sizeof(uint32_t), sizeof(outColor));
return true;
}

hlsl::float64_t4 decoded = {};
if (!colorView.decodeElement(ix, decoded))
return false;
const double rgbaUnit[4] = {
stlNormalizeColorComponentToUnit(decoded.x),
stlNormalizeColorComponentToUnit(decoded.y),
stlNormalizeColorComponentToUnit(decoded.z),
stlNormalizeColorComponentToUnit(decoded.w)
};
encodePixels<EF_B8G8R8A8_UNORM, double>(&outColor, rgbaUnit);
return true;
}

void CSTLMeshWriter::getVectorAsStringLine(const core::vectorSIMDf& v, std::string& s) const
void stlDecodeColorUnitRGBAFromB8G8R8A8(const uint32_t color, double (&out)[4])
{
std::ostringstream tmp;
tmp << v.X << " " << v.Y << " " << v.Z << "\n";
s = std::string(tmp.str().c_str());
const void* src[4] = { &color, nullptr, nullptr, nullptr };
decodePixels<EF_B8G8R8A8_UNORM, double>(src, out, 0u, 0u);
}

void CSTLMeshWriter::writeFaceText(
const core::vectorSIMDf& v1,
const core::vectorSIMDf& v2,
const core::vectorSIMDf& v3,
SContext* context)
bool writeMeshBinary(const asset::ICPUPolygonGeometry* geom, SContext* context)
{
core::vectorSIMDf vertex1 = v3;
core::vectorSIMDf vertex2 = v2;
core::vectorSIMDf vertex3 = v1;
core::vectorSIMDf normal = core::plane3dSIMDf(vertex1, vertex2, vertex3).getNormal();
std::string tmp;
if (!geom || !context || !context->writeContext.outputFile)
return false;

auto flipVectors = [&]()
{
vertex1.X = -vertex1.X;
vertex2.X = -vertex2.X;
vertex3.X = -vertex3.X;
normal = core::plane3dSIMDf(vertex1, vertex2, vertex3).getNormal();
};

if (!(context->writeContext.params.flags & E_WRITER_FLAGS::EWF_MESH_IS_RIGHT_HANDED))
flipVectors();

{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, "facet normal ", context->fileOffset, 13);
const auto& posView = geom->getPositionView();
if (!posView)
return false;

context->fileOffset += success.getBytesProcessed();
}
const bool flipHandedness = !(context->writeContext.params.flags & E_WRITER_FLAGS::EWF_MESH_IS_RIGHT_HANDED);
const size_t vertexCount = posView.getElementCount();
if (vertexCount == 0ull)
return false;

getVectorAsStringLine(normal, tmp);
core::vector<uint32_t> indexData;
const uint32_t* indices = nullptr;
uint32_t facenum = 0u;
if (!decodeTriangleIndices(geom, posView, indexData, indices, facenum))
return false;

{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, tmp.c_str(), context->fileOffset, tmp.size());
const size_t outputSize = stl_writer_detail::BinaryPrefixBytes + static_cast<size_t>(facenum) * stl_writer_detail::BinaryTriangleRecordBytes;
std::unique_ptr<uint8_t[]> output(new (std::nothrow) uint8_t[outputSize]);
if (!output)
return false;
uint8_t* dst = output.get();

context->fileOffset += success.getBytesProcessed();
}
std::memset(dst, 0, stl_writer_detail::BinaryHeaderBytes);
dst += stl_writer_detail::BinaryHeaderBytes;

std::memcpy(dst, &facenum, sizeof(facenum));
dst += sizeof(facenum);

const auto& normalView = geom->getNormalView();
const bool hasNormals = static_cast<bool>(normalView);
const auto* const colorView = stlFindColorView(geom, vertexCount);
const hlsl::float32_t3* const tightPositions = getTightFloat3View(posView);
const hlsl::float32_t3* const tightNormals = hasNormals ? getTightFloat3View(normalView) : nullptr;
const float handednessSign = flipHandedness ? -1.f : 1.f;

auto decodePosition = [&](const uint32_t ix, hlsl::float32_t3& out)->bool
{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, " outer loop\n", context->fileOffset, 13);
if (tightPositions)
{
out = tightPositions[ix];
return true;
}
return posView.decodeElement(ix, out);
};

context->fileOffset += success.getBytesProcessed();
}
auto decodeNormal = [&](const uint32_t ix, hlsl::float32_t3& out)->bool
{
if (!hasNormals)
return false;
if (tightNormals)
{
out = tightNormals[ix];
return true;
}
return normalView.decodeElement(ix, out);
};
auto computeFaceColor = [&](const uint32_t i0, const uint32_t i1, const uint32_t i2, uint16_t& outColor)->bool
{
outColor = 0u;
if (!colorView)
return true;
uint32_t c0 = 0u, c1 = 0u, c2 = 0u;
if (!stlDecodeColorB8G8R8A8(*colorView, i0, c0))
return false;
if (!stlDecodeColorB8G8R8A8(*colorView, i1, c1))
return false;
if (!stlDecodeColorB8G8R8A8(*colorView, i2, c2))
return false;
double rgba0[4] = {};
double rgba1[4] = {};
double rgba2[4] = {};
stlDecodeColorUnitRGBAFromB8G8R8A8(c0, rgba0);
stlDecodeColorUnitRGBAFromB8G8R8A8(c1, rgba1);
stlDecodeColorUnitRGBAFromB8G8R8A8(c2, rgba2);
const double rgbaAvg[4] = {
(rgba0[0] + rgba1[0] + rgba2[0]) / 3.0,
(rgba0[1] + rgba1[1] + rgba2[1]) / 3.0,
(rgba0[2] + rgba1[2] + rgba2[2]) / 3.0,
1.0
};
uint32_t avgColor = 0u;
encodePixels<EF_B8G8R8A8_UNORM, double>(&avgColor, rgbaAvg);
outColor = stlPackViscamColorFromB8G8R8A8(avgColor);
return true;
};
auto writeRecord = [&dst](const float nx, const float ny, const float nz, const float v1x, const float v1y, const float v1z, const float v2x, const float v2y, const float v2z, const float v3x, const float v3y, const float v3z, const uint16_t attribute)->void
{
const float payload[stl_writer_detail::BinaryTriangleFloatCount] = {
nx, ny, nz,
v1x, v1y, v1z,
v2x, v2y, v2z,
v3x, v3y, v3z
};
std::memcpy(dst, payload, stl_writer_detail::BinaryTriangleFloatBytes);
dst += stl_writer_detail::BinaryTriangleFloatBytes;
std::memcpy(dst, &attribute, stl_writer_detail::BinaryTriangleAttributeBytes);
dst += stl_writer_detail::BinaryTriangleAttributeBytes;
};

const bool hasFastTightPath = (indices == nullptr) && (tightPositions != nullptr) && (!hasNormals || (tightNormals != nullptr));
if (hasFastTightPath && hasNormals)
{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, " vertex ", context->fileOffset, 11);
bool allFastNormalsNonZero = true;
const size_t normalCount = static_cast<size_t>(facenum) * 3ull;
for (size_t i = 0ull; i < normalCount; ++i)
{
const auto& n = tightNormals[i];
if (n.x == 0.f && n.y == 0.f && n.z == 0.f)
{
allFastNormalsNonZero = false;
break;
}
}

context->fileOffset += success.getBytesProcessed();
const hlsl::float32_t3* posTri = tightPositions;
const hlsl::float32_t3* nrmTri = tightNormals;
if (allFastNormalsNonZero)
{
for (uint32_t primIx = 0u; primIx < facenum; ++primIx, posTri += 3u, nrmTri += 3u)
{
uint16_t faceColor = 0u;
if (!computeFaceColor(primIx * 3u + 0u, primIx * 3u + 1u, primIx * 3u + 2u, faceColor))
return false;

const hlsl::float32_t3 vertex1 = posTri[2u];
const hlsl::float32_t3 vertex2 = posTri[1u];
const hlsl::float32_t3 vertex3 = posTri[0u];
const float vertex1x = vertex1.x * handednessSign;
const float vertex2x = vertex2.x * handednessSign;
const float vertex3x = vertex3.x * handednessSign;

hlsl::float32_t3 attrNormal = nrmTri[0u];
if (flipHandedness)
attrNormal.x = -attrNormal.x;

writeRecord(
attrNormal.x, attrNormal.y, attrNormal.z,
vertex1x, vertex1.y, vertex1.z,
vertex2x, vertex2.y, vertex2.z,
vertex3x, vertex3.y, vertex3.z,
faceColor);
}
}
else
{
for (uint32_t primIx = 0u; primIx < facenum; ++primIx, posTri += 3u, nrmTri += 3u)
{
uint16_t faceColor = 0u;
if (!computeFaceColor(primIx * 3u + 0u, primIx * 3u + 1u, primIx * 3u + 2u, faceColor))
return false;

const hlsl::float32_t3 vertex1 = posTri[2u];
const hlsl::float32_t3 vertex2 = posTri[1u];
const hlsl::float32_t3 vertex3 = posTri[0u];
const float vertex1x = vertex1.x * handednessSign;
const float vertex2x = vertex2.x * handednessSign;
const float vertex3x = vertex3.x * handednessSign;

float normalX = 0.f;
float normalY = 0.f;
float normalZ = 0.f;
hlsl::float32_t3 attrNormal = nrmTri[0u];
if (attrNormal.x == 0.f && attrNormal.y == 0.f && attrNormal.z == 0.f)
attrNormal = nrmTri[1u];
if (attrNormal.x == 0.f && attrNormal.y == 0.f && attrNormal.z == 0.f)
attrNormal = nrmTri[2u];
if (!(attrNormal.x == 0.f && attrNormal.y == 0.f && attrNormal.z == 0.f))
{
if (flipHandedness)
attrNormal.x = -attrNormal.x;
normalX = attrNormal.x;
normalY = attrNormal.y;
normalZ = attrNormal.z;
}

if (normalX == 0.f && normalY == 0.f && normalZ == 0.f)
{
const float edge21x = vertex2x - vertex1x;
const float edge21y = vertex2.y - vertex1.y;
const float edge21z = vertex2.z - vertex1.z;
const float edge31x = vertex3x - vertex1x;
const float edge31y = vertex3.y - vertex1.y;
const float edge31z = vertex3.z - vertex1.z;

normalX = edge21y * edge31z - edge21z * edge31y;
normalY = edge21z * edge31x - edge21x * edge31z;
normalZ = edge21x * edge31y - edge21y * edge31x;
const float planeNormalLen2 = normalX * normalX + normalY * normalY + normalZ * normalZ;
if (planeNormalLen2 > 0.f)
{
const float invLen = 1.f / std::sqrt(planeNormalLen2);
normalX *= invLen;
normalY *= invLen;
normalZ *= invLen;
}
}

writeRecord(
normalX, normalY, normalZ,
vertex1x, vertex1.y, vertex1.z,
vertex2x, vertex2.y, vertex2.z,
vertex3x, vertex3.y, vertex3.z,
faceColor);
}
}
}
else if (hasFastTightPath)
{
const hlsl::float32_t3* posTri = tightPositions;
for (uint32_t primIx = 0u; primIx < facenum; ++primIx, posTri += 3u)
{
uint16_t faceColor = 0u;
if (!computeFaceColor(primIx * 3u + 0u, primIx * 3u + 1u, primIx * 3u + 2u, faceColor))
return false;

const hlsl::float32_t3 vertex1 = posTri[2u];
const hlsl::float32_t3 vertex2 = posTri[1u];
const hlsl::float32_t3 vertex3 = posTri[0u];
const float vertex1x = vertex1.x * handednessSign;
const float vertex2x = vertex2.x * handednessSign;
const float vertex3x = vertex3.x * handednessSign;

const float edge21x = vertex2x - vertex1x;
const float edge21y = vertex2.y - vertex1.y;
const float edge21z = vertex2.z - vertex1.z;
const float edge31x = vertex3x - vertex1x;
const float edge31y = vertex3.y - vertex1.y;
const float edge31z = vertex3.z - vertex1.z;

float normalX = edge21y * edge31z - edge21z * edge31y;
float normalY = edge21z * edge31x - edge21x * edge31z;
float normalZ = edge21x * edge31y - edge21y * edge31x;
const float planeNormalLen2 = normalX * normalX + normalY * normalY + normalZ * normalZ;
if (planeNormalLen2 > 0.f)
{
const float invLen = 1.f / std::sqrt(planeNormalLen2);
normalX *= invLen;
normalY *= invLen;
normalZ *= invLen;
}

writeRecord(
normalX, normalY, normalZ,
vertex1x, vertex1.y, vertex1.z,
vertex2x, vertex2.y, vertex2.z,
vertex3x, vertex3.y, vertex3.z,
faceColor);
}
}
else
{
for (uint32_t primIx = 0u; primIx < facenum; ++primIx)
{
const uint32_t i0 = indices ? indices[primIx * 3u + 0u] : (primIx * 3u + 0u);
const uint32_t i1 = indices ? indices[primIx * 3u + 1u] : (primIx * 3u + 1u);
const uint32_t i2 = indices ? indices[primIx * 3u + 2u] : (primIx * 3u + 2u);
if (i0 >= vertexCount || i1 >= vertexCount || i2 >= vertexCount)
return false;
uint16_t faceColor = 0u;
if (!computeFaceColor(i0, i1, i2, faceColor))
return false;

hlsl::float32_t3 p0 = {};
hlsl::float32_t3 p1 = {};
hlsl::float32_t3 p2 = {};
if (!decodePosition(i0, p0) || !decodePosition(i1, p1) || !decodePosition(i2, p2))
return false;

hlsl::float32_t3 vertex1 = p2;
hlsl::float32_t3 vertex2 = p1;
hlsl::float32_t3 vertex3 = p0;

if (flipHandedness)
{
vertex1.x = -vertex1.x;
vertex2.x = -vertex2.x;
vertex3.x = -vertex3.x;
}

const hlsl::float32_t3 planeNormal = hlsl::cross(vertex2 - vertex1, vertex3 - vertex1);
const float planeNormalLen2 = hlsl::dot(planeNormal, planeNormal);
hlsl::float32_t3 normal = hlsl::float32_t3(0.f, 0.f, 0.f);
if (!hasNormals)
{
if (planeNormalLen2 > 0.f)
normal = hlsl::normalize(planeNormal);
}

if (hasNormals)
{
hlsl::float32_t3 n0 = {};
if (!decodeNormal(i0, n0))
return false;

hlsl::float32_t3 attrNormal = n0;
if (hlsl::dot(attrNormal, attrNormal) <= 0.f)
{
hlsl::float32_t3 n1 = {};
if (!decodeNormal(i1, n1))
return false;
attrNormal = n1;
}
if (hlsl::dot(attrNormal, attrNormal) <= 0.f)
{
hlsl::float32_t3 n2 = {};
if (!decodeNormal(i2, n2))
return false;
attrNormal = n2;
}

if (hlsl::dot(attrNormal, attrNormal) > 0.f)
{
if (flipHandedness)
attrNormal.x = -attrNormal.x;
if (planeNormalLen2 > 0.f && hlsl::dot(attrNormal, planeNormal) < 0.f)
attrNormal = -attrNormal;
normal = attrNormal;
}
else if (planeNormalLen2 > 0.f)
{
normal = hlsl::normalize(planeNormal);
}
}

writeRecord(
normal.x, normal.y, normal.z,
vertex1.x, vertex1.y, vertex1.z,
vertex2.x, vertex2.y, vertex2.z,
vertex3.x, vertex3.y, vertex3.z,
faceColor);
}
}

getVectorAsStringLine(vertex1, tmp);
const bool writeOk = writeFileWithPolicy(context->writeContext.outputFile, context->ioPlan, output.get(), outputSize, &context->writeTelemetry);
if (writeOk)
context->fileOffset += outputSize;
return writeOk;
}

{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, tmp.c_str(), context->fileOffset, tmp.size());
bool writeMeshASCII(const asset::ICPUPolygonGeometry* geom, SContext* context)
{
if (!geom)
return false;

context->fileOffset += success.getBytesProcessed();
}
const auto* indexing = geom->getIndexingCallback();
if (!indexing || indexing->degree() != 3u)
return false;

{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, " vertex ", context->fileOffset, 11);
const auto& posView = geom->getPositionView();
if (!posView)
return false;
const auto& normalView = geom->getNormalView();
const bool flipHandedness = !(context->writeContext.params.flags & E_WRITER_FLAGS::EWF_MESH_IS_RIGHT_HANDED);

context->fileOffset += success.getBytesProcessed();
}
const std::string name = context->writeContext.outputFile->getFileName().filename().replace_extension().string();
const std::string_view solidName = name.empty() ? std::string_view(stl_writer_detail::AsciiDefaultName) : std::string_view(name);

getVectorAsStringLine(vertex2, tmp);
if (!writeBytes(context, stl_writer_detail::AsciiSolidPrefix, sizeof(stl_writer_detail::AsciiSolidPrefix) - 1ull))
return false;

{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, tmp.c_str(), context->fileOffset, tmp.size());
if (!writeBytes(context, solidName.data(), solidName.size()))
return false;

context->fileOffset += success.getBytesProcessed();
}
if (!writeBytes(context, "\n", sizeof("\n") - 1ull))
return false;

const uint32_t faceCount = static_cast<uint32_t>(geom->getPrimitiveCount());
for (uint32_t primIx = 0u; primIx < faceCount; ++primIx)
{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, " vertex ", context->fileOffset, 11);

context->fileOffset += success.getBytesProcessed();
core::vectorSIMDf v0;
core::vectorSIMDf v1;
core::vectorSIMDf v2;
uint32_t idx[3] = {};
if (!decodeTriangle(geom, indexing, posView, primIx, v0, v1, v2, idx))
return false;
if (!writeFaceText(v0, v1, v2, idx, normalView, flipHandedness, context))
return false;
if (!writeBytes(context, "\n", sizeof("\n") - 1ull))
return false;
}

getVectorAsStringLine(vertex3, tmp);
if (!writeBytes(context, stl_writer_detail::AsciiEndSolidPrefix, sizeof(stl_writer_detail::AsciiEndSolidPrefix) - 1ull))
return false;

{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, tmp.c_str(), context->fileOffset, tmp.size());
if (!writeBytes(context, solidName.data(), solidName.size()))
return false;

context->fileOffset += success.getBytesProcessed();
}
return true;
}

{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, " endloop\n", context->fileOffset, 10);
bool writeFaceText(
const core::vectorSIMDf& v1,
const core::vectorSIMDf& v2,
const core::vectorSIMDf& v3,
const uint32_t* idx,
const asset::ICPUPolygonGeometry::SDataView& normalView,
const bool flipHandedness,
SContext* context)
{
core::vectorSIMDf vertex1 = v3;
core::vectorSIMDf vertex2 = v2;
core::vectorSIMDf vertex3 = v1;

context->fileOffset += success.getBytesProcessed();
if (flipHandedness)
{
vertex1.X = -vertex1.X;
vertex2.X = -vertex2.X;
vertex3.X = -vertex3.X;
}

core::vectorSIMDf normal = core::plane3dSIMDf(vertex1, vertex2, vertex3).getNormal();
core::vectorSIMDf attrNormal;
if (decodeTriangleNormal(normalView, idx, attrNormal))
{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, "endfacet\n", context->fileOffset, 9);

context->fileOffset += success.getBytesProcessed();
if (flipHandedness)
attrNormal.X = -attrNormal.X;
if (core::dot(attrNormal, normal).X < 0.f)
attrNormal = -attrNormal;
normal = attrNormal;
}

std::array<char, stl_writer_detail::AsciiFaceTextMaxBytes> faceText = {};
char* cursor = faceText.data();
char* const end = faceText.data() + faceText.size();
if (!appendLiteral(cursor, end, "facet normal ", sizeof("facet normal ") - 1ull))
return false;
if (!appendVectorAsAsciiLine(cursor, end, normal))
return false;
if (!appendLiteral(cursor, end, " outer loop\n", sizeof(" outer loop\n") - 1ull))
return false;
if (!appendLiteral(cursor, end, " vertex ", sizeof(" vertex ") - 1ull))
return false;
if (!appendVectorAsAsciiLine(cursor, end, vertex1))
return false;
if (!appendLiteral(cursor, end, " vertex ", sizeof(" vertex ") - 1ull))
return false;
if (!appendVectorAsAsciiLine(cursor, end, vertex2))
return false;
if (!appendLiteral(cursor, end, " vertex ", sizeof(" vertex ") - 1ull))
return false;
if (!appendVectorAsAsciiLine(cursor, end, vertex3))
return false;
if (!appendLiteral(cursor, end, " endloop\n", sizeof(" endloop\n") - 1ull))
return false;
if (!appendLiteral(cursor, end, "endfacet\n", sizeof("endfacet\n") - 1ull))
return false;

return writeBytes(context, faceText.data(), static_cast<size_t>(cursor - faceText.data()));
}

}

Choose a reason for hiding this comment

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

fun fact when dealing with mesh LDR colors, they're supposed to be SRGB

whenever they were UNORM before, that was wrong.

Choose a reason for hiding this comment

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

at least when the semantics were clear that they were RGB per-vertex colors

Comment on lines +488 to +492
core::vector<uint32_t> indexData;
const uint32_t* indices = nullptr;
uint32_t facenum = 0u;
if (!decodeTriangleIndices(geom, posView, indexData, indices, facenum))
return false;

Choose a reason for hiding this comment

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

make a common util for all polygon geo writers ?

Comment on lines +494 to +498
const size_t outputSize = stl_writer_detail::BinaryPrefixBytes + static_cast<size_t>(facenum) * stl_writer_detail::BinaryTriangleRecordBytes;
std::unique_ptr<uint8_t[]> output(new (std::nothrow) uint8_t[outputSize]);
if (!output)
return false;
uint8_t* dst = output.get();

Choose a reason for hiding this comment

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

does this make sense if the output file is memory mapped anyway ?

Comment on lines +534 to +562
auto computeFaceColor = [&](const uint32_t i0, const uint32_t i1, const uint32_t i2, uint16_t& outColor)->bool
{
outColor = 0u;
if (!colorView)
return true;
uint32_t c0 = 0u, c1 = 0u, c2 = 0u;
if (!stlDecodeColorB8G8R8A8(*colorView, i0, c0))
return false;
if (!stlDecodeColorB8G8R8A8(*colorView, i1, c1))
return false;
if (!stlDecodeColorB8G8R8A8(*colorView, i2, c2))
return false;
double rgba0[4] = {};
double rgba1[4] = {};
double rgba2[4] = {};
stlDecodeColorUnitRGBAFromB8G8R8A8(c0, rgba0);
stlDecodeColorUnitRGBAFromB8G8R8A8(c1, rgba1);
stlDecodeColorUnitRGBAFromB8G8R8A8(c2, rgba2);
const double rgbaAvg[4] = {
(rgba0[0] + rgba1[0] + rgba2[0]) / 3.0,
(rgba0[1] + rgba1[1] + rgba2[1]) / 3.0,
(rgba0[2] + rgba1[2] + rgba2[2]) / 3.0,
1.0
};
uint32_t avgColor = 0u;
encodePixels<EF_B8G8R8A8_UNORM, double>(&avgColor, rgbaAvg);
outColor = stlPackViscamColorFromB8G8R8A8(avgColor);
return true;
};

Choose a reason for hiding this comment

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

I think you need better SRGB handling

outColor = stlPackViscamColorFromB8G8R8A8(avgColor);
return true;
};
auto writeRecord = [&dst](const float nx, const float ny, const float nz, const float v1x, const float v1y, const float v1z, const float v2x, const float v2y, const float v2z, const float v3x, const float v3y, const float v3z, const uint16_t attribute)->void

Choose a reason for hiding this comment

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

why are we passing stuff as individual scalars and not hlsl::float32_t3 ?

Comment on lines +629 to +673
const hlsl::float32_t3 vertex1 = posTri[2u];
const hlsl::float32_t3 vertex2 = posTri[1u];
const hlsl::float32_t3 vertex3 = posTri[0u];
const float vertex1x = vertex1.x * handednessSign;
const float vertex2x = vertex2.x * handednessSign;
const float vertex3x = vertex3.x * handednessSign;

float normalX = 0.f;
float normalY = 0.f;
float normalZ = 0.f;
hlsl::float32_t3 attrNormal = nrmTri[0u];
if (attrNormal.x == 0.f && attrNormal.y == 0.f && attrNormal.z == 0.f)
attrNormal = nrmTri[1u];
if (attrNormal.x == 0.f && attrNormal.y == 0.f && attrNormal.z == 0.f)
attrNormal = nrmTri[2u];
if (!(attrNormal.x == 0.f && attrNormal.y == 0.f && attrNormal.z == 0.f))
{
if (flipHandedness)
attrNormal.x = -attrNormal.x;
normalX = attrNormal.x;
normalY = attrNormal.y;
normalZ = attrNormal.z;
}

if (normalX == 0.f && normalY == 0.f && normalZ == 0.f)
{
const float edge21x = vertex2x - vertex1x;
const float edge21y = vertex2.y - vertex1.y;
const float edge21z = vertex2.z - vertex1.z;
const float edge31x = vertex3x - vertex1x;
const float edge31y = vertex3.y - vertex1.y;
const float edge31z = vertex3.z - vertex1.z;

normalX = edge21y * edge31z - edge21z * edge31y;
normalY = edge21z * edge31x - edge21x * edge31z;
normalZ = edge21x * edge31y - edge21y * edge31x;
const float planeNormalLen2 = normalX * normalX + normalY * normalY + normalZ * normalZ;
if (planeNormalLen2 > 0.f)
{
const float invLen = 1.f / std::sqrt(planeNormalLen2);
normalX *= invLen;
normalY *= invLen;
normalZ *= invLen;
}
}

Choose a reason for hiding this comment

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

the code could be so much smaller if you used vector types and HLSL lib

Comment on lines +666 to +672
if (planeNormalLen2 > 0.f)
{
const float invLen = 1.f / std::sqrt(planeNormalLen2);
normalX *= invLen;
normalY *= invLen;
normalZ *= invLen;
}

Choose a reason for hiding this comment

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

its okay to write NaN in this case

Comment on lines +711 to +717
if (planeNormalLen2 > 0.f)
{
const float invLen = 1.f / std::sqrt(planeNormalLen2);
normalX *= invLen;
normalY *= invLen;
normalZ *= invLen;
}

Choose a reason for hiding this comment

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

nan is okay

Comment on lines +740 to +755
hlsl::float32_t3 p0 = {};
hlsl::float32_t3 p1 = {};
hlsl::float32_t3 p2 = {};
if (!decodePosition(i0, p0) || !decodePosition(i1, p1) || !decodePosition(i2, p2))
return false;

hlsl::float32_t3 vertex1 = p2;
hlsl::float32_t3 vertex2 = p1;
hlsl::float32_t3 vertex3 = p0;

if (flipHandedness)
{
vertex1.x = -vertex1.x;
vertex2.x = -vertex2.x;
vertex3.x = -vertex3.x;
}

Choose a reason for hiding this comment

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

use arrays instead of separate vals

hlsl::float32_t3 normal = hlsl::float32_t3(0.f, 0.f, 0.f);
if (!hasNormals)
{
if (planeNormalLen2 > 0.f)

Choose a reason for hiding this comment

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

NaN is okay

Comment on lines +766 to +800
if (hasNormals)
{
hlsl::float32_t3 n0 = {};
if (!decodeNormal(i0, n0))
return false;

hlsl::float32_t3 attrNormal = n0;
if (hlsl::dot(attrNormal, attrNormal) <= 0.f)
{
hlsl::float32_t3 n1 = {};
if (!decodeNormal(i1, n1))
return false;
attrNormal = n1;
}
if (hlsl::dot(attrNormal, attrNormal) <= 0.f)
{
hlsl::float32_t3 n2 = {};
if (!decodeNormal(i2, n2))
return false;
attrNormal = n2;
}

if (hlsl::dot(attrNormal, attrNormal) > 0.f)
{
if (flipHandedness)
attrNormal.x = -attrNormal.x;
if (planeNormalLen2 > 0.f && hlsl::dot(attrNormal, planeNormal) < 0.f)
attrNormal = -attrNormal;
normal = attrNormal;
}
else if (planeNormalLen2 > 0.f)
{
normal = hlsl::normalize(planeNormal);
}
}

Choose a reason for hiding this comment

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

what's all this code ?

Comment on lines +868 to 897
bool writeFaceText(
const core::vectorSIMDf& v1,
const core::vectorSIMDf& v2,
const core::vectorSIMDf& v3,
const uint32_t* idx,
const asset::ICPUPolygonGeometry::SDataView& normalView,
const bool flipHandedness,
SContext* context)
{
core::vectorSIMDf vertex1 = v3;
core::vectorSIMDf vertex2 = v2;
core::vectorSIMDf vertex3 = v1;

context->fileOffset += success.getBytesProcessed();
if (flipHandedness)
{
vertex1.X = -vertex1.X;
vertex2.X = -vertex2.X;
vertex3.X = -vertex3.X;
}

core::vectorSIMDf normal = core::plane3dSIMDf(vertex1, vertex2, vertex3).getNormal();
core::vectorSIMDf attrNormal;
if (decodeTriangleNormal(normalView, idx, attrNormal))
{
system::IFile::success_t success;;
context->writeContext.outputFile->write(success, "endfacet\n", context->fileOffset, 9);

context->fileOffset += success.getBytesProcessed();
if (flipHandedness)
attrNormal.X = -attrNormal.X;
if (core::dot(attrNormal, normal).X < 0.f)
attrNormal = -attrNormal;
normal = attrNormal;
}

Choose a reason for hiding this comment

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

get rid of old vector types

Comment on lines +847 to +849
core::vectorSIMDf v0;
core::vectorSIMDf v1;
core::vectorSIMDf v2;

Choose a reason for hiding this comment

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

same

Comment on lines +49 to +53
constexpr size_t ApproxObjBytesPerVertex = 96ull;
constexpr size_t ApproxObjBytesPerFace = 48ull;
constexpr size_t MaxUInt32Chars = 10ull;
constexpr size_t MaxFloatFixed6Chars = 48ull;
constexpr size_t MaxIndexTokenBytes = MaxUInt32Chars * 3ull + 2ull;

Choose a reason for hiding this comment

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

express this with sizeof so we know where all that stuff comes from / assumptions

Comment on lines +61 to +81
bool decodeVec4(const ICPUPolygonGeometry::SDataView& view, const size_t ix, hlsl::float64_t4& out)
{
out = hlsl::float64_t4(0.0, 0.0, 0.0, 0.0);
return view.decodeElement(ix, out);
}

char* appendUIntToBuffer(char* dst, char* const end, const uint32_t value)
{
if (!dst || dst >= end)
return end;

const auto result = std::to_chars(dst, end, value);
if (result.ec == std::errc())
return result.ptr;

const int written = std::snprintf(dst, static_cast<size_t>(end - dst), "%u", value);
if (written <= 0)
return dst;
const size_t writeLen = static_cast<size_t>(written);
return (writeLen < static_cast<size_t>(end - dst)) ? (dst + writeLen) : end;
}

Choose a reason for hiding this comment

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

commonalize?

Comment on lines +83 to +148
void appendVec3Line(std::string& out, const char* prefix, const size_t prefixSize, const float x, const float y, const float z)
{
const size_t oldSize = out.size();
out.resize(oldSize + prefixSize + (3ull * MaxFloatFixed6Chars) + 3ull);
char* const lineBegin = out.data() + oldSize;
char* cursor = lineBegin;
char* const lineEnd = out.data() + out.size();

std::memcpy(cursor, prefix, prefixSize);
cursor += prefixSize;

cursor = appendFloatFixed6ToBuffer(cursor, lineEnd, x);
if (cursor < lineEnd)
*(cursor++) = ' ';
cursor = appendFloatFixed6ToBuffer(cursor, lineEnd, y);
if (cursor < lineEnd)
*(cursor++) = ' ';
cursor = appendFloatFixed6ToBuffer(cursor, lineEnd, z);
if (cursor < lineEnd)
*(cursor++) = '\n';

out.resize(oldSize + static_cast<size_t>(cursor - lineBegin));
}

void appendVec2Line(std::string& out, const char* prefix, const size_t prefixSize, const float x, const float y)
{
const size_t oldSize = out.size();
out.resize(oldSize + prefixSize + (2ull * MaxFloatFixed6Chars) + 2ull);
char* const lineBegin = out.data() + oldSize;
char* cursor = lineBegin;
char* const lineEnd = out.data() + out.size();

std::memcpy(cursor, prefix, prefixSize);
cursor += prefixSize;

cursor = appendFloatFixed6ToBuffer(cursor, lineEnd, x);
if (cursor < lineEnd)
*(cursor++) = ' ';
cursor = appendFloatFixed6ToBuffer(cursor, lineEnd, y);
if (cursor < lineEnd)
*(cursor++) = '\n';

out.resize(oldSize + static_cast<size_t>(cursor - lineBegin));
}

void appendFaceLine(std::string& out, const std::string& storage, const core::vector<SIndexStringRef>& refs, const uint32_t i0, const uint32_t i1, const uint32_t i2)
{
const auto& ref0 = refs[i0];
const auto& ref1 = refs[i1];
const auto& ref2 = refs[i2];
const size_t oldSize = out.size();
const size_t lineSize = 2ull + static_cast<size_t>(ref0.length) + 1ull + static_cast<size_t>(ref1.length) + 1ull + static_cast<size_t>(ref2.length) + 1ull;
out.resize(oldSize + lineSize);
char* cursor = out.data() + oldSize;
*(cursor++) = 'f';
*(cursor++) = ' ';
std::memcpy(cursor, storage.data() + ref0.offset, ref0.length);
cursor += ref0.length;
*(cursor++) = ' ';
std::memcpy(cursor, storage.data() + ref1.offset, ref1.length);
cursor += ref1.length;
*(cursor++) = ' ';
std::memcpy(cursor, storage.data() + ref2.offset, ref2.length);
cursor += ref2.length;
*(cursor++) = '\n';
}

Choose a reason for hiding this comment

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

why not take hlsl::float32_tN and hlsl::uint32_t3 instead of individual scalar arguments?

Also you have this out state that could really be wrapped up in a context struct with methods

const auto* geom = IAsset::castDown<const ICPUPolygonGeometry>(_params.rootAsset);
if (!geom || !geom->valid())
return false;

Choose a reason for hiding this comment

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

btw OBJ writer should be an ICPUScene writer

Comment on lines +57 to +106
inline bool parseObjFloat(const char*& ptr, const char* const end, float& out)
{
SContext ctx(
asset::IAssetLoader::SAssetLoadContext{
_params,
_file
},
_hierarchyLevel,
_override
);
const char* const start = ptr;
if (start >= end)
return false;

if (_params.meshManipulatorOverride == nullptr)
{
_NBL_DEBUG_BREAK_IF(true);
assert(false);
}
const char* p = start;
bool negative = false;
if (*p == '-' || *p == '+')
{
negative = (*p == '-');
++p;
if (p >= end)
return false;
}

CQuantNormalCache* const quantNormalCache = _params.meshManipulatorOverride->getQuantNormalCache();
if (*p == '.' || !isObjDigit(*p))
{
const auto parseResult = fast_float::from_chars(start, end, out);
if (parseResult.ec == std::errc() && parseResult.ptr != start)
{
ptr = parseResult.ptr;
return true;
}
return false;
}

const long filesize = _file->getSize();
if (!filesize)
return {};
uint64_t integerPart = 0ull;
while (p < end && isObjDigit(*p))
{
integerPart = integerPart * 10ull + static_cast<uint64_t>(*p - '0');
++p;
}

const uint32_t WORD_BUFFER_LENGTH = 512u;
char tmpbuf[WORD_BUFFER_LENGTH]{};

uint32_t smoothingGroup=0;

const std::filesystem::path fullName = _file->getFileName();
const std::string relPath = [&fullName]() -> std::string
{
auto dir = fullName.parent_path().string();
return dir;
}();

//value_type: directory from which .mtl (pipeline) was loaded and the pipeline
using pipeline_meta_pair_t = std::pair<core::smart_refctd_ptr<ICPURenderpassIndependentPipeline>,const CMTLMetadata::CRenderpassIndependentPipeline*>;
struct hash_t
{
inline auto operator()(const pipeline_meta_pair_t& item) const
{
return std::hash<std::string>()(item.second->m_name);
}
};
struct key_equal_t
{
inline bool operator()(const pipeline_meta_pair_t& lhs, const pipeline_meta_pair_t& rhs) const
{
return lhs.second->m_name==rhs.second->m_name;
}
};
core::unordered_multiset<pipeline_meta_pair_t,hash_t,key_equal_t> pipelines;

// TODO: map the file whenever possible
std::string fileContents;
fileContents.resize(filesize);
char* const buf = fileContents.data();

system::IFile::success_t success;
_file->read(success, buf, 0, filesize);
if (!success)
return {};

const char* const bufEnd = buf+filesize;
// Process obj information
const char* bufPtr = buf;
std::string grpName, mtlName;

auto performActionBasedOnOrientationSystem = [&](auto performOnRightHanded, auto performOnLeftHanded)
{
if (_params.loaderFlags & E_LOADER_PARAMETER_FLAGS::ELPF_RIGHT_HANDED_MESHES)
performOnRightHanded();
else
performOnLeftHanded();
};


struct vec3 {
float data[3];
};
struct vec2 {
float data[2];
};
core::vector<vec3> vertexBuffer;
core::vector<vec3> normalsBuffer;
core::vector<vec2> textureCoordBuffer;

core::vector<core::smart_refctd_ptr<ICPUMeshBuffer>> submeshes;
core::vector<core::vector<uint32_t>> indices;
core::vector<SObjVertex> vertices;
core::map<SObjVertex, uint32_t> map_vtx2ix;
core::vector<bool> recalcNormals;
core::vector<bool> submeshWasLoadedFromCache;
core::vector<std::string> submeshCacheKeys;
core::vector<std::string> submeshMaterialNames;
core::vector<uint32_t> vtxSmoothGrp;

// TODO: handle failures much better!
constexpr const char* NO_MATERIAL_MTL_NAME = "#";
bool noMaterial = true;
bool dummyMaterialCreated = false;
while(bufPtr != bufEnd)
{
switch(bufPtr[0])
{
case 'm': // mtllib (material)
{
if (ctx.useMaterials)
{
bufPtr = goAndCopyNextWord(tmpbuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
_params.logger.log("Reading material _file %s", system::ILogger::ELL_DEBUG, tmpbuf);

std::string mtllib = tmpbuf;
std::replace(mtllib.begin(), mtllib.end(), '\\', '/');
SAssetLoadParams loadParams(_params);
loadParams.workingDirectory = _file->getFileName().parent_path();
auto bundle = interm_getAssetInHierarchy(AssetManager, mtllib, loadParams, _hierarchyLevel+ICPUMesh::PIPELINE_HIERARCHYLEVELS_BELOW, _override);

if (bundle.getContents().empty())
break;

if (bundle.getMetadata())
{
auto meta = bundle.getMetadata()->selfCast<const CMTLMetadata>();
if (bundle.getAssetType()==IAsset::ET_RENDERPASS_INDEPENDENT_PIPELINE)
for (auto ass : bundle.getContents())
{
auto ppln = core::smart_refctd_ptr_static_cast<ICPURenderpassIndependentPipeline>(ass);
const auto pplnMeta = meta->getAssetSpecificMetadata(ppln.get());
if (!pplnMeta)
continue;

pipelines.emplace(std::move(ppln),pplnMeta);
}
}
}
}
break;

case 'v': // v, vn, vt
//reset flags
noMaterial = true;
dummyMaterialCreated = false;
switch(bufPtr[1])
{
case ' ': // vertex
{
vec3 vec;
bufPtr = readVec3(bufPtr, vec.data, bufEnd);
performActionBasedOnOrientationSystem([&]() {vec.data[0] = -vec.data[0];}, [&]() {});
vertexBuffer.push_back(vec);
}
break;

case 'n': // normal
{
vec3 vec;
bufPtr = readVec3(bufPtr, vec.data, bufEnd);
performActionBasedOnOrientationSystem([&]() {vec.data[0] = -vec.data[0]; }, [&]() {});
normalsBuffer.push_back(vec);
}
break;

case 't': // texcoord
{
vec2 vec;
bufPtr = readUV(bufPtr, vec.data, bufEnd);
textureCoordBuffer.push_back(vec);
}
break;
}
break;

case 'g': // group name
bufPtr = goAndCopyNextWord(tmpbuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
grpName = tmpbuf;
break;
case 's': // smoothing can be a group or off (equiv. to 0)
{
bufPtr = goAndCopyNextWord(tmpbuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
_params.logger.log("Loaded smoothing group start %s",system::ILogger::ELL_DEBUG, tmpbuf);
if (strcmp("off", tmpbuf)==0)
smoothingGroup=0u;
else
sscanf(tmpbuf,"%u",&smoothingGroup);
}
break;

case 'u': // usemtl
// get name of material
{
noMaterial = false;
bufPtr = goAndCopyNextWord(tmpbuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
_params.logger.log("Loaded material start %s", system::ILogger::ELL_DEBUG, tmpbuf);
mtlName=tmpbuf;

if (ctx.useMaterials && !ctx.useGroups)
{
asset::IAsset::E_TYPE types[] {asset::IAsset::ET_SUB_MESH, (asset::IAsset::E_TYPE)0u };
auto mb_bundle = _override->findCachedAsset(genKeyForMeshBuf(ctx, _file->getFileName().string(), mtlName, grpName), types, ctx.inner, _hierarchyLevel+ICPUMesh::MESHBUFFER_HIERARCHYLEVELS_BELOW);
auto mbs = mb_bundle.getContents();
bool notempty = mbs.size()!=0ull;
{
auto mb = notempty ? core::smart_refctd_ptr_static_cast<ICPUMeshBuffer>(*mbs.begin()) : core::make_smart_refctd_ptr<ICPUMeshBuffer>();
submeshes.push_back(std::move(mb));
}
indices.emplace_back();
recalcNormals.push_back(false);
submeshWasLoadedFromCache.push_back(notempty);
//if submesh was loaded from cache - insert empty "cache key" (submesh loaded from cache won't be added to cache again)
submeshCacheKeys.push_back(submeshWasLoadedFromCache.back() ? "" : genKeyForMeshBuf(ctx, _file->getFileName().string(), mtlName, grpName));
submeshMaterialNames.push_back(mtlName);
}
}
break;
case 'f': // face
{
if (noMaterial && !dummyMaterialCreated)
{
dummyMaterialCreated = true;

submeshes.push_back(core::make_smart_refctd_ptr<ICPUMeshBuffer>());
indices.emplace_back();
recalcNormals.push_back(false);
submeshWasLoadedFromCache.push_back(false);
submeshCacheKeys.push_back(genKeyForMeshBuf(ctx, _file->getFileName().string(), NO_MATERIAL_MTL_NAME, grpName));
submeshMaterialNames.push_back(NO_MATERIAL_MTL_NAME);
}

SObjVertex v;

// get all vertices data in this face (current line of obj _file)
const std::string wordBuffer = copyLine(bufPtr, bufEnd);
const char* linePtr = wordBuffer.c_str();
const char* const endPtr = linePtr + wordBuffer.size();

core::vector<uint32_t> faceCorners;
faceCorners.reserve(32ull);

// read in all vertices
linePtr = goNextWord(linePtr, endPtr);
while (0 != linePtr[0])
{
// Array to communicate with retrieveVertexIndices()
// sends the buffer sizes and gets the actual indices
// if index not set returns -1
int32_t Idx[3];
Idx[1] = Idx[2] = -1;

// read in next vertex's data
uint32_t wlength = copyWord(tmpbuf, linePtr, WORD_BUFFER_LENGTH, endPtr);
// this function will also convert obj's 1-based index to c++'s 0-based index
retrieveVertexIndices(tmpbuf, Idx, tmpbuf+wlength+1, vertexBuffer.size(), textureCoordBuffer.size(), normalsBuffer.size());
v.pos[0] = vertexBuffer[Idx[0]].data[0];
v.pos[1] = vertexBuffer[Idx[0]].data[1];
v.pos[2] = vertexBuffer[Idx[0]].data[2];
//set texcoord
if ( -1 != Idx[1] )
{
v.uv[0] = textureCoordBuffer[Idx[1]].data[0];
v.uv[1] = textureCoordBuffer[Idx[1]].data[1];
}
else
{
v.uv[0] = core::nan<float>();
v.uv[1] = core::nan<float>();
}
//set normal
if ( -1 != Idx[2] )
{
core::vectorSIMDf simdNormal;
simdNormal.set(normalsBuffer[Idx[2]].data);
simdNormal.makeSafe3D();
v.normal32bit = quantNormalCache->quantize<EF_A2B10G10R10_SNORM_PACK32>(simdNormal);
}
else
{
v.normal32bit = core::vectorSIMDu32(0u);
recalcNormals.back() = true;
}

uint32_t ix;
auto vtx_ix = map_vtx2ix.find(v);
if (vtx_ix != map_vtx2ix.end() && smoothingGroup==vtxSmoothGrp[vtx_ix->second])
ix = vtx_ix->second;
else
{
ix = vertices.size();
vertices.push_back(v);
vtxSmoothGrp.push_back(smoothingGroup);
map_vtx2ix.insert({v, ix});
}

faceCorners.push_back(ix);

// go to next vertex
linePtr = goNextWord(linePtr, endPtr);
}

// triangulate the face
for (uint32_t i = 1u; i < faceCorners.size()-1u; ++i)
double value = static_cast<double>(integerPart);
if (p < end && *p == '.')
{
const char* const dot = p;
if ((dot + 7) <= end)
{
const char d0 = dot[1];
const char d1 = dot[2];
const char d2 = dot[3];
const char d3 = dot[4];
const char d4 = dot[5];
const char d5 = dot[6];
if (
isObjDigit(d0) && isObjDigit(d1) && isObjDigit(d2) &&
isObjDigit(d3) && isObjDigit(d4) && isObjDigit(d5)
)

Choose a reason for hiding this comment

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

did you have to write your own float parser ?

Comment on lines +173 to +200
const auto createAdoptedView = [](auto&& data, const E_FORMAT format) -> IGeometry<ICPUBuffer>::SDataView
{
using T = typename std::decay_t<decltype(data)>::value_type;
if (data.empty())
return {};

IMeshManipulator::recalculateBoundingBox(mesh.get());
if (mesh->getMeshBuffers().empty())
auto backer = core::make_smart_refctd_ptr<core::adoption_memory_resource<core::vector<T>>>(std::move(data));
auto& storage = backer->getBacker();
auto* const ptr = storage.data();
const size_t byteCount = storage.size() * sizeof(T);
auto buffer = ICPUBuffer::create({ { byteCount }, ptr, core::smart_refctd_ptr<core::refctd_memory_resource>(std::move(backer)), alignof(T) }, core::adopt_memory);
if (!buffer)
return {};

//
auto meta = core::make_smart_refctd_ptr<COBJMetadata>(usedPipelines.size());
uint32_t metaOffset = 0u;
for (auto pipeAndMeta : usedPipelines)
meta->placeMeta(metaOffset++,pipeAndMeta.first.get(),*pipeAndMeta.second);

//at the very end, insert submeshes into cache
uint32_t i = 0u;
for (auto meshbuffer : mesh->getMeshBuffers())
{
auto bundle = SAssetBundle(meta,{ core::smart_refctd_ptr<ICPUMeshBuffer>(meshbuffer) });
_override->insertAssetIntoCache(bundle, submeshCacheKeys[i++], ctx.inner, _hierarchyLevel+ICPUMesh::MESHBUFFER_HIERARCHYLEVELS_BELOW);
}

return SAssetBundle(std::move(meta),{std::move(mesh)});
}

IGeometry<ICPUBuffer>::SDataView view = {
.composed = {
.stride = sizeof(T),
.format = format,
.rangeFormat = IGeometryBase::getMatchingAABBFormat(format)
},
.src = {
.offset = 0u,
.size = byteCount,
.buffer = std::move(buffer)
}
};
return view;
};

Choose a reason for hiding this comment

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

common with other loaders

Comment on lines +207 to 223
inline bool parseUnsignedObjIndex(const char*& ptr, const char* const end, uint32_t& out)
{
const uint32_t WORD_BUFFER_LENGTH = 256;
char wordBuffer[WORD_BUFFER_LENGTH];
if (ptr >= end || !isObjDigit(*ptr))
return false;

bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
sscanf(wordBuffer,"%f",vec);
bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
sscanf(wordBuffer,"%f",vec+1);
uint64_t value = 0ull;
while (ptr < end && isObjDigit(*ptr))
{
value = value * 10ull + static_cast<uint64_t>(*ptr - '0');
++ptr;
}
if (value == 0ull || value > static_cast<uint64_t>(std::numeric_limits<int32_t>::max()))
return false;

vec[1] = 1.f-vec[1]; // change handedness
return bufPtr;
out = static_cast<uint32_t>(value);
return true;
}

Choose a reason for hiding this comment

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

again, own parser needed ?

Comment on lines +225 to +230
inline char toObjLowerAscii(const char c)
{
if (c >= 'A' && c <= 'Z')
return static_cast<char>(c - 'A' + 'a');
return c;
}

Choose a reason for hiding this comment

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

what was wrong with std::to_lower ?

Comment on lines +264 to +280
uint64_t value = 0ull;
bool sawDigit = false;
for (const char* it = tokenStart; it < linePtr; ++it)
{
if (!isObjDigit(*it))
{
outGroup = 0u;
return;
}
sawDigit = true;
value = value * 10ull + static_cast<uint64_t>(*it - '0');
if (value > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()))
{
outGroup = 0u;
return;
}
}

Choose a reason for hiding this comment

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

this is an integer parser again, duplicate code as well

while (ptr < lineEnd && isObjDigit(*ptr))
{
const uint32_t digit = static_cast<uint32_t>(*ptr - '0');
if (value > 429496729u)

Choose a reason for hiding this comment

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

numeric_limits harmed you?

Comment on lines +299 to +306
while (ptr < lineEnd && isObjDigit(*ptr))
{
const uint32_t digit = static_cast<uint32_t>(*ptr - '0');
if (value > 429496729u)
return false;
value = value * 10u + digit;
++ptr;
}

Choose a reason for hiding this comment

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

this is a 32bit uint parser, can you somehow make that with STD or other functions

Comment on lines +317 to +327
uint32_t value = 0u;
if (ptr >= lineEnd || !isObjDigit(*ptr))
return false;
while (ptr < lineEnd && isObjDigit(*ptr))
{
const uint32_t digit = static_cast<uint32_t>(*ptr - '0');
if (value > 429496729u)
return false;
value = value * 10u + digit;
++ptr;
}

Choose a reason for hiding this comment

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

again

Comment on lines +338 to +348
uint32_t value = 0u;
if (ptr >= lineEnd || !isObjDigit(*ptr))
return false;
while (ptr < lineEnd && isObjDigit(*ptr))
{
const uint32_t digit = static_cast<uint32_t>(*ptr - '0');
if (value > 429496729u)
return false;
value = value * 10u + digit;
++ptr;
}

Choose a reason for hiding this comment

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

again

Comment on lines +370 to +398
bool negative = false;
if (*ptr == '-')
{
negative = true;
++ptr;
}
else if (*ptr == '+')
{
++ptr;
}

if (ptr >= end || !isObjDigit(*ptr))
return false;

int64_t value = 0;
while (ptr < end && isObjDigit(*ptr))
{
value = value * 10ll + static_cast<int64_t>(*ptr - '0');
++ptr;
}
if (negative)
value = -value;

if (value == 0)
return false;
if (value < static_cast<int64_t>(std::numeric_limits<int32_t>::min()) || value > static_cast<int64_t>(std::numeric_limits<int32_t>::max()))
return false;

out = static_cast<int32_t>(value);

Choose a reason for hiding this comment

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

and this is a 64bit signed int parser

Comment on lines +563 to +593
SFileReadTelemetry ioTelemetry = {};

const long filesize = _file->getSize();
if (filesize <= 0)
return {};
const auto ioPlan = resolveFileIOPolicy(_params.ioPolicy, static_cast<uint64_t>(filesize), true);
if (!ioPlan.valid)
{
_params.logger.log("OBJ loader: invalid io policy for %s reason=%s", system::ILogger::ELL_ERROR, _file->getFileName().string().c_str(), ioPlan.reason);
return {};
}

std::string fileContents = {};
const char* buf = nullptr;
if (ioPlan.strategy == SResolvedFileIOPolicy::Strategy::WholeFile)
{
const auto* constFile = static_cast<const system::IFile*>(_file);
const auto* mapped = reinterpret_cast<const char*>(constFile->getMappedPointer());
if (mapped)
{
buf = mapped;
ioTelemetry.account(static_cast<uint64_t>(filesize));
}
}
if (!buf)
{
fileContents.resize(static_cast<size_t>(filesize));
if (!readTextFileWithPolicy(_file, fileContents.data(), fileContents.size(), ioPlan, ioTelemetry))
return {};
buf = fileContents.data();
}

Choose a reason for hiding this comment

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

common to all loaders

Comment on lines +627 to +650
struct SDedupHotEntry
{
int32_t pos = -1;
int32_t uv = -1;
int32_t normal = -1;
uint32_t outIndex = 0u;
};
const size_t hw = resolveLoaderHardwareThreads();
const size_t hardMaxWorkers = resolveLoaderHardMaxWorkers(hw, _params.ioPolicy.runtimeTuning.workerHeadroom);
SLoaderRuntimeTuningRequest dedupTuningRequest = {};
dedupTuningRequest.inputBytes = static_cast<uint64_t>(filesize);
dedupTuningRequest.totalWorkUnits = estimatedOutVertexCount;
dedupTuningRequest.hardwareThreads = static_cast<uint32_t>(hw);
dedupTuningRequest.hardMaxWorkers = static_cast<uint32_t>(hardMaxWorkers);
dedupTuningRequest.targetChunksPerWorker = _params.ioPolicy.runtimeTuning.targetChunksPerWorker;
dedupTuningRequest.sampleData = reinterpret_cast<const uint8_t*>(buf);
dedupTuningRequest.sampleBytes = resolveLoaderRuntimeSampleBytes(_params.ioPolicy, static_cast<uint64_t>(filesize));
const auto dedupTuning = tuneLoaderRuntime(_params.ioPolicy, dedupTuningRequest);
const size_t dedupHotSeed = std::max<size_t>(
16ull,
estimatedOutVertexCount / std::max<size_t>(1ull, dedupTuning.workerCount * 8ull));
const size_t dedupHotEntryCount = std::bit_ceil(dedupHotSeed);
core::vector<SDedupHotEntry> dedupHotCache(dedupHotEntryCount);
const size_t dedupHotMask = dedupHotEntryCount - 1ull;

Choose a reason for hiding this comment

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

what is this for?

Comment on lines +1024 to +1091
if (needsNormalGeneration)
{
core::vector<Float3> generatedNormals(outVertexWriteCount, Float3(0.f, 0.f, 0.f));
const size_t triangleCount = indices.size() / 3ull;
for (size_t triIx = 0ull; triIx < triangleCount; ++triIx)
{
const uint32_t i0 = indices[triIx * 3ull + 0ull];
const uint32_t i1 = indices[triIx * 3ull + 1ull];
const uint32_t i2 = indices[triIx * 3ull + 2ull];
if (i0 >= outVertexWriteCount || i1 >= outVertexWriteCount || i2 >= outVertexWriteCount)
continue;

} // end namespace scene
} // end namespace nbl
const auto& p0 = outPositions[static_cast<size_t>(i0)];
const auto& p1 = outPositions[static_cast<size_t>(i1)];
const auto& p2 = outPositions[static_cast<size_t>(i2)];

const float e10x = p1.x - p0.x;
const float e10y = p1.y - p0.y;
const float e10z = p1.z - p0.z;
const float e20x = p2.x - p0.x;
const float e20y = p2.y - p0.y;
const float e20z = p2.z - p0.z;

const Float3 faceNormal(
e10y * e20z - e10z * e20y,
e10z * e20x - e10x * e20z,
e10x * e20y - e10y * e20x);

const float faceLenSq = faceNormal.x * faceNormal.x + faceNormal.y * faceNormal.y + faceNormal.z * faceNormal.z;
if (faceLenSq <= 1e-20f)
continue;

auto accumulateIfNeeded = [&](const uint32_t vertexIx)->void
{
if (outNormalNeedsGeneration[static_cast<size_t>(vertexIx)] == 0u)
return;
auto& dstNormal = generatedNormals[static_cast<size_t>(vertexIx)];
dstNormal.x += faceNormal.x;
dstNormal.y += faceNormal.y;
dstNormal.z += faceNormal.z;
};

accumulateIfNeeded(i0);
accumulateIfNeeded(i1);
accumulateIfNeeded(i2);
}

for (size_t i = 0ull; i < outVertexWriteCount; ++i)
{
if (outNormalNeedsGeneration[i] == 0u)
continue;

auto normal = generatedNormals[i];
const float lenSq = normal.x * normal.x + normal.y * normal.y + normal.z * normal.z;
if (lenSq > 1e-20f)
{
const float invLen = 1.f / std::sqrt(lenSq);
normal.x *= invLen;
normal.y *= invLen;
normal.z *= invLen;
}
else
{
normal = Float3(0.f, 0.f, 1.f);
}
outNormals[i] = normal;
}
}

Choose a reason for hiding this comment

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

don't bother generating normals, I have a polygon geo manipulator fir this I can use later

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.

2 participants

Comments