New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GSoC] Samir55 / 3MF file format #3934

Closed
alranel opened this Issue May 4, 2017 · 114 comments

Comments

@alranel
Member

alranel commented May 4, 2017

This issue is dedicated to the GSoC student @Samir55 who proposed to implement support for the 3MF file format in Slic3r:
https://summerofcode.withgoogle.com/projects/#6343944127381504

This topic was discussed in this original issue:
#2811

@Samir55 already contributed to the Slic3r codebase by adding zoom commands (pull request #3864, merged).

@Samir55, welcome to Slic3r! Would you please introduce yourself to the Slic3r community? Please mention any experience with 3D printing or similar technologies, and include your time zone and operating system.

@Samir55

This comment has been minimized.

Member

Samir55 commented May 4, 2017

I'm Samir, a computer engineering undergraduate student, I have some experience in CNC machines and G-code as I am working with my team this semester to make a machine like CNC machine (but we are using a touch screen stylus instead which draws any picture we send to it) . 3D printers are not so popular yet in our country :) so, I have little experience with 3D printers. I dealt with 3D model files (like: OBJ ) in the computer graphics game project .

Time Zone: Eastern European Time Zone (UTC+02:00)

Operating System : Ubuntu 14.04 and Mac OS Sierra .

@Samir55

This comment has been minimized.

Member

Samir55 commented May 5, 2017

Today's Task (5 May):

  • Studying different Formats ( AMF, OBJ, STL )
  • Listing the differences between them and the information each carries.
  • Putting the generated document in the Slic3r Wiki
    Note: this task may not be finished today :)
@NateTG

This comment has been minimized.

Contributor

NateTG commented May 5, 2017

If there are specs or references about the formats that you used, and those are available on-line, be sure to include links in the wiki.

@Samir55

This comment has been minimized.

Member

Samir55 commented May 6, 2017

I have made a draft about what file formats Slic3r support document. This is the link. I have some questions found in the comments. any help is really appreciated :)

@Samir55

This comment has been minimized.

Member

Samir55 commented May 7, 2017

This Week Task (From May 8):

  • Improve and Finalize the wiki draft in this link (Still, needs feedback).
  • Study Slic3r::IO.
  • Study Slic3r::Model.
  • See how to improve the proposal.
@Samir55

This comment has been minimized.

Member

Samir55 commented May 7, 2017

Question No.1:

Does Slic3r use the loaded materials from OBJ files?
As I see in IO .cpp file in function OBJ::read the vector<tinyobj::material_t> materials 
is not used in this function? 
why it's declared?
@Samir55

This comment has been minimized.

Member

Samir55 commented May 9, 2017

@alexrj @lordofhyphens I am Studying now Slic3r:: Model and I want to write a documentation for it, I want to include Class Diagram, what each function in the class does, should I include something else?
I mean also ModelMaterial, ModelObject, ModelVolume , ModelInstance classes

@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented May 10, 2017

@Samir55 Regarding materials in OBJ files:

Odds are it's there for growth reasons or to make the reader/writer idempotent (read an obj -> write obj should not lose information).

If you're documenting Slic3r::Model, be sure to take a look at https://github.com/prusa3d/Slic3r/blob/master/xs/src/libslic3r/Model.hpp
The patches didn't usually make it upstream because the author tended to also have code changes in the same areas, which makes for very messy PRs.

Personally, I'd prefer to inline the documentation and generate "nice" docs via something like doxygen, which does a decent job of diagramming C/C++.

If you do any documenting in the source, please do (and commit) that first.

@alranel

This comment has been minimized.

Member

alranel commented May 10, 2017

@Samir55, writing documentation for Slic3r::Model is indeed a useful approach. It will help you a lot when working on 3MF. I agree with @lordofhyphens's suggestion about using Doxygen syntax. It's easy to learn, and it's a nice skill to have for coding C++ libraries.

