diff options
29 files changed, 5217 insertions, 959 deletions
diff --git a/core/image.cpp b/core/image.cpp index 73a2ab8013..0b63d9c524 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -390,16 +390,16 @@ void Image::convert( Format p_new_format ){ case FORMAT_R8|(FORMAT_RG8<<8): _convert<1,false,2,false,false,false>( width, height,rptr, wptr ); break; case FORMAT_R8|(FORMAT_RGB8<<8): _convert<1,false,3,false,false,false>( width, height,rptr, wptr ); break; case FORMAT_R8|(FORMAT_RGBA8<<8): _convert<1,false,3,true,false,false>( width, height,rptr, wptr ); break; - case FORMAT_RG8|(FORMAT_L8<<8): _convert<1,false,1,false,false,true>( width, height,rptr, wptr ); break; - case FORMAT_RG8|(FORMAT_LA8<<8): _convert<1,false,1,true,false,true>( width, height,rptr, wptr ); break; - case FORMAT_RG8|(FORMAT_R8<<8): _convert<1,false,1,false,false,false>( width, height,rptr, wptr ); break; - case FORMAT_RG8|(FORMAT_RGB8<<8): _convert<1,false,3,false,false,false>( width, height,rptr, wptr ); break; - case FORMAT_RG8|(FORMAT_RGBA8<<8): _convert<1,false,3,true,false,false>( width, height,rptr, wptr ); break; - case FORMAT_RGB8|(FORMAT_L8<<8): _convert<2,false,1,false,false,true>( width, height,rptr, wptr ); break; - case FORMAT_RGB8|(FORMAT_LA8<<8): _convert<2,false,1,true,false,true>( width, height,rptr, wptr ); break; - case FORMAT_RGB8|(FORMAT_R8<<8): _convert<2,false,1,false,false,false>( width, height,rptr, wptr ); break; - case FORMAT_RGB8|(FORMAT_RG8<<8): _convert<2,false,2,false,false,false>( width, height,rptr, wptr ); break; - case FORMAT_RGB8|(FORMAT_RGBA8<<8): _convert<2,false,3,true,false,false>( width, height,rptr, wptr ); break; + case FORMAT_RG8|(FORMAT_L8<<8): _convert<2,false,1,false,false,true>( width, height,rptr, wptr ); break; + case FORMAT_RG8|(FORMAT_LA8<<8): _convert<2,false,1,true,false,true>( width, height,rptr, wptr ); break; + case FORMAT_RG8|(FORMAT_R8<<8): _convert<2,false,1,false,false,false>( width, height,rptr, wptr ); break; + case FORMAT_RG8|(FORMAT_RGB8<<8): _convert<2,false,3,false,false,false>( width, height,rptr, wptr ); break; + case FORMAT_RG8|(FORMAT_RGBA8<<8): _convert<2,false,3,true,false,false>( width, height,rptr, wptr ); break; + case FORMAT_RGB8|(FORMAT_L8<<8): _convert<3,false,1,false,false,true>( width, height,rptr, wptr ); break; + case FORMAT_RGB8|(FORMAT_LA8<<8): _convert<3,false,1,true,false,true>( width, height,rptr, wptr ); break; + case FORMAT_RGB8|(FORMAT_R8<<8): _convert<3,false,1,false,false,false>( width, height,rptr, wptr ); break; + case FORMAT_RGB8|(FORMAT_RG8<<8): _convert<3,false,2,false,false,false>( width, height,rptr, wptr ); break; + case FORMAT_RGB8|(FORMAT_RGBA8<<8): _convert<3,false,3,true,false,false>( width, height,rptr, wptr ); break; case FORMAT_RGBA8|(FORMAT_L8<<8): _convert<3,true,1,false,false,true>( width, height,rptr, wptr ); break; case FORMAT_RGBA8|(FORMAT_LA8<<8): _convert<3,true,1,true,false,true>( width, height,rptr, wptr ); break; case FORMAT_RGBA8|(FORMAT_R8<<8): _convert<3,true,1,false,false,false>( width, height,rptr, wptr ); break; @@ -414,7 +414,7 @@ void Image::convert( Format p_new_format ){ bool gen_mipmaps=mipmaps; - mipmaps=false; +// mipmaps=false; *this=new_img; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 2c6c9dd9ba..524e683738 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1010,6 +1010,48 @@ void RasterizerSceneGLES3::light_instance_mark_visible(RID p_light_instance) { light_instance->last_scene_pass=scene_pass; } + +////////////////////// + +RID RasterizerSceneGLES3::gi_probe_instance_create() { + + GIProbeInstance *gipi = memnew(GIProbeInstance); + + return gi_probe_instance_owner.make_rid(gipi); +} + +void RasterizerSceneGLES3::gi_probe_instance_set_light_data(RID p_probe,RID p_data) { + + GIProbeInstance *gipi = gi_probe_instance_owner.getornull(p_probe); + ERR_FAIL_COND(!gipi); + gipi->data=p_data; + if (p_data.is_valid()) { + RasterizerStorageGLES3::GIProbeData *gipd = storage->gi_probe_data_owner.getornull(p_data); + ERR_FAIL_COND(!gipd); + if (gipd) { + gipi->tex_cache=gipd->tex_id; + gipi->cell_size_cache.x=1.0/gipd->width; + gipi->cell_size_cache.y=1.0/gipd->height; + gipi->cell_size_cache.z=1.0/gipd->depth; + } + } +} +void RasterizerSceneGLES3::gi_probe_instance_set_transform_to_data(RID p_probe,const Transform& p_xform) { + + GIProbeInstance *gipi = gi_probe_instance_owner.getornull(p_probe); + ERR_FAIL_COND(!gipi); + gipi->transform_to_data=p_xform; + +} + +void RasterizerSceneGLES3::gi_probe_instance_set_bounds(RID p_probe,const Vector3& p_bounds) { + + GIProbeInstance *gipi = gi_probe_instance_owner.getornull(p_probe); + ERR_FAIL_COND(!gipi); + gipi->bounds=p_bounds; + +} + //////////////////////////// //////////////////////////// //////////////////////////// @@ -1438,7 +1480,7 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) { } -void RasterizerSceneGLES3::_setup_light(RenderList::Element *e) { +void RasterizerSceneGLES3::_setup_light(RenderList::Element *e,const Transform& p_view_transform) { int omni_indices[16]; int omni_count=0; @@ -1509,7 +1551,33 @@ void RasterizerSceneGLES3::_setup_light(RenderList::Element *e) { glUniform1iv(state.scene_shader.get_uniform(SceneShaderGLES3::REFLECTION_INDICES),reflection_count,reflection_indices); } + int gi_probe_count = e->instance->gi_probe_instances.size(); + if (gi_probe_count) { + const RID * ridp = e->instance->gi_probe_instances.ptr(); + GIProbeInstance *gipi = gi_probe_instance_owner.getptr(ridp[0]); + + glActiveTexture(GL_TEXTURE0+storage->config.max_texture_image_units-6); + glBindTexture(GL_TEXTURE_3D,gipi->tex_cache); + state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_XFORM1, gipi->transform_to_data * p_view_transform); + state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_BOUNDS1, gipi->bounds); + state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_CELL_SIZE1, gipi->cell_size_cache); + if (gi_probe_count>1) { + + GIProbeInstance *gipi2 = gi_probe_instance_owner.getptr(ridp[1]); + + glActiveTexture(GL_TEXTURE0+storage->config.max_texture_image_units-7); + glBindTexture(GL_TEXTURE_3D,gipi2->tex_cache); + state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_XFORM2, gipi2->transform_to_data * p_view_transform); + state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_BOUNDS2, gipi2->bounds); + state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_CELL_SIZE2, gipi2->cell_size_cache); + + state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE2_ENABLED, true ); + } else { + + state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE2_ENABLED, false ); + } + } } @@ -1672,11 +1740,15 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements,int p_e state.scene_shader.set_conditional(SceneShaderGLES3::LIGHT_USE_PSSM_BLEND,false); state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_5,false); state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_13,false); + state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES,false); //state.scene_shader.set_conditional(SceneShaderGLES3::SHADELESS,true); } else { + + state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES,e->instance->gi_probe_instances.size()>0); + state.scene_shader.set_conditional(SceneShaderGLES3::SHADELESS,false); state.scene_shader.set_conditional(SceneShaderGLES3::USE_FORWARD_LIGHTING,!p_directional_add); state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHT_DIRECTIONAL,false); @@ -1711,9 +1783,12 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements,int p_e } + + rebind=true; } + if (p_alpha_pass || p_directional_add) { int desired_blend_mode; if (p_directional_add) { @@ -1794,7 +1869,8 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements,int p_e } if (!(e->sort_key&RenderList::SORT_KEY_UNSHADED_FLAG) && !p_directional_add && !p_shadow) { - _setup_light(e); + _setup_light(e,p_view_transform); + } @@ -1837,6 +1913,7 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements,int p_e state.scene_shader.set_conditional(SceneShaderGLES3::SHADELESS,false); state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_5,false); state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_13,false); + state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES,false); } @@ -1950,6 +2027,10 @@ void RasterizerSceneGLES3::_add_geometry( RasterizerStorageGLES3::Geometry* p_g copymem(oe,e,sizeof(RenderList::Element)); } + + if (e->instance->gi_probe_instances.size()) { + e->sort_key|=RenderList::SORT_KEY_GI_PROBES_FLAG; + } } //if (e->geometry->type==RasterizerStorageGLES3::Geometry::GEOMETRY_MULTISURFACE) diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 78e8b3e477..5eb2be1cc2 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -526,6 +526,25 @@ public: virtual void light_instance_set_shadow_transform(RID p_light_instance,const CameraMatrix& p_projection,const Transform& p_transform,float p_far,float p_split,int p_pass); virtual void light_instance_mark_visible(RID p_light_instance); + /* REFLECTION INSTANCE */ + + struct GIProbeInstance : public RID_Data { + RID data; + GLuint tex_cache; + Vector3 cell_size_cache; + Vector3 bounds; + Transform transform_to_data; + }; + + + + mutable RID_Owner<GIProbeInstance> gi_probe_instance_owner; + + virtual RID gi_probe_instance_create(); + virtual void gi_probe_instance_set_light_data(RID p_probe,RID p_data); + virtual void gi_probe_instance_set_transform_to_data(RID p_probe,const Transform& p_xform); + virtual void gi_probe_instance_set_bounds(RID p_probe,const Vector3& p_bounds); + /* RENDER LIST */ struct RenderList { @@ -541,8 +560,9 @@ public: SORT_KEY_DEPTH_LAYER_SHIFT=60, SORT_KEY_UNSHADED_FLAG=uint64_t(1)<<59, SORT_KEY_NO_DIRECTIONAL_FLAG=uint64_t(1)<<58, - SORT_KEY_SHADING_SHIFT=58, - SORT_KEY_SHADING_MASK=3, + SORT_KEY_GI_PROBES_FLAG=uint64_t(1)<<57, + SORT_KEY_SHADING_SHIFT=57, + SORT_KEY_SHADING_MASK=7, SORT_KEY_MATERIAL_INDEX_SHIFT=40, SORT_KEY_GEOMETRY_INDEX_SHIFT=20, SORT_KEY_GEOMETRY_TYPE_SHIFT=15, @@ -669,7 +689,7 @@ public: _FORCE_INLINE_ void _setup_transform(InstanceBase *p_instance,const Transform& p_view_transform,const CameraMatrix& p_projection); _FORCE_INLINE_ void _setup_geometry(RenderList::Element *e); _FORCE_INLINE_ void _render_geometry(RenderList::Element *e); - _FORCE_INLINE_ void _setup_light(RenderList::Element *e); + _FORCE_INLINE_ void _setup_light(RenderList::Element *e,const Transform& p_view_transform); void _render_list(RenderList::Element **p_elements, int p_element_count, const Transform& p_view_transform, const CameraMatrix& p_projection, GLuint p_base_env, bool p_reverse_cull, bool p_alpha_pass, bool p_shadow, bool p_directional_add, bool p_directional_shadows); diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index cfa50f6100..8262487f9d 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -1514,6 +1514,7 @@ void RasterizerStorageGLES3::_update_shader(Shader* p_shader) const { p_shader->valid=true; p_shader->version++; + } void RasterizerStorageGLES3::update_dirty_shaders() { @@ -3600,16 +3601,15 @@ void RasterizerStorageGLES3::multimesh_instance_set_color(RID p_multimesh,int p_ ERR_FAIL_COND(multimesh->color_format==VS::MULTIMESH_COLOR_NONE); int stride = multimesh->color_floats+multimesh->xform_floats; - float *dataptr=&multimesh->data[stride*p_index+multimesh->color_floats]; + float *dataptr=&multimesh->data[stride*p_index+multimesh->xform_floats]; if (multimesh->color_format==VS::MULTIMESH_COLOR_8BIT) { - union { - uint32_t colu; - float colf; - } cu; - cu.colu=p_color.to_32(); - dataptr[ 0]=cu.colf; + uint8_t *data8=(uint8_t*)dataptr; + data8[0]=CLAMP(p_color.r*255.0,0,255); + data8[1]=CLAMP(p_color.g*255.0,0,255); + data8[2]=CLAMP(p_color.b*255.0,0,255); + data8[3]=CLAMP(p_color.a*255.0,0,255); } else if (multimesh->color_format==VS::MULTIMESH_COLOR_FLOAT) { dataptr[ 0]=p_color.r; @@ -3701,7 +3701,7 @@ Color RasterizerStorageGLES3::multimesh_instance_get_color(RID p_multimesh,int p float colf; } cu; - return Color::hex(cu.colu); + return Color::hex(BSWAP32(cu.colu)); } else if (multimesh->color_format==VS::MULTIMESH_COLOR_FLOAT) { Color c; @@ -4385,6 +4385,15 @@ float RasterizerStorageGLES3::light_get_param(RID p_light,VS::LightParam p_param return light->param[p_param]; } +Color RasterizerStorageGLES3::light_get_color(RID p_light) { + + const Light * light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light,Color()); + + return light->color; + +} + bool RasterizerStorageGLES3::light_has_shadow(RID p_light) const { const Light * light = light_owner.getornull(p_light); @@ -4668,6 +4677,261 @@ void RasterizerStorageGLES3::portal_set_disabled_color(RID p_portal, const Color } +RID RasterizerStorageGLES3::gi_probe_create() { + + GIProbe *gip = memnew( GIProbe ); + + gip->data_width=0; + gip->data_height=0; + gip->data_depth=0; + gip->bounds=AABB(Vector3(),Vector3(1,1,1)); + gip->dynamic_range=1.0; + gip->version=1; + gip->cell_size=1.0; + + return gi_probe_owner.make_rid(gip); +} + +void RasterizerStorageGLES3::gi_probe_set_bounds(RID p_probe,const AABB& p_bounds){ + + GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!gip); + + gip->bounds=p_bounds; + gip->version++; + gip->instance_change_notify(); +} +AABB RasterizerStorageGLES3::gi_probe_get_bounds(RID p_probe) const{ + + const GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!gip,AABB()); + + return gip->bounds; +} + +void RasterizerStorageGLES3::gi_probe_set_cell_size(RID p_probe,float p_size) { + + GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!gip); + + gip->cell_size=p_size; + gip->version++; + gip->instance_change_notify(); +} + +float RasterizerStorageGLES3::gi_probe_get_cell_size(RID p_probe) const { + + const GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!gip,0); + + return gip->cell_size; + +} + +void RasterizerStorageGLES3::gi_probe_set_to_cell_xform(RID p_probe,const Transform& p_xform) { + + GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!gip); + + gip->to_cell=p_xform; +} + +Transform RasterizerStorageGLES3::gi_probe_get_to_cell_xform(RID p_probe) const { + + const GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!gip,Transform()); + + return gip->to_cell; + +} + + + +void RasterizerStorageGLES3::gi_probe_set_dynamic_data(RID p_probe,const DVector<int>& p_data){ + GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!gip); + + gip->dynamic_data=p_data; + gip->version++; + gip->instance_change_notify(); + +} +DVector<int> RasterizerStorageGLES3::gi_probe_get_dynamic_data(RID p_probe) const{ + + const GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!gip,DVector<int>()); + + return gip->dynamic_data; +} + +void RasterizerStorageGLES3::gi_probe_set_dynamic_range(RID p_probe,float p_range){ + + GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!gip); + + gip->dynamic_range=p_range; + +} +float RasterizerStorageGLES3::gi_probe_get_dynamic_range(RID p_probe) const{ + + const GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!gip,0); + + return gip->dynamic_range; +} + + +void RasterizerStorageGLES3::gi_probe_set_static_data(RID p_gi_probe,const DVector<uint8_t>& p_data,VS::GIProbeDataFormat p_format,int p_width,int p_height,int p_depth) { + + GIProbe *gip = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND(!gip); + + if (gip->data.is_valid()) { + free(gip->data); + } + + gip->data=RID(); + //this is platform dependent + + gip->version++; + gip->instance_change_notify(); + +} +DVector<uint8_t> RasterizerStorageGLES3::gi_probe_get_static_data(RID p_gi_probe) const { + + const GIProbe *gip = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gip,DVector<uint8_t>()); + + //platform dependent + return DVector<uint8_t>(); +} +VS::GIProbeDataFormat RasterizerStorageGLES3::gi_probe_get_static_data_format(RID p_gi_probe) const { + + const GIProbe *gip = gi_probe_owner.getornull(p_gi_probe); + ERR_FAIL_COND_V(!gip,VS::GI_PROBE_DATA_RGBA8); + + return gip->data_format; +} +int RasterizerStorageGLES3::gi_probe_get_static_data_width(RID p_probe) const { + + const GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!gip,0); + + return gip->data_width; +} +int RasterizerStorageGLES3::gi_probe_get_static_data_height(RID p_probe) const { + + const GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!gip,0); + return gip->data_height; +} +int RasterizerStorageGLES3::gi_probe_get_static_data_depth(RID p_probe) const { + + const GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!gip,0); + return gip->data_depth; +} + +RID RasterizerStorageGLES3::gi_probe_get_data(RID p_probe) { + + const GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!gip,RID()); + + return gip->data; +} + +uint32_t RasterizerStorageGLES3::gi_probe_get_version(RID p_probe) { + + const GIProbe *gip = gi_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!gip,0); + + return gip->version; +} + +RID RasterizerStorageGLES3::gi_probe_dynamic_data_create(int p_width,int p_height,int p_depth) { + + GIProbeData *gipd = memnew( GIProbeData ); + + gipd->width=p_width; + gipd->height=p_height; + gipd->depth=p_depth; + + glActiveTexture(GL_TEXTURE0); + glGenTextures(1,&gipd->tex_id); + glBindTexture(GL_TEXTURE_3D,gipd->tex_id); + + int level=0; + + print_line("dyndata create"); + while(true) { + + Vector<uint8_t> data; + data.resize(p_width*p_height*p_depth*4); + + + for(int i=0;i<data.size();i+=4) { + + data[i+0]=0xFF; + data[i+1]=0x00; + data[i+2]=0xFF; + data[i+3]=0xFF; + } + + 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) + break; + p_width>>=1; + p_height>>=1; + p_depth>>=1; + level++; + } + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, level); + + gipd->levels=level+1; + + 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) { + + GIProbeData *gipd = gi_probe_data_owner.getornull(p_gi_probe_data); + ERR_FAIL_COND(!gipd); +/* + Vector<uint8_t> data; + data.resize((gipd->width>>p_mipmap)*(gipd->height>>p_mipmap)*(gipd->depth>>p_mipmap)*4); + + for(int i=0;i<(gipd->width>>p_mipmap);i++) { + for(int j=0;j<(gipd->height>>p_mipmap);j++) { + for(int k=0;k<(gipd->depth>>p_mipmap);k++) { + + int ofs = (k*(gipd->height>>p_mipmap)*(gipd->width>>p_mipmap)) + j *(gipd->width>>p_mipmap) + i; + ofs*=4; + data[ofs+0]=i*0xFF/(gipd->width>>p_mipmap); + data[ofs+1]=j*0xFF/(gipd->height>>p_mipmap); + data[ofs+2]=k*0xFF/(gipd->depth>>p_mipmap); + data[ofs+3]=0xFF; + } + } + } +*/ + 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); + //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)); +} + + + + void RasterizerStorageGLES3::instance_add_skeleton(RID p_skeleton,RasterizerScene::InstanceBase *p_instance) { Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); @@ -4709,6 +4973,10 @@ void RasterizerStorageGLES3::instance_add_dependency(RID p_base,RasterizerScene: inst = light_owner.getornull(p_base); ERR_FAIL_COND(!inst); } break; + case VS::INSTANCE_GI_PROBE: { + inst = gi_probe_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; default: { if (!inst) { ERR_FAIL(); @@ -4744,6 +5012,10 @@ void RasterizerStorageGLES3::instance_remove_dependency(RID p_base,RasterizerSce inst = light_owner.getornull(p_base); ERR_FAIL_COND(!inst); } break; + case VS::INSTANCE_GI_PROBE: { + inst = gi_probe_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; default: { if (!inst) { @@ -5395,19 +5667,27 @@ VS::InstanceType RasterizerStorageGLES3::get_base_type(RID p_rid) const { if (mesh_owner.owns(p_rid)) { return VS::INSTANCE_MESH; } + if (multimesh_owner.owns(p_rid)) { return VS::INSTANCE_MULTIMESH; } + if (immediate_owner.owns(p_rid)) { return VS::INSTANCE_IMMEDIATE; } + if (light_owner.owns(p_rid)) { return VS::INSTANCE_LIGHT; } + if (reflection_probe_owner.owns(p_rid)) { return VS::INSTANCE_REFLECTION_PROBE; } + if (gi_probe_owner.owns(p_rid)) { + return VS::INSTANCE_GI_PROBE; + } + return VS::INSTANCE_NONE; } @@ -5561,6 +5841,27 @@ bool RasterizerStorageGLES3::free(RID p_rid){ reflection_probe_owner.free(p_rid); memdelete(reflection_probe); + } else if (gi_probe_owner.owns(p_rid)) { + + // delete the texture + GIProbe *gi_probe = gi_probe_owner.get(p_rid); + + if (gi_probe->data.is_valid()) { + free(gi_probe->data); + } + + gi_probe_owner.free(p_rid); + memdelete(gi_probe); + } else if (gi_probe_data_owner.owns(p_rid)) { + + // delete the texture + GIProbeData *gi_probe_data = gi_probe_data_owner.get(p_rid); + + print_line("dyndata delete"); + glDeleteTextures(1,&gi_probe_data->tex_id); + gi_probe_owner.free(p_rid); + memdelete(gi_probe_data); + } else if (canvas_occluder_owner.owns(p_rid)) { diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 7802b28158..b81c3df78a 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -799,6 +799,7 @@ public: virtual VS::LightType light_get_type(RID p_light) const; virtual float light_get_param(RID p_light,VS::LightParam p_param); + virtual Color light_get_color(RID p_light); virtual AABB light_get_aabb(RID p_light) const; virtual uint64_t light_get_version(RID p_light) const; @@ -868,6 +869,84 @@ public: virtual void portal_set_disable_distance(RID p_portal, float p_distance); virtual void portal_set_disabled_color(RID p_portal, const Color& p_color); + + + + + + + /* GI PROBE API */ + + struct GIProbe : public Instantiable { + + + AABB bounds; + Transform to_cell; + float cell_size; + + float dynamic_range; + + uint32_t version; + + DVector<int> dynamic_data; + + RID data; + int data_width; + int data_height; + int data_depth; + VS::GIProbeDataFormat data_format; + + + }; + + mutable RID_Owner<GIProbe> gi_probe_owner; + + virtual RID gi_probe_create(); + + virtual void gi_probe_set_bounds(RID p_probe,const AABB& p_bounds); + virtual AABB gi_probe_get_bounds(RID p_probe) const; + + virtual void gi_probe_set_cell_size(RID p_probe, float p_size); + virtual float gi_probe_get_cell_size(RID p_probe) const; + + virtual void gi_probe_set_to_cell_xform(RID p_probe,const Transform& p_xform); + virtual Transform gi_probe_get_to_cell_xform(RID p_probe) const; + + virtual void gi_probe_set_dynamic_data(RID p_probe,const DVector<int>& p_data); + virtual DVector<int> gi_probe_get_dynamic_data(RID p_probe) const; + + virtual void gi_probe_set_dynamic_range(RID p_probe,float p_range); + virtual float gi_probe_get_dynamic_range(RID p_probe) const; + + + virtual void gi_probe_set_static_data(RID p_gi_probe,const DVector<uint8_t>& p_data,VS::GIProbeDataFormat p_format,int p_width,int p_height,int p_depth); + virtual DVector<uint8_t> gi_probe_get_static_data(RID p_gi_probe) const; + virtual VS::GIProbeDataFormat gi_probe_get_static_data_format(RID p_gi_probe) const; + virtual int gi_probe_get_static_data_width(RID p_probe) const; + virtual int gi_probe_get_static_data_height(RID p_probe) const; + virtual int gi_probe_get_static_data_depth(RID p_probe) const; + + virtual RID gi_probe_get_data(RID p_probe); //get data in case this is static + virtual uint32_t gi_probe_get_version(RID p_probe); + + struct GIProbeData : public RID_Data { + + int width; + int height; + int depth; + int levels; + GLuint tex_id; + + GIProbeData() { + } + }; + + mutable RID_Owner<GIProbeData> 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 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/scene.glsl b/drivers/gles3/shaders/scene.glsl index 61e9e37d2b..bf561a7e46 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -838,6 +838,131 @@ void reflection_process(int idx, vec3 vertex, vec3 normal,vec3 binormal, vec3 ta } } +#ifdef USE_GI_PROBES + +uniform mediump sampler3D gi_probe1; //texunit:-6 +uniform highp mat4 gi_probe_xform1; +uniform highp vec3 gi_probe_bounds1; +uniform highp vec3 gi_probe_cell_size1; + +uniform mediump sampler3D gi_probe2; //texunit:-7 +uniform highp mat4 gi_probe_xform2; +uniform highp vec3 gi_probe_bounds2; +uniform highp vec3 gi_probe_cell_size2; +uniform bool gi_probe2_enabled; + +vec3 voxel_cone_trace(sampler3D probe, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance) { + + + float dist = dot(direction,mix(vec3(-1.0),vec3(1.0),greaterThan(direction,vec3(0.0))))*2.0; + float alpha=0.0; + vec4 color = vec4(0.0); + + while(dist < max_distance && alpha < 0.95) { + float diameter = max(1.0, 2.0 * tan_half_angle * dist); + vec4 scolor = textureLod(probe, (pos + dist * direction) * cell_size, log2(diameter) ); + float a = (1.0 - alpha); + color.rgb += a * scolor.rgb; + alpha += a * scolor.a; + dist += diameter * 0.5; + } + + return color.rgb; +} + +void gi_probe_compute(sampler3D probe, mat4 probe_xform, vec3 bounds,vec3 cell_size,vec3 pos, mat3 normal_mtx,vec3 ref_vec, float roughness, out vec4 out_spec, out vec4 out_diff) { + + + + vec3 probe_pos = (probe_xform * vec4(pos,1.0)).xyz; + vec3 ref_pos = (probe_xform * vec4(pos+ref_vec,1.0)).xyz; + + ref_vec = normalize(ref_pos - probe_pos); + +/* out_diff.rgb = voxel_cone_trace(probe,cell_size,probe_pos,normalize((probe_xform * vec4(ref_vec,0.0)).xyz),0.0 ,100.0); + out_diff.a = 1.0; + return;*/ + //out_diff = vec4(textureLod(probe,probe_pos*cell_size,3.0).rgb,1.0); + //return; + + if (any(bvec2(any(lessThan(probe_pos,vec3(0.0))),any(greaterThan(probe_pos,bounds))))) + return; + + vec3 blendv = probe_pos/bounds * 2.0 - 1.0; + float blend = 1.001-max(blendv.x,max(blendv.y,blendv.z)); + blend=1.0; + + //radiance + +#define MAX_CONE_DIRS 6 + vec3 cone_dirs[MAX_CONE_DIRS] = vec3[] ( + vec3(0, 0, 1), + vec3(0.866025, 0, 0.5), + vec3(0.267617, 0.823639, 0.5), + vec3(-0.700629, 0.509037, 0.5), + vec3(-0.700629, -0.509037, 0.5), + vec3(0.267617, -0.823639, 0.5) + ); + + float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15); + + float max_distance = length(bounds); + vec3 light=vec3(0.0); + for(int i=0;i<MAX_CONE_DIRS;i++) { + + vec3 dir = normalize( (probe_xform * vec4(pos + normal_mtx * cone_dirs[i],1.0)).xyz - probe_pos); + light+=cone_weights[i] * voxel_cone_trace(probe,cell_size,probe_pos,dir,0.577,max_distance); + + } + + out_diff = vec4(light*blend,blend); + + //irradiance + + vec3 irr_light = voxel_cone_trace(probe,cell_size,probe_pos,ref_vec,tan(roughness * 0.5 * M_PI) ,max_distance); + //irr_light=vec3(0.0); + + out_spec = vec4(irr_light*blend,blend); +} + + +void gi_probes_compute(vec3 pos, vec3 normal, float roughness, vec3 specular, inout vec3 out_specular, inout vec3 out_ambient) { + + + vec3 ref_vec = normalize(reflect(normalize(pos),normal)); + + //find arbitrary tangent and bitangent, then build a matrix + vec3 v0 = abs(normal.z) < 0.999 ? vec3(0, 0, 1) : vec3(0, 1, 0); + vec3 tangent = normalize(cross(v0, normal)); + vec3 bitangent = normalize(cross(tangent, normal)); + mat3 normal_mat = mat3(tangent,bitangent,normal); + + vec4 diff_accum = vec4(0.0); + vec4 spec_accum = vec4(0.0); + + gi_probe_compute(gi_probe1,gi_probe_xform1,gi_probe_bounds1,gi_probe_cell_size1,pos,normal_mat,ref_vec,roughness,spec_accum,diff_accum); + + if (gi_probe2_enabled) { + + gi_probe_compute(gi_probe2,gi_probe_xform2,gi_probe_bounds2,gi_probe_cell_size2,pos,normal_mat,ref_vec,roughness,spec_accum,diff_accum); + } + + if (diff_accum.a>0.0) { + diff_accum.rgb/=diff_accum.a; + } + + if (spec_accum.a>0.0) { + spec_accum.rgb/=spec_accum.a; + } + + out_specular+=spec_accum.rgb; + out_ambient+=diff_accum.rgb; + +} + +#endif + + void main() { #ifdef RENDER_SHADOW_DUAL_PARABOLOID @@ -1161,21 +1286,27 @@ FRAGMENT_SHADER_CODE #endif //#USE_LIGHT_DIRECTIONAL +#ifdef USE_GI_PROBES + gi_probes_compute(vertex,normal,roughness,specular,specular_light,ambient_light); +#endif + #ifdef USE_FORWARD_LIGHTING highp vec4 reflection_accum = vec4(0.0,0.0,0.0,0.0); highp vec4 ambient_accum = vec4(0.0,0.0,0.0,0.0); + + for(int i=0;i<reflection_count;i++) { reflection_process(reflection_indices[i],vertex,normal,binormal,tangent,roughness,anisotropy,ambient_light,specular_light,brdf,reflection_accum,ambient_accum); } if (reflection_accum.a>0.0) { - specular_light=reflection_accum.rgb/reflection_accum.a; + specular_light+=reflection_accum.rgb/reflection_accum.a; } if (ambient_accum.a>0.0) { - ambient_light=ambient_accum.rgb/ambient_accum.a; + ambient_light+=ambient_accum.rgb/ambient_accum.a; } for(int i=0;i<omni_light_count;i++) { diff --git a/scene/3d/baked_light_instance.cpp b/scene/3d/baked_light_instance.cpp index 07f0e4ee57..29b66b270a 100644 --- a/scene/3d/baked_light_instance.cpp +++ b/scene/3d/baked_light_instance.cpp @@ -28,83 +28,1737 @@ /*************************************************************************/ #include "baked_light_instance.h" #include "scene/scene_string_names.h" +#include "mesh_instance.h" +#include "light.h" +#include "math.h" +#define FINDMINMAX(x0,x1,x2,min,max) \ + min = max = x0; \ + if(x1<min) min=x1;\ + if(x1>max) max=x1;\ + if(x2<min) min=x2;\ + if(x2>max) max=x2; + +static bool planeBoxOverlap(Vector3 normal,float d, Vector3 maxbox) +{ + int q; + Vector3 vmin,vmax; + for(q=0;q<=2;q++) + { + if(normal[q]>0.0f) + { + vmin[q]=-maxbox[q]; + vmax[q]=maxbox[q]; + } + else + { + vmin[q]=maxbox[q]; + vmax[q]=-maxbox[q]; + } + } + if(normal.dot(vmin)+d>0.0f) return false; + if(normal.dot(vmax)+d>=0.0f) return true; + + return false; +} + + +/*======================== X-tests ========================*/ +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p2 = a*v2.y - b*v2.z; \ + if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \ + rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \ + if(min>rad || max<-rad) return false; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p1 = a*v1.y - b*v1.z; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \ + if(min>rad || max<-rad) return false; + +/*======================== Y-tests ========================*/ +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p2 = -a*v2.x + b*v2.z; \ + if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \ + if(min>rad || max<-rad) return false; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p1 = -a*v1.x + b*v1.z; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \ + if(min>rad || max<-rad) return false; + +/*======================== Z-tests ========================*/ + +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a*v1.x - b*v1.y; \ + p2 = a*v2.x - b*v2.y; \ + if(p2<p1) {min=p2; max=p1;} else {min=p1; max=p2;} \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \ + if(min>rad || max<-rad) return false; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a*v0.x - b*v0.y; \ + p1 = a*v1.x - b*v1.y; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \ + if(min>rad || max<-rad) return false; + +static bool fast_tri_box_overlap(const Vector3& boxcenter,const Vector3 boxhalfsize,const Vector3 *triverts) { + + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + Vector3 v0,v1,v2; + float min,max,d,p0,p1,p2,rad,fex,fey,fez; + Vector3 normal,e0,e1,e2; + + /* This is the fastest branch on Sun */ + /* move everything so that the boxcenter is in (0,0,0) */ + + v0=triverts[0]-boxcenter; + v1=triverts[1]-boxcenter; + v2=triverts[2]-boxcenter; + + /* compute triangle edges */ + e0=v1-v0; /* tri edge 0 */ + e1=v2-v1; /* tri edge 1 */ + e2=v0-v2; /* tri edge 2 */ + + /* Bullet 3: */ + /* test the 9 tests first (this was faster) */ + fex = Math::abs(e0.x); + fey = Math::abs(e0.y); + fez = Math::abs(e0.z); + AXISTEST_X01(e0.z, e0.y, fez, fey); + AXISTEST_Y02(e0.z, e0.x, fez, fex); + AXISTEST_Z12(e0.y, e0.x, fey, fex); + + fex = Math::abs(e1.x); + fey = Math::abs(e1.y); + fez = Math::abs(e1.z); + AXISTEST_X01(e1.z, e1.y, fez, fey); + AXISTEST_Y02(e1.z, e1.x, fez, fex); + AXISTEST_Z0(e1.y, e1.x, fey, fex); + + fex = Math::abs(e2.x); + fey = Math::abs(e2.y); + fez = Math::abs(e2.z); + AXISTEST_X2(e2.z, e2.y, fez, fey); + AXISTEST_Y1(e2.z, e2.x, fez, fex); + AXISTEST_Z12(e2.y, e2.x, fey, fex); + + /* Bullet 1: */ + /* first test overlap in the {x,y,z}-directions */ + /* find min, max of the triangle each direction, and test for overlap in */ + /* that direction -- this is equivalent to testing a minimal AABB around */ + /* the triangle against the AABB */ + + /* test in X-direction */ + FINDMINMAX(v0.x,v1.x,v2.x,min,max); + if(min>boxhalfsize.x || max<-boxhalfsize.x) return false; + + /* test in Y-direction */ + FINDMINMAX(v0.y,v1.y,v2.y,min,max); + if(min>boxhalfsize.y || max<-boxhalfsize.y) return false; + + /* test in Z-direction */ + FINDMINMAX(v0.z,v1.z,v2.z,min,max); + if(min>boxhalfsize.z || max<-boxhalfsize.z) return false; + + /* Bullet 2: */ + /* test if the box intersects the plane of the triangle */ + /* compute plane equation of triangle: normal*x+d=0 */ + normal=e0.cross(e1); + d=-normal.dot(v0); /* plane eq: normal.x+d=0 */ + if(!planeBoxOverlap(normal,d,boxhalfsize)) return false; + + return true; /* box and triangle overlaps */ +} + + +Vector<Color> BakedLight::_get_bake_texture(Image &p_image,const Color& p_color) { + + Vector<Color> ret; + + if (p_image.empty()) { + + ret.resize(bake_texture_size*bake_texture_size); + for(int i=0;i<bake_texture_size*bake_texture_size;i++) { + ret[i]=p_color; + } + + return ret; + } + + p_image.convert(Image::FORMAT_RGBA8); + p_image.resize(bake_texture_size,bake_texture_size,Image::INTERPOLATE_CUBIC); + + + DVector<uint8_t>::Read r = p_image.get_data().read(); + ret.resize(bake_texture_size*bake_texture_size); + + for(int i=0;i<bake_texture_size*bake_texture_size;i++) { + Color c; + c.r = r[i*4+0]/255.0; + c.g = r[i*4+1]/255.0; + c.b = r[i*4+2]/255.0; + c.a = r[i*4+3]/255.0; + ret[i]=c; + + } + + return ret; +} + + +BakedLight::MaterialCache BakedLight::_get_material_cache(Ref<Material> p_material) { + + //this way of obtaining materials is inaccurate and also does not support some compressed formats very well + Ref<FixedSpatialMaterial> mat = p_material; + + Ref<Material> material = mat; //hack for now + + if (material_cache.has(material)) { + return material_cache[material]; + } + + MaterialCache mc; + + if (mat.is_valid()) { + + + Ref<ImageTexture> albedo_tex = mat->get_texture(FixedSpatialMaterial::TEXTURE_ALBEDO); + + Image img_albedo; + if (albedo_tex.is_valid()) { + + img_albedo = albedo_tex->get_data(); + } + + mc.albedo=_get_bake_texture(img_albedo,mat->get_albedo()); + + Ref<ImageTexture> emission_tex = mat->get_texture(FixedSpatialMaterial::TEXTURE_EMISSION); + + Color emission_col = mat->get_emission(); + emission_col.r*=mat->get_emission_energy(); + emission_col.g*=mat->get_emission_energy(); + emission_col.b*=mat->get_emission_energy(); + + Image img_emission; + + if (emission_tex.is_valid()) { + + img_emission = emission_tex->get_data(); + } + + mc.emission=_get_bake_texture(img_emission,emission_col); + + } else { + Image empty; + + mc.albedo=_get_bake_texture(empty,Color(0.7,0.7,0.7)); + mc.emission=_get_bake_texture(empty,Color(0,0,0)); + + + } + + material_cache[p_material]=mc; + return mc; + + +} + + + +static _FORCE_INLINE_ Vector2 get_uv(const Vector3& p_pos, const Vector3 *p_vtx, const Vector2* p_uv) { + + if (p_pos.distance_squared_to(p_vtx[0])<CMP_EPSILON2) + return p_uv[0]; + if (p_pos.distance_squared_to(p_vtx[1])<CMP_EPSILON2) + return p_uv[1]; + if (p_pos.distance_squared_to(p_vtx[2])<CMP_EPSILON2) + return p_uv[2]; + + Vector3 v0 = p_vtx[1] - p_vtx[0]; + Vector3 v1 = p_vtx[2] - p_vtx[0]; + Vector3 v2 = p_pos - p_vtx[0]; + + float d00 = v0.dot( v0); + float d01 = v0.dot( v1); + float d11 = v1.dot( v1); + float d20 = v2.dot( v0); + float d21 = v2.dot( v1); + float denom = (d00 * d11 - d01 * d01); + if (denom==0) + return p_uv[0]; + float v = (d11 * d20 - d01 * d21) / denom; + float w = (d00 * d21 - d01 * d20) / denom; + float u = 1.0f - v - w; + + return p_uv[0]*u + p_uv[1]*v + p_uv[2]*w; +} + +void BakedLight::_plot_face(int p_idx, int p_level, const Vector3 *p_vtx, const Vector2* p_uv, const MaterialCache& p_material, const AABB &p_aabb) { + + + + if (p_level==cell_subdiv-1) { + //plot the face by guessing it's albedo and emission value + + //find best axis to map to, for scanning values + int closest_axis; + float closest_dot; + + Vector3 normal = Plane(p_vtx[0],p_vtx[1],p_vtx[2]).normal; + + for(int i=0;i<3;i++) { + + Vector3 axis; + axis[i]=1.0; + float dot=ABS(normal.dot(axis)); + if (i==0 || dot>closest_dot) { + closest_axis=i; + closest_dot=dot; + } + } + + Vector3 axis; + axis[closest_axis]=1.0; + Vector3 t1; + t1[(closest_axis+1)%3]=1.0; + Vector3 t2; + t2[(closest_axis+2)%3]=1.0; + + t1*=p_aabb.size[(closest_axis+1)%3]/float(color_scan_cell_width); + t2*=p_aabb.size[(closest_axis+2)%3]/float(color_scan_cell_width); + + Color albedo_accum; + Color emission_accum; + float alpha=0.0; + + //map to a grid average in the best axis for this face + for(int i=0;i<color_scan_cell_width;i++) { + + Vector3 ofs_i=float(i)*t1; + + for(int j=0;j<color_scan_cell_width;j++) { + + Vector3 ofs_j=float(j)*t2; + + Vector3 from = p_aabb.pos+ofs_i+ofs_j; + Vector3 to = from + t1 + t2 + axis * p_aabb.size[closest_axis]; + Vector3 half = (to-from)*0.5; + + //is in this cell? + if (!fast_tri_box_overlap(from+half,half,p_vtx)) { + continue; //face does not span this cell + } + + //go from -size to +size*2 to avoid skipping collisions + Vector3 ray_from = from + (t1+t2)*0.5 - axis * p_aabb.size[closest_axis]; + Vector3 ray_to = ray_from + axis * p_aabb.size[closest_axis]*2; + + Vector3 intersection; + + if (!Geometry::ray_intersects_triangle(ray_from,ray_to,p_vtx[0],p_vtx[1],p_vtx[2],&intersection)) { + //no intersect? look in edges + + float closest_dist=1e20; + for(int j=0;j<3;j++) { + Vector3 c; + Vector3 inters; + Geometry::get_closest_points_between_segments(p_vtx[j],p_vtx[(j+1)%3],ray_from,ray_to,inters,c); + float d=c.distance_to(intersection); + if (j==0 || d<closest_dist) { + closest_dist=d; + intersection=inters; + } + } + } + + Vector2 uv = get_uv(intersection,p_vtx,p_uv); + + + int uv_x = CLAMP(Math::fposmod(uv.x,1.0)*bake_texture_size,0,bake_texture_size-1); + int uv_y = CLAMP(Math::fposmod(uv.y,1.0)*bake_texture_size,0,bake_texture_size-1); + + int ofs = uv_y*bake_texture_size+uv_x; + albedo_accum.r+=p_material.albedo[ofs].r; + albedo_accum.g+=p_material.albedo[ofs].g; + albedo_accum.b+=p_material.albedo[ofs].b; + albedo_accum.a+=p_material.albedo[ofs].a; + + emission_accum.r+=p_material.emission[ofs].r; + emission_accum.g+=p_material.emission[ofs].g; + emission_accum.b+=p_material.emission[ofs].b; + alpha+=1.0; + + } + } + + + if (alpha==0) { + //could not in any way get texture information.. so use closest point to center + + Face3 f( p_vtx[0],p_vtx[1],p_vtx[2]); + Vector3 inters = f.get_closest_point_to(p_aabb.pos+p_aabb.size*0.5); + + Vector2 uv = get_uv(inters,p_vtx,p_uv); + + int uv_x = CLAMP(Math::fposmod(uv.x,1.0)*bake_texture_size,0,bake_texture_size-1); + int uv_y = CLAMP(Math::fposmod(uv.y,1.0)*bake_texture_size,0,bake_texture_size-1); + + int ofs = uv_y*bake_texture_size+uv_x; + + alpha = 1.0/(color_scan_cell_width*color_scan_cell_width); + + albedo_accum.r=p_material.albedo[ofs].r*alpha; + albedo_accum.g=p_material.albedo[ofs].g*alpha; + albedo_accum.b=p_material.albedo[ofs].b*alpha; + albedo_accum.a=p_material.albedo[ofs].a*alpha; + + emission_accum.r=p_material.emission[ofs].r*alpha; + emission_accum.g=p_material.emission[ofs].g*alpha; + emission_accum.b=p_material.emission[ofs].b*alpha; + + + zero_alphas++; + } else { + + float accdiv = 1.0/(color_scan_cell_width*color_scan_cell_width); + alpha*=accdiv; + + albedo_accum.r*=accdiv; + albedo_accum.g*=accdiv; + albedo_accum.b*=accdiv; + albedo_accum.a*=accdiv; + + emission_accum.r*=accdiv; + emission_accum.g*=accdiv; + emission_accum.b*=accdiv; + } + + //put this temporarily here, corrected in a later step + bake_cells_write[p_idx].albedo[0]+=albedo_accum.r; + bake_cells_write[p_idx].albedo[1]+=albedo_accum.g; + bake_cells_write[p_idx].albedo[2]+=albedo_accum.b; + bake_cells_write[p_idx].light[0]+=emission_accum.r; + bake_cells_write[p_idx].light[1]+=emission_accum.g; + bake_cells_write[p_idx].light[2]+=emission_accum.b; + bake_cells_write[p_idx].alpha+=alpha; + + static const Vector3 side_normals[6]={ + Vector3(-1, 0, 0), + Vector3( 1, 0, 0), + Vector3( 0,-1, 0), + Vector3( 0, 1, 0), + Vector3( 0, 0,-1), + Vector3( 0, 0, 1), + }; + + for(int i=0;i<6;i++) { + if (normal.dot(side_normals[i])>CMP_EPSILON) { + bake_cells_write[p_idx].used_sides|=(1<<i); + } + } + + + } else { + //go down + for(int i=0;i<8;i++) { + + AABB aabb=p_aabb; + aabb.size*=0.5; + + if (i&1) + aabb.pos.x+=aabb.size.x; + if (i&2) + aabb.pos.y+=aabb.size.y; + if (i&4) + aabb.pos.z+=aabb.size.z; + + { + AABB test_aabb=aabb; + //test_aabb.grow_by(test_aabb.get_longest_axis_size()*0.05); //grow a bit to avoid numerical error in real-time + Vector3 qsize = test_aabb.size*0.5; //quarter size, for fast aabb test + + if (!fast_tri_box_overlap(test_aabb.pos+qsize,qsize,p_vtx)) { + //if (!Face3(p_vtx[0],p_vtx[1],p_vtx[2]).intersects_aabb2(aabb)) { + //does not fit in child, go on + continue; + } + + } + + if (bake_cells_write[p_idx].childs[i]==CHILD_EMPTY) { + //sub cell must be created + + if (bake_cells_used==(1<<bake_cells_alloc)) { + //exhausted cells, creating more space + bake_cells_alloc++; + bake_cells_write=DVector<BakeCell>::Write(); + bake_cells.resize(1<<bake_cells_alloc); + bake_cells_write=bake_cells.write(); + } + + bake_cells_write[p_idx].childs[i]=bake_cells_used; + bake_cells_level_used[p_level+1]++; + bake_cells_used++; + + + } + + + _plot_face(bake_cells_write[p_idx].childs[i],p_level+1,p_vtx,p_uv,p_material,aabb); + } + } +} + + + +void BakedLight::_fixup_plot(int p_idx, int p_level,int p_x,int p_y, int p_z) { + + + + if (p_level==cell_subdiv-1) { + + + float alpha = bake_cells_write[p_idx].alpha; + + bake_cells_write[p_idx].albedo[0]/=alpha; + bake_cells_write[p_idx].albedo[1]/=alpha; + bake_cells_write[p_idx].albedo[2]/=alpha; + + //transfer emission to light + bake_cells_write[p_idx].light[0]/=alpha; + bake_cells_write[p_idx].light[1]/=alpha; + bake_cells_write[p_idx].light[2]/=alpha; + + bake_cells_write[p_idx].alpha=1.0; + + //remove neighbours from used sides + + for(int n=0;n<6;n++) { + + int ofs[3]={0,0,0}; + + ofs[n/2]=(n&1)?1:-1; + + //convert to x,y,z on this level + int x=p_x; + int y=p_y; + int z=p_z; + + x+=ofs[0]; + y+=ofs[1]; + z+=ofs[2]; + + int ofs_x=0; + int ofs_y=0; + int ofs_z=0; + int size = 1<<p_level; + int half=size/2; + + + if (x<0 || x>=size || y<0 || y>=size || z<0 || z>=size) { + //neighbour is out, can't use it + bake_cells_write[p_idx].used_sides&=~(1<<uint32_t(n)); + continue; + } + + uint32_t neighbour=0; + + for(int i=0;i<cell_subdiv-1;i++) { + + BakeCell *bc = &bake_cells_write[neighbour]; + + int child = 0; + if (x >= ofs_x + half) { + child|=1; + ofs_x+=half; + } + if (y >= ofs_y + half) { + child|=2; + ofs_y+=half; + } + if (z >= ofs_z + half) { + child|=4; + ofs_z+=half; + } + + neighbour = bc->childs[child]; + if (neighbour==CHILD_EMPTY) { + break; + } + + half>>=1; + } + + if (neighbour!=CHILD_EMPTY) { + bake_cells_write[p_idx].used_sides&=~(1<<uint32_t(n)); + } + } + } else { + + + //go down + + float alpha_average=0; + int half = cells_per_axis >> (p_level+1); + for(int i=0;i<8;i++) { + + uint32_t child = bake_cells_write[p_idx].childs[i]; + + if (child==CHILD_EMPTY) + continue; + + + int nx=p_x; + int ny=p_y; + int nz=p_z; + + if (i&1) + nx+=half; + if (i&2) + ny+=half; + if (i&4) + nz+=half; + + _fixup_plot(child,p_level+1,nx,ny,nz); + alpha_average+=bake_cells_write[child].alpha; + } + + bake_cells_write[p_idx].alpha=alpha_average/8.0; + bake_cells_write[p_idx].light[0]=0; + bake_cells_write[p_idx].light[1]=0; + bake_cells_write[p_idx].light[2]=0; + bake_cells_write[p_idx].albedo[0]=0; + bake_cells_write[p_idx].albedo[1]=0; + bake_cells_write[p_idx].albedo[2]=0; + + } + + //clean up light + bake_cells_write[p_idx].light_pass=0; + //find neighbours + + + +} + + +void BakedLight::_bake_add_mesh(const Transform& p_xform,Ref<Mesh>& p_mesh) { + + + for(int i=0;i<p_mesh->get_surface_count();i++) { + + if (p_mesh->surface_get_primitive_type(i)!=Mesh::PRIMITIVE_TRIANGLES) + continue; //only triangles + + MaterialCache material = _get_material_cache(p_mesh->surface_get_material(i)); + + Array a = p_mesh->surface_get_arrays(i); + + + DVector<Vector3> vertices = a[Mesh::ARRAY_VERTEX]; + DVector<Vector3>::Read vr=vertices.read(); + DVector<Vector2> uv = a[Mesh::ARRAY_TEX_UV]; + DVector<Vector2>::Read uvr; + DVector<int> index = a[Mesh::ARRAY_INDEX]; + + bool read_uv=false; + + if (uv.size()) { + + uvr=uv.read(); + read_uv=true; + } + + if (index.size()) { + + int facecount = index.size()/3; + DVector<int>::Read ir=index.read(); + + for(int j=0;j<facecount;j++) { + + Vector3 vtxs[3]; + Vector2 uvs[3]; + + for(int k=0;k<3;k++) { + vtxs[k]=p_xform.xform(vr[ir[j*3+k]]); + } + + if (read_uv) { + for(int k=0;k<3;k++) { + uvs[k]=uvr[ir[j*3+k]]; + } + } + + //plot face + _plot_face(0,0,vtxs,uvs,material,bounds); + } + + + + } else { + + int facecount = vertices.size()/3; + + for(int j=0;j<facecount;j++) { + + Vector3 vtxs[3]; + Vector2 uvs[3]; + + for(int k=0;k<3;k++) { + vtxs[k]=p_xform.xform(vr[j*3+k]); + } + + if (read_uv) { + for(int k=0;k<3;k++) { + uvs[k]=uvr[j*3+k]; + } + } + + //plot face + _plot_face(0,0,vtxs,uvs,material,bounds); + } + + } + } +} + + + +void BakedLight::_bake_add_to_aabb(const Transform& p_xform,Ref<Mesh>& p_mesh,bool &first) { + + for(int i=0;i<p_mesh->get_surface_count();i++) { + + if (p_mesh->surface_get_primitive_type(i)!=Mesh::PRIMITIVE_TRIANGLES) + continue; //only triangles + + Array a = p_mesh->surface_get_arrays(i); + DVector<Vector3> vertices = a[Mesh::ARRAY_VERTEX]; + int vc = vertices.size(); + DVector<Vector3>::Read vr=vertices.read(); + + if (first) { + bounds.pos=p_xform.xform(vr[0]); + first=false; + } + + + for(int j=0;j<vc;j++) { + bounds.expand_to(p_xform.xform(vr[j])); + } + } +} + +void BakedLight::bake() { + + + bake_cells_alloc=16; + bake_cells.resize(1<<bake_cells_alloc); + bake_cells_used=1; + cells_per_axis=(1<<(cell_subdiv-1)); + zero_alphas=0; + + bool aabb_first=true; + print_line("Generating AABB"); + + bake_cells_level_used.resize(cell_subdiv); + for(int i=0;i<cell_subdiv;i++) { + bake_cells_level_used[i]=0; + } + + int count=0; + for (Set<GeometryInstance*>::Element *E=geometries.front();E;E=E->next()) { + + print_line("aabb geom "+itos(count)+"/"+itos(geometries.size())); + + GeometryInstance *geom = E->get(); + + if (geom->cast_to<MeshInstance>()) { + + MeshInstance *mesh_instance = geom->cast_to<MeshInstance>(); + Ref<Mesh> mesh = mesh_instance->get_mesh(); + if (mesh.is_valid()) { + + _bake_add_to_aabb(geom->get_relative_transform(this),mesh,aabb_first); + } + } + count++; + } + + print_line("AABB: "+bounds); + ERR_FAIL_COND(aabb_first); + + bake_cells_write = bake_cells.write(); + count=0; + + for (Set<GeometryInstance*>::Element *E=geometries.front();E;E=E->next()) { + + GeometryInstance *geom = E->get(); + print_line("plot geom "+itos(count)+"/"+itos(geometries.size())); + + if (geom->cast_to<MeshInstance>()) { + + MeshInstance *mesh_instance = geom->cast_to<MeshInstance>(); + Ref<Mesh> mesh = mesh_instance->get_mesh(); + if (mesh.is_valid()) { + + _bake_add_mesh(geom->get_relative_transform(this),mesh); + } + } + + count++; + } + + + _fixup_plot(0, 0,0,0,0); + + + bake_cells_write=DVector<BakeCell>::Write(); + + bake_cells.resize(bake_cells_used); + + + + print_line("total bake cells used: "+itos(bake_cells_used)); + for(int i=0;i<cell_subdiv;i++) { + print_line("level "+itos(i)+": "+itos(bake_cells_level_used[i])); + } + print_line("zero alphas: "+itos(zero_alphas)); + + + +} + + + +void BakedLight::_bake_directional(int p_idx, int p_level, int p_x,int p_y,int p_z,const Vector3& p_dir,const Color& p_color,int p_sign) { + + + + + if (p_level==cell_subdiv-1) { + + Vector3 end; + end.x = float(p_x+0.5) / cells_per_axis; + end.y = float(p_y+0.5) / cells_per_axis; + end.z = float(p_z+0.5) / cells_per_axis; + + end = bounds.pos + bounds.size*end; + + float max_ray_len = (bounds.size).length()*1.2; + + Vector3 begin = end + max_ray_len*-p_dir; + + //clip begin + + for(int i=0;i<3;i++) { + + if (ABS(p_dir[i])<CMP_EPSILON) { + continue; // parallel to axis, don't clip + } + + Plane p; + p.normal[i]=1.0; + p.d=bounds.pos[i]; + if (p_dir[i]<0) { + p.d+=bounds.size[i]; + } + + Vector3 inters; + if (p.intersects_segment(end,begin,&inters)) { + begin=inters; + } + + } + + + int idx = _plot_ray(begin,end); + + if (idx>=0 && light_pass!=bake_cells_write[idx].light_pass) { + //hit something, add or remove light to it + + Color albedo = Color(bake_cells_write[idx].albedo[0],bake_cells_write[idx].albedo[1],bake_cells_write[idx].albedo[2]); + bake_cells_write[idx].light[0]+=albedo.r*p_color.r*p_sign; + bake_cells_write[idx].light[1]+=albedo.g*p_color.g*p_sign; + bake_cells_write[idx].light[2]+=albedo.b*p_color.b*p_sign; + bake_cells_write[idx].light_pass=light_pass; + + } + + + } else { + + int half = cells_per_axis >> (p_level+1); + + //go down + for(int i=0;i<8;i++) { + + uint32_t child = bake_cells_write[p_idx].childs[i]; + + if (child==CHILD_EMPTY) + continue; + + int nx=p_x; + int ny=p_y; + int nz=p_z; + + if (i&1) + nx+=half; + if (i&2) + ny+=half; + if (i&4) + nz+=half; + + + _bake_directional(child,p_level+1,nx,ny,nz,p_dir,p_color,p_sign); + } + } +} + + + + +void BakedLight::_bake_light(Light* p_light) { + + if (p_light->cast_to<DirectionalLight>()) { + + DirectionalLight * dl = p_light->cast_to<DirectionalLight>(); + + Transform rel_xf = dl->get_relative_transform(this); + + Vector3 light_dir = -rel_xf.basis.get_axis(2); + + Color color = dl->get_color(); + float nrg = dl->get_param(Light::PARAM_ENERGY);; + color.r*=nrg; + color.g*=nrg; + color.b*=nrg; + + light_pass++; + _bake_directional(0,0,0,0,0,light_dir,color,1); + + } +} + + +void BakedLight::_upscale_light(int p_idx,int p_level) { + + + //go down + + float light_accum[3]={0,0,0}; + float alpha_accum=0; + + bool check_children = p_level < (cell_subdiv -2); + + for(int i=0;i<8;i++) { + + uint32_t child = bake_cells_write[p_idx].childs[i]; + + if (child==CHILD_EMPTY) + continue; + + if (check_children) { + _upscale_light(child,p_level+1); + } + + light_accum[0]+=bake_cells_write[child].light[0]; + light_accum[1]+=bake_cells_write[child].light[1]; + light_accum[2]+=bake_cells_write[child].light[2]; + alpha_accum+=bake_cells_write[child].alpha; + + } + + bake_cells_write[p_idx].light[0]=light_accum[0]/8.0; + bake_cells_write[p_idx].light[1]=light_accum[1]/8.0; + bake_cells_write[p_idx].light[2]=light_accum[2]/8.0; + bake_cells_write[p_idx].alpha=alpha_accum/8.0; + +} + + +void BakedLight::bake_lights() { + + ERR_FAIL_COND(bake_cells.size()==0); + + bake_cells_write = bake_cells.write(); + + for(Set<Light*>::Element *E=lights.front();E;E=E->next()) { + + _bake_light(E->get()); + } + + + _upscale_light(0,0); + + bake_cells_write=DVector<BakeCell>::Write(); + +} + + + +Color BakedLight::_cone_trace(const Vector3& p_from, const Vector3& p_dir, float p_half_angle) { + + + Color color(0,0,0,0); + float tha = Math::tan(p_half_angle);//tan half angle + Vector3 from =(p_from-bounds.pos)/bounds.size; //convert to 0..1 + from/=cells_per_axis; //convert to voxels of size 1 + Vector3 dir = (p_dir/bounds.size).normalized(); + + float max_dist = Vector3(cells_per_axis,cells_per_axis,cells_per_axis).length(); + + float dist = 1.0; + // self occlusion in flat surfaces + + float alpha=0; + + + while(dist < max_dist && alpha < 0.95) { + +#if 0 + // smallest sample diameter possible is the voxel size + float diameter = MAX(1.0, 2.0 * tha * dist); + float lod = log2(diameter); + + Vector3 sample_pos = from + dist * dir; + + + Color samples_base[2][8]={{Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0)}, + {Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0),Color(0,0,0,0)}}; + + float levelf = Math::fposmod(lod,1.0); + float fx = Math::fposmod(sample_pos.x,1.0); + float fy = Math::fposmod(sample_pos.y,1.0); + float fz = Math::fposmod(sample_pos.z,1.0); + + for(int l=0;l<2;l++){ + + int bx = Math::floor(sample_pos.x); + int by = Math::floor(sample_pos.y); + int bz = Math::floor(sample_pos.z); + + int lodn=int(Math::floor(lod))-l; + + bx>>=lodn; + by>>=lodn; + bz>>=lodn; + + int limit = MAX(0,cell_subdiv-lodn-1); + + for(int c=0;c<8;c++) { + + int x = bx; + int y = by; + int z = bz; + + if (c&1) { + x+=1; + } + if (c&2) { + y+=1; + } + if (c&4) { + z+=1; + } + + int ofs_x=0; + int ofs_y=0; + int ofs_z=0; + int size = cells_per_axis>>lodn; + int half=size/2; + + bool outside=x<0 || x>=size || y<0 || y>=size || z<0 || z>=size; + + if (outside) + continue; + + + uint32_t cell=0; + + for(int i=0;i<limit;i++) { + + BakeCell *bc = &bake_cells_write[cell]; + + int child = 0; + if (x >= ofs_x + half) { + child|=1; + ofs_x+=half; + } + if (y >= ofs_y + half) { + child|=2; + ofs_y+=half; + } + if (z >= ofs_z + half) { + child|=4; + ofs_z+=half; + } + + cell = bc->childs[child]; + if (cell==CHILD_EMPTY) + break; + + half>>=1; + } + + if (cell!=CHILD_EMPTY) { + + samples_base[l][c].r=bake_cells_write[cell].light[0]; + samples_base[l][c].g=bake_cells_write[cell].light[1]; + samples_base[l][c].b=bake_cells_write[cell].light[2]; + samples_base[l][c].a=bake_cells_write[cell].alpha; + } + + } + + + } + + Color m0x0 = samples_base[0][0].linear_interpolate(samples_base[0][1],fx); + Color m0x1 = samples_base[0][2].linear_interpolate(samples_base[0][3],fx); + Color m0y0 = m0x0.linear_interpolate(m0x1,fy); + m0x0 = samples_base[0][4].linear_interpolate(samples_base[0][5],fx); + m0x1 = samples_base[0][6].linear_interpolate(samples_base[0][7],fx); + Color m0y1 = m0x0.linear_interpolate(m0x1,fy); + Color m0z = m0y0.linear_interpolate(m0y1,fz); + + Color m1x0 = samples_base[1][0].linear_interpolate(samples_base[1][1],fx); + Color m1x1 = samples_base[1][2].linear_interpolate(samples_base[1][3],fx); + Color m1y0 = m1x0.linear_interpolate(m1x1,fy); + m1x0 = samples_base[1][4].linear_interpolate(samples_base[1][5],fx); + m1x1 = samples_base[1][6].linear_interpolate(samples_base[1][7],fx); + Color m1y1 = m1x0.linear_interpolate(m1x1,fy); + Color m1z = m1y0.linear_interpolate(m1y1,fz); + + Color m = m0z.linear_interpolate(m1z,levelf); +#else + float diameter = 1.0; + Vector3 sample_pos = from + dist * dir; + + Color m(0,0,0,0); + { + int x = Math::floor(sample_pos.x); + int y = Math::floor(sample_pos.y); + int z = Math::floor(sample_pos.z); + + int ofs_x=0; + int ofs_y=0; + int ofs_z=0; + int size = cells_per_axis; + int half=size/2; + + bool outside=x<0 || x>=size || y<0 || y>=size || z<0 || z>=size; + + if (!outside) { + + + uint32_t cell=0; + + for(int i=0;i<cell_subdiv-1;i++) { + + BakeCell *bc = &bake_cells_write[cell]; + + int child = 0; + if (x >= ofs_x + half) { + child|=1; + ofs_x+=half; + } + if (y >= ofs_y + half) { + child|=2; + ofs_y+=half; + } + if (z >= ofs_z + half) { + child|=4; + ofs_z+=half; + } + + cell = bc->childs[child]; + if (cell==CHILD_EMPTY) + break; + + half>>=1; + } + + if (cell!=CHILD_EMPTY) { + + m.r=bake_cells_write[cell].light[0]; + m.g=bake_cells_write[cell].light[1]; + m.b=bake_cells_write[cell].light[2]; + m.a=bake_cells_write[cell].alpha; + } + } + } + +#endif + // front-to-back compositing + float a = (1.0 - alpha); + color.r += a * m.r; + color.g += a * m.g; + color.b += a * m.b; + alpha += a * m.a; + //occlusion += a * voxelColor.a; + //occlusion += (a * voxelColor.a) / (1.0 + 0.03 * diameter); + dist += diameter * 0.5; // smoother + //dist += diameter; // faster but misses more voxels + } + + return color; +} + + + +void BakedLight::_bake_radiance(int p_idx, int p_level, int p_x,int p_y,int p_z) { + + + + + if (p_level==cell_subdiv-1) { + + const int NUM_CONES = 6; + Vector3 cone_directions[6] = { + Vector3(1, 0, 0), + Vector3(0.5, 0.866025, 0), + Vector3( 0.5, 0.267617, 0.823639), + Vector3( 0.5, -0.700629, 0.509037), + Vector3( 0.5, -0.700629, -0.509037), + Vector3( 0.5, 0.267617, -0.823639) + }; + float coneWeights[6] = {0.25, 0.15, 0.15, 0.15, 0.15, 0.15}; + + Vector3 pos = (Vector3(p_x,p_y,p_z)/float(cells_per_axis))*bounds.size+bounds.pos; + Vector3 voxel_size = bounds.size/float(cells_per_axis); + pos+=voxel_size*0.5; + + Color accum; + + bake_cells_write[p_idx].light[0]=0; + bake_cells_write[p_idx].light[1]=0; + bake_cells_write[p_idx].light[2]=0; + + int freepix=0; + for(int i=0;i<6;i++) { + + if (!(bake_cells_write[p_idx].used_sides&(1<<i))) + continue; + + if ((i&1)==0) + bake_cells_write[p_idx].light[i/2]=1.0; + freepix++; + continue; + + int ofs = i/2; + + Vector3 dir; + if ((i&1)==0) + dir[ofs]=1.0; + else + dir[ofs]=-1.0; + + for(int j=0;j<1;j++) { + + + Vector3 cone_dir; + cone_dir.x = cone_directions[j][(ofs+0)%3]; + cone_dir.y = cone_directions[j][(ofs+1)%3]; + cone_dir.z = cone_directions[j][(ofs+2)%3]; + + cone_dir[ofs]*=dir[ofs]; + + Color res = _cone_trace(pos+dir*voxel_size,cone_dir,Math::deg2rad(29.9849)); + accum.r+=res.r;//*coneWeights[j]; + accum.g+=res.g;//*coneWeights[j]; + accum.b+=res.b;//*coneWeights[j]; + } + + + } #if 0 + if (freepix==0) { + bake_cells_write[p_idx].light[0]=0; + bake_cells_write[p_idx].light[1]=0; + bake_cells_write[p_idx].light[2]=0; + } + + if (freepix==1) { + bake_cells_write[p_idx].light[0]=1; + bake_cells_write[p_idx].light[1]=0; + bake_cells_write[p_idx].light[2]=0; + } + + if (freepix==2) { + bake_cells_write[p_idx].light[0]=0; + bake_cells_write[p_idx].light[1]=1; + bake_cells_write[p_idx].light[2]=0; + } + + if (freepix==3) { + bake_cells_write[p_idx].light[0]=1; + bake_cells_write[p_idx].light[1]=1; + bake_cells_write[p_idx].light[2]=0; + } -RID BakedLightInstance::get_baked_light_instance() const { + if (freepix==4) { + bake_cells_write[p_idx].light[0]=0; + bake_cells_write[p_idx].light[1]=0; + bake_cells_write[p_idx].light[2]=1; + } - if (baked_light.is_null()) - return RID(); - else - return get_instance(); + if (freepix==5) { + bake_cells_write[p_idx].light[0]=1; + bake_cells_write[p_idx].light[1]=0; + bake_cells_write[p_idx].light[2]=1; + } + if (freepix==6) { + bake_cells_write[p_idx].light[0]=0; + bake_cells_write[p_idx].light[0]=1; + bake_cells_write[p_idx].light[0]=1; + } +#endif + //bake_cells_write[p_idx].radiance[0]=accum.r; + //bake_cells_write[p_idx].radiance[1]=accum.g; + //bake_cells_write[p_idx].radiance[2]=accum.b; + + + } else { + + int half = cells_per_axis >> (p_level+1); + + //go down + for(int i=0;i<8;i++) { + + uint32_t child = bake_cells_write[p_idx].childs[i]; + + if (child==CHILD_EMPTY) + continue; + + int nx=p_x; + int ny=p_y; + int nz=p_z; + + if (i&1) + nx+=half; + if (i&2) + ny+=half; + if (i&4) + nz+=half; + + + _bake_radiance(child,p_level+1,nx,ny,nz); + } + } } -void BakedLightInstance::set_baked_light(const Ref<BakedLight>& p_baked_light) { +void BakedLight::bake_radiance() { + + ERR_FAIL_COND(bake_cells.size()==0); - baked_light=p_baked_light; + bake_cells_write = bake_cells.write(); + + _bake_radiance(0,0,0,0,0); + + bake_cells_write=DVector<BakeCell>::Write(); + +} +int BakedLight::_find_cell(int x,int y, int z) { - RID base_rid; - if (baked_light.is_valid()) - base_rid=baked_light->get_rid(); - else - base_rid=RID(); + uint32_t cell=0; - set_base(base_rid); + int ofs_x=0; + int ofs_y=0; + int ofs_z=0; + int size = cells_per_axis; + int half=size/2; - if (is_inside_world()) { + if (x<0 || x>=size) + return -1; + if (y<0 || y>=size) + return -1; + if (z<0 || z>=size) + return -1; - emit_signal(SceneStringNames::get_singleton()->baked_light_changed); + for(int i=0;i<cell_subdiv-1;i++) { -// for (List<Node*>::Element *E=baked_geometry.front();E;E=E->next()) { -// VS::get_singleton()->instance_geometry_set_baked_light(E->get()->get_instance(),baked_light.is_valid()?get_instance():RID()); -// } + BakeCell *bc = &bake_cells_write[cell]; + + int child = 0; + if (x >= ofs_x + half) { + child|=1; + ofs_x+=half; + } + if (y >= ofs_y + half) { + child|=2; + ofs_y+=half; + } + if (z >= ofs_z + half) { + child|=4; + ofs_z+=half; + } + + cell = bc->childs[child]; + if (cell==CHILD_EMPTY) + return -1; + + half>>=1; } - update_configuration_warning(); + return cell; + } -Ref<BakedLight> BakedLightInstance::get_baked_light() const{ - return baked_light; +int BakedLight::_plot_ray(const Vector3& p_from, const Vector3& p_to) { + + Vector3 from = (p_from - bounds.pos) / bounds.size; + Vector3 to = (p_to - bounds.pos) / bounds.size; + + int x1 = Math::floor(from.x*cells_per_axis); + int y1 = Math::floor(from.y*cells_per_axis); + int z1 = Math::floor(from.z*cells_per_axis); + + int x2 = Math::floor(to.x*cells_per_axis); + int y2 = Math::floor(to.y*cells_per_axis); + int z2 = Math::floor(to.z*cells_per_axis); + + + int i, dx, dy, dz, l, m, n, x_inc, y_inc, z_inc, err_1, err_2, dx2, dy2, dz2; + int point[3]; + + point[0] = x1; + point[1] = y1; + point[2] = z1; + dx = x2 - x1; + dy = y2 - y1; + dz = z2 - z1; + x_inc = (dx < 0) ? -1 : 1; + l = ABS(dx); + y_inc = (dy < 0) ? -1 : 1; + m = ABS(dy); + z_inc = (dz < 0) ? -1 : 1; + n = ABS(dz); + dx2 = l << 1; + dy2 = m << 1; + dz2 = n << 1; + + if ((l >= m) && (l >= n)) { + err_1 = dy2 - l; + err_2 = dz2 - l; + for (i = 0; i < l; i++) { + int cell = _find_cell(point[0],point[1],point[2]); + if (cell>=0) + return cell; + + if (err_1 > 0) { + point[1] += y_inc; + err_1 -= dx2; + } + if (err_2 > 0) { + point[2] += z_inc; + err_2 -= dx2; + } + err_1 += dy2; + err_2 += dz2; + point[0] += x_inc; + } + } else if ((m >= l) && (m >= n)) { + err_1 = dx2 - m; + err_2 = dz2 - m; + for (i = 0; i < m; i++) { + int cell = _find_cell(point[0],point[1],point[2]); + if (cell>=0) + return cell; + if (err_1 > 0) { + point[0] += x_inc; + err_1 -= dy2; + } + if (err_2 > 0) { + point[2] += z_inc; + err_2 -= dy2; + } + err_1 += dx2; + err_2 += dz2; + point[1] += y_inc; + } + } else { + err_1 = dy2 - n; + err_2 = dx2 - n; + for (i = 0; i < n; i++) { + int cell = _find_cell(point[0],point[1],point[2]); + if (cell>=0) + return cell; + + if (err_1 > 0) { + point[1] += y_inc; + err_1 -= dz2; + } + if (err_2 > 0) { + point[0] += x_inc; + err_2 -= dz2; + } + err_1 += dy2; + err_2 += dx2; + point[2] += z_inc; + } + } + return _find_cell(point[0],point[1],point[2]); + } -AABB BakedLightInstance::get_aabb() const { + +void BakedLight::set_cell_subdiv(int p_subdiv) { + + cell_subdiv=p_subdiv; + +// VS::get_singleton()->baked_light_set_subdivision(baked_light,p_subdiv); +} + +int BakedLight::get_cell_subdiv() const { + + return cell_subdiv; +} + + + +AABB BakedLight::get_aabb() const { return AABB(Vector3(0,0,0),Vector3(1,1,1)); } -DVector<Face3> BakedLightInstance::get_faces(uint32_t p_usage_flags) const { +DVector<Face3> BakedLight::get_faces(uint32_t p_usage_flags) const { return DVector<Face3>(); } -String BakedLightInstance::get_configuration_warning() const { - if (get_baked_light().is_null()) { - return TTR("BakedLightInstance does not contain a BakedLight resource."); - } +String BakedLight::get_configuration_warning() const { return String(); } -void BakedLightInstance::_bind_methods() { +void BakedLight::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb,DebugMode p_mode,Ref<MultiMesh> &p_multimesh,int &idx) { + + + if (p_level==cell_subdiv-1) { + + Vector3 center = p_aabb.pos+p_aabb.size*0.5; + Transform xform; + xform.origin=center; + xform.basis.scale(p_aabb.size*0.5); + p_multimesh->set_instance_transform(idx,xform); + Color col; + switch(p_mode) { + case DEBUG_ALBEDO: { + col=Color(bake_cells_write[p_idx].albedo[0],bake_cells_write[p_idx].albedo[1],bake_cells_write[p_idx].albedo[2]); + } break; + case DEBUG_LIGHT: { + col=Color(bake_cells_write[p_idx].light[0],bake_cells_write[p_idx].light[1],bake_cells_write[p_idx].light[2]); + Color colr=Color(bake_cells_write[p_idx].radiance[0],bake_cells_write[p_idx].radiance[1],bake_cells_write[p_idx].radiance[2]); + col.r+=colr.r; + col.g+=colr.g; + col.b+=colr.b; + } break; + + } + p_multimesh->set_instance_color(idx,col); + + + idx++; + + } else { + + for(int i=0;i<8;i++) { + + if (bake_cells_write[p_idx].childs[i]==CHILD_EMPTY) + continue; + + AABB aabb=p_aabb; + aabb.size*=0.5; + + if (i&1) + aabb.pos.x+=aabb.size.x; + if (i&2) + aabb.pos.y+=aabb.size.y; + if (i&4) + aabb.pos.z+=aabb.size.z; + + _debug_mesh(bake_cells_write[p_idx].childs[i],p_level+1,aabb,p_mode,p_multimesh,idx); + } + + } + +} + + +void BakedLight::create_debug_mesh(DebugMode p_mode) { + + Ref<MultiMesh> mm; + mm.instance(); + + mm->set_transform_format(MultiMesh::TRANSFORM_3D); + mm->set_color_format(MultiMesh::COLOR_8BIT); + mm->set_instance_count(bake_cells_level_used[cell_subdiv-1]); + + Ref<Mesh> mesh; + mesh.instance(); + + + + { + Array arr; + arr.resize(Mesh::ARRAY_MAX); + + DVector<Vector3> vertices; + DVector<Color> colors; + + int vtx_idx=0; + #define ADD_VTX(m_idx);\ + vertices.push_back( face_points[m_idx] );\ + colors.push_back( Color(1,1,1,1) );\ + vtx_idx++;\ + + for (int i=0;i<6;i++) { + + + Vector3 face_points[4]; + + for (int j=0;j<4;j++) { + + float v[3]; + v[0]=1.0; + v[1]=1-2*((j>>1)&1); + v[2]=v[1]*(1-2*(j&1)); + + for (int k=0;k<3;k++) { + + if (i<3) + face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1); + else + face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1); + } + } + + //tri 1 + ADD_VTX(0); + ADD_VTX(1); + ADD_VTX(2); + //tri 2 + ADD_VTX(2); + ADD_VTX(3); + ADD_VTX(0); + + } + + + arr[Mesh::ARRAY_VERTEX]=vertices; + arr[Mesh::ARRAY_COLOR]=colors; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES,arr); + } + + { + Ref<FixedSpatialMaterial> fsm; + fsm.instance(); + fsm->set_flag(FixedSpatialMaterial::FLAG_SRGB_VERTEX_COLOR,true); + fsm->set_flag(FixedSpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR,true); + fsm->set_flag(FixedSpatialMaterial::FLAG_UNSHADED,true); + fsm->set_albedo(Color(1,1,1,1)); + + mesh->surface_set_material(0,fsm); + } - ObjectTypeDB::bind_method(_MD("set_baked_light","baked_light"),&BakedLightInstance::set_baked_light); - ObjectTypeDB::bind_method(_MD("get_baked_light"),&BakedLightInstance::get_baked_light); - ObjectTypeDB::bind_method(_MD("get_baked_light_instance"),&BakedLightInstance::get_baked_light_instance); + mm->set_mesh(mesh); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"baked_light",PROPERTY_HINT_RESOURCE_TYPE,"BakedLight"),_SCS("set_baked_light"),_SCS("get_baked_light")); + + bake_cells_write = bake_cells.write(); + + + + int idx=0; + _debug_mesh(0,0,bounds,p_mode,mm,idx); + + print_line("written: "+itos(idx)+" total: "+itos(bake_cells_level_used[cell_subdiv-1])); + + + MultiMeshInstance *mmi = memnew( MultiMeshInstance ); + mmi->set_multimesh(mm); + add_child(mmi); + if (get_tree()->get_edited_scene_root()==this){ + mmi->set_owner(this); + } else { + mmi->set_owner(get_owner()); + + } + +} + +void BakedLight::_debug_mesh_albedo() { + create_debug_mesh(DEBUG_ALBEDO); +} + +void BakedLight::_debug_mesh_light() { + create_debug_mesh(DEBUG_LIGHT); +} + + +void BakedLight::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_cell_subdiv","steps"),&BakedLight::set_cell_subdiv); + ObjectTypeDB::bind_method(_MD("get_cell_subdiv"),&BakedLight::get_cell_subdiv); + + ObjectTypeDB::bind_method(_MD("bake"),&BakedLight::bake); + ObjectTypeDB::set_method_flags(get_type_static(),_SCS("bake"),METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); + + ObjectTypeDB::bind_method(_MD("bake_lights"),&BakedLight::bake_lights); + ObjectTypeDB::set_method_flags(get_type_static(),_SCS("bake_lights"),METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); + + ObjectTypeDB::bind_method(_MD("bake_radiance"),&BakedLight::bake_radiance); + ObjectTypeDB::set_method_flags(get_type_static(),_SCS("bake_radiance"),METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); + + ObjectTypeDB::bind_method(_MD("debug_mesh_albedo"),&BakedLight::_debug_mesh_albedo); + ObjectTypeDB::set_method_flags(get_type_static(),_SCS("debug_mesh_albedo"),METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); + + + ObjectTypeDB::bind_method(_MD("debug_mesh_light"),&BakedLight::_debug_mesh_light); + ObjectTypeDB::set_method_flags(get_type_static(),_SCS("debug_mesh_light"),METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); + + ADD_PROPERTY(PropertyInfo(Variant::INT,"cell_subdiv"),_SCS("set_cell_subdiv"),_SCS("get_cell_subdiv")); ADD_SIGNAL( MethodInfo("baked_light_changed")); + } -BakedLightInstance::BakedLightInstance() { +BakedLight::BakedLight() { +// baked_light=VisualServer::get_singleton()->baked_light_create(); + VS::get_singleton()->instance_set_base(get_instance(),baked_light); + cell_subdiv=8; + bake_texture_size=128; + color_scan_cell_width=8; + light_pass=0; } -///////////////////////// +BakedLight::~BakedLight() { + + VS::get_singleton()->free(baked_light); +} + +///////////////////////// + +#if 0 void BakedLightSampler::set_param(Param p_param,float p_value) { ERR_FAIL_INDEX(p_param,PARAM_MAX); params[p_param]=p_value; diff --git a/scene/3d/baked_light_instance.h b/scene/3d/baked_light_instance.h index 15f04fea31..314e5f1450 100644 --- a/scene/3d/baked_light_instance.h +++ b/scene/3d/baked_light_instance.h @@ -31,38 +31,142 @@ #include "scene/3d/visual_instance.h" #include "scene/resources/baked_light.h" +#include "scene/3d/multimesh_instance.h" + -#if 0 class BakedLightBaker; +class Light; + +class BakedLight : public VisualInstance { + OBJ_TYPE(BakedLight,VisualInstance); + +public: + enum DebugMode { + DEBUG_ALBEDO, + DEBUG_LIGHT + }; + +private: + RID baked_light; + int cell_subdiv; + AABB bounds; + int cells_per_axis; + + enum { + CHILD_EMPTY=0xFFFFFFFF, + }; + + + /* BAKE DATA */ + + struct BakeCell { + + uint32_t childs[8]; + float albedo[3]; //albedo in RGB24 + float light[3]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast) + float radiance[3]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast) + uint32_t used_sides; + float alpha; //used for upsampling + uint32_t light_pass; //used for baking light + + BakeCell() { + for(int i=0;i<8;i++) { + childs[i]=0xFFFFFFFF; + } + + for(int i=0;i<3;i++) { + light[i]=0; + albedo[i]=0; + radiance[i]=0; + } + alpha=0; + light_pass=0; + used_sides=0; + } + }; + + + int bake_texture_size; + int color_scan_cell_width; + + struct MaterialCache { + //128x128 textures + Vector<Color> albedo; + Vector<Color> emission; + }; + + Vector<Color> _get_bake_texture(Image &p_image, const Color &p_color); -class BakedLightInstance : public VisualInstance { - OBJ_TYPE(BakedLightInstance,VisualInstance); - Ref<BakedLight> baked_light; + Map<Ref<Material>,MaterialCache> material_cache; + MaterialCache _get_material_cache(Ref<Material> p_material); + int bake_cells_alloc; + int bake_cells_used; + int zero_alphas; + Vector<int> bake_cells_level_used; + DVector<BakeCell> bake_cells; + DVector<BakeCell>::Write bake_cells_write; + + + + void _plot_face(int p_idx,int p_level,const Vector3 *p_vtx,const Vector2* p_uv, const MaterialCache& p_material,const AABB& p_aabb); + void _fixup_plot(int p_idx, int p_level, int p_x, int p_y, int p_z); + void _bake_add_mesh(const Transform& p_xform,Ref<Mesh>& p_mesh); + void _bake_add_to_aabb(const Transform& p_xform,Ref<Mesh>& p_mesh,bool &first); + + void _debug_mesh(int p_idx, int p_level, const AABB &p_aabb,DebugMode p_mode,Ref<MultiMesh> &p_multimesh,int &idx); + void _debug_mesh_albedo(); + void _debug_mesh_light(); + + + _FORCE_INLINE_ int _find_cell(int x,int y, int z); + int _plot_ray(const Vector3& p_from, const Vector3& p_to); + + uint32_t light_pass; + + + void _bake_directional(int p_idx, int p_level, int p_x,int p_y,int p_z,const Vector3& p_dir,const Color& p_color,int p_sign); + void _upscale_light(int p_idx,int p_level); + void _bake_light(Light* p_light); + + Color _cone_trace(const Vector3& p_from, const Vector3& p_dir, float p_half_angle); + void _bake_radiance(int p_idx, int p_level, int p_x,int p_y,int p_z); + +friend class GeometryInstance; + + Set<GeometryInstance*> geometries; +friend class Light; + + Set<Light*> lights; protected: static void _bind_methods(); public: + void set_cell_subdiv(int p_subdiv); + int get_cell_subdiv() const; + + void bake(); + void bake_lights(); + void bake_radiance(); - RID get_baked_light_instance() const; - void set_baked_light(const Ref<BakedLight>& baked_light); - Ref<BakedLight> get_baked_light() const; + void create_debug_mesh(DebugMode p_mode); virtual AABB get_aabb() const; virtual DVector<Face3> get_faces(uint32_t p_usage_flags) const; String get_configuration_warning() const; - BakedLightInstance(); + BakedLight(); + ~BakedLight(); }; - +#if 0 class BakedLightSampler : public VisualInstance { OBJ_TYPE(BakedLightSampler,VisualInstance); diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp new file mode 100644 index 0000000000..ce360859d6 --- /dev/null +++ b/scene/3d/gi_probe.cpp @@ -0,0 +1,1248 @@ +#include "gi_probe.h" +#include "mesh_instance.h" + +enum DataFormat { + DATA_RGBA8, + DATA_DXT5, + DATA_ETC2_EAC, +}; + + +void GIProbeData::set_bounds(const AABB& p_bounds) { + + VS::get_singleton()->gi_probe_set_bounds(probe,p_bounds); +} + +AABB GIProbeData::get_bounds() const{ + + return VS::get_singleton()->gi_probe_get_bounds(probe); +} + +void GIProbeData::set_cell_size(float p_size) { + + VS::get_singleton()->gi_probe_set_cell_size(probe,p_size); + +} + +float GIProbeData::get_cell_size() const { + + return VS::get_singleton()->gi_probe_get_cell_size(probe); + +} + +void GIProbeData::set_to_cell_xform(const Transform& p_xform) { + + VS::get_singleton()->gi_probe_set_to_cell_xform(probe,p_xform); + +} + +Transform GIProbeData::get_to_cell_xform() const { + + return VS::get_singleton()->gi_probe_get_to_cell_xform(probe); + +} + + +void GIProbeData::set_dynamic_data(const DVector<int>& p_data){ + + VS::get_singleton()->gi_probe_set_dynamic_data(probe,p_data); + +} +DVector<int> GIProbeData::get_dynamic_data() const{ + + return VS::get_singleton()->gi_probe_get_dynamic_data(probe); +} + +void GIProbeData::set_dynamic_range(float p_range){ + + VS::get_singleton()->gi_probe_set_dynamic_range(probe,p_range); + +} +float GIProbeData::get_dynamic_range() const{ + + + return VS::get_singleton()->gi_probe_get_dynamic_range(probe); +} + +void GIProbeData::set_static_data(const DVector<uint8_t>& p_data,DataFormat p_format,int p_width,int p_height,int p_depth){ + + VS::get_singleton()->gi_probe_set_static_data(probe,p_data,VS::GIProbeDataFormat(p_format),p_width,p_height,p_depth); + +} +DVector<uint8_t> GIProbeData::get_static_data() const{ + + return VS::get_singleton()->gi_probe_get_static_data(probe); + +} +GIProbeData::DataFormat GIProbeData::get_static_data_format() const{ + + return GIProbeData::DataFormat(VS::get_singleton()->gi_probe_get_static_data_format(probe)); + +} +int GIProbeData::get_static_data_width() const{ + + return VS::get_singleton()->gi_probe_get_static_data_width(probe); + +} +int GIProbeData::get_static_data_height() const{ + + return VS::get_singleton()->gi_probe_get_static_data_height(probe); + +} +int GIProbeData::get_static_data_depth() const{ + + return VS::get_singleton()->gi_probe_get_static_data_depth(probe); + +} + +RID GIProbeData::get_rid() const { + + return probe; +} + +GIProbeData::GIProbeData() { + + probe=VS::get_singleton()->gi_probe_create(); +} + +GIProbeData::~GIProbeData() { + + VS::get_singleton()->free(probe); +} + + +////////////////////// +////////////////////// + + +void GIProbe::set_probe_data(const Ref<GIProbeData>& p_data) { + + if (p_data.is_valid()) { + VS::get_singleton()->instance_set_base(get_instance(),p_data->get_rid()); + } else { + VS::get_singleton()->instance_set_base(get_instance(),RID()); + } + + probe_data=p_data; +} + +Ref<GIProbeData> GIProbe::get_probe_data() const { + + return probe_data; +} + +void GIProbe::set_subdiv(Subdiv p_subdiv) { + + ERR_FAIL_INDEX(p_subdiv,SUBDIV_MAX); + subdiv=p_subdiv; + update_gizmo(); +} + +GIProbe::Subdiv GIProbe::get_subdiv() const { + + return subdiv; +} + +void GIProbe::set_extents(const Vector3& p_extents) { + + extents=p_extents; + update_gizmo(); +} + +Vector3 GIProbe::get_extents() const { + + return extents; +} + +void GIProbe::set_dynamic_range(float p_dynamic_range) { + + dynamic_range=p_dynamic_range; +} +float GIProbe::get_dynamic_range() const { + + return dynamic_range; +} + +#include "math.h" + +#define FINDMINMAX(x0,x1,x2,min,max) \ + min = max = x0; \ + if(x1<min) min=x1;\ + if(x1>max) max=x1;\ + if(x2<min) min=x2;\ + if(x2>max) max=x2; + +static bool planeBoxOverlap(Vector3 normal,float d, Vector3 maxbox) +{ + int q; + Vector3 vmin,vmax; + for(q=0;q<=2;q++) + { + if(normal[q]>0.0f) + { + vmin[q]=-maxbox[q]; + vmax[q]=maxbox[q]; + } + else + { + vmin[q]=maxbox[q]; + vmax[q]=-maxbox[q]; + } + } + if(normal.dot(vmin)+d>0.0f) return false; + if(normal.dot(vmax)+d>=0.0f) return true; + + return false; +} + + +/*======================== X-tests ========================*/ +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p2 = a*v2.y - b*v2.z; \ + if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \ + rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \ + if(min>rad || max<-rad) return false; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p1 = a*v1.y - b*v1.z; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \ + if(min>rad || max<-rad) return false; + +/*======================== Y-tests ========================*/ +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p2 = -a*v2.x + b*v2.z; \ + if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \ + if(min>rad || max<-rad) return false; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p1 = -a*v1.x + b*v1.z; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \ + if(min>rad || max<-rad) return false; + +/*======================== Z-tests ========================*/ + +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a*v1.x - b*v1.y; \ + p2 = a*v2.x - b*v2.y; \ + if(p2<p1) {min=p2; max=p1;} else {min=p1; max=p2;} \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \ + if(min>rad || max<-rad) return false; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a*v0.x - b*v0.y; \ + p1 = a*v1.x - b*v1.y; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \ + if(min>rad || max<-rad) return false; + +static bool fast_tri_box_overlap(const Vector3& boxcenter,const Vector3 boxhalfsize,const Vector3 *triverts) { + + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + Vector3 v0,v1,v2; + float min,max,d,p0,p1,p2,rad,fex,fey,fez; + Vector3 normal,e0,e1,e2; + + /* This is the fastest branch on Sun */ + /* move everything so that the boxcenter is in (0,0,0) */ + + v0=triverts[0]-boxcenter; + v1=triverts[1]-boxcenter; + v2=triverts[2]-boxcenter; + + /* compute triangle edges */ + e0=v1-v0; /* tri edge 0 */ + e1=v2-v1; /* tri edge 1 */ + e2=v0-v2; /* tri edge 2 */ + + /* Bullet 3: */ + /* test the 9 tests first (this was faster) */ + fex = Math::abs(e0.x); + fey = Math::abs(e0.y); + fez = Math::abs(e0.z); + AXISTEST_X01(e0.z, e0.y, fez, fey); + AXISTEST_Y02(e0.z, e0.x, fez, fex); + AXISTEST_Z12(e0.y, e0.x, fey, fex); + + fex = Math::abs(e1.x); + fey = Math::abs(e1.y); + fez = Math::abs(e1.z); + AXISTEST_X01(e1.z, e1.y, fez, fey); + AXISTEST_Y02(e1.z, e1.x, fez, fex); + AXISTEST_Z0(e1.y, e1.x, fey, fex); + + fex = Math::abs(e2.x); + fey = Math::abs(e2.y); + fez = Math::abs(e2.z); + AXISTEST_X2(e2.z, e2.y, fez, fey); + AXISTEST_Y1(e2.z, e2.x, fez, fex); + AXISTEST_Z12(e2.y, e2.x, fey, fex); + + /* Bullet 1: */ + /* first test overlap in the {x,y,z}-directions */ + /* find min, max of the triangle each direction, and test for overlap in */ + /* that direction -- this is equivalent to testing a minimal AABB around */ + /* the triangle against the AABB */ + + /* test in X-direction */ + FINDMINMAX(v0.x,v1.x,v2.x,min,max); + if(min>boxhalfsize.x || max<-boxhalfsize.x) return false; + + /* test in Y-direction */ + FINDMINMAX(v0.y,v1.y,v2.y,min,max); + if(min>boxhalfsize.y || max<-boxhalfsize.y) return false; + + /* test in Z-direction */ + FINDMINMAX(v0.z,v1.z,v2.z,min,max); + if(min>boxhalfsize.z || max<-boxhalfsize.z) return false; + + /* Bullet 2: */ + /* test if the box intersects the plane of the triangle */ + /* compute plane equation of triangle: normal*x+d=0 */ + normal=e0.cross(e1); + d=-normal.dot(v0); /* plane eq: normal.x+d=0 */ + if(!planeBoxOverlap(normal,d,boxhalfsize)) return false; + + return true; /* box and triangle overlaps */ +} + + + +static _FORCE_INLINE_ Vector2 get_uv(const Vector3& p_pos, const Vector3 *p_vtx, const Vector2* p_uv) { + + if (p_pos.distance_squared_to(p_vtx[0])<CMP_EPSILON2) + return p_uv[0]; + if (p_pos.distance_squared_to(p_vtx[1])<CMP_EPSILON2) + return p_uv[1]; + if (p_pos.distance_squared_to(p_vtx[2])<CMP_EPSILON2) + return p_uv[2]; + + Vector3 v0 = p_vtx[1] - p_vtx[0]; + Vector3 v1 = p_vtx[2] - p_vtx[0]; + Vector3 v2 = p_pos - p_vtx[0]; + + float d00 = v0.dot( v0); + float d01 = v0.dot( v1); + float d11 = v1.dot( v1); + float d20 = v2.dot( v0); + float d21 = v2.dot( v1); + float denom = (d00 * d11 - d01 * d01); + if (denom==0) + return p_uv[0]; + float v = (d11 * d20 - d01 * d21) / denom; + float w = (d00 * d21 - d01 * d20) / denom; + float u = 1.0f - v - w; + + return p_uv[0]*u + p_uv[1]*v + p_uv[2]*w; +} + +void GIProbe::_plot_face(int p_idx, int p_level,int p_x,int p_y,int p_z, const Vector3 *p_vtx, const Vector2* p_uv, const Baker::MaterialCache& p_material, const AABB &p_aabb,Baker *p_baker) { + + + + if (p_level==p_baker->cell_subdiv-1) { + //plot the face by guessing it's albedo and emission value + + //find best axis to map to, for scanning values + int closest_axis; + float closest_dot; + + Vector3 normal = Plane(p_vtx[0],p_vtx[1],p_vtx[2]).normal; + + for(int i=0;i<3;i++) { + + Vector3 axis; + axis[i]=1.0; + float dot=ABS(normal.dot(axis)); + if (i==0 || dot>closest_dot) { + closest_axis=i; + closest_dot=dot; + } + } + + Vector3 axis; + axis[closest_axis]=1.0; + Vector3 t1; + t1[(closest_axis+1)%3]=1.0; + Vector3 t2; + t2[(closest_axis+2)%3]=1.0; + + t1*=p_aabb.size[(closest_axis+1)%3]/float(color_scan_cell_width); + t2*=p_aabb.size[(closest_axis+2)%3]/float(color_scan_cell_width); + + Color albedo_accum; + Color emission_accum; + float alpha=0.0; + + //map to a grid average in the best axis for this face + for(int i=0;i<color_scan_cell_width;i++) { + + Vector3 ofs_i=float(i)*t1; + + for(int j=0;j<color_scan_cell_width;j++) { + + Vector3 ofs_j=float(j)*t2; + + Vector3 from = p_aabb.pos+ofs_i+ofs_j; + Vector3 to = from + t1 + t2 + axis * p_aabb.size[closest_axis]; + Vector3 half = (to-from)*0.5; + + //is in this cell? + if (!fast_tri_box_overlap(from+half,half,p_vtx)) { + continue; //face does not span this cell + } + + //go from -size to +size*2 to avoid skipping collisions + Vector3 ray_from = from + (t1+t2)*0.5 - axis * p_aabb.size[closest_axis]; + Vector3 ray_to = ray_from + axis * p_aabb.size[closest_axis]*2; + + Vector3 intersection; + + if (!Geometry::ray_intersects_triangle(ray_from,ray_to,p_vtx[0],p_vtx[1],p_vtx[2],&intersection)) { + //no intersect? look in edges + + float closest_dist=1e20; + for(int j=0;j<3;j++) { + Vector3 c; + Vector3 inters; + Geometry::get_closest_points_between_segments(p_vtx[j],p_vtx[(j+1)%3],ray_from,ray_to,inters,c); + float d=c.distance_to(intersection); + if (j==0 || d<closest_dist) { + closest_dist=d; + intersection=inters; + } + } + } + + Vector2 uv = get_uv(intersection,p_vtx,p_uv); + + + int uv_x = CLAMP(Math::fposmod(uv.x,1.0)*bake_texture_size,0,bake_texture_size-1); + int uv_y = CLAMP(Math::fposmod(uv.y,1.0)*bake_texture_size,0,bake_texture_size-1); + + int ofs = uv_y*bake_texture_size+uv_x; + albedo_accum.r+=p_material.albedo[ofs].r; + albedo_accum.g+=p_material.albedo[ofs].g; + albedo_accum.b+=p_material.albedo[ofs].b; + albedo_accum.a+=p_material.albedo[ofs].a; + + emission_accum.r+=p_material.emission[ofs].r; + emission_accum.g+=p_material.emission[ofs].g; + emission_accum.b+=p_material.emission[ofs].b; + alpha+=1.0; + + } + } + + + if (alpha==0) { + //could not in any way get texture information.. so use closest point to center + + Face3 f( p_vtx[0],p_vtx[1],p_vtx[2]); + Vector3 inters = f.get_closest_point_to(p_aabb.pos+p_aabb.size*0.5); + + Vector2 uv = get_uv(inters,p_vtx,p_uv); + + int uv_x = CLAMP(Math::fposmod(uv.x,1.0)*bake_texture_size,0,bake_texture_size-1); + int uv_y = CLAMP(Math::fposmod(uv.y,1.0)*bake_texture_size,0,bake_texture_size-1); + + int ofs = uv_y*bake_texture_size+uv_x; + + alpha = 1.0/(color_scan_cell_width*color_scan_cell_width); + + albedo_accum.r=p_material.albedo[ofs].r*alpha; + albedo_accum.g=p_material.albedo[ofs].g*alpha; + albedo_accum.b=p_material.albedo[ofs].b*alpha; + albedo_accum.a=p_material.albedo[ofs].a*alpha; + + emission_accum.r=p_material.emission[ofs].r*alpha; + emission_accum.g=p_material.emission[ofs].g*alpha; + emission_accum.b=p_material.emission[ofs].b*alpha; + + + + } else { + + float accdiv = 1.0/(color_scan_cell_width*color_scan_cell_width); + alpha*=accdiv; + + albedo_accum.r*=accdiv; + albedo_accum.g*=accdiv; + albedo_accum.b*=accdiv; + albedo_accum.a*=accdiv; + + emission_accum.r*=accdiv; + emission_accum.g*=accdiv; + emission_accum.b*=accdiv; + } + + //put this temporarily here, corrected in a later step + p_baker->bake_cells[p_idx].albedo[0]+=albedo_accum.r; + p_baker->bake_cells[p_idx].albedo[1]+=albedo_accum.g; + p_baker->bake_cells[p_idx].albedo[2]+=albedo_accum.b; + p_baker->bake_cells[p_idx].emission[0]+=emission_accum.r; + p_baker->bake_cells[p_idx].emission[1]+=emission_accum.g; + p_baker->bake_cells[p_idx].emission[2]+=emission_accum.b; + p_baker->bake_cells[p_idx].alpha+=alpha; + + static const Vector3 side_normals[6]={ + Vector3(-1, 0, 0), + Vector3( 1, 0, 0), + Vector3( 0,-1, 0), + Vector3( 0, 1, 0), + Vector3( 0, 0,-1), + Vector3( 0, 0, 1), + }; + + for(int i=0;i<6;i++) { + if (normal.dot(side_normals[i])>CMP_EPSILON) { + p_baker->bake_cells[p_idx].used_sides|=(1<<i); + } + } + + + } else { + //go down + + int half = (1<<(p_baker->cell_subdiv-1)) >> (p_level+1); + for(int i=0;i<8;i++) { + + AABB aabb=p_aabb; + aabb.size*=0.5; + + int nx=p_x; + int ny=p_y; + int nz=p_z; + + if (i&1) { + aabb.pos.x+=aabb.size.x; + nx+=half; + } + if (i&2) { + aabb.pos.y+=aabb.size.y; + ny+=half; + } + if (i&4) { + aabb.pos.z+=aabb.size.z; + nz+=half; + } + //make sure to not plot beyond limits + if (nx<0 || nx>=p_baker->axis_cell_size[0] || ny<0 || ny>=p_baker->axis_cell_size[1] || nz<0 || nz>=p_baker->axis_cell_size[2]) + continue; + + { + AABB test_aabb=aabb; + //test_aabb.grow_by(test_aabb.get_longest_axis_size()*0.05); //grow a bit to avoid numerical error in real-time + Vector3 qsize = test_aabb.size*0.5; //quarter size, for fast aabb test + + if (!fast_tri_box_overlap(test_aabb.pos+qsize,qsize,p_vtx)) { + //if (!Face3(p_vtx[0],p_vtx[1],p_vtx[2]).intersects_aabb2(aabb)) { + //does not fit in child, go on + continue; + } + + } + + if (p_baker->bake_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(); + p_baker->bake_cells.resize( p_baker->bake_cells.size() + 1); + + } + + + _plot_face(p_baker->bake_cells[p_idx].childs[i],p_level+1,nx,ny,nz,p_vtx,p_uv,p_material,aabb,p_baker); + } + } +} + + + +void GIProbe::_fixup_plot(int p_idx, int p_level,int p_x,int p_y, int p_z,Baker *p_baker) { + + + + if (p_level==p_baker->cell_subdiv-1) { + + p_baker->leaf_voxel_count++; + float alpha = p_baker->bake_cells[p_idx].alpha; + + p_baker->bake_cells[p_idx].albedo[0]/=alpha; + p_baker->bake_cells[p_idx].albedo[1]/=alpha; + p_baker->bake_cells[p_idx].albedo[2]/=alpha; + + //transfer emission to light + p_baker->bake_cells[p_idx].emission[0]/=alpha; + p_baker->bake_cells[p_idx].emission[1]/=alpha; + p_baker->bake_cells[p_idx].emission[2]/=alpha; + + p_baker->bake_cells[p_idx].alpha=1.0; + + //remove neighbours from used sides + + for(int n=0;n<6;n++) { + + int ofs[3]={0,0,0}; + + ofs[n/2]=(n&1)?1:-1; + + //convert to x,y,z on this level + int x=p_x; + int y=p_y; + int z=p_z; + + x+=ofs[0]; + y+=ofs[1]; + z+=ofs[2]; + + int ofs_x=0; + int ofs_y=0; + int ofs_z=0; + int size = 1<<p_level; + int half=size/2; + + + if (x<0 || x>=size || y<0 || y>=size || z<0 || z>=size) { + //neighbour is out, can't use it + p_baker->bake_cells[p_idx].used_sides&=~(1<<uint32_t(n)); + continue; + } + + uint32_t neighbour=0; + + for(int i=0;i<p_baker->cell_subdiv-1;i++) { + + Baker::Cell *bc = &p_baker->bake_cells[neighbour]; + + int child = 0; + if (x >= ofs_x + half) { + child|=1; + ofs_x+=half; + } + if (y >= ofs_y + half) { + child|=2; + ofs_y+=half; + } + if (z >= ofs_z + half) { + child|=4; + ofs_z+=half; + } + + neighbour = bc->childs[child]; + if (neighbour==Baker::CHILD_EMPTY) { + break; + } + + half>>=1; + } + + if (neighbour!=Baker::CHILD_EMPTY) { + p_baker->bake_cells[p_idx].used_sides&=~(1<<uint32_t(n)); + } + } + } else { + + + //go down + + float alpha_average=0; + int half = (1<<(p_baker->cell_subdiv-1)) >> (p_level+1); + for(int i=0;i<8;i++) { + + uint32_t child = p_baker->bake_cells[p_idx].childs[i]; + + if (child==Baker::CHILD_EMPTY) + continue; + + + int nx=p_x; + int ny=p_y; + int nz=p_z; + + if (i&1) + nx+=half; + if (i&2) + ny+=half; + if (i&4) + nz+=half; + + _fixup_plot(child,p_level+1,nx,ny,nz,p_baker); + alpha_average+=p_baker->bake_cells[child].alpha; + } + + p_baker->bake_cells[p_idx].alpha=alpha_average/8.0; + p_baker->bake_cells[p_idx].emission[0]=0; + p_baker->bake_cells[p_idx].emission[1]=0; + p_baker->bake_cells[p_idx].emission[2]=0; + p_baker->bake_cells[p_idx].albedo[0]=0; + p_baker->bake_cells[p_idx].albedo[1]=0; + p_baker->bake_cells[p_idx].albedo[2]=0; + + } + +} + + + +Vector<Color> GIProbe::_get_bake_texture(Image &p_image,const Color& p_color) { + + Vector<Color> ret; + + if (p_image.empty()) { + + ret.resize(bake_texture_size*bake_texture_size); + for(int i=0;i<bake_texture_size*bake_texture_size;i++) { + ret[i]=p_color; + } + + return ret; + } + + p_image.convert(Image::FORMAT_RGBA8); + p_image.resize(bake_texture_size,bake_texture_size,Image::INTERPOLATE_CUBIC); + + + DVector<uint8_t>::Read r = p_image.get_data().read(); + ret.resize(bake_texture_size*bake_texture_size); + + for(int i=0;i<bake_texture_size*bake_texture_size;i++) { + Color c; + c.r = r[i*4+0]/255.0; + c.g = r[i*4+1]/255.0; + c.b = r[i*4+2]/255.0; + c.a = r[i*4+3]/255.0; + ret[i]=c; + + } + + return ret; +} + + +GIProbe::Baker::MaterialCache GIProbe::_get_material_cache(Ref<Material> p_material,Baker *p_baker) { + + //this way of obtaining materials is inaccurate and also does not support some compressed formats very well + Ref<FixedSpatialMaterial> mat = p_material; + + Ref<Material> material = mat; //hack for now + + if (p_baker->material_cache.has(material)) { + return p_baker->material_cache[material]; + } + + Baker::MaterialCache mc; + + if (mat.is_valid()) { + + + Ref<ImageTexture> albedo_tex = mat->get_texture(FixedSpatialMaterial::TEXTURE_ALBEDO); + + Image img_albedo; + if (albedo_tex.is_valid()) { + + img_albedo = albedo_tex->get_data(); + } + + mc.albedo=_get_bake_texture(img_albedo,mat->get_albedo()); + + Ref<ImageTexture> emission_tex = mat->get_texture(FixedSpatialMaterial::TEXTURE_EMISSION); + + Color emission_col = mat->get_emission(); + emission_col.r*=mat->get_emission_energy(); + emission_col.g*=mat->get_emission_energy(); + emission_col.b*=mat->get_emission_energy(); + + Image img_emission; + + if (emission_tex.is_valid()) { + + img_emission = emission_tex->get_data(); + } + + mc.emission=_get_bake_texture(img_emission,emission_col); + + } else { + Image empty; + + mc.albedo=_get_bake_texture(empty,Color(0.7,0.7,0.7)); + mc.emission=_get_bake_texture(empty,Color(0,0,0)); + + + } + + p_baker->material_cache[p_material]=mc; + return mc; + + +} + +void GIProbe::_plot_mesh(const Transform& p_xform, Ref<Mesh>& p_mesh, Baker *p_baker) { + + + for(int i=0;i<p_mesh->get_surface_count();i++) { + + if (p_mesh->surface_get_primitive_type(i)!=Mesh::PRIMITIVE_TRIANGLES) + continue; //only triangles + + Baker::MaterialCache material = _get_material_cache(p_mesh->surface_get_material(i),p_baker); + + Array a = p_mesh->surface_get_arrays(i); + + + DVector<Vector3> vertices = a[Mesh::ARRAY_VERTEX]; + DVector<Vector3>::Read vr=vertices.read(); + DVector<Vector2> uv = a[Mesh::ARRAY_TEX_UV]; + DVector<Vector2>::Read uvr; + DVector<int> index = a[Mesh::ARRAY_INDEX]; + + bool read_uv=false; + + if (uv.size()) { + + uvr=uv.read(); + read_uv=true; + } + + if (index.size()) { + + int facecount = index.size()/3; + DVector<int>::Read ir=index.read(); + + for(int j=0;j<facecount;j++) { + + Vector3 vtxs[3]; + Vector2 uvs[3]; + + for(int k=0;k<3;k++) { + vtxs[k]=p_xform.xform(vr[ir[j*3+k]]); + } + + if (read_uv) { + for(int k=0;k<3;k++) { + uvs[k]=uvr[ir[j*3+k]]; + } + } + + //test against original bounds + if (!fast_tri_box_overlap(-extents,extents*2,vtxs)) + continue; + //plot + _plot_face(0,0,0,0,0,vtxs,uvs,material,p_baker->po2_bounds,p_baker); + } + + + + } else { + + int facecount = vertices.size()/3; + + for(int j=0;j<facecount;j++) { + + Vector3 vtxs[3]; + Vector2 uvs[3]; + + for(int k=0;k<3;k++) { + vtxs[k]=p_xform.xform(vr[j*3+k]); + } + + if (read_uv) { + for(int k=0;k<3;k++) { + uvs[k]=uvr[j*3+k]; + } + } + + //test against original bounds + if (!fast_tri_box_overlap(-extents,extents*2,vtxs)) + continue; + //plot face + _plot_face(0,0,0,0,0,vtxs,uvs,material,p_baker->po2_bounds,p_baker); + } + + } + } +} + + + +void GIProbe::_find_meshes(Node *p_at_node,Baker *p_baker){ + + MeshInstance *mi = p_at_node->cast_to<MeshInstance>(); + if (mi && mi->get_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT)) { + Ref<Mesh> mesh = mi->get_mesh(); + if (mesh.is_valid()) { + + AABB aabb = mesh->get_aabb(); + + Transform xf = get_global_transform().affine_inverse() * mi->get_global_transform(); + + if (AABB(-extents,extents*2).intersects(xf.xform(aabb))) { + Baker::PlotMesh pm; + pm.local_xform=xf; + pm.mesh=mesh; + p_baker->mesh_list.push_back(pm); + + } + } + } + + for(int i=0;i<p_at_node->get_child_count();i++) { + + Node *child = p_at_node->get_child(i); + if (!child->get_owner()) + continue; //maybe a helper + + _find_meshes(child,p_baker); + + } +} + + + + +void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug){ + + Baker baker; + + static const int subdiv_value[SUBDIV_MAX]={7,8,9,10}; + + baker.cell_subdiv=subdiv_value[subdiv]; + baker.bake_cells.resize(1); + + //find out the actual real bounds, power of 2, which gets the highest subdivision + baker.po2_bounds=AABB(-extents,extents*2.0); + int longest_axis = baker.po2_bounds.get_longest_axis_index(); + baker.axis_cell_size[longest_axis]=(1<<(baker.cell_subdiv-1)); + baker.leaf_voxel_count=0; + + for(int i=0;i<3;i++) { + + if (i==longest_axis) + continue; + + baker.axis_cell_size[i]=baker.axis_cell_size[longest_axis]; + float axis_size = baker.po2_bounds.size[longest_axis]; + + //shrink until fit subdiv + while (axis_size/2.0 >= baker.po2_bounds.size[i]) { + axis_size/=2.0; + baker.axis_cell_size[i]>>=1; + } + + baker.po2_bounds.size[i]=baker.po2_bounds.size[longest_axis]; + } + + + + Transform to_bounds; + to_bounds.basis.scale(Vector3(baker.po2_bounds.size[longest_axis],baker.po2_bounds.size[longest_axis],baker.po2_bounds.size[longest_axis])); + to_bounds.origin=baker.po2_bounds.pos; + + Transform to_grid; + to_grid.basis.scale(Vector3(baker.axis_cell_size[longest_axis],baker.axis_cell_size[longest_axis],baker.axis_cell_size[longest_axis])); + + baker.to_cell_space = to_grid * to_bounds.affine_inverse(); + + + _find_meshes(p_from_node?p_from_node:get_parent(),&baker); + + + + int pmc=0; + + for(List<Baker::PlotMesh>::Element *E=baker.mesh_list.front();E;E=E->next()) { + + print_line("plotting mesh "+itos(pmc++)+"/"+itos(baker.mesh_list.size())); + + _plot_mesh(E->get().local_xform,E->get().mesh,&baker); + } + + _fixup_plot(0,0,0,0,0,&baker); + + //create the data for visual server + + DVector<int> data; + + data.resize( 16+(8+1+1+1+1)*baker.bake_cells.size() ); //4 for header, rest for rest. + + { + DVector<int>::Write w = data.write(); + + uint32_t * w32 = (uint32_t*)w.ptr(); + + w32[0]=0;//version + w32[1]=baker.cell_subdiv; //subdiv + w32[2]=baker.axis_cell_size[0]; + w32[3]=baker.axis_cell_size[1]; + w32[4]=baker.axis_cell_size[2]; + w32[5]=baker.bake_cells.size(); + w32[6]=baker.leaf_voxel_count; + + int ofs=16; + + for(int i=0;i<baker.bake_cells.size();i++) { + + for(int j=0;j<8;j++) { + w32[ofs++]=baker.bake_cells[i].childs[j]; + } + + { //albedo + uint32_t rgba=uint32_t(CLAMP(baker.bake_cells[i].albedo[0]*255.0,0,255))<<16; + rgba|=uint32_t(CLAMP(baker.bake_cells[i].albedo[1]*255.0,0,255))<<8; + rgba|=uint32_t(CLAMP(baker.bake_cells[i].albedo[2]*255.0,0,255))<<0; + + w32[ofs++]=rgba; + + + } + { //emission + + Vector3 e(baker.bake_cells[i].emission[0],baker.bake_cells[i].emission[1],baker.bake_cells[i].emission[2]); + float l = e.length(); + if (l>0) { + e.normalize(); + l=CLAMP(l/8.0,0,1.0); + } + + uint32_t em=uint32_t(CLAMP(e[0]*255,0,255))<<24; + em|=uint32_t(CLAMP(e[1]*255,0,255))<<16; + em|=uint32_t(CLAMP(e[2]*255,0,255))<<8; + em|=uint32_t(CLAMP(l*255,0,255)); + + w32[ofs++]=em; + } + + w32[ofs++]=baker.bake_cells[i].used_sides; + w32[ofs++]=uint32_t(baker.bake_cells[i].alpha*65535.0); + + } + + } + + Ref<GIProbeData> probe_data; + probe_data.instance(); + probe_data->set_bounds(AABB(-extents,extents*2.0)); + probe_data->set_cell_size(baker.po2_bounds.size[longest_axis]/baker.axis_cell_size[longest_axis]); + probe_data->set_dynamic_data(data); + probe_data->set_to_cell_xform(baker.to_cell_space); + + set_probe_data(probe_data); + + + if (p_create_visual_debug) { + // _create_debug_mesh(&baker); + } + + + +} + + +void GIProbe::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb,Ref<MultiMesh> &p_multimesh,int &idx,Baker *p_baker) { + + + if (p_level==p_baker->cell_subdiv-1) { + + Vector3 center = p_aabb.pos+p_aabb.size*0.5; + Transform xform; + xform.origin=center; + xform.basis.scale(p_aabb.size*0.5); + p_multimesh->set_instance_transform(idx,xform); + Color col=Color(p_baker->bake_cells[p_idx].albedo[0],p_baker->bake_cells[p_idx].albedo[1],p_baker->bake_cells[p_idx].albedo[2]); + p_multimesh->set_instance_color(idx,col); + + idx++; + + } else { + + for(int i=0;i<8;i++) { + + if (p_baker->bake_cells[p_idx].childs[i]==Baker::CHILD_EMPTY) + continue; + + AABB aabb=p_aabb; + aabb.size*=0.5; + + if (i&1) + aabb.pos.x+=aabb.size.x; + if (i&2) + aabb.pos.y+=aabb.size.y; + if (i&4) + aabb.pos.z+=aabb.size.z; + + _debug_mesh(p_baker->bake_cells[p_idx].childs[i],p_level+1,aabb,p_multimesh,idx,p_baker); + } + + } + +} + + +void GIProbe::_create_debug_mesh(Baker *p_baker) { + + Ref<MultiMesh> mm; + mm.instance(); + + mm->set_transform_format(MultiMesh::TRANSFORM_3D); + mm->set_color_format(MultiMesh::COLOR_8BIT); + print_line("leaf voxels: "+itos(p_baker->leaf_voxel_count)); + mm->set_instance_count(p_baker->leaf_voxel_count); + + Ref<Mesh> mesh; + mesh.instance(); + + { + Array arr; + arr.resize(Mesh::ARRAY_MAX); + + DVector<Vector3> vertices; + DVector<Color> colors; + + int vtx_idx=0; + #define ADD_VTX(m_idx);\ + vertices.push_back( face_points[m_idx] );\ + colors.push_back( Color(1,1,1,1) );\ + vtx_idx++;\ + + for (int i=0;i<6;i++) { + + + Vector3 face_points[4]; + + for (int j=0;j<4;j++) { + + float v[3]; + v[0]=1.0; + v[1]=1-2*((j>>1)&1); + v[2]=v[1]*(1-2*(j&1)); + + for (int k=0;k<3;k++) { + + if (i<3) + face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1); + else + face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1); + } + } + + //tri 1 + ADD_VTX(0); + ADD_VTX(1); + ADD_VTX(2); + //tri 2 + ADD_VTX(2); + ADD_VTX(3); + ADD_VTX(0); + + } + + + arr[Mesh::ARRAY_VERTEX]=vertices; + arr[Mesh::ARRAY_COLOR]=colors; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES,arr); + } + + { + Ref<FixedSpatialMaterial> fsm; + fsm.instance(); + fsm->set_flag(FixedSpatialMaterial::FLAG_SRGB_VERTEX_COLOR,true); + fsm->set_flag(FixedSpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR,true); + fsm->set_flag(FixedSpatialMaterial::FLAG_UNSHADED,true); + fsm->set_albedo(Color(1,1,1,1)); + + mesh->surface_set_material(0,fsm); + } + + mm->set_mesh(mesh); + + + int idx=0; + _debug_mesh(0,0,p_baker->po2_bounds,mm,idx,p_baker); + + MultiMeshInstance *mmi = memnew( MultiMeshInstance ); + mmi->set_multimesh(mm); + add_child(mmi); + if (get_tree()->get_edited_scene_root()==this){ + mmi->set_owner(this); + } else { + mmi->set_owner(get_owner()); + + } + +} + +void GIProbe::_debug_bake() { + + bake(NULL,true); +} + +AABB GIProbe::get_aabb() const { + + return AABB(-extents,extents*2); +} + +DVector<Face3> GIProbe::get_faces(uint32_t p_usage_flags) const { + + return DVector<Face3>(); +} + +void GIProbe::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_probe_data","data"),&GIProbe::set_probe_data); + ObjectTypeDB::bind_method(_MD("get_probe_data"),&GIProbe::get_probe_data); + + ObjectTypeDB::bind_method(_MD("set_subdiv","subdiv"),&GIProbe::set_subdiv); + ObjectTypeDB::bind_method(_MD("get_subdiv"),&GIProbe::get_subdiv); + + ObjectTypeDB::bind_method(_MD("set_extents","extents"),&GIProbe::set_extents); + ObjectTypeDB::bind_method(_MD("get_extents"),&GIProbe::get_extents); + + ObjectTypeDB::bind_method(_MD("set_dynamic_range","max"),&GIProbe::set_dynamic_range); + ObjectTypeDB::bind_method(_MD("get_dynamic_range"),&GIProbe::get_dynamic_range); + + ObjectTypeDB::bind_method(_MD("bake","from_node","create_visual_debug"),&GIProbe::bake,DEFVAL(Variant()),DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("debug_bake"),&GIProbe::_debug_bake); + ObjectTypeDB::set_method_flags(get_type_static(),_SCS("debug_bake"),METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR); + + ADD_PROPERTY( PropertyInfo(Variant::INT,"subdiv",PROPERTY_HINT_ENUM,"64,128,256,512"),_SCS("set_subdiv"),_SCS("get_subdiv")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"extents"),_SCS("set_extents"),_SCS("get_extents")); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"dynamic_range",PROPERTY_HINT_RANGE,"0,8,0.01"),_SCS("set_dynamic_range"),_SCS("get_dynamic_range")); + ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"data",PROPERTY_HINT_RESOURCE_TYPE,"GIProbeData"),_SCS("set_probe_data"),_SCS("get_probe_data")); + + + BIND_CONSTANT( SUBDIV_64 ); + BIND_CONSTANT( SUBDIV_128 ); + BIND_CONSTANT( SUBDIV_256 ); + BIND_CONSTANT( SUBDIV_MAX ); + +} + +GIProbe::GIProbe() { + + subdiv=SUBDIV_128; + dynamic_range=1.0; + extents=Vector3(10,10,10); + color_scan_cell_width=4; + bake_texture_size=128; + + gi_probe = VS::get_singleton()->gi_probe_create(); + + +} + +GIProbe::~GIProbe() { + + +} diff --git a/scene/3d/gi_probe.h b/scene/3d/gi_probe.h new file mode 100644 index 0000000000..e2017acfc3 --- /dev/null +++ b/scene/3d/gi_probe.h @@ -0,0 +1,174 @@ +#ifndef GIPROBE_H +#define GIPROBE_H + +#include "scene/3d/visual_instance.h" +#include "multimesh_instance.h" + +class GIProbeData : public Resource { + + OBJ_TYPE(GIProbeData,Resource); + + RID probe; + +public: + + enum DataFormat { + DATA_RGBA8, + DATA_DXT5, + DATA_ETC2_EAC, + }; + + + void set_bounds(const AABB& p_bounds); + AABB get_bounds() const; + + void set_cell_size(float p_size); + float get_cell_size() const; + + void set_to_cell_xform(const Transform& p_xform); + Transform get_to_cell_xform() const; + + void set_dynamic_data(const DVector<int>& p_data); + DVector<int> get_dynamic_data() const; + + void set_dynamic_range(float p_range); + float get_dynamic_range() const; + + void set_static_data(const DVector<uint8_t>& p_data,DataFormat p_format,int p_width,int p_height,int p_depth); + DVector<uint8_t> get_static_data() const; + DataFormat get_static_data_format() const; + int get_static_data_width() const; + int get_static_data_height() const; + int get_static_data_depth() const; + + virtual RID get_rid() const; + + GIProbeData(); + ~GIProbeData(); +}; + +VARIANT_ENUM_CAST(GIProbeData::DataFormat); + +class GIProbe : public VisualInstance { + OBJ_TYPE(GIProbe,VisualInstance); +public: + enum Subdiv{ + SUBDIV_64, + SUBDIV_128, + SUBDIV_256, + SUBDIV_512, + SUBDIV_MAX + + }; +private: + + //stuff used for bake + struct Baker { + + enum { + CHILD_EMPTY=0xFFFFFFFF + }; + struct Cell { + + uint32_t childs[8]; + float albedo[3]; //albedo in RGB24 + float emission[3]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast) + uint32_t used_sides; + float alpha; //used for upsampling + + Cell() { + for(int i=0;i<8;i++) { + childs[i]=CHILD_EMPTY; + } + + for(int i=0;i<3;i++) { + emission[i]=0; + albedo[i]=0; + } + alpha=0; + used_sides=0; + } + }; + + Vector<Cell> bake_cells; + int cell_subdiv; + + struct MaterialCache { + //128x128 textures + Vector<Color> albedo; + Vector<Color> emission; + }; + + + Vector<Color> _get_bake_texture(Image &p_image, const Color &p_color); + Map<Ref<Material>,MaterialCache> material_cache; + MaterialCache _get_material_cache(Ref<Material> p_material); + int leaf_voxel_count; + + + AABB po2_bounds; + int axis_cell_size[3]; + + struct PlotMesh { + Ref<Mesh> mesh; + Transform local_xform; + }; + + Transform to_cell_space; + + List<PlotMesh> mesh_list; + }; + + + Ref<GIProbeData> probe_data; + + RID gi_probe; + + Subdiv subdiv; + Vector3 extents; + float dynamic_range; + + int color_scan_cell_width; + int bake_texture_size; + + Vector<Color> _get_bake_texture(Image &p_image,const Color& p_color); + Baker::MaterialCache _get_material_cache(Ref<Material> p_material,Baker *p_baker); + void _plot_face(int p_idx, int p_level, int p_x,int p_y,int p_z,const Vector3 *p_vtx, const Vector2* p_uv, const Baker::MaterialCache& p_material, const AABB &p_aabb,Baker *p_baker); + void _plot_mesh(const Transform& p_xform, Ref<Mesh>& p_mesh, Baker *p_baker); + void _find_meshes(Node *p_at_node,Baker *p_baker); + void _fixup_plot(int p_idx, int p_level,int p_x,int p_y, int p_z,Baker *p_baker); + + void _debug_mesh(int p_idx, int p_level, const AABB &p_aabb,Ref<MultiMesh> &p_multimesh,int &idx,Baker *p_baker); + void _create_debug_mesh(Baker *p_baker); + + void _debug_bake(); + +protected: + + static void _bind_methods(); +public: + + void set_probe_data(const Ref<GIProbeData>& p_data); + Ref<GIProbeData> get_probe_data() const; + + void set_subdiv(Subdiv p_subdiv); + Subdiv get_subdiv() const; + + void set_extents(const Vector3& p_extents); + Vector3 get_extents() const; + + void set_dynamic_range(float p_dynamic_range); + float get_dynamic_range() const; + + void bake(Node *p_from_node=NULL,bool p_create_visual_debug=false); + + virtual AABB get_aabb() const; + virtual DVector<Face3> get_faces(uint32_t p_usage_flags) const; + + GIProbe(); + ~GIProbe(); +}; + +VARIANT_ENUM_CAST(GIProbe::Subdiv) + +#endif // GIPROBE_H diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp index 7177e21e1e..1566743e1b 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -30,7 +30,7 @@ #include "globals.h" #include "scene/resources/surface_tool.h" - +#include "baked_light_instance.h" bool Light::_can_gizmo_scale() const { @@ -168,9 +168,37 @@ void Light::_update_visibility() { void Light::_notification(int p_what) { - if (p_what==NOTIFICATION_ENTER_TREE || p_what==NOTIFICATION_VISIBILITY_CHANGED) { + + if (p_what==NOTIFICATION_VISIBILITY_CHANGED) { + _update_visibility(); + + } + + if (p_what==NOTIFICATION_ENTER_TREE) { + _update_visibility(); + + Node *node = this; + + while(node) { + + baked_light=node->cast_to<BakedLight>(); + if (baked_light) { + baked_light->lights.insert(this); + break; + } + + node=node->get_parent(); + } } + + if (p_what==NOTIFICATION_EXIT_TREE) { + + if (baked_light) { + baked_light->lights.erase(this); + } + } + } @@ -247,6 +275,8 @@ Light::Light(VisualServer::LightType p_type) { light=VisualServer::get_singleton()->light_create(p_type); VS::get_singleton()->instance_set_base(get_instance(),light); + baked_light=NULL; + editor_only=false; set_color(Color(1,1,1,1)); set_shadow(false); diff --git a/scene/3d/light.h b/scene/3d/light.h index fcf5ce90f9..45adfc1dee 100644 --- a/scene/3d/light.h +++ b/scene/3d/light.h @@ -37,6 +37,10 @@ /** @author Juan Linietsky <reduzio@gmail.com> */ + + +class BakedLight; + class Light : public VisualInstance { OBJ_TYPE( Light, VisualInstance ); @@ -72,6 +76,8 @@ private: VS::LightType type; bool editor_only; void _update_visibility(); + + BakedLight *baked_light; // bind helpers protected: diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 5bc332f8fb..f5dbf7c53a 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -31,7 +31,6 @@ #include "servers/visual_server.h" #include "room_instance.h" #include "scene/scene_string_names.h" -#include "baked_light_instance.h" #include "skeleton.h" AABB VisualInstance::get_transformed_aabb() const { @@ -227,7 +226,6 @@ void GeometryInstance::_notification(int p_what) { if (flags[FLAG_USE_BAKED_LIGHT]) { - _find_baked_light(); } _update_visibility(); @@ -236,11 +234,6 @@ void GeometryInstance::_notification(int p_what) { if (flags[FLAG_USE_BAKED_LIGHT]) { - if (baked_light_instance) { - // baked_light_instance->disconnect(SceneStringNames::get_singleton()->baked_light_changed,this,SceneStringNames::get_singleton()->_baked_light_changed); - // baked_light_instance=NULL; - } - _baked_light_changed(); } @@ -252,37 +245,6 @@ void GeometryInstance::_notification(int p_what) { } -void GeometryInstance::_baked_light_changed() { - - //if (!baked_light_instance) - // VS::get_singleton()->instance_geometry_set_baked_light(get_instance(),RID()); -// else -// VS::get_singleton()->instance_geometry_set_baked_light(get_instance(),baked_light_instance->get_baked_light_instance()); - -} - -void GeometryInstance::_find_baked_light() { -/* - Node *n=get_parent(); - while(n) { - - BakedLightInstance *bl=n->cast_to<BakedLightInstance>(); - if (bl) { - - baked_light_instance=bl; - baked_light_instance->connect(SceneStringNames::get_singleton()->baked_light_changed,this,SceneStringNames::get_singleton()->_baked_light_changed); - _baked_light_changed(); - - return; - } - - n=n->get_parent(); - } - - _baked_light_changed(); - */ -} - void GeometryInstance::_update_visibility() { if (!is_inside_tree()) @@ -314,17 +276,6 @@ void GeometryInstance::set_flag(Flags p_flag,bool p_value) { } if (p_flag==FLAG_USE_BAKED_LIGHT) { - /* if (is_inside_world()) { - if (!p_value) { - if (baked_light_instance) { - baked_light_instance->disconnect(SceneStringNames::get_singleton()->baked_light_changed,this,SceneStringNames::get_singleton()->_baked_light_changed); - baked_light_instance=NULL; - } - _baked_light_changed(); - } else { - _find_baked_light(); - } - }*/ } } @@ -357,17 +308,8 @@ GeometryInstance::ShadowCastingSetting GeometryInstance::get_cast_shadows_settin return shadow_casting_setting; } -void GeometryInstance::set_baked_light_texture_id(int p_id) { -// baked_light_texture_id=p_id; -// VS::get_singleton()->instance_geometry_set_baked_light_texture_index(get_instance(),baked_light_texture_id); -} - -int GeometryInstance::get_baked_light_texture_id() const{ - - return baked_light_texture_id; -} void GeometryInstance::set_extra_cull_margin(float p_margin) { @@ -405,15 +347,11 @@ void GeometryInstance::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_lod_min_distance"), &GeometryInstance::get_lod_min_distance); - ObjectTypeDB::bind_method(_MD("set_baked_light_texture_id","id"), &GeometryInstance::set_baked_light_texture_id); - ObjectTypeDB::bind_method(_MD("get_baked_light_texture_id"), &GeometryInstance::get_baked_light_texture_id); - ObjectTypeDB::bind_method(_MD("set_extra_cull_margin","margin"), &GeometryInstance::set_extra_cull_margin); ObjectTypeDB::bind_method(_MD("get_extra_cull_margin"), &GeometryInstance::get_extra_cull_margin); ObjectTypeDB::bind_method(_MD("get_aabb"),&GeometryInstance::get_aabb); - ObjectTypeDB::bind_method(_MD("_baked_light_changed"), &GeometryInstance::_baked_light_changed); ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/visible"), _SCS("set_flag"), _SCS("get_flag"),FLAG_VISIBLE); ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "geometry/material_override",PROPERTY_HINT_RESOURCE_TYPE,"Material"), _SCS("set_material_override"), _SCS("get_material_override")); @@ -424,7 +362,6 @@ void GeometryInstance::_bind_methods() { ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/depth_scale"), _SCS("set_flag"), _SCS("get_flag"),FLAG_DEPH_SCALE); ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/visible_in_all_rooms"), _SCS("set_flag"), _SCS("get_flag"),FLAG_VISIBLE_IN_ALL_ROOMS); ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/use_baked_light"), _SCS("set_flag"), _SCS("get_flag"),FLAG_USE_BAKED_LIGHT); - ADD_PROPERTY( PropertyInfo( Variant::INT, "geometry/baked_light_tex_id"), _SCS("set_baked_light_texture_id"), _SCS("get_baked_light_texture_id")); ADD_PROPERTY( PropertyInfo( Variant::INT, "lod/min_distance",PROPERTY_HINT_RANGE,"0,32768,0.01"), _SCS("set_lod_min_distance"), _SCS("get_lod_min_distance")); ADD_PROPERTY( PropertyInfo( Variant::INT, "lod/min_hysteresis",PROPERTY_HINT_RANGE,"0,32768,0.01"), _SCS("set_lod_min_hysteresis"), _SCS("get_lod_min_hysteresis")); ADD_PROPERTY( PropertyInfo( Variant::INT, "lod/max_distance",PROPERTY_HINT_RANGE,"0,32768,0.01"), _SCS("set_lod_max_distance"), _SCS("get_lod_max_distance")); @@ -461,8 +398,6 @@ GeometryInstance::GeometryInstance() { flags[FLAG_CAST_SHADOW]=true; shadow_casting_setting=SHADOW_CASTING_SETTING_ON; - baked_light_instance=NULL; - baked_light_texture_id=0; extra_cull_margin=0; // VS::get_singleton()->instance_geometry_set_baked_light_texture_index(get_instance(),0); diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index b168bcbfe5..5fd0830d3f 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -79,7 +79,7 @@ public: }; -class BakedLightInstance; +class BakedLight; class GeometryInstance : public VisualInstance { @@ -114,12 +114,9 @@ private: float lod_max_distance; float lod_min_hysteresis; float lod_max_hysteresis; - void _find_baked_light(); - BakedLightInstance *baked_light_instance; - int baked_light_texture_id; + float extra_cull_margin; - void _baked_light_changed(); void _update_visibility(); protected: @@ -148,9 +145,6 @@ public: void set_material_override(const Ref<Material>& p_material); Ref<Material> get_material_override() const; - void set_baked_light_texture_id(int p_id); - int get_baked_light_texture_id() const; - void set_extra_cull_margin(float p_margin); float get_extra_cull_margin() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index d2b9def5c7..1c2620ec9a 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -205,6 +205,7 @@ #include "scene/3d/quad.h" #include "scene/3d/light.h" #include "scene/3d/reflection_probe.h" +#include "scene/3d/gi_probe.h" #include "scene/3d/particles.h" #include "scene/3d/portal.h" #include "scene/resources/environment.h" @@ -424,6 +425,8 @@ void register_scene_types() { ObjectTypeDB::register_type<OmniLight>(); ObjectTypeDB::register_type<SpotLight>(); ObjectTypeDB::register_type<ReflectionProbe>(); + ObjectTypeDB::register_type<GIProbe>(); + ObjectTypeDB::register_type<GIProbeData>(); ObjectTypeDB::register_type<AnimationTreePlayer>(); ObjectTypeDB::register_type<Portal>(); //ObjectTypeDB::register_type<Particles>(); @@ -455,7 +458,7 @@ void register_scene_types() { ObjectTypeDB::register_type<PathFollow>(); ObjectTypeDB::register_type<VisibilityNotifier>(); ObjectTypeDB::register_type<VisibilityEnabler>(); - //ObjectTypeDB::register_type<BakedLightInstance>(); + ObjectTypeDB::register_type<BakedLight>(); //ObjectTypeDB::register_type<BakedLightSampler>(); ObjectTypeDB::register_type<WorldEnvironment>(); ObjectTypeDB::register_type<RemoteTransform>(); diff --git a/scene/resources/baked_light.cpp b/scene/resources/baked_light.cpp index 38ed661cdd..0d53eff7d3 100644 --- a/scene/resources/baked_light.cpp +++ b/scene/resources/baked_light.cpp @@ -29,577 +29,3 @@ #include "baked_light.h" #include "servers/visual_server.h" -#if 0 - -void BakedLight::set_mode(Mode p_mode) { - - mode=p_mode; - VS::get_singleton()->baked_light_set_mode(baked_light,(VS::BakedLightMode(p_mode))); - -} - -BakedLight::Mode BakedLight::get_mode() const{ - - return mode; -} - -void BakedLight::set_octree(const DVector<uint8_t>& p_octree) { - - VS::get_singleton()->baked_light_set_octree(baked_light,p_octree); -} - -DVector<uint8_t> BakedLight::get_octree() const { - - return VS::get_singleton()->baked_light_get_octree(baked_light); -} - -void BakedLight::set_light(const DVector<uint8_t>& p_light) { - - VS::get_singleton()->baked_light_set_light(baked_light,p_light); -} - -DVector<uint8_t> BakedLight::get_light() const { - - return VS::get_singleton()->baked_light_get_light(baked_light); -} - - -void BakedLight::set_sampler_octree(const DVector<int>& p_sampler_octree) { - - VS::get_singleton()->baked_light_set_sampler_octree(baked_light,p_sampler_octree); -} - -DVector<int> BakedLight::get_sampler_octree() const { - - return VS::get_singleton()->baked_light_get_sampler_octree(baked_light); -} - - - - - -void BakedLight::add_lightmap(const Ref<Texture> &p_texture,Size2 p_gen_size) { - - LightMap lm; - lm.texture=p_texture; - lm.gen_size=p_gen_size; - lightmaps.push_back(lm); - _update_lightmaps(); - _change_notify(); -} - -void BakedLight::set_lightmap_gen_size(int p_idx,const Size2& p_size){ - - ERR_FAIL_INDEX(p_idx,lightmaps.size()); - lightmaps[p_idx].gen_size=p_size; - _update_lightmaps(); -} -Size2 BakedLight::get_lightmap_gen_size(int p_idx) const{ - - ERR_FAIL_INDEX_V(p_idx,lightmaps.size(),Size2()); - return lightmaps[p_idx].gen_size; - -} -void BakedLight::set_lightmap_texture(int p_idx,const Ref<Texture> &p_texture){ - - ERR_FAIL_INDEX(p_idx,lightmaps.size()); - lightmaps[p_idx].texture=p_texture; - _update_lightmaps(); - -} -Ref<Texture> BakedLight::get_lightmap_texture(int p_idx) const{ - - ERR_FAIL_INDEX_V(p_idx,lightmaps.size(),Ref<Texture>()); - return lightmaps[p_idx].texture; - -} -void BakedLight::erase_lightmap(int p_idx){ - - ERR_FAIL_INDEX(p_idx,lightmaps.size()); - lightmaps.remove(p_idx); - _update_lightmaps(); - _change_notify(); - -} -int BakedLight::get_lightmaps_count() const{ - - return lightmaps.size(); -} -void BakedLight::clear_lightmaps(){ - - lightmaps.clear(); - _update_lightmaps(); - _change_notify(); -} - - - -void BakedLight::_update_lightmaps() { - - VS::get_singleton()->baked_light_clear_lightmaps(baked_light); - for(int i=0;i<lightmaps.size();i++) { - - RID tid; - if (lightmaps[i].texture.is_valid()) - tid=lightmaps[i].texture->get_rid(); - VS::get_singleton()->baked_light_add_lightmap(baked_light,tid,i); - } -} - - - -RID BakedLight::get_rid() const { - - return baked_light; -} - -Array BakedLight::_get_lightmap_data() const { - - Array ret; - ret.resize(lightmaps.size()*2); - - int idx=0; - for(int i=0;i<lightmaps.size();i++) { - - ret[idx++]=Size2(lightmaps[i].gen_size); - ret[idx++]=lightmaps[i].texture; - } - return ret; - -} - -void BakedLight::_set_lightmap_data(Array p_array){ - - lightmaps.clear(); - for(int i=0;i<p_array.size();i+=2) { - - Size2 size = p_array[i]; - Ref<Texture> tex = p_array[i+1]; -// ERR_CONTINUE(tex.is_null()); - LightMap lm; - lm.gen_size=size; - lm.texture=tex; - lightmaps.push_back(lm); - } - _update_lightmaps(); -} - - -void BakedLight::set_cell_subdivision(int p_subdiv) { - - cell_subdiv=p_subdiv; -} - -int BakedLight::get_cell_subdivision() const{ - - return cell_subdiv; -} - -void BakedLight::set_initial_lattice_subdiv(int p_size){ - - lattice_subdiv=p_size; -} -int BakedLight::get_initial_lattice_subdiv() const{ - - return lattice_subdiv; -} - -void BakedLight::set_plot_size(float p_size){ - - plot_size=p_size; -} -float BakedLight::get_plot_size() const{ - - return plot_size; -} - -void BakedLight::set_bounces(int p_size){ - - bounces=p_size; -} -int BakedLight::get_bounces() const{ - - return bounces; -} - -void BakedLight::set_cell_extra_margin(float p_margin) { - cell_extra_margin=p_margin; -} - -float BakedLight::get_cell_extra_margin() const { - - return cell_extra_margin; -} - -void BakedLight::set_edge_damp(float p_margin) { - edge_damp=p_margin; -} - -float BakedLight::get_edge_damp() const { - - return edge_damp; -} - - -void BakedLight::set_normal_damp(float p_margin) { - normal_damp=p_margin; -} - -float BakedLight::get_normal_damp() const { - - return normal_damp; -} - -void BakedLight::set_tint(float p_margin) { - tint=p_margin; -} - -float BakedLight::get_tint() const { - - return tint; -} - -void BakedLight::set_saturation(float p_margin) { - saturation=p_margin; -} - -float BakedLight::get_saturation() const { - - return saturation; -} - -void BakedLight::set_ao_radius(float p_ao_radius) { - ao_radius=p_ao_radius; -} - -float BakedLight::get_ao_radius() const { - return ao_radius; -} - -void BakedLight::set_ao_strength(float p_ao_strength) { - - ao_strength=p_ao_strength; -} - -float BakedLight::get_ao_strength() const { - - return ao_strength; -} - -void BakedLight::set_realtime_color_enabled(const bool p_realtime_color_enabled) { - - VS::get_singleton()->baked_light_set_realtime_color_enabled(baked_light, p_realtime_color_enabled); -} - -bool BakedLight::get_realtime_color_enabled() const { - - return VS::get_singleton()->baked_light_get_realtime_color_enabled(baked_light); -} - - -void BakedLight::set_realtime_color(const Color &p_realtime_color) { - - VS::get_singleton()->baked_light_set_realtime_color(baked_light, p_realtime_color); -} - -Color BakedLight::get_realtime_color() const { - - return VS::get_singleton()->baked_light_get_realtime_color(baked_light); -} - -void BakedLight::set_realtime_energy(const float p_realtime_energy) { - - VS::get_singleton()->baked_light_set_realtime_energy(baked_light, p_realtime_energy); -} - -float BakedLight::get_realtime_energy() const { - - return VS::get_singleton()->baked_light_get_realtime_energy(baked_light); -} - - - -void BakedLight::set_energy_multiplier(float p_multiplier){ - - energy_multiply=p_multiplier; -} -float BakedLight::get_energy_multiplier() const{ - - return energy_multiply; -} - -void BakedLight::set_gamma_adjust(float p_adjust){ - - gamma_adjust=p_adjust; -} -float BakedLight::get_gamma_adjust() const{ - - return gamma_adjust; -} - -void BakedLight::set_bake_flag(BakeFlags p_flags,bool p_enable){ - - flags[p_flags]=p_enable; -} -bool BakedLight::get_bake_flag(BakeFlags p_flags) const{ - - return flags[p_flags]; -} - -void BakedLight::set_format(Format p_format) { - - format=p_format; - VS::get_singleton()->baked_light_set_lightmap_multiplier(baked_light,format==FORMAT_HDR8?8.0:1.0); -} - -BakedLight::Format BakedLight::get_format() const{ - - return format; -} - -void BakedLight::set_transfer_lightmaps_only_to_uv2(bool p_enable) { - - transfer_only_uv2=p_enable; -} - -bool BakedLight::get_transfer_lightmaps_only_to_uv2() const{ - - return transfer_only_uv2; -} - - -bool BakedLight::_set(const StringName& p_name, const Variant& p_value) { - - String n = p_name; - if (!n.begins_with("lightmap")) - return false; - int idx = n.get_slicec('/',1).to_int(); - ERR_FAIL_COND_V(idx<0,false); - ERR_FAIL_COND_V(idx>lightmaps.size(),false); - - String what = n.get_slicec('/',2); - Ref<Texture> tex; - Size2 gens; - - if (what=="texture") - tex=p_value; - else if (what=="gen_size") - gens=p_value; - - if (idx==lightmaps.size()) { - if (tex.is_valid() || gens!=Size2()) - add_lightmap(tex,gens); - } else { - if (tex.is_valid()) - set_lightmap_texture(idx,tex); - else if (gens!=Size2()) - set_lightmap_gen_size(idx,gens); - } - - - return true; -} - -bool BakedLight::_get(const StringName& p_name,Variant &r_ret) const{ - - String n = p_name; - if (!n.begins_with("lightmap")) - return false; - int idx = n.get_slicec('/',1).to_int(); - ERR_FAIL_COND_V(idx<0,false); - ERR_FAIL_COND_V(idx>lightmaps.size(),false); - - String what = n.get_slicec('/',2); - - if (what=="texture") { - if (idx==lightmaps.size()) - r_ret=Ref<Texture>(); - else - r_ret=lightmaps[idx].texture; - - } else if (what=="gen_size") { - - if (idx==lightmaps.size()) - r_ret=Size2(); - else - r_ret=Size2(lightmaps[idx].gen_size); - } else - return false; - - return true; - - -} -void BakedLight::_get_property_list( List<PropertyInfo> *p_list) const{ - - for(int i=0;i<=lightmaps.size();i++) { - - p_list->push_back(PropertyInfo(Variant::VECTOR2,"lightmaps/"+itos(i)+"/gen_size",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR)); - p_list->push_back(PropertyInfo(Variant::OBJECT,"lightmaps/"+itos(i)+"/texture",PROPERTY_HINT_RESOURCE_TYPE,"Texture",PROPERTY_USAGE_EDITOR)); - } -} - - -void BakedLight::_bind_methods(){ - - - ObjectTypeDB::bind_method(_MD("set_mode","mode"),&BakedLight::set_mode); - ObjectTypeDB::bind_method(_MD("get_mode"),&BakedLight::get_mode); - - ObjectTypeDB::bind_method(_MD("set_octree","octree"),&BakedLight::set_octree); - ObjectTypeDB::bind_method(_MD("get_octree"),&BakedLight::get_octree); - - ObjectTypeDB::bind_method(_MD("set_light","light"),&BakedLight::set_light); - ObjectTypeDB::bind_method(_MD("get_light"),&BakedLight::get_light); - - ObjectTypeDB::bind_method(_MD("set_sampler_octree","sampler_octree"),&BakedLight::set_sampler_octree); - ObjectTypeDB::bind_method(_MD("get_sampler_octree"),&BakedLight::get_sampler_octree); - - - ObjectTypeDB::bind_method(_MD("add_lightmap","texture:Texture","gen_size"),&BakedLight::add_lightmap); - ObjectTypeDB::bind_method(_MD("erase_lightmap","id"),&BakedLight::erase_lightmap); - ObjectTypeDB::bind_method(_MD("clear_lightmaps"),&BakedLight::clear_lightmaps); - - ObjectTypeDB::bind_method(_MD("_set_lightmap_data","lightmap_data"),&BakedLight::_set_lightmap_data); - ObjectTypeDB::bind_method(_MD("_get_lightmap_data"),&BakedLight::_get_lightmap_data); - - ObjectTypeDB::bind_method(_MD("set_cell_subdivision","cell_subdivision"),&BakedLight::set_cell_subdivision); - ObjectTypeDB::bind_method(_MD("get_cell_subdivision"),&BakedLight::get_cell_subdivision); - - ObjectTypeDB::bind_method(_MD("set_initial_lattice_subdiv","cell_subdivision"),&BakedLight::set_initial_lattice_subdiv); - ObjectTypeDB::bind_method(_MD("get_initial_lattice_subdiv","cell_subdivision"),&BakedLight::get_initial_lattice_subdiv); - - ObjectTypeDB::bind_method(_MD("set_plot_size","plot_size"),&BakedLight::set_plot_size); - ObjectTypeDB::bind_method(_MD("get_plot_size"),&BakedLight::get_plot_size); - - ObjectTypeDB::bind_method(_MD("set_bounces","bounces"),&BakedLight::set_bounces); - ObjectTypeDB::bind_method(_MD("get_bounces"),&BakedLight::get_bounces); - - ObjectTypeDB::bind_method(_MD("set_cell_extra_margin","cell_extra_margin"),&BakedLight::set_cell_extra_margin); - ObjectTypeDB::bind_method(_MD("get_cell_extra_margin"),&BakedLight::get_cell_extra_margin); - - ObjectTypeDB::bind_method(_MD("set_edge_damp","edge_damp"),&BakedLight::set_edge_damp); - ObjectTypeDB::bind_method(_MD("get_edge_damp"),&BakedLight::get_edge_damp); - - ObjectTypeDB::bind_method(_MD("set_normal_damp","normal_damp"),&BakedLight::set_normal_damp); - ObjectTypeDB::bind_method(_MD("get_normal_damp"),&BakedLight::get_normal_damp); - - ObjectTypeDB::bind_method(_MD("set_tint","tint"),&BakedLight::set_tint); - ObjectTypeDB::bind_method(_MD("get_tint"),&BakedLight::get_tint); - - ObjectTypeDB::bind_method(_MD("set_saturation","saturation"),&BakedLight::set_saturation); - ObjectTypeDB::bind_method(_MD("get_saturation"),&BakedLight::get_saturation); - - ObjectTypeDB::bind_method(_MD("set_ao_radius","ao_radius"),&BakedLight::set_ao_radius); - ObjectTypeDB::bind_method(_MD("get_ao_radius"),&BakedLight::get_ao_radius); - - ObjectTypeDB::bind_method(_MD("set_ao_strength","ao_strength"),&BakedLight::set_ao_strength); - ObjectTypeDB::bind_method(_MD("get_ao_strength"),&BakedLight::get_ao_strength); - - ObjectTypeDB::bind_method(_MD("set_realtime_color_enabled", "enabled"), &BakedLight::set_realtime_color_enabled); - ObjectTypeDB::bind_method(_MD("get_realtime_color_enabled"), &BakedLight::get_realtime_color_enabled); - - ObjectTypeDB::bind_method(_MD("set_realtime_color", "tint"), &BakedLight::set_realtime_color); - ObjectTypeDB::bind_method(_MD("get_realtime_color"), &BakedLight::get_realtime_color); - - ObjectTypeDB::bind_method(_MD("set_realtime_energy", "energy"), &BakedLight::set_realtime_energy); - ObjectTypeDB::bind_method(_MD("get_realtime_energy"), &BakedLight::get_realtime_energy); - - ObjectTypeDB::bind_method(_MD("set_format","format"),&BakedLight::set_format); - ObjectTypeDB::bind_method(_MD("get_format"),&BakedLight::get_format); - - ObjectTypeDB::bind_method(_MD("set_transfer_lightmaps_only_to_uv2","enable"),&BakedLight::set_transfer_lightmaps_only_to_uv2); - ObjectTypeDB::bind_method(_MD("get_transfer_lightmaps_only_to_uv2"),&BakedLight::get_transfer_lightmaps_only_to_uv2); - - - - - ObjectTypeDB::bind_method(_MD("set_energy_multiplier","energy_multiplier"),&BakedLight::set_energy_multiplier); - ObjectTypeDB::bind_method(_MD("get_energy_multiplier"),&BakedLight::get_energy_multiplier); - - ObjectTypeDB::bind_method(_MD("set_gamma_adjust","gamma_adjust"),&BakedLight::set_gamma_adjust); - ObjectTypeDB::bind_method(_MD("get_gamma_adjust"),&BakedLight::get_gamma_adjust); - - ObjectTypeDB::bind_method(_MD("set_bake_flag","flag","enabled"),&BakedLight::set_bake_flag); - ObjectTypeDB::bind_method(_MD("get_bake_flag","flag"),&BakedLight::get_bake_flag); - - ADD_PROPERTY( PropertyInfo(Variant::INT,"mode/mode",PROPERTY_HINT_ENUM,"Octree,Lightmaps"),_SCS("set_mode"),_SCS("get_mode")); - - ADD_PROPERTY( PropertyInfo(Variant::INT,"baking/format",PROPERTY_HINT_ENUM,"RGB,HDR8,HDR16"),_SCS("set_format"),_SCS("get_format")); - ADD_PROPERTY( PropertyInfo(Variant::INT,"baking/cell_subdiv",PROPERTY_HINT_RANGE,"4,14,1"),_SCS("set_cell_subdivision"),_SCS("get_cell_subdivision")); - ADD_PROPERTY( PropertyInfo(Variant::INT,"baking/lattice_subdiv",PROPERTY_HINT_RANGE,"1,5,1"),_SCS("set_initial_lattice_subdiv"),_SCS("get_initial_lattice_subdiv")); - ADD_PROPERTY( PropertyInfo(Variant::INT,"baking/light_bounces",PROPERTY_HINT_RANGE,"0,3,1"),_SCS("set_bounces"),_SCS("get_bounces")); - ADD_PROPERTY( PropertyInfo(Variant::REAL,"baking/plot_size",PROPERTY_HINT_RANGE,"1.0,16.0,0.01"),_SCS("set_plot_size"),_SCS("get_plot_size")); - ADD_PROPERTY( PropertyInfo(Variant::REAL,"baking/energy_mult",PROPERTY_HINT_RANGE,"0.01,4096.0,0.01"),_SCS("set_energy_multiplier"),_SCS("get_energy_multiplier")); - ADD_PROPERTY( PropertyInfo(Variant::REAL,"baking/gamma_adjust",PROPERTY_HINT_EXP_EASING),_SCS("set_gamma_adjust"),_SCS("get_gamma_adjust")); - ADD_PROPERTY( PropertyInfo(Variant::REAL,"baking/saturation",PROPERTY_HINT_RANGE,"0,8,0.01"),_SCS("set_saturation"),_SCS("get_saturation")); - ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/diffuse"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_DIFFUSE); - ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/specular"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_SPECULAR); - ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/translucent"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_TRANSLUCENT); - ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/conserve_energy"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_CONSERVE_ENERGY); - ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/linear_color"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_LINEAR_COLOR); - ADD_PROPERTY( PropertyInfo(Variant::BOOL,"lightmap/use_only_uv2"),_SCS("set_transfer_lightmaps_only_to_uv2"),_SCS("get_transfer_lightmaps_only_to_uv2")); - - ADD_PROPERTY( PropertyInfo(Variant::RAW_ARRAY,"octree",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_octree"),_SCS("get_octree")); - ADD_PROPERTY( PropertyInfo(Variant::RAW_ARRAY,"light",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_light"),_SCS("get_light")); - ADD_PROPERTY( PropertyInfo(Variant::INT_ARRAY,"sampler_octree",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_sampler_octree"),_SCS("get_sampler_octree")); - ADD_PROPERTY( PropertyInfo(Variant::ARRAY,"lightmaps",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_lightmap_data"),_SCS("_get_lightmap_data")); - ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/cell_margin",PROPERTY_HINT_RANGE,"0.01,0.8,0.01"),_SCS("set_cell_extra_margin"),_SCS("get_cell_extra_margin")); - ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/edge_damp",PROPERTY_HINT_RANGE,"0.0,8.0,0.1"),_SCS("set_edge_damp"),_SCS("get_edge_damp")); - ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/normal_damp",PROPERTY_HINT_RANGE,"0.0,1.0,0.01"),_SCS("set_normal_damp"),_SCS("get_normal_damp")); - ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/light_tint",PROPERTY_HINT_RANGE,"0.0,1.0,0.01"),_SCS("set_tint"),_SCS("get_tint")); - ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/ao_radius",PROPERTY_HINT_RANGE,"0.0,16.0,0.01"),_SCS("set_ao_radius"),_SCS("get_ao_radius")); - ADD_PROPERTY( PropertyInfo(Variant::REAL,"advanced/ao_strength",PROPERTY_HINT_RANGE,"0.0,1.0,0.01"),_SCS("set_ao_strength"),_SCS("get_ao_strength")); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "realtime/enabled"), _SCS("set_realtime_color_enabled"), _SCS("get_realtime_color_enabled")); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "realtime/color", PROPERTY_HINT_COLOR_NO_ALPHA), _SCS("set_realtime_color"), _SCS("get_realtime_color")); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "realtime/energy", PROPERTY_HINT_RANGE, "0.01,4096.0,0.01"), _SCS("set_realtime_energy"), _SCS("get_realtime_energy")); - - - BIND_CONSTANT( MODE_OCTREE ); - BIND_CONSTANT( MODE_LIGHTMAPS ); - - BIND_CONSTANT( BAKE_DIFFUSE ); - BIND_CONSTANT( BAKE_SPECULAR ); - BIND_CONSTANT( BAKE_TRANSLUCENT ); - BIND_CONSTANT( BAKE_CONSERVE_ENERGY ); - BIND_CONSTANT( BAKE_MAX ); - - -} - - -BakedLight::BakedLight() { - - cell_subdiv=8; - lattice_subdiv=4; - plot_size=2.5; - bounces=1; - energy_multiply=2.0; - gamma_adjust=0.7; - cell_extra_margin=0.05; - edge_damp=0.0; - normal_damp=0.0; - saturation=1; - tint=0.0; - ao_radius=2.5; - ao_strength=0.7; - format=FORMAT_RGB8; - transfer_only_uv2=false; - - - flags[BAKE_DIFFUSE]=true; - flags[BAKE_SPECULAR]=false; - flags[BAKE_TRANSLUCENT]=true; - flags[BAKE_CONSERVE_ENERGY]=false; - flags[BAKE_LINEAR_COLOR]=false; - - mode=MODE_OCTREE; - baked_light=VS::get_singleton()->baked_light_create(); -} - -BakedLight::~BakedLight() { - - VS::get_singleton()->free(baked_light); -} -#endif diff --git a/scene/resources/baked_light.h b/scene/resources/baked_light.h index 0eaa3df276..6a0742dd27 100644 --- a/scene/resources/baked_light.h +++ b/scene/resources/baked_light.h @@ -32,169 +32,6 @@ #include "resource.h" #include "scene/resources/texture.h" -#if 0 -class BakedLight : public Resource { - OBJ_TYPE( BakedLight, Resource); -public: - enum Mode { - MODE_OCTREE, - MODE_LIGHTMAPS - }; - - enum Format { - - FORMAT_RGB8, - FORMAT_HDR8, - FORMAT_HDR16 - }; - - enum BakeFlags { - BAKE_DIFFUSE, - BAKE_SPECULAR, - BAKE_TRANSLUCENT, - BAKE_CONSERVE_ENERGY, - BAKE_LINEAR_COLOR, - BAKE_MAX - }; - -private: - - RID baked_light; - Mode mode; - struct LightMap { - Size2i gen_size; - Ref<Texture> texture; - }; - - - Vector< LightMap> lightmaps; - - //bake vars - int cell_subdiv; - int lattice_subdiv; - float plot_size; - float energy_multiply; - float gamma_adjust; - float cell_extra_margin; - float edge_damp; - float normal_damp; - float tint; - float ao_radius; - float ao_strength; - float saturation; - int bounces; - bool transfer_only_uv2; - Format format; - bool flags[BAKE_MAX]; - - - - void _update_lightmaps(); - - Array _get_lightmap_data() const; - void _set_lightmap_data(Array p_array); - -protected: - - bool _set(const StringName& p_name, const Variant& p_value); - bool _get(const StringName& p_name,Variant &r_ret) const; - void _get_property_list( List<PropertyInfo> *p_list) const; - - static void _bind_methods(); - -public: - - void set_cell_subdivision(int p_subdiv); - int get_cell_subdivision() const; - - void set_initial_lattice_subdiv(int p_size); - int get_initial_lattice_subdiv() const; - - void set_plot_size(float p_size); - float get_plot_size() const; - - void set_bounces(int p_size); - int get_bounces() const; - - void set_energy_multiplier(float p_multiplier); - float get_energy_multiplier() const; - - void set_gamma_adjust(float p_adjust); - float get_gamma_adjust() const; - - void set_cell_extra_margin(float p_margin); - float get_cell_extra_margin() const; - - void set_edge_damp(float p_margin); - float get_edge_damp() const; - - void set_normal_damp(float p_margin); - float get_normal_damp() const; - - void set_tint(float p_margin); - float get_tint() const; - - void set_saturation(float p_saturation); - float get_saturation() const; - - void set_ao_radius(float p_ao_radius); - float get_ao_radius() const; - - void set_ao_strength(float p_ao_strength); - float get_ao_strength() const; - - void set_realtime_color_enabled(const bool p_enabled); - bool get_realtime_color_enabled() const; - - void set_realtime_color(const Color& p_realtime_color); - Color get_realtime_color() const; - - void set_realtime_energy(const float p_realtime_energy); - float get_realtime_energy() const; - - void set_bake_flag(BakeFlags p_flags,bool p_enable); - bool get_bake_flag(BakeFlags p_flags) const; - - void set_format(Format p_margin); - Format get_format() const; - - void set_transfer_lightmaps_only_to_uv2(bool p_enable); - bool get_transfer_lightmaps_only_to_uv2() const; - - void set_mode(Mode p_mode); - Mode get_mode() const; - - void set_octree(const DVector<uint8_t>& p_octree); - DVector<uint8_t> get_octree() const; - - void set_light(const DVector<uint8_t>& p_light); - DVector<uint8_t> get_light() const; - - void set_sampler_octree(const DVector<int>& p_sampler_octree); - DVector<int> get_sampler_octree() const; - - - - void add_lightmap(const Ref<Texture> &p_texture,Size2 p_gen_size=Size2(256,256)); - void set_lightmap_gen_size(int p_idx,const Size2& p_size); - Size2 get_lightmap_gen_size(int p_idx) const; - void set_lightmap_texture(int p_idx,const Ref<Texture> &p_texture); - Ref<Texture> get_lightmap_texture(int p_idx) const; - void erase_lightmap(int p_idx); - int get_lightmaps_count() const; - void clear_lightmaps(); - - virtual RID get_rid() const; - - BakedLight(); - ~BakedLight(); -}; - - -VARIANT_ENUM_CAST(BakedLight::Format); -VARIANT_ENUM_CAST(BakedLight::Mode); -VARIANT_ENUM_CAST(BakedLight::BakeFlags); -#endif #endif // BAKED_LIGHT_H diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 228afffdb5..79560f3486 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -91,6 +91,7 @@ public: Vector<RID> materials; Vector<RID> light_instances; Vector<RID> reflection_probe_instances; + Vector<RID> gi_probe_instances; Vector<float> morph_values; @@ -108,12 +109,14 @@ public: float depth; //used for sorting SelfList<InstanceBase> dependency_item; + InstanceBase *baked_light; //baked light to use + SelfList<InstanceBase> baked_light_item; virtual void base_removed()=0; virtual void base_changed()=0; virtual void base_material_changed()=0; - InstanceBase() : dependency_item(this) { + InstanceBase() : dependency_item(this), baked_light_item(this) { base_type=VS::INSTANCE_NONE; cast_shadows=VS::SHADOW_CASTING_SETTING_ON; @@ -123,6 +126,7 @@ public: billboard_y=false; depth_layer=0; layer_mask=1; + baked_light=NULL; } }; @@ -144,6 +148,11 @@ public: virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas)=0; virtual bool reflection_probe_instance_postprocess_step(RID p_instance)=0; + virtual RID gi_probe_instance_create()=0; + virtual void gi_probe_instance_set_light_data(RID p_probe,RID p_data)=0; + virtual void gi_probe_instance_set_transform_to_data(RID p_probe,const Transform& p_xform)=0; + virtual void gi_probe_instance_set_bounds(RID p_probe,const Vector3& p_bounds)=0; + virtual void render_scene(const Transform& p_cam_transform,const CameraMatrix& p_cam_projection,bool p_cam_ortogonal,InstanceBase** p_cull_result,int p_cull_count,RID* p_light_cull_result,int p_light_cull_count,RID* p_reflection_probe_cull_result,int p_reflection_probe_cull_count,RID p_environment,RID p_shadow_atlas,RID p_reflection_atlas,RID p_reflection_probe,int p_reflection_probe_pass)=0; virtual void render_shadow(RID p_light,RID p_shadow_atlas,int p_pass,InstanceBase** p_cull_result,int p_cull_count)=0; @@ -340,6 +349,7 @@ public: virtual VS::LightType light_get_type(RID p_light) const=0; virtual AABB light_get_aabb(RID p_light) const=0; virtual float light_get_param(RID p_light,VS::LightParam p_param)=0; + virtual Color light_get_color(RID p_light)=0; virtual uint64_t light_get_version(RID p_light) const=0; @@ -392,6 +402,39 @@ public: virtual void instance_add_dependency(RID p_base,RasterizerScene::InstanceBase *p_instance)=0; virtual void instance_remove_dependency(RID p_base,RasterizerScene::InstanceBase *p_instance)=0; + /* GI PROBE API */ + + virtual RID gi_probe_create()=0; + + virtual void gi_probe_set_bounds(RID p_probe,const AABB& p_bounds)=0; + virtual AABB gi_probe_get_bounds(RID p_probe) const=0; + + virtual void gi_probe_set_cell_size(RID p_probe,float p_range)=0; + virtual float gi_probe_get_cell_size(RID p_probe) const=0; + + virtual void gi_probe_set_to_cell_xform(RID p_probe,const Transform& p_xform)=0; + virtual Transform gi_probe_get_to_cell_xform(RID p_probe) const=0; + + virtual void gi_probe_set_dynamic_data(RID p_probe,const DVector<int>& p_data)=0; + virtual DVector<int> gi_probe_get_dynamic_data(RID p_probe) const=0; + + virtual void gi_probe_set_dynamic_range(RID p_probe,float p_range)=0; + virtual float gi_probe_get_dynamic_range(RID p_probe) const=0; + + + virtual void gi_probe_set_static_data(RID p_gi_probe,const DVector<uint8_t>& p_data,VS::GIProbeDataFormat p_format,int p_width,int p_height,int p_depth)=0; + virtual DVector<uint8_t> gi_probe_get_static_data(RID p_gi_probe) const=0; + virtual VS::GIProbeDataFormat gi_probe_get_static_data_format(RID p_gi_probe) const=0; + virtual int gi_probe_get_static_data_width(RID p_probe) const=0; + virtual int gi_probe_get_static_data_height(RID p_probe) const=0; + virtual int gi_probe_get_static_data_depth(RID p_probe) const=0; + + virtual RID gi_probe_get_data(RID p_probe)=0; //get data in case this is static + virtual uint32_t gi_probe_get_version(RID p_probe)=0; + + 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; + /* RENDER TARGET */ enum RenderTargetFlags { diff --git a/servers/visual/visual_server_light_baker.cpp b/servers/visual/visual_server_light_baker.cpp new file mode 100644 index 0000000000..4956d78ab0 --- /dev/null +++ b/servers/visual/visual_server_light_baker.cpp @@ -0,0 +1,6 @@ +#include "visual_server_light_baker.h" + +VisualServerLightBaker::VisualServerLightBaker() +{ + +} diff --git a/servers/visual/visual_server_light_baker.h b/servers/visual/visual_server_light_baker.h new file mode 100644 index 0000000000..42f016f614 --- /dev/null +++ b/servers/visual/visual_server_light_baker.h @@ -0,0 +1,29 @@ +#ifndef VISUALSERVERLIGHTBAKER_H +#define VISUALSERVERLIGHTBAKER_H + +#include "servers/visual_server.h" + +class VisualServerLightBaker { +public: + + struct BakeCell { + + uint32_t cells[8]; + uint32_t neighbours[7]; //one unused + uint32_t albedo; //albedo in RGBE + uint32_t emission; //emissive light in RGBE + uint32_t light[4]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast) + float alpha; //used for upsampling + uint32_t directional_pass; //used for baking directional + + }; + + + + + + + VisualServerLightBaker(); +}; + +#endif // VISUALSERVERLIGHTBAKER_H diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp index 6ba63a7690..2fa45d1796 100644 --- a/servers/visual/visual_server_raster.cpp +++ b/servers/visual/visual_server_raster.cpp @@ -90,9 +90,10 @@ void VisualServerRaster::draw(){ changes=0; + VSG::rasterizer->begin_frame(); + VSG::scene->update_dirty_instances(); //update scene stuff - VSG::rasterizer->begin_frame(); VSG::viewport->draw_viewports(); VSG::scene->render_probes(); //_draw_cursors_and_margins(); diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index 78b0e3cadd..191330c66c 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -803,12 +803,39 @@ public: BIND2(portal_set_disable_distance,RID , float ) BIND2(portal_set_disabled_color,RID , const Color& ) - /* CAMERA API */ + /* BAKED LIGHT API */ + + BIND0R(RID, gi_probe_create) + + BIND2(gi_probe_set_bounds,RID,const AABB&) + BIND1RC(AABB,gi_probe_get_bounds,RID) + + BIND2(gi_probe_set_cell_size,RID,float) + BIND1RC(float,gi_probe_get_cell_size,RID) + + BIND2(gi_probe_set_to_cell_xform,RID,const Transform&) + BIND1RC(Transform,gi_probe_get_to_cell_xform,RID) + + BIND2(gi_probe_set_dynamic_range,RID,float) + BIND1RC(float,gi_probe_get_dynamic_range,RID) + + BIND2(gi_probe_set_dynamic_data,RID,const DVector<int>& ) + BIND1RC( DVector<int>,gi_probe_get_dynamic_data,RID) + + BIND6(gi_probe_set_static_data,RID,const DVector<uint8_t>&,GIProbeDataFormat,int,int,int) + BIND1RC(DVector<uint8_t>,gi_probe_get_static_data,RID) + BIND1RC(GIProbeDataFormat,gi_probe_get_static_data_format,RID) + BIND1RC(int,gi_probe_get_static_data_width,RID) + BIND1RC(int,gi_probe_get_static_data_height,RID) + BIND1RC(int,gi_probe_get_static_data_depth,RID) + #undef BINDBASE //from now on, calls forwarded to this singleton #define BINDBASE VSG::scene + /* CAMERA API */ + BIND0R(RID, camera_create) BIND4(camera_set_perspective,RID,float, float , float ) @@ -936,7 +963,6 @@ public: BIND5(instance_geometry_set_draw_range,RID,float ,float ,float ,float ) BIND2(instance_geometry_set_as_instance_lod,RID,RID ) - #undef BINDBASE //from now on, calls forwarded to this singleton #define BINDBASE VSG::canvas diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index 6f7dac7f4d..62b3a23788 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -1,9 +1,12 @@ #include "visual_server_scene.h" #include "visual_server_global.h" - +#include "os/os.h" /* CAMERA API */ + + + RID VisualServerScene::camera_create() { Camera * camera = memnew( Camera ); @@ -118,6 +121,28 @@ void* VisualServerScene::_instance_pair(void *p_self, OctreeElementID, Instance geom->reflection_dirty=true; return E; //this element should make freeing faster + } else if (B->base_type==VS::INSTANCE_GI_PROBE && (1<<A->base_type)&VS::INSTANCE_GEOMETRY_MASK) { + + InstanceGIProbeData * gi_probe = static_cast<InstanceGIProbeData*>(B->base_data); + InstanceGeometryData * geom = static_cast<InstanceGeometryData*>(A->base_data); + + + InstanceGIProbeData::PairInfo pinfo; + pinfo.geometry=A; + pinfo.L = geom->gi_probes.push_back(B); + + List<InstanceGIProbeData::PairInfo>::Element *E = gi_probe->geometries.push_back(pinfo); + + geom->gi_probes_dirty=true; + + return E; //this element should make freeing faster + + } else if (B->base_type==VS::INSTANCE_GI_PROBE && A->base_type==VS::INSTANCE_LIGHT) { + + InstanceGIProbeData * gi_probe = static_cast<InstanceGIProbeData*>(B->base_data); + InstanceLightData * light = static_cast<InstanceLightData*>(A->base_data); + + return gi_probe->lights.insert(A); } @@ -134,14 +159,14 @@ void* VisualServerScene::_instance_pair(void *p_self, OctreeElementID, Instance //attempt to conncet portal A (will go through B anyway) //this is a little hackish, but works fine in practice - } else if (A->base_type==INSTANCE_BAKED_LIGHT || B->base_type==INSTANCE_BAKED_LIGHT) { + } else if (A->base_type==INSTANCE_GI_PROBE || B->base_type==INSTANCE_GI_PROBE) { - if (B->base_type==INSTANCE_BAKED_LIGHT) { + if (B->base_type==INSTANCE_GI_PROBE) { SWAP(A,B); } - ERR_FAIL_COND_V(B->base_type!=INSTANCE_BAKED_LIGHT_SAMPLER,NULL); - B->baked_light_sampler_info->baked_lights.insert(A); + ERR_FAIL_COND_V(B->base_type!=INSTANCE_GI_PROBE_SAMPLER,NULL); + B->gi_probe_sampler_info->gi_probes.insert(A); } else if (A->base_type==INSTANCE_ROOM || B->base_type==INSTANCE_ROOM) { @@ -218,6 +243,28 @@ void VisualServerScene::_instance_unpair(void *p_self, OctreeElementID, Instance geom->reflection_dirty=true; + } else if (B->base_type==VS::INSTANCE_GI_PROBE && (1<<A->base_type)&VS::INSTANCE_GEOMETRY_MASK) { + + InstanceGIProbeData * gi_probe = static_cast<InstanceGIProbeData*>(B->base_data); + InstanceGeometryData * geom = static_cast<InstanceGeometryData*>(A->base_data); + + List<InstanceGIProbeData::PairInfo>::Element *E = reinterpret_cast<List<InstanceGIProbeData::PairInfo>::Element*>(udata); + + geom->gi_probes.erase(E->get().L); + gi_probe->geometries.erase(E); + + geom->gi_probes_dirty=true; + + + } else if (B->base_type==VS::INSTANCE_GI_PROBE && A->base_type==VS::INSTANCE_LIGHT) { + + InstanceGIProbeData * gi_probe = static_cast<InstanceGIProbeData*>(B->base_data); + InstanceLightData * light = static_cast<InstanceLightData*>(A->base_data); + + + Set<Instance*>::Element *E = reinterpret_cast<Set<Instance*>::Element*>(udata); + + gi_probe->lights.erase(E); } #if 0 if (A->base_type==INSTANCE_PORTAL) { @@ -232,14 +279,14 @@ void VisualServerScene::_instance_unpair(void *p_self, OctreeElementID, Instance self->_portal_attempt_connect(A); self->_portal_attempt_connect(B); - } else if (A->base_type==INSTANCE_BAKED_LIGHT || B->base_type==INSTANCE_BAKED_LIGHT) { + } else if (A->base_type==INSTANCE_GI_PROBE || B->base_type==INSTANCE_GI_PROBE) { - if (B->base_type==INSTANCE_BAKED_LIGHT) { + if (B->base_type==INSTANCE_GI_PROBE) { SWAP(A,B); } - ERR_FAIL_COND(B->base_type!=INSTANCE_BAKED_LIGHT_SAMPLER); - B->baked_light_sampler_info->baked_lights.erase(A); + ERR_FAIL_COND(B->base_type!=INSTANCE_GI_PROBE_SAMPLER); + B->gi_probe_sampler_info->gi_probes.erase(A); } else if (A->base_type==INSTANCE_ROOM || B->base_type==INSTANCE_ROOM) { @@ -397,6 +444,25 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base){ reflection_probe_render_list.remove(&reflection_probe->update_list); } } break; + case VS::INSTANCE_GI_PROBE: { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData*>(instance->base_data); + + while(gi_probe->dynamic.updating_stage==GI_UPDATE_STAGE_LIGHTING) { + //wait until bake is done if it's baking + OS::get_singleton()->delay_usec(1); + } + if (gi_probe->update_element.in_list()) { + gi_probe_update_list.remove(&gi_probe->update_element); + } + if (gi_probe->dynamic.probe_data.is_valid()) { + VSG::storage->free(gi_probe->dynamic.probe_data); + } + + VSG::scene_render->free(gi_probe->probe_instance); + + } break; + } if (instance->base_data) { @@ -455,20 +521,20 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base){ } - if (instance->baked_light_info) { + if (instance->gi_probe_info) { - while(instance->baked_light_info->owned_instances.size()) { + while(instance->gi_probe_info->owned_instances.size()) { - Instance *owned=instance->baked_light_info->owned_instances.front()->get(); - owned->baked_light=NULL; - owned->data.baked_light=NULL; - owned->data.baked_light_octree_xform=NULL; + Instance *owned=instance->gi_probe_info->owned_instances.front()->get(); + owned->gi_probe=NULL; + owned->data.gi_probe=NULL; + owned->data.gi_probe_octree_xform=NULL; owned->BLE=NULL; - instance->baked_light_info->owned_instances.pop_front(); + instance->gi_probe_info->owned_instances.pop_front(); } - memdelete(instance->baked_light_info); - instance->baked_light_info=NULL; + memdelete(instance->gi_probe_info); + instance->gi_probe_info=NULL; } @@ -517,18 +583,18 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base){ } - if (instance->baked_light_sampler_info) { + if (instance->gi_probe_sampler_info) { - while (instance->baked_light_sampler_info->owned_instances.size()) { + while (instance->gi_probe_sampler_info->owned_instances.size()) { - instance_geometry_set_baked_light_sampler(instance->baked_light_sampler_info->owned_instances.front()->get()->self,RID()); + instance_geometry_set_gi_probe_sampler(instance->gi_probe_sampler_info->owned_instances.front()->get()->self,RID()); } - if (instance->baked_light_sampler_info->sampled_light.is_valid()) { - rasterizer->free(instance->baked_light_sampler_info->sampled_light); + if (instance->gi_probe_sampler_info->sampled_light.is_valid()) { + rasterizer->free(instance->gi_probe_sampler_info->sampled_light); } - memdelete( instance->baked_light_sampler_info ); - instance->baked_light_sampler_info=NULL; + memdelete( instance->gi_probe_sampler_info ); + instance->gi_probe_sampler_info=NULL; } #endif @@ -572,6 +638,19 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base){ reflection_probe->instance=VSG::scene_render->reflection_probe_instance_create(p_base); } break; + case VS::INSTANCE_GI_PROBE: { + + InstanceGIProbeData *gi_probe = memnew( InstanceGIProbeData ); + instance->base_data=gi_probe; + gi_probe->owner=instance; + + if (scenario && !gi_probe->update_element.in_list()) { + gi_probe_update_list.add(&gi_probe->update_element); + } + + gi_probe->probe_instance=VSG::scene_render->gi_probe_instance_create(); + + } break; } @@ -615,20 +694,20 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base){ instance->base_type=INSTANCE_PORTAL; instance->portal_info = memnew(Instance::PortalInfo); instance->portal_info->portal=portal_owner.get(p_base); - } else if (baked_light_owner.owns(p_base)) { + } else if (gi_probe_owner.owns(p_base)) { - instance->base_type=INSTANCE_BAKED_LIGHT; - instance->baked_light_info=memnew(Instance::BakedLightInfo); - instance->baked_light_info->baked_light=baked_light_owner.get(p_base); + instance->base_type=INSTANCE_GI_PROBE; + instance->gi_probe_info=memnew(Instance::BakedLightInfo); + instance->gi_probe_info->gi_probe=gi_probe_owner.get(p_base); //instance->portal_info = memnew(Instance::PortalInfo); //instance->portal_info->portal=portal_owner.get(p_base); - } else if (baked_light_sampler_owner.owns(p_base)) { + } else if (gi_probe_sampler_owner.owns(p_base)) { - instance->base_type=INSTANCE_BAKED_LIGHT_SAMPLER; - instance->baked_light_sampler_info=memnew( Instance::BakedLightSamplerInfo); - instance->baked_light_sampler_info->sampler=baked_light_sampler_owner.get(p_base); + instance->base_type=INSTANCE_GI_PROBE_SAMPLER; + instance->gi_probe_sampler_info=memnew( Instance::BakedLightSamplerInfo); + instance->gi_probe_sampler_info->sampler=gi_probe_sampler_owner.get(p_base); //instance->portal_info = memnew(Instance::PortalInfo); //instance->portal_info->portal=portal_owner.get(p_base); @@ -676,6 +755,13 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario){ InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData*>(instance->base_data); VSG::scene_render->reflection_probe_release_atlas_index(reflection_probe->instance); } break; + case VS::INSTANCE_GI_PROBE: { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData*>(instance->base_data); + if (gi_probe->update_element.in_list()) { + gi_probe_update_list.remove(&gi_probe->update_element); + } + } break; } @@ -704,6 +790,13 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario){ light->D = scenario->directional_lights.push_back(instance); } } break; + case VS::INSTANCE_GI_PROBE: { + + InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData*>(instance->base_data); + if (!gi_probe->update_element.in_list()) { + gi_probe_update_list.add(&gi_probe->update_element); + } + } break; } _instance_queue_update(instance,true,true); @@ -1006,13 +1099,13 @@ void VisualServerScene::_update_instance(Instance *p_instance) { else if (p_instance->base_type == INSTANCE_ROOM) { p_instance->room_info->affine_inverse=p_instance->data.transform.affine_inverse(); - } else if (p_instance->base_type == INSTANCE_BAKED_LIGHT) { + } else if (p_instance->base_type == INSTANCE_GI_PROBE) { Transform scale; - scale.basis.scale(p_instance->baked_light_info->baked_light->octree_aabb.size); - scale.origin=p_instance->baked_light_info->baked_light->octree_aabb.pos; + scale.basis.scale(p_instance->gi_probe_info->gi_probe->octree_aabb.size); + scale.origin=p_instance->gi_probe_info->gi_probe->octree_aabb.pos; //print_line("scale: "+scale); - p_instance->baked_light_info->affine_inverse=(p_instance->data.transform*scale).affine_inverse(); + p_instance->gi_probe_info->affine_inverse=(p_instance->data.transform*scale).affine_inverse(); } @@ -1077,6 +1170,13 @@ void VisualServerScene::_update_instance(Instance *p_instance) { pairable_mask=p_instance->visible?VS::INSTANCE_GEOMETRY_MASK:0; pairable=true; } + + if (p_instance->base_type == VS::INSTANCE_GI_PROBE) { + //lights and geometries + pairable_mask=p_instance->visible?VS::INSTANCE_GEOMETRY_MASK|(1<<VS::INSTANCE_LIGHT):0; + pairable=true; + } + #if 0 if (p_instance->base_type == VS::INSTANCE_PORTAL) { @@ -1085,9 +1185,9 @@ void VisualServerScene::_update_instance(Instance *p_instance) { pairable=true; } - if (p_instance->base_type == VS::INSTANCE_BAKED_LIGHT_SAMPLER) { + if (p_instance->base_type == VS::INSTANCE_GI_PROBE_SAMPLER) { - pairable_mask=(1<<INSTANCE_BAKED_LIGHT); + pairable_mask=(1<<INSTANCE_GI_PROBE); pairable=true; } @@ -1181,6 +1281,11 @@ void VisualServerScene::_update_instance_aabb(Instance *p_instance) { new_aabb = VSG::storage->reflection_probe_get_aabb(p_instance->base); } break; + case VisualServer::INSTANCE_GI_PROBE: { + + new_aabb = VSG::storage->gi_probe_get_bounds(p_instance->base); + + } break; #if 0 case VisualServer::INSTANCE_ROOM: { @@ -1208,18 +1313,18 @@ void VisualServerScene::_update_instance_aabb(Instance *p_instance) { } } break; - case VisualServer::INSTANCE_BAKED_LIGHT: { + case VisualServer::INSTANCE_GI_PROBE: { - BakedLight *baked_light = baked_light_owner.get( p_instance->base ); - ERR_FAIL_COND(!baked_light); - new_aabb=baked_light->octree_aabb; + BakedLight *gi_probe = gi_probe_owner.get( p_instance->base ); + ERR_FAIL_COND(!gi_probe); + new_aabb=gi_probe->octree_aabb; } break; - case VisualServer::INSTANCE_BAKED_LIGHT_SAMPLER: { + case VisualServer::INSTANCE_GI_PROBE_SAMPLER: { - BakedLightSampler *baked_light_sampler = baked_light_sampler_owner.get( p_instance->base ); - ERR_FAIL_COND(!baked_light_sampler); - float radius = baked_light_sampler->params[VS::BAKED_LIGHT_SAMPLER_RADIUS]; + BakedLightSampler *gi_probe_sampler = gi_probe_sampler_owner.get( p_instance->base ); + ERR_FAIL_COND(!gi_probe_sampler); + float radius = gi_probe_sampler->params[VS::BAKED_LIGHT_SAMPLER_RADIUS]; new_aabb=AABB(Vector3(-radius,-radius,-radius),Vector3(radius*2,radius*2,radius*2)); @@ -1805,6 +1910,13 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform,const Came } } + } else if (ins->base_type==VS::INSTANCE_GI_PROBE && ins->visible) { + + InstanceGIProbeData * gi_probe = static_cast<InstanceGIProbeData*>(ins->base_data); + if (!gi_probe->update_element.in_list()) { + gi_probe_update_list.add(&gi_probe->update_element); + } + } else if ((1<<ins->base_type)&VS::INSTANCE_GEOMETRY_MASK && ins->visible && ins->cast_shadows!=VS::SHADOW_CASTING_SETTING_SHADOWS_ONLY) { keep=true; @@ -1865,10 +1977,10 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform,const Came if (max>cull_range.max) cull_range.max=max; - if (ins->sampled_light && ins->sampled_light->baked_light_sampler_info->last_pass!=render_pass) { + if (ins->sampled_light && ins->sampled_light->gi_probe_sampler_info->last_pass!=render_pass) { if (light_samplers_culled<MAX_LIGHT_SAMPLERS) { light_sampler_cull_result[light_samplers_culled++]=ins->sampled_light; - ins->sampled_light->baked_light_sampler_info->last_pass=render_pass; + ins->sampled_light->gi_probe_sampler_info->last_pass=render_pass; } } } @@ -1908,6 +2020,21 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform,const Came geom->reflection_dirty=false; } + if (geom->gi_probes_dirty) { + int l=0; + //only called when reflection probe AABB enter/exit this geometry + ins->gi_probe_instances.resize(geom->gi_probes.size()); + + for (List<Instance*>::Element *E=geom->gi_probes.front();E;E=E->next()) { + + InstanceGIProbeData * gi_probe = static_cast<InstanceGIProbeData*>(E->get()->base_data); + + ins->gi_probe_instances[l++]=gi_probe->probe_instance; + } + + geom->gi_probes_dirty=false; + } + ins->depth = near_plane.distance_to(ins->transform.origin); ins->depth_layer=CLAMP(int(ins->depth*8/z_far),0,7); @@ -2117,7 +2244,7 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform,const Came } -bool VisualServerScene::_render_probe_step(Instance* p_instance,int p_step) { +bool VisualServerScene::_render_reflection_probe_step(Instance* p_instance,int p_step) { InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData*>(p_instance->base_data); Scenario *scenario = p_instance->scenario; @@ -2188,18 +2315,539 @@ bool VisualServerScene::_render_probe_step(Instance* p_instance,int p_step) { return false; } +void VisualServerScene::_gi_probe_fill_local_data(int p_idx, int p_level, int p_x, int p_y, int p_z, const GIProbeDataCell* p_cell, const GIProbeDataHeader *p_header, InstanceGIProbeData::LocalData *p_local_data, Vector<uint32_t> *prev_cell) { + + if (p_level==p_header->cell_subdiv-1) { + + Vector3 emission; + emission.x=(p_cell[p_idx].emission>>24)/255.0; + emission.y=((p_cell[p_idx].emission>>16)&0xFF)/255.0; + emission.z=((p_cell[p_idx].emission>>8)&0xFF)/255.0; + float l = (p_cell[p_idx].emission&0xFF)/255.0; + l*=8.0; + + emission*=l; + + p_local_data[p_idx].energy[0]=uint16_t(emission.x*1024); //go from 0 to 1024 for light + p_local_data[p_idx].energy[1]=uint16_t(emission.y*1024); //go from 0 to 1024 for light + p_local_data[p_idx].energy[2]=uint16_t(emission.z*1024); //go from 0 to 1024 for light + } else { + + p_local_data[p_idx].energy[0]=0; + p_local_data[p_idx].energy[1]=0; + p_local_data[p_idx].energy[2]=0; + + int half=(1<<(p_header->cell_subdiv-1))>>(p_level+1); + + for(int i=0;i<8;i++) { + + uint32_t child = p_cell[p_idx].children[i]; + + if (child==0xFFFFFFFF) + continue; + + int x = p_x; + int y = p_y; + int z = p_z; + + if (i&1) + x+=half; + if (i&2) + y+=half; + if (i&4) + z+=half; + + _gi_probe_fill_local_data(child,p_level+1,x,y,z,p_cell,p_header,p_local_data,prev_cell); + } + } + + //position for each part of the mipmaped texture + p_local_data[p_idx].pos[0]=p_x>>(p_header->cell_subdiv-p_level-1); + p_local_data[p_idx].pos[1]=p_y>>(p_header->cell_subdiv-p_level-1); + p_local_data[p_idx].pos[2]=p_z>>(p_header->cell_subdiv-p_level-1); + + prev_cell[p_level].push_back(p_idx); + +} + + +void VisualServerScene::_gi_probe_bake_threads(void* self) { + + VisualServerScene* vss = (VisualServerScene*)self; + vss->_gi_probe_bake_thread(); +} + +void VisualServerScene::_setup_gi_probe(Instance *p_instance) { + + + InstanceGIProbeData *probe = static_cast<InstanceGIProbeData*>(p_instance->base_data); + + if (probe->dynamic.probe_data.is_valid()) { + VSG::storage->free(probe->dynamic.probe_data); + probe->dynamic.probe_data=RID(); + } + + probe->dynamic.light_data=VSG::storage->gi_probe_get_dynamic_data(p_instance->base); + + if (probe->dynamic.light_data.size()) { + //using dynamic data + DVector<int>::Read r=probe->dynamic.light_data.read(); + + const GIProbeDataHeader *header = (GIProbeDataHeader *)r.ptr(); + + probe->dynamic.local_data.resize(header->cell_count); + + DVector<InstanceGIProbeData::LocalData>::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.mipmaps_3d.clear(); + + probe->dynamic.grid_size[0]=header->width; + probe->dynamic.grid_size[1]=header->height; + probe->dynamic.grid_size[2]=header->depth; + + for(int i=0;i<(int)header->cell_subdiv;i++) { + + uint32_t x = header->width >> i; + uint32_t y = header->height >> i; + uint32_t z = header->depth >> i; + + //create and clear mipmap + DVector<uint8_t> mipmap; + mipmap.resize(x*y*z*4); + DVector<uint8_t>::Write w = mipmap.write(); + zeromem(w.ptr(),x*y*z*4); + w = DVector<uint8_t>::Write(); + + probe->dynamic.mipmaps_3d.push_back(mipmap); + + if (x<=1 || y<=1 || z<=1) + break; + } + + probe->dynamic.updating_stage=GI_UPDATE_STAGE_CHECK; + probe->invalid=false; + probe->dynamic.enabled=true; + + Transform cell_to_xform = VSG::storage->gi_probe_get_to_cell_xform(p_instance->base); + AABB bounds = VSG::storage->gi_probe_get_bounds(p_instance->base); + float cell_size = VSG::storage->gi_probe_get_cell_size(p_instance->base); + + probe->dynamic.light_to_cell_xform=cell_to_xform * p_instance->transform.affine_inverse(); + + VSG::scene_render->gi_probe_instance_set_light_data(probe->probe_instance,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); + + + } else { + RID data = VSG::storage->gi_probe_get_data(p_instance->base); + + probe->dynamic.enabled=false; + probe->invalid=!data.is_valid(); + if (data.is_valid()) { + VSG::scene_render->gi_probe_instance_set_light_data(probe->probe_instance,data); + } + } + + probe->base_version=VSG::storage->gi_probe_get_version(p_instance->base); + +} + +void VisualServerScene::_gi_probe_bake_thread() { + + while(true) { + + probe_bake_sem->wait(); + if (probe_bake_thread_exit) { + break; + } + + Instance* to_bake=NULL; + + probe_bake_mutex->lock(); + + if (!probe_bake_list.empty()) { + to_bake=probe_bake_list.front()->get(); + probe_bake_list.pop_front(); + + } + probe_bake_mutex->unlock(); + + if (!to_bake) + continue; + + _bake_gi_probe(to_bake); + } +} + + + +uint32_t VisualServerScene::_gi_bake_find_cell(const GIProbeDataCell *cells,int x,int y, int z,int p_cell_subdiv) { + + + uint32_t cell=0; + + int ofs_x=0; + int ofs_y=0; + int ofs_z=0; + int size = 1<<(p_cell_subdiv-1); + int half=size/2; + + if (x<0 || x>=size) + return -1; + if (y<0 || y>=size) + return -1; + if (z<0 || z>=size) + return -1; + + for(int i=0;i<p_cell_subdiv-1;i++) { + + const GIProbeDataCell *bc = &cells[cell]; + + int child = 0; + if (x >= ofs_x + half) { + child|=1; + ofs_x+=half; + } + if (y >= ofs_y + half) { + child|=2; + ofs_y+=half; + } + if (z >= ofs_z + half) { + child|=4; + ofs_z+=half; + } + + cell = bc->children[child]; + if (cell==0xFFFFFFFF) + return 0xFFFFFFFF; + + half>>=1; + } + + return cell; + +} + +void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header,const GIProbeDataCell *cells,InstanceGIProbeData::LocalData *local_data,const uint32_t *leaves,int leaf_count, const InstanceGIProbeData::LightCache& light_cache,int sign) { + + + int light_r = int(light_cache.color.r * light_cache.energy * 1024.0)*sign; + int light_g = int(light_cache.color.g * light_cache.energy * 1024.0)*sign; + int light_b = int(light_cache.color.b * light_cache.energy * 1024.0)*sign; + + switch(light_cache.type) { + + case VS::LIGHT_DIRECTIONAL: { + + float limits[3]={float(header->width),float(header->height),float(header->depth)}; + Plane clip[3]; + int clip_planes=0; + float max_len = Vector3(limits[0],limits[1],limits[2]).length()*1.1; + + Vector3 light_axis = -light_cache.transform.basis.get_axis(2).normalized(); + + print_line("transform directional, axis: "+light_axis); + print_line("limits: "+Vector3(limits[0],limits[1],limits[2])); + + for(int i=0;i<3;i++) { + + if (ABS(light_axis[i])<CMP_EPSILON) + continue; + clip[clip_planes].normal[i]=1.0; + + if (light_axis[i]<0) { + + clip[clip_planes].d=limits[i]+1; + } else { + clip[clip_planes].d-=1.0; + } + + clip_planes++; + } + + float distance_adv; + { + Vector3 normal = light_axis; + Vector3 unorm = normal.abs(); + + if ( (unorm.x >= unorm.y) && (unorm.x >= unorm.z) ) { + // x code + unorm = normal.x > 0.0 ? Vector3( 1.0, 0.0, 0.0 ) : Vector3( -1.0, 0.0, 0.0 ) ; + } else if ( (unorm.y > unorm.x) && (unorm.y >= unorm.z) ) { + // y code + unorm = normal.y > 0.0 ? Vector3( 0.0, 1.0, 0.0 ) : Vector3( 0.0, -1.0, 0.0 ) ; + } else if ( (unorm.z > unorm.x) && (unorm.z > unorm.y) ) { + // z code + unorm = normal.z > 0.0 ? Vector3( 0.0, 0.0, 1.0 ) : Vector3( 0.0, 0.0, -1.0 ) ; + } else { + // oh-no we messed up code + // has to be + unorm = Vector3( 1.0, 0.0, 0.0 ); + } + + distance_adv = 1.0/normal.dot(unorm); + + } + + int success_count=0; + + uint64_t us = OS::get_singleton()->get_ticks_usec(); + + for(int i=0;i<leaf_count;i++) { + + uint32_t idx = leaves[i]; + + const GIProbeDataCell *cell = &cells[idx]; + InstanceGIProbeData::LocalData *light = &local_data[idx]; + + Vector3 to(light->pos[0]+0.5,light->pos[1]+0.5,light->pos[2]+0.5); + + + Vector3 from = to - max_len * light_axis; + + for(int j=0;j<clip_planes;j++) { + + clip[j].intersects_segment(from,to,&from); + } + + float distance = (to - from).length(); + distance+=distance_adv-Math::fmod(distance,distance_adv); //make it reach the center of the box always + from = to - light_axis * distance; + + uint32_t result=0xFFFFFFFF; + + while(distance>-distance_adv) { //use this to avoid precision errors + + result = _gi_bake_find_cell(cells,int(floor(from.x)),int(floor(from.y)),int(floor(from.z)),header->cell_subdiv); + if (result!=0xFFFFFFFF) { + break; + } + + from+=light_axis*distance_adv; + distance-=distance_adv; + } + + if (result==idx) { + //cell hit itself! hooray! + light->energy[0]+=(uint32_t(light_r)*((cell->albedo>>16)&0xFF))>>8; + light->energy[1]+=(uint32_t(light_g)*((cell->albedo>>8)&0xFF))>>8; + light->energy[2]+=(uint32_t(light_b)*((cell->albedo)&0xFF))>>8; + success_count++; + } + } + print_line("BAKE TIME: "+rtos((OS::get_singleton()->get_ticks_usec()-us)/1000000.0)); + print_line("valid cells: "+itos(success_count)); + + + } break; + } +} + + +void VisualServerScene::_bake_gi_downscale_light(int p_idx, int p_level, const GIProbeDataCell* p_cells, const GIProbeDataHeader *p_header, InstanceGIProbeData::LocalData *p_local_data) { + + //average light to upper level + p_local_data[p_idx].energy[0]=0; + p_local_data[p_idx].energy[1]=0; + p_local_data[p_idx].energy[2]=0; + + for(int i=0;i<8;i++) { + + uint32_t child = p_cells[p_idx].children[i]; + + if (child==0xFFFFFFFF) + continue; + + if (p_level+1 < (int)p_header->cell_subdiv-1) { + _bake_gi_downscale_light(child,p_level+1,p_cells,p_header,p_local_data); + } + + p_local_data[p_idx].energy[0]+=p_local_data[child].energy[0]; + p_local_data[p_idx].energy[1]+=p_local_data[child].energy[1]; + p_local_data[p_idx].energy[2]+=p_local_data[child].energy[2]; + + } + + //divide by eight for average + p_local_data[p_idx].energy[0]>>=3; + p_local_data[p_idx].energy[1]>>=3; + p_local_data[p_idx].energy[2]>>=3; + +} + + +void VisualServerScene::_bake_gi_probe(Instance *p_gi_probe) { + + InstanceGIProbeData * probe_data = static_cast<InstanceGIProbeData*>(p_gi_probe->base_data); + + DVector<int>::Read r=probe_data->dynamic.light_data.read(); + + const GIProbeDataHeader *header = (const GIProbeDataHeader *)r.ptr(); + const GIProbeDataCell *cells = (const GIProbeDataCell*)&r[16]; + + int leaf_count = probe_data->dynamic.level_cell_lists[ header->cell_subdiv -1 ].size(); + const uint32_t *leaves = probe_data->dynamic.level_cell_lists[ header->cell_subdiv -1 ].ptr(); + + DVector<InstanceGIProbeData::LocalData>::Write ldw = probe_data->dynamic.local_data.write(); + + InstanceGIProbeData::LocalData *local_data = ldw.ptr(); + + + //remove what must be removed + for (Map<RID,InstanceGIProbeData::LightCache>::Element *E=probe_data->dynamic.light_cache.front();E;E=E->next()) { + + RID rid = E->key(); + const InstanceGIProbeData::LightCache& lc = E->get(); + + if (!probe_data->dynamic.light_cache_changes.has(rid) || !(probe_data->dynamic.light_cache_changes[rid]==lc)) { + //erase light data + + _bake_gi_probe_light(header,cells,local_data,leaves,leaf_count,lc,-1); + } + + } + + //add what must be added + for (Map<RID,InstanceGIProbeData::LightCache>::Element *E=probe_data->dynamic.light_cache_changes.front();E;E=E->next()) { + + RID rid = E->key(); + const InstanceGIProbeData::LightCache& lc = E->get(); + + if (!probe_data->dynamic.light_cache.has(rid) || !(probe_data->dynamic.light_cache[rid]==lc)) { + //add light data + + _bake_gi_probe_light(header,cells,local_data,leaves,leaf_count,lc,1); + } + } + + SWAP(probe_data->dynamic.light_cache_changes,probe_data->dynamic.light_cache); + + //downscale to lower res levels + _bake_gi_downscale_light(0,0,cells,header,local_data); + + //plot result to 3D texture! + + for(int i=0;i<(int)header->cell_subdiv;i++) { + + int stage = header->cell_subdiv - i -1; + + if (stage >= probe_data->dynamic.mipmaps_3d.size()) + continue; //no mipmap for this one + + 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(); + + DVector<uint8_t>::Write lw = probe_data->dynamic.mipmaps_3d[stage].write(); + uint8_t *mipmapw = lw.ptr(); + + uint32_t sizes[3]={header->width>>stage,header->height>>stage,header->depth>>stage}; + + for(int j=0;j<level_cell_count;j++) { + + uint32_t idx = level_cells[j]; + + uint32_t r = local_data[idx].energy[0]>>2; + uint32_t g = local_data[idx].energy[1]>>2; + uint32_t b = local_data[idx].energy[2]>>2; + uint32_t a = cells[idx].alpha>>8; + + 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)); + + + } + } + + //send back to main thread to update un little chunks + probe_data->dynamic.updating_stage=GI_UPDATE_STAGE_UPLOADING; + +} + +bool VisualServerScene::_check_gi_probe(Instance *p_gi_probe) { + + InstanceGIProbeData * probe_data = static_cast<InstanceGIProbeData*>(p_gi_probe->base_data); + + probe_data->dynamic.light_cache_changes.clear(); + + bool all_equal=true; + + + for (List<Instance*>::Element *E=p_gi_probe->scenario->directional_lights.front();E;E=E->next()) { + + InstanceGIProbeData::LightCache lc; + lc.type=VSG::storage->light_get_type(E->get()->base); + lc.color=VSG::storage->light_get_color(E->get()->base); + lc.energy=VSG::storage->light_get_param(E->get()->base,VS::LIGHT_PARAM_ENERGY); + lc.radius=VSG::storage->light_get_param(E->get()->base,VS::LIGHT_PARAM_RANGE); + lc.attenuation=VSG::storage->light_get_param(E->get()->base,VS::LIGHT_PARAM_ATTENUATION); + lc.spot_angle=VSG::storage->light_get_param(E->get()->base,VS::LIGHT_PARAM_SPOT_ANGLE); + lc.spot_attenuation=VSG::storage->light_get_param(E->get()->base,VS::LIGHT_PARAM_SPOT_ATTENUATION); + lc.transform = probe_data->dynamic.light_to_cell_xform * E->get()->transform; + + if (!probe_data->dynamic.light_cache.has(E->get()->self) || !(probe_data->dynamic.light_cache[E->get()->self]==lc)) { + all_equal=false; + } + + probe_data->dynamic.light_cache_changes[E->get()->self]=lc; + + } + + + for (Set<Instance*>::Element *E=probe_data->lights.front();E;E=E->next()) { + + InstanceGIProbeData::LightCache lc; + lc.type=VSG::storage->light_get_type(E->get()->base); + lc.color=VSG::storage->light_get_color(E->get()->base); + lc.energy=VSG::storage->light_get_param(E->get()->base,VS::LIGHT_PARAM_ENERGY); + lc.radius=VSG::storage->light_get_param(E->get()->base,VS::LIGHT_PARAM_RANGE); + lc.attenuation=VSG::storage->light_get_param(E->get()->base,VS::LIGHT_PARAM_ATTENUATION); + lc.spot_angle=VSG::storage->light_get_param(E->get()->base,VS::LIGHT_PARAM_SPOT_ANGLE); + lc.spot_attenuation=VSG::storage->light_get_param(E->get()->base,VS::LIGHT_PARAM_SPOT_ATTENUATION); + lc.transform = probe_data->dynamic.light_to_cell_xform * E->get()->transform; + + if (!probe_data->dynamic.light_cache.has(E->get()->self) || !(probe_data->dynamic.light_cache[E->get()->self]==lc)) { + all_equal=false; + } + + probe_data->dynamic.light_cache_changes[E->get()->self]=lc; + } + + //lighting changed from after to before, must do some updating + return !all_equal || probe_data->dynamic.light_cache_changes.size()!=probe_data->dynamic.light_cache.size(); + +} void VisualServerScene::render_probes() { + /* REFLECTION PROBES */ - SelfList<InstanceReflectionProbeData> *probe = reflection_probe_render_list.first(); + SelfList<InstanceReflectionProbeData> *ref_probe = reflection_probe_render_list.first(); bool busy=false; - while(probe) { + while(ref_probe) { - SelfList<InstanceReflectionProbeData> *next=probe->next(); - RID base = probe->self()->owner->base; + SelfList<InstanceReflectionProbeData> *next=ref_probe->next(); + RID base = ref_probe->self()->owner->base; switch(VSG::storage->reflection_probe_get_update_mode(base)) { @@ -2207,11 +2855,11 @@ void VisualServerScene::render_probes() { if (busy) //already rendering something break; - bool done = _render_probe_step(probe->self()->owner,probe->self()->render_step); + bool done = _render_reflection_probe_step(ref_probe->self()->owner,ref_probe->self()->render_step); if (done) { - reflection_probe_render_list.remove(probe); + reflection_probe_render_list.remove(ref_probe); } else { - probe->self()->render_step++; + ref_probe->self()->render_step++; } busy=true; //do not render another one of this kind @@ -2221,17 +2869,92 @@ void VisualServerScene::render_probes() { int step=0; bool done=false; while(!done) { - done = _render_probe_step(probe->self()->owner,step); + done = _render_reflection_probe_step(ref_probe->self()->owner,step); step++; } - reflection_probe_render_list.remove(probe); + reflection_probe_render_list.remove(ref_probe); } break; } - probe=next; + ref_probe=next; } + + /* GI PROBES */ + + SelfList<InstanceGIProbeData> *gi_probe = gi_probe_update_list.first(); + + while(gi_probe) { + + SelfList<InstanceGIProbeData> *next=gi_probe->next(); + + InstanceGIProbeData *probe = gi_probe->self(); + Instance *instance_probe = probe->owner; + + //check if probe must be setup, but don't do if on the lighting thread + + bool force_lighting=false; + + if (probe->invalid || (probe->dynamic.updating_stage==GI_UPDATE_STAGE_CHECK && probe->base_version!=VSG::storage->gi_probe_get_version(instance_probe->base))) { + + _setup_gi_probe(instance_probe); + force_lighting=true; + } + + if (probe->invalid==false && probe->dynamic.enabled) { + + switch(probe->dynamic.updating_stage) { + case GI_UPDATE_STAGE_CHECK: { + + if (_check_gi_probe(instance_probe) || force_lighting) { + //send to lighting thread + probe->dynamic.updating_stage=GI_UPDATE_STAGE_LIGHTING; + +#ifndef NO_THREADS + probe_bake_mutex->lock(); + probe_bake_list.push_back(instance_probe); + probe_bake_mutex->unlock(); + probe_bake_sem->post(); + +#else + + _bake_gi_probe(instance_probe); +#endif + + } + } break; + case GI_UPDATE_STAGE_LIGHTING: { + //do none, wait til done! + + } break; + case GI_UPDATE_STAGE_UPLOADING: { + + uint64_t us = OS::get_singleton()->get_ticks_usec(); + + for(int i=0;i<(int)probe->dynamic.mipmaps_3d.size();i++) { + + int mmsize = probe->dynamic.mipmaps_3d[i].size(); + DVector<uint8_t>::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()); + } + + + probe->dynamic.updating_stage=GI_UPDATE_STAGE_CHECK; + +// print_line("UPLOAD TIME: "+rtos((OS::get_singleton()->get_ticks_usec()-us)/1000000.0)); + } break; + + } + } + //_update_gi_probe(gi_probe->self()->owner); + + + gi_probe=next; + } + + + } void VisualServerScene::_update_dirty_instance(Instance *p_instance) { @@ -2423,10 +3146,32 @@ bool VisualServerScene::free(RID p_rid) { VisualServerScene *VisualServerScene::singleton=NULL; + VisualServerScene::VisualServerScene() { +#ifndef NO_THREADS + probe_bake_sem = Semaphore::create(); + probe_bake_mutex = Mutex::create(); + probe_bake_thread = Thread::create(_gi_probe_bake_threads,this); + probe_bake_thread_exit=false; +#endif + render_pass=1; singleton=this; } + +VisualServerScene::~VisualServerScene() { + +#ifndef NO_THREADS + probe_bake_thread_exit=true; + Thread::wait_to_finish(probe_bake_thread); + memdelete(probe_bake_thread); + memdelete(probe_bake_sem); + memdelete(probe_bake_mutex); + +#endif + + +} diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h index 0eaad45c97..63cb1fd2b8 100644 --- a/servers/visual/visual_server_scene.h +++ b/servers/visual/visual_server_scene.h @@ -7,6 +7,9 @@ #include "allocators.h" #include "octree.h" #include "self_list.h" +#include "os/thread.h" +#include "os/semaphore.h" +#include "semaphore.h" class VisualServerScene { public: @@ -69,6 +72,8 @@ public: #endif + /* CAMERA API */ + struct Camera : public RID_Data { enum Type { @@ -141,6 +146,7 @@ public: */ + /* SCENARIO API */ struct Instance; @@ -296,11 +302,15 @@ public: List<Instance*> reflection_probes; bool reflection_dirty; + List<Instance*> gi_probes; + bool gi_probes_dirty; + InstanceGeometryData() { lighting_dirty=false; reflection_dirty=true; can_cast_shadows=true; + gi_probes_dirty=true; } }; @@ -310,7 +320,7 @@ public: Instance *owner; struct PairInfo { - List<Instance*>::Element *L; //light iterator in geometry + List<Instance*>::Element *L; //reflection iterator in geometry Instance *geometry; }; List<PairInfo> geometries; @@ -346,14 +356,112 @@ public: List<PairInfo> geometries; + Instance *baked_light; + InstanceLightData() { shadow_dirty=true; D=NULL; last_version=0; + baked_light=NULL; } }; + struct InstanceGIProbeData : public InstanceBaseData { + + + Instance *owner; + + struct PairInfo { + List<Instance*>::Element *L; //gi probe iterator in geometry + Instance *geometry; + }; + + List<PairInfo> geometries; + + Set<Instance*> lights; + + struct LightCache { + + VS::LightType type; + Transform transform; + Color color; + float energy; + float radius; + float attenuation; + float spot_angle; + float spot_attenuation; + + bool operator==(const LightCache& p_cache) { + + return (type==p_cache.type && + transform==p_cache.transform && + color==p_cache.color && + energy==p_cache.energy && + radius==p_cache.radius && + attenuation==p_cache.attenuation && + spot_angle==p_cache.spot_angle && + spot_attenuation==p_cache.spot_attenuation); + } + + LightCache() { + + type=VS::LIGHT_DIRECTIONAL; + energy=1.0; + radius=1.0; + attenuation=1.0; + spot_angle=1.0; + spot_attenuation=1.0; + + } + + }; + + struct LocalData { + uint16_t pos[3]; + uint16_t energy[3]; //using 0..1024 for float range 0..1. integer is needed for deterministic add/remove of lights + }; + + + struct Dynamic { + + Map<RID,LightCache> light_cache; + Map<RID,LightCache> light_cache_changes; + DVector<int> light_data; + DVector<LocalData> local_data; + Vector<Vector<uint32_t> > level_cell_lists; + RID probe_data; + bool enabled; + + Vector< DVector<uint8_t> > mipmaps_3d; + + int updating_stage; + + int grid_size[3]; + + Transform light_to_cell_xform; + + } dynamic; + + + RID probe_instance; + + + bool invalid; + uint32_t base_version; + + SelfList<InstanceGIProbeData> update_element; + + InstanceGIProbeData() : update_element(this) { + invalid=true; + base_version=0; + } + + }; + + + SelfList<InstanceGIProbeData>::List gi_probe_update_list; + Instance *instance_cull_result[MAX_INSTANCE_CULL]; Instance *instance_shadow_cull_result[MAX_INSTANCE_CULL]; //used for generating shadowmaps @@ -410,11 +518,61 @@ public: void render_camera(RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas); void update_dirty_instances(); - bool _render_probe_step(Instance* p_instance,int p_step); + //probes + struct GIProbeDataHeader { + + uint32_t version; + uint32_t cell_subdiv; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t cell_count; + uint32_t leaf_cell_count; + }; + + + struct GIProbeDataCell { + + uint32_t children[8]; + uint32_t albedo; + uint32_t emission; + uint32_t sides_used; + uint32_t alpha; + }; + + enum { + GI_UPDATE_STAGE_CHECK, + GI_UPDATE_STAGE_LIGHTING, + GI_UPDATE_STAGE_UPLOADING, + }; + + void _gi_probe_bake_thread(); + static void _gi_probe_bake_threads(void*); + + volatile bool probe_bake_thread_exit; + Thread *probe_bake_thread; + Semaphore *probe_bake_sem; + Mutex *probe_bake_mutex; + List<Instance*> probe_bake_list; + + bool _render_reflection_probe_step(Instance* p_instance,int p_step); + void _gi_probe_fill_local_data(int p_idx,int p_level,int p_x,int p_y,int p_z,const GIProbeDataCell* p_cell,const GIProbeDataHeader *p_header,InstanceGIProbeData::LocalData *p_local_data,Vector<uint32_t> *prev_cell); + + _FORCE_INLINE_ uint32_t _gi_bake_find_cell(const GIProbeDataCell *cells,int x,int y, int z,int p_cell_subdiv); + void _bake_gi_downscale_light(int p_idx, int p_level, const GIProbeDataCell* p_cells, const GIProbeDataHeader *p_header, InstanceGIProbeData::LocalData *p_local_data); + + void _bake_gi_probe_light(const GIProbeDataHeader *header,const GIProbeDataCell *cells,InstanceGIProbeData::LocalData *local_data,const uint32_t *leaves,int p_leaf_count, const InstanceGIProbeData::LightCache& light_cache,int p_sign); + void _bake_gi_probe(Instance *p_probe); + bool _check_gi_probe(Instance *p_gi_probe); + void _setup_gi_probe(Instance *p_instance); + void render_probes(); + + bool free(RID p_rid); VisualServerScene(); + ~VisualServerScene(); }; #endif // VISUALSERVERSCENE_H diff --git a/servers/visual_server.h b/servers/visual_server.h index 7558fbf818..6f2aa73952 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -445,7 +445,39 @@ public: virtual void portal_set_disable_distance(RID p_portal, float p_distance)=0; virtual void portal_set_disabled_color(RID p_portal, const Color& p_color)=0; - /* BAKED LIGHT API */ + /* GI PROBE API */ + + virtual RID gi_probe_create()=0; + + virtual void gi_probe_set_bounds(RID p_probe,const AABB& p_bounds)=0; + virtual AABB gi_probe_get_bounds(RID p_probe) const=0; + + virtual void gi_probe_set_cell_size(RID p_probe,float p_range)=0; + virtual float gi_probe_get_cell_size(RID p_probe) const=0; + + virtual void gi_probe_set_to_cell_xform(RID p_probe,const Transform& p_xform)=0; + virtual Transform gi_probe_get_to_cell_xform(RID p_probe) const=0; + + virtual void gi_probe_set_dynamic_data(RID p_probe,const DVector<int>& p_data)=0; + virtual DVector<int> gi_probe_get_dynamic_data(RID p_probe) const=0; + + virtual void gi_probe_set_dynamic_range(RID p_probe,float p_range)=0; + virtual float gi_probe_get_dynamic_range(RID p_probe) const=0; + + enum GIProbeDataFormat { + GI_PROBE_DATA_RGBA8, + GI_PROBE_DATA_DXT5, + GI_PROBE_DATA_ETC2_EAC, + }; + + virtual void gi_probe_set_static_data(RID p_gi_probe,const DVector<uint8_t>& p_data,GIProbeDataFormat p_format,int p_width,int p_height,int p_depth)=0; + virtual DVector<uint8_t> gi_probe_get_static_data(RID p_gi_probe) const=0; + virtual GIProbeDataFormat gi_probe_get_static_data_format(RID p_gi_probe) const=0; + virtual int gi_probe_get_static_data_width(RID p_probe) const=0; + virtual int gi_probe_get_static_data_height(RID p_probe) const=0; + virtual int gi_probe_get_static_data_depth(RID p_probe) const=0; + + /* CAMERA API */ @@ -600,9 +632,9 @@ public: INSTANCE_REFLECTION_PROBE, INSTANCE_ROOM, INSTANCE_PORTAL, + INSTANCE_GI_PROBE, INSTANCE_MAX, - /*INSTANCE_BAKED_LIGHT, - INSTANCE_BAKED_LIGHT_SAMPLER,*/ + /*INSTANCE_BAKED_LIGHT_SAMPLER,*/ INSTANCE_GEOMETRY_MASK=(1<<INSTANCE_MESH)|(1<<INSTANCE_MULTIMESH)|(1<<INSTANCE_IMMEDIATE) }; diff --git a/tools/editor/io_plugins/editor_import_collada.cpp b/tools/editor/io_plugins/editor_import_collada.cpp index 217a5fe78d..1d53ccf1ef 100644 --- a/tools/editor/io_plugins/editor_import_collada.cpp +++ b/tools/editor/io_plugins/editor_import_collada.cpp @@ -394,7 +394,8 @@ Error ColladaImport::_create_material(const String& p_target) { Ref<Texture> texture = ResourceLoader::load(texfile,"Texture"); if (texture.is_valid()) { -// material->set_texture(FixedSpatialMaterial::PARAM_DIFFUSE,texture); + material->set_texture(FixedSpatialMaterial::TEXTURE_ALBEDO,texture); + material->set_albedo(Color(1,1,1,1)); // material->set_parameter(FixedSpatialMaterial::PARAM_DIFFUSE,Color(1,1,1,1)); } else { missing_textures.push_back(texfile.get_file()); @@ -413,6 +414,8 @@ Error ColladaImport::_create_material(const String& p_target) { Ref<Texture> texture = ResourceLoader::load(texfile,"Texture"); if (texture.is_valid()) { + material->set_texture(FixedSpatialMaterial::TEXTURE_SPECULAR,texture); + material->set_specular(Color(1,1,1,1)); // material->set_texture(FixedSpatialMaterial::PARAM_SPECULAR,texture); // material->set_parameter(FixedSpatialMaterial::PARAM_SPECULAR,Color(1,1,1,1)); @@ -435,7 +438,9 @@ Error ColladaImport::_create_material(const String& p_target) { Ref<Texture> texture = ResourceLoader::load(texfile,"Texture"); if (texture.is_valid()) { -// material->set_texture(FixedSpatialMaterial::PARAM_EMISSION,texture); + material->set_texture(FixedSpatialMaterial::TEXTURE_EMISSION,texture); + material->set_emission(Color(1,1,1,1)); + // material->set_parameter(FixedSpatialMaterial::PARAM_EMISSION,Color(1,1,1,1)); }else { // missing_textures.push_back(texfile.get_file()); @@ -455,6 +460,8 @@ Error ColladaImport::_create_material(const String& p_target) { Ref<Texture> texture = ResourceLoader::load(texfile,"Texture"); if (texture.is_valid()) { + material->set_texture(FixedSpatialMaterial::TEXTURE_NORMAL,texture); +// material->set_emission(Color(1,1,1,1)); // material->set_texture(FixedSpatialMaterial::PARAM_NORMAL,texture); }else { @@ -466,8 +473,10 @@ Error ColladaImport::_create_material(const String& p_target) { // material->set_parameter(FixedSpatialMaterial::PARAM_SPECULAR_EXP,effect.shininess); -// material->set_flag(Material::FLAG_DOUBLE_SIDED,effect.double_sided); -// material->set_flag(Material::FLAG_UNSHADED,effect.unshaded); + if (effect.double_sided) { + material->set_cull_mode(FixedSpatialMaterial::CULL_DISABLED); + } + material->set_flag(FixedSpatialMaterial::FLAG_UNSHADED,effect.unshaded); diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.cpp b/tools/editor/io_plugins/editor_scene_import_plugin.cpp index 41b245eb7d..e3fb8986c6 100644 --- a/tools/editor/io_plugins/editor_scene_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_scene_import_plugin.cpp @@ -1692,7 +1692,7 @@ Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh> String str=name; int layer = str.substr(str.find("lm")+3,str.length()).to_int(); - mi->set_baked_light_texture_id(layer); + //mi->set_baked_light_texture_id(layer); } if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(name,"colonly")) { diff --git a/tools/editor/spatial_editor_gizmos.cpp b/tools/editor/spatial_editor_gizmos.cpp index a4a1aaedf0..98b9a126dc 100644 --- a/tools/editor/spatial_editor_gizmos.cpp +++ b/tools/editor/spatial_editor_gizmos.cpp @@ -2397,6 +2397,163 @@ ReflectionProbeGizmo::ReflectionProbeGizmo(ReflectionProbe* p_probe){ +/// + + +String GIProbeGizmo::get_handle_name(int p_idx) const { + + switch(p_idx) { + case 0: return "Extents X"; + case 1: return "Extents Y"; + case 2: return "Extents Z"; + } + + return ""; +} +Variant GIProbeGizmo::get_handle_value(int p_idx) const{ + + return probe->get_extents(); +} +void GIProbeGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){ + + Transform gt = probe->get_global_transform(); + //gt.orthonormalize(); + Transform gi = gt.affine_inverse(); + + + Vector3 extents = probe->get_extents(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*16384)}; + + Vector3 axis; + axis[p_idx]=1.0; + + Vector3 ra,rb; + Geometry::get_closest_points_between_segments(Vector3(),axis*16384,sg[0],sg[1],ra,rb); + float d = ra[p_idx]; + if (d<0.001) + d=0.001; + + extents[p_idx]=d; + probe->set_extents(extents); + +} + +void GIProbeGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){ + + Vector3 restore = p_restore; + + if (p_cancel) { + probe->set_extents(restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Probe Extents")); + ur->add_do_method(probe,"set_extents",probe->get_extents()); + ur->add_undo_method(probe,"set_extents",restore); + ur->commit_action(); + +} + +void GIProbeGizmo::redraw(){ + + clear(); + + Vector<Vector3> lines; + Vector3 extents = probe->get_extents(); + + static const int subdivs[GIProbe::SUBDIV_MAX]={64,128,256,512}; + + AABB aabb = AABB(-extents,extents*2); + int subdiv = subdivs[probe->get_subdiv()]; + float cell_size = aabb.get_longest_axis_size()/subdiv; + + + for(int i=0;i<12;i++) { + Vector3 a,b; + aabb.get_edge(i,a,b); + lines.push_back(a); + lines.push_back(b); + } + + add_lines(lines,SpatialEditorGizmos::singleton->gi_probe_material); + add_collision_segments(lines); + + lines.clear(); + + for(int i=1;i<subdiv;i++) { + + for(int j=0;j<3;j++) { + + + + if (cell_size*i>aabb.size[j]) { + continue; + } + + Vector2 dir; + dir[j]=1.0; + Vector2 ta,tb; + int j_n1=(j+1)%3; + int j_n2=(j+2)%3; + ta[j_n1]=1.0; + tb[j_n2]=1.0; + + + for(int k=0;k<4;k++) { + + Vector3 from=aabb.pos,to=aabb.pos; + from[j]+= cell_size*i; + to[j]+=cell_size*i; + + if (k&1) { + to[j_n1]+=aabb.size[j_n1]; + } else { + + to[j_n2]+=aabb.size[j_n2]; + } + + if (k&2) { + from[j_n1]+=aabb.size[j_n1]; + from[j_n2]+=aabb.size[j_n2]; + } + + lines.push_back(from); + lines.push_back(to); + } + + } + + } + + add_lines(lines,SpatialEditorGizmos::singleton->reflection_probe_material_internal); + + Vector<Vector3> handles; + + + for(int i=0;i<3;i++) { + + Vector3 ax; + ax[i]=aabb.pos[i]+aabb.size[i]; + handles.push_back(ax); + } + + + add_handles(handles); + +} +GIProbeGizmo::GIProbeGizmo(GIProbe* p_probe){ + + probe=p_probe; + set_spatial_node(p_probe); +} + +//////// + void NavigationMeshSpatialGizmo::redraw() { @@ -3093,6 +3250,11 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) { Ref<ReflectionProbeGizmo> misg = memnew( ReflectionProbeGizmo(p_spatial->cast_to<ReflectionProbe>()) ); return misg; } + if (p_spatial->cast_to<GIProbe>()) { + + Ref<GIProbeGizmo> misg = memnew( GIProbeGizmo(p_spatial->cast_to<GIProbe>()) ); + return misg; + } if (p_spatial->cast_to<VehicleWheel>()) { @@ -3146,8 +3308,8 @@ Ref<FixedSpatialMaterial> SpatialEditorGizmos::create_line_material(const Color& line_material->set_flag(FixedSpatialMaterial::FLAG_UNSHADED, true); line_material->set_line_width(3.0); line_material->set_feature(FixedSpatialMaterial::FEATURE_TRANSPARENT, true); - line_material->set_flag(FixedSpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - line_material->set_flag(FixedSpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); + //line_material->set_flag(FixedSpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + //->set_flag(FixedSpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); line_material->set_albedo(p_base_color); return line_material; @@ -3298,7 +3460,9 @@ SpatialEditorGizmos::SpatialEditorGizmos() { car_wheel_material = create_line_material(Color(0.6,0.8,1.0)); visibility_notifier_material = create_line_material(Color(1.0,0.5,1.0)); reflection_probe_material = create_line_material(Color(0.5,1.0,0.7)); - reflection_probe_material_internal = create_line_material(Color(0.3,0.8,0.5,0.4)); + reflection_probe_material_internal = create_line_material(Color(0.3,0.8,0.5,0.15)); + gi_probe_material = create_line_material(Color(0.7,1.0,0.5)); + gi_probe_material_internal = create_line_material(Color(0.5,0.8,0.3,0.4)); joint_material = create_line_material(Color(0.6,0.8,1.0)); stream_player_icon = Ref<FixedSpatialMaterial>( memnew( FixedSpatialMaterial )); diff --git a/tools/editor/spatial_editor_gizmos.h b/tools/editor/spatial_editor_gizmos.h index 2cc1a7eab8..eba143d32b 100644 --- a/tools/editor/spatial_editor_gizmos.h +++ b/tools/editor/spatial_editor_gizmos.h @@ -46,6 +46,7 @@ #include "scene/3d/ray_cast.h" #include "scene/3d/navigation_mesh.h" #include "scene/3d/reflection_probe.h" +#include "scene/3d/gi_probe.h" #include "scene/3d/vehicle_body.h" #include "scene/3d/collision_polygon.h" @@ -327,6 +328,25 @@ public: }; +class GIProbeGizmo : public EditorSpatialGizmo { + + OBJ_TYPE(GIProbeGizmo ,EditorSpatialGizmo); + + + GIProbe* probe; + +public: + + virtual String get_handle_name(int p_idx) const; + virtual Variant get_handle_value(int p_idx) const; + virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point); + virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false); + + void redraw(); + GIProbeGizmo(GIProbe* p_notifier=NULL); + +}; + class CollisionShapeSpatialGizmo : public EditorSpatialGizmo { @@ -496,6 +516,8 @@ public: Ref<FixedSpatialMaterial> skeleton_material; Ref<FixedSpatialMaterial> reflection_probe_material; Ref<FixedSpatialMaterial> reflection_probe_material_internal; + Ref<FixedSpatialMaterial> gi_probe_material; + Ref<FixedSpatialMaterial> gi_probe_material_internal; Ref<FixedSpatialMaterial> room_material; Ref<FixedSpatialMaterial> portal_material; Ref<FixedSpatialMaterial> raycast_material; |