2D GPU Particles working..

This commit is contained in:
Juan Linietsky 2017-06-21 16:25:45 -03:00
parent 3c1fd26bb0
commit 95560e02c5
21 changed files with 1039 additions and 1340 deletions

View file

@ -28,6 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "rasterizer_canvas_gles3.h"
#include "servers/visual/visual_server_raster.h"
#include "global_config.h"
#include "os/os.h"
@ -607,6 +608,133 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
}
_draw_polygon(polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1);
} break;
case Item::Command::TYPE_PARTICLES: {
Item::CommandParticles *particles_cmd = static_cast<Item::CommandParticles *>(c);
RasterizerStorageGLES3::Particles *particles = storage->particles_owner.getornull(particles_cmd->particles);
if (!particles)
break;
glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); //not used, so keep white
VisualServerRaster::redraw_request();
storage->particles_request_process(particles_cmd->particles);
//enable instancing
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, true);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, true);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true);
//reset shader and force rebind
state.using_texture_rect = true;
_set_texture_rect_mode(false);
RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(particles_cmd->texture, particles_cmd->normal_map);
if (texture) {
Size2 texpixel_size(1.0 / (texture->width / particles_cmd->h_frames), 1.0 / (texture->height / particles_cmd->v_frames));
state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
} else {
state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, Vector2(1.0, 1.0));
}
if (!particles->use_local_coords) {
Transform2D inv_xf;
inv_xf.set_axis(0, Vector2(particles->emission_transform.basis.get_axis(0).x, particles->emission_transform.basis.get_axis(0).y));
inv_xf.set_axis(1, Vector2(particles->emission_transform.basis.get_axis(1).x, particles->emission_transform.basis.get_axis(1).y));
inv_xf.set_origin(Vector2(particles->emission_transform.get_origin().x, particles->emission_transform.get_origin().y));
inv_xf.affine_invert();
state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * inv_xf);
}
state.canvas_shader.set_uniform(CanvasShaderGLES3::H_FRAMES, particles_cmd->h_frames);
state.canvas_shader.set_uniform(CanvasShaderGLES3::V_FRAMES, particles_cmd->v_frames);
glBindVertexArray(data.particle_quad_array); //use particle quad array
glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); //bind particle buffer
int stride = sizeof(float) * 4 * 6;
int amount = particles->amount;
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);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, amount);
} else {
//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);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, 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);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, split);
}
}
glBindVertexArray(0);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, false);
state.using_texture_rect = true;
_set_texture_rect_mode(false);
} break;
case Item::Command::TYPE_CIRCLE: {
@ -1351,7 +1479,39 @@ void RasterizerCanvasGLES3::initialize() {
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
{
//particle quad buffers
glGenBuffers(1, &data.particle_quad_vertices);
glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices);
{
//quad of size 1, with pivot on the center for particles, then regular UVS. Color is general plus fetched from particle
const float qv[16] = {
-0.5, -0.5,
0.0, 0.0,
-0.5, 0.5,
0.0, 1.0,
0.5, 0.5,
1.0, 1.0,
0.5, -0.5,
1.0, 0.0
};
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
glGenVertexArrays(1, &data.particle_quad_array);
glBindVertexArray(data.particle_quad_array);
glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices);
glEnableVertexAttribArray(VS::ARRAY_VERTEX);
glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0);
glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (float *)0 + 2);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
{
uint32_t poly_size = GLOBAL_DEF("rendering/buffers/canvas_polygon_buffer_size_kb", 128);
@ -1428,6 +1588,9 @@ void RasterizerCanvasGLES3::finalize() {
glDeleteBuffers(1, &data.canvas_quad_vertices);
glDeleteVertexArrays(1, &data.canvas_quad_array);
glDeleteBuffers(1, &data.canvas_quad_vertices);
glDeleteVertexArrays(1, &data.canvas_quad_array);
glDeleteVertexArrays(1, &data.polygon_buffer_pointer_array);
}

View file

@ -51,6 +51,10 @@ public:
GLuint polygon_buffer_quad_arrays[4];
GLuint polygon_buffer_pointer_array;
GLuint polygon_index_buffer;
GLuint particle_quad_vertices;
GLuint particle_quad_array;
uint32_t polygon_buffer_size;
} data;

View file