Regarding your document about formats, a few notes:

  • Create a page for it in the Slic3r wiki instead of using Google Docs.
  • The POV format is supported only for exporting, using the --export-pov option of the C++ command line tool (see src/slic3r.cpp). We just provide a handy tool for converting a model to the POVray format, but no slicing or toolpath generation happens there.
  • It's worth mentioning that meshes in STL format don't have vertex indices. This is a very important weakness of the STL format, because in order to find whether two facets are neighbors you need to compare their vertices coordinates numerically (which may have rounding errors) instead of just comparing the indices.
  • You should improve the document by clearly stating what information is officially supported by the formats (you already wrote this) vs. what is supported by Slic3r when reading vs. what is supported by Slic3r when writing (for example, we ignore any .mtl files for OBJ or textures).
  • Another important thing to understand and document is the meaning of the "materials" word. In OBJ, materials are associated to facets, thus they are basically surface attributes/colors which say nothing about the interior of the object. In AMF, materials are properties of volumes, not just their external surfaces. When working on 3MF you'll need to find out what approach does it have.
  • Did you study the read/write code of all those formats? It's useful to see how we do it. You'll find that when writing AMF files, we include some important information. Find it and add it to the document ;)
@alranel

This comment has been minimized.

Member

alranel commented May 10, 2017

  • Also, for each format you should clarify how we map its native entities (volumes, constellations etc.) to our Model, ModelObject, ModelVolume, ModelInstance objects.
@alranel

This comment has been minimized.

Member

alranel commented May 10, 2017

To answer your question, the materials variable in OBJ::read() is declared because the tinyobj::LoadObj() function signature requires it, even if we discard the contents.

@Samir55

This comment has been minimized.

Member

Samir55 commented May 14, 2017

I have finished the Slic3r::Model documentation (Using Doxygen syntax) . Found Here in my fork Model.hpp. There are some functions I didn't quite understand what they are doing. So, These are the questions:

First Model Class

A.

What is the difference between TriangleMesh mesh() and TriangleMesh raw_mesh() ?

B.

What are these functions do?
 _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out)
arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL)
duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
looks_like_multipart_object()
convert_multipart_object()

Second ModelMaterial Class

C.

What does DynamicPrintConfig object carry ?

Third ModelObject Class

D.

What is the difference between Printable and modifier volumes ?
What is the raw_bounding_box?
In scale_to_fit function , Which size is it ? is it of the viewport or the 3D canvas or what?

Forth ModelInstance Class

E.

Why the object may need repair? What are the reasons behind calling repair on meshes?

@Samir55

This comment has been minimized.

Member

Samir55 commented May 14, 2017

I am now working on Slic3r wiki page in my fork and I'll try to finish very soon. Sorry I and my team are very busy finishing the micro-controller project so, it's taking some considerable time.

@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented May 14, 2017

Objects may need repair because STL is a terrible format. Additionally, the algorithms used tend to want manifold and valid files, so the repair() function is used as input sanitation.

@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented May 14, 2017

A) Consult Model.cpp, lines 589-612. ModelObject::mesh() performs an extra transform (if the model was rotated or translated); ModelObject::raw_mesh() just gives you the mesh as-is without any extra processing. Right there in the source.

@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented May 14, 2017

B)
looks_like_multipart_object() does what it says--checks to see if the Model it belongs to has characteristics of having multiple parts (usually multiple volumes, etc).

convert_multipart_object() takes all of the volumes in the Model and combines them into one.

duplicate_objects_grid() replicates a single object and arranges them on a grid. It's only used by Simple.pm, which is used only in CLI mode.

The two arrangement functions attempt to move model instances around.

It's often helpful to employ a text search tool like grep to find the usages of functions in the code.

@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented May 14, 2017

C)
DyanmicPrintConfig inherits from DynamicConfig. Unlike a StaticConfig, the options map is mutable.
It can carry anything you give it. See Config.hpp:671-673 (grep to the rescue, determined through the existing comments and looking at what DyanmicPrintConfig inherits from).
grep -r DynamicPrintConfig . | grep -v -e "^Binary" -e "\.o:" -e "_build" -e "\/t\/" -e "tiny_obj_loader" -e "XS.c" for usage in the Slic3r codebase.

D)
Printable and modifier volumes are both volumes, one just has a flag on it to indicate to the slicer that it represents a different PrintRegion.

raw_bounding_box() is the same as instance_bounding_box(this->instances.size()-1), at least from my examination of the source (614-636). It is probably a candidate for refactoring, at least to call instance_bounding_box() with the calculated reference to this->instances.front().

scale_to_fit is whatever size you pass to it. grep is your friend here (or Understand, or any other number of source navigation tools).

@bubnikv

This comment has been minimized.

Contributor

bubnikv commented May 16, 2017

The 3MF format uses a ZIP format as a container. For reading / writing the ZIP files you may consider bundling the ZIP reader / writer by Disney, which I believe is self contained.

