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.
pull/144/head
Sergiusz Bazanski 2019-02-25 01:46:57 +01:00 committed by whitequark
parent 9d1601eea9
commit 3a3a2755bf
9 changed files with 83 additions and 2 deletions

6
.gitmodules vendored
View File

@ -20,3 +20,9 @@
[submodule "extlib/angle"] [submodule "extlib/angle"]
path = extlib/angle path = extlib/angle
url = https://github.com/solvespace/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

View File

@ -42,6 +42,10 @@ New export/import features:
* Wavefront OBJ: a material file is exported alongside the model, containing * Wavefront OBJ: a material file is exported alongside the model, containing
mesh color information. mesh color information.
* DXF/DWG: 3D DXF files are imported as construction entities, in 3d. * 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: New rendering features:
* The "Show/hide hidden lines" button is now a tri-state button that allows * The "Show/hide hidden lines" button is now a tri-state button that allows

View File

@ -103,6 +103,17 @@ endif()
message(STATUS "Using in-tree libdxfrw") message(STATUS "Using in-tree libdxfrw")
add_subdirectory(extlib/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) if(WIN32 OR APPLE)
# On Win32 and macOS we use vendored packages, since there is little to no benefit # 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 # to trying to find system versions. In particular, trying to link to libraries from

1
extlib/flatbuffers Submodule

@ -0,0 +1 @@
Subproject commit a1f14005ab823adc1300754fd37c01e9842ed4bc

1
extlib/q3d Submodule

@ -0,0 +1 @@
Subproject commit fe71514837af1c627e9e1054d1aa0193a06d3488

View File

@ -79,7 +79,8 @@ include_directories(
${ZLIB_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}
${PNG_PNG_INCLUDE_DIR} ${PNG_PNG_INCLUDE_DIR}
${FREETYPE_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS}
${CAIRO_INCLUDE_DIRS}) ${CAIRO_INCLUDE_DIRS}
${Q3D_INCLUDE_DIR})
if(Backtrace_FOUND) if(Backtrace_FOUND)
include_directories( include_directories(
@ -211,12 +212,16 @@ add_library(solvespace-core STATIC
${solvespace_core_HEADERS} ${solvespace_core_HEADERS}
${solvespace_core_SOURCES}) ${solvespace_core_SOURCES})
add_dependencies(solvespace-core
q3d_header)
target_link_libraries(solvespace-core target_link_libraries(solvespace-core
dxfrw dxfrw
${util_LIBRARIES} ${util_LIBRARIES}
${ZLIB_LIBRARY} ${ZLIB_LIBRARY}
${PNG_LIBRARY} ${PNG_LIBRARY}
${FREETYPE_LIBRARY}) ${FREETYPE_LIBRARY}
flatbuffers)
if(Backtrace_FOUND) if(Backtrace_FOUND)
target_link_libraries(solvespace-core target_link_libraries(solvespace-core

View File

@ -7,6 +7,7 @@
// Copyright 2008-2013 Jonathan Westhues. // Copyright 2008-2013 Jonathan Westhues.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#include "solvespace.h" #include "solvespace.h"
#include "config.h"
void SolveSpaceUI::ExportSectionTo(const Platform::Path &filename) { void SolveSpaceUI::ExportSectionTo(const Platform::Path &filename) {
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp); Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
@ -826,6 +827,8 @@ void SolveSpaceUI::ExportMeshTo(const Platform::Path &filename) {
ExportMeshAsObjTo(f, fMtl, m); ExportMeshAsObjTo(f, fMtl, m);
fclose(fMtl); fclose(fMtl);
} else if(filename.HasExtension("q3do")) {
ExportMeshAsQ3doTo(f, m);
} else if(filename.HasExtension("js") || } else if(filename.HasExtension("js") ||
filename.HasExtension("html")) { filename.HasExtension("html")) {
SOutlineList *e = &(SK.GetGroup(SS.GW.activeGroup)->displayOutlines); 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<RgbaColor, flatbuffers::Offset<q3d::Material>, RgbaColorCompare> materials;
std::map<RgbaColor, std::vector<flatbuffers::Offset<q3d::Triangle>>, 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<flatbuffers::Offset<q3d::Mesh>> 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 // Export the mesh as Wavefront OBJ format. This requires us to reduce all the
// identical vertices to the same identifier, so do that first. // identical vertices to the same identifier, so do that first.

View File

@ -94,6 +94,7 @@ std::vector<FileFilter> MeshFileFilters = {
{ CN_("file-type", "Wavefront OBJ mesh"), { "obj" } }, { CN_("file-type", "Wavefront OBJ mesh"), { "obj" } },
{ CN_("file-type", "Three.js-compatible mesh, with viewer"), { "html" } }, { CN_("file-type", "Three.js-compatible mesh, with viewer"), { "html" } },
{ CN_("file-type", "Three.js-compatible mesh, mesh only"), { "js" } }, { CN_("file-type", "Three.js-compatible mesh, mesh only"), { "js" } },
{ CN_("file-type", "Q3D Object file"), { "q3do" } },
}; };
std::vector<FileFilter> SurfaceFileFilters = { std::vector<FileFilter> SurfaceFileFilters = {

View File

@ -693,6 +693,7 @@ public:
void ExportAsPngTo(const Platform::Path &filename); void ExportAsPngTo(const Platform::Path &filename);
void ExportMeshTo(const Platform::Path &filename); void ExportMeshTo(const Platform::Path &filename);
void ExportMeshAsStlTo(FILE *f, SMesh *sm); void ExportMeshAsStlTo(FILE *f, SMesh *sm);
void ExportMeshAsQ3doTo(FILE *f, SMesh *sm);
void ExportMeshAsObjTo(FILE *fObj, FILE *fMtl, SMesh *sm); void ExportMeshAsObjTo(FILE *fObj, FILE *fMtl, SMesh *sm);
void ExportMeshAsThreeJsTo(FILE *f, const Platform::Path &filename, void ExportMeshAsThreeJsTo(FILE *f, const Platform::Path &filename,
SMesh *sm, SOutlineList *sol); SMesh *sm, SOutlineList *sol);