From 63369a9c385c485c958e329e3cbd14b28549b571 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Wed, 22 Sep 2021 11:11:59 -0500 Subject: [PATCH 1/2] [Mesh] Improve NASTRAN input support Adds support for original-NASTRAN fixed-field-width low-precision GRID element (the existing code assumed space-delimited input). --- src/Mod/Mesh/App/Core/MeshIO.cpp | 110 ++++++++++++++++++++++++------- src/Mod/Mesh/Gui/Command.cpp | 6 +- 2 files changed, 89 insertions(+), 27 deletions(-) diff --git a/src/Mod/Mesh/App/Core/MeshIO.cpp b/src/Mod/Mesh/App/Core/MeshIO.cpp index f0a1e0b7e7..cb23619c94 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.cpp +++ b/src/Mod/Mesh/App/Core/MeshIO.cpp @@ -148,6 +148,8 @@ std::vector MeshInput::supportedMeshFormats() fmt.emplace_back("stl"); fmt.emplace_back("ast"); fmt.emplace_back("obj"); + fmt.emplace_back("nas"); + fmt.emplace_back("bdf"); fmt.emplace_back("off"); fmt.emplace_back("smf"); return fmt; @@ -1636,10 +1638,6 @@ bool MeshInput::LoadNastran (std::istream &rstrIn) if ((!rstrIn) || (rstrIn.bad() == true)) return false; - boost::regex rx_p("\\s*GRID\\s+([0-9]+)" - "\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)" - "\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)" - "\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)\\s*"); boost::regex rx_t("\\s*CTRIA3\\s+([0-9]+)\\s+([0-9]+)" "\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s*"); boost::regex rx_q("\\s*CQUAD4\\s+([0-9]+)\\s+([0-9]+)" @@ -1656,6 +1654,8 @@ bool MeshInput::LoadNastran (std::istream &rstrIn) std::map mTria; std::map mQuad; + int badElementCounter = 0; + while (std::getline(rstrIn, line)) { upper(ltrim(line)); if (line.empty()) { @@ -1664,22 +1664,24 @@ bool MeshInput::LoadNastran (std::istream &rstrIn) else if (line.rfind("GRID*", 0) == 0) { // This element is the 16-digit-precision GRID element, which occupies two lines of the card. Note that // FreeCAD discards the extra precision, downcasting to an four-byte float. - // - // The two lines are: - // 1 8 24 40 56 - // GRID* Index(16) Blank(16) x(16) y(at least one) - // * z(at least one) - // - // The first character is typically the sign, and may be omitted for positive numbers, - // so it is possible for a field to begin with a blank. Trailing zeros may be omitted, so - // a field may also end with blanks. No space or other delimiter is required between - // the numbers. The following is a valid NASTRAN GRID* element: - // - // GRID* 1 0.1234567890120. - // * 1. - // - if (line.length() < 8 + 16 + 16 + 16 + 1) // Element type(8), index(16), empty(16), x(16), y(>=1) + // + // The two lines are: + // 1 8 24 40 56 + // GRID* Index(16) Blank(16) x(16) y(at least one) + // * z(at least one) + // + // The first character is typically the sign, and may be omitted for positive numbers, + // so it is possible for a field to begin with a blank. Trailing zeros may be omitted, so + // a field may also end with blanks. No space or other delimiter is required between + // the numbers. The following is a valid NASTRAN GRID* element: + // + // GRID* 1 0.1234567890120. + // * 1. + // + if (line.length() < 8 + 16 + 16 + 16 + 1) { // Element type(8), index(16), empty(16), x(16), y(>=1) + badElementCounter++; continue; + } auto indexView = std::string_view(&line[8], 16); auto blankView = std::string_view(&line[8+16], 16); auto xView = std::string_view(&line[8+16+16], 16); @@ -1688,8 +1690,10 @@ bool MeshInput::LoadNastran (std::istream &rstrIn) std::string line2; std::getline(rstrIn, line2); if ((!line2.empty() && line2[0] != '*') || - line2.length() < 9) + line2.length() < 9) { + badElementCounter++; continue; // File format error: second line is not a continuation line + } auto zView = std::string_view(&line2[8]); // We have to strip off any whitespace (technically really just any *trailing* whitespace): @@ -1700,19 +1704,23 @@ bool MeshInput::LoadNastran (std::istream &rstrIn) auto converter = boost::cnv::spirit(); auto indexCheck = boost::convert(indexString, converter); - if (!indexCheck.is_initialized()) + if (!indexCheck.is_initialized()) { // File format error: index couldn't be converted to an integer + badElementCounter++; continue; - index = indexCheck.get(); + } + index = indexCheck.get() - 1; // Minus one so we are zero-indexed to match existing code // Get the high-precision versions first auto x = boost::convert(xString, converter); auto y = boost::convert(yString, converter); auto z = boost::convert(zString, converter); - if (!x.is_initialized() || !y.is_initialized() || !z.is_initialized()) + if (!x.is_initialized() || !y.is_initialized() || !z.is_initialized()) { // File format error: x, y or z could not be converted + badElementCounter++; continue; + } // Now drop precision: mNode[index].x = (float)x.get(); @@ -1720,13 +1728,63 @@ bool MeshInput::LoadNastran (std::istream &rstrIn) mNode[index].z = (float)z.get(); } else if (line.rfind("GRID", 0) == 0) { - if (boost::regex_match(line.c_str(), what, rx_p)) { + + boost::regex rx_spaceDelimited("\\s*GRID\\s+([0-9]+)" + "\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)" + "\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)" + "\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)\\s*"); + + if (boost::regex_match(line.c_str(), what, rx_spaceDelimited)) { // insert the read-in vertex into a map to preserve the order index = std::atol(what[1].first)-1; mNode[index].x = (float)std::atof(what[2].first); mNode[index].y = (float)std::atof(what[5].first); mNode[index].z = (float)std::atof(what[8].first); } + else { + // Classic NASTRAN uses a fixed 8 character field width: + // 1 8 16 24 32 40 + // $-------ID------CP------X1------X2------X3------CD------PS------9-------+------- + // GRID 1 1.2345671.2345671.234567 + // GRID 112 6.0000000.5000000.00E+00 + + if (line.length() < 41) { // Element type(8), id(8), cp(8), x(8), y(8), z(at least 1) + badElementCounter++; + continue; + } + auto indexView = std::string_view(&line[8], 8); + auto xView = std::string_view(&line[24], 8); + auto yView = std::string_view(&line[32], 8); + auto zView = std::string_view(&line[40], 8); + + auto indexString = boost::trim_copy(std::string(indexView)); + auto xString = boost::trim_copy(std::string(xView)); + auto yString = boost::trim_copy(std::string(yView)); + auto zString = boost::trim_copy(std::string(zView)); + + auto converter = boost::cnv::spirit(); + auto indexCheck = boost::convert(indexString, converter); + if (!indexCheck.is_initialized()) { + // File format error: index couldn't be converted to an integer + badElementCounter++; + continue; + } + index = indexCheck.get() - 1; // Minus one so we are zero-indexed to match existing code + + auto x = boost::convert(xString, converter); + auto y = boost::convert(yString, converter); + auto z = boost::convert(zString, converter); + + if (!x.is_initialized() || !y.is_initialized() || !z.is_initialized()) { + // File format error: x, y or z could not be converted + badElementCounter++; + continue; + } + + mNode[index].x = x.get(); + mNode[index].y = y.get(); + mNode[index].z = z.get(); + } } else if (line.rfind("CTRIA3 ", 0) == 0) { if (boost::regex_match(line.c_str(), what, rx_t)) { @@ -1749,6 +1807,10 @@ bool MeshInput::LoadNastran (std::istream &rstrIn) } } + if (badElementCounter > 0) { + Base::Console().Warning("Found bad elements while reading NASTRAN file."); + } + float fLength[2]; if (mTria.empty()) index = 0; diff --git a/src/Mod/Mesh/Gui/Command.cpp b/src/Mod/Mesh/Gui/Command.cpp index 0110e54240..f92ca46735 100644 --- a/src/Mod/Mesh/Gui/Command.cpp +++ b/src/Mod/Mesh/Gui/Command.cpp @@ -449,15 +449,15 @@ void CmdMeshImport::activated(int) { // use current path as default QStringList filter; - filter << QString::fromLatin1("%1 (*.stl *.ast *.bms *.obj *.off *.ply)").arg(QObject::tr("All Mesh Files")); + filter << QString::fromLatin1("%1 (*.stl *.ast *.bms *.obj *.off *.iv *.ply *.nas *.bdf)").arg(QObject::tr("All Mesh Files")); filter << QString::fromLatin1("%1 (*.stl)").arg(QObject::tr("Binary STL")); filter << QString::fromLatin1("%1 (*.ast)").arg(QObject::tr("ASCII STL")); filter << QString::fromLatin1("%1 (*.bms)").arg(QObject::tr("Binary Mesh")); filter << QString::fromLatin1("%1 (*.obj)").arg(QObject::tr("Alias Mesh")); filter << QString::fromLatin1("%1 (*.off)").arg(QObject::tr("Object File Format")); - filter << QString::fromLatin1("%1 (*.iv)").arg(QObject::tr("Inventor V2.1 ascii")); + filter << QString::fromLatin1("%1 (*.iv)").arg(QObject::tr("Inventor V2.1 ASCII")); filter << QString::fromLatin1("%1 (*.ply)").arg(QObject::tr("Stanford Polygon")); - //filter << "Nastran (*.nas *.bdf)"; + filter << QString::fromLatin1("%1 (*.nas *.bdf)").arg(QObject::tr("NASTRAN")); filter << QString::fromLatin1("%1 (*.*)").arg(QObject::tr("All Files")); // Allow multi selection From ba5adca79ba5ff788915b8b04a42e13e9e989126 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sat, 25 Sep 2021 16:05:19 -0500 Subject: [PATCH 2/2] [Mesh] Add unit tests for NASTRAN reader Also make some minor deprecation and verbosity cleanups to the Mesh unit test cases. --- src/Mod/Mesh/App/MeshTestsApp.py | 60 +++++++++++++------ .../NASTRAN_Test_Delimited_GRID_CQUAD4.bdf | 35 +++++++++++ .../TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf | 29 +++++++++ .../App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf | 33 ++++++++++ src/Mod/Mesh/CMakeLists.txt | 16 +++++ 5 files changed, 155 insertions(+), 18 deletions(-) create mode 100644 src/Mod/Mesh/App/TestData/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf create mode 100644 src/Mod/Mesh/App/TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf create mode 100644 src/Mod/Mesh/App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf diff --git a/src/Mod/Mesh/App/MeshTestsApp.py b/src/Mod/Mesh/App/MeshTestsApp.py index 449d91a1f0..7d932b9484 100644 --- a/src/Mod/Mesh/App/MeshTestsApp.py +++ b/src/Mod/Mesh/App/MeshTestsApp.py @@ -12,6 +12,7 @@ try: except Exception: import thread +from os.path import join #--------------------------------------------------------------------------- # define the functions to test the FreeCAD mesh module @@ -167,7 +168,7 @@ class MeshGeoTestCases(unittest.TestCase): f1 = planarMeshObject.Facets[0] f2 = planarMeshObject.Facets[1] res=f1.intersect(f2) - self.failUnless(len(res) == 0) + self.assertTrue(len(res) == 0) def testIntersection2(self): @@ -182,7 +183,7 @@ class MeshGeoTestCases(unittest.TestCase): f2 = planarMeshObject.Facets[1] # does definitely NOT intersect res=f1.intersect(f2) - self.failUnless(len(res) == 0) + self.assertTrue(len(res) == 0) class PivyTestCases(unittest.TestCase): def setUp(self): @@ -208,11 +209,11 @@ class PivyTestCases(unittest.TestCase): rp.setRay(coin.SbVec3f(-16.05,16.0,16.0),coin.SbVec3f(0,-1,0)) rp.apply(view.getSoRenderManager().getSceneGraph()) pp=rp.getPickedPoint() - self.failUnless(pp != None) + self.assertTrue(pp != None) det=pp.getDetail() - self.failUnless(det.getTypeId() == coin.SoFaceDetail.getClassTypeId()) + self.assertTrue(det.getTypeId() == coin.SoFaceDetail.getClassTypeId()) det=coin.cast(det,str(det.getTypeId().getName())) - self.failUnless(det.getFaceIndex() == 1) + self.assertTrue(det.getFaceIndex() == 1) def testPrimitiveCount(self): if not FreeCAD.GuiUp: @@ -231,8 +232,8 @@ class PivyTestCases(unittest.TestCase): view.setAxisCross(False) pc=coin.SoGetPrimitiveCountAction() pc.apply(view.getSceneGraph()) - self.failUnless(pc.getTriangleCount() == 2) - #self.failUnless(pc.getPointCount() == 6) + self.assertTrue(pc.getTriangleCount() == 2) + #self.assertTrue(pc.getPointCount() == 6) def tearDown(self): #closing doc @@ -243,15 +244,15 @@ class PivyTestCases(unittest.TestCase): def loadFile(name): #lock.acquire() mesh=Mesh.Mesh() - FreeCAD.Console.PrintMessage("Create mesh instance\n") + #FreeCAD.Console.PrintMessage("Create mesh instance\n") #lock.release() mesh.read(name) - FreeCAD.Console.PrintMessage("Mesh loaded successfully.\n") + #FreeCAD.Console.PrintMessage("Mesh loaded successfully.\n") def createMesh(r,s): - FreeCAD.Console.PrintMessage("Create sphere (%s,%s)...\n"%(r,s)) + #FreeCAD.Console.PrintMessage("Create sphere (%s,%s)...\n"%(r,s)) mesh=Mesh.createSphere(r,s) - FreeCAD.Console.PrintMessage("... destroy sphere\n") + #FreeCAD.Console.PrintMessage("... destroy sphere\n") class LoadMeshInThreadsCases(unittest.TestCase): @@ -267,7 +268,7 @@ class LoadMeshInThreadsCases(unittest.TestCase): mesh=Mesh.createSphere(10.0,100) # a fine sphere name=tempfile.gettempdir() + os.sep + "mesh.stl" mesh.write(name) - FreeCAD.Console.PrintMessage("Write mesh to %s\n"%(name)) + #FreeCAD.Console.PrintMessage("Write mesh to %s\n"%(name)) #lock=thread.allocate_lock() for i in range(2): thread.start_new(loadFile,(name,)) @@ -295,9 +296,9 @@ class PolynomialFitCases(unittest.TestCase): v.append(FreeCAD.Vector(2,2,0.0)) d = Mesh.polynomialFit(v) c = d["Coefficients"] - print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) + #print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) for i in d["Residuals"]: - self.failUnless(math.fabs(i) < 0.0001, "Too high residual %f" % math.fabs(i)) + self.assertTrue(math.fabs(i) < 0.0001, "Too high residual %f" % math.fabs(i)) def testFitExact(self): # symmetric @@ -310,9 +311,9 @@ class PolynomialFitCases(unittest.TestCase): v.append(FreeCAD.Vector(2,1,0.0)) d = Mesh.polynomialFit(v) c = d["Coefficients"] - print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) + #print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) for i in d["Residuals"]: - self.failUnless(math.fabs(i) < 0.0001, "Too high residual %f" % math.fabs(i)) + self.assertTrue(math.fabs(i) < 0.0001, "Too high residual %f" % math.fabs(i)) def testFitBad(self): # symmetric @@ -328,9 +329,32 @@ class PolynomialFitCases(unittest.TestCase): v.append(FreeCAD.Vector(2,2,0.0)) d = Mesh.polynomialFit(v) c = d["Coefficients"] - print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) + #print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) for i in d["Residuals"]: - self.failIf(math.fabs(i) < 0.0001, "Residual %f must be higher" % math.fabs(i)) + self.assertFalse(math.fabs(i) < 0.0001, "Residual %f must be higher" % math.fabs(i)) + + def tearDown(self): + pass + + +class NastranReader(unittest.TestCase): + def setUp(self): + self.test_dir = join(FreeCAD.getHomePath(), "Mod", "Mesh", "App", "TestData") + + def testEightCharGRIDElement(self): + m = Mesh.read(f"{self.test_dir}/NASTRAN_Test_GRID_CQUAD4.bdf") + self.assertEqual(m.CountPoints,10) + self.assertEqual(m.CountFacets,8) # Quads split into two triangles + + def testDelimitedGRIDElement(self): + m = Mesh.read(f"{self.test_dir}/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf") + self.assertEqual(m.CountPoints,10) + self.assertEqual(m.CountFacets,8) # Quads split into two triangles + + def testSixteenCharGRIDElement(self): + m = Mesh.read(f"{self.test_dir}/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf") + self.assertEqual(m.CountPoints,4) + self.assertEqual(m.CountFacets,2) # Quads split into two triangles def tearDown(self): pass diff --git a/src/Mod/Mesh/App/TestData/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf b/src/Mod/Mesh/App/TestData/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf new file mode 100644 index 0000000000..2e8bb1ec80 --- /dev/null +++ b/src/Mod/Mesh/App/TestData/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf @@ -0,0 +1,35 @@ +$------------------------------------------------------------------------------- +$ Nastran case control file +$ Part of the FreeCAD unit test system. +$ This case uses space-delimited floating-point numbers that are not aligned to +$ eight-character field widths. +$------------------------------------------------------------------------------- +$ Exec control section +$------------------------------------------------------------------------------- +ID TESTCASE,Nastran +APP DISP +SOL 1 +TIME 20 +CEND +$------------------------------------------------------------------------------- +$ Case control section +$------------------------------------------------------------------------------- +MAXLINES = 1000000 +TITLE = TESTCASE +SUBTITLE = Test case for FreeCAD Mesh NASTRAN input + +GRID 1 0.00E+00 0.00E+00 0.00E+00 +GRID 2 0.00E+00 0.500000 0.00E+00 +GRID 3 0.00E+00 1.000000 0.00E+00 +GRID 4 0.00E+00 1.500000 0.00E+00 +GRID 5 0.00E+00 2.000000 0.00E+00 +GRID 6 0.00E+00 0.00E+00 0.500000 +GRID 7 0.00E+00 0.500000 0.500000 +GRID 8 0.00E+00 1.000000 0.500000 +GRID 9 0.00E+00 1.500000 0.500000 +GRID 10 0.00E+00 2.000000 0.500000 +CQUAD4 1 400 1 6 7 2 +CQUAD4 2 400 2 7 8 3 +CQUAD4 3 400 3 8 9 4 +CQUAD4 4 400 4 9 10 5 +ENDDATA diff --git a/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf b/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf new file mode 100644 index 0000000000..5891b060c5 --- /dev/null +++ b/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf @@ -0,0 +1,29 @@ +------------------------------------------------------------------------------- +$ Nastran case control file +$ Part of the FreeCAD unit test system. +$ This cases uses the high-prevision 16-char width GRID* element with no +$ delimiter. +$------------------------------------------------------------------------------- +$ Exec control section +$------------------------------------------------------------------------------- +ID TESTCASE,Nastran +APP DISP +SOL 1 +TIME 20 +CEND +$------------------------------------------------------------------------------- +$ Case control section +$------------------------------------------------------------------------------- +MAXLINES = 1000000 +TITLE = TESTCASE +SUBTITLE = Test case for FreeCAD Mesh NASTRAN input + +GRID* 1 0.00000000000000-1. +* 0. +GRID* 2 1.00000000000000-1. +* 0. +GRID* 3 0.000000000000001. +* 0. +GRID* 4 1.000000000000001. +* 0. +CQUAD4 1 400 1 2 4 3 diff --git a/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf b/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf new file mode 100644 index 0000000000..decf720ede --- /dev/null +++ b/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf @@ -0,0 +1,33 @@ +$------------------------------------------------------------------------------- +$ Nastran case control file +$ Part of the FreeCAD unit test system. +$------------------------------------------------------------------------------- +$ Exec control section +$------------------------------------------------------------------------------- +ID TESTCASE,Nastran +APP DISP +SOL 1 +TIME 20 +CEND +$------------------------------------------------------------------------------- +$ Case control section +$------------------------------------------------------------------------------- +MAXLINES = 1000000 +TITLE = TESTCASE +SUBTITLE = Test case for FreeCAD Mesh NASTRAN input + +GRID 1 0.00E+000.00E+000.00E+00 +GRID 2 0.00E+000.5000000.00E+00 +GRID 3 0.00E+001.0000000.00E+00 +GRID 4 0.00E+001.5000000.00E+00 +GRID 5 0.00E+002.0000000.00E+00 +GRID 6 0.00E+000.00E+000.500000 +GRID 7 0.00E+000.5000000.500000 +GRID 8 0.00E+001.0000000.500000 +GRID 9 0.00E+001.5000000.500000 +GRID 10 0.00E+002.0000000.500000 +CQUAD4 1 400 1 6 7 2 +CQUAD4 2 400 2 7 8 3 +CQUAD4 3 400 3 8 9 4 +CQUAD4 4 400 4 9 10 5 +ENDDATA diff --git a/src/Mod/Mesh/CMakeLists.txt b/src/Mod/Mesh/CMakeLists.txt index e9c35d1be7..c53218a8c1 100644 --- a/src/Mod/Mesh/CMakeLists.txt +++ b/src/Mod/Mesh/CMakeLists.txt @@ -10,6 +10,12 @@ set(Mesh_Scripts App/MeshTestsApp.py ) +set(MeshTestDataFiles + App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf + App/TestData/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf + App/TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf +) + if(BUILD_GUI) list (APPEND Mesh_Scripts InitGui.py) endif(BUILD_GUI) @@ -18,15 +24,25 @@ add_custom_target(MeshScripts ALL SOURCES ${Mesh_Scripts} ) +add_custom_target(MeshTestData ALL + SOURCES ${MeshTestDataFiles} +) + fc_target_copy_resource_flat(MeshScripts ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/Mod/Mesh ${Mesh_Scripts} ) +fc_copy_sources(MeshTestData "${CMAKE_BINARY_DIR}/Mod/Mesh/" ${MeshTestDataFiles}) + INSTALL( FILES ${Mesh_Scripts} DESTINATION Mod/Mesh ) + + +INSTALL(FILES ${MeshTestDataFiles} DESTINATION Mod/Mesh/Data) +