Parser of .obj files in ASCII format

Instantiate from a String and use parse to extract the ObjDef.

var obj_src = """
# Model of a cube
mtllib material_file.mtl
o Cube
v 1.000000 0.000000 0.000000
v 1.000000 0.000000 1.000000
v 0.000000 0.000000 1.000000
v 0.000000 0.000000 0.000000
v 1.000000 1.000000 0.999999
v 0.999999 1.000000 1.000001
v 0.000000 1.000000 1.000000
v 0.000000 1.000000 0.000000
usemtl GreenMaterial
s off
f 1 2 3 4
f 5 6 7 8
f 1 5 8 2
f 2 8 7 3
f 3 7 6 4
f 5 1 4 6
"""

var parser = new ObjFileParser(obj_src)
var parsed_obj = parser.parse
assert parsed_obj.is_coherent
assert parsed_obj.objects.first.name == "Cube"

Introduced properties

fun parse: nullable ObjDef

gamnit :: ObjFileParser :: parse

Execute parsing of src to extract an ObjDef

Redefined properties

redef type SELF: ObjFileParser

gamnit $ ObjFileParser :: SELF

Type of this instance, automatically specialized in every class

All properties

fun !=(other: nullable Object): Bool

core :: Object :: !=

Have self and other different values?
fun ==(other: nullable Object): Bool

core :: Object :: ==

Have self and other the same value?
type CLASS: Class[SELF]

core :: Object :: CLASS

The type of the class of self.
type SELF: Object

core :: Object :: SELF

Type of this instance, automatically specialized in every class
protected fun class_factory(name: String): CLASS

core :: Object :: class_factory

Implementation used by get_class to create the specific class.
fun class_name: String

core :: Object :: class_name

The class name of the object.
fun current_location: Location

parser_base :: StringProcessor :: current_location

Gives the current location in the src
protected fun eof: Bool

parser_base :: StringProcessor :: eof

Is pos at the end of the source?
fun get_class: CLASS

core :: Object :: get_class

The meta-object representing the dynamic type of self.
fun hash: Int

core :: Object :: hash

The hash code of the object.
protected fun hot_location: Location

parser_base :: StringProcessor :: hot_location

Returns the current location as a Location object
protected fun ignore_until(s: String): Int

parser_base :: StringProcessor :: ignore_until

Reads characters until pattern s is found
protected fun ignore_until_whitespace: Int

parser_base :: StringProcessor :: ignore_until_whitespace

Ignores any printable character until a whitespace is encountered
protected fun ignore_until_whitespace_or_comment: Int

parser_base :: StringProcessor :: ignore_until_whitespace_or_comment

Advance pos until a whitespace or # is encountered
protected fun ignore_whitespaces

parser_base :: StringProcessor :: ignore_whitespaces

Advances in src until a non-whitespace character is encountered
init init

core :: Object :: init

fun inspect: String

core :: Object :: inspect

Developer readable representation of self.
protected fun inspect_head: String

core :: Object :: inspect_head

Return "CLASSNAME:#OBJECTID".
intern fun is_same_instance(other: nullable Object): Bool

core :: Object :: is_same_instance

Return true if self and other are the same instance (i.e. same identity).
fun is_same_serialized(other: nullable Object): Bool

core :: Object :: is_same_serialized

Is self the same as other in a serialization context?
intern fun is_same_type(other: Object): Bool

core :: Object :: is_same_type

Return true if self and other have the same dynamic type.
protected fun len: Int

parser_base :: StringProcessor :: len

Length of the source document
protected fun len=(len: Int)

parser_base :: StringProcessor :: len=

Length of the source document
protected fun line: Int

parser_base :: StringProcessor :: line

Current line in src
protected fun line=(line: Int)

parser_base :: StringProcessor :: line=

Current line in src
protected fun line_offset: Int

parser_base :: StringProcessor :: line_offset

Offset in the current line
protected fun line_start: Int

parser_base :: StringProcessor :: line_start

Position at which current line started
protected fun line_start=(line_start: Int)

parser_base :: StringProcessor :: line_start=

Position at which current line started
intern fun object_id: Int

core :: Object :: object_id

An internal hash code for the object based on its identity.
fun output

core :: Object :: output

Display self on stdout (debug only).
intern fun output_class_name

core :: Object :: output_class_name

Display class name on stdout (debug only).
fun parse: nullable ObjDef

gamnit :: ObjFileParser :: parse

Execute parsing of src to extract an ObjDef
protected fun pos: Int

parser_base :: StringProcessor :: pos

Current position in src
protected fun pos=(pos: Int)

parser_base :: StringProcessor :: pos=

Current position in src
protected fun read_number: Float

parser_base :: StringProcessor :: read_number

Read a token and parse it as a Float
protected fun read_token: String

parser_base :: StringProcessor :: read_token

Read a single token after skipping preceding whitespaces
protected fun read_until_eol_or_comment: String

