Short-lived service to convert an ObjDef to fill_leaves

Limitations: This service only support faces with 3 or 4 vertices. Faces with more vertices should be triangulated by the modeling tool.

Introduced properties

private var _errors: Array[Error]

gamnit :: BuildModelFromObj :: _errors

Errors raised by calls to fill_leaves
private var _obj_def: ObjDef

gamnit :: BuildModelFromObj :: _obj_def

Parsed .obj definition
private var _path: String

gamnit :: BuildModelFromObj :: _path

Path to the .obj file in the assets folder, used to find .mtl files
private fun compute_and_append_normal(seq: Sequence[Float], face: ObjFace)

gamnit :: BuildModelFromObj :: compute_and_append_normal

Compute the normal of face and append it as 3 floats to seq
private fun errors: Array[Error]

gamnit :: BuildModelFromObj :: errors

Errors raised by calls to fill_leaves
private fun errors=(errors: Array[Error])

gamnit :: BuildModelFromObj :: errors=

Errors raised by calls to fill_leaves
private fun fill_leaves(target_model: ModelAsset)

gamnit :: BuildModelFromObj :: fill_leaves

Fill leaves with objects described in obj_def
private fun normals(faces: Array[ObjFace]): Array[Float]

gamnit :: BuildModelFromObj :: normals

Compute the normals of faces in a flat Array[Float]
private fun obj_def: ObjDef

gamnit :: BuildModelFromObj :: obj_def

Parsed .obj definition
private fun obj_def=(obj_def: ObjDef)

gamnit :: BuildModelFromObj :: obj_def=

Parsed .obj definition
private fun path: String

gamnit :: BuildModelFromObj :: path

Path to the .obj file in the assets folder, used to find .mtl files
private fun path=(path: String)

gamnit :: BuildModelFromObj :: path=

Path to the .obj file in the assets folder, used to find .mtl files
private fun texture_coords(faces: Array[ObjFace]): Array[Float]

gamnit :: BuildModelFromObj :: texture_coords

Compute the texture coordinates of faces in a flat Array[Float]
private fun vertices(faces: Array[ObjFace]): Array[Float]

gamnit :: BuildModelFromObj :: vertices

Compute the vertices coordinates of faces in a flat Array[Float]

Redefined properties

redef type SELF: BuildModelFromObj

gamnit $ BuildModelFromObj :: 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
private var _errors: Array[Error]

gamnit :: BuildModelFromObj :: _errors

Errors raised by calls to fill_leaves
private var _obj_def: ObjDef

gamnit :: BuildModelFromObj :: _obj_def

Parsed .obj definition
private var _path: String

gamnit :: BuildModelFromObj :: _path

Path to the .obj file in the assets folder, used to find .mtl files
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.
private fun compute_and_append_normal(seq: Sequence[Float], face: ObjFace)

gamnit :: BuildModelFromObj :: compute_and_append_normal

Compute the normal of face and append it as 3 floats to seq
private fun errors: Array[Error]

gamnit :: BuildModelFromObj :: errors

Errors raised by calls to fill_leaves
private fun errors=(errors: Array[Error])

gamnit :: BuildModelFromObj :: errors=

Errors raised by calls to fill_leaves
private fun fill_leaves(target_model: ModelAsset)

gamnit :: BuildModelFromObj :: fill_leaves

Fill leaves with objects described in obj_def
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.
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.
private intern fun native_class_name: CString

core :: Object :: native_class_name

The class name of the object in CString format.
private fun normals(faces: Array[ObjFace]): Array[Float]

gamnit :: BuildModelFromObj :: normals

Compute the normals of faces in a flat Array[Float]
private fun obj_def: ObjDef

gamnit :: BuildModelFromObj :: obj_def

Parsed .obj definition
private fun obj_def=(obj_def: ObjDef)

gamnit :: BuildModelFromObj :: obj_def=

Parsed .obj definition
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).
private fun path: String

gamnit :: BuildModelFromObj :: path

Path to the .obj file in the assets folder, used to find .mtl files
private fun path=(path: String)

gamnit :: BuildModelFromObj :: path=

Path to the .obj file in the assets folder, used to find .mtl files
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
private fun texture_coords(faces: Array[ObjFace]): Array[Float]

gamnit :: BuildModelFromObj :: texture_coords

Compute the texture coordinates of faces in a flat Array[Float]
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_s: String

core :: Object :: to_s

User readable representation of self.
private fun vertices(faces: Array[ObjFace]): Array[Float]

gamnit :: BuildModelFromObj :: vertices

Compute the vertices coordinates of faces in a flat Array[Float]
package_diagram gamnit::more_models::BuildModelFromObj BuildModelFromObj core::Object Object gamnit::more_models::BuildModelFromObj->core::Object

