Skip to content
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

Added new "--no_normals" option to not include normals in 3D tiles #159

Merged
merged 2 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions py3dtilers/Common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,24 @@ Project the features on another CRS. The `crs_in` flag allows to specify the inp
<tiler> <input> --crs_in EPSG:3946 --crs_out EPSG:4171
```

### No Normals

| Tiler | |
| ------------ | ------------------ |
| CityTiler | :heavy_check_mark: |
| ObjTiler | :heavy_check_mark: |
| GeojsonTiler | :heavy_check_mark: |
| IfcTiler | :heavy_check_mark: |
| TilesetTiler | :heavy_check_mark: |

Optionally disable creation of normals in 3D tiles:

```bash
<tiler> <input> --no_normals
```

This could be very useful for 3D tiles created out Photogrammetry OBJ meshes. If normals are not present, Cesium wil display tiles using flat lighning.

### With texture

| Tiler | |
Expand Down
8 changes: 7 additions & 1 deletion py3dtilers/Common/tiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ def __init__(self):
dest='with_texture',
action='store_true',
help='Adds texture to 3DTiles when defined')

self.parser.add_argument('--no_normals',
dest='no_normals',
action='store_true',
help='If specified, no normals will be written to glTf, useful for Photogrammetry meshes')

self.parser.add_argument('--quality',
nargs='?',
Expand Down Expand Up @@ -244,11 +249,12 @@ def create_tileset_from_groups(self, groups: Groups, extension_name=None):
"""
create_loa = self.args.loa is not None
geometric_errors = self.args.geometric_error if hasattr(self.args, 'geometric_error') else [None, None, None]
with_normals = False if self.args.no_normals else True

tree = LodTree(groups, self.args.lod1, create_loa, self.args.with_texture, geometric_errors, self.args.texture_lods)

self.create_output_directory()
return FromGeometryTreeToTileset.convert_to_tileset(tree, self.args, extension_name, self.get_output_dir())
return FromGeometryTreeToTileset.convert_to_tileset(tree, self.args, extension_name, self.get_output_dir(), with_normals=with_normals)

def create_output_directory(self):
"""
Expand Down
14 changes: 7 additions & 7 deletions py3dtilers/Common/tileset_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class FromGeometryTreeToTileset():
nb_nodes = 0

@staticmethod
def convert_to_tileset(geometry_tree: 'GeometryTree', user_arguments=None, extension_name=None, output_dir=None):
def convert_to_tileset(geometry_tree: 'GeometryTree', user_arguments=None, extension_name=None, output_dir=None, with_normals=True):
"""
Recursively creates a tileset from the nodes of a GeometryTree
:param geometry_tree: an instance of GeometryTree to transform into 3DTiles.
Expand All @@ -39,7 +39,7 @@ def convert_to_tileset(geometry_tree: 'GeometryTree', user_arguments=None, exten
root_node = geometry_tree.root_nodes[0]
root_node.set_node_features_geometry(user_arguments)
offset = FromGeometryTreeToTileset.__transform_node(root_node, user_arguments, tree_centroid, obj_writer=obj_writer)
tileset.add_tile(FromGeometryTreeToTileset.__create_tile(root_node, offset, extension_name, output_dir))
tileset.add_tile(FromGeometryTreeToTileset.__create_tile(root_node, offset, extension_name, output_dir, with_normals))
geometry_tree.root_nodes.remove(root_node)

if user_arguments.obj is not None:
Expand Down Expand Up @@ -84,7 +84,7 @@ def __transform_node(node: 'GeometryNode', user_args, tree_centroid=np.array([0,
return distance if user_args.offset[0] == 'centroid' else offset

@staticmethod
def __create_tile(node: 'GeometryNode', transform_offset, extension_name=None, output_dir=None):
def __create_tile(node: 'GeometryNode', transform_offset, extension_name=None, output_dir=None, with_normals=True):
"""
Create a tile from a node. Recursively create tiles from the children of the node.
:param node: the GeometryNode.
Expand All @@ -98,7 +98,7 @@ def __create_tile(node: 'GeometryNode', transform_offset, extension_name=None, o
tile = Tile()
tile.set_geometric_error(node.geometric_error)

content_b3dm = FromGeometryTreeToTileset.__create_tile_content(feature_list, extension_name, node.has_texture(), node.downsample_factor)
content_b3dm = FromGeometryTreeToTileset.__create_tile_content(feature_list, extension_name, node.has_texture(), node.downsample_factor, with_normals)
tile.set_content(content_b3dm)
tile.set_content_uri('tiles/' + f'{FromGeometryTreeToTileset.tile_index}.b3dm')
tile.write_content(output_dir)
Expand All @@ -125,12 +125,12 @@ def __create_tile(node: 'GeometryNode', transform_offset, extension_name=None, o

FromGeometryTreeToTileset.tile_index += 1
for child_node in node.child_nodes:
tile.add_child(FromGeometryTreeToTileset.__create_tile(child_node, [0., 0., 0.], extension_name, output_dir))
tile.add_child(FromGeometryTreeToTileset.__create_tile(child_node, [0., 0., 0.], extension_name, output_dir, with_normals))

return tile

@staticmethod
def __create_tile_content(feature_list: 'FeatureList', extension_name=None, with_texture=False, downsample_factor=1):
def __create_tile_content(feature_list: 'FeatureList', extension_name=None, with_texture=False, downsample_factor=1, with_normals=True):
"""
:param pre_tile: an array containing features of a single tile

Expand Down Expand Up @@ -174,7 +174,7 @@ def __create_tile_content(feature_list: 'FeatureList', extension_name=None, with
0, 1, 0, 0,
0, 0, 0, 1])

gltf = GlTF.from_binary_arrays(arrays, transform, materials=materials)
gltf = GlTF.from_binary_arrays(arrays, transform, materials=materials, withNormals=with_normals)

# Create a batch table and add the ID of each feature to it
ids = [feature.get_id() for feature in feature_list]
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cityTemporalTiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def get_default_namespace():
crs_out='EPSG:3946', offset=[0, 0, 0], with_texture=False, scale=1,
output_dir=None, geometric_error=[None, None, None],
split_surfaces=False, add_color=False, kd_tree_max=None, texture_lods=0,
keep_ids=[], exclude_ids=[])
keep_ids=[], exclude_ids=[], no_normals=False)


class Test_Tile(unittest.TestCase):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cityTiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def get_default_namespace():
crs_out='EPSG:3946', offset=[0, 0, 0], with_texture=False, scale=1,
output_dir=None, geometric_error=[None, None, None],
split_surfaces=False, add_color=False, kd_tree_max=None, ids=[], texture_lods=0,
keep_ids=[], exclude_ids=[])
keep_ids=[], exclude_ids=[], no_normals=False)


class Test_Tile(unittest.TestCase):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_geojsonTiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def get_default_namespace():
return Namespace(obj=None, loa=None, lod1=False, crs_in='EPSG:3946',
crs_out='EPSG:3946', offset=[0, 0, 0], with_texture=False, scale=1,
output_dir=None, geometric_error=[None, None, None], kd_tree_max=None,
texture_lods=0, keep_ids=[], exclude_ids=[])
texture_lods=0, keep_ids=[], exclude_ids=[], no_normals=False)


class Test_Tile(unittest.TestCase):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_ifcTiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
def get_default_namespace():
return Namespace(obj=None, loa=None, lod1=False, crs_in='EPSG:3946',
crs_out='EPSG:3946', offset=[0, 0, 0], with_texture=False, grouped_by='IfcTypeObject', scale=1,
output_dir=None, geometric_error=[None, None, None], kd_tree_max=None, texture_lods=0, keep_ids=[], exclude_ids=[])
output_dir=None, geometric_error=[None, None, None], kd_tree_max=None, texture_lods=0, keep_ids=[], exclude_ids=[], no_normals=False)


class Test_Tile(unittest.TestCase):
Expand Down
13 changes: 12 additions & 1 deletion tests/test_objTiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def get_default_namespace():
return Namespace(obj=None, loa=None, lod1=False, crs_in='EPSG:3946',
crs_out='EPSG:3946', offset=[0, 0, 0], with_texture=False, scale=1,
output_dir=None, geometric_error=[None, None, None], kd_tree_max=None,
texture_lods=0, keep_ids=[], exclude_ids=[])
texture_lods=0, keep_ids=[], exclude_ids=[], no_normals=False)


class Test_Tile(unittest.TestCase):
Expand All @@ -24,6 +24,17 @@ def test_basic_case(self):
if tileset is not None:
tileset.write_as_json(Path(obj_tiler.args.output_dir))

def test_basic_case_no_normals(self):
obj_tiler = ObjTiler()
obj_tiler.files = [Path('tests/obj_tiler_data/Cube/cube_1.obj'), Path('tests/obj_tiler_data/Cube/cube_2.obj')]
obj_tiler.args = get_default_namespace()
obj_tiler.args.output_dir = Path("tests/obj_tiler_data/generated_tilesets/basic_case_no_normals")
obj_tiler.args.no_normals = True

tileset = obj_tiler.from_obj_directory()
if tileset is not None:
tileset.write_as_json(Path(obj_tiler.args.output_dir))

def test_texture(self):
obj_tiler = ObjTiler()
obj_tiler.files = [Path('tests/obj_tiler_data/TexturedCube/cube.obj')]
Expand Down
2 changes: 1 addition & 1 deletion tests/test_tiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def get_default_namespace():
return Namespace(obj=None, loa=None, lod1=False, crs_in='EPSG:3946',
crs_out='EPSG:3946', offset=[0, 0, 0], with_texture=False, scale=1,
output_dir=None, geometric_error=[None, None, None], kd_tree_max=None,
texture_lods=0, keep_ids=[], exclude_ids=[])
texture_lods=0, keep_ids=[], exclude_ids=[], no_normals=False)


triangles = [[np.array([1843366, 5174473, 200]),
Expand Down