https://github.com/wdas/partio/blob/master/src/lib/io/ZIP.h
https://github.com/wdas/partio/blob/master/src/lib/io/ZIP.cpp

We would have to ask @lordofhyphens to check the FSF policies though as it is critically important to include Richard Stallman approved stuff only.

Another possibility is to use the wxZipInputStream / wxZipOutputStream classes from the wxWidgets, which I have used in the Prusa3d fork of Slic3r to import the Prusa Control platter files, which are just bundles of STLs with a tiny XML describing the transformation matrices.

@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented May 16, 2017

@bubnikv Slic3r is GPL3-licensed. We work within the rules around here, as @alexrj also prefers.

@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented May 16, 2017

@Samir55

This comment has been minimized.

Member

Samir55 commented May 16, 2017

I've finished the Slic3r supported format wiki document in my fork. tell me if there are any enhancements should be done.
@alexrj Can I create the page in Slic3r wiki now?

@bubnikv Thanks for your help, I am also thinking about using minizip and in this thread there is a very simple example about reading a zip file.

@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented May 16, 2017

@Samir55 Go ahead and make changes as you see fit. If @alexrj or I feel a page needs to move, we'll move it ;)

@alranel

This comment has been minimized.

Member

alranel commented May 16, 2017

The Disney ZIP library uses the 3-clause BSD license, which is compatible with our GPL license (as open source software developers, we care about licenses). However, that library looks unmaintained (see this bug report) so it doesn't look reliable.
That minizip one looks fine. Here's a comprehensive list.

@alranel

This comment has been minimized.

Member

alranel commented May 16, 2017

Next steps:

  • Open a Pull Request for your Doxygen comments so that we can start a review on them (Pull Requests allow to add discussion to individual lines)
  • Now that you know the Model data structure and the other formats, study the 3MF format. What does it support? What things should we support? Can we use it to store configuration like we do for AMF?
    • (Note that I haven't studied it, so I'll learn from your report)
@Samir55

This comment has been minimized.

Member

Samir55 commented May 17, 2017

This Week Task (From May 17):

  • Study 3mf Specifications
  • See how to include minizip library.
  • Improve the proposal.
@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 6, 2017

Dev Log(5 - July):

  • Read object mesh.
  • Some Refactoring in TMFParserContext and TMF::Editor
  • Read Objects (Objects with meshes, and objects containing other objects -components- )
    Commits : Commit 1
    Commit 2
    Commit 3
    I have some questions, I will write them briefly when I wake up 😪 🛌
@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 6, 2017

What's left in TMF::read()

  • Read Materials extension.
  • Read Slic3r custom config in slic3r:metadata element.
  • Decompose the 3d affine transformation and apply scale, rotation, and translation to the model instances.

Today, I will be working on decomposing the affine 3d transformation matrix.

Materials Problem

The problem in model materials is:

  • There exists many different groups and they may exist multiples of each group in the 3d model file.
  • The current ModelMaterialsMap.
std::map<string, ModelMaterial*> // (material_id, material pointer) 

The current implementation

  • Read all 3mf materials (base materials, color groups, etc) in a new structure.
/// In Model class.
++ std::vector<std::pair<int, ModelMaterialMap>> material_groups;
++    ///< It's a vector carrying pairs each has the material group type and its materials.
  • This implementation has a problem since there we cannot add this material to a ModelVolume without messing with the current code.
  • I know that currently Slic3r doesn't do anything with materials.

Possible Solutions

  1. Ignore the current implementation above, Add all read materials to the original ModelMaterialsMap materials but alter the material_id to carry the following information:
``` "3mf: group_type group_id  material_index_in_that_group" ```

then we will be able to use that to read and write materials without much pain but this will need handling that change when writing AMF.

  1. Continue using the current implementation but beside adding the material to it's material_gorup add it to the original ModelMaterials map (materials) and when we exporting to AMF we can ignore those material groups and accept base materials basematerials are like materials in amf.

  2. Ignore the current implementation and read only basematerials and ignore the material extension groups, But this may in the future cause crash because of integer overflow in case of large number of base materials

@alexrj @lordofhyphens what's the best way to handle this?

Current output.

