Faster I/O for OBJ, PLY, STL: Design #68936
Open
opened 2019-08-20 22:48:35 +02:00 by Dalai Felinto
·
25 comments
No Branch/Tag Specified
main
blender-v3.6-release
temp-sculpt-dyntopo
temp-sculpt-dyntopo-hive-alloc
asset-shelf
brush-assets-project
blender-v3.3-release
tmp-usd-python-mtl
asset-browser-frontend-split
node-group-operators
blender-v2.93-release
universal-scene-description
temp-sculpt-attr-api
blender-v3.5-release
realtime-clock
sculpt-dev
gpencil-next
bevelv2
microfacet_hair
blender-projects-basics
xr-dev
principled-v2
v3.3.7
v2.93.18
v3.5.1
v3.3.6
v2.93.17
v3.5.0
v2.93.16
v3.3.5
v3.3.4
v2.93.15
v2.93.14
v3.3.3
v2.93.13
v2.93.12
v3.4.1
v3.3.2
v3.4.0
v3.3.1
v2.93.11
v3.3.0
v3.2.2
v2.93.10
v3.2.1
v3.2.0
v2.83.20
v2.93.9
v3.1.2
v3.1.1
v3.1.0
v2.83.19
v2.93.8
v3.0.1
v2.93.7
v3.0.0
v2.93.6
v2.93.5
v2.83.18
v2.93.4
v2.93.3
v2.83.17
v2.93.2
v2.93.1
v2.83.16
v2.93.0
v2.83.15
v2.83.14
v2.83.13
v2.92.0
v2.83.12
v2.91.2
v2.83.10
v2.91.0
v2.83.9
v2.83.8
v2.83.7
v2.90.1
v2.83.6.1
v2.83.6
v2.90.0
v2.83.5
v2.83.4
v2.83.3
v2.83.2
v2.83.1
v2.83
v2.82a
v2.82
v2.81a
v2.81
v2.80
v2.80-rc3
v2.80-rc2
v2.80-rc1
v2.79b
v2.79a
v2.79
v2.79-rc2
v2.79-rc1
v2.78c
v2.78b
v2.78a
v2.78
v2.78-rc2
v2.78-rc1
v2.77a
v2.77
v2.77-rc2
v2.77-rc1
v2.76b
v2.76a
v2.76
v2.76-rc3
v2.76-rc2
v2.76-rc1
v2.75a
v2.75
v2.75-rc2
v2.75-rc1
v2.74
v2.74-rc4
v2.74-rc3
v2.74-rc2
v2.74-rc1
v2.73a
v2.73
v2.73-rc1
v2.72b
2.72b
v2.72a
v2.72
v2.72-rc1
v2.71
v2.71-rc2
v2.71-rc1
v2.70a
v2.70
v2.70-rc2
v2.70-rc
v2.69
v2.68a
v2.68
v2.67b
v2.67a
v2.67
v2.66a
v2.66
v2.65a
v2.65
v2.64a
v2.64
v2.63a
v2.63
v2.61
v2.60a
v2.60
v2.59
v2.58a
v2.58
v2.57b
v2.57a
v2.57
v2.56a
v2.56
v2.55
v2.54
v2.53
v2.52
v2.51
v2.50
v2.49b
v2.49a
v2.49
v2.48a
v2.48
v2.47
v2.46
v2.45
v2.44
v2.43
v2.42a
v2.42
v2.41
v2.40
v2.37a
v2.37
v2.36
v2.35a
v2.35
v2.34
v2.33a
v2.33
v2.32
v2.31a
v2.31
v2.30
v2.28c
v2.28a
v2.28
v2.27
v2.26
v2.25
Labels
Clear labels
Issues relating to security: https://wiki.blender.org/wiki/Process/Vulnerability_Reports
Apply labels
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
Eevee & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest/Import
Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest: Wayland
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Issues relating to security: https://wiki.blender.org/wiki/Process/Vulnerability_Reports
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
Eevee & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
Eevee & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest/Import
Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest: Wayland
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
Eevee & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
Milestone
Set milestone
Clear milestone
No items
No Milestone
Projects
Set Project
Clear projects
No project
Assignees
Assign users
Clear assignees
No Assignees
15 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.
No due date set.
Dependencies
No dependencies set.
Reference: blender/blender#68936
Reference in New Issue
There is no content yet.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may exist for a short time before cleaning up, in most cases it CANNOT be undone. Continue?
This task is to track the progress made on the Fast IO project & also for design discussions.
Student: @ankitm
Mentors: @dr.sybren @howardt
Exporter's Design: #68936#962546
Importer's Design: #68936#982751
Status Tracker:
Evaluation 1: I hope to reach halfway in the OBJ importer by evaluation 1.
OBJ Importer ( Nearly the same as above ):
Vertex, vertex normals, faces, texture coordinates
Material Library
Curves,
Modifiers. Grouping,
Experiment with IO methods to see which one works the fastest
Refactor
Profile, Benchmark, Document
To be written in detail later:
Evaluation 2
Evaluation 3
Added subscriber: @dfelinto
Added subscriber: @Jaydead
Added subscriber: @softyoda
Added subscriber: @howardt
Faster I/Oto Faster I/O for OBJ, PLY, STLAdded subscribers: @ankitm, @dr.sybren
Added subscriber: @dark999
Faster I/O for OBJ, PLY, STLto Faster I/O for OBJ, PLY, STL: DesignRemoved subscriber: @dfelinto
Added subscriber: @AlexeyPerminov
Added subscriber: @ckohl_art
An indicator of how data structures & writers will scale up was committed today: 485cc4330a
In
obj_exporter.cc
, the structOBJ_data_to_export
is there, which contains all what needs to be written & nothing else. It is filled up in the same file itself &obj_file_handler.cc
flushes it out.reserve
would save some time there, instead ofpush_back
.With some efforts step 2 & 3 can be made to work together (non-sequentially). Any input on that ?
Added subscriber: @name
https://docs.google.com/document/d/1sYSHF8g63F7zwTjJkoACESZvpQRhImlEPifilmqXaWU/
OBJ Exporter Design Document
The exporter’s working
The exporter tries to separate the file writer and the calculation of numbers/names as much as possible. The Writer contains the syntax, the conditionals which decide whether an element/ property should be written or not.
OBJMesh
,OBJCurve
which are wrappers aroundMesh
orCurve
which give the required data to writers.If multiple frame export is specified, only then the filename is edited to add the frame to the filename. The current frame is exported by default. From the
ViewLayer
, objects are filtered out based on export settings, andObject
type. OnlyOB_MESH
andOB_CURVE
are supported.An
OBJWriter
is instantiated which writes object geometry sequentially to the OBJ file. In most cases,OBJWriter
queries the item for the total number of elements and provides a buffer that is filled by the iterand’s methods, without being concerned about how the writer uses it. Exceptions to this are smooth shading groups and UV indices.OBJ indices which are one-based are handled by the writer, not by the
OBJCurve
/Mesh
.An .MTL file is also created in the beginning and every
Object
’s material is appended to the file when the corresponding OBJMesh is the iterand. IOW, the MTL file is opened and closed as many times are there are objects in the scene.MTLWriter
usesMaterialWrap
class for getting material data.After this all the
Curve
s which should be exported in parametric form (export settings), are written. Their vertex coordinates, degree, parameters need no dynamic allocations. The calculations are done on the fly.OBJCurve
class is used for this.Data Structures
OBJWriter
: It manages theFILE*
, output file.index_offsets_
: all objects index into a flat list of vertices or normals. So these offsets keep track of how many vertices/ normals have been written already.write_vertex_coords
,write_nurbs_curve
,update_index_offsets
etc.OBJMesh
Contains non-owning pointers toObject
,Mesh
, axes transform as specified by the export settings, and two lists which have to be stored since they’re needed at different times by the writer. They are: smooth groups and UV vertex indices. The methods of this class give vertex (UV) coordinates, edge indices, vertex normals, smooth group indices, polygon indices, object names, material names etc., to the caller Due to the aforementioned dynamically allocated lists, theOBJMesh
objects are freed right after they’re written to the file, instead of waiting for the default destructor ofblender::Vector
to free the memory.OBJCurve
is just likeOBJMesh
and has methods for vertex coordinates, curve degree, number of points, etc.MTLMaterial
:It stores
map_Kd, map_Ks, map_Ke, map_d, map_refl, map_Ns, map_Bump
. All of them have three parameters:To avoid code duplication, a generic struct
tex_map_Kx
is used which has the three items listed above in addition todest_node_id
which contains the socket ID to which this texture map should be connected.map_Bump_strength
an extra property for map_Bump (“Normal Map Strength”) in addition to the three listed above.MaterialWrap
is used for extracting material data by traversingObject
’s shader nodetree. It fills up data inMTLMaterial
containers (that are also used in the importer). For fast lookups of linked nodes, linked sockets, it usesnodes::NodetreeRef
. If nodetree is not present, values like ambient color, diffuse color, alpha, etc., are taken fromMaterial
of the object. For images, this node structure is expected ideally.Mapping (location and scale) → Image Texture (filepath) → Normal bump (optional) (bump strength) → p-BSDF (colors, alpha, metallic etc) → Material output (optional).
Notes
While the export process is fairly straightforward, I’ll note some things.
New meshes are created in the following cases
Smooth groups are calculated from sharp edges only if specified. Smooth flag is written in every combination of export parameters if a polygon is smooth shaded.
Normals: if a face is shaded smooth, only then its vertex normals are exported. Otherwise, only face normals are written. If smooth groups are enabled, this still remains the same. What changes is the smooth group which becomes another number instead of the default “1”. So
So for e.g., Smooth shading disabled,
Smooth shading enabled
The way normals and normal indices (in face elements) are written require that polygons are iterated in the same order. Thus sorting them and trying to for e.g., separate smooth shaded and non-smooth shaded polygons to save a few lines is not possible (without allocating more memory).
The same is true for UV vertex indices (
Vector<Vector<uint>>
) because this structure depends on the polygon index. So unless the original polygon index is stored, one shouldn’t reorder polygons and access UV indices using the new polygon indices.The same is not true for vertices since
MLoop->v
remains correct even if vertices are written by say looping overMVert
backward!Vertex deform groups: Suppose a cube has all four vertices of only a face assigned a deform group. Since its adjacent four faces also share at least one vertex (two vertices are shared, in fact), they also get assigned the same group. Only the opposite face which shares no vertex with the original face has no deform group assigned. IOW, the group which has the most number of vertices of a polygon is the group we write to the file.
To denote the absence of any group, we take the same route as smooth groups: “g off”. Other writers may write “g default” or “g (null)” also.
Only loose edges are written to the file. This is checked by the
ME_LOOSEEDGE
flag.How do we add STL, PLY in this ?
Later on,
OBJMesh
can have a parent class which has the methods for the least common denominator all formats need. Vertex coords, object name are examples. Then individual formats will have a derived class that has format specific calculator functions: UV coords in OBJ, edge list in PLY.Similarly,
OBJWriter
can have a superclass for opening files, modifying filenames according to file format and frame number etc. And format specific derived classes which have the required syntax.https://docs.google.com/document/d/17Uzl47OljjoKgaMbukiLHUVGQP220lPTmPS-atb65mw/
OBJ Importer design document
The importer’s working:
After receiving the import parameters from the operator, a parser is instantiated to read the whole file line by line and store the data in containers that we call “Geometry”. They are suited to storing the data in the OBJ format and are not Blender
Objects
.Mesh
type and NURBS type Geometry is supported.After storing the geometry, the material library used by the file is read and all the material definitions are stored in
MTLMaterial
containers. Since the order of materials may not match the objects in the OBJ file, ablender::Map
is used for faster lookups, with keys being the material name.From the
Geometry
andMTLMaterial
, BlenderMesh
orCurve
Object
s are created. The materials are added to the created `Object. All the objects are added to a single import collection.Total import time is printed in the console.
Data Structures:
Geometry
stores the geometry data of an individual item, but so far hasn't been converted into a Blender object. It stores:MEdge
list to store edges that do not belong to a polygon.FaceElem
): this struct stores one face’s smooth shading boolean flag, deform (vertex) group name that this face belongs to, and a list ofFaceCorners
. AFaceCorner
contains one vertex’s vertex index & UV vertex index.NurbsElem
: It keeps data of one NURBS spline: vertex indices that index into the global list of coordinates, parm values, & group name to which this curve belongs to (it can also serve as an object name).GlobalVertices
:Originally, the vertex coordinates were stored in a
Geometry
instance and thus they were not accessible to the other instances which can be problematic when an object in the OBJ file is initialized after the list of vertices is written. So this struct stores all vertex coordinates and UV vertex coordinates and is available to allGeometry
instances. An instance stores its vertex coordinates using indices indexing into this global list. If in the future, normals need to be added here, they can be done.OBJParser
Geometry
containers. This class is a friend ofGeometry
since it edits the whole struct itself. So setters and getters would add code complexity.VertexIndexOffset
:This class also has a number that stores how many vertices belong to other objects so that the faces in upcoming objects can refer to their vertices locally, not in the global list. The correction is to bring a number from global list to an Object's local vertex list for two items:
MLoop.v
andMEdge.v1/2
.MTLParser
:Same as
OBJParser
, but for an MTL file. The MTL filename is acquired from the OBJ file so parsing the OBJ file first is necessary. It stores the materials inMTLMaterial
containers in ablender::Map
The central caller to parser and object converters.
obj_exporter.cc
The code here receives all the import settings from
IO_obj.c
and initializesblender::Map
orblender::Vector
for storingMTLMaterial
andGeometry
.GlobalVertices
that has three vectors for vertex coordinates, UV vertex coordinates & normals (this may be removed)File Parsers
The parser doesn't store the whole OBJ/ MTL file in memory in the beginning. It reads it line by line and stores the incoming data to appropriate fields of
Geometry
,index_offset
&MTLMaterial
.index_offset
is used to store how many vertices belong to previousGeometry
instances. This helps loop vertex indices that index into a Mesh object's own vertex indices, ranging from 0 to (total vertices - 1).For eg:
There are some utility functions here:
split_line_key_rest
: to separate the line identifier of a line in OBJ/ MTL files from the data in the rest of the line. Examples of such identifiers arev, vn, vt, usemtl, #,
etc.split_by_char
to break down a string into smaller ones, delimited by a character. Useful for space-separated and/
separated strings. This was a nice time gain as compared to the “>>” operator which is convenient but slow. Possible optimization: Avoid string allocations and use references in this splitting function if possible. The parser takes over 70% of the import time, & this function is the biggest time sink.copy_string_to_float
andcopy_string_to_int
that convert a string ( orSpan
of strings) to a numeric type usingstd::stoi
/std::stof
and catch exceptions if the string is not a number. A fallback value is expected from the caller so that bad data can be handled by the caller after the conversion.After the parser is done reading, the file is closed,
index_offset
is gone, & we have all theGeometry
andMTLMaterial
instances ready to be converted to Blender objects.Mesh and Curves Creation
MeshFromGeometry
/CurveFromGeometry
make anOB_MESH
/OB_CURVE
typeObject
. It relies on the parser to get correct indices. The method is straightforward: allocate an appropriately sized mesh and change its vertex coordinates, edge's vertex indices, loops, and polygons. Also, set smooth shading flags if required and call normal calculation methods. Also, addMaterial
and its nodetree to theObject
here. Its members:UniqueObjectPtr
:std::unique_ptr
to anObject
with a custom deleter to free theObject
with Blender’s deallocator.mover()
that returnsstd::move(<the object>)
so that it can be added to collections later on & thus ownership is transferred.Possible optimization: remove obsolete Geometry instances after a Mesh block has been created from them to reduce memory pressure.
Material creation:
Using the
MTLMaterial
filled by the parser, BlenderMaterial
is created and a Node tree with only Principled-BSDF node, texture, vector and normal map nodes, is added to the Material.For this a class
ShaderNodetreeWrap
is used which receives aMTLMaterial
reference and offers abNodeTree
that must to be transferred via a public function:bNodeTree *get_nodetree
. This class is responsible for creating nodes, setting socket values, linking nodes, positioning nodes & loading Image for texture nodes.Adding objects to collections
OBJImportCollection
::OBJImportCollection()
makes a new collection to put all theObjects
in.OBJImportCollection
::add_object_to_collection
public method: the newly createdObject
is added to the collection made above.The list of
Geometry
,MTLMaterial
, and global vertices, etc is freed now, and the total time taken is printed.Added subscriber: @JacquesLucke
Added subscriber: @mattli911
Are there any plans to improve FBX I/O or mainly OBJ/other formats? Does Blender want to steer away from FBX, or keep supporting it?
I'm eagerly waiting on these I/O Improvements, since it can be quite painfully slow when importing Zbrush type meshes/etc, or exporting collapsed highpoly geo to another DCC/Marmoset Toolbag.
I'm not sure why, but importing an FBX into blender seems to take 2-4x longer than most any other DCC I've used I feel like. I can be waiting 5/10+ Minutes sometimes to import a highres mesh/file. Where in other DCCs it might take 30 seconds or 1-2 minutes.
@mattli911 AFAIK it's not planned to rewrite FBX I/O in C++ in the near future. If someone works on it, addressing the large number of bugs would be priority & later on trying to improve the speed in python itself.
Ah , ok. I don't HAVE to use FBX either i guess. I wonder if USD/any other formats will give a much better speed boost?
You should definitely give USD, Alembic & Collada a try as per your needs.
@mattli911 This is not a forum, and not the place for asking general questions about future plans for Blender. DevTalk is better for that.
Removed subscriber: @mattli911
I think this task should also be added to BF Blender (3.0) task : https://developer.blender.org/D6616 as it is related.
Added subscriber: @1029910278
Added subscriber: @MichaelAProstka
Added subscriber: @kmcurry