@ -2326,6 +2326,9 @@ void RasterizerStorageGLES3::_update_material(Material *material) {
if (E->get().order < 0)
continue; // texture, does not go here
//if (material->shader->mode == VS::SHADER_PARTICLES) {
// print_line("uniform " + String(E->key()) + " order " + itos(E->get().order) + " offset " + itos(material->shader->ubo_offsets[E->get().order]));
//}
//regular uniform
uint8_t *data = &local_ubo[material->shader->ubo_offsets[E->get().order]];

View file

@ -361,6 +361,8 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() {
ERR_FAIL_V(NULL);
}
//_display_error_with_code("pepo", strings);
/* FRAGMENT SHADER */
strings.resize(strings_base_size);

View file

@ -11,11 +11,26 @@ uniform vec4 src_rect;
#else
#ifdef USE_INSTANCING
layout(location=8) in highp vec4 instance_xform0;
layout(location=9) in highp vec4 instance_xform1;
layout(location=10) in highp vec4 instance_xform2;
layout(location=11) in lowp vec4 instance_color;
#ifdef USE_INSTANCE_CUSTOM
layout(location=12) in highp vec4 instance_custom_data;
#endif
#endif
layout(location=4) in highp vec2 uv_attrib;
//skeletn
#endif
uniform highp vec2 color_texpixel_size;
layout(std140) uniform CanvasItemData { //ubo:0
@ -64,7 +79,10 @@ const bool at_light_pass = true;
const bool at_light_pass = false;
#endif
#ifdef USE_PARTICLES
uniform int h_frames;
uniform int v_frames;
#endif
VERTEX_SHADER_GLOBALS
@ -82,6 +100,12 @@ void main() {
vec4 vertex_color = color_attrib;
#ifdef USE_INSTANCING
mat4 extra_matrix2 = extra_matrix * transpose(mat4(instance_xform0,instance_xform1,instance_xform2,vec4(0.0,0.0,0.0,1.0)));
vertex_color*=instance_color;
#else
mat4 extra_matrix2 = extra_matrix;
#endif
#ifdef USE_TEXTURE_RECT
@ -95,6 +119,22 @@ void main() {
#endif
#ifdef USE_PARTICLES
//scale by texture size
outvec.xy/=color_texpixel_size;
//compute h and v frames and adjust UV interp for animation
int total_frames = h_frames * v_frames;
int frame = min(int(float(total_frames) *instance_custom_data.z),total_frames-1);
float frame_w = 1.0/float(h_frames);
float frame_h = 1.0/float(v_frames);
uv_interp.x = uv_interp.x * frame_w + frame_w * float(frame % h_frames);
uv_interp.y = uv_interp.y * frame_h + frame_h * float(frame / v_frames);
#endif
#define extra_matrix extra_matrix2
{
vec2 src_vtx=outvec.xy;
@ -107,6 +147,8 @@ VERTEX_SHADER_CODE
outvec = modelview_matrix * outvec;
#endif
#undef extra_matrix
color_interp = vertex_color;
#ifdef USE_PIXEL_SNAP

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 B

View file

@ -31,8 +31,8 @@
#include "canvas_item_editor_plugin.h"
#include "io/image_loader.h"
#include "scene/3d/particles.h"
#include "scene/gui/separator.h"
void Particles2DEditorPlugin::edit(Object *p_object) {
if (p_object) {
@ -62,65 +62,16 @@ void Particles2DEditorPlugin::_file_selected(const String &p_file) {
print_line("file: " + p_file);
int epc = epoints->get_value();
Ref<Image> img;
img.instance();
Error err = ImageLoader::load_image(p_file, img);
ERR_EXPLAIN(TTR("Error loading image:") + " " + p_file);
ERR_FAIL_COND(err != OK);
img->convert(Image::FORMAT_LA8);
ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8);
Size2i s = Size2(img->get_width(), img->get_height());
ERR_FAIL_COND(s.width == 0 || s.height == 0);
PoolVector<uint8_t> data = img->get_data();
PoolVector<uint8_t>::Read r = data.read();
Vector<Point2i> valid_positions;
valid_positions.resize(s.width * s.height);
int vpc = 0;
for (int i = 0; i < s.width * s.height; i++) {
uint8_t a = r[i * 2 + 1];
if (a > 128) {
valid_positions[vpc++] = Point2i(i % s.width, i / s.width);
}
}
valid_positions.resize(vpc);
ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image.."));
ERR_FAIL_COND(valid_positions.size() == 0);
PoolVector<Point2> epoints;
epoints.resize(epc);
PoolVector<Point2>::Write w = epoints.write();
Size2 extents = Size2(img->get_width() * 0.5, img->get_height() * 0.5);
for (int i = 0; i < epc; i++) {
Point2 p = valid_positions[Math::rand() % vpc];
p -= s / 2;
w[i] = p / extents;
}
w = PoolVector<Point2>::Write();
undo_redo->create_action(TTR("Set Emission Mask"));
undo_redo->add_do_method(particles, "set_emission_points", epoints);
undo_redo->add_do_method(particles, "set_emission_half_extents", extents);
undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points());
undo_redo->add_undo_method(particles, "set_emission_half_extents", particles->get_emission_half_extents());
undo_redo->commit_action();
source_emission_file = p_file;
emission_mask->popup_centered_minsize();
}
void Particles2DEditorPlugin::_menu_callback(int p_idx) {
switch (p_idx) {
case MENU_GENERATE_VISIBILITY_RECT: {
generate_aabb->popup_centered_minsize();
} break;
case MENU_LOAD_EMISSION_MASK: {
file->popup_centered_ratio();
@ -128,14 +79,249 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) {
} break;
case MENU_CLEAR_EMISSION_MASK: {
undo_redo->create_action(TTR("Clear Emission Mask"));
emission_mask->popup_centered_minsize();
/*undo_redo->create_action(TTR("Clear Emission Mask"));
undo_redo->add_do_method(particles, "set_emission_points", PoolVector<Vector2>());
undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points());
undo_redo->commit_action();
undo_redo->commit_action();*/
} break;
}
}
void Particles2DEditorPlugin::_generate_visibility_rect() {
float time = generate_seconds->get_value();
float running = 0.0;
EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time));
Rect2 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);
Rect2 capture = particles->capture_rect();
if (rect == Rect2())
rect = capture;
else
rect = rect.merge(capture);
running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0;
}
particles->set_visibility_rect(rect);
}
void Particles2DEditorPlugin::_generate_emission_mask() {
Ref<ParticlesMaterial> pm = particles->get_process_material();
if (!pm.is_valid()) {
EditorNode::get_singleton()->show_warning(TTR("Can only set point into a ParticlesMaterial process material"));
return;
}
Ref<Image> img;
img.instance();
Error err = ImageLoader::load_image(source_emission_file, img);
ERR_EXPLAIN(TTR("Error loading image:") + " " + source_emission_file);
ERR_FAIL_COND(err != OK);
if (img->is_compressed()) {
img->decompress();
}
img->convert(Image::FORMAT_RGBA8);
ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8);
Size2i s = Size2(img->get_width(), img->get_height());
ERR_FAIL_COND(s.width == 0 || s.height == 0);
Vector<Point2> valid_positions;
Vector<Point2> valid_normals;
Vector<uint8_t> valid_colors;
valid_positions.resize(s.width * s.height);
EmissionMode emode = (EmissionMode)emission_mask_mode->get_selected();
if (emode == EMISSION_MODE_BORDER_DIRECTED) {
valid_normals.resize(s.width * s.height);
}
bool capture_colors = emission_colors->is_pressed();
if (capture_colors) {
valid_colors.resize(s.width * s.height * 4);
}
int vpc = 0;
{
PoolVector<uint8_t> data = img->get_data();
PoolVector<uint8_t>::Read r = data.read();
for (int i = 0; i < s.width; i++) {
for (int j = 0; j < s.height; j++) {
uint8_t a = r[(j * s.width + i) * 4 + 3];
if (a > 128) {
if (emode == EMISSION_MODE_SOLID) {
if (capture_colors) {
valid_colors[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0];
valid_colors[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1];
valid_colors[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2];
valid_colors[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3];
}
valid_positions[vpc++] = Point2(i, j);
} else {
bool on_border = false;
for (int x = i - 1; x <= i + 1; x++) {
for (int y = j - 1; y <= j + 1; y++) {
if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) {
on_border = true;
break;
}
}
if (on_border)
break;
}
if (on_border) {
valid_positions[vpc] = Point2(i, j);
if (emode == EMISSION_MODE_BORDER_DIRECTED) {
Vector2 normal;
for (int x = i - 2; x <= i + 2; x++) {
for (int y = j - 2; y <= j + 2; y++) {
if (x == i && y == j)
continue;
if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) {
normal += Vector2(x - i, y - j).normalized();
}
}
}
normal.normalize();
valid_normals[vpc] = normal;
}
if (capture_colors) {
valid_colors[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0];
valid_colors[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1];
valid_colors[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2];
valid_colors[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3];
}
vpc++;
}
}
}
}
}
}
valid_positions.resize(vpc);
if (valid_normals.size()) {
valid_normals.resize(vpc);
}
ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image.."));
ERR_FAIL_COND(valid_positions.size() == 0);
PoolVector<uint8_t> texdata;
int w = 2048;
int h = (vpc / 2048) + 1;
texdata.resize(w * h * 2 * sizeof(float));
{
PoolVector<uint8_t>::Write tw = texdata.write();
float *twf = (float *)tw.ptr();
for (int i = 0; i < vpc; i++) {
twf[i * 2 + 0] = valid_positions[i].x;
twf[i * 2 + 1] = valid_positions[i].y;
}
}
img.instance();
img->create(w, h, false, Image::FORMAT_RGF, texdata);
Ref<ImageTexture> imgt;
imgt.instance();
imgt->create_from_image(img, 0);
pm->set_emission_point_texture(imgt);
pm->set_emission_point_count(vpc);
if (capture_colors) {
PoolVector<uint8_t> colordata;
colordata.resize(w * h * 4); //use RG texture
{
PoolVector<uint8_t>::Write tw = colordata.write();
for (int i = 0; i < vpc * 4; i++) {
tw[i] = valid_colors[i];
}
}
img.instance();
img->create(w, h, false, Image::FORMAT_RGBA8, colordata);
imgt.instance();
imgt->create_from_image(img, 0);
pm->set_emission_color_texture(imgt);
}
if (valid_normals.size()) {
pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS);
PoolVector<uint8_t> normdata;
normdata.resize(w * h * 2 * sizeof(float)); //use RG texture
{
PoolVector<uint8_t>::Write tw = normdata.write();
float *twf = (float *)tw.ptr();
for (int i = 0; i < vpc; i++) {
twf[i * 2 + 0] = valid_normals[i].x;
twf[i * 2 + 1] = valid_normals[i].y;
}
}
img.instance();
img->create(w, h, false, Image::FORMAT_RGF, normdata);
imgt.instance();
imgt->create_from_image(img, 0);
pm->set_emission_normal_texture(imgt);
} else {
pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS);
}
/*undo_redo->create_action(TTR("Set Emission Mask"));
undo_redo->add_do_method(particles, "set_emission_points", epoints);
undo_redo->add_do_method(particles, "set_emission_half_extents", extents);
undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points());
undo_redo->add_undo_method(particles, "set_emission_half_extents", particles->get_emission_half_extents());
undo_redo->commit_action();
*/
}
void Particles2DEditorPlugin::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
@ -150,6 +336,8 @@ void Particles2DEditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("_menu_callback"), &Particles2DEditorPlugin::_menu_callback);
ClassDB::bind_method(D_METHOD("_file_selected"), &Particles2DEditorPlugin::_file_selected);
ClassDB::bind_method(D_METHOD("_generate_visibility_rect"), &Particles2DEditorPlugin::_generate_visibility_rect);
ClassDB::bind_method(D_METHOD("_generate_emission_mask"), &Particles2DEditorPlugin::_generate_emission_mask);
}
Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) {
@ -165,8 +353,10 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) {
toolbar->add_child(memnew(VSeparator));
menu = memnew(MenuButton);
menu->get_popup()->add_item(TTR("Generate Visibility Rect"), MENU_GENERATE_VISIBILITY_RECT);
menu->get_popup()->add_separator();
menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK);
menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK);
// menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK);
menu->set_text("Particles");
toolbar->add_child(menu);
@ -185,6 +375,37 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) {
epoints->set_step(1);
epoints->set_value(512);
file->get_vbox()->add_margin_child(TTR("Generated Point Count:"), epoints);
generate_aabb = memnew(ConfirmationDialog);
generate_aabb->set_title(TTR("Generate Visibility Rect"));
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);
toolbar->add_child(generate_aabb);
generate_aabb->connect("confirmed", this, "_generate_visibility_rect");
emission_mask = memnew(ConfirmationDialog);
emission_mask->set_title(TTR("Generate Visibility Rect"));
VBoxContainer *emvb = memnew(VBoxContainer);
emission_mask->add_child(emvb);
emission_mask_mode = memnew(OptionButton);
emvb->add_margin_child(TTR("Emission Mask"), emission_mask_mode);
emission_mask_mode->add_item("Solid Pixels", EMISSION_MODE_SOLID);
emission_mask_mode->add_item("Border Pixels", EMISSION_MODE_BORDER);
emission_mask_mode->add_item("Directed Border Pixels", EMISSION_MODE_BORDER_DIRECTED);
emission_colors = memnew(CheckBox);
emission_colors->set_text(TTR("Capture from Pixel"));
emvb->add_margin_child(TTR("Emission Colors"), emission_colors);
toolbar->add_child(emission_mask);
emission_mask->connect("confirmed", this, "_generate_emission_mask");
}
Particles2DEditorPlugin::~Particles2DEditorPlugin() {

View file

@ -44,10 +44,17 @@ class Particles2DEditorPlugin : public EditorPlugin {
enum {
MENU_GENERATE_VISIBILITY_RECT,
MENU_LOAD_EMISSION_MASK,
MENU_CLEAR_EMISSION_MASK
};
enum EmissionMode {
EMISSION_MODE_SOLID,
EMISSION_MODE_BORDER,
EMISSION_MODE_BORDER_DIRECTED
};
Particles2D *particles;
EditorFileDialog *file;
@ -58,9 +65,20 @@ class Particles2DEditorPlugin : public EditorPlugin {
SpinBox *epoints;
ConfirmationDialog *generate_aabb;
SpinBox *generate_seconds;
ConfirmationDialog *emission_mask;
OptionButton *emission_mask_mode;
CheckBox *emission_colors;
String source_emission_file;
UndoRedo *undo_redo;
void _file_selected(const String &p_file);
void _menu_callback(int p_idx);
void _generate_visibility_rect();
void _generate_emission_mask();
protected:
void _notification(int p_what);

View file

@ -417,14 +417,22 @@ void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color
VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width, p_antialiased);
}
void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color) {
void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled) {
if (!drawing) {
ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL();
}
VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color);
if (p_filled) {
VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color);
} else {
VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position, p_rect.position + Size2(p_rect.size.width, 0), p_color);
VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position, p_rect.position + Size2(0, p_rect.size.height), p_color);
VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position + Point2(0, p_rect.size.height), p_rect.position + p_rect.size, p_color);
VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position + Point2(p_rect.size.width, 0), p_rect.position + p_rect.size, p_color);
}
}
void CanvasItem::draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color) {
@ -754,7 +762,7 @@ void CanvasItem::_bind_methods() {
//ClassDB::bind_method(D_METHOD("get_transform"),&CanvasItem::get_transform);
ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color"), &CanvasItem::draw_rect);
ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled"), &CanvasItem::draw_rect, DEFVAL(true));
ClassDB::bind_method(D_METHOD("draw_circle", "pos", "radius", "color"), &CanvasItem::draw_circle);
ClassDB::bind_method(D_METHOD("draw_texture", "texture:Texture", "pos", "modulate", "normal_map:Texture"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture:Texture", "rect", "tile", "modulate", "transpose", "normal_map:Texture"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()));

View file

@ -156,7 +156,7 @@ public:
/* DRAWING API */
void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
void draw_rect(const Rect2 &p_rect, const Color &p_color);
void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true);
void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color);
void draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1), const Ref<Texture> &p_normal_map = Ref<Texture>());
void draw_texture_rect(const Ref<Texture> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>());