(I haven't yet applied the transformation to the model instances).

  • I have read a 3mf model sample and exported it into .obj.
    thumbnail
    Here is the output

screen shot 2017-07-06 at 3 20 35 pm

* Also, Here I read the same 3mf file in Slic3r.

a

@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 9, 2017

Dev Log( 8 - July):

  • Read scale and translations values from the transformation affine matrix found in the component tag. Regarding the rotation I will do as follows:
    • Convert the rotation matrix -> Quaternions -> Euler angles and call ModelObject:;rotate(Euler angle, axis).
      Commit

Dev Log( 6- July):

  • Read Build items (ModelInstances) without applying transformations yet.
    Commit
@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 10, 2017

Dev Log( 9 - July):

  • Get the rotation Euler angles from the transformation matrix.

Commits: Commit 1, Commit 2
I only did as the code found in this link "ETdoFresh answer" @lordofhyphens Is this method correct?
It's as follows:

  • Get the scale vector by getting the magnitude of each base column.
  • Get the rotation matrix by dividing each column with its scale factor (x or y or z factors).
  • Get the quaternion values (w, x, y, z)
  • Normalize the quaternion values.
  • Get the 3 Euler angles which represent: x, y & z rotations
@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 12, 2017

Dev Log( 11 - July):

  • Read Slic3r custom configs for ModelObjects and ModelVolumes.
  • Some Refactoring.

Dev Log( 10- July):

  • Fix empty metadata as std::maps are not copied by default.
@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 13, 2017

Dev Log (12 - July):

  • Fix in reading 3mf format where wrong objects are deleted from the model. This was a very stupid mistake and It took some considerable time to discover it :). Commit 1
  • Fix in 3MF read: When reading component tags.
    When adding a component to the current object we first copy the object referred by objectid in a component tag, then apply a transformation to the copied object
    then get the mesh of that object and finally delete this object copy and add that mesh to the current object. Commit 2
  • Write model metadata in descending order to make the title tag appear first. This is an enhancement. Commit 3
  • Refactoring: Remove all assertions in 3MF read and replace it with TMFParserContext::stop() as if assertion fails the extracted 3dmodel.model file won't be deleted. Commit 4
@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 13, 2017

@alexrj @lordofhyphens I have a question regarding reading the item tag (which corresponds to creating the model instance of an object).
I extracted the following parameters from the matrix in the item tag:

  • Rotation angle around z axis.
  • Rotation angle around x axis.
  • Rotation angle around y axis.
  • Scale (sx,sy,sz).
  • translation in x, y,z

While what is found in ModelInstance:

  • Rotation angle around z axis. (No rotation in x & y)
  • Scale factor value (Not a vector)
  • translation in x, y (No translation in z)

3MF specification requires (is a must) applying the matrix.
Do you recommend me to add those parameters to the ModelInstance or what's the case?

@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 13, 2017

For reading the transformation matrix in component tag (An object is a part of a bigger object) desn't have the previous problem as I create a copy of the component object apply rotation, translation and scale to the copied object then get the mesh and add it to volumes of the big object.

                    // Create a copy of the current object.
                    ModelObject* object_copy = m_model.add_object(*component_object, true);

                    apply_transformation(object_copy, transformations);

                    // Get the mesh of this object.
                    component_mesh = object_copy->mesh();

                    // Delete the copy of the object.
                    m_model.delete_object(m_model.objects.size() - 1);
void
TMFParserContext::apply_transformation(ModelObject *object, std::vector<double> &transformations)
{
    // Apply scale.
    Pointf3 vec(transformations[3], transformations[4], transformations[5]);
    object->scale(vec);

    // Apply x, y & z rotation.
    object->rotate(transformations[6], X);
    object->rotate(transformations[7], Y);
    object->rotate(transformations[8], Z);

    // Apply translation.
    object->translate(transformations[0], transformations[1], transformations[2]);
    return;
}
@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 18, 2017

Dev Log(17- July):

  • Read color groups.
  • Some refactoring.
    Commits: 1, 2

Dev Log(16- July):

  • Read basematerials in the original model materials map.
  • Fix fatal error in adding volume when reading 3MF model file.
    Commits: 1, 2
@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 18, 2017

Some Screenshots:

Those are 3mf models found in this repository.
screenshot from 2017-07-18 04 16 17

screenshot from 2017-07-18 04 21 22

@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 25, 2017

Dev Log( 25- July):

What is remaining in 3MF read/ write:

  • Complete support for the 3MF material extension. Currently, the code supports base materials and color groups.
  • Fix (slic3r:volume) seg. fault during reading.
  • Testing TMF::read() and TMF::write().
  • Implement my own Zip lib based on miniz.
