Browse Source

Small fixes required to get platformer to work.
Added back CanvasItemMaterial

Juan Linietsky 8 years ago
parent
commit
8ef1c41a4e

+ 30 - 0
drivers/gles3/rasterizer_storage_gles3.cpp

@@ -5288,6 +5288,7 @@ void RasterizerStorageGLES3::_particles_process(Particles *particles, float p_de
 
 	if (particles->clear) {
 		particles->cycle_number = 0;
+		particles->random_seed = Math::rand();
 	} else if (new_phase < particles->phase) {
 		particles->cycle_number++;
 	}
@@ -5298,6 +5299,8 @@ void RasterizerStorageGLES3::_particles_process(Particles *particles, float p_de
 
 	shaders.particles.set_uniform(ParticlesShaderGLES3::DELTA, p_delta * particles->speed_scale);
 	shaders.particles.set_uniform(ParticlesShaderGLES3::CLEAR, particles->clear);
+	glUniform1ui(shaders.particles.get_uniform_location(ParticlesShaderGLES3::RANDOM_SEED), particles->random_seed);
+
 	if (particles->use_local_coords)
 		shaders.particles.set_uniform(ParticlesShaderGLES3::EMISSION_TRANSFORM, Transform());
 	else
@@ -5353,6 +5356,33 @@ void RasterizerStorageGLES3::update_particles() {
 
 		Particles *particles = particle_update_list.first()->self();
 
+		if (particles->inactive && !particles->emitting) {
+
+			particle_update_list.remove(particle_update_list.first());
+			continue;
+		}
+
+		if (particles->emitting) {
+			if (particles->inactive) {
+				//restart system from scratch
+				particles->prev_ticks = 0;
+				particles->phase = 0;
+				particles->prev_phase = 0;
+				particles->clear = true;
+				particles->particle_valid_histories[0] = false;
+				particles->particle_valid_histories[1] = false;
+			}
+			particles->inactive = false;
+			particles->inactive_time = 0;
+		} else {
+			particles->inactive_time += particles->speed_scale * frame.delta;
+			if (particles->inactive_time > particles->lifetime * 1.2) {
+				particles->inactive = true;
+				particle_update_list.remove(particle_update_list.first());
+				continue;
+			}
+		}
+
 		Material *material = material_owner.getornull(particles->process_material);
 		if (!material || !material->shader || material->shader->mode != VS::SHADER_PARTICLES) {
 

+ 6 - 0
drivers/gles3/rasterizer_storage_gles3.h

@@ -1033,6 +1033,8 @@ public:
 
 	struct Particles : public GeometryOwner {
 
+		bool inactive;
+		float inactive_time;
 		bool emitting;
 		int amount;
 		float lifetime;
@@ -1060,6 +1062,7 @@ public:
 		float phase;
 		float prev_phase;
 		uint64_t prev_ticks;
+		uint32_t random_seed;
 
 		uint32_t cycle_number;
 
@@ -1088,6 +1091,7 @@ public:
 			frame_remainder = 0;
 			histories_enabled = false;
 			speed_scale = 1.0;
+			random_seed = 0;
 
 			custom_aabb = Rect3(Vector3(-4, -4, -4), Vector3(8, 8, 8));
 
@@ -1098,6 +1102,8 @@ public:
 			prev_ticks = 0;
 
 			clear = true;
+			inactive = true;
+			inactive_time = false;
 
 			glGenBuffers(2, particle_buffers);
 			glGenVertexArrays(2, particle_vaos);

+ 1 - 0
drivers/gles3/shader_compiler_gles3.cpp

@@ -795,6 +795,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
 	actions[VS::SHADER_PARTICLES].renames["INDEX"] = "index";
 	actions[VS::SHADER_PARTICLES].renames["GRAVITY"] = "current_gravity";
 	actions[VS::SHADER_PARTICLES].renames["EMISSION_TRANSFORM"] = "emission_transform";
+	actions[VS::SHADER_PARTICLES].renames["RANDOM_SEED"] = "random_seed";
 
 	actions[VS::SHADER_SPATIAL].render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
 	actions[VS::SHADER_SPATIAL].render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";

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

@@ -37,6 +37,7 @@ uniform bool clear;
 uniform uint cycle;
 uniform float lifetime;
 uniform mat4 emission_transform;
+uniform uint random_seed;
 
 
 out highp vec4 out_color; //tfb:
@@ -104,7 +105,9 @@ void main() {
 	bool shader_active = velocity_active.a > 0.5;
 
 	if (system_phase > prev_system_phase) {
-		if (prev_system_phase < restart_phase && system_phase >= restart_phase) {
+		// restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
+
+		if (restart_phase >= prev_system_phase && restart_phase < system_phase ) {
 			restart=true;
 #ifdef USE_FRACTIONAL_DELTA
 			local_delta = (system_phase - restart_phase) * lifetime;
@@ -112,12 +115,12 @@ void main() {
 		}
 
 	} else {
-		if (prev_system_phase < restart_phase) {
+		if (restart_phase >= prev_system_phase) {
 			restart=true;
 #ifdef USE_FRACTIONAL_DELTA
 			local_delta = (1.0 - restart_phase + system_phase) * lifetime;
 #endif
-		} else if (system_phase >= restart_phase) {
+		} else if (restart_phase < system_phase ) {
 			restart=true;
 #ifdef USE_FRACTIONAL_DELTA
 			local_delta = (system_phase - restart_phase) * lifetime;

+ 195 - 5
scene/2d/canvas_item.cpp

@@ -39,6 +39,196 @@
 #include "scene/scene_string_names.h"
 #include "servers/visual_server.h"
 
+Mutex *CanvasItemMaterial::material_mutex = NULL;
+SelfList<CanvasItemMaterial>::List CanvasItemMaterial::dirty_materials;
+Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map;
+
+void CanvasItemMaterial::init_shaders() {
+
+#ifndef NO_THREADS
+	material_mutex = Mutex::create();
+#endif
+}
+
+void CanvasItemMaterial::finish_shaders() {
+
+#ifndef NO_THREADS
+	memdelete(material_mutex);
+#endif
+}
+
+void CanvasItemMaterial::_update_shader() {
+
+	dirty_materials.remove(&element);
+
+	MaterialKey mk = _compute_key();
+	if (mk.key == current_key.key)
+		return; //no update required in the end
+
+	if (shader_map.has(current_key)) {
+		shader_map[current_key].users--;
+		if (shader_map[current_key].users == 0) {
+			//deallocate shader, as it's no longer in use
+			VS::get_singleton()->free(shader_map[current_key].shader);
+			shader_map.erase(current_key);
+		}
+	}
+
+	current_key = mk;
+
+	if (shader_map.has(mk)) {
+
+		VS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader);
+		shader_map[mk].users++;
+		return;
+	}
+
+	//must create a shader!
+
+	String code = "shader_type canvas_item;\nrender_mode ";
+	switch (blend_mode) {
+		case BLEND_MODE_MIX: code += "blend_mix"; break;
+		case BLEND_MODE_ADD: code += "blend_add"; break;
+		case BLEND_MODE_SUB: code += "blend_sub"; break;
+		case BLEND_MODE_MUL: code += "blend_mul"; break;
+		case BLEND_MODE_PREMULT_ALPHA: code += "blend_premul_alpha"; break;
+	}
+
+	switch (light_mode) {
+		case LIGHT_MODE_NORMAL: break;
+		case LIGHT_MODE_UNSHADED: code += "unshaded"; break;
+		case LIGHT_MODE_LIGHT_ONLY: code += "light_only"; break;
+	}
+	code += ";\n"; //thats it.
+
+	ShaderData shader_data;
+	shader_data.shader = VS::get_singleton()->shader_create();
+	shader_data.users = 1;
+
+	VS::get_singleton()->shader_set_code(shader_data.shader, code);
+
+	shader_map[mk] = shader_data;
+
+	VS::get_singleton()->material_set_shader(_get_material(), shader_data.shader);
+}
+
+void CanvasItemMaterial::flush_changes() {
+
+	if (material_mutex)
+		material_mutex->lock();
+
+	while (dirty_materials.first()) {
+
+		dirty_materials.first()->self()->_update_shader();
+	}
+
+	if (material_mutex)
+		material_mutex->unlock();
+}
+
+void CanvasItemMaterial::_queue_shader_change() {
+
+	if (material_mutex)
+		material_mutex->lock();
+
+	if (!element.in_list()) {
+		dirty_materials.add(&element);
+	}
+
+	if (material_mutex)
+		material_mutex->unlock();
+}
+
+bool CanvasItemMaterial::_is_shader_dirty() const {
+
+	bool dirty = false;
+
+	if (material_mutex)
+		material_mutex->lock();
+
+	dirty = element.in_list();
+
+	if (material_mutex)
+		material_mutex->unlock();
+
+	return dirty;
+}
+void CanvasItemMaterial::set_blend_mode(BlendMode p_blend_mode) {
+
+	blend_mode = p_blend_mode;
+	_queue_shader_change();
+}
+
+CanvasItemMaterial::BlendMode CanvasItemMaterial::get_blend_mode() const {
+	return blend_mode;
+}
+
+void CanvasItemMaterial::set_light_mode(LightMode p_light_mode) {
+
+	light_mode = p_light_mode;
+	_queue_shader_change();
+}
+
+CanvasItemMaterial::LightMode CanvasItemMaterial::get_light_mode() const {
+
+	return light_mode;
+}
+
+void CanvasItemMaterial::_validate_property(PropertyInfo &property) const {
+}
+
+void CanvasItemMaterial::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("set_blend_mode", "blend_mode"), &CanvasItemMaterial::set_blend_mode);
+	ClassDB::bind_method(D_METHOD("get_blend_mode"), &CanvasItemMaterial::get_blend_mode);
+
+	ClassDB::bind_method(D_METHOD("set_light_mode", "light_mode"), &CanvasItemMaterial::set_light_mode);
+	ClassDB::bind_method(D_METHOD("get_light_mode"), &CanvasItemMaterial::get_light_mode);
+
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,Premult Alpha"), "set_blend_mode", "get_blend_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode");
+
+	BIND_CONSTANT(BLEND_MODE_MIX);
+	BIND_CONSTANT(BLEND_MODE_ADD);
+	BIND_CONSTANT(BLEND_MODE_SUB);
+	BIND_CONSTANT(BLEND_MODE_MUL);
+	BIND_CONSTANT(BLEND_MODE_PREMULT_ALPHA);
+	BIND_CONSTANT(LIGHT_MODE_NORMAL);
+	BIND_CONSTANT(LIGHT_MODE_UNSHADED);
+	BIND_CONSTANT(LIGHT_MODE_LIGHT_ONLY);
+}
+
+CanvasItemMaterial::CanvasItemMaterial()
+	: element(this) {
+
+	blend_mode = BLEND_MODE_MIX;
+	light_mode = LIGHT_MODE_NORMAL;
+
+	current_key.key = 0;
+	current_key.invalid_key = 1;
+	_queue_shader_change();
+}
+
+CanvasItemMaterial::~CanvasItemMaterial() {
+
+	if (material_mutex)
+		material_mutex->lock();
+
+	if (shader_map.has(current_key)) {
+		shader_map[current_key].users--;
+		if (shader_map[current_key].users == 0) {
+			//deallocate shader, as it's no longer in use
+			VS::get_singleton()->free(shader_map[current_key].shader);
+			shader_map.erase(current_key);
+		}
+
+		VS::get_singleton()->material_set_shader(_get_material(), RID());
+	}
+
+	if (material_mutex)
+		material_mutex->unlock();
+}
+
 ///////////////////////////////////////////////////////////////////
 
 bool CanvasItem::is_visible_in_tree() const {
@@ -665,7 +855,7 @@ bool CanvasItem::is_draw_behind_parent_enabled() const {
 	return behind;
 }
 
-void CanvasItem::set_material(const Ref<ShaderMaterial> &p_material) {
+void CanvasItem::set_material(const Ref<Material> &p_material) {
 
 	material = p_material;
 	RID rid;
@@ -686,7 +876,7 @@ bool CanvasItem::get_use_parent_material() const {
 	return use_parent_material;
 }
 
-Ref<ShaderMaterial> CanvasItem::get_material() const {
+Ref<Material> CanvasItem::get_material() const {
 
 	return material;
 }
@@ -788,8 +978,8 @@ void CanvasItem::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_world_2d"), &CanvasItem::get_world_2d);
 	//ClassDB::bind_method(D_METHOD("get_viewport"),&CanvasItem::get_viewport);
 
-	ClassDB::bind_method(D_METHOD("set_material", "material:ShaderMaterial"), &CanvasItem::set_material);
-	ClassDB::bind_method(D_METHOD("get_material:ShaderMaterial"), &CanvasItem::get_material);
+	ClassDB::bind_method(D_METHOD("set_material", "material:Material"), &CanvasItem::set_material);
+	ClassDB::bind_method(D_METHOD("get_material:Material"), &CanvasItem::get_material);
 
 	ClassDB::bind_method(D_METHOD("set_use_parent_material", "enable"), &CanvasItem::set_use_parent_material);
 	ClassDB::bind_method(D_METHOD("get_use_parent_material"), &CanvasItem::get_use_parent_material);
@@ -815,7 +1005,7 @@ void CanvasItem::_bind_methods() {
 	ADD_PROPERTYNO(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
 
 	ADD_GROUP("Material", "");
-	ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial"), "set_material", "get_material");
+	ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material");
 	ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material");
 	//exporting these two things doesn't really make much sense i think
 	//ADD_PROPERTY( PropertyInfo(Variant::BOOL,"transform/toplevel"), "set_as_toplevel","is_set_as_toplevel") ;

+ 89 - 3
scene/2d/canvas_item.h

@@ -42,6 +42,92 @@ class Font;
 
 class StyleBox;
 
+class CanvasItemMaterial : public Material {
+
+	GDCLASS(CanvasItemMaterial, Material)
+
+public:
+	enum BlendMode {
+		BLEND_MODE_MIX,
+		BLEND_MODE_ADD,
+		BLEND_MODE_SUB,
+		BLEND_MODE_MUL,
+		BLEND_MODE_PREMULT_ALPHA
+	};
+
+	enum LightMode {
+		LIGHT_MODE_NORMAL,
+		LIGHT_MODE_UNSHADED,
+		LIGHT_MODE_LIGHT_ONLY
+	};
+
+private:
+	union MaterialKey {
+
+		struct {
+			uint32_t blend_mode : 4;
+			uint32_t light_mode : 4;
+			uint32_t invalid_key : 1;
+		};
+
+		uint32_t key;
+
+		bool operator<(const MaterialKey &p_key) const {
+			return key < p_key.key;
+		}
+	};
+
+	struct ShaderData {
+		RID shader;
+		int users;
+	};
+
+	static Map<MaterialKey, ShaderData> shader_map;
+
+	MaterialKey current_key;
+
+	_FORCE_INLINE_ MaterialKey _compute_key() const {
+
+		MaterialKey mk;
+		mk.key = 0;
+		mk.blend_mode = blend_mode;
+		mk.light_mode = light_mode;
+		return mk;
+	}
+
+	static Mutex *material_mutex;
+	static SelfList<CanvasItemMaterial>::List dirty_materials;
+	SelfList<CanvasItemMaterial> element;
+
+	void _update_shader();
+	_FORCE_INLINE_ void _queue_shader_change();
+	_FORCE_INLINE_ bool _is_shader_dirty() const;
+
+	BlendMode blend_mode;
+	LightMode light_mode;
+
+protected:
+	static void _bind_methods();
+	void _validate_property(PropertyInfo &property) const;
+
+public:
+	void set_blend_mode(BlendMode p_blend_mode);
+	BlendMode get_blend_mode() const;
+
+	void set_light_mode(LightMode p_light_mode);
+	LightMode get_light_mode() const;
+
+	static void init_shaders();
+	static void finish_shaders();
+	static void flush_changes();
+
+	CanvasItemMaterial();
+	virtual ~CanvasItemMaterial();
+};
+
+VARIANT_ENUM_CAST(CanvasItemMaterial::BlendMode)
+VARIANT_ENUM_CAST(CanvasItemMaterial::LightMode)
+
 class CanvasItem : public Node {
 
 	GDCLASS(CanvasItem, Node);
@@ -83,7 +169,7 @@ private:
 	bool notify_local_transform;
 	bool notify_transform;
 
-	Ref<ShaderMaterial> material;
+	Ref<Material> material;
 
 	mutable Transform2D global_transform;
 	mutable bool global_invalid;
@@ -203,8 +289,8 @@ public:
 	RID get_canvas() const;
 	Ref<World2D> get_world_2d() const;
 
-	void set_material(const Ref<ShaderMaterial> &p_material);
-	Ref<ShaderMaterial> get_material() const;
+	void set_material(const Ref<Material> &p_material);
+	Ref<Material> get_material() const;
 
 	void set_use_parent_material(bool p_use_parent_material);
 	bool get_use_parent_material() const;

+ 1 - 1
scene/3d/particles.cpp

@@ -571,7 +571,7 @@ void ParticlesMaterial::_update_shader() {
 	code += "\n";
 
 	code += " uint base_number=NUMBER/uint(trail_divisor);\n";
-	code += " uint alt_seed=hash(base_number+uint(1));\n";
+	code += " uint alt_seed=hash(base_number+uint(1)+RANDOM_SEED);\n";
 	code += " float angle_rand=rand_from_seed(alt_seed);\n";
 	code += " float scale_rand=rand_from_seed(alt_seed);\n";
 	code += " float hue_rot_rand=rand_from_seed(alt_seed);\n";

+ 4 - 0
scene/register_scene_types.cpp

@@ -469,6 +469,9 @@ void register_scene_types() {
 	ClassDB::register_class<Shader>();
 	ClassDB::register_class<ShaderMaterial>();
 	ClassDB::register_virtual_class<CanvasItem>();
+	ClassDB::register_class<CanvasItemMaterial>();
+	SceneTree::add_idle_callback(CanvasItemMaterial::flush_changes);
+	CanvasItemMaterial::init_shaders();
 	ClassDB::register_class<Node2D>();
 	ClassDB::register_class<Particles2D>();
 	//ClassDB::register_class<ParticleAttractor2D>();
@@ -663,5 +666,6 @@ void unregister_scene_types() {
 
 	SpatialMaterial::finish_shaders();
 	ParticlesMaterial::finish_shaders();
+	CanvasItemMaterial::finish_shaders();
 	SceneStringNames::free();
 }

+ 1 - 0
servers/visual/shader_types.cpp

@@ -212,6 +212,7 @@ ShaderTypes::ShaderTypes() {
 	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"]["EMISSION_TRANSFORM"] = ShaderLanguage::TYPE_MAT4;
+	shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["RANDOM_SEED"] = ShaderLanguage::TYPE_UINT;
 
 	shader_modes[VS::SHADER_PARTICLES].modes.insert("billboard");
 	shader_modes[VS::SHADER_PARTICLES].modes.insert("disable_force");