@@ -895,6 +895,52 @@ def check_item(x, y):
895
895
with self .assertRaisesRegex (ValueError , "same type of texture" ):
896
896
join_meshes_as_batch ([mesh_atlas , mesh_rgb , mesh_atlas ])
897
897
898
+ def test_save_obj_with_normal (self ):
899
+ verts = torch .tensor (
900
+ [[0.01 , 0.2 , 0.301 ], [0.2 , 0.03 , 0.408 ], [0.3 , 0.4 , 0.05 ], [0.6 , 0.7 , 0.8 ]],
901
+ dtype = torch .float32 ,
902
+ )
903
+ faces = torch .tensor (
904
+ [[0 , 2 , 1 ], [0 , 1 , 2 ], [3 , 2 , 1 ], [3 , 1 , 0 ]], dtype = torch .int64
905
+ )
906
+ verts_normals = torch .tensor (
907
+ [[0.02 , 0.5 , 0.73 ], [0.3 , 0.03 , 0.361 ], [0.32 , 0.12 , 0.47 ], [0.36 , 0.17 , 0.9 ]],
908
+ dtype = torch .float32 ,
909
+ )
910
+ faces_normals = faces
911
+
912
+ with TemporaryDirectory () as temp_dir :
913
+ obj_file = os .path .join (temp_dir , "mesh.obj" )
914
+ save_obj (
915
+ obj_file ,
916
+ verts ,
917
+ faces ,
918
+ decimal_places = 2 ,
919
+ verts_normals = verts_normals ,
920
+ faces_normals = faces_normals ,
921
+ )
922
+
923
+ expected_obj_file = "\n " .join (
924
+ [
925
+ "v 0.01 0.20 0.30" ,
926
+ "v 0.20 0.03 0.41" ,
927
+ "v 0.30 0.40 0.05" ,
928
+ "v 0.60 0.70 0.80" ,
929
+ "vn 0.02 0.50 0.73" ,
930
+ "vn 0.30 0.03 0.36" ,
931
+ "vn 0.32 0.12 0.47" ,
932
+ "vn 0.36 0.17 0.90" ,
933
+ "f 1//1 3//3 2//2" ,
934
+ "f 1//1 2//2 3//3" ,
935
+ "f 4//4 3//3 2//2" ,
936
+ "f 4//4 2//2 1//1" ,
937
+ ]
938
+ )
939
+
940
+ # Check the obj file is saved correctly
941
+ actual_file = open (obj_file , "r" )
942
+ self .assertEqual (actual_file .read (), expected_obj_file )
943
+
898
944
def test_save_obj_with_texture (self ):
899
945
verts = torch .tensor (
900
946
[[0.01 , 0.2 , 0.301 ], [0.2 , 0.03 , 0.408 ], [0.3 , 0.4 , 0.05 ], [0.6 , 0.7 , 0.8 ]],
@@ -962,6 +1008,84 @@ def test_save_obj_with_texture(self):
962
1008
texture_image = load_rgb_image ("mesh.png" , temp_dir )
963
1009
self .assertClose (texture_image , texture_map )
964
1010
1011
+ def test_save_obj_with_normal_and_texture (self ):
1012
+ verts = torch .tensor (
1013
+ [[0.01 , 0.2 , 0.301 ], [0.2 , 0.03 , 0.408 ], [0.3 , 0.4 , 0.05 ], [0.6 , 0.7 , 0.8 ]],
1014
+ dtype = torch .float32 ,
1015
+ )
1016
+ faces = torch .tensor (
1017
+ [[0 , 2 , 1 ], [0 , 1 , 2 ], [3 , 2 , 1 ], [3 , 1 , 0 ]], dtype = torch .int64
1018
+ )
1019
+ verts_normals = torch .tensor (
1020
+ [[0.02 , 0.5 , 0.73 ], [0.3 , 0.03 , 0.361 ], [0.32 , 0.12 , 0.47 ], [0.36 , 0.17 , 0.9 ]],
1021
+ dtype = torch .float32 ,
1022
+ )
1023
+ faces_normals = faces
1024
+ verts_uvs = torch .tensor (
1025
+ [[0.02 , 0.5 ], [0.3 , 0.03 ], [0.32 , 0.12 ], [0.36 , 0.17 ]],
1026
+ dtype = torch .float32 ,
1027
+ )
1028
+ faces_uvs = faces
1029
+ texture_map = torch .randint (size = (2 , 2 , 3 ), high = 255 ) / 255.0
1030
+
1031
+ with TemporaryDirectory () as temp_dir :
1032
+ obj_file = os .path .join (temp_dir , "mesh.obj" )
1033
+ save_obj (
1034
+ obj_file ,
1035
+ verts ,
1036
+ faces ,
1037
+ decimal_places = 2 ,
1038
+ verts_normals = verts_normals ,
1039
+ faces_normals = faces_normals ,
1040
+ verts_uvs = verts_uvs ,
1041
+ faces_uvs = faces_uvs ,
1042
+ texture_map = texture_map ,
1043
+ )
1044
+
1045
+ expected_obj_file = "\n " .join (
1046
+ [
1047
+ "" ,
1048
+ "mtllib mesh.mtl" ,
1049
+ "usemtl mesh" ,
1050
+ "" ,
1051
+ "v 0.01 0.20 0.30" ,
1052
+ "v 0.20 0.03 0.41" ,
1053
+ "v 0.30 0.40 0.05" ,
1054
+ "v 0.60 0.70 0.80" ,
1055
+ "vn 0.02 0.50 0.73" ,
1056
+ "vn 0.30 0.03 0.36" ,
1057
+ "vn 0.32 0.12 0.47" ,
1058
+ "vn 0.36 0.17 0.90" ,
1059
+ "vt 0.02 0.50" ,
1060
+ "vt 0.30 0.03" ,
1061
+ "vt 0.32 0.12" ,
1062
+ "vt 0.36 0.17" ,
1063
+ "f 1/1/1 3/3/3 2/2/2" ,
1064
+ "f 1/1/1 2/2/2 3/3/3" ,
1065
+ "f 4/4/4 3/3/3 2/2/2" ,
1066
+ "f 4/4/4 2/2/2 1/1/1" ,
1067
+ ]
1068
+ )
1069
+ expected_mtl_file = "\n " .join (["newmtl mesh" , "map_Kd mesh.png" , "" ])
1070
+
1071
+ # Check there are only 3 files in the temp dir
1072
+ tempfiles = ["mesh.obj" , "mesh.png" , "mesh.mtl" ]
1073
+ tempfiles_dir = os .listdir (temp_dir )
1074
+ self .assertEqual (Counter (tempfiles ), Counter (tempfiles_dir ))
1075
+
1076
+ # Check the obj file is saved correctly
1077
+ actual_file = open (obj_file , "r" )
1078
+ self .assertEqual (actual_file .read (), expected_obj_file )
1079
+
1080
+ # Check the mtl file is saved correctly
1081
+ mtl_file_name = os .path .join (temp_dir , "mesh.mtl" )
1082
+ mtl_file = open (mtl_file_name , "r" )
1083
+ self .assertEqual (mtl_file .read (), expected_mtl_file )
1084
+
1085
+ # Check the texture image file is saved correctly
1086
+ texture_image = load_rgb_image ("mesh.png" , temp_dir )
1087
+ self .assertClose (texture_image , texture_map )
1088
+
965
1089
def test_save_obj_with_texture_errors (self ):
966
1090
verts = torch .tensor (
967
1091
[[0.01 , 0.2 , 0.301 ], [0.2 , 0.03 , 0.408 ], [0.3 , 0.4 , 0.05 ], [0.6 , 0.7 , 0.8 ]],
0 commit comments