@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 25, 2017

@alexrj @lordofhyphens I need some overall code review. If there is something that needs modifications or improvements tell me.

@Samir55

This comment has been minimized.

Member

Samir55 commented Jul 30, 2017

List of what to be tested in 3MF (Read/ Write):

  • Model metadata and their Slic3r custom configs.
  • Model materials and their Slic3r custom configs.
  • Model Object, meshes, and their Slic3r configs.
  • Model Instances and the transformation matrices.

I will read a 3mf file and write it, then compare the output with the expected one.

@Samir55

This comment has been minimized.

Member

Samir55 commented Aug 1, 2017

@Samir55

This comment has been minimized.

Member

Samir55 commented Aug 1, 2017

Dev Log(31 - July):

@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented Aug 1, 2017

@Samir55 I left my comments on the pull request (which is probably the easiest way, I find, to leave a code review on github).

@Samir55

This comment has been minimized.

Member

Samir55 commented Aug 5, 2017

@alexrj @lordofhyphens Why those 2 STL models give segmentation fault when adding I try to read them in Slic3r GUI.

This is the message :

ahmedsamir@ahmedsamir:~/Slic3r$ perl slic3r.pl
Parentheses missing around "my" list at /home/ahmedsamir/Slic3r/lib/Slic3r/GUI/Plater.pm line 1874.

(slic3r.pl:24443): Gtk-CRITICAL **: IA__gtk_window_resize: assertion 'width > 0' failed
Segmentation fault (core dumped)

If I converted them using CLI into OBJ it worked. However if they are converted to 3mf, Slic3r gives segmentation fault.

ahmedsamir@ahmedsamir:~/Slic3r/build$ ./slic3r Spider_ascii.stl --export-obj
File exported to Spider_ascii.stl.obj
ahmedsamir@ahmedsamir:~/Slic3r/build$ ./slic3r Spider_binary.stl --export-obj
File exported to Spider_binary.stl.obj
ahmedsamir@ahmedsamir:~/Slic3r/build$ ./slic3r Spider_binary.stl --export-3mf
Segmentation fault (core dumped)
ahmedsamir@ahmedsamir:~/Slic3r/build$ ./slic3r Spider_ascii.stl --export-3mf
Segmentation fault (core dumped)

I tried to trace where this segmentation fault occurs, It is in TriangleMesh::repair() function.

What's wrong with those 2 files?

@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented Aug 5, 2017

Offhand, I'd say it's because STL is a terrible format ;)
See if it still segfaults when exporting to AMF.

@Samir55

This comment has been minimized.

Member

Samir55 commented Aug 10, 2017

Dev Log(7 - August):

  • Include X&Y rotation angles, scaling vector in the transformation matrix in item tag.

Dev Log(9 - August):

  • Add 3MF file extension to GUI.

@alexrj @lordofhyphens Sorry, I have not been working full time this week, I have been busy (changing our home wall paint).
I will finish what is left very soon, Also these issues I want to afterwards work on them (May be extended after GSoC period):

  • Improve auto-arrange algorithm with SVGNest. ( I know it would take some time :) )
  • Undo to: rotate, flip, split, cut.
@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented Aug 10, 2017

You're welcome to have at those issues ;)

@Samir55

This comment has been minimized.

Member

Samir55 commented Aug 16, 2017

Dev Log (11 -> 15 August):

3MF read/ write:


  • Added some enhancements and a test.

Undo to rotate, flip, split, cut, etc


Still working on it. Discussed in issue #3265
Bracnh : gsoc-undo_redo

  • Add Basic UndoableOperation class in Plater.pm.
  • Add Undo/ Redo shortcuts.
  • Add Undo/Redo to rotation.
@Samir55

This comment has been minimized.

Member

Samir55 commented Aug 21, 2017

@alexrj @lordofhyphens I have implemented my own zip wrapper. I will push those commits soon :), I am also working on adding undo/redo. Regarding the final evaluation and submitting my work, I will submit the link of the 3mf pull request. Does that seem fine?

@lordofhyphens

This comment has been minimized.

Member

lordofhyphens commented Aug 21, 2017

@alranel

This comment has been minimized.

Member

alranel commented Sep 4, 2017

The Pull Request was merged. Success! Congrats to @Samir55 for the great job :)

@lordofhyphens lordofhyphens modified the milestones: GSOC 2017, 1.3.0 Sep 29, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment