Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenVolumeMesh
OpenVolumeMesh
Commits
d41f8c2a
Commit
d41f8c2a
authored
Sep 17, 2021
by
Martin Heistermann
Browse files
IO: improved .ovmb v1 with kaiten struct file format specification.
parent
296c435e
Changes
17
Hide whitespace changes
Inline
Side-by-side
extra/ovmb-kaitai/.gitignore
0 → 100644
View file @
d41f8c2a
*.dot
*.pdf
extra/ovmb-kaitai/ovmb.ksy
0 → 100644
View file @
d41f8c2a
meta:
id: ovmb
title: OVMB (OpenVolumeMesh binary file format)
file-extension:
- ovmb
license: CC0-1.0
encoding: ASCII
endian: le
doc:
OVMB files describe volumetric meshes and associated properties.
doc-ref: http://openvolumemesh.org
seq:
- id: file_header
type: file_header
- id: chunks
type: chunk
repeat: eos
enums:
int_encoding:
0: none
1: u8
2: u16
4: u32
vertex_encoding:
0: none
1: float
2: double
property_entity:
0: vertex
1: edge
2: face
3: cell
4: halfedge
5: halfface
6: mesh
topo_type:
0: polyhedral
1: tetrahedral
2: hexahedral
topo_entity:
1: edge
2: face
3: cell
types:
file_header:
seq:
- id: magic
contents: ['O', 'V', 'M', 'B', 0xa, 0xd, 0xa, 0xff]
- id: file_version
type: u1
- id: header_version
type: u1
- id: vertex_dim
type: u1
- id: topo_type
type: u1
enum: topo_type
- id: reserved
contents: [0, 0, 0, 0]
- id: n_vertices
type: u8
- id: n_edges
type: u8
- id: n_faces
type: u8
- id: n_cells
type: u8
chunk:
seq:
- id: type
type: str
size: 4
- id: version
type: u1
- id: padding_bytes
type: u1
- id: compression
type: u1
- id: flags
type: u1
- id: file_length
type: u8
- id: body
size: file_length - padding_bytes
type:
switch-on: type
cases:
'"VERT"': vert_chunk
'"PROP"': prop_chunk
'"TOPO"': topo_chunk
'"DIRP"': propdir_chunk
- id: padding
size: padding_bytes
array_span:
seq:
- id: base
type: u8
- id: count
type: u4
vert_chunk:
seq:
- id: span
type: array_span
- id: vertex_encoding
type: u1
enum: vertex_encoding
- id: reserved
contents: [0, 0, 0]
- id: coordinates
repeat: expr
repeat-expr: span.count * _root.file_header.vertex_dim
type:
switch-on: vertex_encoding
cases:
'vertex_encoding::float': f4
'vertex_encoding::double': f8
topo_chunk:
seq:
- id: span
type: array_span
- id: entity
type: u1
enum: topo_entity
- id: valence
type: u1
doc: 0 for variable valence
- id: valence_encoding
type: u1
enum: int_encoding
- id: handle_encoding
type: u1
enum: int_encoding
- id: handle_offset
type: u8
- id: data
type:
switch-on: valence
cases:
0: topo_data_variable
_: topo_data_fixed
topo_data_fixed:
seq:
- id: handles
repeat: expr
repeat-expr: _parent.span.count * _parent.valence
type:
switch-on: _parent.handle_encoding
cases:
'int_encoding::u8': u1
'int_encoding::u16': u2
'int_encoding::u32': u4
topo_data_variable:
seq:
- id: valences
repeat: expr
repeat-expr: _parent.span.count
type:
switch-on: _parent.valence_encoding
cases:
'int_encoding::u8': u1
'int_encoding::u16': u2
'int_encoding::u32': u4
- id: handles
type:
switch-on: _parent.handle_encoding
cases:
'int_encoding::u8': u1
'int_encoding::u16': u2
'int_encoding::u32': u4
prop_chunk:
seq:
- id: span
type: array_span
- id: prop_idx
type: u4
propdir_chunk:
seq:
- id: entry
type: propdir_entry
repeat: eos
string4:
seq:
- id: len
type: u4
- id: data
type: str
encoding: UTF-8
size: len
bytes4:
seq:
- id: len
type: u4
- id: data
size: len
propdir_entry:
seq:
- id: entity
type: u1
enum: property_entity
- id: name
type: string4
- id: data_type_name
type: string4
- id: serialized_default
type: bytes4
src/OpenVolumeMesh/IO/detail/BinaryFileReader.cc
View file @
d41f8c2a
...
...
@@ -10,6 +10,7 @@
#include
<cassert>
#include
<numeric>
#include
<iostream>
#include
<string>
namespace
OpenVolumeMesh
{
class
TetrahedralMeshTopologyKernel
;
...
...
@@ -18,175 +19,172 @@ class HexahedralMeshTopologyKernel;
namespace
OpenVolumeMesh
::
IO
::
detail
{
template
<
typename
HandleT
,
typename
ReadFunc
,
typename
AddFunc
>
void
BinaryFileReader
::
readFacesOrCells
(
Decoder
&
reader
,
TopoChunkHeader
const
&
header
,
uint8_t
fixed_valence
,
IntEncoding
valence_enc
,
uint64_t
n
,
ReadFunc
read_handle
,
AddFunc
add_entity
)
{
std
::
vector
<
HandleT
>
handles
;
if
(
header
.
span
.
count
==
0
)
{
state_
=
ReadState
::
ErrorEmptyList
;
return
;
}
auto
add_all
=
[
&
](
auto
get_valence
)
{
for
(
uint64_t
i
=
0
;
i
<
header
.
span
.
count
;
++
i
)
{
auto
val
=
get_valence
(
i
);
handles
.
resize
(
val
);
for
(
uint64_t
h
=
0
;
h
<
val
;
++
h
)
{
size_t
idx
=
read_handle
(
reader
)
+
header
.
span
.
base
;
if
(
idx
<
n
)
{
handles
[
h
]
=
HandleT
::
from_unsigned
(
idx
);
}
else
{
state_
=
ReadState
::
ErrorHandleRange
;
return
;
}
}
add_entity
(
handles
);
}
};
auto
esize
=
elem_size
(
header
.
enc
);
if
(
fixed_valence
==
0
)
{
auto
valences
=
read_valences
(
reader
,
valence_enc
,
header
.
span
.
count
);
auto
max_valence
=
*
std
::
max_element
(
valences
.
begin
(),
valences
.
end
());
auto
total_handles
=
std
::
accumulate
(
valences
.
begin
(),
valences
.
end
(),
0ULL
);
if
(
reader
.
remaining_bytes
()
!=
total_handles
*
esize
)
{
state_
=
ReadState
::
Error
;
return
;
}
handles
.
reserve
(
max_valence
);
add_all
([
&
](
size_t
idx
){
return
valences
[
idx
];});
}
else
{
if
(
reader
.
remaining_bytes
()
!=
header
.
span
.
count
*
fixed_valence
*
esize
)
{
state_
=
ReadState
::
Error
;
return
;
}
handles
.
reserve
(
fixed_valence
);
add_all
([
val
=
fixed_valence
](
size_t
){
return
val
;});
}
}
bool
BinaryFileReader
::
read_header
()
{
if
(
state_
==
ReadState
::
Init
)
{
//stream_.seek(0);
auto
decoder
=
stream_
.
make_decoder
(
ovmb_size
<
FileHeader
>
);
auto
ok
=
read
(
decoder
,
file_header_
);
if
(
ok
)
{
state_
=
ReadState
::
HeaderRead
;
return
true
;
}
else
{
state_
=
ReadState
::
ErrorInvalidFile
;
return
false
;
try
{
if
(
state_
==
ReadState
::
Init
)
{
//stream_.seek(0);
auto
decoder
=
stream_
.
make_decoder
(
ovmb_size
<
FileHeader
>
);
auto
ok
=
read
(
decoder
,
file_header_
);
if
(
ok
)
{
state_
=
ReadState
::
HeaderRead
;
return
true
;
}
else
{
state_
=
ReadState
::
ErrorInvalidFile
;
return
false
;
}
}
return
true
;
}
catch
(
parse_error
&
e
)
{
state_
=
ReadState
::
ErrorInvalidFile
;
error_msg_
=
std
::
string
(
"parse_error: "
)
+
e
.
what
();
return
false
;
}
return
true
;
}
bool
BinaryFileReader
::
validate_span
(
uint64_t
total
,
uint64_t
read
,
ArraySpan
const
&
span
)
{
if
(
span
.
base
!=
read
)
{
if
(
span
.
first
!=
read
)
{
state_
=
ReadState
::
Error
;
error_msg_
=
"Invalid span start, must resume where the last chunk of the same topo type left off"
;
return
false
;
}
if
(
total
-
read
<
span
.
count
)
{
state_
=
ReadState
::
Error
;
error_msg_
=
"Invalid span, end exceeds total entity count"
;
return
false
;
}
return
true
;
}
std
::
vector
<
uint32_t
>
BinaryFileReader
::
read_valences
(
Decoder
&
reader
,
IntEncoding
enc
,
size_t
count
)
template
<
typename
T
,
typename
FuncMakeT
>
bool
BinaryFileReader
::
read_n_ints
(
Decoder
&
reader
,
IntEncoding
enc
,
std
::
vector
<
T
>
&
out_vec
,
size_t
count
,
FuncMakeT
make_t
)
{
if
(
!
is_valid
(
enc
))
{
state_
=
ReadState
::
ErrorInvalidEncoding
;
return
{};
return
false
;
}
auto
needed
=
count
*
elem_size
(
enc
);
if
(
reader
.
remaining_bytes
()
<
needed
)
{
state_
=
ReadState
::
ErrorInvalidFile
;
error_msg_
=
"read_n_ints: not enough data in chunk, need "
+
std
::
to_string
(
needed
)
+
", have "
+
std
::
to_string
(
reader
.
remaining_bytes
());
return
false
;
}
std
::
vector
<
uint32_t
>
valences
;
valences
.
reserve
(
count
);
out_vec
.
clear
();
out_vec
.
reserve
(
count
);
auto
read_all
=
[
&
](
auto
read_one
)
{
if
(
reader
.
remaining_bytes
()
<
count
*
elem_size
(
enc
))
{
state_
=
ReadState
::
Error
;
return
;
}
for
(
size_t
i
=
0
;
i
<
count
;
++
i
)
{
valences
.
push_back
(
read_one
(
reader
));
out_vec
.
push_back
(
make_t
(
read_one
(
reader
))
)
;
}
};
call_with_decoder
(
enc
,
read_all
);
return
valences
;
return
true
;
}
void
BinaryFileReader
::
read
PropC
hunk
(
Decoder
&
reader
)
void
BinaryFileReader
::
read
_topo_c
hunk
(
Decoder
&
reader
)
{
Pr
opChunkHeader
header
;
T
op
o
ChunkHeader
header
;
read
(
reader
,
header
);
if
(
header
.
idx
>=
props_
.
size
())
{
state_
=
ReadState
::
Error
;
// TODO more specific error
if
(
header
.
span
.
count
==
0
)
{
state_
=
ReadState
::
ErrorEmptyList
;
error_msg_
=
"TOPO chunk contains no data"
;
return
;
}
auto
&
prop
=
props_
[
header
.
idx
];
if
(
!
prop
.
decoder
)
{
reader
.
skip
();
if
(
!
is_valid
(
header
.
handle_encoding
))
{
state_
=
ReadState
::
ErrorInvalidEncoding
;
error_msg_
=
"TOPO chunk: invalid handle encoding"
;
return
;
}
uint64_t
n
=
0
;
switch
(
prop
.
entity
)
{
case
PropertyEntity
::
Vertex
:
n
=
n_verts_read_
;
break
;
case
PropertyEntity
::
Edge
:
n
=
n_edges_read_
;
break
;
case
PropertyEntity
::
Face
:
n
=
n_faces_read_
;
break
;
case
PropertyEntity
::
Cell
:
n
=
n_cells_read_
;
break
;
case
PropertyEntity
::
HalfEdge
:
n
=
2
*
n_edges_read_
;
break
;
case
PropertyEntity
::
HalfFace
:
n
=
2
*
n_faces_read_
;
break
;
case
PropertyEntity
::
Mesh
:
n
=
1
;
break
;
if
(
!
is_valid
(
header
.
entity
))
{
state_
=
ReadState
::
ErrorInvalidFile
;
error_msg_
=
"TOPO chunk: Invalid topology entity "
+
std
::
to_string
(
static_cast
<
size_t
>
(
header
.
entity
));
return
;
}
if
(
header
.
span
.
base
>=
n
||
n
-
header
.
span
.
base
<
header
.
span
.
count
)
{
state_
=
ReadState
::
ErrorHandleRange
;
if
(
header
.
valence
!=
0
&&
header
.
valence_encoding
!=
IntEncoding
::
None
)
{
state_
=
ReadState
::
ErrorInvalidFile
;
error_msg_
=
"TOPO edge chunk: valence encoding must be None for fixed valences"
;
return
;
}
ValenceVec
valences
;
uint64_t
total_handles
=
header
.
valence
*
header
.
span
.
count
;
if
(
header
.
valence
==
0
)
{
if
(
header
.
valence_encoding
==
IntEncoding
::
None
)
{
state_
=
ReadState
::
ErrorInvalidFile
;
error_msg_
=
"TOPO edge chunk: valence encoding must not be None for variable valences"
;
return
;
}
auto
success
=
read_n_ints
(
reader
,
header
.
valence_encoding
,
valences
,
header
.
span
.
count
,
[](
uint32_t
x
){
return
x
;});
if
(
!
success
)
{
return
;
}
assert
(
header
.
span
.
count
==
valences
.
size
());
total_handles
=
std
::
accumulate
(
valences
.
begin
(),
valences
.
end
(),
0ULL
);
}
auto
handle_size
=
elem_size
(
header
.
handle_encoding
);
auto
expected_bytes
=
total_handles
*
handle_size
;
if
(
reader
.
remaining_bytes
()
!=
expected_bytes
)
{
state_
=
ReadState
::
Error
;
error_msg_
=
"TOPO chunk: number of remaining bytes incorrect, expecting "
+
std
::
to_string
(
expected_bytes
)
+
", have "
+
std
::
to_string
(
reader
.
remaining_bytes
());
return
;
}
switch
(
header
.
entity
)
{
case
OpenVolumeMesh
::
IO
::
detail
::
TopoEntity
::
Edge
:
read_edges
(
reader
,
header
);
return
;
case
OpenVolumeMesh
::
IO
::
detail
::
TopoEntity
::
Face
:
read_faces
(
reader
,
header
,
valences
);
return
;
case
OpenVolumeMesh
::
IO
::
detail
::
TopoEntity
::
Cell
:
read_cells
(
reader
,
header
,
valences
);
return
;
}
prop
.
decoder
->
deserialize
(
prop
.
prop
.
get
(),
reader
,
static_cast
<
size_t
>
(
header
.
span
.
base
),
static_cast
<
size_t
>
(
header
.
span
.
base
+
header
.
span
.
count
));
}
void
BinaryFileReader
::
read
E
dges
Chunk
(
Decoder
&
r
eader
)
void
BinaryFileReader
::
read
_e
dges
(
Deco
der
&
reader
,
const
TopoChunkHea
der
&
h
eader
)
{
TopoChunkHeader
header
;
read
(
reader
,
header
);
if
(
!
is_valid
(
header
.
enc
))
{
state_
=
ReadState
::
ErrorInvalidEncoding
;
if
(
!
validate_span
(
file_header_
.
n_edges
,
n_edges_read_
,
header
.
span
))
{
return
;
}
if
(
!
validate_span
(
file_header_
.
n_edges
,
n_edges_read_
,
header
.
span
))
if
(
header
.
valence
!=
2
)
{
state_
=
ReadState
::
ErrorInvalidFile
;
error_msg_
=
"TOPO edge chunk: valence must be 2"
;
return
;
}
auto
read_all
=
[
&
](
auto
read_one
)
{
if
(
reader
.
remaining_bytes
()
!=
header
.
span
.
count
*
2
*
elem_size
(
header
.
enc
))
{
std
::
cerr
<<
"edge chunk size "
<<
std
::
endl
;
state_
=
ReadState
::
ErrorInvalidChunkSize
;
return
;
}
for
(
size_t
i
=
0
;
i
<
header
.
span
.
count
;
++
i
)
{
uint64_t
src
=
read_one
(
reader
)
+
header
.
span
.
base
;
uint64_t
dst
=
read_one
(
reader
)
+
header
.
span
.
base
;
uint64_t
src
=
read_one
(
reader
)
+
header
.
span
.
first
;
uint64_t
dst
=
read_one
(
reader
)
+
header
.
span
.
first
;
if
(
src
>=
n_verts_read_
||
dst
>=
n_verts_read_
)
{
state_
=
ReadState
::
ErrorHandleRange
;
}
else
{
...
...
@@ -197,113 +195,134 @@ void BinaryFileReader::readEdgesChunk(Decoder &reader)
}
};
call_with_decoder
(
header
.
enc
,
read_all
);
call_with_decoder
(
header
.
handle_encoding
,
read_all
);
if
(
state_
==
ReadState
::
ReadingChunks
)
{
n_edges_read_
+=
header
.
span
.
count
;
}
}
void
BinaryFileReader
::
read
F
aces
Chunk
(
Decoder
&
reader
)
void
BinaryFileReader
::
read
_f
aces
(
Decoder
&
reader
,
const
TopoChunkHeader
&
header
,
const
ValenceVec
&
_valences
)
{
TopoChunkHeader
header
;
read
(
reader
,
header
);
uint32_t
fixed_valence
=
reader
.
u8
();
IntEncoding
valence_enc
;
read
(
reader
,
valence_enc
);
reader
.
reserved
<
2
>
();
if
(
!
is_valid
(
header
.
enc
))
{
state_
=
ReadState
::
ErrorInvalidEncoding
;
return
;
}
if
(
!
validate_span
(
file_header_
.
n_faces
,
n_faces_read_
,
header
.
span
))
return
;
if
(
file_header_
.
topo_type
==
TopoType
::
Tetrahedral
&&
fixed_valence
!=
3
)
{
if
(
file_header_
.
topo_type
==
TopoType
::
Tetrahedral
&&
header
.
valence
!=
3
)
{
state_
=
ReadState
::
ErrorInvalidTopoType
;
error_msg_
=
"TOPO chunk: Faces of tetrahedral meshes must have a fixed valence of 3"
;
return
;
}
if
(
file_header_
.
topo_type
==
TopoType
::
Hexahedral
&&
fixed_
valence
!=
4
)
{
if
(
file_header_
.
topo_type
==
TopoType
::
Hexahedral
&&
header
.
valence
!=
4
)
{
state_
=
ReadState
::
ErrorInvalidTopoType
;
error_msg_
=
"TOPO chunk: Faces of hexahedral meshes must have a fixed valence of 4"
;
return
;
}
auto
read_all
=
[
&
](
auto
read_one
)
assert
(
header
.
valence
!=
0
||
_valences
.
size
()
==
header
.
span
.
count
);
for
(
uint64_t
i
=
0
;
i
<
header
.
span
.
count
;
++
i
)
{
auto
add_face
=
[
&
](
auto
halfedges
)
{
return
mesh_
->
add_face
(
std
::
move
(
halfedges
),
options_
.
topology_check
);
};
readFacesOrCells
<
HalfEdgeHandle
>
(
reader
,
header
,
fixed_valence
,
valence_enc
,
n_edges_read_
*
2
,
read_one
,
add_face
);
uint32_t
valence
=
header
.
valence
==
0
?
_valences
[
i
]
:
header
.
valence
;
std
::
vector
<
HEH
>
halfedges
;
halfedges
.
reserve
(
valence
);
auto
success
=
read_n_ints
(
reader
,
header
.
handle_encoding
,
halfedges
,
valence
,
[
&
](
uint32_t
x
){
return
HEH
::
from_unsigned
(
x
+
header
.
handle_offset
);});
if
(
!
success
)
break
;
mesh_
->
add_face
(
std
::
move
(
halfedges
),
options_
.
topology_check
);
};
call_with_decoder
(
header
.
enc
,
read_all
);
if
(
state_
==
ReadState
::
ReadingChunks
)
{
n_faces_read_
+=
header
.
span
.
count
;
}
}
void
BinaryFileReader
::
read
C
ells
Chunk
(
Decoder
&
reader
)
void
BinaryFileReader
::
read
_c
ells
(
Decoder
&
reader
,
const
TopoChunkHeader
&
header
,
const
ValenceVec
&
_valences
)
{
TopoChunkHeader
header
;
read
(
reader
,
header
);
uint32_t
fixed_valence
=
reader
.
u8
();
IntEncoding
valence_enc
;
read
(
reader
,
valence_enc
);
reader
.
reserved
<
2
>
();