parser_base :: StringProcessor :: read_until_eol_or_comment

Advance pos until the next end of line or a #
protected fun read_vec3: Vec3

parser_base :: StringProcessor :: read_vec3

Read 2 or 3 numbers and return them as a Vec3
protected fun read_vec4: Vec4

parser_base :: StringProcessor :: read_vec4

Read 3 or 4 numbers and return them as a Vec4
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
protected fun skip_eol

parser_base :: StringProcessor :: skip_eol

Advance pos to skip the next end of line
protected fun src: String

parser_base :: StringProcessor :: src

Source document to parse
protected fun src=(src: String)

parser_base :: StringProcessor :: src=

Source document to parse
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_s: String

core :: Object :: to_s

User readable representation of self.
package_diagram gamnit::ObjFileParser ObjFileParser parser_base::StringProcessor StringProcessor gamnit::ObjFileParser->parser_base::StringProcessor core::Object Object parser_base::StringProcessor->core::Object ...core::Object ... ...core::Object->core::Object

Ancestors

interface Object

core :: Object

The root of the class hierarchy.

Parents

class StringProcessor

parser_base :: StringProcessor

Basic facilities for common parser operations on String sources

Class definitions

gamnit $ ObjFileParser
# Parser of .obj files in ASCII format
#
# Instantiate from a `String` and use `parse` to extract the `ObjDef`.
#
# ~~~
# var obj_src = """
# # Model of a cube
# mtllib material_file.mtl
# o Cube
# v 1.000000 0.000000 0.000000
# v 1.000000 0.000000 1.000000
# v 0.000000 0.000000 1.000000
# v 0.000000 0.000000 0.000000
# v 1.000000 1.000000 0.999999
# v 0.999999 1.000000 1.000001
# v 0.000000 1.000000 1.000000
# v 0.000000 1.000000 0.000000
# usemtl GreenMaterial
# s off
# f 1 2 3 4
# f 5 6 7 8
# f 1 5 8 2
# f 2 8 7 3
# f 3 7 6 4
# f 5 1 4 6
# """
#
# var parser = new ObjFileParser(obj_src)
# var parsed_obj = parser.parse
# assert parsed_obj.is_coherent
# assert parsed_obj.objects.first.name == "Cube"
# ~~~
class ObjFileParser
	super StringProcessor

	private var geometry = new ObjDef is lazy

	private var current_material_lib: nullable String = null

	private var current_material_name: nullable String = null

	# Execute parsing of `src` to extract an `ObjDef`
	fun parse: nullable ObjDef
	do
		var obj_obj = null
		while not eof do
			var token = read_token
			if token.is_empty or token == "#" then
				# Ignore empty lines and comments
			else if token == "v" then # Vertex points
				var vec = read_vec4
				geometry.vertex_points.add vec
			else if token == "vt" then # Texture coords
				var vec = read_vec3
				geometry.texture_coords.add vec
			else if token == "vn" then # Normals
				var vec = read_vec3 # This one should not accept `w` values
				geometry.normals.add vec
			else if token == "vp" then # Parameter space vertices
				var vec = read_vec3
				geometry.params.add vec
			else if token == "f" then # Faces
				var face = read_face
				if obj_obj == null then
					obj_obj = new ObjObj("")
					geometry.objects.add obj_obj
				end
				obj_obj.faces.add face
			else if token == "mtllib" then
				current_material_lib = read_until_eol_or_comment
			else if token == "usemtl" then
				current_material_name = read_until_eol_or_comment

			# TODO other line type headers
			else if token == "s" then
			else if token == "o" then
				obj_obj = new ObjObj(read_until_eol_or_comment)
				geometry.objects.add obj_obj
			else if token == "g" then
			end
			skip_eol
		end
		return geometry
	end

	private fun read_face: ObjFace
	do
		var face = new ObjFace(current_material_lib, current_material_name)

		loop
			var r = read_face_index_set(face)
			if not r then break
		end

		return face
	end

	private fun read_face_index_set(face: ObjFace): Bool
	do
		var token = read_token

		var parts = token.split('/')
		if parts.is_empty or parts.first.is_empty then return false

		var v = new ObjVertex
		for i in parts.length.times, part in parts do
			part = part.trim

			var n = null
			if not part.is_empty and part.is_numeric then n = part.to_i

			if i == 0 then
				n = n or else 0 # Error if n == null
				if n < 0 then n = geometry.vertex_points.length + n
				v.vertex_point_index = n
			else if i == 1 then
				if n != null and n < 0 then n = geometry.texture_coords.length + n
				v.texture_coord_index = n
			else if i == 2 then
				if n != null and n < 0 then n = geometry.normals.length + n
				v.normal_index = n
			else abort
		end
		face.vertices.add v

		return true
	end
end
lib/gamnit/model_parsers/obj.nit:20,1--147,3