Path: blob/master/scene/resources/canvas_item_material.cpp
9896 views
/**************************************************************************/1/* canvas_item_material.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "canvas_item_material.h"3132#include "core/version.h"3334Mutex CanvasItemMaterial::material_mutex;35SelfList<CanvasItemMaterial>::List CanvasItemMaterial::dirty_materials;36HashMap<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData, CanvasItemMaterial::MaterialKey> CanvasItemMaterial::shader_map;37CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = nullptr;3839void CanvasItemMaterial::init_shaders() {40shader_names = memnew(ShaderNames);4142shader_names->particles_anim_h_frames = "particles_anim_h_frames";43shader_names->particles_anim_v_frames = "particles_anim_v_frames";44shader_names->particles_anim_loop = "particles_anim_loop";45}4647void CanvasItemMaterial::finish_shaders() {48dirty_materials.clear();4950memdelete(shader_names);51shader_names = nullptr;52}5354void CanvasItemMaterial::_update_shader() {55MaterialKey mk = _compute_key();56if (mk.key == current_key.key) {57return; //no update required in the end58}5960if (shader_map.has(current_key)) {61shader_map[current_key].users--;62if (shader_map[current_key].users == 0) {63//deallocate shader, as it's no longer in use64RS::get_singleton()->free(shader_map[current_key].shader);65shader_map.erase(current_key);66}67}6869current_key = mk;7071if (shader_map.has(mk)) {72RS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader);73shader_map[mk].users++;74return;75}7677//must create a shader!7879// Add a comment to describe the shader origin (useful when converting to ShaderMaterial).80String code = "// NOTE: Shader automatically converted from " GODOT_VERSION_NAME " " GODOT_VERSION_FULL_CONFIG "'s CanvasItemMaterial.\n\n";8182code += "shader_type canvas_item;\nrender_mode ";83switch (blend_mode) {84case BLEND_MODE_MIX:85code += "blend_mix";86break;87case BLEND_MODE_ADD:88code += "blend_add";89break;90case BLEND_MODE_SUB:91code += "blend_sub";92break;93case BLEND_MODE_MUL:94code += "blend_mul";95break;96case BLEND_MODE_PREMULT_ALPHA:97code += "blend_premul_alpha";98break;99case BLEND_MODE_DISABLED:100code += "blend_disabled";101break;102}103104switch (light_mode) {105case LIGHT_MODE_NORMAL:106break;107case LIGHT_MODE_UNSHADED:108code += ",unshaded";109break;110case LIGHT_MODE_LIGHT_ONLY:111code += ",light_only";112break;113}114115code += ";\n";116117if (particles_animation) {118code += "uniform int particles_anim_h_frames;\n";119code += "uniform int particles_anim_v_frames;\n";120code += "uniform bool particles_anim_loop;\n\n";121122code += "void vertex() {\n";123code += " float h_frames = float(particles_anim_h_frames);\n";124code += " float v_frames = float(particles_anim_v_frames);\n";125code += " VERTEX.xy /= vec2(h_frames, v_frames);\n";126code += " float particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n";127code += " float particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";128code += " if (!particles_anim_loop) {\n";129code += " particle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);\n";130code += " } else {\n";131code += " particle_frame = mod(particle_frame, particle_total_frames);\n";132code += " }";133code += " UV /= vec2(h_frames, v_frames);\n";134code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n";135code += "}\n";136}137138ShaderData shader_data;139shader_data.shader = RS::get_singleton()->shader_create();140shader_data.users = 1;141142RS::get_singleton()->shader_set_code(shader_data.shader, code);143144shader_map[mk] = shader_data;145146RS::get_singleton()->material_set_shader(_get_material(), shader_data.shader);147}148149void CanvasItemMaterial::flush_changes() {150MutexLock lock(material_mutex);151152while (dirty_materials.first()) {153dirty_materials.first()->self()->_update_shader();154dirty_materials.first()->remove_from_list();155}156}157158void CanvasItemMaterial::_queue_shader_change() {159if (!_is_initialized()) {160return;161}162163MutexLock lock(material_mutex);164165if (!element.in_list()) {166dirty_materials.add(&element);167}168}169170void CanvasItemMaterial::set_blend_mode(BlendMode p_blend_mode) {171blend_mode = p_blend_mode;172_queue_shader_change();173}174175CanvasItemMaterial::BlendMode CanvasItemMaterial::get_blend_mode() const {176return blend_mode;177}178179void CanvasItemMaterial::set_light_mode(LightMode p_light_mode) {180light_mode = p_light_mode;181_queue_shader_change();182}183184CanvasItemMaterial::LightMode CanvasItemMaterial::get_light_mode() const {185return light_mode;186}187188void CanvasItemMaterial::set_particles_animation(bool p_particles_anim) {189particles_animation = p_particles_anim;190_queue_shader_change();191notify_property_list_changed();192}193194bool CanvasItemMaterial::get_particles_animation() const {195return particles_animation;196}197198void CanvasItemMaterial::set_particles_anim_h_frames(int p_frames) {199particles_anim_h_frames = p_frames;200RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_h_frames, p_frames);201}202203int CanvasItemMaterial::get_particles_anim_h_frames() const {204return particles_anim_h_frames;205}206207void CanvasItemMaterial::set_particles_anim_v_frames(int p_frames) {208particles_anim_v_frames = p_frames;209RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_v_frames, p_frames);210}211212int CanvasItemMaterial::get_particles_anim_v_frames() const {213return particles_anim_v_frames;214}215216void CanvasItemMaterial::set_particles_anim_loop(bool p_loop) {217particles_anim_loop = p_loop;218RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop);219}220221bool CanvasItemMaterial::get_particles_anim_loop() const {222return particles_anim_loop;223}224225void CanvasItemMaterial::_validate_property(PropertyInfo &p_property) const {226if (p_property.name.begins_with("particles_anim_") && !particles_animation) {227p_property.usage = PROPERTY_USAGE_NONE;228}229}230231RID CanvasItemMaterial::get_shader_rid() const {232ERR_FAIL_COND_V(!shader_map.has(current_key), RID());233return shader_map[current_key].shader;234}235236Shader::Mode CanvasItemMaterial::get_shader_mode() const {237return Shader::MODE_CANVAS_ITEM;238}239240void CanvasItemMaterial::_bind_methods() {241ClassDB::bind_method(D_METHOD("set_blend_mode", "blend_mode"), &CanvasItemMaterial::set_blend_mode);242ClassDB::bind_method(D_METHOD("get_blend_mode"), &CanvasItemMaterial::get_blend_mode);243244ClassDB::bind_method(D_METHOD("set_light_mode", "light_mode"), &CanvasItemMaterial::set_light_mode);245ClassDB::bind_method(D_METHOD("get_light_mode"), &CanvasItemMaterial::get_light_mode);246247ClassDB::bind_method(D_METHOD("set_particles_animation", "particles_anim"), &CanvasItemMaterial::set_particles_animation);248ClassDB::bind_method(D_METHOD("get_particles_animation"), &CanvasItemMaterial::get_particles_animation);249250ClassDB::bind_method(D_METHOD("set_particles_anim_h_frames", "frames"), &CanvasItemMaterial::set_particles_anim_h_frames);251ClassDB::bind_method(D_METHOD("get_particles_anim_h_frames"), &CanvasItemMaterial::get_particles_anim_h_frames);252253ClassDB::bind_method(D_METHOD("set_particles_anim_v_frames", "frames"), &CanvasItemMaterial::set_particles_anim_v_frames);254ClassDB::bind_method(D_METHOD("get_particles_anim_v_frames"), &CanvasItemMaterial::get_particles_anim_v_frames);255256ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &CanvasItemMaterial::set_particles_anim_loop);257ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &CanvasItemMaterial::get_particles_anim_loop);258259ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply,Premultiplied Alpha"), "set_blend_mode", "get_blend_mode");260ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode");261ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_animation"), "set_particles_animation", "get_particles_animation");262263ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_h_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_h_frames", "get_particles_anim_h_frames");264ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_v_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_v_frames", "get_particles_anim_v_frames");265ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_anim_loop"), "set_particles_anim_loop", "get_particles_anim_loop");266267BIND_ENUM_CONSTANT(BLEND_MODE_MIX);268BIND_ENUM_CONSTANT(BLEND_MODE_ADD);269BIND_ENUM_CONSTANT(BLEND_MODE_SUB);270BIND_ENUM_CONSTANT(BLEND_MODE_MUL);271BIND_ENUM_CONSTANT(BLEND_MODE_PREMULT_ALPHA);272273BIND_ENUM_CONSTANT(LIGHT_MODE_NORMAL);274BIND_ENUM_CONSTANT(LIGHT_MODE_UNSHADED);275BIND_ENUM_CONSTANT(LIGHT_MODE_LIGHT_ONLY);276}277278CanvasItemMaterial::CanvasItemMaterial() :279element(this) {280_set_material(RS::get_singleton()->material_create());281282set_particles_anim_h_frames(1);283set_particles_anim_v_frames(1);284set_particles_anim_loop(false);285286current_key.invalid_key = 1;287288_mark_initialized(callable_mp(this, &CanvasItemMaterial::_queue_shader_change), callable_mp(this, &CanvasItemMaterial::_update_shader));289}290291CanvasItemMaterial::~CanvasItemMaterial() {292MutexLock lock(material_mutex);293294ERR_FAIL_NULL(RenderingServer::get_singleton());295296if (shader_map.has(current_key)) {297shader_map[current_key].users--;298if (shader_map[current_key].users == 0) {299//deallocate shader, as it's no longer in use300RS::get_singleton()->free(shader_map[current_key].shader);301shader_map.erase(current_key);302}303304RS::get_singleton()->material_set_shader(_get_material(), RID());305}306}307308309