@@ -55,72 +55,140 @@ public static bool ExportObj(Renderer rend, bool bakedMesh, bool bakedWorldPosit
5555 return false ;
5656 }
5757
58+ private static Mesh GetMeshFromRenderer ( Renderer rend , bool baked )
59+ {
60+ switch ( rend )
61+ {
62+ case MeshRenderer meshRenderer :
63+ return meshRenderer . GetComponent < MeshFilter > ( ) . mesh ;
64+ case SkinnedMeshRenderer skinnedMeshRenderer :
65+ return baked ? BakeMesh ( skinnedMeshRenderer ) : skinnedMeshRenderer . sharedMesh ;
66+ default :
67+ throw new ArgumentException ( "Unsupported Renderer type: " + rend . GetType ( ) . FullName ) ;
68+ }
69+ }
70+
71+ private static Mesh BakeMesh ( SkinnedMeshRenderer mesh )
72+ {
73+ var bakedMesh = new Mesh ( ) ;
74+ mesh . BakeMesh ( bakedMesh ) ;
75+ return bakedMesh ;
76+ }
77+
78+ private static string NameFormatted ( this Renderer go ) => go == null ? "" : go . name . Replace ( "(Instance)" , "" ) . Replace ( " Instance" , "" ) . Trim ( ) ;
79+
5880 private static string MeshToObjString ( Renderer rend , bool bakedMesh , bool bakedWorldPosition )
5981 {
6082 if ( rend == null ) throw new ArgumentNullException ( nameof ( rend ) ) ;
6183
62- Mesh mesh ;
63- if ( rend is MeshRenderer meshRenderer )
64- mesh = meshRenderer . GetComponent < MeshFilter > ( ) . mesh ;
65- else if ( rend is SkinnedMeshRenderer skinnedMeshRenderer )
84+ var mesh = GetMeshFromRenderer ( rend , bakedMesh ) ;
85+ var sb = new StringBuilder ( ) ;
86+ var hasData = false ;
87+
88+ hasData |= AppendVertices ( sb , mesh , rend , bakedMesh && bakedWorldPosition ) ;
89+ hasData |= AppendTextureCoordinates ( sb , mesh ) ;
90+ hasData |= AppendNormals ( sb , mesh , rend , bakedMesh && bakedWorldPosition ) ;
91+ hasData |= AppendFaces ( sb , mesh , rend ) ;
92+
93+ if ( ! hasData )
94+ throw new InvalidOperationException ( "No mesh data found or mesh is set as not readable" ) ;
95+ return sb . ToString ( ) ;
96+ }
97+
98+ /// <summary>
99+ /// Appends the vertex data of a mesh to a StringBuilder in a specific format.
100+ /// </summary>
101+ /// <param name="builder">The StringBuilder object to append the vertex data to.</param>
102+ /// <param name="mesh">The Mesh object containing the vertex data to export.</param>
103+ /// <param name="renderer">The Renderer associated with the mesh, used for transformations.</param>
104+ /// <param name="bakedMesh">Indicates whether the mesh is baked.</param>
105+ /// <param name="bakedWorldPosition">Indicates whether to apply world position transformations to the vertices.</param>
106+ /// <returns>Returns true if vertex data was successfully appended, otherwise false.</returns>
107+ private static bool AppendVertices ( StringBuilder builder , Mesh mesh , Renderer renderer , bool baked )
108+ {
109+ if ( mesh . vertices . Length == 0 )
110+ return false ;
111+
112+ foreach ( var v in mesh . vertices )
66113 {
67- if ( bakedMesh )
68- {
69- mesh = new Mesh ( ) ;
70- skinnedMeshRenderer . BakeMesh ( mesh ) ;
71- }
72- else
73- {
74- mesh = skinnedMeshRenderer . sharedMesh ;
75- }
114+ var transformedVertex = baked ? renderer . transform . TransformPoint ( v ) : v ;
115+ builder . AppendLine ( $ "v { - transformedVertex . x } { transformedVertex . y } { transformedVertex . z } ") ;
76116 }
77- else throw new ArgumentException ( "Unsupported Renderer type: " + rend . GetType ( ) . FullName ) ;
78117
79- var scale = rend . transform . lossyScale ;
80- var inverseScale = Matrix4x4 . Scale ( scale ) . inverse ;
118+ return true ;
119+ }
81120
82- var sb = new StringBuilder ( ) ;
83- var any = false ;
84- for ( var x = 0 ; x < mesh . subMeshCount ; x ++ )
121+ /// <summary>
122+ /// Appends the texture coordinates (UV mapping) of a mesh to the provided StringBuilder.
123+ /// </summary>
124+ /// <param name="builder">The StringBuilder instance to append the texture coordinates to.</param>
125+ /// <param name="mesh">The mesh whose texture coordinates will be appended.</param>
126+ /// <returns>True if the mesh has texture coordinates; otherwise, false.</returns>
127+ private static bool AppendTextureCoordinates ( StringBuilder builder , Mesh mesh )
128+ {
129+ if ( mesh . uv . Length == 0 )
130+ return false ;
131+
132+ foreach ( var uv in mesh . uv )
85133 {
86- var subMesh = MeshExtensions . Submesh ( mesh , x ) ;
134+ builder . AppendLine ( $ "vt { uv . x } { uv . y } ") ;
135+ }
87136
88- sb . AppendLine ( $ "g { rend . name . Replace ( "(Instance)" , "" ) . Replace ( " Instance" , "" ) . Trim ( ) } _{ x } ") ;
137+ return true ;
138+ }
89139
90- for ( var i = 0 ; i < subMesh . vertices . Length ; i ++ )
91- {
92- var v = subMesh . vertices [ i ] ;
93- if ( bakedMesh && bakedWorldPosition )
94- v = rend . transform . TransformPoint ( inverseScale . MultiplyPoint ( v ) ) ;
95- sb . AppendLine ( $ "v { - v . x } { v . y } { v . z } ") ;
96- any = true ;
97- }
140+ /// <summary>
141+ /// Appends vertex normal data from a mesh to the provided StringBuilder in the Wavefront .obj format.
142+ /// </summary>
143+ /// <param name="builder">The StringBuilder to which the normal data will be appended.</param>
144+ /// <param name="mesh">The mesh whose normals will be processed and appended.</param>
145+ /// <param name="renderer">The renderer associated with the mesh, used for transforming normals if needed.</param>
146+ /// <param name="bakedMesh">Indicates if the mesh should be treated as baked.</param>
147+ /// <param name="bakedWorldPosition">Indicates if the normals should be transformed to world position when the mesh is baked.</param>
148+ /// <returns>True if normals are successfully appended; false if the mesh contains no normals.</returns>
149+ private static bool AppendNormals ( StringBuilder builder , Mesh mesh , Renderer renderer , bool baked )
150+ {
151+ if ( mesh . normals . Length == 0 )
152+ return false ;
98153
99- for ( var i = 0 ; i < subMesh . uv . Length ; i ++ )
100- {
101- Vector3 v = subMesh . uv [ i ] ;
102- sb . AppendLine ( $ "vt { v . x } { v . y } ") ;
103- any = true ;
104- }
154+ foreach ( var normal in mesh . normals )
155+ {
156+ var transformedNormal = baked ? renderer . transform . TransformDirection ( normal ) : normal ;
157+ builder . AppendLine ( $ "vn { - transformedNormal . x } { transformedNormal . y } { transformedNormal . z } ") ;
158+ }
105159
106- for ( var i = 0 ; i < subMesh . normals . Length ; i ++ )
107- {
108- var v = subMesh . normals [ i ] ;
109- sb . AppendLine ( $ "vn { - v . x } { v . y } { v . z } ") ;
110- any = true ;
111- }
160+ return true ;
161+ }
112162
113- var triangles = subMesh . GetTriangles ( x ) ;
163+ /// <summary>
164+ /// Appends face data from a mesh to the specified StringBuilder in the Wavefront OBJ format.
165+ /// </summary>
166+ /// <param name="builder">The StringBuilder to which the face data will be appended.</param>
167+ /// <param name="mesh">The mesh containing face data to be exported.</param>
168+ /// <param name="renderer">The Renderer associated with the mesh, used for naming and formatting purposes.</param>
169+ /// <returns>Returns true if the mesh contains sub-mesh data and face data was successfully appended; otherwise, false.</returns>
170+ private static bool AppendFaces ( StringBuilder builder , Mesh mesh , Renderer renderer )
171+ {
172+ if ( mesh . subMeshCount == 0 ) return false ;
173+
174+ for ( var subMeshIndex = 0 ; subMeshIndex < mesh . subMeshCount ; subMeshIndex ++ )
175+ {
176+ builder . AppendLine ( $ "g { renderer . NameFormatted ( ) } _{ subMeshIndex } ") ;
177+ var triangles = mesh . GetTriangles ( subMeshIndex ) ;
178+
114179 for ( var i = 0 ; i < triangles . Length ; i += 3 )
115180 {
116- sb . AppendFormat ( "f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n " , triangles [ i ] + 1 , triangles [ i + 2 ] + 1 , triangles [ i + 1 ] + 1 ) ;
117- any = true ;
181+ var v1 = triangles [ i ] + 1 ;
182+ var v2 = triangles [ i + 2 ] + 1 ;
183+ var v3 = triangles [ i + 1 ] + 1 ;
184+ builder . AppendLine ( $ "f { v1 } /{ v1 } /{ v1 } { v2 } /{ v2 } /{ v2 } { v3 } /{ v3 } /{ v3 } ") ;
118185 }
119186 }
120- if ( ! any ) throw new InvalidOperationException ( "No mesh data found or mesh is set as not readable" ) ;
121- return sb . ToString ( ) ;
187+ return true ;
122188 }
123189
190+
191+ // Should be possible to be safely removed, I'll leave it here for now as a fallback in case something goes wrong
124192 private static class MeshExtensions
125193 {
126194 public static Mesh Submesh ( Mesh mesh , int submeshIndex )
0 commit comments