diff options
| -rw-r--r-- | drivers/gles3/rasterizer_storage_gles3.cpp | 30 | ||||
| -rw-r--r-- | drivers/gles3/rasterizer_storage_gles3.h | 6 | ||||
| -rw-r--r-- | drivers/gles3/shader_compiler_gles3.cpp | 1 | ||||
| -rw-r--r-- | drivers/gles3/shaders/particles.glsl | 9 | ||||
| -rw-r--r-- | scene/2d/canvas_item.cpp | 200 | ||||
| -rw-r--r-- | scene/2d/canvas_item.h | 92 | ||||
| -rw-r--r-- | scene/3d/particles.cpp | 2 | ||||
| -rw-r--r-- | scene/register_scene_types.cpp | 4 | ||||
| -rw-r--r-- | servers/visual/shader_types.cpp | 1 | 
9 files changed, 333 insertions, 12 deletions
| diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 81baf542c0..f7e1fdee9d 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/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) { diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index ca0194bd5e..65026a16ec 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/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); diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp index 3376f99112..41421a3e2f 100644 --- a/drivers/gles3/shader_compiler_gles3.cpp +++ b/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"; diff --git a/drivers/gles3/shaders/particles.glsl b/drivers/gles3/shaders/particles.glsl index 7e7b083f73..ec2577538c 100644 --- a/drivers/gles3/shaders/particles.glsl +++ b/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; diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index 89b89a50d8..189dd66a26 100644 --- a/scene/2d/canvas_item.cpp +++ b/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") ; diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index 906a08d219..bffc171fc1 100644 --- a/scene/2d/canvas_item.h +++ b/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; diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index 73a70c07b2..8508962521 100644 --- a/scene/3d/particles.cpp +++ b/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"; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index d4ca55346a..69d1a3aeb8 100644 --- a/scene/register_scene_types.cpp +++ b/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();  } diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp index 81139ecc1c..42f1a98826 100644 --- a/servers/visual/shader_types.cpp +++ b/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"); |