function ParticleGroup(options)
{
	// Ensure we have a map of options to play with
	options = ShaderUtils.ensureTypedArg(options, ShaderUtils.types.OBJECT, {});
	options.texture = ShaderUtils.ensureTypedArg(options.texture, ShaderUtils.types.OBJECT, {});

	// Assign a UUID to this instance
	this.uuid = TMath.generateUUID();

	// If no deltaTime value is passed to the ParticleGroup.tick function, the value of this property will be used to advance the simulation.
	this.fixedTimeStep = ShaderUtils.ensureTypedArg(options.fixedTimeStep, ShaderUtils.types.NUMBER, 0.016);

	// Set properties used in the uniforms map, starting with the texture stuff.
	this.texture = ShaderUtils.ensureInstanceOf(options.texture.value, Texture, null);
	this.textureFrames = ShaderUtils.ensureInstanceOf(options.texture.frames, Vector2, new Vector2(1, 1));
	this.textureFrameCount = ShaderUtils.ensureTypedArg(options.texture.frameCount, ShaderUtils.types.NUMBER, this.textureFrames.x * this.textureFrames.y);
	this.textureLoop = ShaderUtils.ensureTypedArg(options.texture.loop, ShaderUtils.types.NUMBER, 1);
	this.textureFrames.max(new Vector2(1, 1));

	this.hasPerspective = ShaderUtils.ensureTypedArg(options.hasPerspective, ShaderUtils.types.BOOLEAN, true);
	this.colorize = ShaderUtils.ensureTypedArg(options.colorize, ShaderUtils.types.BOOLEAN, true);
	this.maxParticleCount = ShaderUtils.ensureTypedArg(options.maxParticleCount, ShaderUtils.types.NUMBER, null);

	// Set properties used to define the ShaderMaterial"s appearance.
	this.blending = ShaderUtils.ensureTypedArg(options.blending, ShaderUtils.types.NUMBER, AdditiveBlending);
	this.transparent = ShaderUtils.ensureTypedArg(options.transparent, ShaderUtils.types.BOOLEAN, true);
	this.alphaTest = parseFloat(ShaderUtils.ensureTypedArg(options.alphaTest, ShaderUtils.types.NUMBER, 0.0));
	this.depthWrite = ShaderUtils.ensureTypedArg(options.depthWrite, ShaderUtils.types.BOOLEAN, false);
	this.depthTest = ShaderUtils.ensureTypedArg(options.depthTest, ShaderUtils.types.BOOLEAN, true);
	this.fog = ShaderUtils.ensureTypedArg(options.fog, ShaderUtils.types.BOOLEAN, true);
	this.scale = ShaderUtils.ensureTypedArg(options.scale, ShaderUtils.types.NUMBER, 300);

	this.emitters = [];
	this.emitterIDs = [];

	// Create properties for use by the emitter pooling functions.
	this.pool = [];
	this.poolCreationSettings = null;
	this._createNewWhenPoolEmpty = 0;

	// Whether all attributes should be forced to updated their entire buffer contents on the next tick.
	//
	// Used when an emitter is removed.
	this._attributesNeedRefresh = false;
	this._attributesNeedDynamicReset = false;

	this.particleCount = 0;

	/**
	 * Map of uniforms to be applied to the ShaderMaterial instance.
	 *
	 * @attribute uniforms
	 * @type {Object}
	 */
	this.uniforms =
	{
		textureSampler: {
			type: "t",
			value: this.texture
		},
		textureAnimation: {
			type: "v4",
			value: new Vector4(
				this.textureFrames.x,
				this.textureFrames.y,
				this.textureFrameCount,
				Math.max(Math.abs(this.textureLoop), 1.0)
		   )
		},
		fogColor: {
			type: "c",
			value: null
		},
		fogNear: {
			type: "f",
			value: 10
		},
		fogFar: {
			type: "f",
			value: 200
		},
		fogDensity: {
			type: "f",
			value: 0.5
		},
		deltaTime: {
			type: "f",
			value: 0
		},
		runTime: {
			type: "f",
			value: 0
		},
		scale: {
			type: "f",
			value: this.scale
		}
	};

	this.defines =
	{
		HAS_PERSPECTIVE: this.hasPerspective,
		COLORIZE: this.colorize,
		VALUE_OVER_LIFETIME_LENGTH: ParticleEmitter.valueOverLifetimeLength,
		SHOULD_ROTATE_TEXTURE: false,
		SHOULD_ROTATE_PARTICLES: false,
		SHOULD_WIGGLE_PARTICLES: false,
		SHOULD_CALCULATE_SPRITE: this.textureFrames.x > 1 || this.textureFrames.y > 1
	};

	/**
	 * Map of all attributes to be applied to the particles.
	 *
	 * See ShaderAttribute for a bit more info on this bit.
	 *
	 * @attribute attributes
	 * @type {Object}
	 */
	this.attributes =
	{
		position: new ShaderAttribute("v3", true),
		acceleration: new ShaderAttribute("v4", true), // w component is drag
		velocity: new ShaderAttribute("v3", true),
		rotation: new ShaderAttribute("v4", true),
		rotationCenter: new ShaderAttribute("v3", true),
		params: new ShaderAttribute("v4", true), // Holds (alive, age, delay, wiggle)
		size: new ShaderAttribute("v4", true),
		angle: new ShaderAttribute("v4", true),
		color: new ShaderAttribute("v4", true),
		opacity: new ShaderAttribute("v4", true)
	};

	this.attributeKeys = Object.keys(this.attributes);
	this.attributeCount = this.attributeKeys.length;

	this.material = new ShaderMaterial(
		{
			uniforms: this.uniforms,
			vertexShader: ParticleShaders.vertex,
			fragmentShader: ParticleShaders.fragment,
			blending: this.blending,
			transparent: this.transparent,
			alphaTest: this.alphaTest,
			depthWrite: this.depthWrite,
			depthTest: this.depthTest,
			defines: this.defines,
			fog: this.fog
		});

	this.geometry = new BufferGeometry();
	this.mesh = new Points(this.geometry, this.material);

	if (this.maxParticleCount === null)
	{
		console.warn("nunuStudio: No maxParticleCount specified. Adding emitters after rendering will probably cause errors.");
	}
}
The ParticleGroup class. Creates a new group, containing a material, geometry, and mesh.

@constructor
@class ParticleGroup
@param {ParticleGroupOptions} options A map of options to configure the group instance.