Browse Source

Particle system is complete. Rejoice!

Juan Linietsky 8 years ago
parent
commit
4286aef693

+ 11 - 10
core/math/geometry.h

@@ -105,23 +105,23 @@ public:
 	}
 
 	static void get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2, Vector3 &c1, Vector3 &c2) {
-#if 0
-		//do the function 'd' as defined by pb. I think is is dot product of some sort
+#if 1
+//do the function 'd' as defined by pb. I think is is dot product of some sort
 #define d_of(m, n, o, p) ((m.x - n.x) * (o.x - p.x) + (m.y - n.y) * (o.y - p.y) + (m.z - n.z) * (o.z - p.z))
 
 		//calculate the parametric position on the 2 curves, mua and mub
-		real_t mua = ( d_of(p1,q1,q2,q1) * d_of(q2,q1,p2,p1)  - d_of(p1,q1,p2,p1) * d_of(q2,q1,q2,q1) ) / ( d_of(p2,p1,p2,p1) * d_of(q2,q1,q2,q1)  - d_of(q2,q1,p2,p1) * d_of(q2,q1,p2,p1) );
-		real_t mub = ( d_of(p1,q1,q2,q1) + mua * d_of(q2,q1,p2,p1)  ) / d_of(q2,q1,q2,q1);
+		real_t mua = (d_of(p1, q1, q2, q1) * d_of(q2, q1, p2, p1) - d_of(p1, q1, p2, p1) * d_of(q2, q1, q2, q1)) / (d_of(p2, p1, p2, p1) * d_of(q2, q1, q2, q1) - d_of(q2, q1, p2, p1) * d_of(q2, q1, p2, p1));
+		real_t mub = (d_of(p1, q1, q2, q1) + mua * d_of(q2, q1, p2, p1)) / d_of(q2, q1, q2, q1);
 
 		//clip the value between [0..1] constraining the solution to lie on the original curves
 		if (mua < 0) mua = 0;
 		if (mub < 0) mub = 0;
 		if (mua > 1) mua = 1;
 		if (mub > 1) mub = 1;
-		c1 = p1.linear_interpolate(p2,mua);
-		c2 = q1.linear_interpolate(q2,mub);
-#endif
-
+		c1 = p1.linear_interpolate(p2, mua);
+		c2 = q1.linear_interpolate(q2, mub);
+#else
+		//this is broken do not use
 		Vector3 u = p2 - p1;
 		Vector3 v = q2 - q1;
 		Vector3 w = p1 - q1;
@@ -144,8 +144,9 @@ public:
 
 		c1 = w + sc * u;
 		c2 = w + tc * v;
-		// get the difference of the two closest points
-		//Vector   dP = w + (sc * u) - (tc * v);  // =  L1(sc) - L2(tc)
+// get the difference of the two closest points
+//Vector   dP = w + (sc * u) - (tc * v);  // =  L1(sc) - L2(tc)
+#endif
 	}
 
 	static real_t get_closest_distance_between_segments(const Vector3 &p_from_a, const Vector3 &p_to_a, const Vector3 &p_from_b, const Vector3 &p_to_b) {

+ 140 - 24
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -1216,7 +1216,26 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m
 	return rebind;
 }
 
-void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e) {
+struct RasterizerGLES3Particle {
+
+	float color[4];
+	float velocity_active[4];
+	float custom[4];
+	float xform_1[4];
+	float xform_2[4];
+	float xform_3[4];
+};
+
+struct RasterizerGLES3ParticleSort {
+
+	Vector3 z_dir;
+	bool operator()(const RasterizerGLES3Particle &p_a, const RasterizerGLES3Particle &p_b) const {
+
+		return z_dir.dot(Vector3(p_a.xform_1[3], p_a.xform_2[3], p_a.xform_3[3])) < z_dir.dot(Vector3(p_b.xform_1[3], p_b.xform_2[3], p_b.xform_3[3]));
+	}
+};
+
+void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transform &p_view_transform) {
 
 	switch (e->instance->base_type) {
 
@@ -1289,28 +1308,54 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e) {
 			RasterizerStorageGLES3::Particles *particles = static_cast<RasterizerStorageGLES3::Particles *>(e->owner);
 			RasterizerStorageGLES3::Surface *s = static_cast<RasterizerStorageGLES3::Surface *>(e->geometry);
 
-			glBindVertexArray(s->instancing_array_id); // use the instancing array ID
-			glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); //modify the buffer
+			if (particles->draw_order == VS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->particle_valid_histories[1]) {
+
+				glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffer_histories[1]); //modify the buffer, this was used 2 frames ago so it should be good enough for flushing
+				RasterizerGLES3Particle *particle_array = (RasterizerGLES3Particle *)glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * 24 * sizeof(float), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT);
+
+				SortArray<RasterizerGLES3Particle, RasterizerGLES3ParticleSort> sorter;
+
+				if (particles->use_local_coords) {
+					sorter.compare.z_dir = e->instance->transform.affine_inverse().xform(p_view_transform.basis.get_axis(2)).normalized();
+				} else {
+					sorter.compare.z_dir = p_view_transform.basis.get_axis(2).normalized();
+				}
+
+				sorter.sort(particle_array, particles->amount);
+
+				glUnmapBuffer(GL_ARRAY_BUFFER);
+
+				glBindVertexArray(s->instancing_array_id); // use the instancing array ID
+				glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffer_histories[1]); //modify the buffer
+
+			} else {
+
+				glBindVertexArray(s->instancing_array_id); // use the instancing array ID
+				glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); //modify the buffer
+			}
 
 			int stride = sizeof(float) * 4 * 6;
 
 			//transform
 
-			glEnableVertexAttribArray(8); //xform x
-			glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 3);
-			glVertexAttribDivisor(8, 1);
-			glEnableVertexAttribArray(9); //xform y
-			glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 4);
-			glVertexAttribDivisor(9, 1);
-			glEnableVertexAttribArray(10); //xform z
-			glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 5);
-			glVertexAttribDivisor(10, 1);
-			glEnableVertexAttribArray(11); //color
-			glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + 0);
-			glVertexAttribDivisor(11, 1);
-			glEnableVertexAttribArray(12); //custom
-			glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 2);
-			glVertexAttribDivisor(12, 1);
+			if (particles->draw_order != VS::PARTICLES_DRAW_ORDER_LIFETIME) {
+
+				glEnableVertexAttribArray(8); //xform x
+				glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 3);
+				glVertexAttribDivisor(8, 1);
+				glEnableVertexAttribArray(9); //xform y
+				glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 4);
+				glVertexAttribDivisor(9, 1);
+				glEnableVertexAttribArray(10); //xform z
+				glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 5);
+				glVertexAttribDivisor(10, 1);
+				glEnableVertexAttribArray(11); //color
+				glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + 0);
+				glVertexAttribDivisor(11, 1);
+				glEnableVertexAttribArray(12); //custom
+				glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 2);
+				glVertexAttribDivisor(12, 1);
+			}
 
 		} break;
 	}
