From 8fd12d8b7e20d0bb9db2ba4b4f10890a554ca1c9 Mon Sep 17 00:00:00 2001
From: Alex Shvartzkop <dudesuchamazing@gmail.com>
Date: Thu, 16 May 2024 21:57:44 +0300
Subject: [PATCH] Allow reading VRML models for glTF export.

Note that some VRML 2.0 models fill fail until our patches are integrated into OCCT 7.9.0.
---
 cmake/FindOCC.cmake                      |  1 +
 pcbnew/exporters/step/step_pcb_model.cpp | 73 +++++++++++++++++++++++-
 pcbnew/exporters/step/step_pcb_model.h   |  5 +-
 3 files changed, 76 insertions(+), 3 deletions(-)

diff --git a/cmake/FindOCC.cmake b/cmake/FindOCC.cmake
index bcd99682f6..f2726248be 100644
--- a/cmake/FindOCC.cmake
+++ b/cmake/FindOCC.cmake
@@ -86,6 +86,7 @@ set( OCC_LIBS_POST_78
     TKDEGLTF
     TKDESTEP
     TKDESTL
+    TKDEVRML
 )
 
 set(OCC_TYPE "OpenCASCADE Standard Edition")
diff --git a/pcbnew/exporters/step/step_pcb_model.cpp b/pcbnew/exporters/step/step_pcb_model.cpp
index 5c9402ab44..22caf772da 100644
--- a/pcbnew/exporters/step/step_pcb_model.cpp
+++ b/pcbnew/exporters/step/step_pcb_model.cpp
@@ -118,6 +118,7 @@
 #include <GC_MakeCircle.hxx>
 
 #include <RWGltf_CafWriter.hxx>
+#include <VrmlAPI_CafReader.hxx>
 
 #include <macros.h>
 
@@ -592,6 +593,46 @@ static Standard_Boolean rescaleShapes( const TDF_Label& theLabel, const gp_XYZ&
 }
 
 
+// Sets names in assembly to <aPrefix> (<old name>), or to <aPrefix>
+static Standard_Boolean prefixNames( const TDF_Label&                  aLabel,
+                                     const TCollection_ExtendedString& aPrefix )
+{
+    Handle( KI_XCAFDoc_AssemblyGraph ) aG = new KI_XCAFDoc_AssemblyGraph( aLabel );
+
+    if( aG.IsNull() )
+    {
+        Message::SendFail( "Couldn't create assembly graph." );
+        return Standard_False;
+    }
+
+    Standard_Boolean anIsDone = Standard_True;
+
+    for( Standard_Integer idx = 1; idx <= aG->NbNodes(); idx++ )
+    {
+        const TDF_Label& lbl = aG->GetNode( idx );
+        Handle( TDataStd_Name ) nameHandle;
+
+        if( lbl.FindAttribute( TDataStd_Name::GetID(), nameHandle ) )
+        {
+            TCollection_ExtendedString name;
+
+            name += aPrefix;
+            name += " (";
+            name += nameHandle->Get();
+            name += ")";
+
+            TDataStd_Name::Set( lbl, name );
+        }
+        else
+        {
+            TDataStd_Name::Set( lbl, aPrefix );
+        }
+    }
+
+    return anIsDone;
+}
+
+
 STEP_PCB_MODEL::STEP_PCB_MODEL( const wxString& aPcbName )
 {
     m_app = XCAFApp_Application::GetApplication();
@@ -2436,7 +2477,22 @@ bool STEP_PCB_MODEL::getModelLabel( const std::string& aFileNameUTF8, VECTOR3D a
                 }
             }
 
-            return false; // No replacement model found
+            // VRML models only work when exporting to glTF
+            // Also OCCT < 7.9.0 fail to load most VRML 2.0 models because of Switch nodes
+            if( readVRML( doc, aFileNameUTF8.c_str() ) )
+            {
+                Handle( XCAFDoc_ShapeTool ) shapeTool =
+                        XCAFDoc_DocumentTool::ShapeTool( doc->Main() );
+
+                prefixNames( shapeTool->Label(),
+                             TCollection_ExtendedString( baseName.c_str().AsChar() ) );
+            }
+            else
+            {
+                ReportMessage( wxString::Format( wxT( "readVRML() failed on filename '%s'.\n" ),
+                                                 fileName ) );
+                return false;
+            }
         }
         else // Substitution is not allowed
         {
@@ -2624,6 +2680,21 @@ bool STEP_PCB_MODEL::readSTEP( Handle( TDocStd_Document )& doc, const char* fnam
 }
 
 
+bool STEP_PCB_MODEL::readVRML( Handle( TDocStd_Document ) & doc, const char* fname )
+{
+    VrmlAPI_CafReader                reader;
+    RWMesh_CoordinateSystemConverter conv;
+    conv.SetInputLengthUnit( 2.54 );
+    reader.SetCoordinateSystemConverter( conv );
+    reader.SetDocument( doc );
+
+    if( !reader.Perform( TCollection_AsciiString( fname ), Message_ProgressRange() ) )
+        return false;
+
+    return true;
+}
+
+
 TDF_Label STEP_PCB_MODEL::transferModel( Handle( TDocStd_Document ) & source,
                                          Handle( TDocStd_Document ) & dest, VECTOR3D aScale )
 {
diff --git a/pcbnew/exporters/step/step_pcb_model.h b/pcbnew/exporters/step/step_pcb_model.h
index c140602f36..91828684e3 100644
--- a/pcbnew/exporters/step/step_pcb_model.h
+++ b/pcbnew/exporters/step/step_pcb_model.h
@@ -223,8 +223,9 @@ private:
     bool getModelLocation( bool aBottom, VECTOR2D aPosition, double aRotation, VECTOR3D aOffset,
                            VECTOR3D aOrientation, TopLoc_Location& aLocation );
 
-    bool readIGES( Handle( TDocStd_Document )& m_doc, const char* fname );
-    bool readSTEP( Handle( TDocStd_Document )& m_doc, const char* fname );
+    bool readIGES( Handle( TDocStd_Document ) & aDoc, const char* aFname );
+    bool readSTEP( Handle( TDocStd_Document ) & aDoc, const char* aFname );
+    bool readVRML( Handle( TDocStd_Document ) & aDoc, const char* aFname );
 
     TDF_Label transferModel( Handle( TDocStd_Document )& source, Handle( TDocStd_Document ) & dest,
                              VECTOR3D aScale );