File diff suppressed because it is too large Load diff

View file

@ -34,235 +34,98 @@
#include "scene/resources/color_ramp.h"
#include "scene/resources/texture.h"
class Particles2D;
class ParticleAttractor2D : public Node2D {
GDCLASS(ParticleAttractor2D, Node2D);
friend class Particles2D;
bool enabled;
float radius;
float disable_radius;
float gravity;
float absorption;
NodePath path;
Particles2D *owner;
void _update_owner();
void _owner_exited();
void _set_owner(Particles2D *p_owner);
void _notification(int p_what);
static void _bind_methods();
public:
void set_enabled(bool p_enabled);
bool is_enabled() const;
void set_radius(float p_radius);
float get_radius() const;
void set_disable_radius(float p_disable_radius);
float get_disable_radius() const;
void set_gravity(float p_gravity);
float get_gravity() const;
void set_absorption(float p_absorption);
float get_absorption() const;
void set_particles_path(NodePath p_path);
NodePath get_particles_path() const;
virtual String get_configuration_warning() const;
ParticleAttractor2D();
};
class Particles2D : public Node2D {
GDCLASS(Particles2D, Node2D);
private:
GDCLASS(Particles2D, Node2D)
public:
enum Parameter {
PARAM_DIRECTION,
PARAM_SPREAD,
PARAM_LINEAR_VELOCITY,
PARAM_SPIN_VELOCITY,
PARAM_ORBIT_VELOCITY,
PARAM_GRAVITY_DIRECTION,
PARAM_GRAVITY_STRENGTH,
PARAM_RADIAL_ACCEL,
PARAM_TANGENTIAL_ACCEL,
PARAM_DAMPING,
PARAM_INITIAL_ANGLE,
PARAM_INITIAL_SIZE,
PARAM_FINAL_SIZE,
PARAM_HUE_VARIATION,
PARAM_ANIM_SPEED_SCALE,
PARAM_ANIM_INITIAL_POS,
PARAM_MAX
};
enum {
MAX_COLOR_PHASES = 4
};
enum ProcessMode {
PROCESS_FIXED,
PROCESS_IDLE,
enum DrawOrder {
DRAW_ORDER_INDEX,
DRAW_ORDER_LIFETIME,
};
private:
float param[PARAM_MAX];
float randomness[PARAM_MAX];
RID particles;
struct Particle {
bool active;
Point2 pos;
Vector2 velocity;
float rot;
float frame;
uint64_t seed;
Particle() {
active = false;
seed = 123465789;
rot = 0;
frame = 0;
}
};
Vector<Particle> particles;
struct AttractorCache {
Vector2 pos;
ParticleAttractor2D *attractor;
};
Vector<AttractorCache> attractor_cache;
float explosiveness;
float preprocess;
float lifetime;
bool emitting;
bool local_space;
float emit_timeout;
float time_to_live;
float time_scale;
bool flip_h;
bool flip_v;
int h_frames;
int amount;
float lifetime;
float pre_process_time;
float explosiveness_ratio;
float randomness_ratio;
float speed_scale;
Rect2 visibility_rect;
bool local_coords;
int fixed_fps;
bool fractional_delta;
int v_frames;
Point2 emissor_offset;
Vector2 initial_velocity;
Vector2 extents;
PoolVector<Vector2> emission_points;
int h_frames;
ProcessMode process_mode;
Ref<Material> process_material;
float time;
int active_count;
DrawOrder draw_order;
Ref<Texture> texture;
Ref<Texture> normal_map;
//If no color ramp is set then default color is used. Created as simple alternative to color_ramp.
Color default_color;
Ref<Gradient> gradient;
void _process_particles(float p_delta);
friend class ParticleAttractor2D;
Set<ParticleAttractor2D *> attractors;
void _update_particle_emission_transform();
protected:
void _notification(int p_what);
static void _bind_methods();
virtual void _validate_property(PropertyInfo &property) const;
void _notification(int p_what);
public:
void set_emitting(bool p_emitting);
bool is_emitting() const;
void set_process_mode(ProcessMode p_mode);
ProcessMode get_process_mode() const;
void set_amount(int p_amount);
int get_amount() const;
void set_lifetime(float p_lifetime);
void set_pre_process_time(float p_time);
void set_explosiveness_ratio(float p_ratio);
void set_randomness_ratio(float p_ratio);
void set_visibility_rect(const Rect2 &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;
float get_lifetime() const;
void set_time_scale(float p_time_scale);
float get_time_scale() const;
void set_pre_process_time(float p_pre_process_time);
float get_pre_process_time() const;
float get_explosiveness_ratio() const;
float get_randomness_ratio() const;
Rect2 get_visibility_rect() const;
bool get_use_local_coordinates() const;
Ref<Material> get_process_material() const;
float get_speed_scale() const;
void set_emit_timeout(float p_timeout);
float get_emit_timeout() const;
void set_fixed_fps(int p_count);
int get_fixed_fps() const;
void set_emission_half_extents(const Vector2 &p_extents);
Vector2 get_emission_half_extents() const;
void set_fractional_delta(bool p_enable);
bool get_fractional_delta() const;
void set_param(Parameter p_param, float p_value);
float get_param(Parameter p_param) const;
void set_randomness(Parameter p_randomness, float p_value);
float get_randomness(Parameter p_randomness) const;
void set_explosiveness(float p_value);
float get_explosiveness() const;
void set_flip_h(bool p_flip);
bool is_flipped_h() const;
void set_flip_v(bool p_flip);
bool is_flipped_v() const;
void set_h_frames(int p_frames);
int get_h_frames() const;
void set_v_frames(int p_frames);
int get_v_frames() const;
void set_color_phases(int p_phases);
int get_color_phases() const;
void set_color_phase_color(int p_phase, const Color &p_color);
Color get_color_phase_color(int p_phase) const;
void set_color_phase_pos(int p_phase, float p_pos);
float get_color_phase_pos(int p_phase) const;
void set_draw_order(DrawOrder p_order);
DrawOrder get_draw_order() const;
void set_texture(const Ref<Texture> &p_texture);
Ref<Texture> get_texture() const;
void set_color(const Color &p_color);
Color get_color() const;
void set_normal_map(const Ref<Texture> &p_normal_map);
Ref<Texture> get_normal_map() const;
void set_gradient(const Ref<Gradient> &p_texture);
Ref<Gradient> get_gradient() const;
virtual String get_configuration_warning() const;
void set_emissor_offset(const Point2 &p_offset);
Point2 get_emissor_offset() const;
void set_v_frames(int p_count);
int get_v_frames() const;
void set_use_local_space(bool p_use);
bool is_using_local_space() const;
void set_initial_velocity(const Vector2 &p_velocity);
Vector2 get_initial_velocity() const;
void set_emission_points(const PoolVector<Vector2> &p_points);
PoolVector<Vector2> get_emission_points() const;
void pre_process(float p_delta);
void reset();
void set_h_frames(int p_count);
int get_h_frames() const;
Rect2 capture_rect() const;
Particles2D();
~Particles2D();
};
VARIANT_ENUM_CAST(Particles2D::ProcessMode);
VARIANT_ENUM_CAST(Particles2D::Parameter);
VARIANT_ENUM_CAST(Particles2D::DrawOrder)
#endif // PARTICLES_FRAME_H

View file

@ -404,6 +404,7 @@ void ParticlesMaterial::init_shaders() {
shader_names->emission_texture_point_count = "emission_texture_point_count";
shader_names->emission_texture_points = "emission_texture_points";
shader_names->emission_texture_normal = "emission_texture_normal";
shader_names->emission_texture_color = "emission_texture_color";
shader_names->trail_divisor = "trail_divisor";
shader_names->trail_size_modifier = "trail_size_modifier";
@ -481,6 +482,28 @@ void ParticlesMaterial::_update_shader() {
code += "uniform float anim_speed_random;\n";
code += "uniform float anim_offset_random;\n";
switch (emission_shape) {
case EMISSION_SHAPE_POINT: {
//do none
} break;
case EMISSION_SHAPE_SPHERE: {
code += "uniform float emission_sphere_radius;\n";
} break;
case EMISSION_SHAPE_BOX: {
code += "uniform vec3 emission_box_extents;\n";
} break;
case EMISSION_SHAPE_DIRECTED_POINTS: {
code += "uniform sampler2D emission_texture_normal : hint_black;\n";
} //fallthrough
case EMISSION_SHAPE_POINTS: {
code += "uniform sampler2D emission_texture_points : hint_black;\n";
code += "uniform int emission_texture_point_count;\n";
if (emission_color_texture.is_valid()) {
code += "uniform sampler2D emission_texture_color : hint_white;\n";
}
} break;
}
code += "uniform vec4 color_value : hint_color;\n";
code += "uniform int trail_divisor;\n";
@ -515,25 +538,6 @@ void ParticlesMaterial::_update_shader() {
if (tex_parameters[PARAM_ANIM_OFFSET].is_valid())
code += "uniform sampler2D anim_offset_texture;\n";
switch (emission_shape) {
case EMISSION_SHAPE_POINT: {
//do none
} break;
case EMISSION_SHAPE_SPHERE: {
code += "uniform float emission_sphere_radius;\n";
} break;
case EMISSION_SHAPE_BOX: {
code += "uniform vec3 emission_box_extents;\n";
} break;
case EMISSION_SHAPE_DIRECTED_POINTS: {
code += "uniform sampler2D emission_texture_normal : hint_black;\n";
} //fallthrough
case EMISSION_SHAPE_POINTS: {
code += "uniform sampler2D emission_texture_points : hint_black;\n";
code += "uniform int emission_texture_point_count;\n";
} break;
}
if (trail_size_modifier.is_valid()) {
code += "uniform sampler2D trail_size_modifier;\n";
}
@ -576,6 +580,11 @@ void ParticlesMaterial::_update_shader() {
code += "\n";
code += "\n";
code += "\n";
if (emission_shape >= EMISSION_SHAPE_POINTS) {
code += " int point = min(emission_texture_point_count-1,int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n";
code += " ivec2 emission_tex_size = textureSize( emission_texture_points, 0 );\n";
code += " ivec2 emission_tex_ofs = ivec2( point % emission_tex_size.x, point / emission_tex_size.x );\n";
}
code += " if (RESTART) {\n";
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid())
@ -593,11 +602,21 @@ void ParticlesMaterial::_update_shader() {
else
code += " float tex_anim_offset = 0.0;\n";
code += " float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n";
code += " float angle2 = rand_from_seed(alt_seed)*20.0*3.1416; // make it more random like\n";
code += " vec3 rot_xz=vec3( sin(angle1), 0.0, cos(angle1) );\n";
code += " vec3 rot = vec3( cos(angle2)*rot_xz.x,sin(angle2)*rot_xz.x, rot_xz.z);\n";
code += " VELOCITY=(rot*initial_linear_velocity+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\n";
if (flags[FLAG_DISABLE_Z]) {
code += " float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n";
code += " vec3 rot=vec3( cos(angle1), sin(angle1),0.0 );\n";
code += " VELOCITY=(rot*initial_linear_velocity+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\n";
} else {
//initiate velocity spread in 3D
code += " float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n";
code += " float angle2 = rand_from_seed(alt_seed)*20.0*3.1416; // make it more random like\n";
code += " vec3 rot_xz=vec3( sin(angle1), 0.0, cos(angle1) );\n";
code += " vec3 rot = vec3( cos(angle2)*rot_xz.x,sin(angle2)*rot_xz.x, rot_xz.z);\n";
code += " VELOCITY=(rot*initial_linear_velocity+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\n";
}
code += " float base_angle=(initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n";
code += " CUSTOM.x=base_angle*3.1416/180.0;\n"; //angle
code += " CUSTOM.y=0.0;\n"; //phase
@ -614,21 +633,31 @@ void ParticlesMaterial::_update_shader() {
} break;
case EMISSION_SHAPE_POINTS:
case EMISSION_SHAPE_DIRECTED_POINTS: {
code += " int point = min(emission_texture_point_count-1,int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n";
code += " ivec2 tex_size = textureSize( emission_texture_points, 0 );\n";
code += " ivec2 tex_ofs = ivec2( point % tex_size.x, point / tex_size.x );\n";
code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, tex_ofs,0).xyz;\n";
code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs,0).xyz;\n";
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
code += " vec3 normal = texelFetch(emission_texture_normal, tex_ofs,0).xyz;\n";
code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0, 1.0, 0.0);\n";
code += " vec3 tangent = normalize(cross(v0, normal));\n";
code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
code += " VELOCITY = mat3(tangent,bitangent,normal) * VELOCITY;\n";
if (flags[FLAG_DISABLE_Z]) {
code += " mat2 rotm;";
code += " rotm[0]=texelFetch(emission_texture_normal, emission_tex_ofs,0).xy;\n";
code += " rotm[1]=rotm[0].yx * vec2(1.0,-1.0);\n";
code += " VELOCITY.xy = rotm * VELOCITY.xy;\n";
} else {
code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs,0).xyz;\n";
code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0, 1.0, 0.0);\n";
code += " vec3 tangent = normalize(cross(v0, normal));\n";
code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
code += " VELOCITY = mat3(tangent,bitangent,normal) * VELOCITY;\n";
}
}
} break;
}
code += " VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY,0.0)).xyz;\n";
code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
if (flags[FLAG_DISABLE_Z]) {
code += " VELOCITY.z=0.0;\n";
code += " TRANSFORM[3].z=0.0;\n";
}
code += " } else {\n";
@ -685,6 +714,9 @@ void ParticlesMaterial::_update_shader() {
code += " vec3 force = gravity; \n";
code += " vec3 pos = TRANSFORM[3].xyz; \n";
if (flags[FLAG_DISABLE_Z]) {
code += " pos.z=0.0; \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";
code += " //apply radial acceleration\n";
@ -693,11 +725,17 @@ 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";
if (flags[FLAG_DISABLE_Z]) {
code += " force+=vec3(normalize((pos-org).yx * vec2(-1.0,1.0)),0.0) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n";
} else {
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())
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
code += " VELOCITY=normalize(VELOCITY)*tex_linear_velocity;\n";
}
code += " if (damping+tex_damping>0.0) {\n";
code += " \n";
code += " float v = length(VELOCITY);\n";
@ -709,9 +747,16 @@ void ParticlesMaterial::_update_shader() {
code += " VELOCITY=normalize(VELOCITY) * v;\n";
code += " }\n";
code += " }\n";
code += " float base_angle=(initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random)*3.1416/180.0;\n";
code += " CUSTOM.x=((base_angle+tex_angle)+CUSTOM.y*LIFETIME*(angular_velocity+tex_angular_velocity)*mix(1.0,rand_from_seed(alt_seed)*2.0-1.0,angular_velocity_random))*3.1416/180.0;\n"; //angle
code += " CUSTOM.z=(anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random)+CUSTOM.y*LIFETIME*(anim_speed+tex_anim_speed)*mix(1.0,rand_from_seed(alt_seed),anim_speed_random);\n"; //angle
code += " float base_angle=(initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n";
code += " base_angle+=CUSTOM.y*LIFETIME*(angular_velocity+tex_angular_velocity)*mix(1.0,rand_from_seed(alt_seed)*2.0-1.0,angular_velocity_random);\n";
code += " CUSTOM.x=base_angle*3.1416/180.0;\n"; //angle
code += " CUSTOM.z=(anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random)+CUSTOM.y*(anim_speed+tex_anim_speed)*mix(1.0,rand_from_seed(alt_seed),anim_speed_random);\n"; //angle
if (flags[FLAG_ANIM_LOOP]) {
code += " CUSTOM.z=mod(CUSTOM.z,1.0);\n"; //loop
} else {
code += " CUSTOM.z=clamp(CUSTOM.z,0.0,1.0);\n"; //0 to 1 only
}
code += " }\n";
//apply color
//apply hue rotation
@ -747,28 +792,40 @@ void ParticlesMaterial::_update_shader() {
} else {
code += " COLOR = color_value * hue_rot_mat;\n";
}
if (emission_color_texture.is_valid() && emission_shape >= EMISSION_SHAPE_POINTS) {
code += " COLOR*= texelFetch(emission_texture_color,emission_tex_ofs,0);\n";
}
if (trail_color_modifier.is_valid()) {
code += "if (trail_divisor>1) { COLOR*=textureLod(trail_color_modifier,vec2(float(int(NUMBER)%trail_divisor)/float(trail_divisor-1),0.0),0.0); }\n";
}
code += "\n";
//orient particle Y towards velocity
if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
code += " if (length(VELOCITY)>0.0) {TRANSFORM[1].xyz=normalize(VELOCITY);} else {TRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);}\n";
code += " if (TRANSFORM[1].xyz==normalize(TRANSFORM[0].xyz)) {\n";
code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
code += " } else {\n";
code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
code += " }\n";
if (flags[FLAG_DISABLE_Z]) {
code += " TRANSFORM[0]=vec4(cos(CUSTOM.x),-sin(CUSTOM.x),0.0,0.0);\n";
code += " TRANSFORM[1]=vec4(sin(CUSTOM.x),cos(CUSTOM.x),0.0,0.0);\n";
code += " TRANSFORM[2]=vec4(0.0,0.0,1.0,0.0);\n";
} else {
code += "\tTRANSFORM[0].xyz=normalize(TRANSFORM[0].xyz);\n";
code += "\tTRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);\n";
code += "\tTRANSFORM[2].xyz=normalize(TRANSFORM[2].xyz);\n";
}
//turn particle by rotation in Y
if (flags[FLAG_ROTATE_Y]) {
code += "\tTRANSFORM = TRANSFORM * mat4( vec4(cos(CUSTOM.x),0.0,-sin(CUSTOM.x),0.0), vec4(0.0,1.0,0.0,0.0),vec4(sin(CUSTOM.x),0.0,cos(CUSTOM.x),0.0),vec4(0.0,0.0,0.0,1.0));\n";
//orient particle Y towards velocity
if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
code += " if (length(VELOCITY)>0.0) {TRANSFORM[1].xyz=normalize(VELOCITY);} else {TRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);}\n";
code += " if (TRANSFORM[1].xyz==normalize(TRANSFORM[0].xyz)) {\n";
code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
code += " } else {\n";
code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
code += " }\n";
} else {
code += "\tTRANSFORM[0].xyz=normalize(TRANSFORM[0].xyz);\n";
code += "\tTRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);\n";
code += "\tTRANSFORM[2].xyz=normalize(TRANSFORM[2].xyz);\n";
}
//turn particle by rotation in Y
if (flags[FLAG_ROTATE_Y]) {
code += "\tTRANSFORM = TRANSFORM * mat4( vec4(cos(CUSTOM.x),0.0,-sin(CUSTOM.x),0.0), vec4(0.0,1.0,0.0,0.0),vec4(sin(CUSTOM.x),0.0,cos(CUSTOM.x),0.0),vec4(0.0,0.0,0.0,1.0));\n";
}
}
//scale by scale
code += " float base_scale=mix(scale*tex_scale,1.0,scale_random*scale_rand);\n";
@ -779,6 +836,10 @@ void ParticlesMaterial::_update_shader() {
code += " TRANSFORM[0].xyz*=base_scale;\n";
code += " TRANSFORM[1].xyz*=base_scale;\n";
code += " TRANSFORM[2].xyz*=base_scale;\n";
if (flags[FLAG_DISABLE_Z]) {
code += " VELOCITY.z=0.0;\n";
code += " TRANSFORM[3].z=0.0;\n";
}
code += "}\n";
code += "\n";
@ -1130,6 +1191,16 @@ void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture> &p_normal
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, texture);
}
void ParticlesMaterial::set_emission_color_texture(const Ref<Texture> &p_colors) {
emission_color_texture = p_colors;
RID texture;
if (p_colors.is_valid())
texture = p_colors->get_rid();
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, texture);
_queue_shader_change();
}
void ParticlesMaterial::set_emission_point_count(int p_count) {
emission_point_count = p_count;
@ -1158,6 +1229,11 @@ Ref<Texture> ParticlesMaterial::get_emission_normal_texture() const {
return emission_normal_texture;
}
Ref<Texture> ParticlesMaterial::get_emission_color_texture() const {
return emission_color_texture;
}
int ParticlesMaterial::get_emission_point_count() const {
return emission_point_count;
@ -1247,7 +1323,7 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}
if (property.name == "emission_point_texture" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) {
if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) {
property.usage = 0;
}
@ -1301,6 +1377,9 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_emission_normal_texture", "texture:Texture"), &ParticlesMaterial::set_emission_normal_texture);
ClassDB::bind_method(D_METHOD("get_emission_normal_texture:Texture"), &ParticlesMaterial::get_emission_normal_texture);
ClassDB::bind_method(D_METHOD("set_emission_color_texture", "texture:Texture"), &ParticlesMaterial::set_emission_color_texture);
ClassDB::bind_method(D_METHOD("get_emission_color_texture:Texture"), &ParticlesMaterial::get_emission_color_texture);
ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticlesMaterial::set_emission_point_count);
ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count);
@ -1326,10 +1405,12 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_point_texture", "get_emission_point_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_normal_texture", "get_emission_normal_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_color_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_color_texture", "get_emission_color_texture");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_point_count", PROPERTY_HINT_RANGE, "0,1000000,1"), "set_emission_point_count", "get_emission_point_count");
ADD_GROUP("Flags", "flag_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_flag", "get_flag", FLAG_ALIGN_Y_TO_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_flag", "get_flag", FLAG_ROTATE_Y);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_flag", "get_flag", FLAG_DISABLE_Z);
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");
@ -1379,12 +1460,13 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION);
ADD_GROUP("Animation", "anim_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_param", "get_param", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_flag", "get_flag", FLAG_ANIM_LOOP);
BIND_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_CONSTANT(PARAM_ANGULAR_VELOCITY);

View file

@ -154,6 +154,8 @@ public:
enum Flags {
FLAG_ALIGN_Y_TO_VELOCITY,
FLAG_ROTATE_Y,
FLAG_DISABLE_Z,
FLAG_ANIM_LOOP,
FLAG_MAX
};
@ -171,11 +173,12 @@ private:
struct {
uint32_t texture_mask : 16;
uint32_t texture_color : 1;
uint32_t flags : 2;
uint32_t flags : 4;
uint32_t emission_shape : 2;
uint32_t trail_size_texture : 1;
uint32_t trail_color_texture : 1;
uint32_t invalid_key : 1;
uint32_t has_emission_color : 1;
};
uint32_t key;
@ -213,6 +216,7 @@ private:
mk.emission_shape = emission_shape;
mk.trail_color_texture = trail_color_modifier.is_valid() ? 1 : 0;
mk.trail_size_texture = trail_size_modifier.is_valid() ? 1 : 0;
mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid();
return mk;
}
@ -269,6 +273,7 @@ private:
StringName emission_texture_point_count;
StringName emission_texture_points;
StringName emission_texture_normal;
StringName emission_texture_color;
StringName trail_divisor;
StringName trail_size_modifier;
@ -302,8 +307,11 @@ private:
Vector3 emission_box_extents;
Ref<Texture> emission_point_texture;
Ref<Texture> emission_normal_texture;
Ref<Texture> emission_color_texture;
int emission_point_count;
bool anim_loop;
int trail_divisor;
Ref<CurveTexture> trail_size_modifier;
@ -347,6 +355,7 @@ public:
void set_emission_box_extents(Vector3 p_extents);
void set_emission_point_texture(const Ref<Texture> &p_points);
void set_emission_normal_texture(const Ref<Texture> &p_normals);
void set_emission_color_texture(const Ref<Texture> &p_colors);
void set_emission_point_count(int p_count);
EmissionShape get_emission_shape() const;
@ -354,6 +363,7 @@ public:
Vector3 get_emission_box_extents() const;
Ref<Texture> get_emission_point_texture() const;
Ref<Texture> get_emission_normal_texture() const;
Ref<Texture> get_emission_color_texture() const;
int get_emission_point_count() const;
void set_trail_divisor(int p_divisor);