@@ -1491,17 +1536,88 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) {
 
 			int amount = particles->amount;
 
-			if (s->index_array_len > 0) {
+			if (particles->draw_order == VS::PARTICLES_DRAW_ORDER_LIFETIME) {
+				//split
+
+				int stride = sizeof(float) * 4 * 6;
+				int split = int(Math::ceil(particles->phase * particles->amount));
+
+				if (amount - split > 0) {
+					glEnableVertexAttribArray(8); //xform x
+					glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 3);
+					glVertexAttribDivisor(8, 1);
+					glEnableVertexAttribArray(9); //xform y
+					glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 4);
+					glVertexAttribDivisor(9, 1);
+					glEnableVertexAttribArray(10); //xform z
+					glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 5);
+					glVertexAttribDivisor(10, 1);
+					glEnableVertexAttribArray(11); //color
+					glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + 0);
+					glVertexAttribDivisor(11, 1);
+					glEnableVertexAttribArray(12); //custom
+					glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 2);
+					glVertexAttribDivisor(12, 1);
 
-				glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, amount);
+					if (s->index_array_len > 0) {
 
-				storage->info.render_vertices_count += s->index_array_len * amount;
+						glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, amount - split);
+
+						storage->info.render_vertices_count += s->index_array_len * (amount - split);
+
+					} else {
+
+						glDrawArraysInstanced(gl_primitive[s->primitive], 0, s->array_len, amount - split);
+
+						storage->info.render_vertices_count += s->array_len * (amount - split);
+					}
+				}
+
+				if (split > 0) {
+					glEnableVertexAttribArray(8); //xform x
+					glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 3);
+					glVertexAttribDivisor(8, 1);
+					glEnableVertexAttribArray(9); //xform y
+					glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 4);
+					glVertexAttribDivisor(9, 1);
+					glEnableVertexAttribArray(10); //xform z
+					glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 5);
+					glVertexAttribDivisor(10, 1);
+					glEnableVertexAttribArray(11); //color
+					glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + 0);
+					glVertexAttribDivisor(11, 1);
+					glEnableVertexAttribArray(12); //custom
+					glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 2);
+					glVertexAttribDivisor(12, 1);
+
+					if (s->index_array_len > 0) {
+
+						glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, split);
+
+						storage->info.render_vertices_count += s->index_array_len * split;
+
+					} else {
+
+						glDrawArraysInstanced(gl_primitive[s->primitive], 0, s->array_len, split);
+
+						storage->info.render_vertices_count += s->array_len * split;
+					}
+				}
 
 			} else {
 
-				glDrawArraysInstanced(gl_primitive[s->primitive], 0, s->array_len, amount);
+				if (s->index_array_len > 0) {
 
-				storage->info.render_vertices_count += s->array_len * amount;
+					glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, amount);
+
+					storage->info.render_vertices_count += s->index_array_len * amount;
+
+				} else {
+
+					glDrawArraysInstanced(gl_primitive[s->primitive], 0, s->array_len, amount);
+
+					storage->info.render_vertices_count += s->array_len * amount;
+				}
 			}
 
 		} break;
@@ -1841,7 +1957,7 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
 
 		if (e->owner != prev_owner || prev_base_type != e->instance->base_type || prev_geometry != e->geometry) {
 
-			_setup_geometry(e);
+			_setup_geometry(e, p_view_transform);
 			storage->info.render_surface_switch_count++;
 		}
 

+ 1 - 1
drivers/gles3/rasterizer_scene_gles3.h

@@ -692,7 +692,7 @@ public:
 	_FORCE_INLINE_ void _set_cull(bool p_front, bool p_reverse_cull);
 
 	_FORCE_INLINE_ bool _setup_material(RasterizerStorageGLES3::Material *p_material, bool p_alpha_pass);
-	_FORCE_INLINE_ void _setup_geometry(RenderList::Element *e);
+	_FORCE_INLINE_ void _setup_geometry(RenderList::Element *e, const Transform &p_view_transform);
 	_FORCE_INLINE_ void _render_geometry(RenderList::Element *e);
 	_FORCE_INLINE_ void _setup_light(RenderList::Element *e, const Transform &p_view_transform);
 

+ 113 - 7
drivers/gles3/rasterizer_storage_gles3.cpp

@@ -3005,6 +3005,7 @@ void RasterizerStorageGLES3::mesh_set_custom_aabb(RID p_mesh, const Rect3 &p_aab
 
 	mesh->custom_aabb = p_aabb;
 }
+
 Rect3 RasterizerStorageGLES3::mesh_get_custom_aabb(RID p_mesh) const {
 
 	const Mesh *mesh = mesh_owner.getornull(p_mesh);
@@ -4883,6 +4884,22 @@ void RasterizerStorageGLES3::particles_set_amount(RID p_particles, int p_amount)
 		}
 	}
 
+	if (particles->histories_enabled) {
+
+		for (int i = 0; i < 2; i++) {
+			glBindVertexArray(particles->particle_vao_histories[i]);
+
+			glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffer_histories[i]);
+			glBufferData(GL_ARRAY_BUFFER, floats * sizeof(float), data, GL_DYNAMIC_COPY);
+
+			for (int j = 0; j < 6; j++) {
+				glEnableVertexAttribArray(j);
+				glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4 * 6, ((uint8_t *)0) + (j * 16));
+			}
+			particles->particle_valid_histories[i] = false;
+		}
+	}
+
 	glBindVertexArray(0);
 
 	particles->prev_ticks = 0;
@@ -4917,18 +4934,61 @@ void RasterizerStorageGLES3::particles_set_randomness_ratio(RID p_particles, flo
 	ERR_FAIL_COND(!particles);
 	particles->randomness = p_ratio;
 }
+
+void RasterizerStorageGLES3::_particles_update_histories(Particles *particles) {
+
+	bool needs_histories = particles->draw_order == VS::PARTICLES_DRAW_ORDER_VIEW_DEPTH;
+
+	if (needs_histories == particles->histories_enabled)
+		return;
+
+	particles->histories_enabled = needs_histories;
+
+	int floats = particles->amount * 24;
+
+	if (!needs_histories) {
+
+		glDeleteBuffers(2, particles->particle_buffer_histories);
+		glDeleteVertexArrays(2, particles->particle_vao_histories);
+
+	} else {
+
+		glGenBuffers(2, particles->particle_buffer_histories);
+		glGenVertexArrays(2, particles->particle_vao_histories);
+
+		for (int i = 0; i < 2; i++) {
+			glBindVertexArray(particles->particle_vao_histories[i]);
+
+			glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffer_histories[i]);
+			glBufferData(GL_ARRAY_BUFFER, floats * sizeof(float), NULL, GL_DYNAMIC_COPY);
+
+			for (int j = 0; j < 6; j++) {
+				glEnableVertexAttribArray(j);
+				glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4 * 6, ((uint8_t *)0) + (j * 16));
+			}
+
+			particles->particle_valid_histories[i] = false;
+		}
+	}
+
+	particles->clear = true;
+}
+
 void RasterizerStorageGLES3::particles_set_custom_aabb(RID p_particles, const Rect3 &p_aabb) {
 
 	Particles *particles = particles_owner.getornull(p_particles);
 	ERR_FAIL_COND(!particles);
 	particles->custom_aabb = p_aabb;
+	_particles_update_histories(particles);
+	particles->instance_change_notify();
 }
