From 3a3a2755bf9f76e24ea9477228201c4a5f1561c9 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Mon, 25 Feb 2019 01:46:57 +0100 Subject: [PATCH] Implement Q3DO export. We plan to use flatbuffers in the future for the next generation of the .slvs file format, so flatbuffers are built unconditionally; and the Q3DO exporter itself is tiny. --- .gitmodules | 6 ++++++ CHANGELOG.md | 4 ++++ CMakeLists.txt | 11 ++++++++++ extlib/flatbuffers | 1 + extlib/q3d | 1 + src/CMakeLists.txt | 9 ++++++-- src/export.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++ src/platform/gui.cpp | 1 + src/solvespace.h | 1 + 9 files changed, 83 insertions(+), 2 deletions(-) create mode 160000 extlib/flatbuffers create mode 160000 extlib/q3d diff --git a/.gitmodules b/.gitmodules index f81e342..51dd079 100644 --- a/.gitmodules +++ b/.gitmodules @@ -20,3 +20,9 @@ [submodule "extlib/angle"] path = extlib/angle url = https://github.com/solvespace/angle +[submodule "extlib/flatbuffers"] + path = extlib/flatbuffers + url = https://github.com/google/flatbuffers +[submodule "extlib/q3d"] + path = extlib/q3d + url = https://github.com/q3k/q3d diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dd1ca8..f9b2b5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,10 @@ New export/import features: * Wavefront OBJ: a material file is exported alongside the model, containing mesh color information. * DXF/DWG: 3D DXF files are imported as construction entities, in 3d. + * Q3D: [Q3D](https://github.com/q3k/q3d/) triangle meshes can now be + exported. This format allows to easily hack on triangle mesh data created + in SolveSpace, supports colour information and is more space efficient than + most other formats. New rendering features: * The "Show/hide hidden lines" button is now a tri-state button that allows diff --git a/CMakeLists.txt b/CMakeLists.txt index 83a0a51..46528bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,17 @@ endif() message(STATUS "Using in-tree libdxfrw") add_subdirectory(extlib/libdxfrw) +message(STATUS "Using in-tree flatbuffers") +set(FLATBUFFERS_BUILD_FLATLIB ON CACHE BOOL "") +set(FLATBUFFERS_BUILD_FLATC ON CACHE BOOL "") +set(FLATBUFFERS_BUILD_FLATHASH OFF CACHE BOOL "") +set(FLATBUFFERS_BUILD_TESTS OFF CACHE BOOL "") +add_subdirectory(extlib/flatbuffers EXCLUDE_FROM_ALL) + +message(STATUS "Using in-tree q3d") +add_subdirectory(extlib/q3d) +set(Q3D_INCLUDE_DIR ${CMAKE_BINARY_DIR}/extlib/q3d) + if(WIN32 OR APPLE) # On Win32 and macOS we use vendored packages, since there is little to no benefit # to trying to find system versions. In particular, trying to link to libraries from diff --git a/extlib/flatbuffers b/extlib/flatbuffers new file mode 160000 index 0000000..a1f1400 --- /dev/null +++ b/extlib/flatbuffers @@ -0,0 +1 @@ +Subproject commit a1f14005ab823adc1300754fd37c01e9842ed4bc diff --git a/extlib/q3d b/extlib/q3d new file mode 160000 index 0000000..fe71514 --- /dev/null +++ b/extlib/q3d @@ -0,0 +1 @@ +Subproject commit fe71514837af1c627e9e1054d1aa0193a06d3488 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c79442..874e88d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -79,7 +79,8 @@ include_directories( ${ZLIB_INCLUDE_DIR} ${PNG_PNG_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS} - ${CAIRO_INCLUDE_DIRS}) + ${CAIRO_INCLUDE_DIRS} + ${Q3D_INCLUDE_DIR}) if(Backtrace_FOUND) include_directories( @@ -211,12 +212,16 @@ add_library(solvespace-core STATIC ${solvespace_core_HEADERS} ${solvespace_core_SOURCES}) +add_dependencies(solvespace-core + q3d_header) + target_link_libraries(solvespace-core dxfrw ${util_LIBRARIES} ${ZLIB_LIBRARY} ${PNG_LIBRARY} - ${FREETYPE_LIBRARY}) + ${FREETYPE_LIBRARY} + flatbuffers) if(Backtrace_FOUND) target_link_libraries(solvespace-core diff --git a/src/export.cpp b/src/export.cpp index d68dce0..01020e1 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -7,6 +7,7 @@ // Copyright 2008-2013 Jonathan Westhues. //----------------------------------------------------------------------------- #include "solvespace.h" +#include "config.h" void SolveSpaceUI::ExportSectionTo(const Platform::Path &filename) { Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp); @@ -826,6 +827,8 @@ void SolveSpaceUI::ExportMeshTo(const Platform::Path &filename) { ExportMeshAsObjTo(f, fMtl, m); fclose(fMtl); + } else if(filename.HasExtension("q3do")) { + ExportMeshAsQ3doTo(f, m); } else if(filename.HasExtension("js") || filename.HasExtension("html")) { SOutlineList *e = &(SK.GetGroup(SS.GW.activeGroup)->displayOutlines); @@ -877,6 +880,54 @@ void SolveSpaceUI::ExportMeshAsStlTo(FILE *f, SMesh *sm) { } } +//----------------------------------------------------------------------------- +// Export the mesh as a Q3DO (https://github.com/q3k/q3d) file. +//----------------------------------------------------------------------------- + +#include "object_generated.h" +void SolveSpaceUI::ExportMeshAsQ3doTo(FILE *f, SMesh *sm) { + flatbuffers::FlatBufferBuilder builder(1024); + double s = SS.exportScale; + + // Create a material for every colour used, keep note of triangles belonging to color/material. + std::map, RgbaColorCompare> materials; + std::map>, RgbaColorCompare> materialTriangles; + for (const STriangle &t : sm->l) { + auto color = t.meta.color; + if (materials.find(color) == materials.end()) { + auto name = builder.CreateString(ssprintf("Color #%02x%02x%02x%02x", color.red, color.green, color.blue, color.alpha)); + auto co = q3d::CreateColor(builder, color.red, color.green, color.blue, color.alpha); + auto mo = q3d::CreateMaterial(builder, name, co); + materials.emplace(color, mo); + } + + Vector faceNormal = t.Normal(); + auto a = q3d::Vector3(t.a.x/s, t.a.y/s, t.a.z/s); + auto b = q3d::Vector3(t.b.x/s, t.b.y/s, t.b.z/s); + auto c = q3d::Vector3(t.c.x/s, t.c.y/s, t.c.z/s); + auto fn = q3d::Vector3(faceNormal.x, faceNormal.y, faceNormal.x); + auto n1 = q3d::Vector3(t.normals[0].x, t.normals[0].y, t.normals[0].z); + auto n2 = q3d::Vector3(t.normals[1].x, t.normals[1].y, t.normals[1].z); + auto n3 = q3d::Vector3(t.normals[2].x, t.normals[2].y, t.normals[2].z); + auto tri = q3d::CreateTriangle(builder, &a, &b, &c, &fn, &n1, &n2, &n3); + materialTriangles[color].push_back(tri); + } + + // Build all meshes sorted by material. + std::vector> meshes; + for (auto &it : materials) { + auto &mato = it.second; + auto to = builder.CreateVector(materialTriangles[it.first]); + auto mo = q3d::CreateMesh(builder, to, mato); + meshes.push_back(mo); + } + + auto mo = builder.CreateVector(meshes); + auto o = q3d::CreateObject(builder, mo); + q3d::FinishObjectBuffer(builder, o); + fwrite(builder.GetBufferPointer(), builder.GetSize(), 1, f); +} + //----------------------------------------------------------------------------- // Export the mesh as Wavefront OBJ format. This requires us to reduce all the // identical vertices to the same identifier, so do that first. diff --git a/src/platform/gui.cpp b/src/platform/gui.cpp index e881f30..e4475c7 100644 --- a/src/platform/gui.cpp +++ b/src/platform/gui.cpp @@ -94,6 +94,7 @@ std::vector MeshFileFilters = { { CN_("file-type", "Wavefront OBJ mesh"), { "obj" } }, { CN_("file-type", "Three.js-compatible mesh, with viewer"), { "html" } }, { CN_("file-type", "Three.js-compatible mesh, mesh only"), { "js" } }, + { CN_("file-type", "Q3D Object file"), { "q3do" } }, }; std::vector SurfaceFileFilters = { diff --git a/src/solvespace.h b/src/solvespace.h index 9c78856..dce1d17 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -693,6 +693,7 @@ public: void ExportAsPngTo(const Platform::Path &filename); void ExportMeshTo(const Platform::Path &filename); void ExportMeshAsStlTo(FILE *f, SMesh *sm); + void ExportMeshAsQ3doTo(FILE *f, SMesh *sm); void ExportMeshAsObjTo(FILE *fObj, FILE *fMtl, SMesh *sm); void ExportMeshAsThreeJsTo(FILE *f, const Platform::Path &filename, SMesh *sm, SOutlineList *sol);