| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | package iron.object; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if lnx_particles | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | import kha.FastFloat; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | import kha.graphics4.Usage; | 
					
						
							|  |  |  | import kha.arrays.Float32Array; | 
					
						
							|  |  |  | import iron.data.Data; | 
					
						
							|  |  |  | import iron.data.ParticleData; | 
					
						
							|  |  |  | import iron.data.SceneFormat; | 
					
						
							|  |  |  | import iron.system.Time; | 
					
						
							|  |  |  | import iron.math.Mat4; | 
					
						
							|  |  |  | import iron.math.Quat; | 
					
						
							|  |  |  | import iron.math.Vec3; | 
					
						
							|  |  |  | import iron.math.Vec4; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ParticleSystem { | 
					
						
							|  |  |  | 	public var data: ParticleData; | 
					
						
							|  |  |  | 	public var speed = 1.0; | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 	var currentSpeed = 0.0; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 	var particles: Array<Particle>; | 
					
						
							|  |  |  | 	var ready: Bool; | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 	var frameRate = 24; | 
					
						
							|  |  |  | 	var lifetime = 0.0; | 
					
						
							| 
									
										
										
										
											2025-06-02 16:24:22 +00:00
										 |  |  | 	var looptime = 0.0; | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 	var animtime = 0.0; | 
					
						
							|  |  |  | 	var time = 0.0; | 
					
						
							|  |  |  | 	var spawnRate = 0.0; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 	var seed = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 	var r: TParticleData; | 
					
						
							|  |  |  | 	var gx: Float; | 
					
						
							|  |  |  | 	var gy: Float; | 
					
						
							|  |  |  | 	var gz: Float; | 
					
						
							|  |  |  | 	var alignx: Float; | 
					
						
							|  |  |  | 	var aligny: Float; | 
					
						
							|  |  |  | 	var alignz: Float; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 	var dimx: Float; | 
					
						
							|  |  |  | 	var dimy: Float; | 
					
						
							|  |  |  | 	var tilesx: Int; | 
					
						
							|  |  |  | 	var tilesy: Int; | 
					
						
							|  |  |  | 	var tilesFramerate: Int; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 	var count = 0; | 
					
						
							|  |  |  | 	var lap = 0; | 
					
						
							|  |  |  | 	var lapTime = 0.0; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 	var m = Mat4.identity(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var ownerLoc = new Vec4(); | 
					
						
							|  |  |  | 	var ownerRot = new Quat(); | 
					
						
							|  |  |  | 	var ownerScl = new Vec4(); | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	var random = 0.0; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	public function new(sceneName: String, pref: TParticleReference) { | 
					
						
							|  |  |  | 		seed = pref.seed; | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 		currentSpeed = speed; | 
					
						
							|  |  |  | 		speed = 0; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 		particles = []; | 
					
						
							|  |  |  | 		ready = false; | 
					
						
							| 
									
										
										
										
											2025-05-13 17:58:05 +00:00
										 |  |  | 		 | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 		Data.getParticle(sceneName, pref.particle, function(b: ParticleData) { | 
					
						
							|  |  |  | 			data = b; | 
					
						
							|  |  |  | 			r = data.raw; | 
					
						
							|  |  |  | 			if (Scene.active.raw.gravity != null) { | 
					
						
							|  |  |  | 				gx = Scene.active.raw.gravity[0] * r.weight_gravity; | 
					
						
							|  |  |  | 				gy = Scene.active.raw.gravity[1] * r.weight_gravity; | 
					
						
							|  |  |  | 				gz = Scene.active.raw.gravity[2] * r.weight_gravity; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			else { | 
					
						
							|  |  |  | 				gx = 0; | 
					
						
							|  |  |  | 				gy = 0; | 
					
						
							|  |  |  | 				gz = -9.81 * r.weight_gravity; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 			alignx = r.object_align_factor[0]; | 
					
						
							|  |  |  | 			aligny = r.object_align_factor[1]; | 
					
						
							|  |  |  | 			alignz = r.object_align_factor[2]; | 
					
						
							|  |  |  | 			looptime = (r.frame_end - r.frame_start) / frameRate; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 			lifetime = r.lifetime / frameRate; | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 			animtime = r.loop ? looptime : looptime + lifetime; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 			spawnRate = ((r.frame_end - r.frame_start) / r.count) / frameRate; | 
					
						
							| 
									
										
										
										
											2025-05-13 17:58:05 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			for (i in 0...r.count) { | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 				particles.push(new Particle(i)); | 
					
						
							| 
									
										
										
										
											2025-05-13 17:58:05 +00:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 			ready = true; | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 			if (r.auto_start){ | 
					
						
							|  |  |  | 				start(); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 	public function start() { | 
					
						
							|  |  |  | 		if (r.is_unique) random = Math.random(); | 
					
						
							|  |  |  | 		lifetime = r.lifetime / frameRate; | 
					
						
							|  |  |  | 		time = 0; | 
					
						
							|  |  |  | 		lap = 0; | 
					
						
							|  |  |  | 		lapTime = 0; | 
					
						
							|  |  |  | 		speed = currentSpeed; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 	public function pause() { | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 		speed = 0; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public function resume() { | 
					
						
							|  |  |  | 		lifetime = r.lifetime / frameRate; | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 		speed = currentSpeed; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// TODO: interrupt smoothly | 
					
						
							|  |  |  | 	public function stop() { | 
					
						
							|  |  |  | 		end(); | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 	function end() { | 
					
						
							|  |  |  | 		lifetime = 0; | 
					
						
							|  |  |  | 		speed = 0; | 
					
						
							|  |  |  | 		lap = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 	public function update(object: MeshObject, owner: MeshObject) { | 
					
						
							|  |  |  | 		if (!ready || object == null || speed == 0.0) return; | 
					
						
							| 
									
										
										
										
											2025-06-02 16:24:22 +00:00
										 |  |  | 		if (iron.App.pauseUpdates) return; | 
					
						
							|  |  |  | 		 | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 		var prevLap = lap; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 		// Copy owner world transform but discard scale | 
					
						
							|  |  |  | 		owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl); | 
					
						
							|  |  |  | 		object.transform.loc = ownerLoc; | 
					
						
							|  |  |  | 		object.transform.rot = ownerRot; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Set particle size per particle system | 
					
						
							|  |  |  | 		object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		object.transform.buildMatrix(); | 
					
						
							|  |  |  | 		owner.transform.buildMatrix(); | 
					
						
							|  |  |  | 		object.transform.dim.setFrom(owner.transform.dim); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		dimx = object.transform.dim.x; | 
					
						
							|  |  |  | 		dimy = object.transform.dim.y; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (object.activeTilesheet != null) { | 
					
						
							|  |  |  | 			tilesx = object.activeTilesheet.raw.tilesx; | 
					
						
							|  |  |  | 			tilesy = object.activeTilesheet.raw.tilesy; | 
					
						
							|  |  |  | 			tilesFramerate = object.activeTilesheet.raw.framerate; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Animate | 
					
						
							| 
									
										
										
										
											2025-06-01 23:33:30 +00:00
										 |  |  | 		time += Time.renderDelta * speed; // realDelta to renderDelta | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 		lap = Std.int(time / animtime); | 
					
						
							|  |  |  | 		lapTime = time - lap * animtime; | 
					
						
							|  |  |  | 		count = Std.int(lapTime / spawnRate); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 		if (lap > prevLap && !r.loop) { | 
					
						
							|  |  |  | 			end(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		 | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 		updateGpu(object, owner); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public function getData(): Mat4 { | 
					
						
							|  |  |  | 		var hair = r.type == 1; | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 		m._00 = animtime; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 		m._01 = hair ? 1 / particles.length : spawnRate; | 
					
						
							|  |  |  | 		m._02 = hair ? 1 : lifetime; | 
					
						
							|  |  |  | 		m._03 = particles.length; | 
					
						
							|  |  |  | 		m._10 = hair ? 0 : alignx; | 
					
						
							|  |  |  | 		m._11 = hair ? 0 : aligny; | 
					
						
							|  |  |  | 		m._12 = hair ? 0 : alignz; | 
					
						
							|  |  |  | 		m._13 = hair ? 0 : r.factor_random; | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 		m._20 = hair ? 0 : gx; | 
					
						
							|  |  |  | 		m._21 = hair ? 0 : gy; | 
					
						
							|  |  |  | 		m._22 = hair ? 0 : gz; | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 		m._23 = hair ? 0 : r.lifetime_random; | 
					
						
							|  |  |  | 		m._30 = tilesx; | 
					
						
							|  |  |  | 		m._31 = tilesy; | 
					
						
							|  |  |  | 		m._32 = 1 / tilesFramerate; | 
					
						
							|  |  |  | 		m._33 = hair ? 1 : lapTime; | 
					
						
							|  |  |  | 		return m; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 	public function getSizeRandom(): FastFloat { | 
					
						
							|  |  |  | 		return r.size_random; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public function getRandom(): FastFloat { | 
					
						
							|  |  |  | 		return random; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public function getSize(): FastFloat { | 
					
						
							|  |  |  | 		return r.particle_size; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 	function updateGpu(object: MeshObject, owner: MeshObject) { | 
					
						
							|  |  |  | 		if (!object.data.geom.instanced) setupGeomGpu(object, owner); | 
					
						
							|  |  |  | 		// GPU particles transform is attached to owner object | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 	function setupGeomGpu(object: MeshObject, owner: MeshObject) { | 
					
						
							|  |  |  | 		var instancedData = new Float32Array(particles.length * 3); | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 		var i = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var normFactor = 1 / 32767; // pa.values are not normalized | 
					
						
							|  |  |  | 		var scalePosOwner = owner.data.scalePos; | 
					
						
							|  |  |  | 		var scalePosParticle = object.data.scalePos; | 
					
						
							|  |  |  | 		var particleSize = r.particle_size; | 
					
						
							|  |  |  | 		var scaleFactor = new Vec4().setFrom(owner.transform.scale); | 
					
						
							|  |  |  | 		scaleFactor.mult(scalePosOwner / (particleSize * scalePosParticle)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (r.emit_from) { | 
					
						
							|  |  |  | 			case 0: // Vert | 
					
						
							|  |  |  | 				var pa = owner.data.geom.positions; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				for (p in particles) { | 
					
						
							|  |  |  | 					var j = Std.int(fhash(i) * (pa.values.length / pa.size)); | 
					
						
							|  |  |  | 					instancedData.set(i, pa.values[j * pa.size    ] * normFactor * scaleFactor.x); i++; | 
					
						
							|  |  |  | 					instancedData.set(i, pa.values[j * pa.size + 1] * normFactor * scaleFactor.y); i++; | 
					
						
							|  |  |  | 					instancedData.set(i, pa.values[j * pa.size + 2] * normFactor * scaleFactor.z); i++; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			case 1: // Face | 
					
						
							|  |  |  | 				var positions = owner.data.geom.positions.values; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				for (p in particles) { | 
					
						
							|  |  |  | 					// Choose random index array (there is one per material) and random face | 
					
						
							|  |  |  | 					var ia = owner.data.geom.indices[Std.random(owner.data.geom.indices.length)]; | 
					
						
							|  |  |  | 					var faceIndex = Std.random(Std.int(ia.length / 3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					var i0 = ia[faceIndex * 3 + 0]; | 
					
						
							|  |  |  | 					var i1 = ia[faceIndex * 3 + 1]; | 
					
						
							|  |  |  | 					var i2 = ia[faceIndex * 3 + 2]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					var v0 = new Vec3(positions[i0 * 4], positions[i0 * 4 + 1], positions[i0 * 4 + 2]); | 
					
						
							|  |  |  | 					var v1 = new Vec3(positions[i1 * 4], positions[i1 * 4 + 1], positions[i1 * 4 + 2]); | 
					
						
							|  |  |  | 					var v2 = new Vec3(positions[i2 * 4], positions[i2 * 4 + 1], positions[i2 * 4 + 2]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					var pos = randomPointInTriangle(v0, v1, v2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					instancedData.set(i, pos.x * normFactor * scaleFactor.x); i++; | 
					
						
							|  |  |  | 					instancedData.set(i, pos.y * normFactor * scaleFactor.y); i++; | 
					
						
							|  |  |  | 					instancedData.set(i, pos.z * normFactor * scaleFactor.z); i++; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			case 2: // Volume | 
					
						
							|  |  |  | 				var scaleFactorVolume = new Vec4().setFrom(object.transform.dim); | 
					
						
							|  |  |  | 				scaleFactorVolume.mult(0.5 / (particleSize * scalePosParticle)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				for (p in particles) { | 
					
						
							|  |  |  | 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.x); i++; | 
					
						
							|  |  |  | 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.y); i++; | 
					
						
							|  |  |  | 					instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.z); i++; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 		object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage); | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	function fhash(n: Int): Float { | 
					
						
							|  |  |  | 		var s = n + 1.0; | 
					
						
							|  |  |  | 		s *= 9301.0 % s; | 
					
						
							|  |  |  | 		s = (s * 9301.0 + 49297.0) % 233280.0; | 
					
						
							|  |  |  | 		return s / 233280.0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public function remove() {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 		Generates a random point in the triangle with vertex positions abc. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Please note that the given position vectors are changed in-place by this | 
					
						
							|  |  |  | 		function and can be considered garbage afterwards, so make sure to clone | 
					
						
							|  |  |  | 		them first if needed. | 
					
						
							|  |  |  | 	**/ | 
					
						
							|  |  |  | 	public static inline function randomPointInTriangle(a: Vec3, b: Vec3, c: Vec3): Vec3 { | 
					
						
							|  |  |  | 		// Generate a random point in a square where (0, 0) <= (x, y) < (1, 1) | 
					
						
							|  |  |  | 		var x = Math.random(); | 
					
						
							|  |  |  | 		var y = Math.random(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (x + y > 1) { | 
					
						
							|  |  |  | 			// We're in the upper right triangle in the square, mirror to lower left | 
					
						
							|  |  |  | 			x = 1 - x; | 
					
						
							|  |  |  | 			y = 1 - y; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Transform the point to the triangle abc | 
					
						
							|  |  |  | 		var u = b.sub(a); | 
					
						
							|  |  |  | 		var v = c.sub(a); | 
					
						
							|  |  |  | 		return a.add(u.mult(x).add(v.mult(y))); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Particle { | 
					
						
							|  |  |  | 	public var i: Int; | 
					
						
							| 
									
										
										
										
											2025-06-01 21:51:09 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	public var x = 0.0; | 
					
						
							|  |  |  | 	public var y = 0.0; | 
					
						
							|  |  |  | 	public var z = 0.0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | 	public var cameraDistance: Float; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public function new(i: Int) { | 
					
						
							|  |  |  | 		this.i = i; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #end |