View file

@ -471,7 +471,7 @@ void register_scene_types() {
ClassDB::register_virtual_class<CanvasItem>();
ClassDB::register_class<Node2D>();
ClassDB::register_class<Particles2D>();
ClassDB::register_class<ParticleAttractor2D>();
//ClassDB::register_class<ParticleAttractor2D>();
ClassDB::register_class<Sprite>();
//ClassDB::register_type<ViewportSprite>();
ClassDB::register_class<SpriteFrames>();

View file

@ -612,6 +612,7 @@ public:
TYPE_POLYGON,
TYPE_MESH,
TYPE_MULTIMESH,
TYPE_PARTICLES,
TYPE_CIRCLE,
TYPE_TRANSFORM,
TYPE_CLIP_IGNORE,
@ -707,6 +708,16 @@ public:
CommandMultiMesh() { type = TYPE_MULTIMESH; }
};
struct CommandParticles : public Command {
RID particles;
RID texture;
RID normal_map;
int h_frames;
int v_frames;
CommandParticles() { type = TYPE_PARTICLES; }
};
struct CommandCircle : public Command {
Point2 pos;
@ -844,6 +855,15 @@ public:
r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
} break;
case Item::Command::TYPE_PARTICLES: {
const Item::CommandParticles *particles_cmd = static_cast<const Item::CommandParticles *>(c);
if (particles_cmd->particles.is_valid()) {
Rect3 aabb = RasterizerStorage::base_singleton->particles_get_aabb(particles_cmd->particles);
r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
}
} break;
case Item::Command::TYPE_CIRCLE: {

View file

@ -637,6 +637,25 @@ void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID
canvas_item->commands.push_back(m);
}
void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
ERR_FAIL_COND(!canvas_item);
Item::CommandParticles *part = memnew(Item::CommandParticles);
ERR_FAIL_COND(!part);
part->particles = p_particles;
part->texture = p_texture;
part->normal_map = p_normal;
part->h_frames = p_h_frames;
part->v_frames = p_v_frames;
//take the chance and request processing for them, at least once until they become visible again
VSG::storage->particles_request_process(p_particles);
canvas_item->commands.push_back(part);
}
void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton) {
Item *canvas_item = canvas_item_owner.getornull(p_item);

View file

@ -173,6 +173,7 @@ public:
void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID());
void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_skeleton = RID());
void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton = RID());
void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames);
void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform);
void canvas_item_add_clip_ignore(RID p_item, bool p_ignore);
void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable);