-void RasterizerStorageGLES3::particles_set_gravity(RID p_particles, const Vector3 &p_gravity) {
+
+void RasterizerStorageGLES3::particles_set_speed_scale(RID p_particles, float p_scale) {
 
 	Particles *particles = particles_owner.getornull(p_particles);
 	ERR_FAIL_COND(!particles);
 
-	particles->gravity = p_gravity;
+	particles->speed_scale = p_scale;
 }
 void RasterizerStorageGLES3::particles_set_use_local_coordinates(RID p_particles, bool p_enable) {
 
@@ -4968,6 +5028,7 @@ void RasterizerStorageGLES3::particles_set_draw_order(RID p_particles, VS::Parti
 	ERR_FAIL_COND(!particles);
 
 	particles->draw_order = p_order;
+	_particles_update_histories(particles);
 }
 
 void RasterizerStorageGLES3::particles_set_draw_passes(RID p_particles, int p_passes) {
@@ -5001,7 +5062,39 @@ Rect3 RasterizerStorageGLES3::particles_get_current_aabb(RID p_particles) {
 	const Particles *particles = particles_owner.getornull(p_particles);
 	ERR_FAIL_COND_V(!particles, Rect3());
 
-	return particles->computed_aabb;
+	glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]);
+
+	float *data = (float *)glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * 16 * 6, GL_MAP_READ_BIT);
+	Rect3 aabb;
+
+	Transform inv = particles->emission_transform.affine_inverse();
+
+	for (int i = 0; i < particles->amount; i++) {
+		int ofs = i * 24;
+		Vector3 pos = Vector3(data[ofs + 15], data[ofs + 19], data[ofs + 23]);
+		if (!particles->use_local_coords) {
+			pos = inv.xform(pos);
+		}
+		if (i == 0)
+			aabb.pos = pos;
+		else
+			aabb.expand_to(pos);
+	}
+
+	glUnmapBuffer(GL_ARRAY_BUFFER);
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+	float longest_axis = 0;
+	for (int i = 0; i < particles->draw_passes.size(); i++) {
+		if (particles->draw_passes[i].is_valid()) {
+			Rect3 maabb = mesh_get_aabb(particles->draw_passes[i], RID());
+			longest_axis = MAX(maabb.get_longest_axis_size(), longest_axis);
+		}
+	}
+
+	aabb.grow_by(longest_axis);
+
+	return aabb;
 }
 
 Rect3 RasterizerStorageGLES3::particles_get_aabb(RID p_particles) const {
@@ -5009,7 +5102,7 @@ Rect3 RasterizerStorageGLES3::particles_get_aabb(RID p_particles) const {
 	const Particles *particles = particles_owner.getornull(p_particles);
 	ERR_FAIL_COND_V(!particles, Rect3());
 
-	return Rect3(Vector3(-1, -1, -1), Vector3(2, 2, 2));
+	return particles->custom_aabb;
 }
 
 void RasterizerStorageGLES3::particles_set_emission_transform(RID p_particles, const Transform &p_transform) {
@@ -5022,7 +5115,7 @@ void RasterizerStorageGLES3::particles_set_emission_transform(RID p_particles, c
 
 void RasterizerStorageGLES3::_particles_process(Particles *particles, float p_delta) {
 
-	float new_phase = Math::fmod((float)particles->phase + (p_delta / particles->lifetime), (float)1.0);
+	float new_phase = Math::fmod((float)particles->phase + (p_delta / particles->lifetime) * particles->speed_scale, (float)1.0);
 
 	if (particles->clear) {
 		particles->cycle_number = 0;
@@ -5034,7 +5127,7 @@ void RasterizerStorageGLES3::_particles_process(Particles *particles, float p_de
 	shaders.particles.set_uniform(ParticlesShaderGLES3::PREV_SYSTEM_PHASE, particles->phase);
 	particles->phase = new_phase;
 
-	shaders.particles.set_uniform(ParticlesShaderGLES3::DELTA, p_delta);
+	shaders.particles.set_uniform(ParticlesShaderGLES3::DELTA, p_delta * particles->speed_scale);
 	shaders.particles.set_uniform(ParticlesShaderGLES3::CLEAR, particles->clear);
 	if (particles->use_local_coords)
 		shaders.particles.set_uniform(ParticlesShaderGLES3::EMISSION_TRANSFORM, Transform());
@@ -5154,7 +5247,6 @@ void RasterizerStorageGLES3::update_particles() {
 		shaders.particles.set_uniform(ParticlesShaderGLES3::TIME, Color(frame.time[0], frame.time[1], frame.time[2], frame.time[3]));
 		shaders.particles.set_uniform(ParticlesShaderGLES3::EXPLOSIVENESS, particles->explosiveness);
 		shaders.particles.set_uniform(ParticlesShaderGLES3::LIFETIME, particles->lifetime);
-		shaders.particles.set_uniform(ParticlesShaderGLES3::GRAVITY, particles->gravity);
 		shaders.particles.set_uniform(ParticlesShaderGLES3::ATTRACTOR_COUNT, 0);
 		shaders.particles.set_uniform(ParticlesShaderGLES3::EMITTING, particles->emitting);
 		shaders.particles.set_uniform(ParticlesShaderGLES3::RANDOMNESS, particles->randomness);
@@ -5201,6 +5293,20 @@ void RasterizerStorageGLES3::update_particles() {
 		}
 
 		particle_update_list.remove(particle_update_list.first());
+
+		if (particles->histories_enabled) {
+
+			SWAP(particles->particle_buffer_histories[0], particles->particle_buffer_histories[1]);
+			SWAP(particles->particle_vao_histories[0], particles->particle_vao_histories[1]);
+			SWAP(particles->particle_valid_histories[0], particles->particle_valid_histories[1]);
+
+			//copy
+			glBindBuffer(GL_COPY_READ_BUFFER, particles->particle_buffers[0]);
+			glBindBuffer(GL_COPY_WRITE_BUFFER, particles->particle_buffer_histories[0]);
+			glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, particles->amount * 24 * sizeof(float));
+
+			particles->particle_valid_histories[0] = true;
+		}
 	}
 
 	glDisable(GL_RASTERIZER_DISCARD);

+ 18 - 4
drivers/gles3/rasterizer_storage_gles3.h

@@ -995,7 +995,6 @@ public:
 		float explosiveness;
 		float randomness;
 		Rect3 custom_aabb;
-		Vector3 gravity;
 		bool use_local_coords;
 		RID process_material;
 
@@ -1003,11 +1002,14 @@ public:
 
 		Vector<RID> draw_passes;
 
-		Rect3 computed_aabb;
-
 		GLuint particle_buffers[2];
 		GLuint particle_vaos[2];
 
+		GLuint particle_buffer_histories[2];
+		GLuint particle_vao_histories[2];
+		bool particle_valid_histories[2];
+		bool histories_enabled;
+
 		SelfList<Particles> particle_element;
 
 		float phase;
@@ -1016,6 +1018,8 @@ public:
 
 		uint32_t cycle_number;
 
+		float speed_scale;
+
 		int fixed_fps;
 		bool fractional_delta;
 		float frame_remainder;
@@ -1037,6 +1041,10 @@ public:
 			fixed_fps = 0;
 			fractional_delta = false;
 			frame_remainder = 0;
+			histories_enabled = false;
+			speed_scale = 1.0;
+
+			custom_aabb = Rect3(Vector3(-4, -4, -4), Vector3(8, 8, 8));
 
 			draw_order = VS::PARTICLES_DRAW_ORDER_INDEX;
 			particle_buffers[0] = 0;
@@ -1054,6 +1062,10 @@ public:
 
 			glDeleteBuffers(2, particle_buffers);
 			glDeleteVertexArrays(2, particle_vaos);
+			if (histories_enabled) {
+				glDeleteBuffers(2, particle_buffer_histories);
+				glDeleteVertexArrays(2, particle_vao_histories);
+			}
 		}
 	};
 
@@ -1072,7 +1084,7 @@ public:
 	virtual void particles_set_explosiveness_ratio(RID p_particles, float p_ratio);
 	virtual void particles_set_randomness_ratio(RID p_particles, float p_ratio);
 	virtual void particles_set_custom_aabb(RID p_particles, const Rect3 &p_aabb);
-	virtual void particles_set_gravity(RID p_particles, const Vector3 &p_gravity);
+	virtual void particles_set_speed_scale(RID p_particles, float p_scale);
 	virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable);
 	virtual void particles_set_process_material(RID p_particles, RID p_material);
 	virtual void particles_set_fixed_fps(RID p_particles, int p_fps);
@@ -1087,6 +1099,8 @@ public:
 	virtual Rect3 particles_get_current_aabb(RID p_particles);
 	virtual Rect3 particles_get_aabb(RID p_particles) const;
 
+	virtual void _particles_update_histories(Particles *particles);
+
 	virtual void particles_set_emission_transform(RID p_particles, const Transform &p_transform);
 	void _particles_process(Particles *p_particles, float p_delta);
 

+ 14 - 3
drivers/gles3/shaders/particles.glsl

@@ -30,7 +30,7 @@ uniform float explosiveness;
 uniform float randomness;
 uniform vec4 time;
 uniform float delta;
-uniform vec3 gravity;
+
 uniform int attractor_count;
 uniform Attractor attractors[MAX_ATTRACTORS];
 uniform bool clear;
@@ -69,9 +69,19 @@ uint hash(uint x) {
 
 void main() {
 
+#ifdef PARTICLES_COPY
+
+	out_color=color;
+	out_velocity_active=velocity_active;
+	out_custom = custom;
+	out_xform_1 = xform_1;
+	out_xform_2 = xform_2;
+	out_xform_3 = xform_3;
+
+#else
+
 	bool apply_forces=true;
 	bool apply_velocity=true;
-	vec3 current_gravity = gravity;
 	float local_delta=delta;
 
 	float mass = 1.0;
@@ -164,7 +174,7 @@ VERTEX_SHADER_CODE
 
 		if (true) {
 
-			vec3 force = current_gravity;
+			vec3 force = vec3(0.0);
 			for(int i=0;i<attractor_count;i++) {
 
 				vec3 rel_vec = xform[3].xyz - attractors[i].pos;
@@ -211,6 +221,7 @@ VERTEX_SHADER_CODE
 	out_xform_2 = xform[1];
 	out_xform_3 = xform[2];
 
+#endif //PARTICLES_COPY
 
 }
 

+ 43 - 42
editor/plugins/particles_editor_plugin.cpp

@@ -113,48 +113,7 @@ void ParticlesEditor::_menu_option(int p_option) {
 	switch (p_option) {
 
 		case MENU_OPTION_GENERATE_AABB: {
-#if 0
-			Transform globalizer = node->get_global_transform();
-			ParticleSystemSW pssw;
-			for (int i = 0; i < VS::PARTICLE_VAR_MAX; i++) {
-
-				pssw.particle_vars[i] = node->get_variable((Particles::Variable)i);
-				pssw.particle_randomness[i] = node->get_randomness((Particles::Variable)i);
-			}
-
-			pssw.emission_half_extents = node->get_emission_half_extents();
-			pssw.emission_points = node->get_emission_points();
-			pssw.emission_base_velocity = node->get_emission_base_velocity();
-			pssw.amount = node->get_amount();
-			pssw.gravity_normal = node->get_gravity_normal();
-			pssw.emitting = true;
-			pssw.height_from_velocity = node->has_height_from_velocity();
-			pssw.color_phase_count = 1;
-
-			ParticleSystemProcessSW pp;
-			float delta = 0.01;
-			float lifetime = pssw.particle_vars[VS::PARTICLE_LIFETIME];
-
-			Transform localizer = globalizer.affine_inverse();
-			AABB aabb;
-			for (float t = 0; t < lifetime; t += delta) {
-
-				pp.process(&pssw, globalizer, delta);
-				for (int i = 0; i < pp.particle_data.size(); i++) {
-
-					Vector3 p = localizer.xform(pp.particle_data[i].pos);
-
-					if (t == 0 && i == 0)
-						aabb.pos = p;
-					else
-						aabb.expand_to(p);
-				}
-			}
-
-			aabb.grow_by(aabb.get_longest_axis_size() * 0.2);
-
-			node->set_visibility_aabb(aabb);
-#endif
+			generate_aabb->popup_centered_minsize();
 		} break;
 		case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH: {
 
@@ -186,6 +145,33 @@ void ParticlesEditor::_menu_option(int p_option) {
 	}
 }
 
+void ParticlesEditor::_generate_aabb() {
+
+	float time = generate_seconds->get_value();
+
+	float running = 0.0;
+
+	EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time));
+
+	Rect3 rect;
+	while (running < time) {
+
+		uint64_t ticks = OS::get_singleton()->get_ticks_usec();
+		ep.step("Generating..", int(running), true);
+		OS::get_singleton()->delay_usec(1000);
+
+		Rect3 capture = node->capture_aabb();
+		if (rect == Rect3())
+			rect = capture;
+		else
+			rect.merge_with(capture);
+
+		running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0;
+	}
+
+	node->set_visibility_aabb(rect);
+}
+
 void ParticlesEditor::edit(Particles *p_particles) {
 
 	node = p_particles;
@@ -392,6 +378,7 @@ void ParticlesEditor::_bind_methods() {
 	ClassDB::bind_method("_resource_seleted", &ParticlesEditor::_resource_seleted);
 	ClassDB::bind_method("_node_selected", &ParticlesEditor::_node_selected);
 	ClassDB::bind_method("_generate_emission_points", &ParticlesEditor::_generate_emission_points);
+	ClassDB::bind_method("_generate_aabb", &ParticlesEditor::_generate_aabb);
 
 	//ClassDB::bind_method("_populate",&ParticlesEditor::_populate);
 }
@@ -456,6 +443,20 @@ ParticlesEditor::ParticlesEditor() {
 
 	emission_file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
 
+	generate_aabb = memnew(ConfirmationDialog);
+	generate_aabb->set_title(TTR("Generate Visibility AABB"));
+	VBoxContainer *genvb = memnew(VBoxContainer);
+	generate_aabb->add_child(genvb);
+	generate_seconds = memnew(SpinBox);
+	genvb->add_margin_child(TTR("Generation Time (sec):"), generate_seconds);
+	generate_seconds->set_min(0.1);
+	generate_seconds->set_max(25);
+	generate_seconds->set_value(2);
+
+	add_child(generate_aabb);
+
+	generate_aabb->connect("confirmed", this, "_generate_aabb");
+
 	//options->set_anchor(MARGIN_LEFT,Control::ANCHOR_END);
 	//options->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END);
 }

+ 4 - 0
editor/plugins/particles_editor_plugin.h

@@ -57,6 +57,9 @@ class ParticlesEditor : public Control {
 	SpinBox *emission_amount;
 	OptionButton *emission_fill;
 
+	ConfirmationDialog *generate_aabb;
+	SpinBox *generate_seconds;
+
 	enum Menu {
 
 		MENU_OPTION_GENERATE_AABB,
@@ -68,6 +71,7 @@ class ParticlesEditor : public Control {
 
 	PoolVector<Face3> geometry;
 
+	void _generate_aabb();
 	void _generate_emission_points();
 	void _resource_seleted(const String &p_res);
 	void _node_selected(const NodePath &p_path);

+ 132 - 0
editor/spatial_editor_gizmos.cpp

@@ -2021,6 +2021,131 @@ VisibilityNotifierGizmo::VisibilityNotifierGizmo(VisibilityNotifier *p_notifier)
 
 ///
 
+String ParticlesGizmo::get_handle_name(int p_idx) const {
+
+	switch (p_idx) {
+		case 0: return "Size X";
+		case 1: return "Size Y";
+		case 2: return "Size Z";
+		case 3: return "Pos X";
+		case 4: return "Pos Y";
+		case 5: return "Pos Z";
+	}
+
+	return "";
+}
+Variant ParticlesGizmo::get_handle_value(int p_idx) const {
+
+	return particles->get_visibility_aabb();
+}
+void ParticlesGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) {
+
+	Transform gt = particles->get_global_transform();
+	//gt.orthonormalize();
+	Transform gi = gt.affine_inverse();
+
+	bool move = p_idx >= 3;
+	p_idx = p_idx % 3;
+
+	Rect3 aabb = particles->get_visibility_aabb();
+	Vector3 ray_from = p_camera->project_ray_origin(p_point);
+	Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+	Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
+
+	Vector3 ofs = aabb.pos + aabb.size * 0.5;
+
+	Vector3 axis;
+	axis[p_idx] = 1.0;
+
+	if (move) {
+
+		Vector3 ra, rb;
+		Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb);
+
+		float d = ra[p_idx];
+
+		aabb.pos[p_idx] = d - 1.0 - aabb.size[p_idx] * 0.5;
+		particles->set_visibility_aabb(aabb);
+
+	} else {
+		Vector3 ra, rb;
+		Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb);
+
+		float d = ra[p_idx] - ofs[p_idx];
+		if (d < 0.001)
+			d = 0.001;
+		//resize
+		aabb.pos[p_idx] = (aabb.pos[p_idx] + aabb.size[p_idx] * 0.5) - d;
+		aabb.size[p_idx] = d * 2;
+		particles->set_visibility_aabb(aabb);
+	}
+}
+
+void ParticlesGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
+
+	if (p_cancel) {
+		particles->set_visibility_aabb(p_restore);
+		return;
+	}
+
+	UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+	ur->create_action(TTR("Change Particles AABB"));
+	ur->add_do_method(particles, "set_custom_aabb", particles->get_visibility_aabb());
+	ur->add_undo_method(particles, "set_custom_aabb", p_restore);
+	ur->commit_action();
+}
+
+void ParticlesGizmo::redraw() {
+
+	clear();
+
+	Vector<Vector3> lines;
+	Rect3 aabb = particles->get_visibility_aabb();
+
+	for (int i = 0; i < 12; i++) {
+		Vector3 a, b;
+		aabb.get_edge(i, a, b);
+		lines.push_back(a);
+		lines.push_back(b);
+	}
+
+	Vector<Vector3> handles;
+
+	for (int i = 0; i < 3; i++) {
+
+		Vector3 ax;
+		ax[i] = aabb.pos[i] + aabb.size[i];
+		ax[(i + 1) % 3] = aabb.pos[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5;
+		ax[(i + 2) % 3] = aabb.pos[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5;
+		handles.push_back(ax);
+	}
+
+	Vector3 center = aabb.pos + aabb.size * 0.5;
+	for (int i = 0; i < 3; i++) {
+
+		Vector3 ax;
+		ax[i] = 1.0;
+		handles.push_back(center + ax);
+		lines.push_back(center);
+		lines.push_back(center + ax);
+	}
+
+	add_lines(lines, SpatialEditorGizmos::singleton->particles_material);
+	add_collision_segments(lines);
+	//add_unscaled_billboard(SpatialEditorGizmos::singleton->visi,0.05);
+	add_handles(handles);
+}
+ParticlesGizmo::ParticlesGizmo(Particles *p_particles) {
+
+	particles = p_particles;
+	set_spatial_node(p_particles);
+}
+
+////////
+
+///
+
 String ReflectionProbeGizmo::get_handle_name(int p_idx) const {
 
 	switch (p_idx) {
@@ -2938,6 +3063,12 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
 		return misg;
 	}
 
+	if (p_spatial->cast_to<Particles>()) {
+
+		Ref<ParticlesGizmo> misg = memnew(ParticlesGizmo(p_spatial->cast_to<Particles>()));
+		return misg;
+	}
+
 	if (p_spatial->cast_to<ReflectionProbe>()) {
 
 		Ref<ReflectionProbeGizmo> misg = memnew(ReflectionProbeGizmo(p_spatial->cast_to<ReflectionProbe>()));
@@ -3152,6 +3283,7 @@ SpatialEditorGizmos::SpatialEditorGizmos() {
 	raycast_material = create_line_material(Color(1.0, 0.8, 0.6));
 	car_wheel_material = create_line_material(Color(0.6, 0.8, 1.0));
 	visibility_notifier_material = create_line_material(Color(1.0, 0.5, 1.0));
+	particles_material = create_line_material(Color(1.0, 1.0, 0.5));
 	reflection_probe_material = create_line_material(Color(0.5, 1.0, 0.7));
 	reflection_probe_material_internal = create_line_material(Color(0.3, 0.8, 0.5, 0.15));
 	gi_probe_material = create_line_material(Color(0.7, 1.0, 0.5));

+ 21 - 4
editor/spatial_editor_gizmos.h

@@ -33,22 +33,22 @@
 #include "editor/plugins/spatial_editor_plugin.h"
 #include "scene/3d/body_shape.h"
 #include "scene/3d/camera.h"
+#include "scene/3d/collision_polygon.h"
 #include "scene/3d/gi_probe.h"
 #include "scene/3d/light.h"
 #include "scene/3d/listener.h"
 #include "scene/3d/mesh_instance.h"
 #include "scene/3d/navigation_mesh.h"
+#include "scene/3d/particles.h"
+#include "scene/3d/physics_joint.h"
 #include "scene/3d/portal.h"
 #include "scene/3d/position_3d.h"
 #include "scene/3d/ray_cast.h"
 #include "scene/3d/reflection_probe.h"
 #include "scene/3d/room_instance.h"
 #include "scene/3d/test_cube.h"
-#include "scene/3d/visibility_notifier.h"
-
-#include "scene/3d/collision_polygon.h"
-#include "scene/3d/physics_joint.h"
 #include "scene/3d/vehicle_body.h"
+#include "scene/3d/visibility_notifier.h"
 
 class Camera;
 
@@ -244,6 +244,22 @@ public:
 	VisibilityNotifierGizmo(VisibilityNotifier *p_notifier = NULL);
 };
 
+class ParticlesGizmo : public EditorSpatialGizmo {
+
+	GDCLASS(ParticlesGizmo, EditorSpatialGizmo);
+
+	Particles *particles;
+
+public:
+	virtual String get_handle_name(int p_idx) const;
+	virtual Variant get_handle_value(int p_idx) const;
+	virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point);
+	virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
+
+	void redraw();
+	ParticlesGizmo(Particles *p_particles = NULL);
+};
+
 class ReflectionProbeGizmo : public EditorSpatialGizmo {
 
 	GDCLASS(ReflectionProbeGizmo, EditorSpatialGizmo);
@@ -420,6 +436,7 @@ public:
 	Ref<SpatialMaterial> portal_material;
 	Ref<SpatialMaterial> raycast_material;
 	Ref<SpatialMaterial> visibility_notifier_material;
+	Ref<SpatialMaterial> particles_material;
 	Ref<SpatialMaterial> car_wheel_material;
 	Ref<SpatialMaterial> joint_material;
 

+ 94 - 25
scene/3d/particles.cpp

@@ -71,15 +71,12 @@ void Particles::set_randomness_ratio(float p_ratio) {
 	randomness_ratio = p_ratio;
 	VS::get_singleton()->particles_set_randomness_ratio(particles, randomness_ratio);
 }
-void Particles::set_custom_aabb(const Rect3 &p_aabb) {
+void Particles::set_visibility_aabb(const Rect3 &p_aabb) {
 
-	custom_aabb = p_aabb;
-	VS::get_singleton()->particles_set_custom_aabb(particles, custom_aabb);
-}
-void Particles::set_gravity(const Vector3 &p_gravity) {
-
-	gravity = p_gravity;
-	VS::get_singleton()->particles_set_gravity(particles, gravity);
+	visibility_aabb = p_aabb;
+	VS::get_singleton()->particles_set_custom_aabb(particles, visibility_aabb);
+	update_gizmo();
+	_change_notify("visibility_aabb");
 }
 void Particles::set_use_local_coordinates(bool p_enable) {
 
@@ -93,6 +90,14 @@ void Particles::set_process_material(const Ref<Material> &p_material) {
 	if (process_material.is_valid())
 		material_rid = process_material->get_rid();
 	VS::get_singleton()->particles_set_process_material(particles, material_rid);
+
+	update_configuration_warning();
+}
+
+void Particles::set_speed_scale(float p_scale) {
+
+	speed_scale = p_scale;
+	VS::get_singleton()->particles_set_speed_scale(particles, p_scale);
 }
 
 bool Particles::is_emitting() const {
@@ -119,13 +124,9 @@ float Particles::get_randomness_ratio() const {
 
 	return randomness_ratio;
 }
-Rect3 Particles::get_custom_aabb() const {
-
-	return custom_aabb;
-}
-Vector3 Particles::get_gravity() const {
+Rect3 Particles::get_visibility_aabb() const {
 
-	return gravity;
+	return visibility_aabb;
 }
 bool Particles::get_use_local_coordinates() const {
 
@@ -136,6 +137,11 @@ Ref<Material> Particles::get_process_material() const {
 	return process_material;
 }
 
+float Particles::get_speed_scale() const {
+
+	return speed_scale;
+}
+
 void Particles::set_draw_order(DrawOrder p_order) {
 
 	draw_order = p_order;
@@ -170,6 +176,8 @@ void Particles::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) {
 		mesh_rid = p_mesh->get_rid();
 
 	VS::get_singleton()->particles_set_draw_pass_mesh(particles, p_pass, mesh_rid);
+
+	update_configuration_warning();
 }
 
 Ref<Mesh> Particles::get_draw_pass_mesh(int p_pass) const {
@@ -197,6 +205,37 @@ bool Particles::get_fractional_delta() const {
 	return fractional_delta;
 }
 
+String Particles::get_configuration_warning() const {
+
+	String warnings;
+
+	bool meshes_found = false;
+
+	for (int i = 0; i < draw_passes.size(); i++) {
+		if (draw_passes[i].is_valid()) {
+			meshes_found = true;
+			break;
+		}
+	}
+
+	if (!meshes_found) {
+		warnings += "- " + TTR("Nothing is visible because meshes have not been assigned to draw passes.");
+	}
+
+	if (process_material.is_null()) {
+		if (warnings != String())
+			warnings += "\n";
+		warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted.");
+	}
+
+	return warnings;
+}
+
+Rect3 Particles::capture_aabb() const {
+
+	return VS::get_singleton()->particles_get_current_aabb(particles);
+}
+
 void Particles::_validate_property(PropertyInfo &property) const {
 
 	if (property.name.begins_with("draw_pass_")) {
@@ -216,12 +255,12 @@ void Particles::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &Particles::set_pre_process_time);
 	ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &Particles::set_explosiveness_ratio);
 	ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &Particles::set_randomness_ratio);
-	ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &Particles::set_custom_aabb);
-	ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &Particles::set_gravity);
+	ClassDB::bind_method(D_METHOD("set_visibility_aabb", "aabb"), &Particles::set_visibility_aabb);
 	ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &Particles::set_use_local_coordinates);
 	ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &Particles::set_fixed_fps);
 	ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &Particles::set_fractional_delta);
 	ClassDB::bind_method(D_METHOD("set_process_material", "material:Material"), &Particles::set_process_material);
+	ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &Particles::set_speed_scale);
 
 	ClassDB::bind_method(D_METHOD("is_emitting"), &Particles::is_emitting);
 	ClassDB::bind_method(D_METHOD("get_amount"), &Particles::get_amount);
@@ -229,12 +268,12 @@ void Particles::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_pre_process_time"), &Particles::get_pre_process_time);
 	ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &Particles::get_explosiveness_ratio);
 	ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &Particles::get_randomness_ratio);
-	ClassDB::bind_method(D_METHOD("get_custom_aabb"), &Particles::get_custom_aabb);
-	ClassDB::bind_method(D_METHOD("get_gravity"), &Particles::get_gravity);
+	ClassDB::bind_method(D_METHOD("get_visibility_aabb"), &Particles::get_visibility_aabb);
 	ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &Particles::get_use_local_coordinates);
 	ClassDB::bind_method(D_METHOD("get_fixed_fps"), &Particles::get_fixed_fps);
 	ClassDB::bind_method(D_METHOD("get_fractional_delta"), &Particles::get_fractional_delta);
 	ClassDB::bind_method(D_METHOD("get_process_material:Material"), &Particles::get_process_material);
+	ClassDB::bind_method(D_METHOD("get_speed_scale"), &Particles::get_speed_scale);
 
 	ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &Particles::set_draw_order);
 
@@ -246,20 +285,23 @@ void Particles::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_draw_passes"), &Particles::get_draw_passes);
 	ClassDB::bind_method(D_METHOD("get_draw_pass_mesh:Mesh", "pass"), &Particles::get_draw_pass_mesh);
 
+	ClassDB::bind_method(D_METHOD("capture_aabb"), &Particles::capture_aabb);
+
 	ADD_GROUP("Parameters", "");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,100000,1"), "set_amount", "get_amount");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
-	ADD_PROPERTY(PropertyInfo(Variant::RECT3, "custom_aabb"), "set_custom_aabb", "get_custom_aabb");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
+	ADD_PROPERTY(PropertyInfo(Variant::RECT3, "visibility_aabb"), "set_visibility_aabb", "get_visibility_aabb");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ParticlesMaterial,ShaderMaterial"), "set_process_material", "get_process_material");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,View Depth"), "set_draw_order", "get_draw_order");
+	ADD_GROUP("Process Material", "");
+	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ParticlesMaterial,ShaderMaterial"), "set_process_material", "get_process_material");
 	ADD_GROUP("Draw Passes", "draw_");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_passes", PROPERTY_HINT_RANGE, "0," + itos(MAX_DRAW_PASSES) + ",1"), "set_draw_passes", "get_draw_passes");
 	for (int i = 0; i < MAX_DRAW_PASSES; i++) {
@@ -278,16 +320,18 @@ Particles::Particles() {
 	particles = VS::get_singleton()->particles_create();
 	set_base(particles);
 	set_emitting(true);
-	set_amount(100);
+	set_amount(8);
 	set_lifetime(1);
 	set_fixed_fps(0);
 	set_fractional_delta(true);
 	set_pre_process_time(0);
 	set_explosiveness_ratio(0);
 	set_randomness_ratio(0);
-	set_gravity(Vector3(0, -9.8, 0));
+	set_visibility_aabb(Rect3(Vector3(-4, -4, -4), Vector3(8, 8, 8)));
 	set_use_local_coordinates(true);
 	set_draw_passes(1);
+	set_draw_order(DRAW_ORDER_INDEX);
+	set_speed_scale(1);
 }
 
 Particles::~Particles() {
@@ -362,6 +406,8 @@ void ParticlesMaterial::init_shaders() {
 	shader_names->trail_divisor = "trail_divisor";
 	shader_names->trail_size_modifier = "trail_size_modifier";
 	shader_names->trail_color_modifier = "trail_color_modifier";
+
+	shader_names->gravity = "gravity";
 }
 
 void ParticlesMaterial::finish_shaders() {
@@ -437,6 +483,8 @@ void ParticlesMaterial::_update_shader() {
 
 	code += "uniform int trail_divisor;\n";
 
+	code += "uniform vec3 gravity;\n";
+
 	if (color_ramp.is_valid())
 		code += "uniform sampler2D color_ramp;\n";
 
@@ -633,7 +681,7 @@ void ParticlesMaterial::_update_shader() {
 	else
 		code += "    float tex_anim_offset = 0.0;\n";
 
-	code += "    vec3 force = vec3(0.0); \n";
+	code += "    vec3 force = gravity; \n";
 	code += "    vec3 pos = TRANSFORM[3].xyz; \n";
 	code += "    //apply linear acceleration\n";
 	code += "    force+=normalize(VELOCITY) * (linear_accel+tex_linear_accel)*mix(1.0,rand_from_seed(alt_seed),linear_accel_random);\n";
@@ -643,7 +691,7 @@ void ParticlesMaterial::_update_shader() {
 	code += "	//org=p_transform.origin;\n";
 	code += "    force+=normalize(pos-org) * (radial_accel+tex_radial_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random);\n";
 	code += "    //apply tangential acceleration;\n";
-	code += "    force+=normalize(cross(normalize(pos-org),normalize(GRAVITY))) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n";
+	code += "    force+=normalize(cross(normalize(pos-org),normalize(gravity))) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n";
 	code += "    //apply attractor forces\n";
 	code += "    VELOCITY+=force * DELTA;\n";
 	if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid())
@@ -1168,6 +1216,21 @@ Ref<GradientTexture> ParticlesMaterial::get_trail_color_modifier() const {
 	return trail_color_modifier;
 }
 
+void ParticlesMaterial::set_gravity(const Vector3 &p_gravity) {
+
+	gravity = p_gravity;
+	Vector3 gset = gravity;
+	if (gset == Vector3()) {
+		gset = Vector3(0, -0.000001, 0); //as gravity is used as upvector in some calculations
+	}
+	VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->gravity, gset);
+}
+
+Vector3 ParticlesMaterial::get_gravity() const {
+
+	return gravity;
+}
+
 void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
 
 	if (property.name == "color" && color_ramp.is_valid()) {
@@ -1248,6 +1311,9 @@ void ParticlesMaterial::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_trail_color_modifier", "texture:GradientTexture"), &ParticlesMaterial::set_trail_color_modifier);
 	ClassDB::bind_method(D_METHOD("get_trail_color_modifier:GradientTexture"), &ParticlesMaterial::get_trail_color_modifier);
 
+	ClassDB::bind_method(D_METHOD("get_gravity"), &ParticlesMaterial::get_gravity);
+	ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &ParticlesMaterial::set_gravity);
+
 	ADD_GROUP("Trail", "trail_");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_divisor", PROPERTY_HINT_RANGE, "1,1000000,1"), "set_trail_divisor", "get_trail_divisor");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_size_modifier", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_trail_size_modifier", "get_trail_size_modifier");
@@ -1265,6 +1331,8 @@ void ParticlesMaterial::_bind_methods() {
 	ADD_GROUP("Spread", "");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "flatness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_flatness", "get_flatness");
+	ADD_GROUP("Gravity", "");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
 	ADD_GROUP("Initial Velocity", "initial_");
 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY);
 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY);
@@ -1361,6 +1429,7 @@ ParticlesMaterial::ParticlesMaterial()
 	set_emission_sphere_radius(1);
 	set_emission_box_extents(Vector3(1, 1, 1));
 	set_trail_divisor(1);
+	set_gravity(Vector3(0, -9.8, 0));
 	emission_point_count = 1;
 
 	for (int i = 0; i < PARAM_MAX; i++) {

+ 16 - 6
scene/3d/particles.h

@@ -63,8 +63,8 @@ private:
 	float pre_process_time;
 	float explosiveness_ratio;
 	float randomness_ratio;
-	Rect3 custom_aabb;
-	Vector3 gravity;
+	float speed_scale;
+	Rect3 visibility_aabb;
 	bool local_coords;
 	int fixed_fps;
 	bool fractional_delta;
@@ -89,10 +89,10 @@ public:
 	void set_pre_process_time(float p_time);
 	void set_explosiveness_ratio(float p_ratio);
 	void set_randomness_ratio(float p_ratio);
-	void set_custom_aabb(const Rect3 &p_aabb);
-	void set_gravity(const Vector3 &p_gravity);
+	void set_visibility_aabb(const Rect3 &p_aabb);
 	void set_use_local_coordinates(bool p_enable);
 	void set_process_material(const Ref<Material> &p_material);
+	void set_speed_scale(float p_scale);
 
 	bool is_emitting() const;
 	int get_amount() const;
@@ -100,10 +100,10 @@ public:
 	float get_pre_process_time() const;
 	float get_explosiveness_ratio() const;
 	float get_randomness_ratio() const;
-	Rect3 get_custom_aabb() const;
-	Vector3 get_gravity() const;
+	Rect3 get_visibility_aabb() const;
 	bool get_use_local_coordinates() const;
 	Ref<Material> get_process_material() const;
+	float get_speed_scale() const;
 
 	void set_fixed_fps(int p_count);
 	int get_fixed_fps() const;
@@ -120,6 +120,9 @@ public:
 	void set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh);
 	Ref<Mesh> get_draw_pass_mesh(int p_pass) const;
 
+	virtual String get_configuration_warning() const;
+
+	Rect3 capture_aabb() const;
 	Particles();
 	~Particles();
 };
@@ -270,6 +273,8 @@ private:
 		StringName trail_divisor;
 		StringName trail_size_modifier;
 		StringName trail_color_modifier;
+
+		StringName gravity;
 	};
 
 	static ShaderNames *shader_names;
@@ -304,6 +309,8 @@ private:
 	Ref<CurveTexture> trail_size_modifier;
 	Ref<GradientTexture> trail_color_modifier;
 
+	Vector3 gravity;
+
 	//do not save emission points here
 
 protected:
@@ -358,6 +365,9 @@ public:
 	void set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier);
 	Ref<GradientTexture> get_trail_color_modifier() const;
 
+	void set_gravity(const Vector3 &p_gravity);
+	Vector3 get_gravity() const;
+
 	static void init_shaders();
 	static void finish_shaders();
 	static void flush_changes();

+ 1 - 1
servers/visual/rasterizer.h

@@ -441,7 +441,7 @@ public:
 	virtual void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) = 0;
 	virtual void particles_set_randomness_ratio(RID p_particles, float p_ratio) = 0;
 	virtual void particles_set_custom_aabb(RID p_particles, const Rect3 &p_aabb) = 0;
