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