View file

@ -877,7 +877,8 @@ public:
BIND2(particles_set_draw_passes, RID, int)
BIND3(particles_set_draw_pass_mesh, RID, int, RID)
BIND1R(Rect3, particles_get_current_aabb, RID);
BIND1R(Rect3, particles_get_current_aabb, RID)
BIND2(particles_set_emission_transform, RID, const Transform &)
#undef BINDBASE
//from now on, calls forwarded to this singleton
@ -1049,6 +1050,7 @@ public:
BIND8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID)
BIND3(canvas_item_add_mesh, RID, const RID &, RID)
BIND3(canvas_item_add_multimesh, RID, RID, RID)
BIND6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
BIND2(canvas_item_add_set_transform, RID, const Transform2D &)
BIND2(canvas_item_add_clip_ignore, RID, bool)
BIND2(canvas_item_set_sort_children_by_y, RID, bool)

View file

@ -320,6 +320,7 @@ public:
FUNC2(particles_set_draw_passes, RID, int)
FUNC3(particles_set_draw_pass_mesh, RID, int, RID)
FUNC2(particles_set_emission_transform, RID, const Transform &)
FUNC1R(Rect3, particles_get_current_aabb, RID)
@ -476,6 +477,7 @@ public:
FUNC8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID)
FUNC3(canvas_item_add_mesh, RID, const RID &, RID)
FUNC3(canvas_item_add_multimesh, RID, RID, RID)
FUNC6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
FUNC2(canvas_item_add_set_transform, RID, const Transform2D &)
FUNC2(canvas_item_add_clip_ignore, RID, bool)
FUNC2(canvas_item_set_sort_children_by_y, RID, bool)

View file

@ -501,6 +501,8 @@ public:
virtual Rect3 particles_get_current_aabb(RID p_particles) = 0;
virtual void particles_set_emission_transform(RID p_particles, const Transform &p_transform) = 0; //this is only used for 2D, in 3D it's automatic
/* CAMERA API */
virtual RID camera_create() = 0;
@ -793,6 +795,7 @@ public:
virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID()) = 0;
virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_skeleton = RID()) = 0;
virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton = RID()) = 0;
virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map, int p_h_frames, int p_v_frames) = 0;
virtual void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform) = 0;
virtual void canvas_item_add_clip_ignore(RID p_item, bool p_ignore) = 0;
virtual void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable) = 0;