Parents

interface Object

core :: Object

The root of the class hierarchy.

Class definitions

gamnit $ BuildModelFromObj
# Short-lived service to convert an `ObjDef` to `fill_leaves`
#
# Limitations: This service only support faces with 3 or 4 vertices.
# Faces with more vertices should be triangulated by the modeling tool.
private class BuildModelFromObj

	# Path to the .obj file in the assets folder, used to find .mtl files
	var path: String

	# Parsed .obj definition
	var obj_def: ObjDef

	# Errors raised by calls to `fill_leaves`
	var errors = new Array[Error]

	# Fill `leaves` with objects described in `obj_def`
	fun fill_leaves(target_model: ModelAsset)
	do
		var leaves = target_model.leaves_cache

		# Sort faces by material
		var obj_mtl_to_faces = new Map[ObjObj, MultiHashMap[String, ObjFace]]
		for obj in obj_def.objects do
			var mtl_to_faces = new MultiHashMap[String, ObjFace]
			obj_mtl_to_faces[obj] = mtl_to_faces
			for face in obj.faces do
				var mtl_lib_name = face.material_lib
				var mtl_name = face.material_name

				var full_name = ""
				if mtl_lib_name != null and mtl_name != null then full_name = mtl_lib_name / mtl_name

				mtl_to_faces[full_name].add face
			end
		end

		# Load material libs
		var mtl_libs = sys.mtl_libs
		var lib_names = obj_def.material_libs
		for name in lib_names do
			var asset_path = self.path.dirname / name
			var lib_asset = new TextAsset(asset_path)
			lib_asset.load

			var error = lib_asset.error
			if error != null then
				errors.add error
				continue
			end

			var mtl_parser = new MtlFileParser(lib_asset.to_s)
			var mtl_lib = mtl_parser.parse
			mtl_libs[asset_path] = mtl_lib
		end

		# Create 1 mesh per material per object, and prepare materials
		var mesh_to_mtl = new Map[Mesh, nullable MtlDef]
		var mesh_to_name = new Map[Mesh, String]
		var texture_names = new Set[String]
		for obj in obj_def.objects do
			var mtl_to_faces = obj_mtl_to_faces[obj]
			for mtl_path, faces in mtl_to_faces do

				# Create mesh
				var mesh = new Mesh
				mesh.vertices = vertices(faces)
				mesh.normals = normals(faces)
				mesh.texture_coords = texture_coords(faces)

				# Material
				var mtl_def = null

				var mtl_lib_name = faces.first.material_lib
				var mtl_name = faces.first.material_name
				if mtl_lib_name != null and mtl_name != null then
					var asset_path = self.path.dirname / mtl_lib_name
					var mtl_lib = mtl_libs[asset_path]
					var mtl = mtl_lib.get_or_null(mtl_name)
					if mtl != null then
						mtl_def = mtl

						for e in mtl.maps do
							texture_names.add self.path.dirname / e
						end
					else
						errors.add new Error("Error loading model at '{path}': mtl '{mtl_name}' not found in '{asset_path}'")
					end
				end

				mesh_to_mtl[mesh] = mtl_def
				mesh_to_name[mesh] = obj.name
			end
		end

		# Load textures need for these materials
		for name in texture_names do
			if not asset_textures_by_name.keys.has(name) then
				var tex = new TextureAsset(name)
				asset_textures_by_name[name] = tex

				tex.load
				var error = tex.error
				if error != null then errors.add error
			end
		end

		# Create final `Materials` from defs and textures
		var materials = new Map[MtlDef, Material]
		for mtl in mesh_to_mtl.values do
			if mtl == null then continue

			var ambient = mtl.ambient.to_a
			ambient.add 1.0

			var diffuse = mtl.diffuse.to_a
			diffuse.add 1.0

			var specular = mtl.specular.to_a
			specular.add 1.0

			var material = new TexturedMaterial(ambient, diffuse, specular)
			materials[mtl] = material

			var tex_name = mtl.map_ambient
			if tex_name != null then
				tex_name = self.path.dirname / tex_name
				material.ambient_texture = asset_textures_by_name[tex_name]
			end

			tex_name = mtl.map_diffuse
			if tex_name != null then
				tex_name = self.path.dirname / tex_name
				material.diffuse_texture = asset_textures_by_name[tex_name]
			end

			tex_name = mtl.map_specular
			if tex_name != null then
				tex_name = self.path.dirname / tex_name
				material.specular_texture = asset_textures_by_name[tex_name]
			end
		end

		# Create models and store them
		var name_to_leaves = new MultiHashMap[String, LeafModel]
		for mesh, mtl_def in mesh_to_mtl do

			var material = materials.get_or_null(mtl_def)
			if material == null then material = new Material

			var model = new LeafModel(mesh, material)
			leaves.add model

			name_to_leaves[mesh_to_name[mesh]].add model
		end

		# Collect objects with a name
		for name, models in name_to_leaves do
			if models.length == 1 then
				target_model.named_leaves_cache[name] = models.first
			else
				var named_model = new CompositeModel
				named_model.leaves.add_all models
				target_model.named_leaves_cache[name] = named_model
			end
		end
	end

	# Compute the vertices coordinates of `faces` in a flat `Array[Float]`
	fun vertices(faces: Array[ObjFace]): Array[Float] do
		var obj_def = obj_def

		var vertices = new Array[Float]
		for face in faces do

			# 1st triangle
			var count = 0
			for e in face.vertices do
				var i = e.vertex_point_index - 1
				var v = obj_def.vertex_points[i]

				vertices.add v.x
				vertices.add v.y
				vertices.add v.z

				if count == 2 then break
				count += 1
			end

			# If square, 2nd triangle
			#
			# This may not support all vertices ordering.
			if face.vertices.length > 3 then
				for e in [face.vertices[0], face.vertices[2], face.vertices[3]] do
					var i = e.vertex_point_index - 1
					var v = obj_def.vertex_points[i]

					vertices.add v.x
					vertices.add v.y
					vertices.add v.z
				end
			end

			# TODO use polygon triangulation to support larger polygons
		end
		return vertices
	end

	# Compute the normals of `faces` in a flat `Array[Float]`
	fun normals(faces: Array[ObjFace]): Array[Float] do
		var obj_def = obj_def

		var normals = new Array[Float]
		for face in faces do
			# 1st triangle
			var count = 0
			for e in face.vertices do
				var i = e.normal_index
				if i == null then
					compute_and_append_normal(normals, face)
				else
					var v = obj_def.normals[i-1]
					normals.add v.x
					normals.add v.y
					normals.add v.z
				end

				if count == 2 then break
				count += 1
			end

			# If square, 2nd triangle
			#
			# This may not support all vertices ordering.
			if face.vertices.length > 3 then
				for e in [face.vertices[0], face.vertices[2], face.vertices[3]] do
					var i = e.normal_index
					if i == null then
						compute_and_append_normal(normals, face)
					else
						var v = obj_def.normals[i-1]
						normals.add v.x
						normals.add v.y
						normals.add v.z
					end
				end
			end
		end
		return normals
	end

	# Compute the normal of `face` and append it as 3 floats to `seq`
	#
	# Resulting normals are not normalized.
	fun compute_and_append_normal(seq: Sequence[Float], face: ObjFace)
	do
		var i1 = face.vertices[0].vertex_point_index
		var i2 = face.vertices[1].vertex_point_index
		var i3 = face.vertices[2].vertex_point_index

		var v1 = obj_def.vertex_points[i1-1]
		var v2 = obj_def.vertex_points[i2-1]
		var v3 = obj_def.vertex_points[i3-1]

		var vx = v2.x - v1.x
		var vy = v2.y - v1.y
		var vz = v2.z - v1.z
		var wx = v3.x - v1.x
		var wy = v3.y - v1.y
		var wz = v3.z - v1.z

		var nx = (vy*wz) - (vz*wy)
		var ny = (vz*wx) - (vx*wz)
		var nz = (vx*wy) - (vy*wx)

		# Append to `seq`
		seq.add nx
		seq.add ny
		seq.add nz
	end

	# Compute the texture coordinates of `faces` in a flat `Array[Float]`
	fun texture_coords(faces: Array[ObjFace]): Array[Float] do
		var obj_def = obj_def

		var coords = new Array[Float]
		for face in faces do

			# 1st triangle
			var count = 0
			for e in face.vertices do
				var i = e.texture_coord_index
				if i == null then
					coords.add 0.0
					coords.add 0.0
				else
					var tc = obj_def.texture_coords[i-1]
					coords.add tc.u
					coords.add tc.v
				end

				if count == 2 then break
				count += 1
			end

			# If square, 2nd triangle
			#
			# This may not support all vertices ordering.
			if face.vertices.length > 3 then
				for e in [face.vertices[0], face.vertices[2], face.vertices[3]] do
					var i = e.texture_coord_index
					if i == null then
						coords.add 0.0
						coords.add 0.0
					else
					var tc = obj_def.texture_coords[i-1]
					coords.add tc.u
					coords.add tc.v
					end
				end
			end
		end
		return coords
	end
end
lib/gamnit/depth/more_models.nit:126,1--449,3