AC: Support Double-Sided Faces (#6252)

* AC: Support Double-Sided Faces

The AC format marks double-sided SURF elements with the 0x20 flag, which Assimp ignored. This commit adds support for double-sided faces.

On encountering a double-sided face via the flag mentioned above, the front face is generated as usual but is then duplicated. The winding order of the duplicate is flipped to form a back face. Vertices are duplicated too so that back faces work correctly with normal vector generation and face smoothing.

* Add test file

* Simplify test case

---------

Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
This commit is contained in:
DIGITAL IMAGE DESIGN
2025-07-29 13:09:30 +02:00
committed by GitHub
parent 9f4e7c6d8d
commit 6fa9d09a97
3 changed files with 69 additions and 4 deletions

View File

@@ -75,6 +75,8 @@ static constexpr aiImporterDesc desc = {
"ac acc ac3d"
};
static constexpr auto ACDoubleSidedFlag = 0x20;
// ------------------------------------------------------------------------------------------------
// skip to the next token
inline const char *AcSkipToNextToken(const char *buffer, const char *end) {
@@ -129,6 +131,30 @@ inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end,
return buffer;
}
// ------------------------------------------------------------------------------------------------
// Reverses vertex indices in a face.
static void flipWindingOrder(aiFace &f) {
std::reverse(f.mIndices, f.mIndices + f.mNumIndices);
}
// ------------------------------------------------------------------------------------------------
// Duplicates a face and inverts it. Also duplicates all vertices (so the new face gets its own
// set of normals and isnt smoothed against the original).
static void buildBacksideOfFace(const aiFace &origFace, aiFace *&outFaces, aiVector3D *&outVertices, const aiVector3D *allVertices,
aiVector3D *&outUV, const aiVector3D *allUV, unsigned &curIdx) {
auto &newFace = *outFaces++;
newFace = origFace;
flipWindingOrder(newFace);
for (unsigned f = 0; f < newFace.mNumIndices; ++f) {
*outVertices++ = allVertices[newFace.mIndices[f]];
if (outUV) {
*outUV = allUV[newFace.mIndices[f]];
outUV++;
}
newFace.mIndices[f] = curIdx++;
}
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
AC3DImporter::AC3DImporter() :
@@ -451,6 +477,8 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
if ((*it).entries.empty()) {
ASSIMP_LOG_WARN("AC3D: surface has zero vertex references");
}
const bool isDoubleSided = ACDoubleSidedFlag == (it->flags & ACDoubleSidedFlag);
const int doubleSidedFactor = isDoubleSided ? 2 : 1;
// validate all vertex indices to make sure we won't crash here
for (it2 = (*it).entries.begin(),
@@ -480,8 +508,8 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
// triangle strip
case Surface::TriangleStrip:
needMat[idx].first += (unsigned int)(*it).entries.size() - 2;
needMat[idx].second += ((unsigned int)(*it).entries.size() - 2) * 3;
needMat[idx].first += static_cast<unsigned int>(it->entries.size() - 2) * doubleSidedFactor;
needMat[idx].second += static_cast<unsigned int>(it->entries.size() - 2) * 3 * doubleSidedFactor;
break;
default:
@@ -494,8 +522,8 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
case Surface::Polygon:
// the number of faces increments by one, the number
// of vertices by surface.numref.
needMat[idx].first++;
needMat[idx].second += (unsigned int)(*it).entries.size();
needMat[idx].first += doubleSidedFactor;
needMat[idx].second += static_cast<unsigned int>(it->entries.size()) * doubleSidedFactor;
};
}
unsigned int *pip = node->mMeshes = new unsigned int[node->mNumMeshes];
@@ -545,6 +573,7 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
for (it = object.surfaces.begin(); it != end; ++it) {
if (mat == (*it).mat) {
const Surface &src = *it;
const bool isDoubleSided = ACDoubleSidedFlag == (src.flags & ACDoubleSidedFlag);
// closed polygon
uint8_t type = (*it).GetType();
@@ -570,6 +599,8 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
++uv;
}
}
if(isDoubleSided) // Need a backface?
buildBacksideOfFace(faces[-1], faces, vertices, mesh->mVertices, uv, mesh->mTextureCoords[0], cur);
}
} else if (type == Surface::TriangleStrip) {
for (unsigned int i = 0; i < (unsigned int)src.entries.size() - 2; ++i) {
@@ -619,6 +650,8 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
uv->y = entry3.second.y;
++uv;
}
if(isDoubleSided) // Need a backface?
buildBacksideOfFace(faces[-1], faces, vertices, mesh->mVertices, uv, mesh->mTextureCoords[0], cur);
}
} else {