From f4a56e7782526e5e20a4351c4c293a7b4f020acd Mon Sep 17 00:00:00 2001 From: reduz Date: Fri, 30 Dec 2016 08:35:54 -0300 Subject: [PATCH] begin work on new particle system --- drivers/gles3/rasterizer_storage_gles3.cpp | 194 +++++++++++- drivers/gles3/rasterizer_storage_gles3.h | 86 +++++- drivers/gles3/shaders/SCsub | 1 + drivers/gles3/shaders/particles.glsl | 114 +++++++ drivers/gles3/shaders/scene.glsl | 10 +- scene/2d/particles_2d.h | 1 - scene/3d/gi_probe.cpp | 11 +- scene/3d/gi_probe.h | 2 + servers/visual/rasterizer.h | 36 ++- servers/visual/visual_server_raster.h | 28 ++ servers/visual/visual_server_scene.cpp | 341 +++++++++++++++++++-- servers/visual/visual_server_scene.h | 11 +- servers/visual_server.h | 56 ++++ 13 files changed, 832 insertions(+), 59 deletions(-) create mode 100644 drivers/gles3/shaders/particles.glsl diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 698788316ce..3fc05135423 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -4882,37 +4882,45 @@ uint32_t RasterizerStorageGLES3::gi_probe_get_version(RID p_probe) { return gip->version; } -RID RasterizerStorageGLES3::gi_probe_dynamic_data_create(int p_width,int p_height,int p_depth) { +RasterizerStorage::GIProbeCompression RasterizerStorageGLES3::gi_probe_get_dynamic_data_get_preferred_compression() const { + if (config.s3tc_supported) { + return GI_PROBE_S3TC; + } else { + return GI_PROBE_UNCOMPRESSED; + } +} + +RID RasterizerStorageGLES3::gi_probe_dynamic_data_create(int p_width, int p_height, int p_depth, GIProbeCompression p_compression) { GIProbeData *gipd = memnew( GIProbeData ); gipd->width=p_width; gipd->height=p_height; gipd->depth=p_depth; + gipd->compression=p_compression; glActiveTexture(GL_TEXTURE0); glGenTextures(1,&gipd->tex_id); glBindTexture(GL_TEXTURE_3D,gipd->tex_id); int level=0; + int min_size=1; + + if (gipd->compression==GI_PROBE_S3TC) { + min_size=4; + } print_line("dyndata create"); while(true) { - Vector data; - data.resize(p_width*p_height*p_depth*4); - - - for(int i=0;icompression==GI_PROBE_S3TC) { + int size = p_width * p_height * p_depth; + glCompressedTexImage3D(GL_TEXTURE_3D,level,_EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT,p_width,p_height,p_depth,0, size,NULL); + } else { + glTexImage3D(GL_TEXTURE_3D,level,GL_RGBA8,p_width,p_height,p_depth,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL); } - glTexImage3D(GL_TEXTURE_3D,level,GL_RGBA8,p_width,p_height,p_depth,0,GL_RGBA,GL_UNSIGNED_BYTE,data.ptr()); - if (p_width<=1 || p_height<=1 || p_depth<=1) + if (p_width<=min_size || p_height<=min_size || p_depth<=min_size) break; p_width>>=1; p_height>>=1; @@ -4933,7 +4941,7 @@ RID RasterizerStorageGLES3::gi_probe_dynamic_data_create(int p_width,int p_heigh return gi_probe_data_owner.make_rid(gipd); } -void RasterizerStorageGLES3::gi_probe_dynamic_data_update_rgba8(RID p_gi_probe_data, int p_depth_slice, int p_slice_count, int p_mipmap, const void *p_data) { +void RasterizerStorageGLES3::gi_probe_dynamic_data_update(RID p_gi_probe_data, int p_depth_slice, int p_slice_count, int p_mipmap, const void *p_data) { GIProbeData *gipd = gi_probe_data_owner.getornull(p_gi_probe_data); ERR_FAIL_COND(!gipd); @@ -4957,14 +4965,168 @@ void RasterizerStorageGLES3::gi_probe_dynamic_data_update_rgba8(RID p_gi_probe_d */ glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_3D,gipd->tex_id); - glTexSubImage3D(GL_TEXTURE_3D,p_mipmap,0,0,p_depth_slice,gipd->width>>p_mipmap,gipd->height>>p_mipmap,p_slice_count,GL_RGBA,GL_UNSIGNED_BYTE,p_data); + if (gipd->compression==GI_PROBE_S3TC) { + int size = (gipd->width>>p_mipmap) * (gipd->height>>p_mipmap) * p_slice_count; + glCompressedTexSubImage3D(GL_TEXTURE_3D,p_mipmap,0,0,p_depth_slice,gipd->width>>p_mipmap,gipd->height>>p_mipmap,p_slice_count,_EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT,size, p_data); + } else { + glTexSubImage3D(GL_TEXTURE_3D,p_mipmap,0,0,p_depth_slice,gipd->width>>p_mipmap,gipd->height>>p_mipmap,p_slice_count,GL_RGBA,GL_UNSIGNED_BYTE,p_data); + } //glTexImage3D(GL_TEXTURE_3D,p_mipmap,GL_RGBA8,gipd->width>>p_mipmap,gipd->height>>p_mipmap,gipd->depth>>p_mipmap,0,GL_RGBA,GL_UNSIGNED_BYTE,p_data); //glTexImage3D(GL_TEXTURE_3D,p_mipmap,GL_RGBA8,gipd->width>>p_mipmap,gipd->height>>p_mipmap,gipd->depth>>p_mipmap,0,GL_RGBA,GL_UNSIGNED_BYTE,data.ptr()); - print_line("update rgba8 "+itos(p_mipmap)); + +} + +/////// + + +RID RasterizerStorageGLES3::particles_create() { + + Particles *particles = memnew( Particles ); + + + return particles_owner.make_rid(particles); +} + +void RasterizerStorageGLES3::particles_set_emitting(RID p_particles,bool p_emitting) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + particles->emitting=p_emitting; + +} +void RasterizerStorageGLES3::particles_set_amount(RID p_particles,int p_amount) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + +} +void RasterizerStorageGLES3::particles_set_lifetime(RID p_particles,float p_lifetime){ + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + particles->lifetime=p_lifetime; +} +void RasterizerStorageGLES3::particles_set_pre_process_time(RID p_particles,float p_time) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + particles->pre_process_time=p_time; + +} +void RasterizerStorageGLES3::particles_set_explosiveness_ratio(RID p_particles,float p_ratio) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + particles->explosiveness=p_ratio; +} +void RasterizerStorageGLES3::particles_set_randomness_ratio(RID p_particles,float p_ratio) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + particles->randomness=p_ratio; + +} +void RasterizerStorageGLES3::particles_set_custom_aabb(RID p_particles,const AABB& p_aabb) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + particles->custom_aabb=p_aabb; + +} +void RasterizerStorageGLES3::particles_set_gravity(RID p_particles,const Vector3& p_gravity) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + + particles->gravity=p_gravity; + +} +void RasterizerStorageGLES3::particles_set_use_local_coordinates(RID p_particles,bool p_enable) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + + particles->use_local_coords=p_enable; +} +void RasterizerStorageGLES3::particles_set_process_material(RID p_particles,RID p_material) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + + particles->process_material=p_material; +} + +void RasterizerStorageGLES3::particles_set_emission_shape(RID p_particles, VS::ParticlesEmissionShape p_shape) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + + particles->emission_shape=p_shape; +} +void RasterizerStorageGLES3::particles_set_emission_sphere_radius(RID p_particles,float p_radius) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + + particles->emission_sphere_radius=p_radius; +} +void RasterizerStorageGLES3::particles_set_emission_box_extents(RID p_particles,const Vector3& p_extents) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + + particles->emission_box_extents=p_extents; +} +void RasterizerStorageGLES3::particles_set_emission_points(RID p_particles,const DVector& p_points) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + + particles->emission_points=p_points; } +void RasterizerStorageGLES3::particles_set_draw_order(RID p_particles,VS::ParticlesDrawOrder p_order) { + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + + particles->draw_order=p_order; +} + +void RasterizerStorageGLES3::particles_set_draw_passes(RID p_particles,int p_count) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + + particles->draw_passes.resize(p_count); +} +void RasterizerStorageGLES3::particles_set_draw_pass_material(RID p_particles,int p_pass, RID p_material) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + ERR_FAIL_INDEX(p_pass,particles->draw_passes.size()); + p_pass,particles->draw_passes[p_pass].material=p_material; + +} +void RasterizerStorageGLES3::particles_set_draw_pass_mesh(RID p_particles,int p_pass, RID p_mesh) { + + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + ERR_FAIL_INDEX(p_pass,particles->draw_passes.size()); + p_pass,particles->draw_passes[p_pass].mesh=p_mesh; + +} + +AABB RasterizerStorageGLES3::particles_get_current_aabb(RID p_particles) { + + const Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND_V(!particles,AABB()); + + return particles->computed_aabb; +} + +//////// void RasterizerStorageGLES3::instance_add_skeleton(RID p_skeleton,RasterizerScene::InstanceBase *p_instance) { diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 5a8246f030e..b0f98001597 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -941,6 +941,7 @@ public: int depth; int levels; GLuint tex_id; + GIProbeCompression compression; GIProbeData() { } @@ -948,9 +949,90 @@ public: mutable RID_Owner gi_probe_data_owner; - virtual RID gi_probe_dynamic_data_create(int p_width,int p_height,int p_depth); - virtual void gi_probe_dynamic_data_update_rgba8(RID p_gi_probe_data,int p_depth_slice,int p_slice_count,int p_mipmap,const void* p_data); + virtual GIProbeCompression gi_probe_get_dynamic_data_get_preferred_compression() const; + virtual RID gi_probe_dynamic_data_create(int p_width,int p_height,int p_depth,GIProbeCompression p_compression); + virtual void gi_probe_dynamic_data_update(RID p_gi_probe_data,int p_depth_slice,int p_slice_count,int p_mipmap,const void* p_data); + /* PARTICLES */ + + struct Particles : public Instantiable { + + bool emitting; + int amount; + float lifetime; + float pre_process_time; + float explosiveness; + float randomness; + AABB custom_aabb; + Vector3 gravity; + bool use_local_coords; + RID process_material; + + VS::ParticlesEmissionShape emission_shape; + float emission_sphere_radius; + Vector3 emission_box_extents; + DVector emission_points; + GLuint emission_point_texture; + + VS::ParticlesDrawOrder draw_order; + struct DrawPass { + RID mesh; + RID material; + }; + + Vector draw_passes; + + AABB computed_aabb; + + Particles() { + emitting=false; + amount=0; + lifetime=1.0;; + pre_process_time=0.0; + explosiveness=0.0; + randomness=0.0; + use_local_coords=true; + + draw_order=VS::PARTICLES_DRAW_ORDER_INDEX; + emission_shape=VS::PARTICLES_EMSSION_POINT; + emission_sphere_radius=1.0; + float emission_sphere_radius; + emission_box_extents=Vector3(1,1,1); + emission_point_texture=0; + } + + }; + + mutable RID_Owner particles_owner; + + virtual RID particles_create(); + + virtual void particles_set_emitting(RID p_particles,bool p_emitting); + virtual void particles_set_amount(RID p_particles,int p_amount); + virtual void particles_set_lifetime(RID p_particles,float p_lifetime); + virtual void particles_set_pre_process_time(RID p_particles,float p_time); + virtual void particles_set_explosiveness_ratio(RID p_particles,float p_ratio); + virtual void particles_set_randomness_ratio(RID p_particles,float p_ratio); + virtual void particles_set_custom_aabb(RID p_particles,const AABB& p_aabb); + virtual void particles_set_gravity(RID p_particles,const Vector3& p_gravity); + virtual void particles_set_use_local_coordinates(RID p_particles,bool p_enable); + virtual void particles_set_process_material(RID p_particles,RID p_material); + + virtual void particles_set_emission_shape(RID p_particles,VS::ParticlesEmissionShape p_shape); + virtual void particles_set_emission_sphere_radius(RID p_particles,float p_radius); + virtual void particles_set_emission_box_extents(RID p_particles,const Vector3& p_extents); + virtual void particles_set_emission_points(RID p_particles,const DVector& p_points); + + + virtual void particles_set_draw_order(RID p_particles,VS::ParticlesDrawOrder p_order); + + virtual void particles_set_draw_passes(RID p_particles,int p_count); + virtual void particles_set_draw_pass_material(RID p_particles,int p_pass, RID p_material); + virtual void particles_set_draw_pass_mesh(RID p_particles,int p_pass, RID p_mesh); + + virtual AABB particles_get_current_aabb(RID p_particles); + + /* INSTANCE */ virtual void instance_add_skeleton(RID p_skeleton,RasterizerScene::InstanceBase *p_instance); virtual void instance_remove_skeleton(RID p_skeleton,RasterizerScene::InstanceBase *p_instance); diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index 272f9bb5e16..f9baeae97de 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -17,5 +17,6 @@ if env['BUILDERS'].has_key('GLES3_GLSL'): env.GLES3_GLSL('ssao_blur.glsl'); env.GLES3_GLSL('exposure.glsl'); env.GLES3_GLSL('tonemap.glsl'); + env.GLES3_GLSL('particles.glsl'); diff --git a/drivers/gles3/shaders/particles.glsl b/drivers/gles3/shaders/particles.glsl new file mode 100644 index 00000000000..fb6060957e5 --- /dev/null +++ b/drivers/gles3/shaders/particles.glsl @@ -0,0 +1,114 @@ +[vertex] + + + +layout(location=0) in highp vec4 pos_lifetime; +layout(location=1) in highp vec4 color; +layout(location=2) in highp vec4 velocity_seed; +layout(location=3) in highp vec4 rot_active; + + +struct Attractor { + + vec3 pos; + vec3 dir; + float radius; + float eat_radius; + float strength; + float attenuation; +}; + +#define MAX_ATTRACTORS 64 + +uniform mat4 origin; +uniform float system_phase; +uniform float prev_system_phase; +uniform float total_particles; +uniform float explosiveness; +uniform vec4 time; +uniform float delta; +uniform vec3 gravity; +uniform int attractor_count; +uniform Attractor attractors[MAX_ATTRACTORS]; + + +out highp vec4 out_pos_lifetime; //tfb: +out highp vec4 out_color; //tfb: +out highp vec4 out_velocity_seed; //tfb: +out highp vec4 out_rot_active; //tfb: + +void main() { + + bool apply_forces=true; + bool apply_velocity=true; + + float mass = 1.0; + + float restart_phase = float(gl_InstanceID)/total_particles; + restart_phase*= explosiveness; + bool restart=false; + + if (system_phase > prev_system_phase) { + restart = prev_system_phase < restart_phase && system_phase >= restart_phase; + } else { + restart = prev_system_phase < restart_phase || system_phase >= restart_phase; + } + + if (restart) { + out_rot_active.a=1.0; + } + + out_pos_lifetime=pos_lifetime; + out_color=color; + out_velocity_seed=velocity_seed; + out_rot_active=rot_active; + + if (out_rot_active.a) { + //execute shader + + } + + + if (apply_forces) { + + vec3 force = gravity; + for(int i=0;i0 && attractors[i].eat_radius > dist) { + rot_active.a=0.0; + } + + rel_vec = normalize(rel_vec); + + float attenuation = pow(dist / attractors[i].radius,attractors[i].attenuation); + + if (attractors[i].dir==vec3(0.0)) { + //towards center + force+=attractors[i].strength * rel_vec * attenuation * mass; + } else { + force+=attractors[i].strength * attractors[i].dir * attenuation *mass; + + } + } + + out_velocity_seed.xyz += force * delta; + } + + if (apply_velocity) { + + out_pos_lifetime.xyz += out_velocity_seed.xyz * delta; + } + +} + +[fragment] + + +void main() { + + +} diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index b0b27823075..90c501ea32e 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -899,8 +899,10 @@ void gi_probe_compute(sampler3D probe, mat4 probe_xform, vec3 bounds,vec3 cell_s float blend = 1.001-max(blendv.x,max(blendv.y,blendv.z)); blend=1.0; + float max_distance = length(bounds); + //radiance -#ifdef VCT_QUALITY_HIGH +#ifndef VCT_QUALITY_HIGH #define MAX_CONE_DIRS 6 vec3 cone_dirs[MAX_CONE_DIRS] = vec3[] ( @@ -914,6 +916,7 @@ void gi_probe_compute(sampler3D probe, mat4 probe_xform, vec3 bounds,vec3 cell_s float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15); float cone_angle_tan = 0.577; + float min_ref_tan = 0.0; #else #define MAX_CONE_DIRS 4 @@ -927,9 +930,10 @@ void gi_probe_compute(sampler3D probe, mat4 probe_xform, vec3 bounds,vec3 cell_s float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.25, 0.25, 0.25); float cone_angle_tan = 0.98269; + max_distance*=0.5; + float min_ref_tan = 0.2; #endif - float max_distance = length(bounds); vec3 light=vec3(0.0); for(int i=0;ibake_cells[p_idx].childs[i]==Baker::CHILD_EMPTY) { //sub cell must be created - p_baker->bake_cells[p_idx].childs[i]=p_baker->bake_cells.size(); + uint32_t child_idx = p_baker->bake_cells.size(); + p_baker->bake_cells[p_idx].childs[i]=child_idx; p_baker->bake_cells.resize( p_baker->bake_cells.size() + 1); + p_baker->bake_cells[child_idx].level=p_level+1; } @@ -1124,7 +1126,12 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug){ w32[ofs++]=norm; } - w32[ofs++]=uint32_t(baker.bake_cells[i].alpha*65535.0); + { + uint16_t alpha = CLAMP(uint32_t(baker.bake_cells[i].alpha*65535.0),0,65535); + uint16_t level = baker.bake_cells[i].level; + + w32[ofs++] = (uint32_t(level)<<16)|uint32_t(alpha); + } } diff --git a/scene/3d/gi_probe.h b/scene/3d/gi_probe.h index aea01b5367c..ca882856cf2 100644 --- a/scene/3d/gi_probe.h +++ b/scene/3d/gi_probe.h @@ -73,6 +73,7 @@ private: float normal[3]; uint32_t used_sides; float alpha; //used for upsampling + int level; Cell() { for(int i=0;i<8;i++) { @@ -86,6 +87,7 @@ private: } alpha=0; used_sides=0; + level=0; } }; diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 844504372e9..eda67f2ed4b 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -432,14 +432,44 @@ public: virtual uint32_t gi_probe_get_version(RID p_probe)=0; - enum GIProbeCompress { + enum GIProbeCompression { GI_PROBE_UNCOMPRESSED, GI_PROBE_S3TC, GI_PROBE_ETC2 }; - virtual RID gi_probe_dynamic_data_create(int p_width,int p_height,int p_depth)=0; - virtual void gi_probe_dynamic_data_update_rgba8(RID p_gi_probe_data,int p_depth_slice,int p_slice_count,int p_mipmap,const void* p_data)=0; + virtual GIProbeCompression gi_probe_get_dynamic_data_get_preferred_compression() const=0; + virtual RID gi_probe_dynamic_data_create(int p_width,int p_height,int p_depth,GIProbeCompression p_compression)=0; + virtual void gi_probe_dynamic_data_update(RID p_gi_probe_data,int p_depth_slice,int p_slice_count,int p_mipmap,const void* p_data)=0; + + /* PARTICLES */ + + virtual RID particles_create()=0; + + virtual void particles_set_emitting(RID p_particles,bool p_emitting)=0; + virtual void particles_set_amount(RID p_particles,int p_amount)=0; + virtual void particles_set_lifetime(RID p_particles,float p_lifetime)=0; + virtual void particles_set_pre_process_time(RID p_particles,float p_time)=0; + virtual void particles_set_explosiveness_ratio(RID p_particles,float p_ratio)=0; + virtual void particles_set_randomness_ratio(RID p_particles,float p_ratio)=0; + virtual void particles_set_custom_aabb(RID p_particles,const AABB& p_aabb)=0; + virtual void particles_set_gravity(RID p_particles,const Vector3& p_gravity)=0; + virtual void particles_set_use_local_coordinates(RID p_particles,bool p_enable)=0; + virtual void particles_set_process_material(RID p_particles,RID p_material)=0; + + virtual void particles_set_emission_shape(RID p_particles,VS::ParticlesEmissionShape p_shape)=0; + virtual void particles_set_emission_sphere_radius(RID p_particles,float p_radius)=0; + virtual void particles_set_emission_box_extents(RID p_particles,const Vector3& p_extents)=0; + virtual void particles_set_emission_points(RID p_particles,const DVector& p_points)=0; + + + virtual void particles_set_draw_order(RID p_particles,VS::ParticlesDrawOrder p_order)=0; + + virtual void particles_set_draw_passes(RID p_particles,int p_count)=0; + virtual void particles_set_draw_pass_material(RID p_particles,int p_pass, RID p_material)=0; + virtual void particles_set_draw_pass_mesh(RID p_particles,int p_pass, RID p_mesh)=0; + + virtual AABB particles_get_current_aabb(RID p_particles)=0; diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index fcfaf52e0b3..5088dedb536 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -830,6 +830,34 @@ public: BIND2(gi_probe_set_dynamic_data,RID,const DVector& ) BIND1RC( DVector,gi_probe_get_dynamic_data,RID) + /* PARTICLES */ + + BIND0R(RID, particles_create) + + BIND2(particles_set_emitting,RID,bool) + BIND2(particles_set_amount,RID,int ) + BIND2(particles_set_lifetime,RID,float ) + BIND2(particles_set_pre_process_time,RID,float ) + BIND2(particles_set_explosiveness_ratio,RID,float ) + BIND2(particles_set_randomness_ratio,RID,float ) + BIND2(particles_set_custom_aabb,RID,const AABB& ) + BIND2(particles_set_gravity,RID,const Vector3& ) + BIND2(particles_set_use_local_coordinates,RID,bool ) + BIND2(particles_set_process_material,RID,RID ) + + BIND2(particles_set_emission_shape,RID,VS::ParticlesEmissionShape ) + BIND2(particles_set_emission_sphere_radius,RID,float ) + BIND2(particles_set_emission_box_extents,RID,const Vector3& ) + BIND2(particles_set_emission_points,RID,const DVector& ) + + + BIND2(particles_set_draw_order,RID,VS::ParticlesDrawOrder ) + + BIND2(particles_set_draw_passes,RID,int ) + BIND3(particles_set_draw_pass_material,RID,int , RID ) + BIND3(particles_set_draw_pass_mesh,RID,int , RID ) + + BIND1R(AABB,particles_get_current_aabb,RID); #undef BINDBASE diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index aa97b2ebd69..deb391c77ef 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -2391,7 +2391,7 @@ void VisualServerScene::_setup_gi_probe(Instance *p_instance) { probe->dynamic.light_data=VSG::storage->gi_probe_get_dynamic_data(p_instance->base); if (probe->dynamic.light_data.size()==0) - return; + return; //using dynamic data DVector::Read r=probe->dynamic.light_data.read(); @@ -2399,15 +2399,17 @@ void VisualServerScene::_setup_gi_probe(Instance *p_instance) { probe->dynamic.local_data.resize(header->cell_count); + int cell_count = probe->dynamic.local_data.size(); DVector::Write ldw = probe->dynamic.local_data.write(); - const GIProbeDataCell *cells = (GIProbeDataCell*)&r[16]; probe->dynamic.level_cell_lists.resize(header->cell_subdiv); _gi_probe_fill_local_data(0,0,0,0,0,cells,header,ldw.ptr(),probe->dynamic.level_cell_lists.ptr()); - probe->dynamic.probe_data=VSG::storage->gi_probe_dynamic_data_create(header->width,header->height,header->depth); + probe->dynamic.compression = VSG::storage->gi_probe_get_dynamic_data_get_preferred_compression(); + + probe->dynamic.probe_data=VSG::storage->gi_probe_dynamic_data_create(header->width,header->height,header->depth,probe->dynamic.compression); probe->dynamic.bake_dynamic_range=VSG::storage->gi_probe_get_dynamic_range(p_instance->base); @@ -2417,6 +2419,14 @@ void VisualServerScene::_setup_gi_probe(Instance *p_instance) { probe->dynamic.grid_size[1]=header->height; probe->dynamic.grid_size[2]=header->depth; + int size_limit = 1; + int size_divisor = 1; + + if (probe->dynamic.compression==RasterizerStorage::GI_PROBE_S3TC) { + print_line("S3TC"); + size_limit=4; + size_divisor=4; + } for(int i=0;i<(int)header->cell_subdiv;i++) { uint32_t x = header->width >> i; @@ -2425,14 +2435,16 @@ void VisualServerScene::_setup_gi_probe(Instance *p_instance) { //create and clear mipmap DVector mipmap; - mipmap.resize(x*y*z*4); + int size = x*y*z*4; + size/=size_divisor; + mipmap.resize(size); DVector::Write w = mipmap.write(); - zeromem(w.ptr(),x*y*z*4); + zeromem(w.ptr(),size); w = DVector::Write(); probe->dynamic.mipmaps_3d.push_back(mipmap); - if (x<=1 || y<=1 || z<=1) + if (x<=size_limit || y<=size_limit || z<=size_limit) break; } @@ -2449,12 +2461,132 @@ void VisualServerScene::_setup_gi_probe(Instance *p_instance) { VSG::scene_render->gi_probe_instance_set_light_data(probe->probe_instance,p_instance->base,probe->dynamic.probe_data); VSG::scene_render->gi_probe_instance_set_transform_to_data(probe->probe_instance,probe->dynamic.light_to_cell_xform); - - VSG::scene_render->gi_probe_instance_set_bounds(probe->probe_instance,bounds.size/cell_size); probe->base_version=VSG::storage->gi_probe_get_version(p_instance->base); + //if compression is S3TC, fill it up + if (probe->dynamic.compression==RasterizerStorage::GI_PROBE_S3TC) { + + //create all blocks + Vector > comp_blocks; + int mipmap_count = probe->dynamic.mipmaps_3d.size(); + comp_blocks.resize(mipmap_count); + + for(int i=0;i>16; + int mipmap = header->cell_subdiv - level -1; + if (mipmap >= mipmap_count) + continue;//uninteresting + + + int blockx = (ld.pos[0]>>2); + int blocky = (ld.pos[1]>>2); + int blockz = (ld.pos[2]); //compression is x/y only + + int blockw = (header->width >> mipmap) >> 2; + int blockh = (header->height >> mipmap) >> 2; + + //print_line("cell "+itos(i)+" level "+itos(level)+"mipmap: "+itos(mipmap)+" pos: "+Vector3(blockx,blocky,blockz)+" size "+Vector2(blockw,blockh)); + + uint32_t key = blockz * blockw*blockh + blocky * blockw + blockx; + + Map & cmap = comp_blocks[mipmap]; + + if (!cmap.has(key)) { + + InstanceGIProbeData::CompBlockS3TC k; + k.offset=key; //use offset as counter first + k.source_count=0; + cmap[key]=k; + } + + InstanceGIProbeData::CompBlockS3TC &k=cmap[key]; + ERR_CONTINUE(k.source_count==16); + k.sources[k.source_count++]=i; + } + + //fix the blocks, precomputing what is needed + probe->dynamic.mipmaps_s3tc.resize(mipmap_count); + + for(int i=0;idynamic.mipmaps_s3tc[i].resize(comp_blocks[i].size()); + DVector::Write w = probe->dynamic.mipmaps_s3tc[i].write(); + int block_idx=0; + + for (Map::Element *E=comp_blocks[i].front();E;E=E->next()) { + + InstanceGIProbeData::CompBlockS3TC k = E->get(); + + //PRECOMPUTE ALPHA + int max_alpha=-100000; + int min_alpha=k.source_count==16 ?100000 :0; //if the block is not completely full, minimum is always 0, (and those blocks will map to 1, which will be zero) + + uint8_t alpha_block[4][4]={ {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0} }; + + for(int j=0;j>8)&0xFF; + if (alphamax_alpha) + max_alpha=alpha; + //fill up alpha block + alpha_block[ldw[k.sources[j]].pos[0]%4][ldw[k.sources[j]].pos[1]%4]=alpha; + + } + + //use the first mode (8 adjustable levels) + k.alpha[0]=max_alpha; + k.alpha[1]=min_alpha; + + uint64_t alpha_bits=0; + + if (max_alpha!=min_alpha) { + + int idx=0; + + for(int y=0;y<4;y++) { + for(int x=0;x<4;x++) { + + //substract minimum + uint32_t a = uint32_t(alpha_block[x][y])-min_alpha; + //convert range to 3 bits + a =int((a * 7.0 / (max_alpha-min_alpha))+0.5); + a = CLAMP(a,0,7); //just to be sure + a = 7-a; //because range is inverted in this mode + if (a==0) { + //do none, remain + } else if (a==7) { + a=1; + } else { + a=a+1; + } + + alpha_bits|=uint64_t(a)<<(idx*3); + idx++; + } + } + } + + k.alpha[2]=(alpha_bits >> 0)&0xFF; + k.alpha[3]=(alpha_bits >> 8)&0xFF; + k.alpha[4]=(alpha_bits >> 16)&0xFF; + k.alpha[5]=(alpha_bits >> 24)&0xFF; + k.alpha[6]=(alpha_bits >> 32)&0xFF; + k.alpha[7]=(alpha_bits >> 40)&0xFF; + + w[block_idx++]=k; + + } + + } + } + } void VisualServerScene::_gi_probe_bake_thread() { @@ -2859,43 +2991,190 @@ void VisualServerScene::_bake_gi_probe(Instance *p_gi_probe) { //plot result to 3D texture! - for(int i=0;i<(int)header->cell_subdiv;i++) { + if (probe_data->dynamic.compression==RasterizerStorage::GI_PROBE_UNCOMPRESSED) { - int stage = header->cell_subdiv - i -1; + for(int i=0;i<(int)header->cell_subdiv;i++) { - if (stage >= probe_data->dynamic.mipmaps_3d.size()) - continue; //no mipmap for this one + int stage = header->cell_subdiv - i -1; - print_line("generating mipmap stage: "+itos(stage)); - int level_cell_count = probe_data->dynamic.level_cell_lists[ i ].size(); - const uint32_t *level_cells = probe_data->dynamic.level_cell_lists[ i ].ptr(); + if (stage >= probe_data->dynamic.mipmaps_3d.size()) + continue; //no mipmap for this one - DVector::Write lw = probe_data->dynamic.mipmaps_3d[stage].write(); - uint8_t *mipmapw = lw.ptr(); + print_line("generating mipmap stage: "+itos(stage)); + int level_cell_count = probe_data->dynamic.level_cell_lists[ i ].size(); + const uint32_t *level_cells = probe_data->dynamic.level_cell_lists[ i ].ptr(); - uint32_t sizes[3]={header->width>>stage,header->height>>stage,header->depth>>stage}; + DVector::Write lw = probe_data->dynamic.mipmaps_3d[stage].write(); + uint8_t *mipmapw = lw.ptr(); - for(int j=0;jwidth>>stage,header->height>>stage,header->depth>>stage}; - uint32_t idx = level_cells[j]; + for(int j=0;jdynamic.bake_dynamic_range)>>2; - uint32_t g = (uint32_t(local_data[idx].energy[1])/probe_data->dynamic.bake_dynamic_range)>>2; - uint32_t b = (uint32_t(local_data[idx].energy[2])/probe_data->dynamic.bake_dynamic_range)>>2; - uint32_t a = cells[idx].alpha>>8; + uint32_t idx = level_cells[j]; - uint32_t mm_ofs = sizes[0]*sizes[1]*(local_data[idx].pos[2]) + sizes[0]*(local_data[idx].pos[1]) + (local_data[idx].pos[0]); - mm_ofs*=4; //for RGBA (4 bytes) + uint32_t r = (uint32_t(local_data[idx].energy[0])/probe_data->dynamic.bake_dynamic_range)>>2; + uint32_t g = (uint32_t(local_data[idx].energy[1])/probe_data->dynamic.bake_dynamic_range)>>2; + uint32_t b = (uint32_t(local_data[idx].energy[2])/probe_data->dynamic.bake_dynamic_range)>>2; + uint32_t a = (cells[idx].level_alpha>>8)&0xFF; - mipmapw[mm_ofs+0]=uint8_t(CLAMP(r,0,255)); - mipmapw[mm_ofs+1]=uint8_t(CLAMP(g,0,255)); - mipmapw[mm_ofs+2]=uint8_t(CLAMP(b,0,255)); - mipmapw[mm_ofs+3]=uint8_t(CLAMP(a,0,255)); + uint32_t mm_ofs = sizes[0]*sizes[1]*(local_data[idx].pos[2]) + sizes[0]*(local_data[idx].pos[1]) + (local_data[idx].pos[0]); + mm_ofs*=4; //for RGBA (4 bytes) + + mipmapw[mm_ofs+0]=uint8_t(CLAMP(r,0,255)); + mipmapw[mm_ofs+1]=uint8_t(CLAMP(g,0,255)); + mipmapw[mm_ofs+2]=uint8_t(CLAMP(b,0,255)); + mipmapw[mm_ofs+3]=uint8_t(CLAMP(a,0,255)); + + + } + } + } else if (probe_data->dynamic.compression==RasterizerStorage::GI_PROBE_S3TC) { + + + int mipmap_count = probe_data->dynamic.mipmaps_3d.size(); + + for(int mmi=0;mmi::Write mmw = probe_data->dynamic.mipmaps_3d[mmi].write(); + int block_count = probe_data->dynamic.mipmaps_s3tc[mmi].size(); + DVector::Read mmr = probe_data->dynamic.mipmaps_s3tc[mmi].read(); + + for(int i=0;idynamic.bake_dynamic_range))/1024.0; + colors[j].y=(local_data[b.sources[j]].energy[1]/float(probe_data->dynamic.bake_dynamic_range))/1024.0; + colors[j].z=(local_data[b.sources[j]].energy[2]/float(probe_data->dynamic.bake_dynamic_range))/1024.0; + } + //super quick and dirty compression + //find 2 most futher apart + float distance=0; + Vector3 from,to; + + if (b.source_count==16) { + //all cells are used so, find minmax between them + int further_apart[2]={0,0}; + for(int j=0;jdistance) { + distance=d; + further_apart[0]=j; + further_apart[1]=k; + } + } + } + + from = colors[further_apart[0]]; + to = colors[further_apart[1]]; + + } else { + //if a block is missing, the priority is that this block remains black, + //otherwise the geometry will appear deformed + //correct shape wins over correct color in this case + //average all colors first + Vector3 average; + + for(int j=0;j color_0) { + SWAP(color_1,color_0); + SWAP(from,to); + //} + + + if (distance>0) { + + Vector3 dir = (to-from).normalized(); + + + for(int j=0;j>8)&0xFF; + blockptr[10]=color_1&0xFF; + blockptr[11]=(color_1>>8)&0xFF; + blockptr[12]=encode&0xFF; + blockptr[13]=(encode>>8)&0xFF; + blockptr[14]=(encode>>16)&0xFF; + blockptr[15]=(encode>>24)&0xFF; + + } } + } + //send back to main thread to update un little chunks probe_data->dynamic.updating_stage=GI_UPDATE_STAGE_UPLOADING; @@ -3055,7 +3334,7 @@ void VisualServerScene::render_probes() { int mmsize = probe->dynamic.mipmaps_3d[i].size(); DVector::Read r = probe->dynamic.mipmaps_3d[i].read(); - VSG::storage->gi_probe_dynamic_data_update_rgba8(probe->dynamic.probe_data,0,probe->dynamic.grid_size[2]>>i,i,r.ptr()); + VSG::storage->gi_probe_dynamic_data_update(probe->dynamic.probe_data,0,probe->dynamic.grid_size[2]>>i,i,r.ptr()); } diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h index 418e8387bfa..aa498e2752f 100644 --- a/servers/visual/visual_server_scene.h +++ b/servers/visual/visual_server_scene.h @@ -422,6 +422,13 @@ public: uint16_t energy[3]; //using 0..1024 for float range 0..1. integer is needed for deterministic add/remove of lights }; + struct CompBlockS3TC { + uint32_t offset; //offset in mipmap + uint32_t source_count; //sources + uint32_t sources[16]; //id for each source + uint8_t alpha[8]; //alpha block is pre-computed + }; + struct Dynamic { @@ -433,8 +440,10 @@ public: RID probe_data; bool enabled; int bake_dynamic_range; + RasterizerStorage::GIProbeCompression compression; Vector< DVector > mipmaps_3d; + Vector< DVector > mipmaps_s3tc; //for s3tc int updating_stage; @@ -538,7 +547,7 @@ public: uint32_t albedo; uint32_t emission; uint32_t normal; - uint32_t alpha; + uint32_t level_alpha; }; enum { diff --git a/servers/visual_server.h b/servers/visual_server.h index ef7cbc03af6..33a40834896 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -472,6 +472,53 @@ public: virtual void gi_probe_set_interior(RID p_probe,bool p_enable)=0; virtual bool gi_probe_is_interior(RID p_probe) const=0; + /* PARTICLES API */ + + virtual RID particles_create()=0; + + virtual void particles_set_emitting(RID p_particles,bool p_emitting)=0; + virtual void particles_set_amount(RID p_particles,int p_amount)=0; + virtual void particles_set_lifetime(RID p_particles,float p_lifetime)=0; + virtual void particles_set_pre_process_time(RID p_particles,float p_time)=0; + virtual void particles_set_explosiveness_ratio(RID p_particles,float p_ratio)=0; + virtual void particles_set_randomness_ratio(RID p_particles,float p_ratio)=0; + virtual void particles_set_custom_aabb(RID p_particles,const AABB& p_aabb)=0; + virtual void particles_set_gravity(RID p_particles,const Vector3& p_gravity)=0; + virtual void particles_set_use_local_coordinates(RID p_particles,bool p_enable)=0; + virtual void particles_set_process_material(RID p_particles,RID p_material)=0; + + enum ParticlesEmissionShape { + PARTICLES_EMSSION_POINT, + PARTICLES_EMSSION_SPHERE, + PARTICLES_EMSSION_BOX, + PARTICLES_EMSSION_POINTS, + PARTICLES_EMSSION_SEGMENTS, + }; + + virtual void particles_set_emission_shape(RID p_particles,ParticlesEmissionShape)=0; + virtual void particles_set_emission_sphere_radius(RID p_particles,float p_radius)=0; + virtual void particles_set_emission_box_extents(RID p_particles,const Vector3& p_extents)=0; + virtual void particles_set_emission_points(RID p_particles,const DVector& p_points)=0; + + enum ParticlesDrawOrder { + PARTICLES_DRAW_ORDER_INDEX, + PARTICLES_DRAW_ORDER_LIFETIME, + PARTICLES_DRAW_ORDER_VIEW_DEPTH, + }; + + virtual void particles_set_draw_order(RID p_particles,ParticlesDrawOrder p_order)=0; + + enum ParticlesDrawPassMode { + PARTICLES_DRAW_PASS_MODE_QUAD, + PARTICLES_DRAW_PASS_MODE_MESH + }; + + + virtual void particles_set_draw_passes(RID p_particles,int p_count)=0; + virtual void particles_set_draw_pass_material(RID p_particles,int p_pass, RID p_material)=0; + virtual void particles_set_draw_pass_mesh(RID p_particles,int p_pass, RID p_mesh)=0; + + virtual AABB particles_get_current_aabb(RID p_particles)=0; /* CAMERA API */ @@ -483,7 +530,15 @@ public: virtual void camera_set_environment(RID p_camera,RID p_env)=0; virtual void camera_set_use_vertical_aspect(RID p_camera,bool p_enable)=0; +/* + enum ParticlesCollisionMode { + PARTICLES_COLLISION_NONE, + PARTICLES_COLLISION_TEXTURE, + PARTICLES_COLLISION_CUBEMAP, + }; + virtual void particles_set_collision(RID p_particles,ParticlesCollisionMode p_mode,const Transform&, p_xform,const RID p_depth_tex,const RID p_normal_tex)=0; +*/ /* VIEWPORT TARGET API */ virtual RID viewport_create()=0; @@ -622,6 +677,7 @@ public: INSTANCE_MESH, INSTANCE_MULTIMESH, INSTANCE_IMMEDIATE, + INSTANCE_PARTICLES, INSTANCE_LIGHT, INSTANCE_REFLECTION_PROBE, INSTANCE_ROOM,