1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Services to parse .obj geometry files
18 import model_parser_base
20 # Parser of .obj files in ASCII format
22 # Instantiate from a `String` and use `parse` to extract the `ObjDef`.
27 # mtllib material_file.mtl
29 # v 1.000000 0.000000 0.000000
30 # v 1.000000 0.000000 1.000000
31 # v 0.000000 0.000000 1.000000
32 # v 0.000000 0.000000 0.000000
33 # v 1.000000 1.000000 0.999999
34 # v 0.999999 1.000000 1.000001
35 # v 0.000000 1.000000 1.000000
36 # v 0.000000 1.000000 0.000000
37 # usemtl GreenMaterial
47 # var parser = new ObjFileParser(obj_src)
48 # var parsed_obj = parser.parse
49 # assert parsed_obj.is_coherent
54 private var geometry
= new ObjDef is lazy
56 private var current_material_lib
: nullable String = null
58 private var current_material_name
: nullable String = null
60 # Execute parsing of `src` to extract an `ObjDef`
61 fun parse
: nullable ObjDef
64 var token
= read_token
65 if token
.is_empty
or token
== "#" then
66 # Ignore empty lines and comments
67 else if token
== "v" then # Vertex points
69 geometry
.vertex_points
.add vec
70 else if token
== "vt" then # Texture coords
72 geometry
.texture_coords
.add vec
73 else if token
== "vn" then # Normals
74 var vec
= read_vec3
# This one should not accept `w` values
75 geometry
.normals
.add vec
76 else if token
== "vp" then # Parameter space vertices
78 geometry
.params
.add vec
79 else if token
== "f" then # Faces
81 geometry
.faces
.add face
82 else if token
== "mtllib" then
83 current_material_lib
= read_until_eol_or_comment
84 else if token
== "usemtl" then
85 current_material_name
= read_until_eol_or_comment
87 # TODO other line type headers
88 else if token
== "s" then
89 else if token
== "o" then
90 else if token
== "g" then
97 private fun read_face
: ObjFace
99 var face
= new ObjFace(current_material_lib
, current_material_name
)
102 var r
= read_face_index_set
(face
)
109 private fun read_face_index_set
(face
: ObjFace): Bool
111 var token
= read_token
113 var parts
= token
.split
('/')
114 if parts
.is_empty
or parts
.first
.is_empty
then return false
116 var v
= new ObjVertex
117 for i
in parts
.length
.times
, part
in parts
do
121 if not part
.is_empty
and part
.is_numeric
then n
= part
.to_i
124 n
= n
or else 0 # Error if n == null
125 if n
< 0 then n
= geometry
.vertex_points
.length
+ n
126 v
.vertex_point_index
= n
128 if n
!= null and n
< 0 then n
= geometry
.texture_coords
.length
+ n
129 v
.texture_coord_index
= n
131 if n
!= null and n
< 0 then n
= geometry
.normals
.length
+ n
141 # Geometry from a .obj file
144 var vertex_points
= new Array[Vec4]
146 # Texture coordinates
147 var texture_coords
= new Array[Vec3]
150 var normals
= new Array[Vec3]
153 var params
= new Array[Vec3]
156 var faces
= new Array[ObjFace]
158 # Relative paths to referenced material libraries
159 fun material_libs
: Set[String] do
160 var libs
= new Set[String]
162 var lib
= face
.material_lib
163 if lib
!= null then libs
.add lib
168 # Check the coherence of the model
170 # Returns `false` on error and prints details to stderr.
172 # This service can be useful for debugging, however it should not
173 # be executed at each execution of a game.
174 fun is_coherent
: Bool
177 if f
.vertices
.length
< 3 then return error
("ObjFace with less than 3 vertices")
179 for v
in f
.vertices
do
180 var i
= v
.vertex_point_index
181 if i
< 1 then return error
("Vertex point index < 1")
182 if i
> vertex_points
.length
then return error
("Vertex point index > than length")
184 var j
= v
.texture_coord_index
186 if j
< 1 then return error
("Texture coord index < 1")
187 if j
> texture_coords
.length
then return error
("Texture coord index > than length")
192 if j
< 1 then return error
("Normal index < 1")
193 if j
> normals
.length
then return error
("Normal index > than length")
200 # Service to print errors for `is_coherent`
201 private fun error
(msg
: Text): Bool
203 print_error
"ObjDef Error: {msg}"
208 # Flat surface of an `ObjDef`
210 # Vertex composing this surface, thene should be 3 or more
211 var vertices
= new Array[ObjVertex]
213 # Relative path to the .mtl material lib
214 var material_lib
: nullable String
216 # Name of the material in `material_lib`
217 var material_name
: nullable String
220 # Vertex composing a `ObjFace`
222 # Vertex coordinates index in `ObjDef::vertex_points`, starting at 1
223 var vertex_point_index
= 0
225 # Texture coordinates index in `ObjDef::texture_coords`, starting at 1
226 var texture_coord_index
: nullable Int = null
228 # Normal index in `ObjDef::normals`, starting at 1
229 var normal_index
: nullable Int = null