Ver código fonte

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 <[email protected]>
DIGITAL IMAGE DESIGN 1 mês atrás
pai
commit
6fa9d09a97

+ 37 - 4
code/AssetLib/AC/ACLoader.cpp

@@ -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 isn’t 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 {
 

+ 21 - 0
test/models/AC/doubleSidedFace.ac

@@ -0,0 +1,21 @@
+AC3Db
+MATERIAL "" rgb 1 1 1  amb 0.2 0.2 0.2  emis 0 0 0  spec 0.5 0.5 0.5  shi 10  trans 0
+OBJECT world
+kids 1
+OBJECT poly
+name "rect"
+loc 1 0.5 0
+numvert 4
+-1 0.5 0
+1 0.5 0
+1 -0.5 0
+-1 -0.5 0
+numsurf 1
+SURF 0x20
+mat 0
+refs 4
+3 0 0
+2 1 0
+1 1 1
+0 0 1
+kids 0

+ 11 - 0
test/unit/utACImportExport.cpp

@@ -141,3 +141,14 @@ TEST(utACImportExport, testFormatDetection) {
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/AC/TestFormatDetection", aiProcess_ValidateDataStructure);
     ASSERT_NE(nullptr, scene);
 }
+
+TEST(utACImportExport, importDobuleSidedFaces) {
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/AC/doubleSidedFace.ac", aiProcess_ValidateDataStructure);
+    ASSERT_NE(nullptr, scene);
+    // The scene contains one double-sided, rectangular AC surface. It should resolve to two quads (front + back) with eight
+    // vertices (one per side to guarantee proper normal vectors).
+    ASSERT_EQ(scene->mNumMeshes, 1u);
+    ASSERT_EQ(scene->mMeshes[0]->mNumFaces, 2u);
+    ASSERT_EQ(scene->mMeshes[0]->mNumVertices, 8u);
+}