-	virtual void particles_set_gravity(RID p_particles, const Vector3 &p_gravity) = 0;
+	virtual void particles_set_speed_scale(RID p_particles, float p_scale) = 0;
 	virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable) = 0;
 	virtual void particles_set_process_material(RID p_particles, RID p_material) = 0;
 	virtual void particles_set_fixed_fps(RID p_particles, int p_fps) = 0;

+ 0 - 2
servers/visual/shader_types.cpp

@@ -203,8 +203,6 @@ ShaderTypes::ShaderTypes() {
 	shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["DELTA"] = ShaderLanguage::TYPE_FLOAT;
 	shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["NUMBER"] = ShaderLanguage::TYPE_UINT;
 	shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["INDEX"] = ShaderLanguage::TYPE_INT;
-	shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["SEED"] = ShaderLanguage::TYPE_UINT;
-	shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["GRAVITY"] = ShaderLanguage::TYPE_VEC3;
 	shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["EMISSION_TRANSFORM"] = ShaderLanguage::TYPE_MAT4;
 
 	shader_modes[VS::SHADER_PARTICLES].modes.insert("billboard");

+ 1 - 1
servers/visual/visual_server_raster.h

@@ -852,7 +852,7 @@ public:
 	BIND2(particles_set_explosiveness_ratio, RID, float)
 	BIND2(particles_set_randomness_ratio, RID, float)
 	BIND2(particles_set_custom_aabb, RID, const Rect3 &)
-	BIND2(particles_set_gravity, RID, const Vector3 &)
+	BIND2(particles_set_speed_scale, RID, float)
 	BIND2(particles_set_use_local_coordinates, RID, bool)
 	BIND2(particles_set_process_material, RID, RID)
 	BIND2(particles_set_fixed_fps, RID, int)

+ 1 - 1
servers/visual_server.h

@@ -480,7 +480,7 @@ public:
 	virtual void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) = 0;
 	virtual void particles_set_randomness_ratio(RID p_particles, float p_ratio) = 0;
 	virtual void particles_set_custom_aabb(RID p_particles, const Rect3 &p_aabb) = 0;
-	virtual void particles_set_gravity(RID p_particles, const Vector3 &p_gravity) = 0;
+	virtual void particles_set_speed_scale(RID p_particles, float p_scale) = 0;
 	virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable) = 0;
 	virtual void particles_set_process_material(RID p_particles, RID p_material) = 0;
 	virtual void particles_set_fixed_fps(RID p_particles, int p_fps) = 0;